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>
This commit is contained in:
Ludvig Samuelsen Jordet 2023-08-09 09:30:29 +02:00 committed by Fabio Baltieri
commit b990a74f8b
13 changed files with 803 additions and 337 deletions

View file

@ -1034,15 +1034,14 @@ Firmware Update Client model
The Firmware Update Client model can be added to the mesh shell by enabling configuration options :kconfig:option:`CONFIG_BT_MESH_BLOB_CLI` and :kconfig:option:`CONFIG_BT_MESH_DFU_CLI`. The Firmware Update Client demonstrates the firmware update Distributor role by transferring a dummy firmware update to a set of Target nodes.
``mesh models dfu slot add <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>``

View file

@ -29,11 +29,47 @@ extern "C" {
#define CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX 0
#endif
#ifndef CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE
#define CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE 0
#endif
#ifndef CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE
#define CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE 0
#endif
struct bt_mesh_dfd_srv;
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
/**
*
* @brief Initialization parameters for the @ref bt_mesh_dfd_srv with OOB
* upload support.
*
* @param[in] _cb Pointer to a @ref bt_mesh_dfd_srv_cb instance.
* @param[in] _oob_schemes Array of OOB schemes supported by the server,
* each scheme being a code point from the
* Bluetooth SIG Assigned Numbers document.
* @param[in] _oob_schemes_count Number of schemes in @c _oob_schemes.
*/
#define BT_MESH_DFD_SRV_OOB_INIT(_cb, _oob_schemes, _oob_schemes_count) \
{ \
.cb = _cb, \
.dfu = BT_MESH_DFU_CLI_INIT(&_bt_mesh_dfd_srv_dfu_cb), \
.upload = { \
.blob = { .cb = &_bt_mesh_dfd_srv_blob_cb }, \
}, \
.oob_schemes = { \
.schemes = _oob_schemes, \
.count = _oob_schemes_count, \
}, \
}
#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */
/**
*
* @brief Initialization parameters for the @ref bt_mesh_dfd_srv.
*
* @param[in] _cb Pointer to a @ref bt_mesh_dfd_srv_cb instance.
*/
#define BT_MESH_DFD_SRV_INIT(_cb) \
{ \
@ -75,6 +111,64 @@ struct bt_mesh_dfd_srv_cb {
const struct bt_mesh_dfu_slot *slot,
const struct bt_mesh_blob_io **io);
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
/** @brief Firmware upload OOB start callback.
*
* Called at the start of an OOB firmware upload. The application must
* start a firmware check using an OOB mechanism, and then call
* @ref bt_mesh_dfd_srv_oob_check_complete. Depending on the return
* value of this function, the application must then start storing the
* firmware image using an OOB mechanism, and call
* @ref bt_mesh_dfd_srv_oob_store_complete. This callback is mandatory
* to support OOB uploads.
*
* @param srv Firmware Distribution Server model instance.
* @param slot Slot to be used for the upload.
* @param uri Pointer to buffer containing the URI used to
* check for new firmware.
* @param uri_len Length of the URI buffer.
* @param fwid Pointer to buffer containing the current
* firmware ID to be used when checking for
* availability of new firmware.
* @param fwid_len Length of the current firmware ID. Must be set
* to the length of the new firmware ID if it is
* available, or to 0 if new firmware is not
* available.
*
* @return BT_MESH_DFD_SUCCESS on success, or error code otherwise.
*/
int (*start_oob_upload)(struct bt_mesh_dfd_srv *srv,
const struct bt_mesh_dfu_slot *slot,
const char *uri, uint8_t uri_len,
const uint8_t *fwid, uint16_t fwid_len);
/** @brief Cancel store OOB callback
*
* Called when an OOB store is cancelled. The application must stop
* any ongoing OOB image transfer. This callback is mandatory to
* support OOB uploads.
*
* @param srv Firmware Distribution Server model instance.
* @param slot DFU image slot to cancel
*/
void (*cancel_oob_upload)(struct bt_mesh_dfd_srv *srv,
const struct bt_mesh_dfu_slot *slot);
/** @brief Get the progress of an ongoing OOB store
*
* Called by the Firmware Distribution Server model when it needs to
* get the current progress of an ongoing OOB store from the
* application. This callback is mandatory to support OOB uploads.
*
* @param srv Firmware Distribution Server model instance.
* @param slot DFU image slot to get progress for.
*
* @return The current progress of the ongoing OOB store, in percent.
*/
uint8_t (*oob_progress_get)(struct bt_mesh_dfd_srv *srv,
const struct bt_mesh_dfu_slot *slot);
#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */
/** @brief Slot delete callback.
*
* Called when the Firmware Distribution Server is about to delete a DFU image slot.
@ -129,12 +223,79 @@ struct bt_mesh_dfd_srv {
struct {
enum bt_mesh_dfd_upload_phase phase;
const struct bt_mesh_dfu_slot *slot;
struct bt_mesh_dfu_slot *slot;
const struct flash_area *area;
struct bt_mesh_blob_srv blob;
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
bool is_oob;
struct {
uint8_t uri_len;
uint8_t uri[CONFIG_BT_MESH_DFU_URI_MAXLEN];
uint16_t current_fwid_len;
uint8_t current_fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN];
struct bt_mesh_msg_ctx ctx;
} oob;
#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */
} upload;
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
struct {
const uint8_t *schemes;
const uint8_t count;
} oob_schemes;
#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */
};
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
/** @brief Call when an OOB check has completed or failed
*
* This should be called by the application after an OOB check started by the @c start_oob_upload
* callback has completed or failed. The @p status param should be set to one of the following
* values:
*
* * @c BT_MESH_DFD_SUCCESS if the check was succesfull and a new firmware ID was found.
* * @c BT_MESH_DFD_ERR_URI_MALFORMED if the URI is not formatted correctly.
* * @c BT_MESH_DFD_ERR_URI_NOT_SUPPORTED if the URI scheme is not supported by the node.
* * @c BT_MESH_DFD_ERR_URI_UNREACHABLE if the URI can't be reached.
* * @c BT_MESH_DFD_ERR_NEW_FW_NOT_AVAILABLE if the check completes successfully but no new
* firmware is available.
*
* If this function returns 0, the application should then download the firmware to the
* slot. If an error code is returned, the application should abort the OOB upload.
*
* @param srv Firmware Distribution Server model instance.
* @param slot The slot used in the OOB upload.
* @param status The result of the firmware check.
* @param fwid If the check was successful and new firmware found, this should point to a
* buffer containing the new firmware ID to store.
* @param fwid_len The length of the firmware ID pointed to by @p fwid.
*
* @return 0 on success, (negative) error code otherwise.
*/
int bt_mesh_dfd_srv_oob_check_complete(struct bt_mesh_dfd_srv *srv,
const struct bt_mesh_dfu_slot *slot, int status,
uint8_t *fwid, size_t fwid_len);
/** @brief Call when an OOB store has completed or failed
*
* This should be called by the application after an OOB store started after a succesfull call to
* @c bt_mesh_dfd_srv_oob_check_complete has completed successfully or failed.
*
* @param srv Firmware Distribution Server model instance.
* @param slot The slot used when storing the firmware image.
* @param success @c true if the OOB store completed successfully, @c false otherwise.
* @param size The size of the stored firmware image, in bytes.
* @param metadata Pointer to the metadata received OOB, or @c NULL if no metadata was
* received.
* @param metadata_len Size of the metadata pointed to by @p metadata.
*
* @return 0 on success, (negative) error code otherwise.
*/
int bt_mesh_dfd_srv_oob_store_complete(struct bt_mesh_dfd_srv *srv,
const struct bt_mesh_dfu_slot *slot, bool success,
size_t size, const uint8_t *metadata, size_t metadata_len);
#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */
/** @cond INTERNAL_HIDDEN */
extern const struct bt_mesh_model_op _bt_mesh_dfd_srv_op[];
extern const struct bt_mesh_model_cb _bt_mesh_dfd_srv_cb;

View file

@ -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];
};
/** @} */

View file

@ -1235,6 +1235,14 @@ config BT_MESH_DFD_SRV_TARGETS_MAX
This value defines the maximum number of Target nodes the Firmware
Distribution Server can target simultaneously.
config BT_MESH_DFD_SRV_OOB_UPLOAD
bool "Support for DFU image OOB upload"
help
This enables support for OOB upload of firmware images for
distribution. This makes several callbacks and use of the init
macro BT_MESH_DFD_SRV_INIT_OOB mandatory. See the API documentation
for bt_mesh_dfd_srv_cb for details about the mandatory callbacks.
endif
config BT_MESH_RPR_SRV

View file

@ -224,7 +224,20 @@ static int handle_capabilities_get(struct bt_mesh_model *mod, struct bt_mesh_msg
size = MIN(size, CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE);
net_buf_simple_add_le32(&rsp, CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE - size);
net_buf_simple_add_u8(&rsp, 0U); /* OOB retrieval not supported */
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
struct bt_mesh_dfd_srv *srv = mod->user_data;
if (srv->oob_schemes.count > 0) {
net_buf_simple_add_u8(&rsp, 1);
net_buf_simple_add_mem(&rsp, srv->oob_schemes.schemes,
srv->oob_schemes.count);
} else
#else
{
net_buf_simple_add_u8(&rsp, 0);
}
#endif
bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL);
@ -346,10 +359,19 @@ static void upload_status_rsp(struct bt_mesh_dfd_srv *srv,
return;
}
net_buf_simple_add_u8(&rsp,
bt_mesh_blob_srv_progress(&srv->upload.blob));
net_buf_simple_add_mem(&rsp, srv->upload.slot->fwid,
srv->upload.slot->fwid_len);
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
if (srv->upload.is_oob) {
net_buf_simple_add_u8(&rsp,
srv->cb->oob_progress_get(srv, srv->upload.slot) | BIT(7));
net_buf_simple_add_mem(&rsp, srv->upload.oob.current_fwid,
srv->upload.oob.current_fwid_len);
} else
#endif
{
net_buf_simple_add_u8(&rsp, bt_mesh_blob_srv_progress(&srv->upload.blob));
net_buf_simple_add_mem(&rsp, srv->upload.slot->fwid,
srv->upload.slot->fwid_len);
}
bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL);
}
@ -364,16 +386,42 @@ static int handle_upload_get(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *
return 0;
}
static inline int set_upload_fwid(struct bt_mesh_dfd_srv *srv, struct bt_mesh_msg_ctx *ctx,
const uint8_t *fwid, size_t fwid_len)
{
int err = bt_mesh_dfu_slot_fwid_set(srv->upload.slot, fwid, fwid_len);
switch (err) {
case -EFBIG: /* Fwid too long */
case -EALREADY: /* Other server is in progress with this fwid */
bt_mesh_dfu_slot_release(srv->upload.slot);
upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL);
break;
case -EEXIST: /* Img with this fwid already is in list */
srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS;
bt_mesh_dfu_slot_release(srv->upload.slot);
upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS);
break;
case 0:
srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE;
break;
case -EINVAL: /* Slot in wrong state. */
default:
break;
}
return err;
}
static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_dfd_srv *srv = mod->user_data;
const struct bt_mesh_dfu_slot *old_slot = srv->upload.slot;
size_t meta_len, fwid_len, size;
const uint8_t *meta, *fwid;
uint16_t timeout_base;
uint64_t blob_id;
int err, idx;
int err;
uint8_t ttl;
ttl = net_buf_simple_pull_u8(buf);
@ -392,9 +440,7 @@ static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx
LOG_DBG("Upload Start: size: %d, fwid: %s, metadata: %s", size, bt_hex(fwid, fwid_len),
bt_hex(meta, meta_len));
if (size > CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE ||
fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN ||
meta_len > CONFIG_BT_MESH_DFU_METADATA_MAXLEN) {
if (size > CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE) {
upload_status_rsp(srv, ctx,
BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES);
return 0;
@ -413,7 +459,11 @@ static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx
!memcmp(srv->upload.slot->metadata, meta, meta_len) &&
srv->upload.blob.state.xfer.id == blob_id &&
srv->upload.blob.state.ttl == ttl &&
srv->upload.blob.state.timeout_base == timeout_base) {
srv->upload.blob.state.timeout_base == timeout_base
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
&& !srv->upload.is_oob
#endif
) {
LOG_DBG("Duplicate upload start");
upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS);
return 0;
@ -424,23 +474,16 @@ static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx
return 0;
}
idx = bt_mesh_dfu_slot_get(fwid, fwid_len, &srv->upload.slot);
if (idx >= 0 && bt_mesh_dfu_slot_is_valid(srv->upload.slot)) {
LOG_DBG("Already received image");
srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS;
upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS);
return 0;
}
/* This will be a no-op if the slot state isn't RESERVED, which is
* what we want.
*/
bt_mesh_dfu_slot_release(srv->upload.slot);
if (old_slot && !bt_mesh_dfu_slot_is_valid(old_slot)) {
LOG_DBG("Deleting old invalid slot");
slot_del(srv, old_slot);
}
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
srv->upload.is_oob = false;
#endif
srv->upload.slot = bt_mesh_dfu_slot_reserve();
/* TODO Store transfer state before slot is added. */
srv->upload.slot = bt_mesh_dfu_slot_add(size, fwid, fwid_len, meta,
meta_len, NULL, 0);
if (!srv->upload.slot) {
LOG_WRN("No space for slot");
upload_status_rsp(srv, ctx,
@ -448,11 +491,27 @@ static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx
return 0;
}
err = set_upload_fwid(srv, ctx, fwid, fwid_len);
if (err) {
return err;
}
err = bt_mesh_dfu_slot_info_set(srv->upload.slot, size, meta, meta_len);
switch (err) {
case -EFBIG:
upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL);
break;
case 0:
break;
default:
return err;
}
srv->io = NULL;
err = srv->cb->recv(srv, srv->upload.slot, &srv->io);
if (err || !srv->io) {
LOG_ERR("App rejected upload. err: %d io: %p", err, srv->io);
slot_del(srv, srv->upload.slot);
bt_mesh_dfu_slot_release(srv->upload.slot);
upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL);
return 0;
}
@ -461,7 +520,7 @@ static int handle_upload_start(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx
timeout_base);
if (err) {
LOG_ERR("BLOB Server rejected upload (err: %d)", err);
slot_del(srv, srv->upload.slot);
bt_mesh_dfu_slot_release(srv->upload.slot);
upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL);
return 0;
}
@ -478,10 +537,71 @@ static int handle_upload_start_oob(struct bt_mesh_model *mod, struct bt_mesh_msg
struct net_buf_simple *buf)
{
struct bt_mesh_dfd_srv *srv = mod->user_data;
uint8_t uri_len;
uint8_t *uri;
uint16_t fwid_len;
uint8_t *fwid;
LOG_DBG("");
uri_len = net_buf_simple_pull_u8(buf);
if (uri_len > buf->len) {
return -EINVAL;
}
uri = net_buf_simple_pull_mem(buf, uri_len);
fwid_len = buf->len;
fwid = net_buf_simple_pull_mem(buf, fwid_len);
if (upload_is_busy(srv)) {
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
if (srv->upload.is_oob &&
uri_len == srv->upload.oob.uri_len &&
fwid_len == srv->upload.oob.current_fwid_len &&
!memcmp(uri, srv->upload.oob.uri, uri_len) &&
!memcmp(fwid, srv->upload.oob.current_fwid, fwid_len)) {
/* Same image, return SUCCESS for idempotency */
upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS);
return 0;
}
#endif
upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_BUSY_WITH_UPLOAD);
return 0;
}
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
if (uri_len > CONFIG_BT_MESH_DFU_URI_MAXLEN ||
fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN) {
upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL);
return 0;
}
struct bt_mesh_dfu_slot *slot = bt_mesh_dfu_slot_reserve();
if (slot == NULL) {
upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES);
return 0;
}
srv->upload.is_oob = true;
srv->upload.slot = slot;
memcpy(srv->upload.oob.uri, uri, uri_len);
srv->upload.oob.uri_len = uri_len;
memcpy(srv->upload.oob.current_fwid, fwid, fwid_len);
srv->upload.oob.current_fwid_len = fwid_len;
memcpy(&srv->upload.oob.ctx, ctx, sizeof(struct bt_mesh_msg_ctx));
int status = srv->cb->start_oob_upload(srv, srv->upload.slot, srv->upload.oob.uri,
srv->upload.oob.uri_len,
srv->upload.oob.current_fwid,
srv->upload.oob.current_fwid_len);
if (status != BT_MESH_DFD_SUCCESS) {
upload_status_rsp(srv, ctx, status);
bt_mesh_dfu_slot_release(srv->upload.slot);
}
#else
upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_URI_NOT_SUPPORTED);
#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */
return 0;
}
@ -492,7 +612,14 @@ static int handle_upload_cancel(struct bt_mesh_model *mod, struct bt_mesh_msg_ct
struct bt_mesh_dfd_srv *srv = mod->user_data;
srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_IDLE;
(void)bt_mesh_blob_srv_cancel(&srv->upload.blob);
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
if (srv->upload.is_oob) {
srv->cb->cancel_oob_upload(srv, srv->upload.slot);
} else
#endif
{
(void)bt_mesh_blob_srv_cancel(&srv->upload.blob);
}
upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS);
return 0;
@ -508,7 +635,7 @@ static void fw_status_rsp(struct bt_mesh_dfd_srv *srv,
bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_FW_STATUS);
net_buf_simple_add_u8(&rsp, status);
net_buf_simple_add_le16(&rsp, bt_mesh_dfu_slot_foreach(NULL, NULL));
net_buf_simple_add_le16(&rsp, bt_mesh_dfu_slot_count());
net_buf_simple_add_le16(&rsp, idx);
if (fwid) {
@ -522,7 +649,7 @@ static int handle_fw_get(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_dfd_srv *srv = mod->user_data;
const struct bt_mesh_dfu_slot *slot;
struct bt_mesh_dfu_slot *slot;
const uint8_t *fwid;
size_t fwid_len;
int idx;
@ -531,7 +658,7 @@ static int handle_fw_get(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
fwid = net_buf_simple_pull_mem(buf, fwid_len);
idx = bt_mesh_dfu_slot_get(fwid, fwid_len, &slot);
if (idx >= 0 && bt_mesh_dfu_slot_is_valid(slot)) {
if (idx >= 0) {
fw_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS, idx, fwid,
fwid_len);
} else {
@ -552,7 +679,7 @@ static int handle_fw_get_by_index(struct bt_mesh_model *mod, struct bt_mesh_msg_
idx = net_buf_simple_pull_le16(buf);
slot = bt_mesh_dfu_slot_at(idx);
if (slot && bt_mesh_dfu_slot_is_valid(slot)) {
if (slot) {
fw_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS, idx, slot->fwid,
slot->fwid_len);
} else {
@ -729,8 +856,7 @@ static void upload_end(struct bt_mesh_blob_srv *b, uint64_t id, bool success)
LOG_DBG("%u", success);
if (success) {
bt_mesh_dfu_slot_valid_set(srv->upload.slot, true);
if (success && (bt_mesh_dfu_slot_commit(srv->upload.slot) == 0)) {
srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS;
return;
}
@ -850,7 +976,7 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_start(struct bt_mesh_dfd_srv *srv,
xfer.mode = params->xfer_mode;
xfer.slot = bt_mesh_dfu_slot_at(params->slot_idx);
if (!xfer.slot || !bt_mesh_dfu_slot_is_valid(xfer.slot)) {
if (!xfer.slot) {
return BT_MESH_DFD_ERR_FW_NOT_FOUND;
}
@ -1013,7 +1139,7 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_apply(struct bt_mesh_dfd_srv *srv)
enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete(struct bt_mesh_dfd_srv *srv, size_t *fwid_len,
const uint8_t **fwid)
{
const struct bt_mesh_dfu_slot *slot;
struct bt_mesh_dfu_slot *slot;
int idx, err;
if (srv->phase != BT_MESH_DFD_PHASE_IDLE) {
@ -1023,7 +1149,7 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete(struct bt_mesh_dfd_srv *srv, s
}
idx = bt_mesh_dfu_slot_get(*fwid, *fwid_len, &slot);
if (idx < 0 || !bt_mesh_dfu_slot_is_valid(slot)) {
if (idx < 0) {
return BT_MESH_DFD_SUCCESS;
}
@ -1049,3 +1175,69 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete_all(struct bt_mesh_dfd_srv *sr
return BT_MESH_DFD_SUCCESS;
}
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
int bt_mesh_dfd_srv_oob_check_complete(struct bt_mesh_dfd_srv *srv,
const struct bt_mesh_dfu_slot *slot, int status,
uint8_t *fwid, size_t fwid_len)
{
int err;
if (slot != srv->upload.slot || !srv->upload.is_oob ||
srv->upload.phase == BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE) {
/* This should not happen, unless the application calls the function with a
* "wrong" pointer or at a wrong time.
*/
return -EINVAL;
}
if (status != BT_MESH_DFD_SUCCESS) {
bt_mesh_dfu_slot_release(srv->upload.slot);
upload_status_rsp(srv, &srv->upload.oob.ctx, status);
return -ECANCELED;
}
err = set_upload_fwid(srv, &srv->upload.oob.ctx, fwid, fwid_len);
if (err) {
return err;
}
upload_status_rsp(srv, &srv->upload.oob.ctx, BT_MESH_DFD_SUCCESS);
return 0;
}
int bt_mesh_dfd_srv_oob_store_complete(struct bt_mesh_dfd_srv *srv,
const struct bt_mesh_dfu_slot *slot, bool success,
size_t size, const uint8_t *metadata, size_t metadata_len)
{
int err = 0;
if (srv->upload.phase != BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE ||
srv->upload.slot != slot || !srv->upload.is_oob) {
return -EINVAL;
}
if (!success) {
goto error;
}
err = bt_mesh_dfu_slot_info_set(srv->upload.slot, size, metadata, metadata_len);
if (err) {
goto error;
}
err = bt_mesh_dfu_slot_commit(srv->upload.slot);
if (err) {
goto error;
}
srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS;
return 0;
error:
srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR;
bt_mesh_dfu_slot_release(srv->upload.slot);
return err;
}
#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */

View file

@ -20,23 +20,22 @@ LOG_MODULE_REGISTER(bt_mesh_dfu_slot);
#define DFU_SLOT_SETTINGS_PATH "bt/mesh-dfu/slot"
#define HEADER_SIZE offsetof(struct bt_mesh_dfu_slot, fwid)
#define HEADER_SIZE offsetof(struct slot, slot.fwid)
#define PROP_HEADER "h"
#define PROP_FWID "id"
#define PROP_METADATA "m"
#define PROP_URI "u"
#define VALID_SLOTS_TAG "v"
#define SLOT_IN_ARRAY(_slot) PART_OF_ARRAY(slots, CONTAINER_OF(_slot, struct slot, slot))
static ATOMIC_DEFINE(valid_slots, CONFIG_BT_MESH_DFU_SLOT_CNT);
static sys_slist_t list;
static struct slot {
sys_snode_t n;
uint32_t idx;
struct bt_mesh_dfu_slot slot;
sys_snode_t n;
} slots[CONFIG_BT_MESH_DFU_SLOT_CNT];
static uint32_t slot_index;
static char *slot_entry_encode(uint16_t idx, char buf[SLOT_ENTRY_BUFLEN],
const char *property)
{
@ -46,22 +45,6 @@ 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)
{
@ -69,9 +52,22 @@ static bool slot_eq(const struct bt_mesh_dfu_slot *slot,
!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 @@ static int slot_store(const struct slot *slot_to_store)
err = settings_save_one(slot_entry_encode(idx, buf,
PROP_METADATA),
slot_to_store->slot.metadata, slot_to_store->slot.metadata_len);
if (err) {
return err;
}
return settings_save_one(slot_entry_encode(idx, buf, PROP_URI),
slot_to_store->slot.uri, slot_to_store->slot.uri_len);
return err;
}
static void slot_erase(struct slot *slot_to_erase)
{
uint16_t idx = slot_to_erase - &slots[0];
uint16_t idx = ARRAY_INDEX(slots, slot_to_erase);
char buf[SLOT_ENTRY_BUFLEN];
settings_delete(slot_entry_encode(idx, buf, PROP_HEADER));
settings_delete(slot_entry_encode(idx, buf, PROP_FWID));
settings_delete(slot_entry_encode(idx, buf, PROP_METADATA));
settings_delete(slot_entry_encode(idx, buf, PROP_URI));
}
static int valid_slots_store(void)
static void slot_index_defrag(void)
{
return settings_save_one(DFU_SLOT_SETTINGS_PATH "/" VALID_SLOTS_TAG,
valid_slots, sizeof(valid_slots));
slot_index = 0;
struct slot *s;
SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
s->idx = ++slot_index;
slot_store(s);
}
}
const struct bt_mesh_dfu_slot *
bt_mesh_dfu_slot_add(size_t size, const uint8_t *fwid,
size_t fwid_len, const uint8_t *metadata,
size_t metadata_len, const char *uri, size_t uri_len)
int bt_mesh_dfu_slot_count(void)
{
struct slot *slot = NULL;
int err, i;
int cnt = 0;
sys_snode_t *n;
if (size == 0 || fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN ||
metadata_len > CONFIG_BT_MESH_DFU_METADATA_MAXLEN ||
uri_len > CONFIG_BT_MESH_DFU_URI_MAXLEN) {
LOG_WRN("Param too large: (size: %d, fwid: %d, metadata: %d, uri: %d)",
size, fwid_len, metadata_len, uri_len);
return NULL;
SYS_SLIST_FOR_EACH_NODE(&list, n) {
cnt++;
}
for (i = 0; i < ARRAY_SIZE(slots); ++i) {
if (!slot_in_use(&slots[i].slot)) {
slot = &slots[i];
continue;
}
return cnt;
}
if (slot_eq(&slots[i].slot, fwid, fwid_len)) {
return &slots[i].slot;
struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_reserve(void)
{
struct slot *slot = NULL;
for (int i = 0; i < ARRAY_SIZE(slots); ++i) {
if (slots[i].idx == 0) {
slot = &slots[i];
break;
}
}
@ -147,110 +139,136 @@ bt_mesh_dfu_slot_add(size_t size, const uint8_t *fwid,
return NULL;
}
slot->slot.fwid_len = fwid_len;
slot->slot.metadata_len = metadata_len;
slot->slot.uri_len = uri_len;
memcpy(slot->slot.fwid, fwid, fwid_len);
memcpy(slot->slot.metadata, metadata, metadata_len);
memcpy(slot->slot.uri, uri, uri_len);
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 @@ const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t idx)
return NULL;
}
int bt_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len,
const struct bt_mesh_dfu_slot **slot)
int bt_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len, struct bt_mesh_dfu_slot **slot)
{
struct slot *s;
int idx = 0;
SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
if (slot_eq(&s->slot, fwid, fwid_len)) {
*slot = &s->slot;
if (slot) {
*slot = &s->slot;
}
return idx;
}
idx++;
}
return -ENOENT;
}
int bt_mesh_dfu_slot_idx_get(const struct bt_mesh_dfu_slot *slot)
int bt_mesh_dfu_slot_img_idx_get(const struct bt_mesh_dfu_slot *dfu_slot)
{
struct slot *s;
int idx = 0;
SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
if (&s->slot == slot) {
if (&s->slot == dfu_slot) {
return idx;
}
idx++;
}
@ -295,11 +312,12 @@ int bt_mesh_dfu_slot_idx_get(const struct bt_mesh_dfu_slot *slot)
size_t bt_mesh_dfu_slot_foreach(bt_mesh_dfu_slot_cb_t cb, void *user_data)
{
enum bt_mesh_dfu_iter iter;
struct slot *s;
size_t cnt = 0;
struct slot *s;
SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
cnt++;
if (!cb) {
continue;
}
@ -320,15 +338,6 @@ static int slot_data_load(const char *key, size_t len_rd,
size_t len;
uint16_t idx;
if (!strncmp(key, VALID_SLOTS_TAG, 1)) {
if (read_cb(cb_arg, valid_slots,
MIN(sizeof(valid_slots), len_rd)) < 0) {
return -EINVAL;
}
return 0;
}
idx = strtol(key, NULL, 16);
if (idx >= ARRAY_SIZE(slots)) {
@ -339,16 +348,34 @@ static int slot_data_load(const char *key, size_t len_rd,
if (!strncmp(prop, PROP_HEADER, len)) {
if (read_cb(cb_arg, &slots[idx], HEADER_SIZE) > 0) {
sys_slist_append(&list, &slots[idx].n);
}
struct slot *s, *prev = NULL;
SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) {
if (s->idx > slots[idx].idx) {
break;
}
prev = s;
}
if (prev == NULL) {
sys_slist_prepend(&list, &slots[idx].n);
} else {
sys_slist_insert(&list, &prev->n, &slots[idx].n);
}
if (slots[idx].idx >= slot_index) {
slot_index = slots[idx].idx + 1;
}
}
return 0;
}
if (!strncmp(prop, PROP_FWID, len)) {
if (read_cb(cb_arg, &slots[idx].slot.fwid,
sizeof(slots[idx].slot.fwid)) < 0) {
slot_invalidate(&slots[idx]);
slots[idx].idx = 0;
sys_slist_find_and_remove(&list, &slots[idx].n);
return 0;
}
@ -359,7 +386,8 @@ static int slot_data_load(const char *key, size_t len_rd,
if (!strncmp(prop, PROP_METADATA, len)) {
if (read_cb(cb_arg, &slots[idx].slot.metadata,
sizeof(slots[idx].slot.metadata)) < 0) {
slot_invalidate(&slots[idx]);
slots[idx].idx = 0;
sys_slist_find_and_remove(&list, &slots[idx].n);
return 0;
}
@ -367,16 +395,6 @@ static int slot_data_load(const char *key, size_t len_rd,
return 0;
}
if (!strncmp(prop, PROP_URI, len)) {
if (read_cb(cb_arg, &slots[idx].slot.uri,
sizeof(slots[idx].slot.uri)) < 0) {
slot_invalidate(&slots[idx]);
return 0;
}
slots[idx].slot.uri_len = len_rd;
}
return 0;
}

View file

@ -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 @@ int bt_mesh_dfu_slot_del(const struct bt_mesh_dfu_slot *slot);
*
* @return 0 on success, or (negative) error code on failure.
*/
int bt_mesh_dfu_slot_del_all(void);
void bt_mesh_dfu_slot_del_all(void);
/** @brief Get the DFU image slot at the given index.
/** @brief Get the DFU image slot at the given firmware image list index.
*
* @param idx DFU image slot index.
*
* @return The DFU image slot at the given index, or NULL if no slot exists with the
* given index.
*/
const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t idx);
const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t img_idx);
/** @brief Get the DFU image slot for the image with the given firmware ID.
/** @brief Get the committed DFU image slot for the image with the given
* firmware ID.
*
* @param fwid Firmware ID.
* @param fwid_len Firmware ID length.
@ -91,16 +111,15 @@ const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t idx);
*
* @return Slot index on success, or negative error code on failure.
*/
int bt_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len,
const struct bt_mesh_dfu_slot **slot);
int bt_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len, struct bt_mesh_dfu_slot **slot);
/** @brief Get the DFU image slot index of the given slot.
/** @brief Get the index in the firmware image list for the given slot.
*
* @param slot Slot to find.
*
* @return Slot index on success, or negative error code on failure.
*/
int bt_mesh_dfu_slot_idx_get(const struct bt_mesh_dfu_slot *slot);
int bt_mesh_dfu_slot_img_idx_get(const struct bt_mesh_dfu_slot *slot);
/** @brief Iterate through all DFU image slots.
*

View file

@ -43,7 +43,7 @@ static void print_fw_status(const struct shell *sh, enum bt_mesh_dfd_status stat
uint16_t idx, const uint8_t *fwid, size_t fwid_len)
{
shell_fprintf(sh, SHELL_NORMAL, "{ \"status\": %d, \"slot_cnt\": %d, \"idx\": %d",
status, bt_mesh_dfu_slot_foreach(NULL, NULL), idx);
status, bt_mesh_dfu_slot_count(), idx);
if (fwid) {
shell_fprintf(sh, SHELL_NORMAL, ", \"fwid\": \"");
for (size_t i = 0; i < fwid_len; i++) {
@ -325,10 +325,9 @@ static int cmd_dfd_fw_get(const struct shell *sh, size_t argc, char *argv[])
return -EINVAL;
}
const struct bt_mesh_dfu_slot *slot;
int idx = bt_mesh_dfu_slot_get(fwid, fwid_len, &slot);
int idx = bt_mesh_dfu_slot_get(fwid, fwid_len, NULL);
if (idx >= 0 && bt_mesh_dfu_slot_is_valid(slot)) {
if (idx >= 0) {
print_fw_status(sh, BT_MESH_DFD_SUCCESS, idx, fwid, fwid_len);
} else {
print_fw_status(sh, BT_MESH_DFD_ERR_FW_NOT_FOUND, 0xffff, fwid, fwid_len);
@ -349,7 +348,7 @@ static int cmd_dfd_fw_get_by_idx(const struct shell *sh, size_t argc, char *argv
return err;
}
if (slot && bt_mesh_dfu_slot_is_valid(slot)) {
if (slot) {
print_fw_status(sh, BT_MESH_DFD_SUCCESS, idx, slot->fwid, slot->fwid_len);
} else {
print_fw_status(sh, BT_MESH_DFD_ERR_FW_NOT_FOUND, idx, NULL, 0);

View file

@ -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);
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(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,
"<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),

View file

@ -58,7 +58,8 @@ CONFIG_BT_MESH_LARGE_COMP_DATA_SRV=y
CONFIG_BT_MESH_DFU_SRV=y
CONFIG_BT_MESH_DFU_CLI=y
CONFIG_BT_MESH_DFD_SRV=y
CONFIG_BT_MESH_DFU_SLOT_CNT=3
CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD=y
CONFIG_BT_MESH_DFU_SLOT_CNT=4
CONFIG_BT_MESH_PRIV_BEACON_SRV=y
CONFIG_BT_MESH_PRIV_BEACON_CLI=y
CONFIG_BT_MESH_OD_PRIV_PROXY_SRV=y

View file

@ -434,25 +434,50 @@ static void target_prov_and_conf_default(void)
target_prov_and_conf(addr, bind_params, ARRAY_SIZE(bind_params));
}
static struct bt_mesh_dfu_slot *slot_reserve_and_set(size_t size, uint8_t *fwid, size_t fwid_len,
uint8_t *metadata, size_t metadata_len)
{
struct bt_mesh_dfu_slot *new_slot = bt_mesh_dfu_slot_reserve();
if (!new_slot) {
LOG_WRN("Reserving slot failed");
return NULL;
}
int err = bt_mesh_dfu_slot_fwid_set(new_slot, fwid, fwid_len);
if (err) {
return NULL;
}
err = bt_mesh_dfu_slot_info_set(new_slot, size, metadata, metadata_len);
if (err) {
return NULL;
}
return new_slot;
}
static bool slot_add(const struct bt_mesh_dfu_slot **slot)
{
const struct bt_mesh_dfu_slot *new_slot;
struct bt_mesh_dfu_slot *new_slot;
size_t size = 100;
uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN] = { 0xAA, 0xBB, 0xCC, 0xDD };
size_t fwid_len = 4;
uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN] = { 0xAA, 0xBB, 0xCC, 0xDD };
size_t metadata_len = 4;
const char *uri = "";
ASSERT_EQUAL(sizeof(target_fw_ver_new), fwid_len);
new_slot = bt_mesh_dfu_slot_add(size, fwid, fwid_len, metadata, metadata_len, uri,
strlen(uri));
new_slot = slot_reserve_and_set(size, fwid, fwid_len, metadata, metadata_len);
if (!new_slot) {
return false;
}
bt_mesh_dfu_slot_valid_set(new_slot, true);
if (bt_mesh_dfu_slot_commit(new_slot) != 0) {
return false;
}
if (slot) {
*slot = new_slot;
@ -568,13 +593,12 @@ static void test_dist_dfu_self_update(void)
static void test_dist_dfu_slot_create(void)
{
const struct bt_mesh_dfu_slot *slot[3];
struct bt_mesh_dfu_slot *slot[CONFIG_BT_MESH_DFU_SLOT_CNT];
size_t size = 100;
uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN] = { 0 };
size_t fwid_len = 4;
uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN] = { 0 };
size_t metadata_len = 4;
const char *uri = "test";
int err, i;
ASSERT_TRUE(CONFIG_BT_MESH_DFU_SLOT_CNT >= 3,
@ -584,36 +608,26 @@ static void test_dist_dfu_slot_create(void)
bt_mesh_device_setup(&prov, &dist_comp);
dist_prov_and_conf(DIST_ADDR);
for (i = CONFIG_BT_MESH_DFU_SLOT_CNT - 1; i >= 0; i--) {
for (i = 0; i < CONFIG_BT_MESH_DFU_SLOT_CNT; i++) {
fwid[0] = i;
metadata[0] = i;
slot[i] = bt_mesh_dfu_slot_add(size, fwid, fwid_len, metadata, metadata_len, uri,
strlen(uri));
slot[i] = slot_reserve_and_set(size, fwid, fwid_len, metadata, metadata_len);
ASSERT_FALSE(slot[i] == NULL, "Failed to add slot");
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 @@ enum bt_mesh_dfu_iter check_slot(const struct bt_mesh_dfu_slot *slot, void *data
size_t fwid_len = 4;
uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN] = { 0 };
size_t metadata_len = 4;
const char *uri = "test";
int idx = bt_mesh_dfu_slot_idx_get(slot);
ASSERT_TRUE(idx >= 0, "Failed to retrieve slot index");
int idx = bt_mesh_dfu_slot_img_idx_get(slot);
int *i = data;
ASSERT_EQUAL(idx, (*i)++);
ASSERT_EQUAL(size, slot->size);
ASSERT_TRUE(strcmp(uri, slot->uri) == 0);
fwid[0] = idx;
fwid[0] = idx + 2;
ASSERT_EQUAL(fwid_len, slot->fwid_len);
ASSERT_TRUE(memcmp(fwid, slot->fwid, fwid_len) == 0);
metadata[0] = idx;
metadata[0] = idx + 2;
ASSERT_EQUAL(metadata_len, slot->metadata_len);
ASSERT_TRUE(memcmp(metadata, slot->metadata, metadata_len) == 0);
@ -649,13 +660,12 @@ enum bt_mesh_dfu_iter check_slot(const struct bt_mesh_dfu_slot *slot, void *data
static void test_dist_dfu_slot_create_recover(void)
{
size_t slot_count;
const struct bt_mesh_dfu_slot *slot;
struct bt_mesh_dfu_slot *slot;
size_t size = 100;
uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN] = { 0 };
size_t fwid_len = 4;
uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN] = { 0 };
size_t metadata_len = 4;
const char *uri = "test";
int i, idx;
ASSERT_TRUE(CONFIG_BT_MESH_DFU_SLOT_CNT >= 3,
@ -664,26 +674,17 @@ static void test_dist_dfu_slot_create_recover(void)
bt_mesh_test_cfg_set(NULL, WAIT_TIME);
bt_mesh_device_setup(&prov, &dist_comp);
slot_count = bt_mesh_dfu_slot_foreach(check_slot, NULL);
ASSERT_EQUAL(CONFIG_BT_MESH_DFU_SLOT_CNT - 1, slot_count);
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 test_dist_dfu_slot_create_recover(void)
static void check_delete_all(void)
{
int i, idx, err;
int i;
const struct bt_mesh_dfu_slot *slot;
size_t slot_count;
@ -706,14 +707,6 @@ static void check_delete_all(void)
for (i = 0; i < CONFIG_BT_MESH_DFU_SLOT_CNT - 1; i++) {
slot = bt_mesh_dfu_slot_at(i);
ASSERT_TRUE(slot == NULL);
idx = bt_mesh_dfu_slot_idx_get(slot);
ASSERT_TRUE(idx < 0);
err = bt_mesh_dfu_slot_valid_set(slot, true);
ASSERT_EQUAL(err, -ENOENT);
ASSERT_TRUE(!bt_mesh_dfu_slot_is_valid(slot));
}
}
@ -726,7 +719,6 @@ static void test_dist_dfu_slot_delete_all(void)
bt_mesh_device_setup(&prov, &dist_comp);
bt_mesh_dfu_slot_del_all();
check_delete_all();
PASS();
@ -742,6 +734,63 @@ static void test_dist_dfu_slot_check_delete_all(void)
PASS();
}
static void test_dist_dfu_slot_reservation(void)
{
int i;
struct bt_mesh_dfu_slot *slots[CONFIG_BT_MESH_DFU_SLOT_CNT];
bt_mesh_test_cfg_set(NULL, WAIT_TIME);
bt_mesh_device_setup(&prov, &dist_comp);
for (i = 0; i < CONFIG_BT_MESH_DFU_SLOT_CNT; i++) {
slots[i] = bt_mesh_dfu_slot_reserve();
ASSERT_TRUE(slots[i] != NULL);
}
ASSERT_EQUAL(NULL, bt_mesh_dfu_slot_reserve());
bt_mesh_dfu_slot_release(slots[0]);
/* Release twice to check idempotency with empty pool */
bt_mesh_dfu_slot_release(slots[0]);
ASSERT_TRUE(bt_mesh_dfu_slot_reserve() != NULL);
ASSERT_EQUAL(NULL, bt_mesh_dfu_slot_reserve());
PASS();
}
static void test_dist_dfu_slot_idempotency(void)
{
uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN] = { 0 };
size_t fwid_len = 4;
struct bt_mesh_dfu_slot *slot;
ASSERT_TRUE(CONFIG_BT_MESH_DFU_SLOT_CNT >= 1,
"CONFIG_BT_MESH_DFU_SLOT_CNT must be at least 1");
bt_mesh_test_cfg_set(NULL, WAIT_TIME);
bt_mesh_device_setup(&prov, &dist_comp);
dist_prov_and_conf(DIST_ADDR);
slot = bt_mesh_dfu_slot_reserve();
ASSERT_TRUE(slot != NULL);
bt_mesh_dfu_slot_release(slot);
bt_mesh_dfu_slot_release(slot);
slot = bt_mesh_dfu_slot_reserve();
ASSERT_TRUE(slot != NULL);
ASSERT_EQUAL(0, bt_mesh_dfu_slot_fwid_set(slot, fwid, fwid_len));
ASSERT_EQUAL(0, bt_mesh_dfu_slot_info_set(slot, 100, NULL, 0));
ASSERT_EQUAL(0, bt_mesh_dfu_slot_commit(slot));
ASSERT_EQUAL(-EINVAL, bt_mesh_dfu_slot_commit(slot));
ASSERT_EQUAL(0, bt_mesh_dfu_slot_del(slot));
ASSERT_EQUAL(-EINVAL, bt_mesh_dfu_slot_del(slot));
PASS();
}
static void target_test_effect(enum bt_mesh_dfu_effect effect)
{
dfu_target_effect = effect;
@ -921,12 +970,14 @@ static void cli_common_fail_on_init(void)
static void cli_common_init_recover(void)
{
const struct bt_mesh_dfu_slot *slot;
struct bt_mesh_dfu_slot *slot;
uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN] = { 0xAA, 0xBB, 0xCC, 0xDD };
size_t fwid_len = 4;
bt_mesh_test_cfg_set(NULL, 300);
bt_mesh_device_setup(&prov, &cli_comp);
ASSERT_TRUE(slot_add(&slot));
ASSERT_TRUE(bt_mesh_dfu_slot_get(fwid, fwid_len, &slot) >= 0);
dfu_cli_inputs_prepare(0);
dfu_cli_xfer.xfer.mode = BT_MESH_BLOB_XFER_MODE_PUSH;
@ -1595,6 +1646,10 @@ static const struct bst_test_instance test_dfu[] = {
TEST_CASE(dist, dfu_slot_delete_all, "Distributor deletes all image slots"),
TEST_CASE(dist, dfu_slot_check_delete_all,
"Distributor checks if all slots are removed from persistent storage"),
TEST_CASE(dist, dfu_slot_reservation,
"Distributor checks that the correct number of slots can be reserved"),
TEST_CASE(dist, dfu_slot_idempotency,
"Distributor checks that the the DFU slot APIs are idempotent"),
TEST_CASE(cli, stop, "DFU Client stops at configured point of Firmware Distribution"),
TEST_CASE(cli, fail_on_persistency, "DFU Client doesn't give up DFU Transfer"),
TEST_CASE(cli, all_targets_lost_on_metadata,

View file

@ -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

View file

@ -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