diff --git a/doc/connectivity/bluetooth/api/mesh/shell.rst b/doc/connectivity/bluetooth/api/mesh/shell.rst index f9acf24d277cb3..e6609f40e5cc12 100644 --- a/doc/connectivity/bluetooth/api/mesh/shell.rst +++ b/doc/connectivity/bluetooth/api/mesh/shell.rst @@ -1034,15 +1034,14 @@ Firmware Update Client model The Firmware Update Client model can be added to the mesh shell by enabling configuration options :kconfig:option:`CONFIG_BT_MESH_BLOB_CLI` and :kconfig:option:`CONFIG_BT_MESH_DFU_CLI`. The Firmware Update Client demonstrates the firmware update Distributor role by transferring a dummy firmware update to a set of Target nodes. -``mesh models dfu slot add [ [ []]]`` +``mesh models dfu slot add []`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Add a virtual DFU image slot that can be transferred as a DFU image. The image slot will be assigned an image slot index, which is printed as a response, and can be used to reference the slot in other commands. To update the image slot, remove it using the ``mesh models dfu slot del`` shell command and then add it again. * ``Size``: DFU image slot size in bytes. - * ``FwID``: Optional firmware ID, formatted as a hexstring. + * ``FwID``: Firmware ID, formatted as a hexstring. * ``Metadata``: Optional firmware metadata, formatted as a hexstring. - * ``URI``: Optional URI for the firmware. ``mesh models dfu slot del `` diff --git a/include/zephyr/bluetooth/mesh/dfd_srv.h b/include/zephyr/bluetooth/mesh/dfd_srv.h index 0e281b29186d5a..da339c57ec6d5d 100644 --- a/include/zephyr/bluetooth/mesh/dfd_srv.h +++ b/include/zephyr/bluetooth/mesh/dfd_srv.h @@ -29,11 +29,47 @@ extern "C" { #define CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX 0 #endif +#ifndef CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE +#define CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE 0 +#endif + +#ifndef CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE +#define CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE 0 +#endif + struct bt_mesh_dfd_srv; +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD +/** + * + * @brief Initialization parameters for the @ref bt_mesh_dfd_srv with OOB + * upload support. + * + * @param[in] _cb Pointer to a @ref bt_mesh_dfd_srv_cb instance. + * @param[in] _oob_schemes Array of OOB schemes supported by the server, + * each scheme being a code point from the + * Bluetooth SIG Assigned Numbers document. + * @param[in] _oob_schemes_count Number of schemes in @c _oob_schemes. + */ +#define BT_MESH_DFD_SRV_OOB_INIT(_cb, _oob_schemes, _oob_schemes_count) \ + { \ + .cb = _cb, \ + .dfu = BT_MESH_DFU_CLI_INIT(&_bt_mesh_dfd_srv_dfu_cb), \ + .upload = { \ + .blob = { .cb = &_bt_mesh_dfd_srv_blob_cb }, \ + }, \ + .oob_schemes = { \ + .schemes = _oob_schemes, \ + .count = _oob_schemes_count, \ + }, \ + } +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ + /** * * @brief Initialization parameters for the @ref bt_mesh_dfd_srv. + * + * @param[in] _cb Pointer to a @ref bt_mesh_dfd_srv_cb instance. */ #define BT_MESH_DFD_SRV_INIT(_cb) \ { \ @@ -75,6 +111,64 @@ struct bt_mesh_dfd_srv_cb { const struct bt_mesh_dfu_slot *slot, const struct bt_mesh_blob_io **io); +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + /** @brief Firmware upload OOB start callback. + * + * Called at the start of an OOB firmware upload. The application must + * start a firmware check using an OOB mechanism, and then call + * @ref bt_mesh_dfd_srv_oob_check_complete. Depending on the return + * value of this function, the application must then start storing the + * firmware image using an OOB mechanism, and call + * @ref bt_mesh_dfd_srv_oob_store_complete. This callback is mandatory + * to support OOB uploads. + * + * @param srv Firmware Distribution Server model instance. + * @param slot Slot to be used for the upload. + * @param uri Pointer to buffer containing the URI used to + * check for new firmware. + * @param uri_len Length of the URI buffer. + * @param fwid Pointer to buffer containing the current + * firmware ID to be used when checking for + * availability of new firmware. + * @param fwid_len Length of the current firmware ID. Must be set + * to the length of the new firmware ID if it is + * available, or to 0 if new firmware is not + * available. + * + * @return BT_MESH_DFD_SUCCESS on success, or error code otherwise. + */ + int (*start_oob_upload)(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, + const char *uri, uint8_t uri_len, + const uint8_t *fwid, uint16_t fwid_len); + + /** @brief Cancel store OOB callback + * + * Called when an OOB store is cancelled. The application must stop + * any ongoing OOB image transfer. This callback is mandatory to + * support OOB uploads. + * + * @param srv Firmware Distribution Server model instance. + * @param slot DFU image slot to cancel + */ + void (*cancel_oob_upload)(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot); + + /** @brief Get the progress of an ongoing OOB store + * + * Called by the Firmware Distribution Server model when it needs to + * get the current progress of an ongoing OOB store from the + * application. This callback is mandatory to support OOB uploads. + * + * @param srv Firmware Distribution Server model instance. + * @param slot DFU image slot to get progress for. + * + * @return The current progress of the ongoing OOB store, in percent. + */ + uint8_t (*oob_progress_get)(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot); +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ + /** @brief Slot delete callback. * * Called when the Firmware Distribution Server is about to delete a DFU image slot. @@ -129,12 +223,79 @@ struct bt_mesh_dfd_srv { struct { enum bt_mesh_dfd_upload_phase phase; - const struct bt_mesh_dfu_slot *slot; + struct bt_mesh_dfu_slot *slot; const struct flash_area *area; struct bt_mesh_blob_srv blob; +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + bool is_oob; + struct { + uint8_t uri_len; + uint8_t uri[CONFIG_BT_MESH_DFU_URI_MAXLEN]; + uint16_t current_fwid_len; + uint8_t current_fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN]; + struct bt_mesh_msg_ctx ctx; + } oob; +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ } upload; + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + struct { + const uint8_t *schemes; + const uint8_t count; + } oob_schemes; +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ }; +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD +/** @brief Call when an OOB check has completed or failed + * + * This should be called by the application after an OOB check started by the @c start_oob_upload + * callback has completed or failed. The @p status param should be set to one of the following + * values: + * + * * @c BT_MESH_DFD_SUCCESS if the check was succesfull and a new firmware ID was found. + * * @c BT_MESH_DFD_ERR_URI_MALFORMED if the URI is not formatted correctly. + * * @c BT_MESH_DFD_ERR_URI_NOT_SUPPORTED if the URI scheme is not supported by the node. + * * @c BT_MESH_DFD_ERR_URI_UNREACHABLE if the URI can't be reached. + * * @c BT_MESH_DFD_ERR_NEW_FW_NOT_AVAILABLE if the check completes successfully but no new + * firmware is available. + * + * If this function returns 0, the application should then download the firmware to the + * slot. If an error code is returned, the application should abort the OOB upload. + * + * @param srv Firmware Distribution Server model instance. + * @param slot The slot used in the OOB upload. + * @param status The result of the firmware check. + * @param fwid If the check was successful and new firmware found, this should point to a + * buffer containing the new firmware ID to store. + * @param fwid_len The length of the firmware ID pointed to by @p fwid. + * + * @return 0 on success, (negative) error code otherwise. + */ +int bt_mesh_dfd_srv_oob_check_complete(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, int status, + uint8_t *fwid, size_t fwid_len); + +/** @brief Call when an OOB store has completed or failed + * + * This should be called by the application after an OOB store started after a succesfull call to + * @c bt_mesh_dfd_srv_oob_check_complete has completed successfully or failed. + * + * @param srv Firmware Distribution Server model instance. + * @param slot The slot used when storing the firmware image. + * @param success @c true if the OOB store completed successfully, @c false otherwise. + * @param size The size of the stored firmware image, in bytes. + * @param metadata Pointer to the metadata received OOB, or @c NULL if no metadata was + * received. + * @param metadata_len Size of the metadata pointed to by @p metadata. + * + * @return 0 on success, (negative) error code otherwise. + */ +int bt_mesh_dfd_srv_oob_store_complete(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, bool success, + size_t size, const uint8_t *metadata, size_t metadata_len); +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ + /** @cond INTERNAL_HIDDEN */ extern const struct bt_mesh_model_op _bt_mesh_dfd_srv_op[]; extern const struct bt_mesh_model_cb _bt_mesh_dfd_srv_cb; diff --git a/include/zephyr/bluetooth/mesh/dfu.h b/include/zephyr/bluetooth/mesh/dfu.h index 91f36cdbaeb445..4745355f0392e1 100644 --- a/include/zephyr/bluetooth/mesh/dfu.h +++ b/include/zephyr/bluetooth/mesh/dfu.h @@ -34,6 +34,10 @@ extern "C" { #define CONFIG_BT_MESH_DFU_URI_MAXLEN 0 #endif +#ifndef CONFIG_BT_MESH_DFU_SLOT_CNT +#define CONFIG_BT_MESH_DFU_SLOT_CNT 0 +#endif + /** DFU transfer phase. */ enum bt_mesh_dfu_phase { /** Ready to start a Receive Firmware procedure. */ @@ -140,10 +144,7 @@ struct bt_mesh_dfu_img { /** Length of the firmware ID. */ size_t fwid_len; - /** Update URI, or NULL. - * - * Must use one of the http: or https: schemes. - */ + /** Update URI, or NULL. */ const char *uri; }; @@ -155,14 +156,10 @@ struct bt_mesh_dfu_slot { size_t fwid_len; /** Length of the metadata. */ size_t metadata_len; - /** Length of the image URI. */ - size_t uri_len; /** Firmware ID. */ uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN]; /** Metadata. */ uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN]; - /** Image URI. */ - char uri[CONFIG_BT_MESH_DFU_URI_MAXLEN]; }; /** @} */ diff --git a/subsys/bluetooth/mesh/Kconfig b/subsys/bluetooth/mesh/Kconfig index 99012e9646a9cc..6bc4c8b5673d54 100644 --- a/subsys/bluetooth/mesh/Kconfig +++ b/subsys/bluetooth/mesh/Kconfig @@ -1235,6 +1235,14 @@ config BT_MESH_DFD_SRV_TARGETS_MAX This value defines the maximum number of Target nodes the Firmware Distribution Server can target simultaneously. +config BT_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 BT_MESH_DFD_SRV_INIT_OOB mandatory. See the API documentation + for bt_mesh_dfd_srv_cb for details about the mandatory callbacks. + endif config BT_MESH_RPR_SRV diff --git a/subsys/bluetooth/mesh/dfd_srv.c b/subsys/bluetooth/mesh/dfd_srv.c index 2898ca39a39f8f..54de343c94a4ec 100644 --- a/subsys/bluetooth/mesh/dfd_srv.c +++ b/subsys/bluetooth/mesh/dfd_srv.c @@ -224,7 +224,20 @@ static int handle_capabilities_get(struct bt_mesh_model *mod, struct bt_mesh_msg size = MIN(size, CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE); net_buf_simple_add_le32(&rsp, CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE - size); - net_buf_simple_add_u8(&rsp, 0U); /* OOB retrieval not supported */ + +#ifdef CONFIG_BT_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 +#else + { + net_buf_simple_add_u8(&rsp, 0); + } +#endif bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL); @@ -346,10 +359,19 @@ static void upload_status_rsp(struct bt_mesh_dfd_srv *srv, return; } - net_buf_simple_add_u8(&rsp, - bt_mesh_blob_srv_progress(&srv->upload.blob)); - net_buf_simple_add_mem(&rsp, srv->upload.slot->fwid, - srv->upload.slot->fwid_len); +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + if (srv->upload.is_oob) { + net_buf_simple_add_u8(&rsp, + srv->cb->oob_progress_get(srv, srv->upload.slot) | 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, bt_mesh_blob_srv_progress(&srv->upload.blob)); + 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); } @@ -364,16 +386,42 @@ static int handle_upload_get(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx * 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); + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + break; + case -EEXIST: /* Img with this fwid already is in list */ + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; + bt_mesh_dfu_slot_release(srv->upload.slot); + upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + break; + case 0: + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE; + break; + case -EINVAL: /* Slot in wrong state. */ + default: + break; + } + + return err; +} + static int handle_upload_start(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 *old_slot = srv->upload.slot; size_t meta_len, fwid_len, size; const uint8_t *meta, *fwid; uint16_t timeout_base; uint64_t blob_id; - int err, idx; + int err; uint8_t ttl; ttl = net_buf_simple_pull_u8(buf); @@ -392,9 +440,7 @@ static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx LOG_DBG("Upload Start: size: %d, fwid: %s, metadata: %s", size, bt_hex(fwid, fwid_len), bt_hex(meta, meta_len)); - if (size > CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE || - fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN || - meta_len > CONFIG_BT_MESH_DFU_METADATA_MAXLEN) { + if (size > CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE) { upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); return 0; @@ -413,7 +459,11 @@ static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx !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) { + srv->upload.blob.state.timeout_base == timeout_base +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + && !srv->upload.is_oob +#endif + ) { LOG_DBG("Duplicate upload start"); upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); return 0; @@ -424,23 +474,16 @@ static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx return 0; } - idx = bt_mesh_dfu_slot_get(fwid, fwid_len, &srv->upload.slot); - if (idx >= 0 && bt_mesh_dfu_slot_is_valid(srv->upload.slot)) { - LOG_DBG("Already received image"); - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; - upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); - return 0; - } + /* This will be a no-op if the slot state isn't RESERVED, which is + * what we want. + */ + bt_mesh_dfu_slot_release(srv->upload.slot); - if (old_slot && !bt_mesh_dfu_slot_is_valid(old_slot)) { - LOG_DBG("Deleting old invalid slot"); - slot_del(srv, old_slot); - } +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + srv->upload.is_oob = false; +#endif + srv->upload.slot = bt_mesh_dfu_slot_reserve(); - /* TODO Store transfer state before slot is added. */ - - srv->upload.slot = bt_mesh_dfu_slot_add(size, fwid, fwid_len, meta, - meta_len, NULL, 0); if (!srv->upload.slot) { LOG_WRN("No space for slot"); upload_status_rsp(srv, ctx, @@ -448,11 +491,27 @@ static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx return 0; } + err = set_upload_fwid(srv, ctx, fwid, fwid_len); + if (err) { + return err; + } + + err = bt_mesh_dfu_slot_info_set(srv->upload.slot, size, meta, meta_len); + switch (err) { + case -EFBIG: + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + break; + case 0: + break; + default: + return err; + } + srv->io = NULL; err = srv->cb->recv(srv, srv->upload.slot, &srv->io); if (err || !srv->io) { LOG_ERR("App rejected upload. err: %d io: %p", err, srv->io); - slot_del(srv, srv->upload.slot); + bt_mesh_dfu_slot_release(srv->upload.slot); upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); return 0; } @@ -461,7 +520,7 @@ static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx timeout_base); if (err) { LOG_ERR("BLOB Server rejected upload (err: %d)", err); - slot_del(srv, srv->upload.slot); + bt_mesh_dfu_slot_release(srv->upload.slot); upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); return 0; } @@ -478,10 +537,71 @@ static int handle_upload_start_oob(struct bt_mesh_model *mod, struct bt_mesh_msg 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; - LOG_DBG(""); + 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); + if (upload_is_busy(srv)) { +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + if (srv->upload.is_oob && + uri_len == srv->upload.oob.uri_len && + fwid_len == srv->upload.oob.current_fwid_len && + !memcmp(uri, srv->upload.oob.uri, uri_len) && + !memcmp(fwid, srv->upload.oob.current_fwid, fwid_len)) { + /* Same image, return SUCCESS for idempotency */ + upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + return 0; + } +#endif + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_BUSY_WITH_UPLOAD); + return 0; + } + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + if (uri_len > CONFIG_BT_MESH_DFU_URI_MAXLEN || + fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN) { + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + return 0; + } + + struct bt_mesh_dfu_slot *slot = bt_mesh_dfu_slot_reserve(); + + if (slot == NULL) { + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); + return 0; + } + + srv->upload.is_oob = true; + srv->upload.slot = slot; + memcpy(srv->upload.oob.uri, uri, uri_len); + srv->upload.oob.uri_len = uri_len; + memcpy(srv->upload.oob.current_fwid, fwid, fwid_len); + srv->upload.oob.current_fwid_len = fwid_len; + memcpy(&srv->upload.oob.ctx, ctx, sizeof(struct bt_mesh_msg_ctx)); + + int status = srv->cb->start_oob_upload(srv, srv->upload.slot, srv->upload.oob.uri, + srv->upload.oob.uri_len, + srv->upload.oob.current_fwid, + srv->upload.oob.current_fwid_len); + + if (status != BT_MESH_DFD_SUCCESS) { + upload_status_rsp(srv, ctx, status); + bt_mesh_dfu_slot_release(srv->upload.slot); + } +#else upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_URI_NOT_SUPPORTED); +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ return 0; } @@ -492,7 +612,14 @@ static int handle_upload_cancel(struct bt_mesh_model *mod, struct bt_mesh_msg_ct struct bt_mesh_dfd_srv *srv = mod->user_data; srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_IDLE; - (void)bt_mesh_blob_srv_cancel(&srv->upload.blob); +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + if (srv->upload.is_oob) { + srv->cb->cancel_oob_upload(srv, srv->upload.slot); + } else +#endif + { + (void)bt_mesh_blob_srv_cancel(&srv->upload.blob); + } upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); return 0; @@ -508,7 +635,7 @@ static void fw_status_rsp(struct bt_mesh_dfd_srv *srv, bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_FW_STATUS); net_buf_simple_add_u8(&rsp, status); - net_buf_simple_add_le16(&rsp, bt_mesh_dfu_slot_foreach(NULL, NULL)); + net_buf_simple_add_le16(&rsp, bt_mesh_dfu_slot_count()); net_buf_simple_add_le16(&rsp, idx); if (fwid) { @@ -522,7 +649,7 @@ static int handle_fw_get(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; + struct bt_mesh_dfu_slot *slot; const uint8_t *fwid; size_t fwid_len; int idx; @@ -531,7 +658,7 @@ static int handle_fw_get(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, fwid = net_buf_simple_pull_mem(buf, fwid_len); idx = bt_mesh_dfu_slot_get(fwid, fwid_len, &slot); - if (idx >= 0 && bt_mesh_dfu_slot_is_valid(slot)) { + if (idx >= 0) { fw_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS, idx, fwid, fwid_len); } else { @@ -552,7 +679,7 @@ static int handle_fw_get_by_index(struct bt_mesh_model *mod, struct bt_mesh_msg_ idx = net_buf_simple_pull_le16(buf); slot = bt_mesh_dfu_slot_at(idx); - if (slot && bt_mesh_dfu_slot_is_valid(slot)) { + if (slot) { fw_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS, idx, slot->fwid, slot->fwid_len); } else { @@ -729,8 +856,7 @@ static void upload_end(struct bt_mesh_blob_srv *b, uint64_t id, bool success) LOG_DBG("%u", success); - if (success) { - bt_mesh_dfu_slot_valid_set(srv->upload.slot, true); + if (success && (bt_mesh_dfu_slot_commit(srv->upload.slot) == 0)) { srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; return; } @@ -850,7 +976,7 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_start(struct bt_mesh_dfd_srv *srv, xfer.mode = params->xfer_mode; xfer.slot = bt_mesh_dfu_slot_at(params->slot_idx); - if (!xfer.slot || !bt_mesh_dfu_slot_is_valid(xfer.slot)) { + if (!xfer.slot) { return BT_MESH_DFD_ERR_FW_NOT_FOUND; } @@ -1013,7 +1139,7 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_apply(struct bt_mesh_dfd_srv *srv) enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete(struct bt_mesh_dfd_srv *srv, size_t *fwid_len, const uint8_t **fwid) { - const struct bt_mesh_dfu_slot *slot; + struct bt_mesh_dfu_slot *slot; int idx, err; if (srv->phase != BT_MESH_DFD_PHASE_IDLE) { @@ -1023,7 +1149,7 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete(struct bt_mesh_dfd_srv *srv, s } idx = bt_mesh_dfu_slot_get(*fwid, *fwid_len, &slot); - if (idx < 0 || !bt_mesh_dfu_slot_is_valid(slot)) { + if (idx < 0) { return BT_MESH_DFD_SUCCESS; } @@ -1049,3 +1175,69 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete_all(struct bt_mesh_dfd_srv *sr return BT_MESH_DFD_SUCCESS; } + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD +int bt_mesh_dfd_srv_oob_check_complete(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, int status, + uint8_t *fwid, size_t fwid_len) +{ + int err; + + if (slot != srv->upload.slot || !srv->upload.is_oob || + srv->upload.phase == BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE) { + /* This should not happen, unless the application calls the function with a + * "wrong" pointer or at a wrong time. + */ + return -EINVAL; + } + + if (status != BT_MESH_DFD_SUCCESS) { + bt_mesh_dfu_slot_release(srv->upload.slot); + upload_status_rsp(srv, &srv->upload.oob.ctx, status); + return -ECANCELED; + } + + err = set_upload_fwid(srv, &srv->upload.oob.ctx, fwid, fwid_len); + + if (err) { + return err; + } + + upload_status_rsp(srv, &srv->upload.oob.ctx, BT_MESH_DFD_SUCCESS); + return 0; +} + +int bt_mesh_dfd_srv_oob_store_complete(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, bool success, + size_t size, const uint8_t *metadata, size_t metadata_len) +{ + int err = 0; + + if (srv->upload.phase != BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE || + srv->upload.slot != slot || !srv->upload.is_oob) { + return -EINVAL; + } + + if (!success) { + goto error; + } + + err = bt_mesh_dfu_slot_info_set(srv->upload.slot, size, metadata, metadata_len); + if (err) { + goto error; + } + + err = bt_mesh_dfu_slot_commit(srv->upload.slot); + if (err) { + goto error; + } + + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; + return 0; + +error: + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR; + bt_mesh_dfu_slot_release(srv->upload.slot); + return err; +} +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ diff --git a/subsys/bluetooth/mesh/dfu_slot.c b/subsys/bluetooth/mesh/dfu_slot.c index 16628793457cd4..097d5abcc82238 100644 --- a/subsys/bluetooth/mesh/dfu_slot.c +++ b/subsys/bluetooth/mesh/dfu_slot.c @@ -20,23 +20,22 @@ LOG_MODULE_REGISTER(bt_mesh_dfu_slot); #define DFU_SLOT_SETTINGS_PATH "bt/mesh-dfu/slot" -#define HEADER_SIZE offsetof(struct bt_mesh_dfu_slot, fwid) +#define HEADER_SIZE offsetof(struct slot, slot.fwid) #define PROP_HEADER "h" #define PROP_FWID "id" #define PROP_METADATA "m" -#define PROP_URI "u" -#define VALID_SLOTS_TAG "v" -#define SLOT_IN_ARRAY(_slot) PART_OF_ARRAY(slots, CONTAINER_OF(_slot, struct slot, slot)) - -static ATOMIC_DEFINE(valid_slots, CONFIG_BT_MESH_DFU_SLOT_CNT); static sys_slist_t list; + static struct slot { - sys_snode_t n; + uint32_t idx; struct bt_mesh_dfu_slot slot; + sys_snode_t n; } slots[CONFIG_BT_MESH_DFU_SLOT_CNT]; +static uint32_t slot_index; + static char *slot_entry_encode(uint16_t idx, char buf[SLOT_ENTRY_BUFLEN], const char *property) { @@ -46,32 +45,29 @@ static char *slot_entry_encode(uint16_t idx, char buf[SLOT_ENTRY_BUFLEN], return buf; } -static inline bool slot_in_use(const struct bt_mesh_dfu_slot *slot) +static bool slot_eq(const struct bt_mesh_dfu_slot *slot, + const uint8_t *fwid, size_t fwid_len) { - return slot->size > 0U; + return (slot->fwid_len == fwid_len) && + !memcmp(fwid, slot->fwid, fwid_len); } -static inline uint16_t slot_idx(const struct bt_mesh_dfu_slot *slot) +static bool is_slot_committed(struct slot *slot_to_check) { - return CONTAINER_OF(slot, struct slot, slot) - &slots[0]; -} + struct slot *s; -static inline void slot_invalidate(struct slot *slot_to_invalidate) -{ - slot_to_invalidate->slot.size = 0U; - atomic_clear_bit(valid_slots, slot_to_invalidate - &slots[0]); -} + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + if (s == slot_to_check) { + return true; + } + } -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); + return false; } static int slot_store(const struct slot *slot_to_store) { - uint16_t idx = slot_to_store - &slots[0]; + uint16_t idx = ARRAY_INDEX(slots, slot_to_store); char buf[SLOT_ENTRY_BUFLEN]; int err; @@ -90,55 +86,51 @@ static int slot_store(const struct slot *slot_to_store) err = settings_save_one(slot_entry_encode(idx, buf, PROP_METADATA), slot_to_store->slot.metadata, slot_to_store->slot.metadata_len); - if (err) { - return err; - } - return settings_save_one(slot_entry_encode(idx, buf, PROP_URI), - slot_to_store->slot.uri, slot_to_store->slot.uri_len); + return err; } static void slot_erase(struct slot *slot_to_erase) { - uint16_t idx = slot_to_erase - &slots[0]; + uint16_t idx = ARRAY_INDEX(slots, slot_to_erase); char buf[SLOT_ENTRY_BUFLEN]; settings_delete(slot_entry_encode(idx, buf, PROP_HEADER)); settings_delete(slot_entry_encode(idx, buf, PROP_FWID)); settings_delete(slot_entry_encode(idx, buf, PROP_METADATA)); - settings_delete(slot_entry_encode(idx, buf, PROP_URI)); } -static int valid_slots_store(void) +static void slot_index_defrag(void) { - return settings_save_one(DFU_SLOT_SETTINGS_PATH "/" VALID_SLOTS_TAG, - valid_slots, sizeof(valid_slots)); + slot_index = 0; + struct slot *s; + + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + s->idx = ++slot_index; + slot_store(s); + } } -const struct bt_mesh_dfu_slot * -bt_mesh_dfu_slot_add(size_t size, const uint8_t *fwid, - size_t fwid_len, const uint8_t *metadata, - size_t metadata_len, const char *uri, size_t uri_len) +int bt_mesh_dfu_slot_count(void) { - struct slot *slot = NULL; - int err, i; + int cnt = 0; + sys_snode_t *n; - if (size == 0 || fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN || - metadata_len > CONFIG_BT_MESH_DFU_METADATA_MAXLEN || - uri_len > CONFIG_BT_MESH_DFU_URI_MAXLEN) { - LOG_WRN("Param too large: (size: %d, fwid: %d, metadata: %d, uri: %d)", - size, fwid_len, metadata_len, uri_len); - return NULL; + SYS_SLIST_FOR_EACH_NODE(&list, n) { + cnt++; } - for (i = 0; i < ARRAY_SIZE(slots); ++i) { - if (!slot_in_use(&slots[i].slot)) { - slot = &slots[i]; - continue; - } + return cnt; +} - if (slot_eq(&slots[i].slot, fwid, fwid_len)) { - return &slots[i].slot; +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; } } @@ -147,110 +139,136 @@ bt_mesh_dfu_slot_add(size_t size, const uint8_t *fwid, return NULL; } - slot->slot.fwid_len = fwid_len; - slot->slot.metadata_len = metadata_len; - slot->slot.uri_len = uri_len; - memcpy(slot->slot.fwid, fwid, fwid_len); - memcpy(slot->slot.metadata, metadata, metadata_len); - memcpy(slot->slot.uri, uri, uri_len); - slot->slot.size = size; - - err = slot_store(slot); - if (err) { - slot_invalidate(slot); - LOG_WRN("Store failed (err: %d)", err); - return NULL; + if (slot_index == UINT32_MAX) { + slot_index_defrag(); } - sys_slist_append(&list, &slot->n); + slot->slot.fwid_len = 0; + slot->slot.metadata_len = 0; + slot->slot.size = 0; + slot->idx = ++slot_index; + + LOG_DBG("Reserved slot #%u", slot - &slots[0]); - LOG_DBG("Added slot #%u: %s", slot - &slots[0], - bt_hex(slot->slot.fwid, slot->slot.fwid_len)); return &slot->slot; } -int bt_mesh_dfu_slot_valid_set(const struct bt_mesh_dfu_slot *slot, bool valid) +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) { - uint16_t idx; - bool prev; - int err; + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); + + if (metadata_len > CONFIG_BT_MESH_DFU_METADATA_MAXLEN) { + return -EFBIG; + } - if (!SLOT_IN_ARRAY(slot) || !slot_in_use(slot)) { - return -ENOENT; + if (slot->idx == 0 || is_slot_committed(slot)) { + return -EINVAL; } - idx = slot_idx(slot); + 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); - LOG_DBG("%u: %u", idx, valid); + if (fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN) { + return -EFBIG; + } - if (valid) { - prev = atomic_test_and_set_bit(valid_slots, idx); - } else { - prev = atomic_test_and_clear_bit(valid_slots, idx); + if (slot->idx == 0 || is_slot_committed(slot)) { + return -EINVAL; } - if (valid == prev) { - return 0; + for (int i = 0; i < ARRAY_SIZE(slots); i++) { + if (slots[i].idx != 0 && + slot_eq(&slots[i].slot, fwid, fwid_len)) { + return is_slot_committed(&slots[i]) ? + -EEXIST : -EALREADY; + } + } + + slot->slot.fwid_len = fwid_len; + memcpy(slot->slot.fwid, fwid, fwid_len); + return 0; +} + +int bt_mesh_dfu_slot_commit(struct bt_mesh_dfu_slot *dfu_slot) +{ + int err; + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); + + if (slot->idx == 0 || + slot->slot.fwid_len == 0 || + slot->slot.size == 0 || + is_slot_committed(slot)) { + return -EINVAL; } - err = valid_slots_store(); + err = slot_store(slot); if (err) { - LOG_WRN("Storage failed. err: %d", err); - atomic_set_bit_to(valid_slots, idx, prev); + LOG_WRN("Store failed (err: %d)", err); + return err; } - return err; + sys_slist_append(&list, &slot->n); + + LOG_DBG("Stored slot #%u: %s", ARRAY_INDEX(slots, slot), + bt_hex(slot->slot.fwid, slot->slot.fwid_len)); + return 0; } -bool bt_mesh_dfu_slot_is_valid(const struct bt_mesh_dfu_slot *slot) +void bt_mesh_dfu_slot_release(const struct bt_mesh_dfu_slot *dfu_slot) { - uint16_t idx; + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); - if (!SLOT_IN_ARRAY(slot) || !slot_in_use(slot)) { - return false; + if (is_slot_committed(slot)) { + return; } - idx = slot_idx(slot); - return atomic_test_bit(valid_slots, idx); + slot->idx = 0; } -int bt_mesh_dfu_slot_del(const struct bt_mesh_dfu_slot *slot) +int bt_mesh_dfu_slot_del(const struct bt_mesh_dfu_slot *dfu_slot) { - struct slot *s = CONTAINER_OF(slot, struct slot, slot); + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); - if (!SLOT_IN_ARRAY(slot) || !slot_in_use(slot)) { - return -ENOENT; + if (!sys_slist_find_and_remove(&list, &slot->n)) { + return -EINVAL; } - LOG_DBG("%u", s - &slots[0]); + int idx = ARRAY_INDEX(slots, slot); - slot_erase(s); - slot_invalidate(s); - sys_slist_find_and_remove(&list, &s->n); + LOG_DBG("%u", idx); + + slot_erase(slot); + slot->idx = 0; return 0; } -int bt_mesh_dfu_slot_del_all(void) +void bt_mesh_dfu_slot_del_all(void) { struct slot *s; - SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { slot_erase(s); - slot_invalidate(s); + s->idx = 0; } sys_slist_init(&list); - - return 0; } -const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t idx) +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 (!idx--) { + if (!img_idx--) { return &s->slot; } } @@ -258,34 +276,33 @@ const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t idx) return NULL; } -int bt_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len, - const struct bt_mesh_dfu_slot **slot) +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)) { - *slot = &s->slot; + if (slot) { + *slot = &s->slot; + } return idx; } - idx++; } return -ENOENT; } -int bt_mesh_dfu_slot_idx_get(const struct bt_mesh_dfu_slot *slot) +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 == slot) { + if (&s->slot == dfu_slot) { return idx; } - idx++; } @@ -295,11 +312,12 @@ int bt_mesh_dfu_slot_idx_get(const struct bt_mesh_dfu_slot *slot) size_t bt_mesh_dfu_slot_foreach(bt_mesh_dfu_slot_cb_t cb, void *user_data) { enum bt_mesh_dfu_iter iter; - struct slot *s; size_t cnt = 0; + struct slot *s; SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { cnt++; + if (!cb) { continue; } @@ -320,15 +338,6 @@ static int slot_data_load(const char *key, size_t len_rd, size_t len; uint16_t idx; - if (!strncmp(key, VALID_SLOTS_TAG, 1)) { - if (read_cb(cb_arg, valid_slots, - MIN(sizeof(valid_slots), len_rd)) < 0) { - return -EINVAL; - } - - return 0; - } - idx = strtol(key, NULL, 16); if (idx >= ARRAY_SIZE(slots)) { @@ -339,16 +348,34 @@ static int slot_data_load(const char *key, size_t len_rd, if (!strncmp(prop, PROP_HEADER, len)) { if (read_cb(cb_arg, &slots[idx], HEADER_SIZE) > 0) { - sys_slist_append(&list, &slots[idx].n); - } + 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) { - slot_invalidate(&slots[idx]); + slots[idx].idx = 0; + sys_slist_find_and_remove(&list, &slots[idx].n); return 0; } @@ -359,7 +386,8 @@ static int slot_data_load(const char *key, size_t len_rd, if (!strncmp(prop, PROP_METADATA, len)) { if (read_cb(cb_arg, &slots[idx].slot.metadata, sizeof(slots[idx].slot.metadata)) < 0) { - slot_invalidate(&slots[idx]); + slots[idx].idx = 0; + sys_slist_find_and_remove(&list, &slots[idx].n); return 0; } @@ -367,16 +395,6 @@ static int slot_data_load(const char *key, size_t len_rd, return 0; } - if (!strncmp(prop, PROP_URI, len)) { - if (read_cb(cb_arg, &slots[idx].slot.uri, - sizeof(slots[idx].slot.uri)) < 0) { - slot_invalidate(&slots[idx]); - return 0; - } - - slots[idx].slot.uri_len = len_rd; - } - return 0; } diff --git a/subsys/bluetooth/mesh/dfu_slot.h b/subsys/bluetooth/mesh/dfu_slot.h index 1bc58f64de961a..969c6d3d25b453 100644 --- a/subsys/bluetooth/mesh/dfu_slot.h +++ b/subsys/bluetooth/mesh/dfu_slot.h @@ -16,50 +16,69 @@ typedef enum bt_mesh_dfu_iter (*bt_mesh_dfu_slot_cb_t)( const struct bt_mesh_dfu_slot *slot, void *user_data); -/** @brief Register a new DFU image slot for a distributable image. +/** @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. + * 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. * - * @note The slot is allocated as invalid. Call - * @ref bt_mesh_dfu_slot_valid_set to make it valid. + * @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 size Size of the image in bytes. - * @param fwid Firmware ID. - * @param fwid_len Length of the firmware ID, at most @c - * CONFIG_BT_MESH_DFU_FWID_MAXLEN. + * @param dfu_slot Pointer to the reserved slot for which to set the + * metadata. + * @param size The size of the image. * @param metadata Metadata or NULL. * @param metadata_len Length of the metadata, at most @c * CONFIG_BT_MESH_DFU_METADATA_MAXLEN. - * @param uri Image URI or NULL. - * @param uri_len Length of the image URI, at most @c - * CONFIG_BT_MESH_DFU_URI_MAXLEN. * - * @return A pointer to the allocated slot, or NULL if allocation failed. + * @return 0 on success, (negative) error code otherwise. */ -const struct bt_mesh_dfu_slot * -bt_mesh_dfu_slot_add(size_t size, const uint8_t *fwid, size_t fwid_len, - const uint8_t *metadata, size_t metadata_len, - const char *uri, size_t uri_len); +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 whether the given slot is valid. +/** @brief Set the new fwid for the incoming image for a reserved slot. * - * @param slot Allocated DFU image slot. - * @param valid New valid state of the slot. + * @param dfu_slot Pointer to the reserved slot for which to set the fwid. + * @param fwid Fwid to set. + * @param fwid_len Length of the fwid, at most @c + * CONFIG_BT_MESH_DFU_FWID_MAXLEN. * - * @return 0 on success, or (negative) error code on failure. + * @return 0 on success, (negative) error code otherwise. */ -int bt_mesh_dfu_slot_valid_set(const struct bt_mesh_dfu_slot *slot, bool valid); +int bt_mesh_dfu_slot_fwid_set(struct bt_mesh_dfu_slot *dfu_slot, + const uint8_t *fwid, size_t fwid_len); -/** @brief Check whether a slot is valid. +/** @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. * - * @param slot Slot to check. + * @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. * - * @return true if the slot is valid, false otherwise. + * @param dfu_slot Pointer to the reserved slot. */ -bool bt_mesh_dfu_slot_is_valid(const struct bt_mesh_dfu_slot *slot); +void bt_mesh_dfu_slot_release(const struct bt_mesh_dfu_slot *dfu_slot); -/** @brief Delete an allocated DFU image slot. +/** @brief Delete a committed DFU image slot. * * @param slot Slot to delete. Must be a valid pointer acquired from this * module. @@ -72,18 +91,19 @@ int bt_mesh_dfu_slot_del(const struct bt_mesh_dfu_slot *slot); * * @return 0 on success, or (negative) error code on failure. */ -int bt_mesh_dfu_slot_del_all(void); +void bt_mesh_dfu_slot_del_all(void); -/** @brief Get the DFU image slot at the given index. +/** @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 idx); +const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t img_idx); -/** @brief Get the DFU image slot for the image with the given firmware ID. +/** @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. @@ -91,16 +111,15 @@ const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t idx); * * @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, - const struct bt_mesh_dfu_slot **slot); +int bt_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len, struct bt_mesh_dfu_slot **slot); -/** @brief Get the DFU image slot index of the given 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_idx_get(const struct bt_mesh_dfu_slot *slot); +int bt_mesh_dfu_slot_img_idx_get(const struct bt_mesh_dfu_slot *slot); /** @brief Iterate through all DFU image slots. * diff --git a/subsys/bluetooth/mesh/shell/dfd.c b/subsys/bluetooth/mesh/shell/dfd.c index 94d656f3689e50..b7daf42af1cfe2 100644 --- a/subsys/bluetooth/mesh/shell/dfd.c +++ b/subsys/bluetooth/mesh/shell/dfd.c @@ -43,7 +43,7 @@ static void print_fw_status(const struct shell *sh, enum bt_mesh_dfd_status stat uint16_t idx, const uint8_t *fwid, size_t fwid_len) { shell_fprintf(sh, SHELL_NORMAL, "{ \"status\": %d, \"slot_cnt\": %d, \"idx\": %d", - status, bt_mesh_dfu_slot_foreach(NULL, NULL), idx); + status, bt_mesh_dfu_slot_count(), idx); if (fwid) { shell_fprintf(sh, SHELL_NORMAL, ", \"fwid\": \""); for (size_t i = 0; i < fwid_len; i++) { @@ -325,10 +325,9 @@ static int cmd_dfd_fw_get(const struct shell *sh, size_t argc, char *argv[]) return -EINVAL; } - const struct bt_mesh_dfu_slot *slot; - int idx = bt_mesh_dfu_slot_get(fwid, fwid_len, &slot); + int idx = bt_mesh_dfu_slot_get(fwid, fwid_len, NULL); - if (idx >= 0 && bt_mesh_dfu_slot_is_valid(slot)) { + if (idx >= 0) { print_fw_status(sh, BT_MESH_DFD_SUCCESS, idx, fwid, fwid_len); } else { print_fw_status(sh, BT_MESH_DFD_ERR_FW_NOT_FOUND, 0xffff, fwid, fwid_len); @@ -349,7 +348,7 @@ static int cmd_dfd_fw_get_by_idx(const struct shell *sh, size_t argc, char *argv return err; } - if (slot && bt_mesh_dfu_slot_is_valid(slot)) { + if (slot) { print_fw_status(sh, BT_MESH_DFD_SUCCESS, idx, slot->fwid, slot->fwid_len); } else { print_fw_status(sh, BT_MESH_DFD_ERR_FW_NOT_FOUND, idx, NULL, 0); diff --git a/subsys/bluetooth/mesh/shell/dfu.c b/subsys/bluetooth/mesh/shell/dfu.c index 71d27b158e416b..8d7fc96e0141af 100644 --- a/subsys/bluetooth/mesh/shell/dfu.c +++ b/subsys/bluetooth/mesh/shell/dfu.c @@ -375,13 +375,12 @@ static int cmd_dfu_metadata_encode(const struct shell *sh, size_t argc, char *ar static int cmd_dfu_slot_add(const struct shell *sh, size_t argc, char *argv[]) { - const struct bt_mesh_dfu_slot *slot; + struct bt_mesh_dfu_slot *slot; size_t size; uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN]; size_t fwid_len = 0; uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN]; size_t metadata_len = 0; - const char *uri = ""; int err = 0; size = shell_strtoul(argv[1], 0, &err); @@ -390,32 +389,33 @@ static int cmd_dfu_slot_add(const struct shell *sh, size_t argc, char *argv[]) return err; } - if (argc > 2) { - fwid_len = hex2bin(argv[2], strlen(argv[2]), fwid, - sizeof(fwid)); + shell_print(sh, "Adding slot (size: %u)", size); + slot = bt_mesh_dfu_slot_reserve(); + + if (!slot) { + shell_print(sh, "Failed to reserve slot."); + return 0; } + fwid_len = hex2bin(argv[2], strlen(argv[2]), fwid, + sizeof(fwid)); + bt_mesh_dfu_slot_fwid_set(slot, fwid, fwid_len); + if (argc > 3) { metadata_len = hex2bin(argv[3], strlen(argv[3]), metadata, sizeof(metadata)); } - if (argc > 4) { - uri = argv[4]; - } + bt_mesh_dfu_slot_info_set(slot, size, metadata, metadata_len); - shell_print(sh, "Adding slot (size: %u)", size); - - slot = bt_mesh_dfu_slot_add(size, fwid, fwid_len, metadata, - metadata_len, uri, strlen(uri)); - if (!slot) { - shell_print(sh, "Failed."); - return 0; + err = bt_mesh_dfu_slot_commit(slot); + if (err) { + shell_print(sh, "Failed to commit slot: %d", err); + bt_mesh_dfu_slot_release(slot); + return err; } - bt_mesh_dfu_slot_valid_set(slot, true); - - shell_print(sh, "Slot added. ID: %u", bt_mesh_dfu_slot_idx_get(slot)); + shell_print(sh, "Slot added. Index: %u", bt_mesh_dfu_slot_img_idx_get(slot)); return 0; } @@ -451,14 +451,7 @@ static int cmd_dfu_slot_del(const struct shell *sh, size_t argc, char *argv[]) static int cmd_dfu_slot_del_all(const struct shell *sh, size_t argc, char *argv[]) { - int err; - - err = bt_mesh_dfu_slot_del_all(); - if (err) { - shell_print(sh, "Failed deleting all slots (err: %d)", err); - return 0; - } - + bt_mesh_dfu_slot_del_all(); shell_print(sh, "All slots deleted."); return 0; } @@ -468,7 +461,6 @@ static void slot_info_print(const struct shell *sh, const struct bt_mesh_dfu_slo { char fwid[2 * CONFIG_BT_MESH_DFU_FWID_MAXLEN + 1]; char metadata[2 * CONFIG_BT_MESH_DFU_METADATA_MAXLEN + 1]; - char uri[CONFIG_BT_MESH_DFU_URI_MAXLEN + 1]; size_t len; len = bin2hex(slot->fwid, slot->fwid_len, fwid, sizeof(fwid)); @@ -476,8 +468,6 @@ static void slot_info_print(const struct shell *sh, const struct bt_mesh_dfu_slo len = bin2hex(slot->metadata, slot->metadata_len, metadata, sizeof(metadata)); metadata[len] = '\0'; - memcpy(uri, slot->uri, slot->uri_len); - uri[slot->uri_len] = '\0'; if (idx != NULL) { shell_print(sh, "Slot %u:", *idx); @@ -487,7 +477,6 @@ static void slot_info_print(const struct shell *sh, const struct bt_mesh_dfu_slo shell_print(sh, "\tSize: %u bytes", slot->size); shell_print(sh, "\tFWID: %s", fwid); shell_print(sh, "\tMetadata: %s", metadata); - shell_print(sh, "\tURI: %s", uri); } static int cmd_dfu_slot_get(const struct shell *sh, size_t argc, char *argv[]) @@ -970,8 +959,8 @@ SHELL_STATIC_SUBCMD_SET_CREATE( SHELL_STATIC_SUBCMD_SET_CREATE( dfu_slot_cmds, SHELL_CMD_ARG(add, NULL, - " [ [ []]]", - cmd_dfu_slot_add, 2, 3), + " []", + cmd_dfu_slot_add, 3, 1), SHELL_CMD_ARG(del, NULL, "", cmd_dfu_slot_del, 2, 0), SHELL_CMD_ARG(del-all, NULL, NULL, cmd_dfu_slot_del_all, 1, 0), SHELL_CMD_ARG(get, NULL, "", cmd_dfu_slot_get, 2, 0), diff --git a/tests/bsim/bluetooth/mesh/prj_mesh1d1.conf b/tests/bsim/bluetooth/mesh/prj_mesh1d1.conf index 69c1055999f4d3..15df7b8a5d3732 100644 --- a/tests/bsim/bluetooth/mesh/prj_mesh1d1.conf +++ b/tests/bsim/bluetooth/mesh/prj_mesh1d1.conf @@ -58,7 +58,8 @@ CONFIG_BT_MESH_LARGE_COMP_DATA_SRV=y CONFIG_BT_MESH_DFU_SRV=y CONFIG_BT_MESH_DFU_CLI=y CONFIG_BT_MESH_DFD_SRV=y -CONFIG_BT_MESH_DFU_SLOT_CNT=3 +CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD=y +CONFIG_BT_MESH_DFU_SLOT_CNT=4 CONFIG_BT_MESH_PRIV_BEACON_SRV=y CONFIG_BT_MESH_PRIV_BEACON_CLI=y CONFIG_BT_MESH_OD_PRIV_PROXY_SRV=y diff --git a/tests/bsim/bluetooth/mesh/src/test_dfu.c b/tests/bsim/bluetooth/mesh/src/test_dfu.c index f7c4ca6f4f0bf4..a235e86eeec2b8 100644 --- a/tests/bsim/bluetooth/mesh/src/test_dfu.c +++ b/tests/bsim/bluetooth/mesh/src/test_dfu.c @@ -434,25 +434,50 @@ static void target_prov_and_conf_default(void) target_prov_and_conf(addr, bind_params, ARRAY_SIZE(bind_params)); } +static struct bt_mesh_dfu_slot *slot_reserve_and_set(size_t size, uint8_t *fwid, size_t fwid_len, + uint8_t *metadata, size_t metadata_len) +{ + struct bt_mesh_dfu_slot *new_slot = bt_mesh_dfu_slot_reserve(); + + if (!new_slot) { + LOG_WRN("Reserving slot failed"); + return NULL; + } + + int err = bt_mesh_dfu_slot_fwid_set(new_slot, fwid, fwid_len); + + if (err) { + return NULL; + } + + err = bt_mesh_dfu_slot_info_set(new_slot, size, metadata, metadata_len); + + if (err) { + return NULL; + } + + return new_slot; +} + static bool slot_add(const struct bt_mesh_dfu_slot **slot) { - const struct bt_mesh_dfu_slot *new_slot; + struct bt_mesh_dfu_slot *new_slot; size_t size = 100; uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN] = { 0xAA, 0xBB, 0xCC, 0xDD }; size_t fwid_len = 4; uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN] = { 0xAA, 0xBB, 0xCC, 0xDD }; size_t metadata_len = 4; - const char *uri = ""; ASSERT_EQUAL(sizeof(target_fw_ver_new), fwid_len); - new_slot = bt_mesh_dfu_slot_add(size, fwid, fwid_len, metadata, metadata_len, uri, - strlen(uri)); + new_slot = slot_reserve_and_set(size, fwid, fwid_len, metadata, metadata_len); if (!new_slot) { return false; } - bt_mesh_dfu_slot_valid_set(new_slot, true); + if (bt_mesh_dfu_slot_commit(new_slot) != 0) { + return false; + } if (slot) { *slot = new_slot; @@ -568,13 +593,12 @@ static void test_dist_dfu_self_update(void) static void test_dist_dfu_slot_create(void) { - const struct bt_mesh_dfu_slot *slot[3]; + struct bt_mesh_dfu_slot *slot[CONFIG_BT_MESH_DFU_SLOT_CNT]; size_t size = 100; uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN] = { 0 }; size_t fwid_len = 4; uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN] = { 0 }; size_t metadata_len = 4; - const char *uri = "test"; int err, i; ASSERT_TRUE(CONFIG_BT_MESH_DFU_SLOT_CNT >= 3, @@ -584,36 +608,26 @@ static void test_dist_dfu_slot_create(void) bt_mesh_device_setup(&prov, &dist_comp); dist_prov_and_conf(DIST_ADDR); - for (i = CONFIG_BT_MESH_DFU_SLOT_CNT - 1; i >= 0; i--) { + for (i = 0; i < CONFIG_BT_MESH_DFU_SLOT_CNT; i++) { fwid[0] = i; metadata[0] = i; - slot[i] = bt_mesh_dfu_slot_add(size, fwid, fwid_len, metadata, metadata_len, uri, - strlen(uri)); + slot[i] = slot_reserve_and_set(size, fwid, fwid_len, metadata, metadata_len); ASSERT_FALSE(slot[i] == NULL, "Failed to add slot"); - } - - /* First slot is set as valid */ - err = bt_mesh_dfu_slot_valid_set(slot[0], true); - if (err) { - FAIL("Setting slot to valid state failed (err %d)", err); - return; - } - ASSERT_TRUE(bt_mesh_dfu_slot_is_valid(slot[0])); - /* Second slot is set as invalid */ - err = bt_mesh_dfu_slot_valid_set(slot[1], false); - if (err) { - FAIL("Setting slot to invalid state failed (err %d)", err); - return; + if (i > 0) { + /* All but first slot are committed */ + err = bt_mesh_dfu_slot_commit(slot[i]); + if (err) { + FAIL("Committing slot failed (err %d)", err); + } + } } - ASSERT_TRUE(!bt_mesh_dfu_slot_is_valid(slot[1])); - /* Last slot is deleted */ - err = bt_mesh_dfu_slot_del(slot[CONFIG_BT_MESH_DFU_SLOT_CNT - 1]); + /* Second slot is deleted */ + err = bt_mesh_dfu_slot_del(slot[1]); if (err) { FAIL("Slot delete failed (err %d)", err); - return; } PASS(); @@ -626,20 +640,17 @@ enum bt_mesh_dfu_iter check_slot(const struct bt_mesh_dfu_slot *slot, void *data size_t fwid_len = 4; uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN] = { 0 }; size_t metadata_len = 4; - const char *uri = "test"; - int idx = bt_mesh_dfu_slot_idx_get(slot); - - ASSERT_TRUE(idx >= 0, "Failed to retrieve slot index"); + int idx = bt_mesh_dfu_slot_img_idx_get(slot); + int *i = data; + ASSERT_EQUAL(idx, (*i)++); ASSERT_EQUAL(size, slot->size); - ASSERT_TRUE(strcmp(uri, slot->uri) == 0); - - fwid[0] = idx; + fwid[0] = idx + 2; ASSERT_EQUAL(fwid_len, slot->fwid_len); ASSERT_TRUE(memcmp(fwid, slot->fwid, fwid_len) == 0); - metadata[0] = idx; + metadata[0] = idx + 2; ASSERT_EQUAL(metadata_len, slot->metadata_len); ASSERT_TRUE(memcmp(metadata, slot->metadata, metadata_len) == 0); @@ -649,13 +660,12 @@ enum bt_mesh_dfu_iter check_slot(const struct bt_mesh_dfu_slot *slot, void *data static void test_dist_dfu_slot_create_recover(void) { size_t slot_count; - const struct bt_mesh_dfu_slot *slot; + struct bt_mesh_dfu_slot *slot; size_t size = 100; uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN] = { 0 }; size_t fwid_len = 4; uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN] = { 0 }; size_t metadata_len = 4; - const char *uri = "test"; int i, idx; ASSERT_TRUE(CONFIG_BT_MESH_DFU_SLOT_CNT >= 3, @@ -664,26 +674,17 @@ static void test_dist_dfu_slot_create_recover(void) bt_mesh_test_cfg_set(NULL, WAIT_TIME); bt_mesh_device_setup(&prov, &dist_comp); - slot_count = bt_mesh_dfu_slot_foreach(check_slot, NULL); - ASSERT_EQUAL(CONFIG_BT_MESH_DFU_SLOT_CNT - 1, slot_count); - - slot = bt_mesh_dfu_slot_at(0); - ASSERT_EQUAL(true, bt_mesh_dfu_slot_is_valid(slot)); + i = 0; + slot_count = bt_mesh_dfu_slot_foreach(check_slot, &i); + ASSERT_EQUAL(CONFIG_BT_MESH_DFU_SLOT_CNT - 2, slot_count); - slot = bt_mesh_dfu_slot_at(1); - ASSERT_TRUE(slot != NULL); - ASSERT_EQUAL(false, bt_mesh_dfu_slot_is_valid(slot)); - - for (i = 0; i < (CONFIG_BT_MESH_DFU_SLOT_CNT - 1); i++) { + for (i = 2; i < CONFIG_BT_MESH_DFU_SLOT_CNT; i++) { fwid[0] = i; idx = bt_mesh_dfu_slot_get(fwid, fwid_len, &slot); - ASSERT_TRUE(idx >= 0); - ASSERT_EQUAL(idx, bt_mesh_dfu_slot_idx_get(slot)); - + ASSERT_EQUAL(idx, i - 2); ASSERT_EQUAL(size, slot->size); - ASSERT_TRUE(strcmp(uri, slot->uri) == 0); - metadata[0] = idx; + metadata[0] = i; ASSERT_EQUAL(metadata_len, slot->metadata_len); ASSERT_TRUE(memcmp(metadata, slot->metadata, metadata_len) == 0); } @@ -693,7 +694,7 @@ static void test_dist_dfu_slot_create_recover(void) static void check_delete_all(void) { - int i, idx, err; + int i; const struct bt_mesh_dfu_slot *slot; size_t slot_count; @@ -706,14 +707,6 @@ static void check_delete_all(void) for (i = 0; i < CONFIG_BT_MESH_DFU_SLOT_CNT - 1; i++) { slot = bt_mesh_dfu_slot_at(i); ASSERT_TRUE(slot == NULL); - - idx = bt_mesh_dfu_slot_idx_get(slot); - ASSERT_TRUE(idx < 0); - - err = bt_mesh_dfu_slot_valid_set(slot, true); - ASSERT_EQUAL(err, -ENOENT); - - ASSERT_TRUE(!bt_mesh_dfu_slot_is_valid(slot)); } } @@ -726,7 +719,6 @@ static void test_dist_dfu_slot_delete_all(void) bt_mesh_device_setup(&prov, &dist_comp); bt_mesh_dfu_slot_del_all(); - check_delete_all(); PASS(); @@ -742,6 +734,63 @@ static void test_dist_dfu_slot_check_delete_all(void) PASS(); } +static void test_dist_dfu_slot_reservation(void) +{ + int i; + struct bt_mesh_dfu_slot *slots[CONFIG_BT_MESH_DFU_SLOT_CNT]; + + bt_mesh_test_cfg_set(NULL, WAIT_TIME); + bt_mesh_device_setup(&prov, &dist_comp); + + for (i = 0; i < CONFIG_BT_MESH_DFU_SLOT_CNT; i++) { + slots[i] = bt_mesh_dfu_slot_reserve(); + ASSERT_TRUE(slots[i] != NULL); + } + + ASSERT_EQUAL(NULL, bt_mesh_dfu_slot_reserve()); + bt_mesh_dfu_slot_release(slots[0]); + /* Release twice to check idempotency with empty pool */ + bt_mesh_dfu_slot_release(slots[0]); + ASSERT_TRUE(bt_mesh_dfu_slot_reserve() != NULL); + ASSERT_EQUAL(NULL, bt_mesh_dfu_slot_reserve()); + + PASS(); +} + +static void test_dist_dfu_slot_idempotency(void) +{ + uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN] = { 0 }; + size_t fwid_len = 4; + struct bt_mesh_dfu_slot *slot; + + ASSERT_TRUE(CONFIG_BT_MESH_DFU_SLOT_CNT >= 1, + "CONFIG_BT_MESH_DFU_SLOT_CNT must be at least 1"); + + bt_mesh_test_cfg_set(NULL, WAIT_TIME); + bt_mesh_device_setup(&prov, &dist_comp); + dist_prov_and_conf(DIST_ADDR); + + slot = bt_mesh_dfu_slot_reserve(); + ASSERT_TRUE(slot != NULL); + + bt_mesh_dfu_slot_release(slot); + bt_mesh_dfu_slot_release(slot); + + slot = bt_mesh_dfu_slot_reserve(); + ASSERT_TRUE(slot != NULL); + + ASSERT_EQUAL(0, bt_mesh_dfu_slot_fwid_set(slot, fwid, fwid_len)); + ASSERT_EQUAL(0, bt_mesh_dfu_slot_info_set(slot, 100, NULL, 0)); + + ASSERT_EQUAL(0, bt_mesh_dfu_slot_commit(slot)); + ASSERT_EQUAL(-EINVAL, bt_mesh_dfu_slot_commit(slot)); + + ASSERT_EQUAL(0, bt_mesh_dfu_slot_del(slot)); + ASSERT_EQUAL(-EINVAL, bt_mesh_dfu_slot_del(slot)); + + PASS(); +} + static void target_test_effect(enum bt_mesh_dfu_effect effect) { dfu_target_effect = effect; @@ -921,12 +970,14 @@ static void cli_common_fail_on_init(void) static void cli_common_init_recover(void) { - const struct bt_mesh_dfu_slot *slot; + struct bt_mesh_dfu_slot *slot; + uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN] = { 0xAA, 0xBB, 0xCC, 0xDD }; + size_t fwid_len = 4; bt_mesh_test_cfg_set(NULL, 300); bt_mesh_device_setup(&prov, &cli_comp); - ASSERT_TRUE(slot_add(&slot)); + ASSERT_TRUE(bt_mesh_dfu_slot_get(fwid, fwid_len, &slot) >= 0); dfu_cli_inputs_prepare(0); dfu_cli_xfer.xfer.mode = BT_MESH_BLOB_XFER_MODE_PUSH; @@ -1595,6 +1646,10 @@ static const struct bst_test_instance test_dfu[] = { TEST_CASE(dist, dfu_slot_delete_all, "Distributor deletes all image slots"), TEST_CASE(dist, dfu_slot_check_delete_all, "Distributor checks if all slots are removed from persistent storage"), + TEST_CASE(dist, dfu_slot_reservation, + "Distributor checks that the correct number of slots can be reserved"), + TEST_CASE(dist, dfu_slot_idempotency, + "Distributor checks that the the DFU slot APIs are idempotent"), TEST_CASE(cli, stop, "DFU Client stops at configured point of Firmware Distribution"), TEST_CASE(cli, fail_on_persistency, "DFU Client doesn't give up DFU Transfer"), TEST_CASE(cli, all_targets_lost_on_metadata, diff --git a/tests/bsim/bluetooth/mesh/tests_scripts/dfu/dfu_slot_idempotency.sh b/tests/bsim/bluetooth/mesh/tests_scripts/dfu/dfu_slot_idempotency.sh new file mode 100755 index 00000000000000..3f18f1a26514b7 --- /dev/null +++ b/tests/bsim/bluetooth/mesh/tests_scripts/dfu/dfu_slot_idempotency.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# Copyright 2023 Nordic Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +source $(dirname "${BASH_SOURCE[0]}")/../../_mesh_test.sh + +# Test DFU Slot API. This test tests that the APIs are idempotent. +conf=prj_mesh1d1_conf +overlay=overlay_pst_conf +RunTest dfu_slot_idempotency dfu_dist_dfu_slot_idempotency + +conf=prj_mesh1d1_conf +overlay="overlay_pst_conf_overlay_psa_conf" +RunTest dfu_slot_idempotency_psa dfu_dist_dfu_slot_idempotency diff --git a/tests/bsim/bluetooth/mesh/tests_scripts/dfu/dfu_slot_reservation.sh b/tests/bsim/bluetooth/mesh/tests_scripts/dfu/dfu_slot_reservation.sh new file mode 100755 index 00000000000000..ddd7d0123f506b --- /dev/null +++ b/tests/bsim/bluetooth/mesh/tests_scripts/dfu/dfu_slot_reservation.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# Copyright 2023 Nordic Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +source $(dirname "${BASH_SOURCE[0]}")/../../_mesh_test.sh + +# Test DFU Slot API. This test tests slot reservation APIs. +conf=prj_mesh1d1_conf +overlay=overlay_pst_conf +RunTest dfu_slot_reservation dfu_dist_dfu_slot_reservation + +conf=prj_mesh1d1_conf +overlay="overlay_pst_conf_overlay_psa_conf" +RunTest dfu_slot_reservation_psa dfu_dist_dfu_slot_reservation