123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799 |
- /**
- * Copyright (c) 2018 - 2019, 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 <stddef.h>
- #include <stdint.h>
- #include <stdbool.h>
- #include <string.h>
- #include "sdk_config.h"
- #include "nrf_cli.h"
- #include "cli_utils.h"
- #include "raw.h"
- #include "vars.h"
- /* =============================================================================================
- * Configuration and definitions
- * =============================================================================================
- */
- #ifndef APP_VAR_MEMORY_SIZE
- #define APP_VAR_MEMORY_SIZE (16 * 1024) ///< @brief Define default size of the variable memory space.
- #endif
- #define TEMP_PREFIX "$_TMP@" ///< @brief Variable name prefix for temporary variables.
- #define TEMP_PREFIX_LEN 6 ///< @brief Length of @ref TEMP_PREFIX.
- /* =============================================================================================
- * Variable memory buffer definition
- * =============================================================================================
- */
- #define VAR_MEMORY_BEGIN ((uint8_t *)var_memory) ///< @brief Returns pointer to the first byte of the variable memory space.
- #define VAR_MEMORY_END ((uint8_t *)var_memory + APP_VAR_MEMORY_SIZE) ///< @brief Returns pointer to the next byte after the last byte of the variable memory space.
- static uint32_t var_memory[(APP_VAR_MEMORY_SIZE + 3) / 4]; ///< @brief Array holding variable memory space. It is uint32_t to force correct alignment.
- /* =============================================================================================
- * Dynamically allocated memory block type definition
- * =============================================================================================
- */
- /** @brief Callback function to prevent user from deleting a dynamically allocated block. @see var_free_fn_t.
- */
- static bool alloc_user_free_prevent(var_t * p_var)
- {
- nrf_cli_fprintf(cli_get(), NRF_CLI_WARNING, "Cannot delete dynamically allocated memory.\n");
- return false;
- }
- /** @brief Variable type that contains the dynamically allocated block.
- */
- static var_type_t var_alloc =
- {
- .p_description = "Dynamically allocated memory buffer",
- .free_fn = alloc_user_free_prevent,
- .print_fn = NULL,
- .p_tag = NULL,
- };
- /* =============================================================================================
- * Block access functions
- * =============================================================================================
- */
- /** @brief Returns the first block of the variable memory space, including free and dynamically allocated blocks.
- */
- static var_header_t * block_begin()
- {
- return (var_header_t *)VAR_MEMORY_BEGIN;
- }
- /** @brief Returns the next block after the one provided.
- */
- static var_header_t * block_next(var_header_t * current)
- {
- uint8_t * p_this = (uint8_t *)current;
- uint8_t * p_next = p_this + current->total_size;
- if (p_next < VAR_MEMORY_END)
- {
- return (var_header_t *)p_next;
- }
- else
- {
- return NULL;
- }
- }
- /** @brief Returns true if block is a variable, meaning it is not free and it is not a dynamically allocated block.
- */
- static bool block_is_var(var_header_t * p_block)
- {
- return (p_block->p_type != NULL) && (p_block->p_type != &var_alloc);
- }
- /** @brief Combines all neighboring free blocks, starting with the block provided in the parameter.
- */
- static void block_free_space_consolidate(var_header_t * p_block)
- {
- var_header_t * p_next_block;
- if ((p_block == NULL) || (p_block->p_type != NULL))
- {
- return;
- }
- p_next_block = block_next(p_block);
- while ((p_next_block != NULL) && (p_next_block->p_type == NULL))
- {
- p_block->total_size += p_next_block->total_size;
- p_next_block = block_next(p_block);
- }
- }
- /** @brief Allocates a block inside the variable memory space.
- *
- * Only total size of the block is set after this call. The rest of data (including content) is zeroed.
- * @param size Requested total size of a block, including header.
- * @param verbose Prints messages in case of problems if true.
- * @return Pointer to the newly allocated block, or NULL if there is no space available.
- */
- static var_header_t * block_allocate(size_t size, bool verbose)
- {
- var_header_t * p_block;
- var_header_t * p_next_block;
- uint32_t allocate_size = (size + 3) & ~3;
- // Iterate over all blocks
- for (p_block = block_begin(); p_block != NULL; p_block = block_next(p_block))
- {
- // Skip used blocks
- if (p_block->p_type != NULL)
- {
- continue;
- }
- // If after this unused block there are also unused blocks, merge them all.
- block_free_space_consolidate(p_block);
- // If this unused block is big enough to hold requested size, use it.
- if (p_block->total_size >= allocate_size)
- {
- size_t old_size = p_block->total_size;
- memset(p_block, 0x00, p_block->total_size);
- if (old_size - allocate_size > sizeof(var_header_t))
- {
- // This block is bigger than requested, so divide it into two blocks.
- p_block->total_size = allocate_size;
- p_next_block = block_next(p_block);
- p_next_block->p_type = NULL;
- p_next_block->total_size = old_size - allocate_size;
- }
- else
- {
- p_block->total_size = old_size;
- }
- return p_block;
- }
- }
- if (verbose)
- {
- nrf_cli_fprintf(cli_get(), NRF_CLI_WARNING, "No more space in variable memory\n");
- }
- return NULL;
- }
- /* =============================================================================================
- * Variable access functions
- * =============================================================================================
- */
- void var_init(void)
- {
- var_header_t * initial_block = block_begin();
- initial_block->total_size = APP_VAR_MEMORY_SIZE;
- initial_block->p_type = NULL;
- }
- var_t * var_begin(void)
- {
- var_header_t * p_start = block_begin();
- if (block_is_var(p_start))
- {
- return (var_t *)p_start;
- }
- else
- {
- return var_next((var_t *)p_start);
- }
- }
- var_t * var_next(var_t * current_var)
- {
- var_header_t * p_next = block_next((var_header_t *)current_var);
- while ((p_next != NULL) && !block_is_var(p_next))
- {
- p_next = block_next(p_next);
- }
- return (var_t *)p_next;
- }
- var_t * var_get(const char * p_name, bool verbose)
- {
- var_t * p_var;
- // Iterate over all objects
- for (p_var = var_begin(); p_var != NULL; p_var = var_next(p_var))
- {
- if (strcmp(p_var->name, p_name) == 0)
- {
- return p_var;
- }
- }
- if (verbose)
- {
- nrf_cli_fprintf(cli_get(), NRF_CLI_WARNING, "Variable '%s' not found\n", p_name);
- }
- return NULL;
- }
- /** @brief Checks whether name of the variable is valid.
- * @param p_name Name to check.
- * @param verbose Print messages if name is invalid.
- * @return true is name is valid.
- */
- static bool var_name_validate(const char * p_name, bool verbose)
- {
- size_t i;
- size_t len = strlen(p_name);
- if (p_name[0] != '$')
- {
- if (verbose)
- {
- nrf_cli_fprintf(cli_get(), NRF_CLI_WARNING, "Variable name must start with '$' sign\n");
- }
- return false;
- }
- else if (len <= 1)
- {
- if (verbose)
- {
- nrf_cli_fprintf(cli_get(), NRF_CLI_WARNING, "Variable name empty\n");
- }
- return false;
- }
- else if (len > VAR_NAME_LENGTH_MAX)
- {
- if (verbose)
- {
- nrf_cli_fprintf(cli_get(),
- NRF_CLI_WARNING,
- "Variable name too long, maximum %d, actual %d\n",
- VAR_NAME_LENGTH_MAX,
- len);
- }
- return false;
- }
- for (i = 0; i < len; i++)
- {
- if ((p_name[i] <= ' ') || (p_name[i] >= '\x7F'))
- {
- if (verbose)
- {
- nrf_cli_fprintf(cli_get(),
- NRF_CLI_WARNING,
- "Invalid character in variable name\n");
- }
- return false;
- }
- }
- return true;
- }
- var_t * var_create(const char * p_name, var_type_t const * p_type, size_t size, bool verbose)
- {
- var_t * p_var;
- if (!var_name_validate(p_name, verbose))
- {
- return NULL;
- }
- p_var = var_get(p_name, false);
- if (p_var != NULL)
- {
- if (verbose)
- {
- nrf_cli_fprintf(cli_get(),
- NRF_CLI_INFO,
- "Variable '%s' already exists, overriding.\n",
- p_name);
- }
- if (!var_delete(p_var->name, verbose))
- {
- return NULL;
- }
- }
- p_var = (var_t *)block_allocate(sizeof(var_t) + size, verbose);
- if (p_var == NULL)
- {
- return NULL;
- }
- p_var->data_size = size;
- strcpy(p_var->name, p_name);
- p_var->header.p_type = p_type;
- return p_var;
- }
- /** @brief Deletes variable by using pointer to the variable.
- * @param p_var Pointer to variable to delete.
- * @return false if variable free function prevents from its deletion.
- */
- static bool var_delete_by_ptr(var_t * p_var)
- {
- if (p_var->header.p_type->free_fn != NULL)
- {
- if (!p_var->header.p_type->free_fn(p_var))
- {
- return false;
- }
- }
- // Mark matching object as unused block
- p_var->header.p_type = NULL;
- return true;
- }
- bool var_delete(const char * p_name, bool verbose)
- {
- var_t * p_var = var_get(p_name, verbose);
- if (p_var != NULL)
- {
- if (!var_delete_by_ptr(p_var))
- {
- if (verbose)
- {
- nrf_cli_fprintf(cli_get(), NRF_CLI_WARNING, "Cannot delete '%s'\n", p_name);
- }
- return false;
- }
- return true;
- }
- return false;
- }
- bool var_rename(const char * p_name, const char * p_new_name, bool verbose)
- {
- var_t * p_dest_var;
- var_t * p_var = var_get(p_name, verbose);
- if (p_var == NULL)
- {
- return false;
- }
- if (!var_name_validate(p_new_name, verbose))
- {
- return false;
- }
- p_dest_var = var_get(p_new_name, false);
- if (p_dest_var != NULL)
- {
- if (verbose)
- {
- nrf_cli_fprintf(cli_get(),
- NRF_CLI_WARNING,
- "Variable '%s' already exists, overriding.\n",
- p_new_name);
- }
- if (!var_delete(p_dest_var->name, verbose))
- {
- return false;
- }
- }
- strcpy(p_var->name, p_new_name);
- return true;
- }
- void var_show(var_t * p_var)
- {
- nrf_cli_fprintf(cli_get(), NRF_CLI_NORMAL, "Name: %s\n", p_var->name);
- nrf_cli_fprintf(cli_get(), NRF_CLI_NORMAL, "Description: %s", p_var->header.p_type->p_description);
- if (p_var->header.p_type->print_fn)
- {
- nrf_cli_fprintf(cli_get(), NRF_CLI_NORMAL, ". ");
- p_var->header.p_type->print_fn(p_var, true);
- }
- nrf_cli_fprintf(cli_get(), NRF_CLI_NORMAL, "\n");
- nrf_cli_fprintf(cli_get(), NRF_CLI_NORMAL, "Allocated bytes: %d\n", p_var->header.total_size);
- nrf_cli_fprintf(cli_get(), NRF_CLI_NORMAL, "Used bytes: %d\n", p_var->data_size);
- if (p_var->header.p_type->print_fn != NULL)
- {
- p_var->header.p_type->print_fn(p_var, false);
- }
- }
- void var_dynamic_cmd(nrf_cli_cmd_entry_t const * p_subcmd, size_t idx, nrf_cli_static_entry_t * p_static)
- {
- var_t * p_var;
- // Iterate over all objects
- for (p_var = var_begin(); p_var != NULL; p_var = var_next(p_var))
- {
- if (idx == 0)
- {
- p_static->p_syntax = p_var->name;
- p_static->handler = NULL;
- p_static->p_subcmd = p_subcmd;
- p_static->p_help = NULL;
- return;
- }
- idx--;
- }
- p_static->p_syntax = NULL;
- }
- /* =============================================================================================
- * Temporary variable handling functions
- * =============================================================================================
- */
- /** @brief Generates new name for the temporary variable.
- * @return Pointer to the static buffer that contains the new name. It is valid until the next
- * var_temp_name_generate call.
- */
- static char * var_temp_name_generate()
- {
- int i;
- static char name[] = TEMP_PREFIX "AAAAAAAAA";
- for (i = TEMP_PREFIX_LEN + 8; i > 0; i--)
- {
- if (name[i] == 'Z')
- {
- name[i] = 'A';
- }
- else
- {
- name[i]++;
- break;
- }
- }
- return name;
- }
- var_t * var_temp_create(var_type_t const * p_type, size_t size, bool verbose)
- {
- return var_create(var_temp_name_generate(), p_type, size, verbose);
- }
- bool var_is_temp(var_t const * p_var)
- {
- return strncmp(p_var->name, TEMP_PREFIX, TEMP_PREFIX_LEN) == 0;
- }
- void var_all_temp_delete(void)
- {
- var_t * p_var;
- for (p_var = var_begin(); p_var != NULL; p_var = var_next(p_var))
- {
- if (strncmp(p_var->name, TEMP_PREFIX, TEMP_PREFIX_LEN) == 0)
- {
- UNUSED_RETURN_VALUE(var_delete(p_var->name, false));
- }
- }
- }
- /* =============================================================================================
- * Memory allocation inside variables memory
- * =============================================================================================
- */
- void * var_malloc(size_t size)
- {
- var_header_t * p_block = block_allocate(sizeof(var_header_t) + size, true);
- if (p_block != NULL)
- {
- p_block->p_type = &var_alloc;
- }
- return (void*)(&p_block[1]);
- }
- void * var_calloc(size_t count, size_t size)
- {
- return var_malloc(count * size);
- }
- void var_free(void* p_data)
- {
- var_header_t * p_block = &((var_header_t *)p_data)[-1];
- if (p_data != NULL)
- {
- p_block->p_type = NULL;
- }
- }
- void * var_realloc(void * p_old, size_t new_size)
- {
- void * p_dest = var_malloc(new_size);
- var_free(p_old);
- if ((p_old != NULL) && (p_dest != NULL))
- {
- var_header_t * p_src_block = &((var_header_t *)p_old)[-1];
- size_t copy_bytes = p_src_block->total_size - sizeof(var_header_t);
- if (copy_bytes < new_size)
- {
- copy_bytes = new_size;
- }
- memcpy(p_dest, p_old, copy_bytes);
- }
- return p_dest;
- }
- void * var_temp_alloc(size_t size)
- {
- var_t * p_temp_var;
- p_temp_var = var_temp_create(&g_raw_type, size, true);
- if (p_temp_var == NULL)
- {
- return NULL;
- }
- return VAR_DATA(p_temp_var);
- }
- /* =============================================================================================
- * Variable handling commands
- * =============================================================================================
- */
- VAR_REGISTER_SUB_COMMANDS(m_subcmd_var, NULL);
- /** @brief Command to list all variables. See command help for details.
- */
- static bool cmd_ls(nrf_cli_t const * p_cli, size_t argc, char ** argv)
- {
- var_t * p_var;
- UNUSED_PARAMETER(argv);
- CLI_ARGS_CHECK(argc == 1);
- nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "%-16s %6s %s\n", "Name", "Size", "Description");
- nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "%-16s %6s %s\n", "----", "----", "-----------");
- // Iterate over all variables
- for (p_var = var_begin(); p_var != NULL; p_var = var_next(p_var))
- {
- nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "%-16s %6d %s. ", p_var->name,
- p_var->data_size, p_var->header.p_type->p_description);
- if (p_var->header.p_type->print_fn)
- {
- p_var->header.p_type->print_fn(p_var, true);
- }
- nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "\n");
- }
- return true;
- }
- CLI_CMD_REGISTER(ls, NULL, cmd_ls,
- "List all defined variables.\n"
- "Usage:\n"
- " ls\n"
- );
- /** @brief Command to remove a variable. See command help for details.
- */
- static bool cmd_rm(nrf_cli_t const * p_cli, size_t argc, char ** argv)
- {
- UNUSED_PARAMETER(p_cli);
- CLI_ARGS_CHECK(argc == 2);
- if (strcmp(argv[1], "*") == 0)
- {
- var_t * p_next_var;
- var_t * p_var = var_begin();
- bool ok = true;
- while (p_var != NULL)
- {
- p_next_var = var_next(p_var);
- ok = var_delete(p_var->name, true) && ok;
- p_var = p_next_var;
- }
- return ok;
- }
- else
- {
- return var_delete(argv[1], true);
- }
- }
- CLI_CMD_REGISTER(rm, &m_subcmd_var, cmd_rm,
- "Remove a variable.\n"
- "Usage:\n"
- " rm variable_name\n"
- "Arguments:\n"
- " IN variable_name Name of the variable to remove or \"*\" to delete all.\n"
- );
- /** @brief Command to rename a variable. See command help for details.
- */
- static bool cmd_mv(nrf_cli_t const * p_cli, size_t argc, char ** argv)
- {
- var_t * p_var;
- UNUSED_PARAMETER(p_cli);
- CLI_ARGS_CHECK(argc == 3);
- p_var = cli_input_arg_get(1, NULL);
- CLI_CHECK(p_var != NULL);
- return var_rename(p_var->name, argv[2], true);
- }
- CLI_CMD_REGISTER(mv, &m_subcmd_var, cmd_mv,
- "Rename a variable.\n"
- "Usage:\n"
- " mv old_name new_name\n"
- "Arguments:\n"
- " IN old_name Name of the variable to rename.\n"
- " IN new_name New name of the variable.\n"
- );
- /** @brief Command to show a variable. See command help for details.
- */
- static bool cmd_show(nrf_cli_t const * p_cli, size_t argc, char ** argv)
- {
- var_t * p_var;
- UNUSED_PARAMETER(p_cli);
- UNUSED_PARAMETER(argv);
- CLI_ARGS_CHECK(argc == 2);
- p_var = cli_input_arg_get(1, NULL);
- CLI_CHECK(p_var != NULL);
- var_show(p_var);
- return true;
- }
- CLI_CMD_REGISTER(show, &m_subcmd_var, cmd_show,
- "Show a content of a variable.\n"
- "Usage:\n"
- " show variable_name\n"
- "Arguments:\n"
- " IN variable_name Name of the variable to show.\n"
- );
- /** @brief Command to show full content of variable memory space. See command help for details.
- */
- static bool cmd_varmap(nrf_cli_t const * p_cli, size_t argc, char ** argv)
- {
- var_header_t * p_block;
- UNUSED_PARAMETER(p_cli);
- CLI_ARGS_CHECK(argc == 1);
- UNUSED_RETURN_VALUE(block_allocate(APP_VAR_MEMORY_SIZE + 1, false)); // Force free block consolidation
- nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "%6s %6s %-16s %s\n", "Size", "Used", "Name",
- "Description");
- nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "%6s %6s %-16s %s\n", "----", "----", "----",
- "-----------");
- // Iterate over all blocks
- for (p_block = block_begin(); p_block != NULL; p_block = block_next(p_block))
- {
- if (p_block->p_type != NULL)
- {
- char const * name;
- size_t data_size;
- if (p_block->p_type == &var_alloc)
- {
- name = "[dynamic]";
- data_size = p_block->total_size - sizeof(var_header_t);
- }
- else
- {
- name = ((var_t *)p_block)->name;
- data_size = ((var_t *)p_block)->data_size;
- }
- nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "%6d %6d %-16s %s. ", p_block->total_size,
- data_size, name, p_block->p_type->p_description);
- if (p_block->p_type->print_fn)
- {
- p_block->p_type->print_fn((var_t *)p_block, true);
- }
- nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "\n");
- }
- else
- {
- nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "%6d %6s %-16s %s\n", p_block->total_size,
- "", "[FREE]", "Unused memory block");
- }
- }
- return true;
- }
- CLI_CMD_REGISTER(varmap, NULL, cmd_varmap,
- "Print full content of the variable memory.\n"
- "Usage:\n"
- " varmap\n"
- );
|