zephyr/subsys/bluetooth/mesh/shell/dfu.c
Ingar Kulbrandstad eafa2bb81b Bluetooth: Mesh: Fixed shell call to metadata-encode
The call to metadata-encode call in code and documentation
does does not match. Because the shell call in the documentation
looks correct the shell call in the code has been changed by
removeing the double metadata request.

Signed-off-by: Ingar Kulbrandstad <ingar.kulbrandstad@nordicsemi.no>
2023-08-09 18:14:41 +03:00

1033 lines
27 KiB
C

/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "dfu.h"
#include <stdlib.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/util.h>
#include <zephyr/shell/shell.h>
#include <zephyr/bluetooth/mesh.h>
#include <zephyr/bluetooth/mesh/shell.h>
#include <zephyr/dfu/mcuboot.h>
#include <zephyr/storage/flash_map.h>
#include "utils.h"
#include "blob.h"
#include "../dfu_slot.h"
/***************************************************************************************************
* Implementation of models' instances
**************************************************************************************************/
extern const struct shell *bt_mesh_shell_ctx_shell;
#if defined(CONFIG_BT_MESH_SHELL_DFU_CLI)
static void dfu_cli_ended(struct bt_mesh_dfu_cli *cli,
enum bt_mesh_dfu_status reason)
{
shell_print(bt_mesh_shell_ctx_shell, "DFU ended: %u", reason);
}
static void dfu_cli_applied(struct bt_mesh_dfu_cli *cli)
{
shell_print(bt_mesh_shell_ctx_shell, "DFU applied.");
}
static void dfu_cli_lost_target(struct bt_mesh_dfu_cli *cli,
struct bt_mesh_dfu_target *target)
{
shell_print(bt_mesh_shell_ctx_shell, "DFU target lost: 0x%04x", target->blob.addr);
}
static void dfu_cli_confirmed(struct bt_mesh_dfu_cli *cli)
{
shell_print(bt_mesh_shell_ctx_shell, "DFU confirmed");
}
const struct bt_mesh_dfu_cli_cb dfu_cli_cb = {
.ended = dfu_cli_ended,
.applied = dfu_cli_applied,
.lost_target = dfu_cli_lost_target,
.confirmed = dfu_cli_confirmed,
};
struct bt_mesh_dfu_cli bt_mesh_shell_dfu_cli = BT_MESH_DFU_CLI_INIT(&dfu_cli_cb);
#endif /* CONFIG_BT_MESH_SHELL_DFU_CLI */
#if defined(CONFIG_BT_MESH_SHELL_DFU_SRV)
struct shell_dfu_fwid {
uint8_t type;
struct mcuboot_img_sem_ver ver;
};
static struct bt_mesh_dfu_img dfu_imgs[] = { {
.fwid = &((struct shell_dfu_fwid){ 0x01, { 1, 0, 0, 0 } }),
.fwid_len = sizeof(struct shell_dfu_fwid),
} };
static int dfu_meta_check(struct bt_mesh_dfu_srv *srv,
const struct bt_mesh_dfu_img *img,
struct net_buf_simple *metadata,
enum bt_mesh_dfu_effect *effect)
{
return 0;
}
static int dfu_start(struct bt_mesh_dfu_srv *srv,
const struct bt_mesh_dfu_img *img,
struct net_buf_simple *metadata,
const struct bt_mesh_blob_io **io)
{
shell_print(bt_mesh_shell_ctx_shell, "DFU setup");
*io = bt_mesh_shell_blob_io;
return 0;
}
static void dfu_end(struct bt_mesh_dfu_srv *srv, const struct bt_mesh_dfu_img *img, bool success)
{
if (!success) {
shell_print(bt_mesh_shell_ctx_shell, "DFU failed");
return;
}
if (!bt_mesh_shell_blob_valid) {
bt_mesh_dfu_srv_rejected(srv);
return;
}
bt_mesh_dfu_srv_verified(srv);
}
static int dfu_apply(struct bt_mesh_dfu_srv *srv,
const struct bt_mesh_dfu_img *img)
{
if (!bt_mesh_shell_blob_valid) {
return -EINVAL;
}
shell_print(bt_mesh_shell_ctx_shell, "Applying DFU transfer...");
return 0;
}
static const struct bt_mesh_dfu_srv_cb dfu_handlers = {
.check = dfu_meta_check,
.start = dfu_start,
.end = dfu_end,
.apply = dfu_apply,
};
struct bt_mesh_dfu_srv bt_mesh_shell_dfu_srv =
BT_MESH_DFU_SRV_INIT(&dfu_handlers, dfu_imgs, ARRAY_SIZE(dfu_imgs));
#endif /* CONFIG_BT_MESH_SHELL_DFU_SRV */
void bt_mesh_shell_dfu_cmds_init(void)
{
#if defined(CONFIG_BT_MESH_SHELL_DFU_SRV) && defined(CONFIG_BOOTLOADER_MCUBOOT)
struct mcuboot_img_header img_header;
int err = boot_read_bank_header(FIXED_PARTITION_ID(slot0_partition),
&img_header, sizeof(img_header));
if (!err) {
struct shell_dfu_fwid *fwid =
(struct shell_dfu_fwid *)dfu_imgs[0].fwid;
fwid->ver = img_header.h.v1.sem_ver;
boot_write_img_confirmed();
}
#endif
}
/***************************************************************************************************
* Shell Commands
**************************************************************************************************/
#if defined(CONFIG_BT_MESH_SHELL_DFU_METADATA)
NET_BUF_SIMPLE_DEFINE_STATIC(dfu_comp_data, BT_MESH_TX_SDU_MAX);
static int cmd_dfu_comp_clear(const struct shell *sh, size_t argc, char *argv[])
{
net_buf_simple_reset(&dfu_comp_data);
return 0;
}
static int cmd_dfu_comp_add(const struct shell *sh, size_t argc, char *argv[])
{
struct net_buf_simple_state state;
int err = 0;
if (argc < 6) {
return -EINVAL;
}
if (net_buf_simple_tailroom(&dfu_comp_data) < 10) {
shell_print(sh, "Buffer is too small: %u",
net_buf_simple_tailroom(&dfu_comp_data));
return -EMSGSIZE;
}
net_buf_simple_save(&dfu_comp_data, &state);
for (size_t i = 1; i <= 5; i++) {
net_buf_simple_add_le16(&dfu_comp_data, shell_strtoul(argv[i], 0, &err));
}
if (err) {
net_buf_simple_restore(&dfu_comp_data, &state);
shell_warn(sh, "Unable to parse input string argument");
return err;
}
return 0;
}
static int cmd_dfu_comp_elem_add(const struct shell *sh, size_t argc, char *argv[])
{
uint8_t sig_model_count;
uint8_t vnd_model_count;
struct net_buf_simple_state state;
int err = 0;
if (argc < 5) {
return -EINVAL;
}
net_buf_simple_save(&dfu_comp_data, &state);
sig_model_count = shell_strtoul(argv[2], 0, &err);
vnd_model_count = shell_strtoul(argv[3], 0, &err);
if (argc < 4 + sig_model_count + vnd_model_count * 2) {
return -EINVAL;
}
if (net_buf_simple_tailroom(&dfu_comp_data) < 4 + sig_model_count * 2 +
vnd_model_count * 4) {
shell_print(sh, "Buffer is too small: %u",
net_buf_simple_tailroom(&dfu_comp_data));
return -EMSGSIZE;
}
net_buf_simple_add_le16(&dfu_comp_data, shell_strtoul(argv[1], 0, &err));
net_buf_simple_add_u8(&dfu_comp_data, sig_model_count);
net_buf_simple_add_u8(&dfu_comp_data, vnd_model_count);
for (size_t i = 0; i < sig_model_count; i++) {
net_buf_simple_add_le16(&dfu_comp_data, shell_strtoul(argv[4 + i], 0, &err));
}
for (size_t i = 0; i < vnd_model_count; i++) {
size_t arg_i = 4 + sig_model_count + i * 2;
net_buf_simple_add_le16(&dfu_comp_data, shell_strtoul(argv[arg_i], 0, &err));
net_buf_simple_add_le16(&dfu_comp_data, shell_strtoul(argv[arg_i + 1], 0, &err));
}
if (err) {
net_buf_simple_restore(&dfu_comp_data, &state);
shell_warn(sh, "Unable to parse input string argument");
return err;
}
return 0;
}
static int cmd_dfu_comp_hash_get(const struct shell *sh, size_t argc, char *argv[])
{
uint8_t key[16] = {};
uint32_t hash;
int err;
if (dfu_comp_data.len < 14) {
shell_print(sh, "Composition data is not set");
return -EINVAL;
}
if (argc > 1) {
hex2bin(argv[1], strlen(argv[1]), key, sizeof(key));
}
shell_print(sh, "Composition data to be hashed:");
shell_print(sh, "\tCID: 0x%04x", sys_get_le16(&dfu_comp_data.data[0]));
shell_print(sh, "\tPID: 0x%04x", sys_get_le16(&dfu_comp_data.data[2]));
shell_print(sh, "\tVID: 0x%04x", sys_get_le16(&dfu_comp_data.data[4]));
shell_print(sh, "\tCPRL: %u", sys_get_le16(&dfu_comp_data.data[6]));
shell_print(sh, "\tFeatures: 0x%x", sys_get_le16(&dfu_comp_data.data[8]));
for (size_t i = 10; i < dfu_comp_data.len - 4;) {
uint8_t sig_model_count = dfu_comp_data.data[i + 2];
uint8_t vnd_model_count = dfu_comp_data.data[i + 3];
shell_print(sh, "\tElem: %u", sys_get_le16(&dfu_comp_data.data[i]));
shell_print(sh, "\t\tNumS: %u", sig_model_count);
shell_print(sh, "\t\tNumV: %u", vnd_model_count);
for (size_t j = 0; j < sig_model_count; j++) {
shell_print(sh, "\t\tSIG Model ID: 0x%04x",
sys_get_le16(&dfu_comp_data.data[i + 4 + j * 2]));
}
for (size_t j = 0; j < vnd_model_count; j++) {
size_t arg_i = i + 4 + sig_model_count * 2 + j * 4;
shell_print(sh, "\t\tVnd Company ID: 0x%04x, Model ID: 0x%04x",
sys_get_le16(&dfu_comp_data.data[arg_i]),
sys_get_le16(&dfu_comp_data.data[arg_i + 2]));
}
i += 4 + sig_model_count * 2 + vnd_model_count * 4;
}
err = bt_mesh_dfu_metadata_comp_hash_get(&dfu_comp_data, key, &hash);
if (err) {
shell_print(sh, "Failed to compute composition data hash: %d\n", err);
return err;
}
shell_print(sh, "Composition data hash: 0x%04x", hash);
return 0;
}
static int cmd_dfu_metadata_encode(const struct shell *sh, size_t argc, char *argv[])
{
char md_str[2 * CONFIG_BT_MESH_DFU_METADATA_MAXLEN + 1];
uint8_t user_data[CONFIG_BT_MESH_DFU_METADATA_MAXLEN - 18];
struct bt_mesh_dfu_metadata md;
size_t len;
int err = 0;
NET_BUF_SIMPLE_DEFINE(buf, CONFIG_BT_MESH_DFU_METADATA_MAXLEN);
if (argc < 9) {
return -EINVAL;
}
md.fw_ver.major = shell_strtoul(argv[1], 0, &err);
md.fw_ver.minor = shell_strtoul(argv[2], 0, &err);
md.fw_ver.revision = shell_strtoul(argv[3], 0, &err);
md.fw_ver.build_num = shell_strtoul(argv[4], 0, &err);
md.fw_size = shell_strtoul(argv[5], 0, &err);
md.fw_core_type = shell_strtoul(argv[6], 0, &err);
md.comp_hash = shell_strtoul(argv[7], 0, &err);
md.elems = shell_strtoul(argv[8], 0, &err);
if (err) {
shell_warn(sh, "Unable to parse input string argument");
return err;
}
if (argc > 9) {
if (sizeof(user_data) < strlen(argv[9]) / 2) {
shell_print(sh, "User data is too big.");
return -EINVAL;
}
md.user_data_len = hex2bin(argv[9], strlen(argv[9]), user_data, sizeof(user_data));
md.user_data = user_data;
} else {
md.user_data_len = 0;
}
shell_print(sh, "Metadata to be encoded:");
shell_print(sh, "\tVersion: %u.%u.%u+%u", md.fw_ver.major, md.fw_ver.minor,
md.fw_ver.revision, md.fw_ver.build_num);
shell_print(sh, "\tSize: %u", md.fw_size);
shell_print(sh, "\tCore Type: 0x%x", md.fw_core_type);
shell_print(sh, "\tComposition data hash: 0x%x", md.comp_hash);
shell_print(sh, "\tElements: %u", md.elems);
if (argc > 9) {
shell_print(sh, "\tUser data: %s", argv[10]);
}
shell_print(sh, "\tUser data length: %u", md.user_data_len);
err = bt_mesh_dfu_metadata_encode(&md, &buf);
if (err) {
shell_print(sh, "Failed to encode metadata: %d", err);
return err;
}
len = bin2hex(buf.data, buf.len, md_str, sizeof(md_str));
md_str[len] = '\0';
shell_print(sh, "Encoded metadata: %s", md_str);
return 0;
}
#endif /* CONFIG_BT_MESH_SHELL_DFU_METADATA */
#if defined(CONFIG_BT_MESH_SHELL_DFU_SLOT)
static int cmd_dfu_slot_add(const struct shell *sh, size_t argc, char *argv[])
{
const 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);
if (err) {
shell_warn(sh, "Unable to parse input string argument");
return err;
}
if (argc > 2) {
fwid_len = hex2bin(argv[2], strlen(argv[2]), fwid,
sizeof(fwid));
}
if (argc > 3) {
metadata_len = hex2bin(argv[3], strlen(argv[3]), metadata,
sizeof(metadata));
}
if (argc > 4) {
uri = argv[4];
}
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));
return 0;
}
static int cmd_dfu_slot_del(const struct shell *sh, size_t argc, char *argv[])
{
const struct bt_mesh_dfu_slot *slot;
uint8_t idx;
int err = 0;
idx = shell_strtoul(argv[1], 0, &err);
if (err) {
shell_warn(sh, "Unable to parse input string argument");
return err;
}
slot = bt_mesh_dfu_slot_at(idx);
if (!slot) {
shell_print(sh, "No slot at %u", idx);
return 0;
}
err = bt_mesh_dfu_slot_del(slot);
if (err) {
shell_print(sh, "Failed deleting slot %u (err: %d)", idx,
err);
return 0;
}
shell_print(sh, "Slot %u deleted.", idx);
return 0;
}
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;
}
shell_print(sh, "All slots deleted.");
return 0;
}
static void slot_info_print(const struct shell *sh, const struct bt_mesh_dfu_slot *slot,
const uint8_t *idx)
{
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));
fwid[len] = '\0';
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);
} else {
shell_print(sh, "Slot:");
}
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[])
{
const struct bt_mesh_dfu_slot *slot;
uint8_t idx;
int err = 0;
idx = shell_strtoul(argv[1], 0, &err);
if (err) {
shell_warn(sh, "Unable to parse input string argument");
return err;
}
slot = bt_mesh_dfu_slot_at(idx);
if (!slot) {
shell_print(sh, "No slot at %u", idx);
return 0;
}
slot_info_print(sh, slot, &idx);
return 0;
}
#endif /* defined(CONFIG_BT_MESH_SHELL_DFU_SLOT) */
#if defined(CONFIG_BT_MESH_SHELL_DFU_CLI)
static struct bt_mesh_model *mod_cli;
static struct {
struct bt_mesh_dfu_target targets[32];
struct bt_mesh_blob_target_pull pull[32];
size_t target_cnt;
struct bt_mesh_blob_cli_inputs inputs;
} dfu_tx;
static void dfu_tx_prepare(void)
{
sys_slist_init(&dfu_tx.inputs.targets);
for (size_t i = 0; i < dfu_tx.target_cnt; i++) {
/* Reset target context. */
uint16_t addr = dfu_tx.targets[i].blob.addr;
memset(&dfu_tx.targets[i].blob, 0, sizeof(struct bt_mesh_blob_target));
memset(&dfu_tx.pull[i], 0, sizeof(struct bt_mesh_blob_target_pull));
dfu_tx.targets[i].blob.addr = addr;
dfu_tx.targets[i].blob.pull = &dfu_tx.pull[i];
sys_slist_append(&dfu_tx.inputs.targets, &dfu_tx.targets[i].blob.n);
}
}
static int cmd_dfu_target(const struct shell *sh, size_t argc, char *argv[])
{
uint8_t img_idx;
uint16_t addr;
int err = 0;
addr = shell_strtoul(argv[1], 0, &err);
img_idx = shell_strtoul(argv[2], 0, &err);
if (err) {
shell_warn(sh, "Unable to parse input string argument");
return err;
}
if (dfu_tx.target_cnt == ARRAY_SIZE(dfu_tx.targets)) {
shell_print(sh, "No room.");
return 0;
}
for (size_t i = 0; i < dfu_tx.target_cnt; i++) {
if (dfu_tx.targets[i].blob.addr == addr) {
shell_print(sh, "Target 0x%04x already exists", addr);
return 0;
}
}
dfu_tx.targets[dfu_tx.target_cnt].blob.addr = addr;
dfu_tx.targets[dfu_tx.target_cnt].img_idx = img_idx;
sys_slist_append(&dfu_tx.inputs.targets, &dfu_tx.targets[dfu_tx.target_cnt].blob.n);
dfu_tx.target_cnt++;
shell_print(sh, "Added target 0x%04x", addr);
return 0;
}
static int cmd_dfu_targets_reset(const struct shell *sh, size_t argc, char *argv[])
{
dfu_tx_prepare();
return 0;
}
static int cmd_dfu_target_state(const struct shell *sh, size_t argc, char *argv[])
{
struct bt_mesh_dfu_target_status rsp;
struct bt_mesh_msg_ctx ctx = {
.send_ttl = BT_MESH_TTL_DEFAULT,
.net_idx = bt_mesh_shell_target_ctx.net_idx,
.addr = bt_mesh_shell_target_ctx.dst,
.app_idx = bt_mesh_shell_target_ctx.app_idx,
};
int err;
if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
return -ENODEV;
}
err = bt_mesh_dfu_cli_status_get((struct bt_mesh_dfu_cli *)mod_cli->user_data,
&ctx, &rsp);
if (err) {
shell_print(sh, "Failed getting target status (err: %d)",
err);
return 0;
}
shell_print(sh, "Target 0x%04x:", bt_mesh_shell_target_ctx.dst);
shell_print(sh, "\tStatus: %u", rsp.status);
shell_print(sh, "\tPhase: %u", rsp.phase);
if (rsp.phase != BT_MESH_DFU_PHASE_IDLE) {
shell_print(sh, "\tEffect: %u", rsp.effect);
shell_print(sh, "\tImg Idx: %u", rsp.img_idx);
shell_print(sh, "\tTTL: %u", rsp.ttl);
shell_print(sh, "\tTimeout base: %u", rsp.timeout_base);
}
return 0;
}
static enum bt_mesh_dfu_iter dfu_img_cb(struct bt_mesh_dfu_cli *cli,
struct bt_mesh_msg_ctx *ctx,
uint8_t idx, uint8_t total,
const struct bt_mesh_dfu_img *img,
void *cb_data)
{
char fwid[2 * CONFIG_BT_MESH_DFU_FWID_MAXLEN + 1];
size_t len;
len = bin2hex(img->fwid, img->fwid_len, fwid, sizeof(fwid));
fwid[len] = '\0';
shell_print(bt_mesh_shell_ctx_shell, "Image %u:", idx);
shell_print(bt_mesh_shell_ctx_shell, "\tFWID: %s", fwid);
if (img->uri) {
shell_print(bt_mesh_shell_ctx_shell, "\tURI: %s", img->uri);
}
return BT_MESH_DFU_ITER_CONTINUE;
}
static int cmd_dfu_target_imgs(const struct shell *sh, size_t argc, char *argv[])
{
struct bt_mesh_msg_ctx ctx = {
.send_ttl = BT_MESH_TTL_DEFAULT,
.net_idx = bt_mesh_shell_target_ctx.net_idx,
.addr = bt_mesh_shell_target_ctx.dst,
.app_idx = bt_mesh_shell_target_ctx.app_idx,
};
uint8_t img_cnt = 0xff;
int err = 0;
if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
return -ENODEV;
}
if (argc == 2) {
img_cnt = shell_strtoul(argv[1], 0, &err);
if (err) {
shell_warn(sh, "Unable to parse input string argument");
return err;
}
}
shell_print(sh, "Requesting DFU images in 0x%04x", bt_mesh_shell_target_ctx.dst);
err = bt_mesh_dfu_cli_imgs_get((struct bt_mesh_dfu_cli *)mod_cli->user_data,
&ctx, dfu_img_cb, NULL, img_cnt);
if (err) {
shell_print(sh, "Request failed (err: %d)", err);
}
return 0;
}
static int cmd_dfu_target_check(const struct shell *sh, size_t argc, char *argv[])
{
struct bt_mesh_dfu_metadata_status rsp;
const struct bt_mesh_dfu_slot *slot;
struct bt_mesh_msg_ctx ctx = {
.send_ttl = BT_MESH_TTL_DEFAULT,
.net_idx = bt_mesh_shell_target_ctx.net_idx,
.addr = bt_mesh_shell_target_ctx.dst,
.app_idx = bt_mesh_shell_target_ctx.app_idx,
};
uint8_t slot_idx, img_idx;
int err = 0;
if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
return -ENODEV;
}
slot_idx = shell_strtoul(argv[1], 0, &err);
img_idx = shell_strtoul(argv[2], 0, &err);
if (err) {
shell_warn(sh, "Unable to parse input string argument");
return err;
}
slot = bt_mesh_dfu_slot_at(slot_idx);
if (!slot) {
shell_print(sh, "No image in slot %u", slot_idx);
return 0;
}
err = bt_mesh_dfu_cli_metadata_check((struct bt_mesh_dfu_cli *)mod_cli->user_data,
&ctx, img_idx, slot, &rsp);
if (err) {
shell_print(sh, "Metadata check failed. err: %d", err);
return 0;
}
shell_print(sh, "Slot %u check for 0x%04x image %u:", slot_idx,
bt_mesh_shell_target_ctx.dst, img_idx);
shell_print(sh, "\tStatus: %u", rsp.status);
shell_print(sh, "\tEffect: 0x%x", rsp.effect);
return 0;
}
static int cmd_dfu_send(const struct shell *sh, size_t argc, char *argv[])
{
struct bt_mesh_dfu_cli_xfer_blob_params blob_params;
struct bt_mesh_dfu_cli_xfer xfer = { 0 };
uint8_t slot_idx;
uint16_t group;
int err = 0;
if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
return -ENODEV;
}
slot_idx = shell_strtoul(argv[1], 0, &err);
if (argc > 2) {
group = shell_strtoul(argv[2], 0, &err);
} else {
group = BT_MESH_ADDR_UNASSIGNED;
}
if (argc > 3) {
xfer.mode = shell_strtoul(argv[3], 0, &err);
} else {
xfer.mode = BT_MESH_BLOB_XFER_MODE_PUSH;
}
if (argc > 5) {
blob_params.block_size_log = shell_strtoul(argv[4], 0, &err);
blob_params.chunk_size = shell_strtoul(argv[5], 0, &err);
xfer.blob_params = &blob_params;
} else {
xfer.blob_params = NULL;
}
if (err) {
shell_warn(sh, "Unable to parse input string argument");
return err;
}
if (!dfu_tx.target_cnt) {
shell_print(sh, "No targets.");
return 0;
}
xfer.slot = bt_mesh_dfu_slot_at(slot_idx);
if (!xfer.slot) {
shell_print(sh, "No image in slot %u", slot_idx);
return 0;
}
shell_print(sh, "Starting DFU from slot %u (%u targets)", slot_idx,
dfu_tx.target_cnt);
dfu_tx.inputs.group = group;
dfu_tx.inputs.app_idx = bt_mesh_shell_target_ctx.app_idx;
dfu_tx.inputs.ttl = BT_MESH_TTL_DEFAULT;
err = bt_mesh_dfu_cli_send((struct bt_mesh_dfu_cli *)mod_cli->user_data,
&dfu_tx.inputs, bt_mesh_shell_blob_io, &xfer);
if (err) {
shell_print(sh, "Failed (err: %d)", err);
return 0;
}
return 0;
}
static int cmd_dfu_tx_cancel(const struct shell *sh, size_t argc, char *argv[])
{
struct bt_mesh_msg_ctx ctx = {
.send_ttl = BT_MESH_TTL_DEFAULT,
.net_idx = bt_mesh_shell_target_ctx.net_idx,
.addr = bt_mesh_shell_target_ctx.dst,
.app_idx = bt_mesh_shell_target_ctx.app_idx,
};
int err = 0;
if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
return -ENODEV;
}
if (argc == 2) {
ctx.addr = shell_strtoul(argv[1], 0, &err);
if (err) {
shell_warn(sh, "Unable to parse input string argument");
return err;
}
shell_print(sh, "Cancelling DFU for 0x%04x", ctx.addr);
} else {
shell_print(sh, "Cancelling DFU");
}
err = bt_mesh_dfu_cli_cancel((struct bt_mesh_dfu_cli *)mod_cli->user_data,
(argc == 2) ? &ctx : NULL);
if (err) {
shell_print(sh, "Failed (err: %d)", err);
}
return 0;
}
static int cmd_dfu_apply(const struct shell *sh, size_t argc, char *argv[])
{
int err;
if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
return -ENODEV;
}
shell_print(sh, "Applying DFU");
err = bt_mesh_dfu_cli_apply((struct bt_mesh_dfu_cli *)mod_cli->user_data);
if (err) {
shell_print(sh, "Failed (err: %d)", err);
}
return 0;
}
static int cmd_dfu_confirm(const struct shell *sh, size_t argc, char *argv[])
{
int err;
if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
return -ENODEV;
}
shell_print(sh, "Confirming DFU");
err = bt_mesh_dfu_cli_confirm((struct bt_mesh_dfu_cli *)mod_cli->user_data);
if (err) {
shell_print(sh, "Failed (err: %d)", err);
}
return 0;
}
static int cmd_dfu_suspend(const struct shell *sh, size_t argc, char *argv[])
{
int err;
if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
return -ENODEV;
}
shell_print(sh, "Suspending DFU");
err = bt_mesh_dfu_cli_suspend((struct bt_mesh_dfu_cli *)mod_cli->user_data);
if (err) {
shell_print(sh, "Failed (err: %d)", err);
}
return 0;
}
static int cmd_dfu_resume(const struct shell *sh, size_t argc, char *argv[])
{
int err;
if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
return -ENODEV;
}
shell_print(sh, "Resuming DFU");
err = bt_mesh_dfu_cli_resume((struct bt_mesh_dfu_cli *)mod_cli->user_data);
if (err) {
shell_print(sh, "Failed (err: %d)", err);
}
return 0;
}
static int cmd_dfu_tx_progress(const struct shell *sh, size_t argc, char *argv[])
{
if (!mod_cli && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_CLI, &mod_cli)) {
return -ENODEV;
}
shell_print(sh, "DFU progress: %u %%",
bt_mesh_dfu_cli_progress((struct bt_mesh_dfu_cli *)mod_cli->user_data));
return 0;
}
#endif /* CONFIG_BT_MESH_SHELL_DFU_CLI */
#if defined(CONFIG_BT_MESH_SHELL_DFU_SRV)
static struct bt_mesh_model *mod_srv;
static int cmd_dfu_applied(const struct shell *sh, size_t argc, char *argv[])
{
if (!mod_srv && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_SRV, &mod_srv)) {
return -ENODEV;
}
bt_mesh_dfu_srv_applied((struct bt_mesh_dfu_srv *)mod_srv->user_data);
return 0;
}
static int cmd_dfu_rx_cancel(const struct shell *sh, size_t argc, char *argv[])
{
if (!mod_srv && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_SRV, &mod_srv)) {
return -ENODEV;
}
bt_mesh_dfu_srv_cancel((struct bt_mesh_dfu_srv *)mod_srv->user_data);
return 0;
}
static int cmd_dfu_rx_progress(const struct shell *sh, size_t argc, char *argv[])
{
if (!mod_srv && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFU_SRV, &mod_srv)) {
return -ENODEV;
}
shell_print(sh, "DFU progress: %u %%",
bt_mesh_dfu_srv_progress((struct bt_mesh_dfu_srv *)mod_srv->user_data));
return 0;
}
#endif /* CONFIG_BT_MESH_SHELL_DFU_SRV */
#if defined(CONFIG_BT_MESH_SHELL_DFU_CLI)
BT_MESH_SHELL_MDL_INSTANCE_CMDS(cli_instance_cmds, BT_MESH_MODEL_ID_DFU_CLI, mod_cli);
#endif
#if defined(CONFIG_BT_MESH_SHELL_DFU_SRV)
BT_MESH_SHELL_MDL_INSTANCE_CMDS(srv_instance_cmds, BT_MESH_MODEL_ID_DFU_SRV, mod_srv);
#endif
#if defined(CONFIG_BT_MESH_SHELL_DFU_METADATA)
SHELL_STATIC_SUBCMD_SET_CREATE(
dfu_metadata_cmds,
SHELL_CMD_ARG(comp-clear, NULL, NULL, cmd_dfu_comp_clear, 1, 0),
SHELL_CMD_ARG(comp-add, NULL, "<CID> <ProductID> <VendorID> <Crpl> <Features>",
cmd_dfu_comp_add, 6, 0),
SHELL_CMD_ARG(comp-elem-add, NULL, "<Loc> <NumS> <NumV> "
"{<SigMID>|<VndCID> <VndMID>}...",
cmd_dfu_comp_elem_add, 5, 10),
SHELL_CMD_ARG(comp-hash-get, NULL, "[<Key>]", cmd_dfu_comp_hash_get, 1, 1),
SHELL_CMD_ARG(encode, NULL, "<Major> <Minor> <Rev> <BuildNum> <Size> "
"<CoreType> <Hash> <Elems> [<UserData>]",
cmd_dfu_metadata_encode, 9, 1),
SHELL_SUBCMD_SET_END);
#endif
#if defined(CONFIG_BT_MESH_SHELL_DFU_SLOT)
SHELL_STATIC_SUBCMD_SET_CREATE(
dfu_slot_cmds,
SHELL_CMD_ARG(add, NULL,
"<Size> [<FwID> [<Metadata> [<URI>]]]",
cmd_dfu_slot_add, 2, 3),
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),
SHELL_SUBCMD_SET_END);
#endif
#if defined(CONFIG_BT_MESH_SHELL_DFU_CLI)
SHELL_STATIC_SUBCMD_SET_CREATE(
dfu_cli_cmds,
/* DFU Client Model Operations */
SHELL_CMD_ARG(target, NULL, "<Addr> <ImgIdx>", cmd_dfu_target, 3,
0),
SHELL_CMD_ARG(targets-reset, NULL, NULL, cmd_dfu_targets_reset, 1, 0),
SHELL_CMD_ARG(target-state, NULL, NULL, cmd_dfu_target_state, 1, 0),
SHELL_CMD_ARG(target-imgs, NULL, "[<MaxCount>]",
cmd_dfu_target_imgs, 1, 1),
SHELL_CMD_ARG(target-check, NULL, "<SlotIdx> <TargetImgIdx>",
cmd_dfu_target_check, 3, 0),
SHELL_CMD_ARG(send, NULL, "<SlotIdx> [<Group> "
"[<Mode(push, pull)> [<BlockSizeLog> <ChunkSize>]]]", cmd_dfu_send, 2, 4),
SHELL_CMD_ARG(cancel, NULL, "[<Addr>]", cmd_dfu_tx_cancel, 1, 1),
SHELL_CMD_ARG(apply, NULL, NULL, cmd_dfu_apply, 0, 0),
SHELL_CMD_ARG(confirm, NULL, NULL, cmd_dfu_confirm, 0, 0),
SHELL_CMD_ARG(suspend, NULL, NULL, cmd_dfu_suspend, 0, 0),
SHELL_CMD_ARG(resume, NULL, NULL, cmd_dfu_resume, 0, 0),
SHELL_CMD_ARG(progress, NULL, NULL, cmd_dfu_tx_progress, 1, 0),
SHELL_CMD(instance, &cli_instance_cmds, "Instance commands", bt_mesh_shell_mdl_cmds_help),
SHELL_SUBCMD_SET_END);
#endif
#if defined(CONFIG_BT_MESH_SHELL_DFU_SRV)
SHELL_STATIC_SUBCMD_SET_CREATE(
dfu_srv_cmds,
SHELL_CMD_ARG(applied, NULL, NULL, cmd_dfu_applied, 1, 0),
SHELL_CMD_ARG(rx-cancel, NULL, NULL, cmd_dfu_rx_cancel, 1, 0),
SHELL_CMD_ARG(progress, NULL, NULL, cmd_dfu_rx_progress, 1, 0),
SHELL_CMD(instance, &srv_instance_cmds, "Instance commands", bt_mesh_shell_mdl_cmds_help),
SHELL_SUBCMD_SET_END);
#endif
SHELL_STATIC_SUBCMD_SET_CREATE(
dfu_cmds,
#if defined(CONFIG_BT_MESH_SHELL_DFU_METADATA)
SHELL_CMD(metadata, &dfu_metadata_cmds, "Metadata commands", bt_mesh_shell_mdl_cmds_help),
#endif
#if defined(CONFIG_BT_MESH_SHELL_DFU_SLOT)
SHELL_CMD(slot, &dfu_slot_cmds, "Slot commands", bt_mesh_shell_mdl_cmds_help),
#endif
#if defined(CONFIG_BT_MESH_SHELL_DFU_CLI)
SHELL_CMD(cli, &dfu_cli_cmds, "DFU Cli commands", bt_mesh_shell_mdl_cmds_help),
#endif
#if defined(CONFIG_BT_MESH_SHELL_DFU_SRV)
SHELL_CMD(srv, &dfu_srv_cmds, "DFU Srv commands", bt_mesh_shell_mdl_cmds_help),
#endif
SHELL_SUBCMD_SET_END);
SHELL_SUBCMD_ADD((mesh, models), dfu, &dfu_cmds, "DFU models commands",
bt_mesh_shell_mdl_cmds_help, 1, 1);