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 <ludvig.jordet@nordicsemi.no>
diff --git a/doc/connectivity/bluetooth/api/mesh/shell.rst b/doc/connectivity/bluetooth/api/mesh/shell.rst
index f9acf24..e6609f4 100644
--- a/doc/connectivity/bluetooth/api/mesh/shell.rst
+++ b/doc/connectivity/bluetooth/api/mesh/shell.rst
@@ -1034,15 +1034,14 @@
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 <Size> [<FwID> [<Metadata> [<URI>]]]``
+``mesh models dfu slot add <Size> <FwID> [<Metadata>]``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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 <SlotIdx>``
diff --git a/include/zephyr/bluetooth/mesh/dfd_srv.h b/include/zephyr/bluetooth/mesh/dfd_srv.h
index 0e281b2..da339c5 100644
--- a/include/zephyr/bluetooth/mesh/dfd_srv.h
+++ b/include/zephyr/bluetooth/mesh/dfd_srv.h
@@ -29,11 +29,47 @@
#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 @@
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 {
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 91f36cd..4745355 100644
--- a/include/zephyr/bluetooth/mesh/dfu.h
+++ b/include/zephyr/bluetooth/mesh/dfu.h
@@ -34,6 +34,10 @@
#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 @@
/** 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 @@
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 99012e9..6bc4c8b 100644
--- a/subsys/bluetooth/mesh/Kconfig
+++ b/subsys/bluetooth/mesh/Kconfig
@@ -1235,6 +1235,14 @@
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 2898ca3..54de343 100644
--- a/subsys/bluetooth/mesh/dfd_srv.c
+++ b/subsys/bluetooth/mesh/dfd_srv.c
@@ -224,7 +224,20 @@
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 @@
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 @@
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 @@
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 @@
!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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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_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 @@
}
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 @@
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 1662879..097d5ab 100644
--- a/subsys/bluetooth/mesh/dfu_slot.c
+++ b/subsys/bluetooth/mesh/dfu_slot.c
@@ -20,23 +20,22 @@
#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,22 +45,6 @@
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)
{
@@ -69,9 +52,22 @@
!memcmp(fwid, slot->fwid, fwid_len);
}
+static bool is_slot_committed(struct slot *slot_to_check)
+{
+ struct slot *s;
+
+ SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
+ if (s == slot_to_check) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
static int slot_store(const struct slot *slot_to_store)
{
- uint16_t idx = 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 @@
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 @@
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);
+ if (slot_index == UINT32_MAX) {
+ slot_index_defrag();
+ }
+
+ slot->slot.fwid_len = 0;
+ slot->slot.metadata_len = 0;
+ slot->slot.size = 0;
+ slot->idx = ++slot_index;
+
+ LOG_DBG("Reserved slot #%u", slot - &slots[0]);
+
+ return &slot->slot;
+}
+
+int bt_mesh_dfu_slot_info_set(struct bt_mesh_dfu_slot *dfu_slot, size_t size,
+ const uint8_t *metadata, size_t metadata_len)
+{
+ struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot);
+
+ if (metadata_len > CONFIG_BT_MESH_DFU_METADATA_MAXLEN) {
+ return -EFBIG;
+ }
+
+ if (slot->idx == 0 || is_slot_committed(slot)) {
+ return -EINVAL;
+ }
+
slot->slot.size = size;
+ slot->slot.metadata_len = metadata_len;
+ memcpy(slot->slot.metadata, metadata, metadata_len);
+ return 0;
+}
+
+int bt_mesh_dfu_slot_fwid_set(struct bt_mesh_dfu_slot *dfu_slot,
+ const uint8_t *fwid, size_t fwid_len)
+{
+ struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot);
+
+ if (fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN) {
+ return -EFBIG;
+ }
+
+ if (slot->idx == 0 || is_slot_committed(slot)) {
+ return -EINVAL;
+ }
+
+ for (int i = 0; i < ARRAY_SIZE(slots); i++) {
+ if (slots[i].idx != 0 &&
+ slot_eq(&slots[i].slot, fwid, fwid_len)) {
+ return is_slot_committed(&slots[i]) ?
+ -EEXIST : -EALREADY;
+ }
+ }
+
+ slot->slot.fwid_len = fwid_len;
+ memcpy(slot->slot.fwid, fwid, fwid_len);
+ return 0;
+}
+
+int bt_mesh_dfu_slot_commit(struct bt_mesh_dfu_slot *dfu_slot)
+{
+ int err;
+ struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot);
+
+ if (slot->idx == 0 ||
+ slot->slot.fwid_len == 0 ||
+ slot->slot.size == 0 ||
+ is_slot_committed(slot)) {
+ return -EINVAL;
+ }
err = slot_store(slot);
if (err) {
- slot_invalidate(slot);
LOG_WRN("Store failed (err: %d)", err);
- return NULL;
+ return err;
}
sys_slist_append(&list, &slot->n);
- LOG_DBG("Added slot #%u: %s", slot - &slots[0],
+ LOG_DBG("Stored slot #%u: %s", ARRAY_INDEX(slots, slot),
bt_hex(slot->slot.fwid, slot->slot.fwid_len));
- return &slot->slot;
+ return 0;
}
-int bt_mesh_dfu_slot_valid_set(const struct bt_mesh_dfu_slot *slot, bool valid)
+void bt_mesh_dfu_slot_release(const struct bt_mesh_dfu_slot *dfu_slot)
{
- 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 (is_slot_committed(slot)) {
+ return;
}
- idx = slot_idx(slot);
-
- LOG_DBG("%u: %u", idx, valid);
-
- if (valid) {
- prev = atomic_test_and_set_bit(valid_slots, idx);
- } else {
- prev = atomic_test_and_clear_bit(valid_slots, idx);
- }
-
- if (valid == prev) {
- return 0;
- }
-
- err = valid_slots_store();
- if (err) {
- LOG_WRN("Storage failed. err: %d", err);
- atomic_set_bit_to(valid_slots, idx, prev);
- }
-
- return err;
+ slot->idx = 0;
}
-bool bt_mesh_dfu_slot_is_valid(const struct bt_mesh_dfu_slot *slot)
+int bt_mesh_dfu_slot_del(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 (!sys_slist_find_and_remove(&list, &slot->n)) {
+ return -EINVAL;
}
- idx = slot_idx(slot);
- return atomic_test_bit(valid_slots, idx);
-}
+ int idx = ARRAY_INDEX(slots, slot);
-int bt_mesh_dfu_slot_del(const struct bt_mesh_dfu_slot *slot)
-{
- struct slot *s = CONTAINER_OF(slot, struct slot, slot);
+ LOG_DBG("%u", idx);
- if (!SLOT_IN_ARRAY(slot) || !slot_in_use(slot)) {
- return -ENOENT;
- }
-
- LOG_DBG("%u", s - &slots[0]);
-
- slot_erase(s);
- slot_invalidate(s);
- sys_slist_find_and_remove(&list, &s->n);
+ 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 1bc58f6..969c6d3 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.
*
- * @param slot Slot to check.
+ * If the commit fails for any reason, the slot will still be in the reserved
+ * state after this call.
*
- * @return true if the slot is valid, false otherwise.
+ * @param dfu_slot Pointer to the reserved slot.
+ *
+ * @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_commit(struct bt_mesh_dfu_slot *dfu_slot);
-/** @brief Delete an allocated DFU image slot.
+/** @brief Release a reserved slot so that it can be reserved again.
+ *
+ * @param dfu_slot Pointer to the reserved slot.
+ */
+void bt_mesh_dfu_slot_release(const struct bt_mesh_dfu_slot *dfu_slot);
+
+/** @brief Delete a committed DFU image slot.
*
* @param slot Slot to delete. Must be a valid pointer acquired from this
* module.
@@ -72,18 +91,19 @@
*
* @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 @@
*
* @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 94d656f..b7daf42 100644
--- a/subsys/bluetooth/mesh/shell/dfd.c
+++ b/subsys/bluetooth/mesh/shell/dfd.c
@@ -43,7 +43,7 @@
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 @@
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 @@
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 71d27b1..8d7fc96 100644
--- a/subsys/bluetooth/mesh/shell/dfu.c
+++ b/subsys/bluetooth/mesh/shell/dfu.c
@@ -375,13 +375,12 @@
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 @@
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);
+
+ 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;
}
- 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;
- }
-
- 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_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 @@
{
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 @@
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 @@
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(
dfu_slot_cmds,
SHELL_CMD_ARG(add, NULL,
- "<Size> [<FwID> [<Metadata> [<URI>]]]",
- cmd_dfu_slot_add, 2, 3),
+ "<Size> <FwID> [<Metadata>]",
+ cmd_dfu_slot_add, 3, 1),
SHELL_CMD_ARG(del, NULL, "<SlotIdx>", 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, "<SlotIdx>", 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 69c1055..15df7b8 100644
--- a/tests/bsim/bluetooth/mesh/prj_mesh1d1.conf
+++ b/tests/bsim/bluetooth/mesh/prj_mesh1d1.conf
@@ -58,7 +58,8 @@
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 f7c4ca6..a235e86 100644
--- a/tests/bsim/bluetooth/mesh/src/test_dfu.c
+++ b/tests/bsim/bluetooth/mesh/src/test_dfu.c
@@ -434,25 +434,50 @@
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_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 @@
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");
+
+ 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);
+ }
+ }
}
- /* 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;
- }
- 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 @@
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 idx = bt_mesh_dfu_slot_img_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);
@@ -649,13 +660,12 @@
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 @@
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);
+ 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(0);
- ASSERT_EQUAL(true, bt_mesh_dfu_slot_is_valid(slot));
-
- 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 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 @@
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 @@
bt_mesh_device_setup(&prov, &dist_comp);
bt_mesh_dfu_slot_del_all();
-
check_delete_all();
PASS();
@@ -742,6 +734,63 @@
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_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 @@
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 0000000..3f18f1a
--- /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 0000000..ddd7d01
--- /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