From 702ec88e3c9da13932b6780ab0ef3624408bc344 Mon Sep 17 00:00:00 2001 From: Ludvig Samuelsen Jordet Date: Wed, 9 Aug 2023 09:30:29 +0200 Subject: [PATCH] Bluetooth: Mesh: Add support for Upload OOB Start This adds support for the Upload OOB Start message to the DFD server, by providing callbacks that the application can use to hook any OOB scheme into the model behavior. There are also extensive changes to the dfu_slot module, to accomodate the new needs that appeared with the support for OOB transfer (mainly, fwid, size and metadata are no longer available when the slot is allocated, they appear later in the handling). Signed-off-by: Ludvig Samuelsen Jordet --- include/zephyr/bluetooth/mesh/dfd_srv.h | 131 +++++++- include/zephyr/bluetooth/mesh/dfu.h | 13 +- subsys/bluetooth/mesh/dfd_srv.c | 231 ++++++++++++-- subsys/bluetooth/mesh/dfu_slot.c | 298 +++++++++--------- subsys/bluetooth/mesh/dfu_slot.h | 76 +++-- subsys/bluetooth/mesh/shell/dfd.c | 11 +- subsys/bluetooth/mesh/shell/dfu.c | 53 ++-- tests/bsim/bluetooth/mesh/prj_mesh1d1.conf | 2 +- tests/bsim/bluetooth/mesh/src/test_dfu.c | 147 +++++---- .../mesh/tests_scripts/dfu/dfu_slot.sh | 3 + 10 files changed, 646 insertions(+), 319 deletions(-) diff --git a/include/zephyr/bluetooth/mesh/dfd_srv.h b/include/zephyr/bluetooth/mesh/dfd_srv.h index 0e281b29186d5a9..8a2f8a946bb0b6c 100644 --- a/include/zephyr/bluetooth/mesh/dfd_srv.h +++ b/include/zephyr/bluetooth/mesh/dfd_srv.h @@ -29,19 +29,31 @@ 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; /** * * @brief Initialization parameters for the @ref bt_mesh_dfd_srv. */ -#define BT_MESH_DFD_SRV_INIT(_cb) \ +#define BT_MESH_DFD_SRV_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, \ + }, \ } /** @@ -75,6 +87,60 @@ struct bt_mesh_dfd_srv_cb { const struct bt_mesh_dfu_slot *slot, const struct bt_mesh_blob_io **io); + /** @brief Firware 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 + * @c 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 + * @c bt_mesh_dfd_srv_oob_store_complete. + * + * @param srv Firmware Distribution Server model instance. + * @param slot Slot 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. + * + * @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. + * + * @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); + /** @brief Slot delete callback. * * Called when the Firmware Distribution Server is about to delete a DFU image slot. @@ -128,13 +194,74 @@ struct bt_mesh_dfd_srv { struct bt_mesh_blob_cli_inputs inputs; struct { + bool is_oob; 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; + 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; } upload; + + struct { + uint8_t *schemes; + uint8_t count; + } oob_schemes; }; +/** @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 + * 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 by the @c start_oob + * callback has completed 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); + /** @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 91f36cdbaeb445e..4745355f0392e1e 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/dfd_srv.c b/subsys/bluetooth/mesh/dfd_srv.c index 2898ca39a39f8f4..bb298c250444335 100644 --- a/subsys/bluetooth/mesh/dfd_srv.c +++ b/subsys/bluetooth/mesh/dfd_srv.c @@ -77,6 +77,15 @@ static struct bt_mesh_dfu_target *target_get(struct bt_mesh_dfd_srv *srv, return NULL; } +static bool is_oob_supported(const struct bt_mesh_dfd_srv *srv) +{ + return srv->cb && + srv->cb->start_oob_upload && + srv->cb->cancel_oob_upload && + srv->cb->oob_progress_get && + srv->oob_schemes.count > 0; +} + static bool is_busy(const struct bt_mesh_dfd_srv *srv) { return srv->phase == BT_MESH_DFD_PHASE_TRANSFER_ACTIVE || @@ -210,6 +219,7 @@ static int handle_capabilities_get(struct bt_mesh_model *mod, struct bt_mesh_msg struct net_buf_simple *buf) { size_t size = 0; + struct bt_mesh_dfd_srv *srv = mod->user_data; BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_CAPABILITIES_STATUS, 17); bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_CAPABILITIES_STATUS); @@ -224,7 +234,15 @@ 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 */ + + if (is_oob_supported(srv)) { + net_buf_simple_add_u8(&rsp, 1); + net_buf_simple_add_mem(&rsp, srv->oob_schemes.schemes, + srv->oob_schemes.count); + } else { + net_buf_simple_add_u8(&rsp, 0); + } + bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL); @@ -346,8 +364,15 @@ 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)); + uint8_t progress_and_mode; + + if (srv->upload.is_oob) { + progress_and_mode = srv->cb->oob_progress_get(srv, srv->upload.slot) | BIT(7); + } else { + progress_and_mode = bt_mesh_blob_srv_progress(&srv->upload.blob); + } + + net_buf_simple_add_u8(&rsp, progress_and_mode); net_buf_simple_add_mem(&rsp, srv->upload.slot->fwid, srv->upload.slot->fwid_len); @@ -368,7 +393,6 @@ static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_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; @@ -413,7 +437,8 @@ 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 && + !srv->upload.is_oob) { LOG_DBG("Duplicate upload start"); upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); return 0; @@ -424,23 +449,24 @@ static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx return 0; } + /* This will be a no-op if the slot state isn't RESERVED, which is + * what we want. + */ + (void)bt_mesh_dfu_slot_release(srv->upload.slot); + idx = bt_mesh_dfu_slot_get(fwid, fwid_len, &srv->upload.slot); - if (idx >= 0 && bt_mesh_dfu_slot_is_valid(srv->upload.slot)) { + if (idx >= 0) { 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; } - if (old_slot && !bt_mesh_dfu_slot_is_valid(old_slot)) { - LOG_DBG("Deleting old invalid slot"); - slot_del(srv, old_slot); - } - /* 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); + srv->upload.is_oob = false; + srv->upload.slot = bt_mesh_dfu_slot_reserve(); + if (!srv->upload.slot) { LOG_WRN("No space for slot"); upload_status_rsp(srv, ctx, @@ -448,11 +474,14 @@ static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx return 0; } + bt_mesh_dfu_slot_fwid_set(srv->upload.slot, fwid, fwid_len); + bt_mesh_dfu_slot_info_set(srv->upload.slot, size, meta, meta_len); + 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 +490,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 +507,72 @@ 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; + int status; + struct bt_mesh_dfu_slot *slot; - LOG_DBG(""); + uri_len = net_buf_simple_pull_u8(buf); + + if (uri_len > buf->len) { + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + 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 (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; + } - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_URI_NOT_SUPPORTED); + if (upload_is_busy(srv)) { + 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; + } + + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_BUSY_WITH_UPLOAD); + } + + if (!is_oob_supported(srv) || uri_len == 0) { + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_URI_NOT_SUPPORTED); + return 0; + } + + 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, net_buf_simple_pull_mem(buf, fwid_len), fwid_len); + srv->upload.oob.current_fwid_len = fwid_len; + memcpy(&srv->upload.oob.ctx, ctx, sizeof(struct bt_mesh_msg_ctx)); + + 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 != 0) { + upload_status_rsp(srv, ctx, status); + bt_mesh_dfu_slot_release(srv->upload.slot); + } return 0; } @@ -492,7 +583,11 @@ 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); + if (srv->upload.is_oob) { + srv->cb->cancel_oob_upload(srv, srv->upload.slot); + } else { + (void)bt_mesh_blob_srv_cancel(&srv->upload.blob); + } upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); return 0; @@ -508,7 +603,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 +617,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 +626,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 +647,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 { @@ -730,7 +825,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); + bt_mesh_dfu_slot_commit(srv->upload.slot); srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; return; } @@ -850,7 +945,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 +1108,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 +1118,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 +1144,87 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete_all(struct bt_mesh_dfd_srv *sr return BT_MESH_DFD_SUCCESS; } + +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) +{ + struct bt_mesh_dfu_slot *existing_slot; + int idx; + + if (slot != srv->upload.slot || !srv->upload.is_oob) { + /* This should not happen, unless the application calls the function with a + * "wrong" pointer or at a wrong time. + */ + return -EINVAL; + } + + if (srv->upload.slot->fwid_len) { + /* We are already in progress with an OOB upload. */ + if (srv->upload.slot->fwid_len == fwid_len && + !memcmp(srv->upload.slot->fwid, fwid, fwid_len)) { + /* Duplicate request */ + return -EALREADY; + } + /* Should not happen */ + return -EINVAL; + } + + if (status != BT_MESH_DFD_SUCCESS) { + upload_status_rsp(srv, &srv->upload.oob.ctx, status); + return 0; + } + + if (fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN) { + upload_status_rsp(srv, &srv->upload.oob.ctx, + BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); + return -EFBIG; + } + + idx = bt_mesh_dfu_slot_get(fwid, fwid_len, &existing_slot); + if (idx >= 0) { + LOG_DBG("Already received image"); + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; + bt_mesh_dfu_slot_release(srv->upload.slot); + srv->upload.slot = existing_slot; + upload_status_rsp(srv, &srv->upload.oob.ctx, BT_MESH_DFD_SUCCESS); + return -EALREADY; + } + + bt_mesh_dfu_slot_fwid_set(srv->upload.slot, fwid, fwid_len); + + srv->upload.phase = BT_MESH_DFD_PHASE_TRANSFER_ACTIVE; + 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) { + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR; + bt_mesh_dfu_slot_release(srv->upload.slot); + return 0; + } + + err = bt_mesh_dfu_slot_info_set(srv->upload.slot, size, metadata, metadata_len); + if (err) { + return err; + } + + err = bt_mesh_dfu_slot_commit(srv->upload.slot); + if (err) { + return err; + } + + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; + return 0; +} diff --git a/subsys/bluetooth/mesh/dfu_slot.c b/subsys/bluetooth/mesh/dfu_slot.c index 16628793457cd42..8a975d9800c0d14 100644 --- a/subsys/bluetooth/mesh/dfu_slot.c +++ b/subsys/bluetooth/mesh/dfu_slot.c @@ -20,7 +20,7 @@ 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" @@ -28,15 +28,22 @@ LOG_MODULE_REGISTER(bt_mesh_dfu_slot); #define PROP_URI "u" #define VALID_SLOTS_TAG "v" +enum slot_state { + SLOT_STATE_FREE, + SLOT_STATE_RESERVED, + SLOT_STATE_COMMITTED +}; + #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; + uint8_t state; + uint16_t idx; struct bt_mesh_dfu_slot slot; } slots[CONFIG_BT_MESH_DFU_SLOT_CNT]; +int committed_slot_count; + static char *slot_entry_encode(uint16_t idx, char buf[SLOT_ENTRY_BUFLEN], const char *property) { @@ -46,21 +53,11 @@ 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) -{ - return slot->size > 0U; -} - static inline uint16_t slot_idx(const struct bt_mesh_dfu_slot *slot) { return CONTAINER_OF(slot, struct slot, slot) - &slots[0]; } -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]); -} static bool slot_eq(const struct bt_mesh_dfu_slot *slot, const uint8_t *fwid, size_t fwid_len) @@ -90,12 +87,8 @@ 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) @@ -106,39 +99,21 @@ static void slot_erase(struct slot *slot_to_erase) 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) +int bt_mesh_dfu_slot_count(void) { - return settings_save_one(DFU_SLOT_SETTINGS_PATH "/" VALID_SLOTS_TAG, - valid_slots, sizeof(valid_slots)); + return committed_slot_count; } -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) +struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_reserve(void) { struct slot *slot = NULL; - int err, i; - 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; - } - - for (i = 0; i < ARRAY_SIZE(slots); ++i) { - if (!slot_in_use(&slots[i].slot)) { + for (int i = 0; i < ARRAY_SIZE(slots); ++i) { + if (slots[i].state == SLOT_STATE_FREE) { slot = &slots[i]; - continue; - } - - if (slot_eq(&slots[i].slot, fwid, fwid_len)) { - return &slots[i].slot; + break; } } @@ -147,146 +122,172 @@ 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; + slot->slot.fwid_len = 0; + slot->slot.metadata_len = 0; + slot->slot.size = 0; + slot->state = SLOT_STATE_RESERVED; - err = slot_store(slot); - if (err) { - slot_invalidate(slot); - LOG_WRN("Store failed (err: %d)", err); - return NULL; - } - - sys_slist_append(&list, &slot->n); + 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 (!SLOT_IN_ARRAY(slot) || !slot_in_use(slot)) { - return -ENOENT; + if (metadata_len > CONFIG_BT_MESH_DFU_METADATA_MAXLEN) { + return -EFBIG; } - idx = slot_idx(slot); + if (slot->state != SLOT_STATE_RESERVED) { + return -EINVAL; + } - LOG_DBG("%u: %u", idx, valid); + slot->slot.size = size; + slot->slot.metadata_len = metadata_len; + memcpy(slot->slot.metadata, metadata, metadata_len); + return 0; +} - if (valid) { - prev = atomic_test_and_set_bit(valid_slots, idx); - } else { - prev = atomic_test_and_clear_bit(valid_slots, idx); - } +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 (valid == prev) { - return 0; + if (fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN) { + return -EFBIG; } - err = valid_slots_store(); - if (err) { - LOG_WRN("Storage failed. err: %d", err); - atomic_set_bit_to(valid_slots, idx, prev); + if (slot->state != SLOT_STATE_RESERVED) { + return -EINVAL; } - return err; + slot->slot.fwid_len = fwid_len; + memcpy(slot->slot.fwid, fwid, fwid_len); + return 0; } -bool bt_mesh_dfu_slot_is_valid(const struct bt_mesh_dfu_slot *slot) +int bt_mesh_dfu_slot_commit(struct bt_mesh_dfu_slot *dfu_slot) { - uint16_t idx; + int err; + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); + + if (slot->state != SLOT_STATE_RESERVED || + slot->slot.fwid_len == 0 || + slot->slot.size == 0) { + return -EINVAL; + } + + if (bt_mesh_dfu_slot_get(slot->slot.fwid, slot->slot.fwid_len, NULL) + >= 0) { + return -EALREADY; + } + + slot->idx = committed_slot_count; + slot->state = SLOT_STATE_COMMITTED; - if (!SLOT_IN_ARRAY(slot) || !slot_in_use(slot)) { - return false; + err = slot_store(slot); + if (err) { + LOG_WRN("Store failed (err: %d)", err); + slot->state = SLOT_STATE_RESERVED; + slot->idx = 0; + return err; } - idx = slot_idx(slot); - return atomic_test_bit(valid_slots, idx); + committed_slot_count++; + + LOG_DBG("Stored slot #%u: %s", slot - &slots[0], + bt_hex(slot->slot.fwid, slot->slot.fwid_len)); + return 0; } -int bt_mesh_dfu_slot_del(const struct bt_mesh_dfu_slot *slot) +int bt_mesh_dfu_slot_release(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 (slot->state != SLOT_STATE_RESERVED) { + return -EINVAL; } - LOG_DBG("%u", s - &slots[0]); - - slot_erase(s); - slot_invalidate(s); - sys_slist_find_and_remove(&list, &s->n); + slot->state = SLOT_STATE_FREE; return 0; } -int bt_mesh_dfu_slot_del_all(void) +int bt_mesh_dfu_slot_del(const struct bt_mesh_dfu_slot *dfu_slot) { - struct slot *s; + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); + + if (slot->state != SLOT_STATE_COMMITTED) { + return -EINVAL; + } + + int idx = slot - &slots[0]; + + LOG_DBG("%u", idx); - SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { - slot_erase(s); - slot_invalidate(s); + slot_erase(slot); + slot->state = SLOT_STATE_FREE; + + for (int i = 0; i < CONFIG_BT_MESH_DFU_SLOT_CNT; i++) { + if (slots[i].idx > slot->idx) { + slots[i].idx--; + slot_store(&slots[i]); + } } - sys_slist_init(&list); + committed_slot_count--; return 0; } -const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t idx) +void bt_mesh_dfu_slot_del_all(void) { - struct slot *s; + for (int i = 0; i < CONFIG_BT_MESH_DFU_SLOT_CNT; i++) { + if (slots[i].state == SLOT_STATE_COMMITTED) { + slot_erase(&slots[i]); + slots[i].state = SLOT_STATE_FREE; + } + } + + committed_slot_count = 0; +} - SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { - if (!idx--) { - return &s->slot; +struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t idx) +{ + for (int i = 0; i < CONFIG_BT_MESH_DFU_SLOT_CNT; i++) { + if (slots[i].state == SLOT_STATE_COMMITTED && + slots[i].idx == idx) { + return &slots[i].slot; } } 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; - return idx; + for (int i = 0; i < CONFIG_BT_MESH_DFU_SLOT_CNT; i++) { + if (slots[i].state == SLOT_STATE_COMMITTED && + slot_eq(&slots[i].slot, fwid, fwid_len)) { + if (slot) { + *slot = &slots[i].slot; + } + return slots[i].idx; } - - idx++; } return -ENOENT; } -int bt_mesh_dfu_slot_idx_get(const struct bt_mesh_dfu_slot *slot) +int bt_mesh_dfu_slot_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) { - return idx; - } + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); - idx++; + if (slot->state == SLOT_STATE_COMMITTED) { + return slot->idx; } return -ENOENT; @@ -295,16 +296,27 @@ 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 bt_mesh_dfu_slot *order[CONFIG_BT_MESH_DFU_SLOT_CNT] = { 0 }; + + for (int i = 0; i < CONFIG_BT_MESH_DFU_SLOT_CNT; i++) { + if (slots[i].state == SLOT_STATE_COMMITTED) { + order[slots[i].idx] = &slots[i].slot; + } + } + + for (int i = 0; i < CONFIG_BT_MESH_DFU_SLOT_CNT; i++) { + if (!order[i]) { + break; + } - SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { cnt++; + if (!cb) { continue; } - iter = cb(&s->slot, user_data); + iter = cb(order[i], user_data); if (iter != BT_MESH_DFU_ITER_CONTINUE) { break; } @@ -320,15 +332,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 +342,16 @@ 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); + committed_slot_count++; } - 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].state = SLOT_STATE_FREE; + committed_slot_count--; return 0; } @@ -359,7 +362,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].state = SLOT_STATE_FREE; + committed_slot_count--; return 0; } @@ -367,16 +371,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 1bc58f64de961a2..3d07dff808f031d 100644 --- a/subsys/bluetooth/mesh/dfu_slot.h +++ b/subsys/bluetooth/mesh/dfu_slot.h @@ -16,48 +16,67 @@ 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 and @c bt_mesh_dfu_slot_commit must be + * called 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 fwid 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 Metadata or NULL. + * @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_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_valid_set(const struct bt_mesh_dfu_slot *slot, bool valid); +int bt_mesh_dfu_slot_commit(struct bt_mesh_dfu_slot *dfu_slot); -/** @brief Check whether a slot is valid. +/** @brief Release a reserved slot so that it can be reserved again. * - * @param slot Slot to check. + * @param dfu_slot Pointer to the reserved slot. * - * @return true if the slot is valid, false otherwise. + * @return 0 on success, (negative) error code otherwise. */ -bool bt_mesh_dfu_slot_is_valid(const struct bt_mesh_dfu_slot *slot); +int bt_mesh_dfu_slot_release(const struct bt_mesh_dfu_slot *dfu_slot); /** @brief Delete an allocated DFU image slot. * @@ -72,7 +91,7 @@ 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. * @@ -81,7 +100,7 @@ int bt_mesh_dfu_slot_del_all(void); * @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); +struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t idx); /** @brief Get the DFU image slot for the image with the given firmware ID. * @@ -91,8 +110,7 @@ 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. * diff --git a/subsys/bluetooth/mesh/shell/dfd.c b/subsys/bluetooth/mesh/shell/dfd.c index 94d656f3689e50f..4d322ed2f9194a5 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); @@ -342,14 +341,14 @@ static int cmd_dfd_fw_get_by_idx(const struct shell *sh, size_t argc, char *argv { int err = 0; uint16_t idx = shell_strtoul(argv[1], 0, &err); - const struct bt_mesh_dfu_slot *slot = bt_mesh_dfu_slot_at(idx); + struct bt_mesh_dfu_slot *slot = bt_mesh_dfu_slot_at(idx); if (err) { shell_warn(sh, "Unable to parse input string argument"); 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 61fad0a9b5cc885..baa7d093bc9b6b7 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_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 302ff522c131407..51dd1d95e1dfd84 100644 --- a/tests/bsim/bluetooth/mesh/prj_mesh1d1.conf +++ b/tests/bsim/bluetooth/mesh/prj_mesh1d1.conf @@ -58,7 +58,7 @@ 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_DFU_SLOT_CNT=4 CONFIG_BT_MESH_PRIV_BEACON_SRV=y CONFIG_BT_MESH_PRIV_BEACON_CLI=y diff --git a/tests/bsim/bluetooth/mesh/src/test_dfu.c b/tests/bsim/bluetooth/mesh/src/test_dfu.c index b2824f54b46d289..91d0f3df4f12dac 100644 --- a/tests/bsim/bluetooth/mesh/src/test_dfu.c +++ b/tests/bsim/bluetooth/mesh/src/test_dfu.c @@ -162,7 +162,7 @@ static struct bt_mesh_dfd_srv_cb dfd_srv_cb = { .phase = dist_phase_changed, }; -struct bt_mesh_dfd_srv dfd_srv = BT_MESH_DFD_SRV_INIT(&dfd_srv_cb); +struct bt_mesh_dfd_srv dfd_srv = BT_MESH_DFD_SRV_INIT(&dfd_srv_cb, NULL, 0); static struct k_sem dfu_metadata_check_sem; static bool dfu_metadata_fail = true; @@ -435,25 +435,52 @@ 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) { + return NULL; + } + + int err = bt_mesh_dfu_slot_fwid_set(new_slot, fwid, fwid_len); + + if (err) { + (void)bt_mesh_dfu_slot_release(new_slot); + return NULL; + } + + err = bt_mesh_dfu_slot_info_set(new_slot, size, metadata, metadata_len); + + if (err) { + (void)bt_mesh_dfu_slot_release(new_slot); + 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) { + bt_mesh_dfu_slot_release(new_slot); + return false; + } if (slot) { *slot = new_slot; @@ -571,13 +598,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, @@ -592,30 +618,22 @@ static void test_dist_dfu_slot_create(void) 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); + return; + } + } } - 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; @@ -631,20 +649,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); + int *i = data; - ASSERT_TRUE(idx >= 0, "Failed to retrieve slot index"); - + 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); @@ -654,13 +669,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, @@ -669,26 +683,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); } @@ -698,7 +703,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; @@ -711,14 +716,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)); } } @@ -731,7 +728,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(); @@ -747,6 +743,27 @@ 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()); + ASSERT_EQUAL(0, 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 target_test_effect(enum bt_mesh_dfu_effect effect) { dfu_target_effect = effect; @@ -928,12 +945,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; @@ -1604,6 +1623,8 @@ 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(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.sh b/tests/bsim/bluetooth/mesh/tests_scripts/dfu/dfu_slot.sh index 506a49838ef771b..946e9ddd9fc4c1c 100755 --- a/tests/bsim/bluetooth/mesh/tests_scripts/dfu/dfu_slot.sh +++ b/tests/bsim/bluetooth/mesh/tests_scripts/dfu/dfu_slot.sh @@ -11,6 +11,7 @@ source $(dirname "${BASH_SOURCE[0]}")/../../_mesh_test.sh # and verifies they do not exist. # - Fourth test is rebooted device that verifies if removing all slots also removed them # from storage. +# - Fifth test tests slot reservation APIs. conf=prj_mesh1d1_conf overlay=overlay_pst_conf RunTest dfu_slot dfu_dist_dfu_slot_create @@ -20,3 +21,5 @@ RunTest dfu_slot dfu_dist_dfu_slot_create_recover RunTest dfu_slot dfu_dist_dfu_slot_delete_all RunTest dfu_slot dfu_dist_dfu_slot_check_delete_all + +RunTest dfu_slot dfu_dist_dfu_slot_reservation