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

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