436 lines
12 KiB
C
436 lines
12 KiB
C
/*
|
|
* Copyright (c) 2022 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <zephyr/bluetooth/mesh.h>
|
|
#include <zephyr/shell/shell.h>
|
|
|
|
#include "utils.h"
|
|
|
|
#include "../dfu_slot.h"
|
|
#include "../dfd_srv_internal.h"
|
|
#include "../access.h"
|
|
|
|
static const struct bt_mesh_model *mod;
|
|
|
|
static void print_receivers_status(const struct shell *sh, struct bt_mesh_dfd_srv *srv,
|
|
enum bt_mesh_dfd_status status)
|
|
{
|
|
shell_print(sh, "{\"status\": %d, \"target_cnt\": %d}", status, srv->target_cnt);
|
|
}
|
|
|
|
static void print_dfd_status(const struct shell *sh, struct bt_mesh_dfd_srv *srv,
|
|
enum bt_mesh_dfd_status status)
|
|
{
|
|
shell_fprintf(sh, SHELL_NORMAL, "{ \"status\": %d, \"phase\": %d", status,
|
|
srv->phase);
|
|
|
|
if (srv->phase != BT_MESH_DFD_PHASE_IDLE && srv->dfu.xfer.slot) {
|
|
shell_fprintf(sh, SHELL_NORMAL, ", \"group\": %d, \"app_idx\": %d, "
|
|
"\"ttl\": %d, \"timeout_base\": %d, \"xfer_mode\": %d, "
|
|
"\"apply\": %d, \"slot_idx\": %d", srv->inputs.group,
|
|
srv->inputs.app_idx, srv->inputs.ttl, srv->inputs.timeout_base,
|
|
srv->dfu.xfer.blob.mode, srv->apply, srv->slot_idx);
|
|
}
|
|
|
|
shell_print(sh, " }");
|
|
}
|
|
|
|
static void print_fw_status(const struct shell *sh, enum bt_mesh_dfd_status status,
|
|
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_count(), idx);
|
|
if (fwid) {
|
|
shell_fprintf(sh, SHELL_NORMAL, ", \"fwid\": \"");
|
|
for (size_t i = 0; i < fwid_len; i++) {
|
|
shell_fprintf(sh, SHELL_NORMAL, "%02x", fwid[i]);
|
|
}
|
|
shell_fprintf(sh, SHELL_NORMAL, "\"");
|
|
}
|
|
shell_print(sh, " }");
|
|
}
|
|
|
|
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 cmd_dfd_receivers_add(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
|
|
|
|
if (bt_mesh_dfu_cli_is_busy(&dfd_srv->dfu)) {
|
|
print_receivers_status(sh, dfd_srv,
|
|
BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION);
|
|
return -EBUSY;
|
|
}
|
|
|
|
char *outer_state, *inner_state;
|
|
|
|
char *token = strtok_r(argv[1], ";", &outer_state);
|
|
|
|
while (token) {
|
|
char *addr_str = strtok_r(token, ",", &inner_state);
|
|
char *img_idx_str = strtok_r(NULL, ",", &inner_state);
|
|
int err = 0;
|
|
|
|
if (addr_str == NULL || img_idx_str == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
uint16_t addr = shell_strtoul(addr_str, 0, &err);
|
|
uint8_t img_idx = shell_strtoul(img_idx_str, 0, &err);
|
|
|
|
if (err) {
|
|
shell_warn(sh, "Unable to parse input string argument");
|
|
return err;
|
|
}
|
|
|
|
enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_receiver_add(
|
|
dfd_srv, addr, img_idx);
|
|
|
|
if (status != BT_MESH_DFD_SUCCESS) {
|
|
print_receivers_status(sh, dfd_srv, status);
|
|
return status == BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES ?
|
|
-ENOSPC : -EINVAL;
|
|
}
|
|
|
|
token = strtok_r(NULL, ";", &outer_state);
|
|
}
|
|
|
|
print_receivers_status(sh, dfd_srv, BT_MESH_DFD_SUCCESS);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_dfd_receivers_delete_all(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
|
|
|
|
enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_receivers_delete_all(
|
|
dfd_srv);
|
|
|
|
print_receivers_status(sh, dfd_srv, status);
|
|
|
|
if (status != BT_MESH_DFD_SUCCESS) {
|
|
return status == BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION ? -EBUSY : -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_dfd_receivers_get(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
|
|
int err = 0;
|
|
|
|
uint16_t first = shell_strtoul(argv[1], 0, &err);
|
|
uint16_t cnt = shell_strtoul(argv[2], 0, &err);
|
|
|
|
if (err) {
|
|
shell_warn(sh, "Unable to parse input string argument");
|
|
return err;
|
|
}
|
|
|
|
if (cnt == 0 || dfd_srv->target_cnt <= first) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
cnt = MIN(cnt, dfd_srv->target_cnt - first);
|
|
uint8_t progress = bt_mesh_dfu_cli_progress(&dfd_srv->dfu) / 2;
|
|
|
|
shell_print(sh, "{\n\t\"target_cnt\": %d,\n\t\"targets\": {",
|
|
dfd_srv->target_cnt);
|
|
for (int i = 0; i < cnt; i++) {
|
|
const struct bt_mesh_dfu_target *t = &dfd_srv->targets[i + first];
|
|
|
|
shell_print(sh, "\t\t\"%d\": { \"blob_addr\": %d, \"phase\": %d, "
|
|
"\"status\": %d, \"blob_status\": %d, \"progress\": %d, "
|
|
"\"img_idx\": %d }%s", i + first, t->blob.addr, t->phase, t->status,
|
|
t->blob.status, progress, t->img_idx, (i == cnt - 1) ? "" : ",");
|
|
}
|
|
shell_print(sh, "\t}\n}");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_dfd_capabilities_get(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
size_t size = 0;
|
|
/* Remaining size */
|
|
(void)bt_mesh_dfu_slot_foreach(slot_space_cb, &size);
|
|
size = MIN(size, CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE);
|
|
|
|
shell_print(sh, "{ \"targets_max\": %d, \"slot_cnt\": %d, \"slot_max_size\": %d, "
|
|
"\"slot_space\": %d, \"remaining_space\": %d, \"oob_supported\": false }",
|
|
CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX, CONFIG_BT_MESH_DFU_SLOT_CNT,
|
|
CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE, CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE,
|
|
CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE - size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_dfd_get(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
|
|
|
|
print_dfd_status(sh, dfd_srv, BT_MESH_DFD_SUCCESS);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_dfd_start(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
|
|
struct bt_mesh_dfd_start_params params;
|
|
int err = 0;
|
|
|
|
params.app_idx = shell_strtoul(argv[1], 0, &err);
|
|
params.slot_idx = shell_strtoul(argv[2], 0, &err);
|
|
if (argc > 3) {
|
|
params.group = shell_strtoul(argv[3], 0, &err);
|
|
} else {
|
|
params.group = BT_MESH_ADDR_UNASSIGNED;
|
|
}
|
|
|
|
if (argc > 4) {
|
|
params.apply = shell_strtobool(argv[4], 0, &err);
|
|
} else {
|
|
params.apply = true;
|
|
}
|
|
|
|
if (argc > 5) {
|
|
params.ttl = shell_strtoul(argv[5], 0, &err);
|
|
} else {
|
|
params.ttl = BT_MESH_TTL_DEFAULT;
|
|
}
|
|
|
|
if (argc > 6) {
|
|
params.timeout_base = shell_strtoul(argv[6], 0, &err);
|
|
} else {
|
|
params.timeout_base = 0U;
|
|
}
|
|
|
|
if (argc > 7) {
|
|
params.xfer_mode = (enum bt_mesh_blob_xfer_mode)shell_strtoul(argv[7], 0, &err);
|
|
} else {
|
|
params.xfer_mode = BT_MESH_BLOB_XFER_MODE_PUSH;
|
|
}
|
|
|
|
if (err) {
|
|
shell_warn(sh, "Unable to parse input string argument");
|
|
return err;
|
|
}
|
|
|
|
enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_start(dfd_srv, ¶ms);
|
|
|
|
print_dfd_status(sh, dfd_srv, status);
|
|
if (status != BT_MESH_DFD_SUCCESS) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_dfd_suspend(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
|
|
|
|
enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_suspend(dfd_srv);
|
|
|
|
print_dfd_status(sh, dfd_srv, status);
|
|
if (status != BT_MESH_DFD_SUCCESS) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_dfd_cancel(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
|
|
|
|
enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_cancel(dfd_srv, NULL);
|
|
|
|
print_dfd_status(sh, dfd_srv, status);
|
|
if (status != BT_MESH_DFD_SUCCESS) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_dfd_apply(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
|
|
|
|
enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_apply(dfd_srv);
|
|
|
|
print_dfd_status(sh, dfd_srv, status);
|
|
if (status != BT_MESH_DFD_SUCCESS) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_dfd_fw_get(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN];
|
|
size_t hexlen = strlen(argv[1]);
|
|
size_t fwid_len = hex2bin(argv[1], hexlen, fwid, CONFIG_BT_MESH_DFU_FWID_MAXLEN);
|
|
|
|
if (fwid_len != ((hexlen + 1) / 2)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
int idx = bt_mesh_dfu_slot_get(fwid, fwid_len, NULL);
|
|
|
|
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);
|
|
return -ENOENT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_dfd_fw_get_by_idx(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
int err = 0;
|
|
uint16_t idx = shell_strtoul(argv[1], 0, &err);
|
|
const struct bt_mesh_dfu_slot *slot = bt_mesh_dfu_slot_at(idx);
|
|
|
|
if (err) {
|
|
shell_warn(sh, "Unable to parse input string argument");
|
|
return err;
|
|
}
|
|
|
|
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);
|
|
return -ENOENT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_dfd_fw_delete(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
|
|
|
|
uint8_t fwid_buf[CONFIG_BT_MESH_DFU_FWID_MAXLEN];
|
|
size_t hexlen = strlen(argv[1]);
|
|
size_t fwid_len = hex2bin(argv[1], hexlen, fwid_buf, CONFIG_BT_MESH_DFU_FWID_MAXLEN);
|
|
|
|
if (fwid_len != ((hexlen + 1) / 2)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
const uint8_t *fwid = &fwid_buf[0];
|
|
|
|
enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_fw_delete(dfd_srv,
|
|
&fwid_len, &fwid);
|
|
|
|
print_fw_status(sh, status, 0xffff, fwid, fwid_len);
|
|
|
|
if (status != BT_MESH_DFD_SUCCESS) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_dfd_fw_delete_all(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
|
|
|
|
enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_fw_delete_all(dfd_srv);
|
|
|
|
print_fw_status(sh, status, 0xffff, NULL, 0);
|
|
|
|
if (status != BT_MESH_DFD_SUCCESS) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
BT_MESH_SHELL_MDL_INSTANCE_CMDS(instance_cmds, BT_MESH_MODEL_ID_DFD_SRV, mod);
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(
|
|
dfd_cmds,
|
|
SHELL_CMD_ARG(receivers-add, NULL, "<Addr>,<FwIdx>[;<Addr>,<FwIdx>]...",
|
|
cmd_dfd_receivers_add, 2, 0),
|
|
SHELL_CMD_ARG(receivers-delete-all, NULL, NULL, cmd_dfd_receivers_delete_all, 1, 0),
|
|
SHELL_CMD_ARG(receivers-get, NULL, "<First> <Count>", cmd_dfd_receivers_get, 3, 0),
|
|
SHELL_CMD_ARG(capabilities-get, NULL, NULL, cmd_dfd_capabilities_get, 1, 0),
|
|
SHELL_CMD_ARG(get, NULL, NULL, cmd_dfd_get, 1, 0),
|
|
SHELL_CMD_ARG(start, NULL,
|
|
"<AppKeyIdx> <SlotIdx> [<Group> [<PolicyApply> [<TTL> "
|
|
"[<TimeoutBase> [<XferMode>]]]]]",
|
|
cmd_dfd_start, 3, 5),
|
|
SHELL_CMD_ARG(suspend, NULL, NULL, cmd_dfd_suspend, 1, 0),
|
|
SHELL_CMD_ARG(cancel, NULL, NULL, cmd_dfd_cancel, 1, 0),
|
|
SHELL_CMD_ARG(apply, NULL, NULL, cmd_dfd_apply, 1, 0),
|
|
SHELL_CMD_ARG(fw-get, NULL, "<FwID>", cmd_dfd_fw_get, 2, 0),
|
|
SHELL_CMD_ARG(fw-get-by-idx, NULL, "<Idx>", cmd_dfd_fw_get_by_idx, 2, 0),
|
|
SHELL_CMD_ARG(fw-delete, NULL, "<FwID>", cmd_dfd_fw_delete, 2, 0),
|
|
SHELL_CMD_ARG(fw-delete-all, NULL, NULL, cmd_dfd_fw_delete_all, 1, 0),
|
|
SHELL_CMD(instance, &instance_cmds, "Instance commands", bt_mesh_shell_mdl_cmds_help),
|
|
SHELL_SUBCMD_SET_END);
|
|
|
|
SHELL_SUBCMD_ADD((mesh, models), dfd, &dfd_cmds, "Distributor commands",
|
|
bt_mesh_shell_mdl_cmds_help, 1, 1);
|