Files
2023-05-04 20:56:36 +08:00

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);