Sysparam by @foogod (#180)
* Sysparam implementation sysparam improvements Mostly done, a few minor cleanups left. Add sysparam_editor example Sysparam code cleanup Add documentation to sysparam.h Fix up sysparam.h docs Added a couple more debug statements Fix potential memory leak if realloc() fails Major sysparam overhaul Add sysparam_get_info function Add sysparam initialization to app_main.c * Fixed warnings, added license
This commit is contained in:
parent
b07c34b863
commit
40dc3bf945
6 changed files with 1740 additions and 0 deletions
|
@ -28,6 +28,7 @@
|
|||
#include "espressif/phy_info.h"
|
||||
#include "sdk_internal.h"
|
||||
#include "esplibs/libmain.h"
|
||||
#include "sysparam.h"
|
||||
|
||||
/* This is not declared in any header file (but arguably should be) */
|
||||
|
||||
|
@ -141,6 +142,8 @@ void IRAM sdk_user_start(void) {
|
|||
uint32_t cksum_len;
|
||||
uint32_t cksum_value;
|
||||
uint32_t ic_flash_addr;
|
||||
uint32_t sysparam_addr;
|
||||
sysparam_status_t status;
|
||||
|
||||
SPI(0).USER0 |= SPI_USER0_CS_SETUP;
|
||||
sdk_SPIRead(0, buf32, 4);
|
||||
|
@ -206,6 +209,20 @@ void IRAM sdk_user_start(void) {
|
|||
}
|
||||
memcpy(&sdk_g_ic.s, buf32, sizeof(struct sdk_g_ic_saved_st));
|
||||
|
||||
// By default, put the sysparam region just below the config sectors at the
|
||||
// top of the flash space
|
||||
sysparam_addr = flash_size - (4 + DEFAULT_SYSPARAM_SECTORS) * sdk_flashchip.sector_size;
|
||||
status = sysparam_init(sysparam_addr, flash_size);
|
||||
if (status == SYSPARAM_NOTFOUND) {
|
||||
status = sysparam_create_area(sysparam_addr, DEFAULT_SYSPARAM_SECTORS, false);
|
||||
if (status == SYSPARAM_OK) {
|
||||
status = sysparam_init(sysparam_addr, 0);
|
||||
}
|
||||
}
|
||||
if (status != SYSPARAM_OK) {
|
||||
printf("WARNING: Could not initialize sysparams (%d)!\n", status);
|
||||
}
|
||||
|
||||
user_start_phase2();
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef volatile uint32_t *esp_reg_t;
|
||||
|
||||
|
|
419
core/include/sysparam.h
Normal file
419
core/include/sysparam.h
Normal file
|
@ -0,0 +1,419 @@
|
|||
/*
|
||||
* Part of esp-open-rtos
|
||||
* Copyright (C) 2016 Alex Stewart
|
||||
* BSD Licensed as described in the file LICENSE
|
||||
*/
|
||||
|
||||
#ifndef _SYSPARAM_H_
|
||||
#define _SYSPARAM_H_
|
||||
|
||||
#include <esp/types.h>
|
||||
|
||||
#ifndef DEFAULT_SYSPARAM_SECTORS
|
||||
#define DEFAULT_SYSPARAM_SECTORS 4
|
||||
#endif
|
||||
|
||||
/** @file sysparam.h
|
||||
*
|
||||
* Read/write "system parameters" to persistent flash.
|
||||
*
|
||||
* System parameters are stored as key/value pairs. Keys are string values
|
||||
* between 1 and 65535 characters long. Values can be any data up to 65535
|
||||
* bytes in length (but are most commonly also text strings). Up to 126 key/
|
||||
* value pairs can be stored at a time.
|
||||
*
|
||||
* Keys and values are stored in flash using a progressive list structure
|
||||
* which allows space-efficient storage and minimizes flash erase cycles,
|
||||
* improving write speed and increasing the lifespan of the flash memory.
|
||||
*/
|
||||
|
||||
/** Status codes returned by all sysparam functions
|
||||
*
|
||||
* Error codes (`SYSPARAM_ERR_*`) all have values less than zero, and can be
|
||||
* returned by any function. Values greater than zero are non-error status
|
||||
* codes which may be returned by some functions to indicate various results.
|
||||
*/
|
||||
typedef enum {
|
||||
SYSPARAM_OK = 0, ///< Success
|
||||
SYSPARAM_NOTFOUND = 1, ///< Entry not found matching criteria
|
||||
SYSPARAM_PARSEFAILED = 2, ///< Unable to parse retrieved value
|
||||
SYSPARAM_ERR_NOINIT = -1, ///< sysparam_init() must be called first
|
||||
SYSPARAM_ERR_BADVALUE = -2, ///< One or more arguments were invalid
|
||||
SYSPARAM_ERR_FULL = -3, ///< No space left in sysparam area (or too many keys in use)
|
||||
SYSPARAM_ERR_IO = -4, ///< I/O error reading/writing flash
|
||||
SYSPARAM_ERR_CORRUPT = -5, ///< Sysparam region has bad/corrupted data
|
||||
SYSPARAM_ERR_NOMEM = -6, ///< Unable to allocate memory
|
||||
} sysparam_status_t;
|
||||
|
||||
/** Structure used by sysparam_iter_next() to keep track of its current state
|
||||
* and return its results. This should be initialized by calling
|
||||
* sysparam_iter_start() and cleaned up afterward by calling
|
||||
* sysparam_iter_end().
|
||||
*/
|
||||
typedef struct {
|
||||
char *key;
|
||||
uint8_t *value;
|
||||
size_t key_len;
|
||||
size_t value_len;
|
||||
bool binary;
|
||||
size_t bufsize;
|
||||
struct sysparam_context *ctx;
|
||||
} sysparam_iter_t;
|
||||
|
||||
/** Initialize sysparam and set up the current area of flash to use.
|
||||
*
|
||||
* This must be called (and return successfully) before any other sysparam
|
||||
* routines (except sysparam_create_area()) are called.
|
||||
*
|
||||
* This should normally be taken care of automatically on boot by the OS
|
||||
* startup routines. It may be necessary to call it specially, however, if
|
||||
* the normal initialization failed, or after calling sysparam_create_area()
|
||||
* to reformat the current area.
|
||||
*
|
||||
* This routine will start at `base_addr` and scan all sectors up to
|
||||
* `top_addr` looking for a valid sysparam area. If `top_addr` is zero (or
|
||||
* equal to `base_addr`, then only the sector at `base_addr` will be checked.
|
||||
*
|
||||
* @param[in] base_addr The flash address to start looking for the start of
|
||||
* the (already present) sysparam area
|
||||
* @param[in] top_addr The flash address to stop looking for the sysparam
|
||||
* area
|
||||
*
|
||||
* @retval ::SYSPARAM_OK Initialization successful.
|
||||
* @retval ::SYSPARAM_NOTFOUND The specified address does not appear to
|
||||
* contain a sysparam area. It may be
|
||||
* necessary to call sysparam_create_area() to
|
||||
* create one first.
|
||||
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
|
||||
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
|
||||
*/
|
||||
sysparam_status_t sysparam_init(uint32_t base_addr, uint32_t top_addr);
|
||||
|
||||
/** Create a new sysparam area in flash at the specified address.
|
||||
*
|
||||
* By default, this routine will scan the specified area to make sure it
|
||||
* appears to be empty (i.e. all 0xFF bytes) before setting it up as a new
|
||||
* sysparam area. If there appears to be other data already present, it will
|
||||
* not overwrite it. Setting `force` to `true` will cause it to clobber any
|
||||
* existing data instead.
|
||||
*
|
||||
* @param[in] base_addr The flash address at which it should start
|
||||
* (must be a multiple of the sector size)
|
||||
* @param[in] num_sectors The number of flash sectors to use for the sysparam
|
||||
* area. This should be an even number >= 2. Note
|
||||
* that the actual amount of useable parameter space
|
||||
* will be roughly half this amount.
|
||||
* @param[in] force Proceed even if the space does not appear to be empty
|
||||
*
|
||||
* @retval ::SYSPARAM_OK Area (re)created successfully.
|
||||
* @retval ::SYSPARAM_NOTFOUND `force` was not specified, and the area at
|
||||
* `base_addr` appears to have other data. No
|
||||
* action taken.
|
||||
* @retval ::SYSPARAM_ERR_BADVALUE The `num_sectors` value was not even (or
|
||||
* was zero)
|
||||
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
|
||||
*
|
||||
* Note: This routine can create a sysparam area in another location than the
|
||||
* one currently being used, but does not change which area is currently used
|
||||
* (you will need to call sysparam_init() again if you want to do that). If
|
||||
* you reformat the area currently being used, you will also need to call
|
||||
* sysparam_init() again afterward before you will be able to continue using
|
||||
* it.
|
||||
*/
|
||||
sysparam_status_t sysparam_create_area(uint32_t base_addr, uint16_t num_sectors, bool force);
|
||||
|
||||
/** Get the start address and size of the currently active sysparam area
|
||||
*
|
||||
* Fills in `base_addr` and `num_sectors` with the location and size of the
|
||||
* currently active sysparam area. The returned values correspond to the
|
||||
* arguments passed to the sysparam_create_area() call when the area was
|
||||
* originally created.
|
||||
*
|
||||
* @param[out] base_addr The flash address at which the sysparam area starts
|
||||
* @param[out] num_sectors The number of flash sectors used by the sysparam
|
||||
* area
|
||||
*
|
||||
* @retval ::SYSPARAM_OK Completed successfully
|
||||
* @retval ::SYSPARAM_ERR_NOINIT No current sysparam area is active
|
||||
*/
|
||||
sysparam_status_t sysparam_get_info(uint32_t *base_addr, uint32_t *num_sectors);
|
||||
|
||||
/** Get the value associated with a key
|
||||
*
|
||||
* This is the core "get value" function. It will retrieve the value for the
|
||||
* specified key in a freshly malloc()'d buffer and return it. Raw values can
|
||||
* contain any data (including zero bytes), so the `actual_length` parameter
|
||||
* should be used to determine the length of the data in the buffer.
|
||||
*
|
||||
* It is up to the caller to free() the returned buffer when done using it.
|
||||
*
|
||||
* Note: If the status result is anything other than ::SYSPARAM_OK, the value
|
||||
* in `destptr` is not changed. This means it is possible to set a default
|
||||
* value before calling this function which will be left as-is if a sysparam
|
||||
* value could not be successfully read.
|
||||
*
|
||||
* @param[in] key Key name (zero-terminated string)
|
||||
* @param[out] destptr Pointer to a location to hold the address of the
|
||||
* returned data buffer
|
||||
* @param[out] actual_length Pointer to a location to hold the length of the
|
||||
* returned data buffer (may be NULL)
|
||||
* @param[out] is_binary Pointer to a bool to hold whether the returned
|
||||
* value is "binary" or not (may be NULL)
|
||||
*
|
||||
* @retval ::SYSPARAM_OK Value successfully retrieved.
|
||||
* @retval ::SYSPARAM_NOTFOUND Key/value not found. No buffer returned.
|
||||
* @retval ::SYSPARAM_ERR_NOINIT sysparam_init() must be called first
|
||||
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
|
||||
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
|
||||
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
|
||||
*/
|
||||
sysparam_status_t sysparam_get_data(const char *key, uint8_t **destptr, size_t *actual_length, bool *is_binary);
|
||||
|
||||
/** Get the value associate with a key (static buffers only)
|
||||
*
|
||||
* This performs the same function as sysparam_get_data() but without
|
||||
* performing any memory allocations. It can thus be used before the heap has
|
||||
* been configured or in other cases where using the heap would be a problem
|
||||
* (i.e. in an OOM handler, etc). It requires that the caller pass in a
|
||||
* suitably sized buffer for the value to be read (if the supplied buffer is
|
||||
* not large enough, the returned value will be truncated and the full
|
||||
* required length will be returned in `actual_length`).
|
||||
*
|
||||
* NOTE: In addition to being large enough for the value, the supplied buffer
|
||||
* must also be at least as large as the length of the key being requested.
|
||||
* If it is not, an error will be returned.
|
||||
*
|
||||
* @param[in] key Key name (zero-terminated string)
|
||||
* @param[in] buffer Pointer to a buffer to hold the returned value
|
||||
* @param[in] buffer_size Length of the supplied buffer in bytes
|
||||
* @param[out] actual_length pointer to a location to hold the actual length
|
||||
* of the data which was associated with the key
|
||||
* (may be NULL).
|
||||
* @param[out] is_binary Pointer to a bool to hold whether the returned
|
||||
* value is "binary" or not (may be NULL)
|
||||
*
|
||||
* @retval ::SYSPARAM_OK Value successfully retrieved
|
||||
* @retval ::SYSPARAM_NOTFOUND Key/value not found
|
||||
* @retval ::SYSPARAM_ERR_NOINIT sysparam_init() must be called first
|
||||
* @retval ::SYSPARAM_ERR_NOMEM The supplied buffer is too small
|
||||
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
|
||||
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
|
||||
*/
|
||||
sysparam_status_t sysparam_get_data_static(const char *key, uint8_t *buffer, size_t buffer_size, size_t *actual_length, bool *is_binary);
|
||||
|
||||
/** Get the string value associated with a key
|
||||
*
|
||||
* This routine can be used if you know that the value in a key will (or at
|
||||
* least should) be a string. It will return a zero-terminated char buffer
|
||||
* containing the value retrieved.
|
||||
*
|
||||
* It is up to the caller to free() the returned buffer when done using it.
|
||||
*
|
||||
* Note: If the status result is anything other than ::SYSPARAM_OK, the value
|
||||
* in `destptr` is not changed. This means it is possible to set a default
|
||||
* value before calling this function which will be left as-is if a sysparam
|
||||
* value could not be successfully read.
|
||||
*
|
||||
* @param[in] key Key name (zero-terminated string)
|
||||
* @param[out] destptr Pointer to a location to hold the address of the
|
||||
* returned data buffer
|
||||
*
|
||||
* @retval ::SYSPARAM_OK Value successfully retrieved.
|
||||
* @retval ::SYSPARAM_NOTFOUND Key/value not found.
|
||||
* @retval ::SYSPARAM_PARSEFAILED The retrieved value was a binary value
|
||||
* @retval ::SYSPARAM_ERR_NOINIT sysparam_init() must be called first
|
||||
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
|
||||
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
|
||||
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
|
||||
*/
|
||||
sysparam_status_t sysparam_get_string(const char *key, char **destptr);
|
||||
|
||||
/** Get the int32_t value associated with a key
|
||||
*
|
||||
* This routine can be used if you know that the value in a key will (or at
|
||||
* least should) be an integer value. It will parse the stored data as a
|
||||
* number (in standard decimal or "0x" hex notation) and return the result.
|
||||
*
|
||||
* Note: If the status result is anything other than ::SYSPARAM_OK, the value
|
||||
* in `result` is not changed. This means it is possible to set a default
|
||||
* value before calling this function which will be left as-is if a sysparam
|
||||
* value could not be successfully read.
|
||||
*
|
||||
* @param[in] key Key name (zero-terminated string)
|
||||
* @param[out] result Pointer to a location to hold returned integer value
|
||||
*
|
||||
* @retval ::SYSPARAM_OK Value successfully retrieved.
|
||||
* @retval ::SYSPARAM_NOTFOUND Key/value not found.
|
||||
* @retval ::SYSPARAM_PARSEFAILED The retrieved value could not be parsed as
|
||||
* an integer.
|
||||
* @retval ::SYSPARAM_ERR_NOINIT sysparam_init() must be called first
|
||||
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
|
||||
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
|
||||
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
|
||||
*/
|
||||
sysparam_status_t sysparam_get_int(const char *key, int32_t *result);
|
||||
|
||||
/** Get the boolean value associated with a key
|
||||
*
|
||||
* This routine can be used if you know that the value in a key will (or at
|
||||
* least should) be a boolean setting. It will read the specified value as a
|
||||
* text string and attempt to parse it as a boolean value.
|
||||
*
|
||||
* It will recognize the following (case-insensitive) strings:
|
||||
* * True: "yes", "y", "true", "t", "1"
|
||||
* * False: "no", "n", "false", "f", "0"
|
||||
*
|
||||
* Note: If the status result is anything other than ::SYSPARAM_OK, the value
|
||||
* in `result` is not changed. This means it is possible to set a default
|
||||
* value before calling this function which will be left as-is if a sysparam
|
||||
* value could not be successfully read.
|
||||
*
|
||||
* @param[in] key Key name (zero-terminated string)
|
||||
* @param[out] result Pointer to a location to hold returned boolean value
|
||||
*
|
||||
* @retval ::SYSPARAM_OK Value successfully retrieved.
|
||||
* @retval ::SYSPARAM_NOTFOUND Key/value not found.
|
||||
* @retval ::SYSPARAM_PARSEFAILED The retrieved value could not be parsed as a
|
||||
* boolean setting.
|
||||
* @retval ::SYSPARAM_ERR_NOINIT sysparam_init() must be called first
|
||||
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
|
||||
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
|
||||
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
|
||||
*/
|
||||
sysparam_status_t sysparam_get_bool(const char *key, bool *result);
|
||||
|
||||
/** Set the value associated with a key
|
||||
*
|
||||
* The supplied value can be any data, up to 255 bytes in length. If `value`
|
||||
* is NULL or `value_len` is 0, this is treated as a request to delete any
|
||||
* current entry matching `key`.
|
||||
*
|
||||
* If `binary` is true, the data will be considered binary (unprintable) data,
|
||||
* and this will be annotated in the saved entry. This does not affect the
|
||||
* saving or loading process in any way, but may be used by some applications
|
||||
* to (for example) print binary data differently than text entries when
|
||||
* printing parameter values.
|
||||
*
|
||||
* @param[in] key Key name (zero-terminated string)
|
||||
* @param[in] value Pointer to a buffer containing the value data
|
||||
* @param[in] value_len Length of the data in the buffer
|
||||
* @param[in] binary Whether the data should be considered "binary"
|
||||
* (unprintable) data
|
||||
*
|
||||
* @retval ::SYSPARAM_OK Value successfully set.
|
||||
* @retval ::SYSPARAM_ERR_NOINIT sysparam_init() must be called first
|
||||
* @retval ::SYSPARAM_ERR_BADVALUE Either an empty key was provided or
|
||||
* value_len is too large
|
||||
* @retval ::SYSPARAM_ERR_FULL No space left in sysparam area
|
||||
* (or too many keys in use)
|
||||
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
|
||||
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
|
||||
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
|
||||
*/
|
||||
sysparam_status_t sysparam_set_data(const char *key, const uint8_t *value, size_t value_len, bool binary);
|
||||
|
||||
/** Set a key's value from a string
|
||||
*
|
||||
* Performs the same function as sysparam_set_data(), but accepts a
|
||||
* zero-terminated string value instead.
|
||||
*
|
||||
* @param[in] key Key name (zero-terminated string)
|
||||
* @param[in] value Value to set (zero-terminated string)
|
||||
*
|
||||
* @retval ::SYSPARAM_OK Value successfully set.
|
||||
* @retval ::SYSPARAM_ERR_BADVALUE Either an empty key was provided or the
|
||||
* length of `value` is too large
|
||||
* @retval ::SYSPARAM_ERR_FULL No space left in sysparam area
|
||||
* (or too many keys in use)
|
||||
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
|
||||
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
|
||||
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
|
||||
*/
|
||||
sysparam_status_t sysparam_set_string(const char *key, const char *value);
|
||||
|
||||
/** Set a key's value as a number
|
||||
*
|
||||
* Converts an int32_t value to a decimal number and writes it to the
|
||||
* specified key. This does the inverse of the sysparam_get_int()
|
||||
* function.
|
||||
*
|
||||
* @param[in] key Key name (zero-terminated string)
|
||||
* @param[in] value Value to set
|
||||
*
|
||||
* @retval ::SYSPARAM_OK Value successfully set.
|
||||
* @retval ::SYSPARAM_ERR_BADVALUE An empty key was provided.
|
||||
* @retval ::SYSPARAM_ERR_FULL No space left in sysparam area
|
||||
* (or too many keys in use)
|
||||
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
|
||||
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
|
||||
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
|
||||
*/
|
||||
sysparam_status_t sysparam_set_int(const char *key, int32_t value);
|
||||
|
||||
/** Set a key's value as a boolean (yes/no) string
|
||||
*
|
||||
* Converts a bool value to a corresponding text string and writes it to the
|
||||
* specified key. This does the inverse of the sysparam_get_bool()
|
||||
* function.
|
||||
*
|
||||
* Note that if the key already contains a value which parses to the same
|
||||
* boolean (true/false) value, it is left unchanged.
|
||||
*
|
||||
* @param[in] key Key name (zero-terminated string)
|
||||
* @param[in] value Value to set
|
||||
*
|
||||
* @retval ::SYSPARAM_OK Value successfully set.
|
||||
* @retval ::SYSPARAM_ERR_BADVALUE An empty key was provided.
|
||||
* @retval ::SYSPARAM_ERR_FULL No space left in sysparam area
|
||||
* (or too many keys in use)
|
||||
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
|
||||
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
|
||||
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
|
||||
*/
|
||||
sysparam_status_t sysparam_set_bool(const char *key, bool value);
|
||||
|
||||
/** Begin iterating through all key/value pairs
|
||||
*
|
||||
* This function initializes a sysparam_iter_t structure to prepare it for
|
||||
* iterating through the list of key/value pairs using sysparam_iter_next().
|
||||
* This does not fetch any items (the first successive call to
|
||||
* sysparam_iter_next() will return the first key/value in the list).
|
||||
*
|
||||
* NOTE: When done, you must call sysparam_iter_end() to free the resources
|
||||
* associated with `iter`, or you will leak memory.
|
||||
*
|
||||
* @param[in] iter A pointer to a sysparam_iter_t structure to initialize
|
||||
*
|
||||
* @retval ::SYSPARAM_OK Initialization successful
|
||||
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
|
||||
*/
|
||||
sysparam_status_t sysparam_iter_start(sysparam_iter_t *iter);
|
||||
|
||||
/** Fetch the next key/value pair
|
||||
*
|
||||
* This will retrieve the next key and value from the sysparam area, placing
|
||||
* them in `iter->key`, and `iter->value` (and updating `iter->key_len` and
|
||||
* `iter->value_len`).
|
||||
*
|
||||
* NOTE: `iter->key` and `iter->value` are static buffers local to the `iter`
|
||||
* structure, and will be overwritten with the next call to
|
||||
* sysparam_iter_next() using the same `iter`. They should *not* be free()d
|
||||
* after use.
|
||||
*
|
||||
* @param[in] iter The iterator structure to update
|
||||
*
|
||||
* @retval ::SYSPARAM_OK Next key/value retrieved
|
||||
* @retval ::SYSPARAM_ERR_NOMEM Unable to allocate memory
|
||||
* @retval ::SYSPARAM_ERR_CORRUPT Sysparam region has bad/corrupted data
|
||||
* @retval ::SYSPARAM_ERR_IO I/O error reading/writing flash
|
||||
*/
|
||||
sysparam_status_t sysparam_iter_next(sysparam_iter_t *iter);
|
||||
|
||||
/** Finish iterating through keys/values
|
||||
*
|
||||
* Cleans up and releases resources allocated by sysparam_iter_start() /
|
||||
* sysparam_iter_next().
|
||||
*/
|
||||
void sysparam_iter_end(sysparam_iter_t *iter);
|
||||
|
||||
#endif /* _SYSPARAM_H_ */
|
1056
core/sysparam.c
Normal file
1056
core/sysparam.c
Normal file
File diff suppressed because it is too large
Load diff
14
examples/sysparam_editor/Makefile
Normal file
14
examples/sysparam_editor/Makefile
Normal file
|
@ -0,0 +1,14 @@
|
|||
PROGRAM=sysparam_editor
|
||||
|
||||
# Setting this to 1..3 will add extra debugging output to stdout
|
||||
EXTRA_CFLAGS=-DSYSPARAM_DEBUG=0
|
||||
|
||||
include ../../common.mk
|
||||
|
||||
# `make dump-flash` can be used to view the current contents of the sysparam
|
||||
# regions in flash.
|
||||
dump-flash:
|
||||
esptool.py read_flash 0x1f8000 8192 r1.bin
|
||||
hexdump -C r1.bin
|
||||
esptool.py read_flash 0x1fa000 8192 r2.bin
|
||||
hexdump -C r2.bin
|
233
examples/sysparam_editor/sysparam_editor.c
Normal file
233
examples/sysparam_editor/sysparam_editor.c
Normal file
|
@ -0,0 +1,233 @@
|
|||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysparam.h>
|
||||
|
||||
#include <espressif/spi_flash.h>
|
||||
|
||||
#define CMD_BUF_SIZE 5000
|
||||
|
||||
const int status_base = -6;
|
||||
const char *status_messages[] = {
|
||||
"SYSPARAM_ERR_NOMEM",
|
||||
"SYSPARAM_ERR_CORRUPT",
|
||||
"SYSPARAM_ERR_IO",
|
||||
"SYSPARAM_ERR_FULL",
|
||||
"SYSPARAM_ERR_BADVALUE",
|
||||
"SYSPARAM_ERR_NOINIT",
|
||||
"SYSPARAM_OK",
|
||||
"SYSPARAM_NOTFOUND",
|
||||
"SYSPARAM_PARSEFAILED",
|
||||
};
|
||||
|
||||
void usage(void) {
|
||||
printf(
|
||||
"Available commands:\n"
|
||||
" <key>? -- Query the value of <key>\n"
|
||||
" <key>=<value> -- Set <key> to text <value>\n"
|
||||
" <key>:<hexdata> -- Set <key> to binary value represented as hex\n"
|
||||
" dump -- Show all currently set keys/values\n"
|
||||
" reformat -- Reinitialize (clear) the sysparam area\n"
|
||||
" help -- Show this help screen\n"
|
||||
);
|
||||
}
|
||||
|
||||
size_t tty_readline(char *buffer, size_t buf_size, bool echo) {
|
||||
size_t i = 0;
|
||||
int c;
|
||||
|
||||
while (true) {
|
||||
c = getchar();
|
||||
if (c == '\r') {
|
||||
if (echo) putchar('\n');
|
||||
break;
|
||||
} else if (c == '\b' || c == 0x7f) {
|
||||
if (i) {
|
||||
if (echo) printf("\b \b");
|
||||
i--;
|
||||
}
|
||||
} else if (c < 0x20) {
|
||||
/* Ignore other control characters */
|
||||
} else if (i >= buf_size - 1) {
|
||||
if (echo) putchar('\a');
|
||||
} else {
|
||||
buffer[i++] = c;
|
||||
if (echo) putchar(c);
|
||||
}
|
||||
}
|
||||
|
||||
buffer[i] = 0;
|
||||
return i;
|
||||
}
|
||||
|
||||
void print_text_value(char *key, char *value) {
|
||||
printf(" '%s' = '%s'\n", key, value);
|
||||
}
|
||||
|
||||
void print_binary_value(char *key, uint8_t *value, size_t len) {
|
||||
size_t i;
|
||||
|
||||
printf(" %s:", key);
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!(i & 0x0f)) {
|
||||
printf("\n ");
|
||||
}
|
||||
printf(" %02x", value[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
sysparam_status_t dump_params(void) {
|
||||
sysparam_status_t status;
|
||||
sysparam_iter_t iter;
|
||||
|
||||
status = sysparam_iter_start(&iter);
|
||||
if (status < 0) return status;
|
||||
while (true) {
|
||||
status = sysparam_iter_next(&iter);
|
||||
if (status != SYSPARAM_OK) break;
|
||||
if (!iter.binary) {
|
||||
print_text_value(iter.key, (char *)iter.value);
|
||||
} else {
|
||||
print_binary_value(iter.key, iter.value, iter.value_len);
|
||||
}
|
||||
}
|
||||
sysparam_iter_end(&iter);
|
||||
|
||||
if (status == SYSPARAM_NOTFOUND) {
|
||||
// This is the normal status when we've reached the end of all entries.
|
||||
return SYSPARAM_OK;
|
||||
} else {
|
||||
// Something apparently went wrong
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *parse_hexdata(char *string, size_t *result_length) {
|
||||
size_t string_len = strlen(string);
|
||||
uint8_t *buf = malloc(string_len / 2);
|
||||
uint8_t c;
|
||||
int i, j;
|
||||
bool digit = false;
|
||||
|
||||
j = 0;
|
||||
for (i = 0; string[i]; i++) {
|
||||
c = string[i];
|
||||
if (c >= 0x30 && c <= 0x39) {
|
||||
c &= 0x0f;
|
||||
} else if (c >= 0x41 && c <= 0x46) {
|
||||
c -= 0x37;
|
||||
} else if (c >= 0x61 && c <= 0x66) {
|
||||
c -= 0x57;
|
||||
} else if (c == ' ') {
|
||||
continue;
|
||||
} else {
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
if (!digit) {
|
||||
buf[j] = c << 4;
|
||||
} else {
|
||||
buf[j++] |= c;
|
||||
}
|
||||
digit = !digit;
|
||||
}
|
||||
if (digit) {
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
*result_length = j;
|
||||
return buf;
|
||||
}
|
||||
|
||||
void sysparam_editor_task(void *pvParameters) {
|
||||
char *cmd_buffer = malloc(CMD_BUF_SIZE);
|
||||
sysparam_status_t status;
|
||||
char *value;
|
||||
uint8_t *bin_value;
|
||||
size_t len;
|
||||
uint8_t *data;
|
||||
uint32_t base_addr, num_sectors;
|
||||
|
||||
if (!cmd_buffer) {
|
||||
printf("ERROR: Cannot allocate command buffer!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("\nWelcome to the system parameter editor! Enter 'help' for more information.\n\n");
|
||||
|
||||
status = sysparam_get_info(&base_addr, &num_sectors);
|
||||
if (status == SYSPARAM_OK) {
|
||||
printf("[current sysparam region is at 0x%08x (%d sectors)]\n", base_addr, num_sectors);
|
||||
} else {
|
||||
printf("[NOTE: No current sysparam region (initialization problem during boot?)]\n");
|
||||
// Default to the same place/size as the normal system initialization
|
||||
// stuff, so if the user uses this utility to reformat it, it will put
|
||||
// it somewhere the system will find it later
|
||||
num_sectors = DEFAULT_SYSPARAM_SECTORS;
|
||||
base_addr = sdk_flashchip.chip_size - (4 + num_sectors) * sdk_flashchip.sector_size;
|
||||
}
|
||||
while (true) {
|
||||
printf("==> ");
|
||||
len = tty_readline(cmd_buffer, CMD_BUF_SIZE, true);
|
||||
status = 0;
|
||||
if (!len) continue;
|
||||
if (cmd_buffer[len - 1] == '?') {
|
||||
cmd_buffer[len - 1] = 0;
|
||||
printf("Querying '%s'...\n", cmd_buffer);
|
||||
status = sysparam_get_string(cmd_buffer, &value);
|
||||
if (status == SYSPARAM_OK) {
|
||||
print_text_value(cmd_buffer, value);
|
||||
free(value);
|
||||
} else if (status == SYSPARAM_PARSEFAILED) {
|
||||
// This means it's actually a binary value
|
||||
status = sysparam_get_data(cmd_buffer, &bin_value, &len, NULL);
|
||||
if (status == SYSPARAM_OK) {
|
||||
print_binary_value(cmd_buffer, bin_value, len);
|
||||
free(value);
|
||||
}
|
||||
}
|
||||
} else if ((value = strchr(cmd_buffer, '='))) {
|
||||
*value++ = 0;
|
||||
printf("Setting '%s' to '%s'...\n", cmd_buffer, value);
|
||||
status = sysparam_set_string(cmd_buffer, value);
|
||||
} else if ((value = strchr(cmd_buffer, ':'))) {
|
||||
*value++ = 0;
|
||||
data = parse_hexdata(value, &len);
|
||||
if (value) {
|
||||
printf("Setting '%s' to binary data...\n", cmd_buffer);
|
||||
status = sysparam_set_data(cmd_buffer, data, len, true);
|
||||
free(data);
|
||||
} else {
|
||||
printf("Error: Unable to parse hex data\n");
|
||||
}
|
||||
} else if (!strcmp(cmd_buffer, "dump")) {
|
||||
printf("Dumping all params:\n");
|
||||
status = dump_params();
|
||||
} else if (!strcmp(cmd_buffer, "reformat")) {
|
||||
printf("Re-initializing region...\n");
|
||||
status = sysparam_create_area(base_addr, num_sectors, true);
|
||||
if (status == SYSPARAM_OK) {
|
||||
// We need to re-init after wiping out the region we've been
|
||||
// using.
|
||||
status = sysparam_init(base_addr, 0);
|
||||
}
|
||||
} else if (!strcmp(cmd_buffer, "help")) {
|
||||
usage();
|
||||
} else {
|
||||
printf("Unrecognized command.\n\n");
|
||||
usage();
|
||||
}
|
||||
|
||||
if (status != SYSPARAM_OK) {
|
||||
printf("! Operation returned status: %d (%s)\n", status, status_messages[status - status_base]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void user_init(void)
|
||||
{
|
||||
xTaskCreate(sysparam_editor_task, (signed char *)"sysparam_editor_task", 512, NULL, 2, NULL);
|
||||
}
|
Loading…
Reference in a new issue