zephyr/subsys/bluetooth/mesh/dfd_srv.c
alperen sener e653a39ed7 Bluetooth: Mesh: Check that required models exists on the same element
Referring to MshDFU_v1.0 Sections 6.1.1, 6.2.1 and  7.1.1 model
descriptions: DFU/DFD server/clients extend BLOB Transfer root models
and DFD server requires Firmware Update Client on the same element. For
this reason we need to make sure that those main models or root models
exist on the same element. And also firmware update client can not be
forced to be in the first element.

For all model extention call return the error code in case of an error.

Signed-off-by: alperen sener <alperen.sener@nordicsemi.no>
2024-12-04 09:23:44 +01:00

1329 lines
36 KiB
C

/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <zephyr/bluetooth/mesh.h>
#include <common/bt_str.h>
#include "dfu_slot.h"
#include "dfd.h"
#include "dfu.h"
#include "dfd_srv_internal.h"
#include "net.h"
#include "transport.h"
#define LOG_LEVEL CONFIG_BT_MESH_DFU_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_mesh_dfd_srv);
#define ERASE_BLOCK_SIZE DT_PROP(DT_CHOSEN(zephyr_flash), erase_block_size)
#define DFD_UPLOAD_STATUS_MSG_MAXLEN (5 + CONFIG_BT_MESH_DFU_FWID_MAXLEN)
BUILD_ASSERT((DFD_UPLOAD_STATUS_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_UPLOAD_STATUS) +
BT_MESH_MIC_SHORT) <= BT_MESH_TX_SDU_MAX,
"The Firmware Distribution Upload Status message does not fit into the maximum "
"outgoing SDU size.");
#define DFD_UPLOAD_START_MSG_MAXLEN (16 + CONFIG_BT_MESH_DFU_FWID_MAXLEN + \
CONFIG_BT_MESH_DFU_METADATA_MAXLEN)
BUILD_ASSERT((DFD_UPLOAD_START_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_UPLOAD_START) +
BT_MESH_MIC_SHORT) <= BT_MESH_RX_SDU_MAX,
"The Firmware Distribution Upload Start message does not fit into the maximum "
"incoming SDU size.");
#define DFD_RECEIVERS_LIST_MSG_MAXLEN (4 + CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX * 5)
BUILD_ASSERT((DFD_RECEIVERS_LIST_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_RECEIVERS_LIST) +
BT_MESH_MIC_SHORT) <= BT_MESH_TX_SDU_MAX,
"The Firmware Distribution Receivers List message does not fit into the maximum "
"outgoing SDU size.");
#define DFD_RECEIVERS_ADD_MSG_MAXLEN (CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX * 3)
BUILD_ASSERT((DFD_RECEIVERS_ADD_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_RECEIVERS_ADD) +
BT_MESH_MIC_SHORT) <= BT_MESH_RX_SDU_MAX,
"The Firmware Distribution Receivers Add message does not fit into the maximum "
"incoming SDU size.");
struct slot_search_ctx {
off_t offset;
size_t size;
bool failed;
};
static void dfd_phase_set(struct bt_mesh_dfd_srv *srv,
enum bt_mesh_dfd_phase new_phase)
{
srv->phase = new_phase;
if (srv->cb && srv->cb->phase) {
srv->cb->phase(srv, srv->phase);
}
}
static struct bt_mesh_dfu_target *target_get(struct bt_mesh_dfd_srv *srv,
uint16_t addr)
{
for (int i = 0; i < srv->target_cnt; ++i) {
if (addr == srv->targets[i].blob.addr) {
return &srv->targets[i];
}
}
return NULL;
}
static bool is_busy(const struct bt_mesh_dfd_srv *srv)
{
return srv->phase == BT_MESH_DFD_PHASE_TRANSFER_ACTIVE ||
srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUCCESS ||
srv->phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE;
}
static bool upload_is_busy(const struct bt_mesh_dfd_srv *srv)
{
return bt_mesh_blob_srv_is_busy(&srv->upload.blob) ||
srv->upload.phase == BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE;
}
static int slot_del(struct bt_mesh_dfd_srv *srv, const struct bt_mesh_dfu_slot *slot)
{
if (srv->cb && srv->cb->del) {
srv->cb->del(srv, slot);
}
return bt_mesh_dfu_slot_del(slot);
}
static void receivers_status_rsp(struct bt_mesh_dfd_srv *srv,
struct bt_mesh_msg_ctx *ctx,
enum bt_mesh_dfd_status status)
{
BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFD_OP_RECEIVERS_STATUS, 3);
bt_mesh_model_msg_init(&buf, BT_MESH_DFD_OP_RECEIVERS_STATUS);
net_buf_simple_add_u8(&buf, status);
net_buf_simple_add_le16(&buf, srv->target_cnt);
bt_mesh_model_send(srv->mod, ctx, &buf, NULL, NULL);
}
static int handle_receivers_add(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
enum bt_mesh_dfd_status status = BT_MESH_DFD_SUCCESS;
struct bt_mesh_dfd_srv *srv = mod->rt->user_data;
if (buf->len % 3) {
return -EINVAL;
}
if (bt_mesh_dfu_cli_is_busy(&srv->dfu)) {
receivers_status_rsp(srv, ctx,
BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION);
return 0;
}
while (buf->len >= 3 && status == BT_MESH_DFD_SUCCESS) {
uint8_t img_idx;
uint16_t addr;
addr = net_buf_simple_pull_le16(buf);
img_idx = net_buf_simple_pull_u8(buf);
status = bt_mesh_dfd_srv_receiver_add(srv, addr, img_idx);
}
receivers_status_rsp(srv, ctx, status);
return 0;
}
static int handle_receivers_delete_all(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_dfd_srv *srv = mod->rt->user_data;
receivers_status_rsp(srv, ctx, bt_mesh_dfd_srv_receivers_delete_all(srv));
return 0;
}
static int handle_receivers_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_dfd_srv *srv = mod->rt->user_data;
uint16_t first, cnt;
uint8_t progress;
int i;
first = net_buf_simple_pull_le16(buf);
cnt = net_buf_simple_pull_le16(buf);
if (cnt == 0) {
return -EINVAL;
}
/* Create a buffer that can fit the full target list, maxing out at TX_SDU_MAX: */
NET_BUF_SIMPLE_DEFINE(
rsp, BT_MESH_MODEL_BUF_LEN(BT_MESH_DFD_OP_RECEIVERS_LIST,
DFD_RECEIVERS_LIST_MSG_MAXLEN));
bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_RECEIVERS_LIST);
net_buf_simple_add_le16(&rsp, srv->target_cnt);
net_buf_simple_add_le16(&rsp, first);
cnt = MIN(cnt, srv->target_cnt - first);
progress = bt_mesh_dfu_cli_progress(&srv->dfu) / 2;
for (i = 0; i < cnt && net_buf_simple_tailroom(&rsp) >= 5 + BT_MESH_MIC_SHORT; i++) {
const struct bt_mesh_dfu_target *t = &srv->targets[i + first];
net_buf_simple_add_le32(
&rsp, ((t->blob.addr & BIT_MASK(15)) |
((t->phase & BIT_MASK(4)) << 15U) |
((t->status & BIT_MASK(3)) << 19U) |
((t->blob.status & BIT_MASK(4)) << 22U) |
((progress & BIT_MASK(6)) << 26U)));
net_buf_simple_add_u8(&rsp, t->img_idx);
}
bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL);
return 0;
}
static enum bt_mesh_dfu_iter slot_space_cb(const struct bt_mesh_dfu_slot *slot,
void *user_data)
{
size_t *total = user_data;
*total += slot->size;
return BT_MESH_DFU_ITER_CONTINUE;
}
static int handle_capabilities_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
size_t size = 0;
BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_CAPABILITIES_STATUS, 17);
bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_CAPABILITIES_STATUS);
net_buf_simple_add_le16(&rsp, CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX);
net_buf_simple_add_le16(&rsp, CONFIG_BT_MESH_DFU_SLOT_CNT);
net_buf_simple_add_le32(&rsp, CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE);
net_buf_simple_add_le32(&rsp, CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE);
/* Remaining size */
(void)bt_mesh_dfu_slot_foreach(slot_space_cb, &size);
size = MIN(size, CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE);
net_buf_simple_add_le32(&rsp, CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE - size);
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
struct bt_mesh_dfd_srv *srv = mod->rt->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
#endif
{
net_buf_simple_add_u8(&rsp, 0);
}
bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL);
return 0;
}
static void status_rsp(struct bt_mesh_dfd_srv *srv, struct bt_mesh_msg_ctx *ctx,
enum bt_mesh_dfd_status status)
{
BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_STATUS, 12);
bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_STATUS);
net_buf_simple_add_u8(&rsp, status);
net_buf_simple_add_u8(&rsp, srv->phase);
if (srv->phase == BT_MESH_DFD_PHASE_IDLE || !srv->dfu.xfer.slot) {
bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL);
return;
}
net_buf_simple_add_le16(&rsp, srv->inputs.group);
net_buf_simple_add_le16(&rsp, srv->inputs.app_idx);
net_buf_simple_add_u8(&rsp, srv->inputs.ttl);
net_buf_simple_add_le16(&rsp, srv->inputs.timeout_base);
net_buf_simple_add_u8(&rsp, ((srv->dfu.xfer.blob.mode & BIT_MASK(2)) |
((srv->apply & BIT_MASK(1)) << 2)));
net_buf_simple_add_le16(&rsp, srv->slot_idx);
bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL);
}
static int handle_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_dfd_srv *srv = mod->rt->user_data;
status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS);
return 0;
}
static int handle_start(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_dfd_srv *srv = mod->rt->user_data;
struct bt_mesh_dfd_start_params params;
uint8_t byte;
params.app_idx = net_buf_simple_pull_le16(buf);
params.ttl = net_buf_simple_pull_u8(buf);
params.timeout_base = net_buf_simple_pull_le16(buf);
byte = net_buf_simple_pull_u8(buf);
params.xfer_mode = byte & BIT_MASK(2);
params.apply = (byte >> 2U) & BIT_MASK(1);
params.slot_idx = net_buf_simple_pull_le16(buf);
if (buf->len == 16) {
/* TODO: Virtual addresses not supported. */
status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL);
return 0;
}
if (buf->len != 2) {
return -EINVAL;
}
params.group = net_buf_simple_pull_le16(buf);
status_rsp(srv, ctx, bt_mesh_dfd_srv_start(srv, &params));
return 0;
}
static int handle_suspend(const struct bt_mesh_model *mod,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_dfd_srv *srv = mod->rt->user_data;
status_rsp(srv, ctx, bt_mesh_dfd_srv_suspend(srv));
return 0;
}
static int handle_cancel(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_dfd_srv *srv = mod->rt->user_data;
bt_mesh_dfd_srv_cancel(srv, ctx);
return 0;
}
static int handle_apply(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_dfd_srv *srv = mod->rt->user_data;
status_rsp(srv, ctx, bt_mesh_dfd_srv_apply(srv));
return 0;
}
static void upload_status_rsp_with_progress(struct bt_mesh_dfd_srv *srv,
struct bt_mesh_msg_ctx *ctx,
enum bt_mesh_dfd_status status,
uint8_t progress)
{
BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_UPLOAD_STATUS,
DFD_UPLOAD_STATUS_MSG_MAXLEN);
bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_UPLOAD_STATUS);
net_buf_simple_add_u8(&rsp, status);
net_buf_simple_add_u8(&rsp, srv->upload.phase);
if (srv->upload.phase == BT_MESH_DFD_UPLOAD_PHASE_IDLE ||
!srv->upload.slot) {
bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL);
return;
}
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
if (srv->upload.is_oob) {
net_buf_simple_add_u8(&rsp, progress | 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, progress);
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);
}
static void upload_status_rsp(struct bt_mesh_dfd_srv *srv,
struct bt_mesh_msg_ctx *ctx,
enum bt_mesh_dfd_status status)
{
uint8_t progress;
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
if (srv->upload.is_oob) {
progress = srv->cb->oob_progress_get(srv, srv->upload.slot);
} else
#endif
{
progress = bt_mesh_blob_srv_progress(&srv->upload.blob);
}
upload_status_rsp_with_progress(srv, ctx, status, progress);
}
static int handle_upload_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_dfd_srv *srv = mod->rt->user_data;
upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS);
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);
srv->upload.slot = NULL;
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);
err = bt_mesh_dfu_slot_get(fwid, fwid_len, &srv->upload.slot);
if (!err) {
upload_status_rsp_with_progress(srv, ctx, BT_MESH_DFD_SUCCESS, 100);
} else {
srv->upload.slot = NULL;
upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL);
}
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(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_dfd_srv *srv = mod->rt->user_data;
size_t meta_len, fwid_len, size;
const uint8_t *meta, *fwid;
uint16_t timeout_base;
uint64_t blob_id;
int err;
uint8_t ttl;
ttl = net_buf_simple_pull_u8(buf);
timeout_base = net_buf_simple_pull_le16(buf);
blob_id = net_buf_simple_pull_le64(buf);
size = net_buf_simple_pull_le32(buf);
meta_len = net_buf_simple_pull_u8(buf);
if (buf->len < meta_len) {
return -EINVAL;
}
meta = net_buf_simple_pull_mem(buf, meta_len);
fwid_len = buf->len;
fwid = net_buf_simple_pull_mem(buf, fwid_len);
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) {
upload_status_rsp(srv, ctx,
BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES);
return 0;
}
if (upload_is_busy(srv)) {
if (!srv->upload.slot) {
LOG_WRN("Busy with no upload slot");
upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL);
return 0;
}
if (srv->upload.slot->fwid_len == fwid_len &&
!memcmp(srv->upload.slot->fwid, fwid, fwid_len) &&
srv->upload.slot->metadata_len == meta_len &&
!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
#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;
}
LOG_WRN("Upload already in progress");
upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_BUSY_WITH_UPLOAD);
return 0;
}
/* This will be a no-op if the slot state isn't RESERVED, which is
* what we want.
*/
if (srv->upload.slot) {
bt_mesh_dfu_slot_release(srv->upload.slot);
}
#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD
srv->upload.is_oob = false;
#endif
srv->upload.slot = bt_mesh_dfu_slot_reserve();
if (!srv->upload.slot) {
LOG_WRN("No space for slot");
upload_status_rsp(srv, ctx,
BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES);
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);
bt_mesh_dfu_slot_release(srv->upload.slot);
upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL);
return 0;
}
err = bt_mesh_blob_srv_recv(&srv->upload.blob, blob_id, srv->io, ttl,
timeout_base);
if (err) {
LOG_ERR("BLOB Server rejected upload (err: %d)", err);
bt_mesh_dfu_slot_release(srv->upload.slot);
upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL);
return 0;
}
LOG_DBG("%s", bt_hex(fwid, fwid_len));
srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE;
upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS);
return 0;
}
static int handle_upload_start_oob(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_dfd_srv *srv = mod->rt->user_data;
uint8_t uri_len;
uint8_t *uri;
uint16_t fwid_len;
uint8_t *fwid;
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);
LOG_DBG("Upload OOB Start");
LOG_HEXDUMP_DBG(uri, uri_len, "URI");
LOG_HEXDUMP_DBG(fwid, fwid_len, "FWID");
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
} else if (srv->upload.is_oob && srv->upload.is_pending_oob_check) {
/* Ignore the request if we didn't confirm the previous one. */
return 0;
#endif
}
#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;
}
/* This will be a no-op if the slot state isn't RESERVED, which is
* what we want.
*/
if (srv->upload.slot) {
bt_mesh_dfu_slot_release(srv->upload.slot);
}
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 {
srv->upload.is_pending_oob_check = true;
}
#else
upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_URI_NOT_SUPPORTED);
#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */
return 0;
}
static int handle_upload_cancel(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_dfd_srv *srv = mod->rt->user_data;
srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_IDLE;
#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;
}
static void fw_status_rsp(struct bt_mesh_dfd_srv *srv,
struct bt_mesh_msg_ctx *ctx,
enum bt_mesh_dfd_status status, uint16_t idx,
const uint8_t *fwid, size_t fwid_len)
{
BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_FW_STATUS,
7 + CONFIG_BT_MESH_DFU_FWID_MAXLEN);
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_count());
net_buf_simple_add_le16(&rsp, idx);
if (fwid) {
net_buf_simple_add_mem(&rsp, fwid, fwid_len);
}
bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL);
}
static int handle_fw_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_dfd_srv *srv = mod->rt->user_data;
struct bt_mesh_dfu_slot *slot;
const uint8_t *fwid;
size_t fwid_len;
int idx;
fwid_len = buf->len;
fwid = net_buf_simple_pull_mem(buf, fwid_len);
idx = bt_mesh_dfu_slot_get(fwid, fwid_len, &slot);
if (idx >= 0) {
fw_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS, idx, fwid,
fwid_len);
} else {
fw_status_rsp(srv, ctx, BT_MESH_DFD_ERR_FW_NOT_FOUND, 0xffff,
fwid, fwid_len);
}
return 0;
}
static int handle_fw_get_by_index(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_dfd_srv *srv = mod->rt->user_data;
const struct bt_mesh_dfu_slot *slot;
uint16_t idx;
idx = net_buf_simple_pull_le16(buf);
slot = bt_mesh_dfu_slot_at(idx);
if (slot) {
fw_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS, idx, slot->fwid,
slot->fwid_len);
} else {
fw_status_rsp(srv, ctx, BT_MESH_DFD_ERR_FW_NOT_FOUND, idx,
NULL, 0);
}
return 0;
}
static int handle_fw_delete(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_dfd_srv *srv = mod->rt->user_data;
const uint8_t *fwid;
size_t fwid_len;
fwid_len = buf->len;
fwid = net_buf_simple_pull_mem(buf, fwid_len);
enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_fw_delete(srv, &fwid_len, &fwid);
fw_status_rsp(srv, ctx, status, 0xffff, fwid, fwid_len);
return 0;
}
static enum bt_mesh_dfu_iter slot_del_cb(const struct bt_mesh_dfu_slot *slot,
void *user_data)
{
struct bt_mesh_dfd_srv *srv = user_data;
if (srv->cb && srv->cb->del) {
srv->cb->del(srv, slot);
}
return BT_MESH_DFU_ITER_CONTINUE;
}
static int handle_fw_delete_all(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_dfd_srv *srv = mod->rt->user_data;
fw_status_rsp(srv, ctx, bt_mesh_dfd_srv_fw_delete_all(srv), 0xffff, NULL, 0);
return 0;
}
const struct bt_mesh_model_op _bt_mesh_dfd_srv_op[] = {
{ BT_MESH_DFD_OP_RECEIVERS_ADD, BT_MESH_LEN_MIN(3), handle_receivers_add },
{ BT_MESH_DFD_OP_RECEIVERS_DELETE_ALL, BT_MESH_LEN_EXACT(0), handle_receivers_delete_all },
{ BT_MESH_DFD_OP_RECEIVERS_GET, BT_MESH_LEN_EXACT(4), handle_receivers_get },
{ BT_MESH_DFD_OP_CAPABILITIES_GET, BT_MESH_LEN_EXACT(0), handle_capabilities_get },
{ BT_MESH_DFD_OP_GET, BT_MESH_LEN_EXACT(0), handle_get },
{ BT_MESH_DFD_OP_START, BT_MESH_LEN_MIN(10), handle_start },
{ BT_MESH_DFD_OP_SUSPEND, BT_MESH_LEN_EXACT(0), handle_suspend },
{ BT_MESH_DFD_OP_CANCEL, BT_MESH_LEN_EXACT(0), handle_cancel },
{ BT_MESH_DFD_OP_APPLY, BT_MESH_LEN_EXACT(0), handle_apply },
{ BT_MESH_DFD_OP_UPLOAD_GET, BT_MESH_LEN_EXACT(0), handle_upload_get },
{ BT_MESH_DFD_OP_UPLOAD_START, BT_MESH_LEN_MIN(16), handle_upload_start },
{ BT_MESH_DFD_OP_UPLOAD_START_OOB, BT_MESH_LEN_MIN(2), handle_upload_start_oob },
{ BT_MESH_DFD_OP_UPLOAD_CANCEL, BT_MESH_LEN_EXACT(0), handle_upload_cancel },
{ BT_MESH_DFD_OP_FW_GET, BT_MESH_LEN_MIN(0), handle_fw_get },
{ BT_MESH_DFD_OP_FW_GET_BY_INDEX, BT_MESH_LEN_EXACT(2), handle_fw_get_by_index },
{ BT_MESH_DFD_OP_FW_DELETE, BT_MESH_LEN_MIN(0), handle_fw_delete },
{ BT_MESH_DFD_OP_FW_DELETE_ALL, BT_MESH_LEN_EXACT(0), handle_fw_delete_all },
BT_MESH_MODEL_OP_END
};
static void dfu_suspended(struct bt_mesh_dfu_cli *cli)
{
struct bt_mesh_dfd_srv *srv =
CONTAINER_OF(cli, struct bt_mesh_dfd_srv, dfu);
dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED);
}
static void dfu_ended(struct bt_mesh_dfu_cli *cli,
enum bt_mesh_dfu_status reason)
{
struct bt_mesh_dfd_srv *srv =
CONTAINER_OF(cli, struct bt_mesh_dfd_srv, dfu);
int err;
LOG_DBG("reason: %u, phase: %u, apply: %u", reason, srv->phase, srv->apply);
if (srv->phase == BT_MESH_DFD_PHASE_IDLE) {
return;
}
if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE) {
dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE);
return;
}
if (reason != BT_MESH_DFU_SUCCESS) {
dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED);
return;
}
if (!srv->apply) {
dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_SUCCESS);
return;
}
dfd_phase_set(srv, BT_MESH_DFD_PHASE_APPLYING_UPDATE);
err = bt_mesh_dfu_cli_apply(cli);
if (err) {
LOG_ERR("Apply failed: %d", err);
dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED);
}
}
static void dfu_applied(struct bt_mesh_dfu_cli *cli)
{
struct bt_mesh_dfd_srv *srv =
CONTAINER_OF(cli, struct bt_mesh_dfd_srv, dfu);
int err;
if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE) {
dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED);
return;
}
if (srv->phase != BT_MESH_DFD_PHASE_APPLYING_UPDATE) {
return;
}
err = bt_mesh_dfu_cli_confirm(cli);
if (err) {
LOG_ERR("Confirm failed: %d", err);
dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED);
}
}
static void dfu_confirmed(struct bt_mesh_dfu_cli *cli)
{
struct bt_mesh_dfd_srv *srv =
CONTAINER_OF(cli, struct bt_mesh_dfd_srv, dfu);
if (srv->phase != BT_MESH_DFD_PHASE_APPLYING_UPDATE &&
srv->phase != BT_MESH_DFD_PHASE_CANCELING_UPDATE) {
return;
}
dfd_phase_set(srv, BT_MESH_DFD_PHASE_COMPLETED);
}
const struct bt_mesh_dfu_cli_cb _bt_mesh_dfd_srv_dfu_cb = {
.suspended = dfu_suspended,
.ended = dfu_ended,
.applied = dfu_applied,
.confirmed = dfu_confirmed,
};
static int upload_start(struct bt_mesh_blob_srv *b, struct bt_mesh_msg_ctx *ctx,
struct bt_mesh_blob_xfer *xfer)
{
LOG_DBG("");
return 0;
}
static void upload_end(struct bt_mesh_blob_srv *b, uint64_t id, bool success)
{
struct bt_mesh_dfd_srv *srv =
CONTAINER_OF(b, struct bt_mesh_dfd_srv, upload.blob);
if (srv->upload.phase != BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE) {
return;
}
LOG_DBG("%u", success);
if (success && (bt_mesh_dfu_slot_commit(srv->upload.slot) == 0)) {
srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS;
return;
}
/* Will delete slot when we start a new upload */
srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR;
}
static void upload_timeout(struct bt_mesh_blob_srv *b)
{
LOG_DBG("");
upload_end(b, b->state.xfer.id, false);
}
const struct bt_mesh_blob_srv_cb _bt_mesh_dfd_srv_blob_cb = {
.start = upload_start,
.end = upload_end,
.suspended = upload_timeout,
};
static int dfd_srv_init(const struct bt_mesh_model *mod)
{
int err;
struct bt_mesh_dfd_srv *srv = mod->rt->user_data;
srv->mod = mod;
const struct bt_mesh_model *blob_srv =
bt_mesh_model_find(bt_mesh_model_elem(mod), BT_MESH_MODEL_ID_BLOB_SRV);
if (blob_srv == NULL) {
LOG_ERR("Missing BLOB Srv.");
return -EINVAL;
}
/** BLOB client also shall be present on the same element, but it is already checked by
* initiation of dfu client which we check here.
*/
const struct bt_mesh_model *dfu_cli =
bt_mesh_model_find(bt_mesh_model_elem(mod), BT_MESH_MODEL_ID_DFU_CLI);
if (dfu_cli == NULL) {
LOG_ERR("Missing FU Cli.");
return -EINVAL;
}
err = bt_mesh_model_extend(mod, srv->upload.blob.mod);
if (err) {
return err;
}
return 0;
}
static void dfd_srv_reset(const struct bt_mesh_model *mod)
{
struct bt_mesh_dfd_srv *srv = mod->rt->user_data;
dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE);
srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_IDLE;
sys_slist_init(&srv->inputs.targets);
srv->target_cnt = 0;
bt_mesh_dfu_slot_foreach(slot_del_cb, srv);
bt_mesh_dfu_slot_del_all();
}
const struct bt_mesh_model_cb _bt_mesh_dfd_srv_cb = {
.init = dfd_srv_init,
.reset = dfd_srv_reset,
};
enum bt_mesh_dfd_status bt_mesh_dfd_srv_receiver_add(struct bt_mesh_dfd_srv *srv, uint16_t addr,
uint8_t img_idx)
{
struct bt_mesh_dfu_target *t;
struct bt_mesh_blob_target_pull *p;
if (!BT_MESH_ADDR_IS_UNICAST(addr)) {
return BT_MESH_DFD_SUCCESS;
}
t = target_get(srv, addr);
if (t) {
t->img_idx = img_idx;
return BT_MESH_DFD_SUCCESS;
}
/* New target node, add it to the list */
if (srv->target_cnt == ARRAY_SIZE(srv->targets)) {
return BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES;
}
t = &srv->targets[srv->target_cnt];
p = &srv->pull_ctxs[srv->target_cnt];
srv->target_cnt++;
memset(t, 0, sizeof(*t));
memset(p, 0, sizeof(*p));
t->blob.addr = addr;
t->blob.pull = p;
t->img_idx = img_idx;
LOG_DBG("Added receiver 0x%04x img: %u", t->blob.addr,
t->img_idx);
return BT_MESH_DFD_SUCCESS;
}
enum bt_mesh_dfd_status bt_mesh_dfd_srv_receivers_delete_all(struct bt_mesh_dfd_srv *srv)
{
if (bt_mesh_dfu_cli_is_busy(&srv->dfu)) {
return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION;
}
sys_slist_init(&srv->inputs.targets);
srv->target_cnt = 0;
return BT_MESH_DFD_SUCCESS;
}
enum bt_mesh_dfd_status bt_mesh_dfd_srv_start(struct bt_mesh_dfd_srv *srv,
struct bt_mesh_dfd_start_params *params)
{
int err, i;
struct bt_mesh_dfu_cli_xfer xfer = { 0 };
if (!srv->target_cnt) {
return BT_MESH_DFD_ERR_RECEIVERS_LIST_EMPTY;
}
if (!bt_mesh_app_key_exists(params->app_idx)) {
return BT_MESH_DFD_ERR_INVALID_APPKEY_INDEX;
}
xfer.mode = params->xfer_mode;
xfer.slot = bt_mesh_dfu_slot_at(params->slot_idx);
if (!xfer.slot) {
return BT_MESH_DFD_ERR_FW_NOT_FOUND;
}
if (srv->inputs.app_idx == params->app_idx &&
srv->inputs.timeout_base == params->timeout_base &&
srv->inputs.group == params->group && srv->inputs.ttl == params->ttl &&
srv->dfu.xfer.blob.mode == xfer.mode && srv->apply == params->apply &&
srv->slot_idx == params->slot_idx) {
if (is_busy(srv) ||
srv->phase == BT_MESH_DFD_PHASE_COMPLETED) {
LOG_WRN("Already completed or in progress");
return BT_MESH_DFD_SUCCESS;
} else if (srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED) {
bt_mesh_dfu_cli_resume(&srv->dfu);
dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_ACTIVE);
return BT_MESH_DFD_SUCCESS;
}
} else if (is_busy(srv) ||
srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED) {
LOG_WRN("Busy with distribution");
return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION;
}
if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE) {
LOG_WRN("Canceling distribution");
return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION;
}
srv->io = NULL;
err = srv->cb->send(srv, xfer.slot, &srv->io);
if (err || !srv->io) {
return BT_MESH_DFD_ERR_INTERNAL;
}
sys_slist_init(&srv->inputs.targets);
for (i = 0; i < srv->target_cnt; i++) {
uint16_t addr = srv->targets[i].blob.addr;
memset(&srv->targets[i].blob, 0, sizeof(struct bt_mesh_blob_target));
memset(&srv->pull_ctxs[i], 0, sizeof(struct bt_mesh_blob_target_pull));
srv->targets[i].blob.addr = addr;
srv->targets[i].blob.pull = &srv->pull_ctxs[i];
sys_slist_append(&srv->inputs.targets, &srv->targets[i].blob.n);
}
srv->slot_idx = params->slot_idx;
srv->inputs.app_idx = params->app_idx;
srv->inputs.timeout_base = params->timeout_base;
srv->inputs.group = params->group;
srv->inputs.ttl = params->ttl;
srv->apply = params->apply;
LOG_DBG("Distribution Start: slot: %d, appidx: %d, tb: %d, addr: %04X, ttl: %d, apply: %d",
params->slot_idx, params->app_idx, params->timeout_base, params->group, params->ttl,
params->apply);
/* DFD Server will always retrieve targets' capabilities before distributing a firmware.*/
xfer.blob_params = NULL;
dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_ACTIVE);
err = bt_mesh_dfu_cli_send(&srv->dfu, &srv->inputs, srv->io, &xfer);
if (err) {
dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE);
return BT_MESH_DFD_ERR_INTERNAL;
}
return BT_MESH_DFD_SUCCESS;
}
enum bt_mesh_dfd_status bt_mesh_dfd_srv_suspend(struct bt_mesh_dfd_srv *srv)
{
int err;
if (srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED) {
return BT_MESH_DFD_SUCCESS;
}
if (srv->phase != BT_MESH_DFD_PHASE_TRANSFER_ACTIVE) {
return BT_MESH_DFD_ERR_WRONG_PHASE;
}
err = bt_mesh_dfu_cli_suspend(&srv->dfu);
if (err) {
return BT_MESH_DFD_ERR_SUSPEND_FAILED;
}
srv->phase = BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED;
return BT_MESH_DFD_SUCCESS;
}
enum bt_mesh_dfd_status bt_mesh_dfd_srv_cancel(struct bt_mesh_dfd_srv *srv,
struct bt_mesh_msg_ctx *ctx)
{
enum bt_mesh_dfd_phase prev_phase;
int err;
if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE ||
srv->phase == BT_MESH_DFD_PHASE_IDLE) {
if (ctx != NULL) {
status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS);
}
return BT_MESH_DFD_SUCCESS;
}
if (srv->phase == BT_MESH_DFD_PHASE_COMPLETED ||
srv->phase == BT_MESH_DFD_PHASE_FAILED) {
dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE);
if (ctx != NULL) {
status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS);
}
return BT_MESH_DFD_SUCCESS;
}
/* Phase TRANSFER_ACTIVE, TRANSFER_SUSPENDED, TRANSFER_SUCCESS, APPLYING_UPDATE: */
prev_phase = srv->phase;
dfd_phase_set(srv, BT_MESH_DFD_PHASE_CANCELING_UPDATE);
err = bt_mesh_dfu_cli_cancel(&srv->dfu, NULL);
if (err) {
if (ctx != NULL) {
status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL);
}
return BT_MESH_DFD_ERR_INTERNAL;
}
if (prev_phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE && ctx) {
/* Disable randomization for the Firmware Distribution State message to avoid
* reordering when Firmware Distribution Server sends 2 messages in a row when
* cancelling the update (see section 6.2.3.10 of MshDFUv1.0).
*/
ctx->rnd_delay = false;
}
if (ctx != NULL) {
status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS);
}
if (prev_phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE) {
dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE);
if (ctx != NULL) {
status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS);
}
}
return BT_MESH_DFD_SUCCESS;
}
enum bt_mesh_dfd_status bt_mesh_dfd_srv_apply(struct bt_mesh_dfd_srv *srv)
{
int err;
if (srv->phase == BT_MESH_DFD_PHASE_IDLE ||
srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE ||
srv->phase == BT_MESH_DFD_PHASE_TRANSFER_ACTIVE ||
srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED ||
srv->phase == BT_MESH_DFD_PHASE_FAILED) {
return BT_MESH_DFD_ERR_WRONG_PHASE;
}
if (srv->phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE ||
srv->phase == BT_MESH_DFD_PHASE_COMPLETED) {
return BT_MESH_DFD_SUCCESS;
}
err = bt_mesh_dfu_cli_apply(&srv->dfu);
if (err) {
return BT_MESH_DFD_ERR_INTERNAL;
}
dfd_phase_set(srv, BT_MESH_DFD_PHASE_APPLYING_UPDATE);
return BT_MESH_DFD_SUCCESS;
}
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)
{
struct bt_mesh_dfu_slot *slot;
int idx, err;
if (srv->phase != BT_MESH_DFD_PHASE_IDLE) {
*fwid = NULL;
*fwid_len = 0;
return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION;
}
idx = bt_mesh_dfu_slot_get(*fwid, *fwid_len, &slot);
if (idx < 0) {
return BT_MESH_DFD_SUCCESS;
}
err = slot_del(srv, slot);
if (err) {
*fwid = NULL;
*fwid_len = 0;
return BT_MESH_DFD_ERR_INTERNAL;
} else {
return BT_MESH_DFD_SUCCESS;
}
}
enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete_all(struct bt_mesh_dfd_srv *srv)
{
if (srv->phase != BT_MESH_DFD_PHASE_IDLE) {
return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION;
}
bt_mesh_dfu_slot_foreach(slot_del_cb, srv);
bt_mesh_dfu_slot_del_all();
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 ||
!srv->upload.is_pending_oob_check) {
/* This should not happen, unless the application calls the function with a
* "wrong" pointer or at a wrong time.
*/
return -EINVAL;
}
srv->upload.is_pending_oob_check = false;
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 */