554 lines
17 KiB
C
554 lines
17 KiB
C
/**
|
|
* Copyright (c) 2017 - 2021, Nordic Semiconductor ASA
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form, except as embedded into a Nordic
|
|
* Semiconductor ASA integrated circuit in a product or a software update for
|
|
* such product, must reproduce the above copyright notice, this list of
|
|
* conditions and the following disclaimer in the documentation and/or other
|
|
* materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
|
* contributors may be used to endorse or promote products derived from this
|
|
* software without specific prior written permission.
|
|
*
|
|
* 4. This software, with or without modification, must only be used with a
|
|
* Nordic Semiconductor ASA integrated circuit.
|
|
*
|
|
* 5. Any software provided in binary form under this license must not be reverse
|
|
* engineered, decompiled, modified and/or disassembled.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include "app_error.h"
|
|
#include "boards.h"
|
|
#include "nrf_cli.h"
|
|
#include "nrf_cli_uart.h"
|
|
#include "nrf_drv_uart.h"
|
|
#include "fds.h"
|
|
#include "nrf_soc.h"
|
|
#include "sdk_config.h"
|
|
#include "fds_example.h"
|
|
|
|
|
|
#define PRINT_HELP "print records\n" \
|
|
"usage: print all|config"
|
|
|
|
#define PRINT_ALL_HELP "print all records\n" \
|
|
"usage: print all"
|
|
|
|
#define PRINT_CFG_HELP "print configuration\n" \
|
|
"usage: print config"
|
|
|
|
#define WRITE_HELP "write a record\n" \
|
|
"usage: write file_id key \"data\"\n" \
|
|
"- file_id:\tfile ID, in HEX\n" \
|
|
"- key:\trecord key, in HEX\n" \
|
|
"- data:\trecord contents"
|
|
|
|
#define DELETE_HELP "delete a record\n" \
|
|
"usage: delete file_id key\n" \
|
|
"- file_id:\tfile ID, in HEX\n" \
|
|
"- key:\trecord key, in HEX\n" \
|
|
|
|
#define DELETE_ALL_HELP "delete all records\n" \
|
|
"usage: delete_all" \
|
|
|
|
#define UPDATE_HELP "update configuration\n" \
|
|
"usage: update on|off on|off boot_count device_name" \
|
|
|
|
#define STAT_HELP "print statistics\n" \
|
|
"usage: stat"
|
|
|
|
#define GC_HELP "run garbage collection\n" \
|
|
"usage: gc"
|
|
|
|
|
|
NRF_CLI_UART_DEF(cli_uart, 0, 64, 16);
|
|
NRF_CLI_DEF(m_cli_uart, "fds example:~$ ", &cli_uart.transport, '\r', 4);
|
|
|
|
|
|
/* Defined in main.c */
|
|
extern char const * fds_err_str(ret_code_t ret);
|
|
|
|
|
|
void cli_init(void)
|
|
{
|
|
nrf_drv_uart_config_t uart_config = NRF_DRV_UART_DEFAULT_CONFIG;
|
|
uart_config.pseltxd = TX_PIN_NUMBER;
|
|
uart_config.pselrxd = RX_PIN_NUMBER;
|
|
uart_config.hwfc = NRF_UART_HWFC_DISABLED;
|
|
|
|
ret_code_t rc = nrf_cli_init(&m_cli_uart, &uart_config, true, true, NRF_LOG_SEVERITY_INFO);
|
|
APP_ERROR_CHECK(rc);
|
|
}
|
|
|
|
|
|
void cli_start(void)
|
|
{
|
|
ret_code_t rc = nrf_cli_start(&m_cli_uart);
|
|
APP_ERROR_CHECK(rc);
|
|
}
|
|
|
|
|
|
void cli_process(void)
|
|
{
|
|
nrf_cli_process(&m_cli_uart);
|
|
}
|
|
|
|
|
|
static void cli_unknown_param_help(nrf_cli_t const * p_cli,
|
|
char const * p_param,
|
|
char const * p_cmd)
|
|
{
|
|
nrf_cli_fprintf(p_cli,
|
|
NRF_CLI_ERROR,
|
|
"%s: unknown parameter '%s'\n"
|
|
"Try '%s -h' for help.\n",
|
|
p_cmd,
|
|
p_param,
|
|
p_cmd);
|
|
}
|
|
|
|
|
|
static void cli_wrong_param_count_help(nrf_cli_t const * p_cli, char const * p_cmd)
|
|
{
|
|
nrf_cli_fprintf(p_cli,
|
|
NRF_CLI_ERROR,
|
|
"%s: wrong parameter count.\n"
|
|
"Try '%s -h' for help.\n",
|
|
p_cmd,
|
|
p_cmd);
|
|
}
|
|
|
|
|
|
static void record_write(nrf_cli_t const * p_cli,
|
|
uint32_t fid,
|
|
uint32_t key,
|
|
void const * p_data,
|
|
uint32_t len)
|
|
{
|
|
fds_record_t const rec =
|
|
{
|
|
.file_id = fid,
|
|
.key = key,
|
|
.data.p_data = p_data,
|
|
.data.length_words = (len + 3) / sizeof(uint32_t)
|
|
};
|
|
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_DEFAULT,
|
|
"writing record to flash...\n"
|
|
"file: 0x%x, key: 0x%x, \"%s\", len: %u bytes\n",
|
|
fid, key, p_data, len);
|
|
|
|
ret_code_t rc = fds_record_write(NULL, &rec);
|
|
if (rc != NRF_SUCCESS)
|
|
{
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_ERROR,
|
|
"error: fds_record_write() returned %s.\n",
|
|
fds_err_str(rc));
|
|
}
|
|
}
|
|
|
|
|
|
static void record_update(nrf_cli_t const * p_cli, configuration_t const * p_cfg)
|
|
{
|
|
fds_record_desc_t desc = {0};
|
|
fds_find_token_t ftok = {0};
|
|
|
|
if (fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &ftok) == NRF_SUCCESS)
|
|
{
|
|
fds_record_t const rec =
|
|
{
|
|
.file_id = CONFIG_FILE,
|
|
.key = CONFIG_REC_KEY,
|
|
.data.p_data = p_cfg,
|
|
.data.length_words = (sizeof(configuration_t) + 3) / sizeof(uint32_t)
|
|
};
|
|
|
|
ret_code_t rc = fds_record_update(&desc, &rec);
|
|
if (rc != NRF_SUCCESS)
|
|
{
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_ERROR,
|
|
"error: fds_record_update() returned %s.\n",
|
|
fds_err_str(rc));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "error: could not find config file.\n");
|
|
}
|
|
}
|
|
|
|
|
|
static void record_delete(nrf_cli_t const * p_cli, uint32_t fid, uint32_t key)
|
|
{
|
|
fds_find_token_t tok = {0};
|
|
fds_record_desc_t desc = {0};
|
|
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_DEFAULT,
|
|
"deleting record...\n"
|
|
"file: 0x%x, key: 0x%x\n",
|
|
fid,
|
|
key);
|
|
|
|
if (fds_record_find(fid, key, &desc, &tok) == NRF_SUCCESS)
|
|
{
|
|
ret_code_t rc = fds_record_delete(&desc);
|
|
if (rc != NRF_SUCCESS)
|
|
{
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_ERROR,
|
|
"error: fds_record_delete() returned %s.\n", fds_err_str(rc));
|
|
|
|
return;
|
|
}
|
|
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_DEFAULT, "record id: 0x%x\n", desc.record_id);
|
|
}
|
|
else
|
|
{
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "error: record not found!\n");
|
|
}
|
|
}
|
|
|
|
|
|
bool record_delete_next(void)
|
|
{
|
|
fds_find_token_t tok = {0};
|
|
fds_record_desc_t desc = {0};
|
|
|
|
if (fds_record_iterate(&desc, &tok) == NRF_SUCCESS)
|
|
{
|
|
ret_code_t rc = fds_record_delete(&desc);
|
|
if (rc != NRF_SUCCESS)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
/* No records left to delete. */
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
static void print_cmd(nrf_cli_t const * p_cli, size_t argc, char ** argv)
|
|
{
|
|
if (nrf_cli_help_requested(p_cli))
|
|
{
|
|
nrf_cli_help_print(p_cli, NULL, 0);
|
|
}
|
|
else if (argc != 2)
|
|
{
|
|
cli_wrong_param_count_help(p_cli, "print");
|
|
}
|
|
else
|
|
{
|
|
cli_unknown_param_help(p_cli, argv[1], "print");
|
|
}
|
|
}
|
|
|
|
|
|
static void print_cfg_cmd(nrf_cli_t const * p_cli, size_t argc, char ** argv)
|
|
{
|
|
fds_record_desc_t desc = {0};
|
|
fds_find_token_t tok = {0};
|
|
|
|
while (fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok) == NRF_SUCCESS)
|
|
{
|
|
ret_code_t rc;
|
|
fds_flash_record_t frec = {0};
|
|
|
|
rc = fds_record_open(&desc, &frec);
|
|
switch (rc)
|
|
{
|
|
case NRF_SUCCESS:
|
|
break;
|
|
|
|
case FDS_ERR_CRC_CHECK_FAILED:
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "error: CRC check failed!\n");
|
|
continue;
|
|
|
|
case FDS_ERR_NOT_FOUND:
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "error: record not found!\n");
|
|
continue;
|
|
|
|
default:
|
|
{
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_ERROR,
|
|
"error: unexpecte error %s.\n",
|
|
fds_err_str(rc));
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
configuration_t * p_cfg = (configuration_t *)(frec.p_data);
|
|
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_DEFAULT,
|
|
"config1:\t%s\n"
|
|
"config2:\t%s\n"
|
|
"boot count:\t%u\n"
|
|
"device name:\t%s\n",
|
|
p_cfg->config1_on ? "on" : "off",
|
|
p_cfg->config2_on ? "on" : "off",
|
|
p_cfg->boot_count,
|
|
p_cfg->device_name);
|
|
|
|
rc = fds_record_close(&desc);
|
|
APP_ERROR_CHECK(rc);
|
|
}
|
|
}
|
|
|
|
|
|
static void print_all_cmd(nrf_cli_t const * p_cli, size_t argc, char ** argv)
|
|
{
|
|
fds_find_token_t tok = {0};
|
|
fds_record_desc_t desc = {0};
|
|
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_DEFAULT,
|
|
"rec. id\t"
|
|
"\tfile id\t"
|
|
"\trec. key"
|
|
"\tlength\n");
|
|
|
|
while (fds_record_iterate(&desc, &tok) != FDS_ERR_NOT_FOUND)
|
|
{
|
|
ret_code_t rc;
|
|
fds_flash_record_t frec = {0};
|
|
|
|
rc = fds_record_open(&desc, &frec);
|
|
switch (rc)
|
|
{
|
|
case NRF_SUCCESS:
|
|
break;
|
|
|
|
case FDS_ERR_CRC_CHECK_FAILED:
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "error: CRC check failed!\n");
|
|
continue;
|
|
|
|
case FDS_ERR_NOT_FOUND:
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "error: record not found!\n");
|
|
continue;
|
|
|
|
default:
|
|
{
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_ERROR,
|
|
"error: unexpecte error %s.\n",
|
|
fds_err_str(rc));
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
uint32_t const len = frec.p_header->length_words * sizeof(uint32_t);
|
|
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_DEFAULT,
|
|
" 0x%04x\t"
|
|
"\t 0x%04x\t"
|
|
"\t 0x%04x\t"
|
|
"\t %4u bytes\n",
|
|
frec.p_header->record_id,
|
|
frec.p_header->file_id,
|
|
frec.p_header->record_key,
|
|
len);
|
|
|
|
rc = fds_record_close(&desc);
|
|
APP_ERROR_CHECK(rc);
|
|
}
|
|
}
|
|
|
|
|
|
static void write_cmd(nrf_cli_t const * p_cli, size_t argc, char ** argv)
|
|
{
|
|
|
|
if (nrf_cli_help_requested(p_cli))
|
|
{
|
|
nrf_cli_help_print(p_cli, NULL, 0);
|
|
}
|
|
else if (argc != 4)
|
|
{
|
|
cli_wrong_param_count_help(p_cli, "write");
|
|
}
|
|
else
|
|
{
|
|
/* Must be statically allocated, because it is going to be written in flash. */
|
|
static uint8_t m_data[256];
|
|
|
|
uint16_t const fid = strtol(argv[1], NULL, 16);
|
|
uint16_t const key = strtol(argv[2], NULL, 16);
|
|
|
|
uint32_t const len = strlen(argv[3]) < sizeof(m_data) ?
|
|
strlen(argv[3]) : sizeof(m_data);
|
|
|
|
/* Copy data to static variable. */
|
|
memset(m_data, 0x00, sizeof(m_data));
|
|
memcpy(m_data, argv[3], len);
|
|
|
|
record_write(p_cli, fid, key, m_data, len);
|
|
}
|
|
}
|
|
|
|
|
|
static void update_cmd(nrf_cli_t const * p_cli, size_t argc, char ** argv)
|
|
{
|
|
if (nrf_cli_help_requested(p_cli))
|
|
{
|
|
nrf_cli_help_print(p_cli, NULL, 0);
|
|
}
|
|
else if (argc != 5)
|
|
{
|
|
cli_wrong_param_count_help(p_cli, "update");
|
|
}
|
|
else
|
|
{
|
|
/* Must be statically allocated, because it is going to be written in flash. */
|
|
static configuration_t cfg = {0};
|
|
|
|
memset(&cfg, 0x00, sizeof(configuration_t));
|
|
|
|
cfg.config1_on = !strcmp(argv[1], "on");
|
|
cfg.config2_on = !strcmp(argv[2], "on");
|
|
cfg.boot_count = strtol(argv[3], NULL, 10);
|
|
|
|
uint16_t const len = strlen(argv[4]) < sizeof(cfg.device_name) ?
|
|
strlen(argv[4]) : sizeof(cfg.device_name);
|
|
|
|
memcpy(cfg.device_name, argv[4], len);
|
|
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_DEFAULT,
|
|
"updating configuration: %s, %s, boot_count=%u, device_name=\"%s\"\n",
|
|
cfg.config1_on ? "on" : "off",
|
|
cfg.config2_on ? "on" : "off",
|
|
cfg.boot_count,
|
|
cfg.device_name);
|
|
|
|
record_update(p_cli, &cfg);
|
|
}
|
|
}
|
|
|
|
|
|
static void delete_cmd(nrf_cli_t const * p_cli, size_t argc, char ** argv)
|
|
{
|
|
if (nrf_cli_help_requested(p_cli))
|
|
{
|
|
nrf_cli_help_print(p_cli, NULL, 0);
|
|
}
|
|
else if (argc < 3)
|
|
{
|
|
cli_wrong_param_count_help(p_cli, "delete");
|
|
}
|
|
else
|
|
{
|
|
uint32_t const fid = strtol(argv[1], NULL, 16);
|
|
uint32_t const key = strtol(argv[2], NULL, 16);
|
|
|
|
record_delete(p_cli, fid, key);
|
|
}
|
|
}
|
|
|
|
|
|
static void delete_all_cmd(nrf_cli_t const * p_cli, size_t argc, char ** argv)
|
|
{
|
|
if (nrf_cli_help_requested(p_cli))
|
|
{
|
|
nrf_cli_help_print(p_cli, NULL, 0);
|
|
}
|
|
else if (argc != 1)
|
|
{
|
|
cli_wrong_param_count_help(p_cli, "delete_all");
|
|
}
|
|
else
|
|
{
|
|
delete_all_begin();
|
|
}
|
|
}
|
|
|
|
|
|
static void stat_cmd(nrf_cli_t const * p_cli, size_t argc, char **argv)
|
|
{
|
|
if (nrf_cli_help_requested(p_cli))
|
|
{
|
|
nrf_cli_help_print(p_cli, NULL, 0);
|
|
}
|
|
else
|
|
{
|
|
/* Print fds_stat(). */
|
|
fds_stat_t stat = {0};
|
|
|
|
ret_code_t rc = fds_stat(&stat);
|
|
APP_ERROR_CHECK(rc);
|
|
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_DEFAULT,
|
|
"total pages:\t%u\n"
|
|
"total records:\t%u\n"
|
|
"valid records:\t%u\n"
|
|
"dirty records:\t%u\n"
|
|
"largest contig:\t%u\n"
|
|
"freeable words:\t%u (%u bytes)\n",
|
|
stat.pages_available,
|
|
stat.valid_records + stat.dirty_records,
|
|
stat.valid_records,
|
|
stat.dirty_records,
|
|
stat.largest_contig,
|
|
stat.freeable_words,
|
|
stat.freeable_words * sizeof(uint32_t));
|
|
}
|
|
}
|
|
|
|
|
|
static void gc_cmd(nrf_cli_t const * p_cli, size_t argc, char ** argv)
|
|
{
|
|
ret_code_t rc = fds_gc();
|
|
switch (rc)
|
|
{
|
|
case NRF_SUCCESS:
|
|
break;
|
|
|
|
default:
|
|
nrf_cli_fprintf(p_cli, NRF_CLI_ERROR,
|
|
"error: garbage collection returned %s\n", fds_err_str(rc));
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
NRF_CLI_CREATE_STATIC_SUBCMD_SET(m_print)
|
|
{
|
|
NRF_CLI_CMD(all, NULL, PRINT_ALL_HELP, print_all_cmd),
|
|
NRF_CLI_CMD(config, NULL, PRINT_CFG_HELP, print_cfg_cmd),
|
|
NRF_CLI_SUBCMD_SET_END
|
|
};
|
|
|
|
|
|
NRF_CLI_CMD_REGISTER(print, &m_print, PRINT_HELP, print_cmd);
|
|
NRF_CLI_CMD_REGISTER(write, NULL, WRITE_HELP, write_cmd);
|
|
NRF_CLI_CMD_REGISTER(update, NULL, UPDATE_HELP, update_cmd);
|
|
NRF_CLI_CMD_REGISTER(delete, NULL, DELETE_HELP, delete_cmd);
|
|
NRF_CLI_CMD_REGISTER(delete_all, NULL, DELETE_ALL_HELP, delete_all_cmd);
|
|
NRF_CLI_CMD_REGISTER(gc, NULL, GC_HELP, gc_cmd);
|
|
NRF_CLI_CMD_REGISTER(stat, NULL, STAT_HELP, stat_cmd);
|