From 7721e24b0e49d3f1c0733fd1f5a9f09a994e6c9c Mon Sep 17 00:00:00 2001 From: Alex Stewart Date: Tue, 8 Mar 2016 17:00:24 -0800 Subject: [PATCH] Add documentation to sysparam.h --- core/include/sysparam.h | 329 +++++++++++++++++++++++++++++++++++++++- core/sysparam.c | 9 ++ 2 files changed, 335 insertions(+), 3 deletions(-) diff --git a/core/include/sysparam.h b/core/include/sysparam.h index 6f158ee..300d447 100644 --- a/core/include/sysparam.h +++ b/core/include/sysparam.h @@ -4,12 +4,20 @@ #include #ifndef SYSPARAM_REGION_SECTORS -// Number of (4K) sectors that make up a sysparam region. Total sysparam data -// cannot be larger than this. Note that the actual amount of used flash -// space will be *twice* this amount. +/** Number of (4K) sectors that make up a sysparam region. Total sysparam data + * cannot be larger than this. Note that the full sysparam area is two + * regions, so the actual amount of used flash space will be *twice* this + * amount. + */ #define SYSPARAM_REGION_SECTORS 1 #endif +/** Status codes returned by all sysparam functions + * + * Error codes 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 (each function should document which non-error statuses it may return). + */ typedef enum { SYSPARAM_ERR_NOMEM = -6, SYSPARAM_ERR_CORRUPT = -5, @@ -22,6 +30,19 @@ typedef enum { SYSPARAM_PARSEFAILED = 2, } sysparam_status_t; + * @retval SYSPARAM_ERR_NOINIT sysparam_init() must be called first + * @retval SYSPARAM_ERR_BADVALUE One or more arguments are invalid + * @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 + +/** 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; @@ -31,19 +52,321 @@ typedef struct { 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. + * + * @param base_addr [in] The flash address which should contain the start of + * the (already present) 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); + +/** 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 base_addr [in] The flash address at which it should start + * (must be a multiple of the sector size) + * @param force [in] 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_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, bool force); + +/** 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. + * + * Note: It is up to the caller to free() the returned buffer when done using + * it. + * + * @param key [in] Key name (zero-terminated string) + * @param destptr [out] Pointer to a location to hold the address of the + * returned data buffer + * @param actual_length [out] Pointer to a location to hold the length of the + * returned data buffer + * + * @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 + * + * Note: If the 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. + */ sysparam_status_t sysparam_get_data(const char *key, uint8_t **destptr, size_t *actual_length); + +/** 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 key [in] Key name (zero-terminated string) + * @param buffer [in] Pointer to a buffer to hold the returned value + * @param buffer_size [in] Length of the supplied buffer in bytes + * @param actual_length [out] pointer to a location to hold the actual length + * of the data which was associated with the key + * (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); + +/** Return 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. + * + * Note: It is up to the caller to free() the returned buffer when done using + * it. + * + * @param key [in] Key name (zero-terminated string) + * @param destptr [out] 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_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 + * + * Note: If the 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. + */ sysparam_status_t sysparam_get_string(const char *key, char **destptr); + +/** Return 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. + * + * @param key [in] Key name (zero-terminated string) + * @param result [out] 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 + * + * Note: If the 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. + */ sysparam_status_t sysparam_get_int(const char *key, int32_t *result); + +/** Return 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" + * + * @param key [in] Key name (zero-terminated string) + * @param result [out] 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 + * + * Note: If the 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. + */ 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`. + * + * @param key [in] Key name (zero-terminated string) + * @param value [in] Pointer to a buffer containing the value data + * @param value_len [in] Length of the data in the buffer + * + * @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); + +/** 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 key [in] Key name (zero-terminated string) + * @param value [in] 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 key [in] Key name (zero-terminated string) + * @param value [in] 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 key [in] Key name (zero-terminated string) + * @param value [in] 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 iter [in] 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 iter [in] 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_ */ diff --git a/core/sysparam.c b/core/sysparam.c index 8f9415d..b664dbd 100644 --- a/core/sysparam.c +++ b/core/sysparam.c @@ -575,6 +575,8 @@ sysparam_status_t sysparam_set_data(const char *key, const uint8_t *value, size_ if (!key_len) return SYSPARAM_ERR_BADVALUE; if (value_len > 0xff) return SYSPARAM_ERR_BADVALUE; + if (!value) value_len = 0; + if (value_len && ((intptr_t)value & 0x3)) { // The passed value isn't word-aligned. This will be a problem later // when calling `sdk_spi_flash_write`, so make a word-aligned copy. @@ -733,6 +735,13 @@ sysparam_status_t sysparam_set_int(const char *key, int32_t value) { sysparam_status_t sysparam_set_bool(const char *key, bool value) { uint8_t buf[4] = {0xff, 0xff, 0xff, 0xff}; + bool old_value; + + // Don't write anything if the current setting already evaluates to the + // same thing. + if (sysparam_get_bool(key, &old_value) == SYSPARAM_OK) { + if (old_value == value) return SYSPARAM_OK; + } buf[0] = value ? 'y' : 'n'; return sysparam_set_data(key, buf, 1);