618 lines
15 KiB
C
618 lines
15 KiB
C
/*
|
|
* Copyright (c) 2020 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/bluetooth/mesh.h>
|
|
#include "dfu.h"
|
|
#include "blob.h"
|
|
#include "access.h"
|
|
|
|
#define LOG_LEVEL CONFIG_BT_MESH_DFU_LOG_LEVEL
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(bt_mesh_dfu_srv);
|
|
|
|
#define UPDATE_IDX_NONE 0xff
|
|
|
|
BUILD_ASSERT((DFU_UPDATE_START_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_DFU_OP_UPDATE_START) +
|
|
BT_MESH_MIC_SHORT) <= BT_MESH_RX_SDU_MAX,
|
|
"The Firmware Update Start message does not fit into the maximum incoming SDU size.");
|
|
|
|
BUILD_ASSERT((DFU_UPDATE_INFO_STATUS_MSG_MINLEN +
|
|
BT_MESH_MODEL_OP_LEN(BT_MESH_DFU_OP_UPDATE_INFO_STATUS) + BT_MESH_MIC_SHORT)
|
|
<= BT_MESH_TX_SDU_MAX,
|
|
"The Firmware Update Info Status message does not fit into the maximum outgoing SDU "
|
|
"size.");
|
|
|
|
static void store_state(struct bt_mesh_dfu_srv *srv)
|
|
{
|
|
bt_mesh_model_data_store(srv->mod, false, NULL, &srv->update,
|
|
sizeof(srv->update));
|
|
}
|
|
|
|
static void erase_state(struct bt_mesh_dfu_srv *srv)
|
|
{
|
|
bt_mesh_model_data_store(srv->mod, false, NULL, NULL, 0);
|
|
}
|
|
|
|
static void xfer_failed(struct bt_mesh_dfu_srv *srv)
|
|
{
|
|
if (!bt_mesh_dfu_srv_is_busy(srv) ||
|
|
srv->update.idx >= srv->img_count) {
|
|
return;
|
|
}
|
|
|
|
erase_state(srv);
|
|
|
|
if (srv->cb->end) {
|
|
srv->cb->end(srv, &srv->imgs[srv->update.idx], false);
|
|
}
|
|
}
|
|
|
|
static enum bt_mesh_dfu_status metadata_check(struct bt_mesh_dfu_srv *srv,
|
|
uint8_t idx,
|
|
struct net_buf_simple *buf,
|
|
enum bt_mesh_dfu_effect *effect)
|
|
{
|
|
*effect = BT_MESH_DFU_EFFECT_NONE;
|
|
|
|
if (idx >= srv->img_count) {
|
|
return BT_MESH_DFU_ERR_FW_IDX;
|
|
}
|
|
|
|
if (!srv->cb->check) {
|
|
return BT_MESH_DFU_SUCCESS;
|
|
}
|
|
|
|
if (srv->cb->check(srv, &srv->imgs[idx], buf, effect)) {
|
|
*effect = BT_MESH_DFU_EFFECT_NONE;
|
|
return BT_MESH_DFU_ERR_METADATA;
|
|
}
|
|
|
|
return BT_MESH_DFU_SUCCESS;
|
|
}
|
|
|
|
static void apply_rsp_sent(int err, void *cb_params)
|
|
{
|
|
struct bt_mesh_dfu_srv *srv = cb_params;
|
|
|
|
if (err) {
|
|
/* return phase back to give client one more chance. */
|
|
srv->update.phase = BT_MESH_DFU_PHASE_VERIFY_OK;
|
|
LOG_WRN("Apply response failed, wait for retry (err %d)", err);
|
|
return;
|
|
}
|
|
|
|
LOG_DBG("");
|
|
|
|
if (!srv->cb->apply || srv->update.idx == UPDATE_IDX_NONE) {
|
|
srv->update.phase = BT_MESH_DFU_PHASE_IDLE;
|
|
store_state(srv);
|
|
LOG_DBG("Prerequisites for apply callback are wrong");
|
|
return;
|
|
}
|
|
|
|
store_state(srv);
|
|
|
|
err = srv->cb->apply(srv, &srv->imgs[srv->update.idx]);
|
|
if (err) {
|
|
srv->update.phase = BT_MESH_DFU_PHASE_IDLE;
|
|
store_state(srv);
|
|
LOG_DBG("Application apply callback failed (err %d)", err);
|
|
}
|
|
}
|
|
|
|
static void apply_rsp_sending(uint16_t duration, int err, void *cb_params)
|
|
{
|
|
if (err) {
|
|
apply_rsp_sent(err, cb_params);
|
|
}
|
|
}
|
|
|
|
static void verify(struct bt_mesh_dfu_srv *srv)
|
|
{
|
|
srv->update.phase = BT_MESH_DFU_PHASE_VERIFY;
|
|
|
|
if (srv->update.idx >= srv->img_count) {
|
|
bt_mesh_dfu_srv_rejected(srv);
|
|
return;
|
|
}
|
|
|
|
if (!srv->cb->end) {
|
|
bt_mesh_dfu_srv_verified(srv);
|
|
return;
|
|
}
|
|
|
|
srv->cb->end(srv, &srv->imgs[srv->update.idx], true);
|
|
if (srv->update.phase == BT_MESH_DFU_PHASE_VERIFY) {
|
|
store_state(srv);
|
|
}
|
|
}
|
|
|
|
static int handle_info_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
struct bt_mesh_dfu_srv *srv = mod->rt->user_data;
|
|
uint8_t idx, limit;
|
|
|
|
if (srv->update.phase == BT_MESH_DFU_PHASE_APPLYING) {
|
|
LOG_INF("Still applying, not responding");
|
|
return -EBUSY;
|
|
}
|
|
|
|
idx = net_buf_simple_pull_u8(buf);
|
|
limit = net_buf_simple_pull_u8(buf);
|
|
|
|
LOG_DBG("from %u (limit: %u)", idx, limit);
|
|
|
|
NET_BUF_SIMPLE_DEFINE(rsp, BT_MESH_TX_SDU_MAX);
|
|
bt_mesh_model_msg_init(&rsp, BT_MESH_DFU_OP_UPDATE_INFO_STATUS);
|
|
net_buf_simple_add_u8(&rsp, srv->img_count);
|
|
net_buf_simple_add_u8(&rsp, idx);
|
|
|
|
for (; idx < srv->img_count && limit > 0; ++idx) {
|
|
uint32_t entry_len;
|
|
|
|
if (!srv->imgs[idx].fwid) {
|
|
continue;
|
|
}
|
|
|
|
entry_len = 2 + srv->imgs[idx].fwid_len;
|
|
if (srv->imgs[idx].uri) {
|
|
entry_len += strlen(srv->imgs[idx].uri);
|
|
}
|
|
|
|
if (net_buf_simple_tailroom(&rsp) + BT_MESH_MIC_SHORT < entry_len) {
|
|
break;
|
|
}
|
|
|
|
net_buf_simple_add_u8(&rsp, srv->imgs[idx].fwid_len);
|
|
net_buf_simple_add_mem(&rsp, srv->imgs[idx].fwid,
|
|
srv->imgs[idx].fwid_len);
|
|
|
|
if (srv->imgs[idx].uri) {
|
|
size_t len = strlen(srv->imgs[idx].uri);
|
|
|
|
net_buf_simple_add_u8(&rsp, len);
|
|
net_buf_simple_add_mem(&rsp, srv->imgs[idx].uri, len);
|
|
} else {
|
|
net_buf_simple_add_u8(&rsp, 0);
|
|
}
|
|
|
|
limit--;
|
|
}
|
|
|
|
if (srv->update.phase != BT_MESH_DFU_PHASE_IDLE) {
|
|
ctx->send_ttl = srv->update.ttl;
|
|
}
|
|
|
|
bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handle_metadata_check(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
struct bt_mesh_dfu_srv *srv = mod->rt->user_data;
|
|
enum bt_mesh_dfu_status status;
|
|
enum bt_mesh_dfu_effect effect;
|
|
uint8_t idx;
|
|
|
|
BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFU_OP_UPDATE_METADATA_STATUS, 2);
|
|
bt_mesh_model_msg_init(&rsp, BT_MESH_DFU_OP_UPDATE_METADATA_STATUS);
|
|
|
|
idx = net_buf_simple_pull_u8(buf);
|
|
status = metadata_check(srv, idx, buf, &effect);
|
|
|
|
LOG_DBG("%u", idx);
|
|
|
|
net_buf_simple_add_u8(&rsp, (status & BIT_MASK(3)) | (effect << 3));
|
|
net_buf_simple_add_u8(&rsp, idx);
|
|
|
|
if (srv->update.phase != BT_MESH_DFU_PHASE_IDLE) {
|
|
ctx->send_ttl = srv->update.ttl;
|
|
}
|
|
|
|
bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void update_status_rsp(struct bt_mesh_dfu_srv *srv,
|
|
struct bt_mesh_msg_ctx *ctx,
|
|
enum bt_mesh_dfu_status status,
|
|
const struct bt_mesh_send_cb *send_cb)
|
|
{
|
|
BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_STATUS, 14);
|
|
bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_STATUS);
|
|
|
|
net_buf_simple_add_u8(&buf, ((status & BIT_MASK(3)) |
|
|
(srv->update.phase << 5)));
|
|
|
|
if (srv->update.phase != BT_MESH_DFU_PHASE_IDLE) {
|
|
net_buf_simple_add_u8(&buf, srv->update.ttl);
|
|
net_buf_simple_add_u8(&buf, srv->update.effect);
|
|
net_buf_simple_add_le16(&buf, srv->update.timeout_base);
|
|
net_buf_simple_add_le64(&buf, srv->blob.state.xfer.id);
|
|
net_buf_simple_add_u8(&buf, srv->update.idx);
|
|
|
|
ctx->send_ttl = srv->update.ttl;
|
|
}
|
|
|
|
bt_mesh_model_send(srv->mod, ctx, &buf, send_cb, srv);
|
|
}
|
|
|
|
static int handle_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
struct bt_mesh_dfu_srv *srv = mod->rt->user_data;
|
|
|
|
LOG_DBG("");
|
|
|
|
update_status_rsp(srv, ctx, BT_MESH_DFU_SUCCESS, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline bool is_active_update(struct bt_mesh_dfu_srv *srv, uint8_t idx,
|
|
uint16_t timeout_base,
|
|
const uint64_t *blob_id, uint8_t ttl,
|
|
uint16_t meta_checksum)
|
|
{
|
|
return (srv->update.idx != idx || srv->blob.state.xfer.id != *blob_id ||
|
|
srv->update.ttl != ttl ||
|
|
srv->update.timeout_base != timeout_base ||
|
|
srv->update.meta != meta_checksum);
|
|
}
|
|
|
|
static int handle_start(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
struct bt_mesh_dfu_srv *srv = mod->rt->user_data;
|
|
const struct bt_mesh_blob_io *io;
|
|
uint16_t timeout_base, meta_checksum;
|
|
enum bt_mesh_dfu_status status;
|
|
uint8_t ttl, idx;
|
|
uint64_t blob_id;
|
|
int err;
|
|
struct net_buf_simple_state buf_state;
|
|
|
|
ttl = net_buf_simple_pull_u8(buf);
|
|
timeout_base = net_buf_simple_pull_le16(buf);
|
|
blob_id = net_buf_simple_pull_le64(buf);
|
|
idx = net_buf_simple_pull_u8(buf);
|
|
meta_checksum = dfu_metadata_checksum(buf);
|
|
|
|
LOG_DBG("%u ttl: %u extra time: %u", idx, ttl, timeout_base);
|
|
|
|
if ((!buf->len || meta_checksum == srv->update.meta) &&
|
|
srv->update.phase == BT_MESH_DFU_PHASE_TRANSFER_ERR &&
|
|
srv->update.ttl == ttl &&
|
|
srv->update.timeout_base == timeout_base &&
|
|
srv->update.idx == idx &&
|
|
srv->blob.state.xfer.id == blob_id) {
|
|
srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ACTIVE;
|
|
status = BT_MESH_DFU_SUCCESS;
|
|
store_state(srv);
|
|
/* blob srv will resume the transfer. */
|
|
LOG_DBG("Resuming transfer");
|
|
goto rsp;
|
|
}
|
|
|
|
if (bt_mesh_dfu_srv_is_busy(srv)) {
|
|
if (is_active_update(srv, idx, timeout_base, &blob_id, ttl,
|
|
meta_checksum)) {
|
|
status = BT_MESH_DFU_ERR_WRONG_PHASE;
|
|
} else {
|
|
status = BT_MESH_DFU_SUCCESS;
|
|
srv->update.ttl = ttl;
|
|
srv->blob.state.xfer.id = blob_id;
|
|
}
|
|
|
|
LOG_WRN("Busy. Phase: %u", srv->update.phase);
|
|
goto rsp;
|
|
}
|
|
|
|
net_buf_simple_save(buf, &buf_state);
|
|
status = metadata_check(srv, idx, buf,
|
|
(enum bt_mesh_dfu_effect *)&srv->update.effect);
|
|
net_buf_simple_restore(buf, &buf_state);
|
|
if (status != BT_MESH_DFU_SUCCESS) {
|
|
goto rsp;
|
|
}
|
|
|
|
srv->update.ttl = ttl;
|
|
srv->update.timeout_base = timeout_base;
|
|
srv->update.meta = meta_checksum;
|
|
|
|
io = NULL;
|
|
err = srv->cb->start(srv, &srv->imgs[idx], buf, &io);
|
|
if (err == -EALREADY || (!err && bt_mesh_has_addr(ctx->addr))) {
|
|
/* This image has already been received or this is a
|
|
* self-update. Skip the transfer phase and proceed to
|
|
* verifying update.
|
|
*/
|
|
status = BT_MESH_DFU_SUCCESS;
|
|
srv->update.idx = idx;
|
|
srv->blob.state.xfer.id = blob_id;
|
|
srv->update.phase = BT_MESH_DFU_PHASE_VERIFY;
|
|
update_status_rsp(srv, ctx, status, NULL);
|
|
verify(srv);
|
|
return 0;
|
|
}
|
|
|
|
if (err == -ENOMEM) {
|
|
status = BT_MESH_DFU_ERR_RESOURCES;
|
|
goto rsp;
|
|
}
|
|
|
|
if (err == -EBUSY) {
|
|
status = BT_MESH_DFU_ERR_TEMPORARILY_UNAVAILABLE;
|
|
goto rsp;
|
|
}
|
|
|
|
if (err || !io || !io->wr) {
|
|
status = BT_MESH_DFU_ERR_INTERNAL;
|
|
goto rsp;
|
|
}
|
|
|
|
err = bt_mesh_blob_srv_recv(&srv->blob, blob_id, io,
|
|
ttl, timeout_base);
|
|
if (err) {
|
|
status = BT_MESH_DFU_ERR_BLOB_XFER_BUSY;
|
|
goto rsp;
|
|
}
|
|
|
|
srv->update.idx = idx;
|
|
srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ACTIVE;
|
|
status = BT_MESH_DFU_SUCCESS;
|
|
store_state(srv);
|
|
|
|
rsp:
|
|
update_status_rsp(srv, ctx, status, NULL);
|
|
|
|
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_dfu_srv *srv = mod->rt->user_data;
|
|
|
|
if (srv->update.idx == UPDATE_IDX_NONE) {
|
|
goto rsp;
|
|
}
|
|
|
|
LOG_DBG("");
|
|
|
|
bt_mesh_blob_srv_cancel(&srv->blob);
|
|
srv->update.phase = BT_MESH_DFU_PHASE_IDLE;
|
|
xfer_failed(srv);
|
|
|
|
rsp:
|
|
update_status_rsp(srv, ctx, BT_MESH_DFU_SUCCESS, NULL);
|
|
|
|
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_dfu_srv *srv = mod->rt->user_data;
|
|
static const struct bt_mesh_send_cb send_cb = {
|
|
.start = apply_rsp_sending,
|
|
.end = apply_rsp_sent,
|
|
};
|
|
|
|
LOG_DBG("");
|
|
|
|
if (srv->update.phase == BT_MESH_DFU_PHASE_APPLYING) {
|
|
update_status_rsp(srv, ctx, BT_MESH_DFU_SUCCESS, NULL);
|
|
return 0;
|
|
}
|
|
|
|
if (srv->update.phase != BT_MESH_DFU_PHASE_VERIFY_OK) {
|
|
LOG_WRN("Apply: Invalid phase %u", srv->update.phase);
|
|
update_status_rsp(srv, ctx, BT_MESH_DFU_ERR_WRONG_PHASE, NULL);
|
|
return 0;
|
|
}
|
|
|
|
/* Postponing the apply callback until the response has been sent, in
|
|
* case it triggers a reboot:
|
|
*/
|
|
srv->update.phase = BT_MESH_DFU_PHASE_APPLYING;
|
|
update_status_rsp(srv, ctx, BT_MESH_DFU_SUCCESS, &send_cb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
const struct bt_mesh_model_op _bt_mesh_dfu_srv_op[] = {
|
|
{ BT_MESH_DFU_OP_UPDATE_INFO_GET, BT_MESH_LEN_EXACT(2), handle_info_get },
|
|
{ BT_MESH_DFU_OP_UPDATE_METADATA_CHECK, BT_MESH_LEN_MIN(1), handle_metadata_check },
|
|
{ BT_MESH_DFU_OP_UPDATE_GET, BT_MESH_LEN_EXACT(0), handle_get },
|
|
{ BT_MESH_DFU_OP_UPDATE_START, BT_MESH_LEN_MIN(12), handle_start },
|
|
{ BT_MESH_DFU_OP_UPDATE_CANCEL, BT_MESH_LEN_EXACT(0), handle_cancel },
|
|
{ BT_MESH_DFU_OP_UPDATE_APPLY, BT_MESH_LEN_EXACT(0), handle_apply },
|
|
BT_MESH_MODEL_OP_END,
|
|
};
|
|
|
|
static int dfu_srv_init(const struct bt_mesh_model *mod)
|
|
{
|
|
struct bt_mesh_dfu_srv *srv = mod->rt->user_data;
|
|
|
|
srv->mod = mod;
|
|
srv->update.idx = UPDATE_IDX_NONE;
|
|
|
|
if (!srv->cb || !srv->cb->start || !srv->imgs || srv->img_count == 0 ||
|
|
srv->img_count == UPDATE_IDX_NONE) {
|
|
LOG_ERR("Invalid DFU Server initialization");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_MESH_MODEL_EXTENSIONS)) {
|
|
bt_mesh_model_extend(mod, srv->blob.mod);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dfu_srv_settings_set(const struct bt_mesh_model *mod, const char *name,
|
|
size_t len_rd, settings_read_cb read_cb,
|
|
void *cb_arg)
|
|
{
|
|
struct bt_mesh_dfu_srv *srv = mod->rt->user_data;
|
|
ssize_t len;
|
|
|
|
if (len_rd < sizeof(srv->update)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
len = read_cb(cb_arg, &srv->update, sizeof(srv->update));
|
|
if (len < 0) {
|
|
return len;
|
|
}
|
|
|
|
LOG_DBG("Recovered transfer (phase: %u, idx: %u)", srv->update.phase,
|
|
srv->update.idx);
|
|
if (srv->update.phase == BT_MESH_DFU_PHASE_TRANSFER_ACTIVE) {
|
|
LOG_DBG("Settings recovered mid-transfer, setting transfer error");
|
|
srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ERR;
|
|
} else if (srv->update.phase == BT_MESH_DFU_PHASE_VERIFY_OK) {
|
|
LOG_DBG("Settings recovered before application, setting verification fail");
|
|
srv->update.phase = BT_MESH_DFU_PHASE_VERIFY_FAIL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void dfu_srv_reset(const struct bt_mesh_model *mod)
|
|
{
|
|
struct bt_mesh_dfu_srv *srv = mod->rt->user_data;
|
|
|
|
srv->update.phase = BT_MESH_DFU_PHASE_IDLE;
|
|
erase_state(srv);
|
|
}
|
|
|
|
const struct bt_mesh_model_cb _bt_mesh_dfu_srv_cb = {
|
|
.init = dfu_srv_init,
|
|
.settings_set = dfu_srv_settings_set,
|
|
.reset = dfu_srv_reset,
|
|
};
|
|
|
|
static void blob_suspended(struct bt_mesh_blob_srv *b)
|
|
{
|
|
struct bt_mesh_dfu_srv *srv = CONTAINER_OF(b, struct bt_mesh_dfu_srv, blob);
|
|
|
|
srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ERR;
|
|
store_state(srv);
|
|
}
|
|
|
|
static void blob_end(struct bt_mesh_blob_srv *b, uint64_t id, bool success)
|
|
{
|
|
struct bt_mesh_dfu_srv *srv =
|
|
CONTAINER_OF(b, struct bt_mesh_dfu_srv, blob);
|
|
|
|
LOG_DBG("success: %u", success);
|
|
|
|
if (!success) {
|
|
srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ERR;
|
|
xfer_failed(srv);
|
|
return;
|
|
}
|
|
|
|
verify(srv);
|
|
}
|
|
|
|
static int blob_recover(struct bt_mesh_blob_srv *b,
|
|
struct bt_mesh_blob_xfer *xfer,
|
|
const struct bt_mesh_blob_io **io)
|
|
{
|
|
struct bt_mesh_dfu_srv *srv =
|
|
CONTAINER_OF(b, struct bt_mesh_dfu_srv, blob);
|
|
|
|
if (!srv->cb->recover ||
|
|
srv->update.phase != BT_MESH_DFU_PHASE_TRANSFER_ERR ||
|
|
srv->update.idx >= srv->img_count) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
return srv->cb->recover(srv, &srv->imgs[srv->update.idx], io);
|
|
}
|
|
|
|
const struct bt_mesh_blob_srv_cb _bt_mesh_dfu_srv_blob_cb = {
|
|
.suspended = blob_suspended,
|
|
.end = blob_end,
|
|
.recover = blob_recover,
|
|
};
|
|
|
|
void bt_mesh_dfu_srv_verified(struct bt_mesh_dfu_srv *srv)
|
|
{
|
|
if (srv->update.phase != BT_MESH_DFU_PHASE_VERIFY) {
|
|
LOG_WRN("Wrong state");
|
|
return;
|
|
}
|
|
|
|
LOG_DBG("");
|
|
|
|
srv->update.phase = BT_MESH_DFU_PHASE_VERIFY_OK;
|
|
store_state(srv);
|
|
}
|
|
|
|
void bt_mesh_dfu_srv_rejected(struct bt_mesh_dfu_srv *srv)
|
|
{
|
|
if (srv->update.phase != BT_MESH_DFU_PHASE_VERIFY) {
|
|
LOG_WRN("Wrong state");
|
|
return;
|
|
}
|
|
|
|
LOG_DBG("");
|
|
|
|
srv->update.phase = BT_MESH_DFU_PHASE_VERIFY_FAIL;
|
|
store_state(srv);
|
|
}
|
|
|
|
void bt_mesh_dfu_srv_cancel(struct bt_mesh_dfu_srv *srv)
|
|
{
|
|
if (srv->update.phase == BT_MESH_DFU_PHASE_IDLE) {
|
|
LOG_WRN("Wrong state");
|
|
return;
|
|
}
|
|
|
|
(void)bt_mesh_blob_srv_cancel(&srv->blob);
|
|
}
|
|
|
|
void bt_mesh_dfu_srv_applied(struct bt_mesh_dfu_srv *srv)
|
|
{
|
|
if (srv->update.phase != BT_MESH_DFU_PHASE_APPLYING) {
|
|
LOG_WRN("Wrong state");
|
|
return;
|
|
}
|
|
|
|
LOG_DBG("");
|
|
|
|
srv->update.phase = BT_MESH_DFU_PHASE_IDLE;
|
|
store_state(srv);
|
|
}
|
|
|
|
bool bt_mesh_dfu_srv_is_busy(const struct bt_mesh_dfu_srv *srv)
|
|
{
|
|
return srv->update.phase != BT_MESH_DFU_PHASE_IDLE &&
|
|
srv->update.phase != BT_MESH_DFU_PHASE_TRANSFER_ERR &&
|
|
srv->update.phase != BT_MESH_DFU_PHASE_VERIFY_FAIL;
|
|
}
|
|
|
|
uint8_t bt_mesh_dfu_srv_progress(const struct bt_mesh_dfu_srv *srv)
|
|
{
|
|
if (!bt_mesh_dfu_srv_is_busy(srv)) {
|
|
return 0U;
|
|
}
|
|
|
|
if (srv->update.phase == BT_MESH_DFU_PHASE_TRANSFER_ACTIVE) {
|
|
return bt_mesh_blob_srv_progress(&srv->blob);
|
|
}
|
|
|
|
return 100U;
|
|
}
|