Added support for NVS iterators

Closes https://github.com/espressif/esp-idf/issues/129
This commit is contained in:
MartinValik 2018-11-05 09:03:04 +01:00
parent bde1c30c5b
commit 5268960235
9 changed files with 545 additions and 47 deletions

View file

@ -205,7 +205,7 @@ Data
(Only for blob index.) Total number of blob-data chunks into which the blob was divided during storage.
- ChunkStart
(Only for blob index.) ChunkIndex of the first blob-data chunk of this blob. Subsequent chunks have chunkIndex incrementely allocated (step of 1).
(Only for blob index.) ChunkIndex of the first blob-data chunk of this blob. Subsequent chunks have chunkIndex incrementally allocated (step of 1)
For string and blob data chunks, these 8 bytes hold additional data about the value, described next:
@ -241,7 +241,7 @@ Item hash list
To reduce the number of reads performed from flash memory, each member of Page class maintains a list of pairs: (item index; item hash). This list makes searches much quicker. Instead of iterating over all entries, reading them from flash one at a time, ``Page::findItem`` first performs search for item hash in the hash list. This gives the item index within the page, if such an item exists. Due to a hash collision it is possible that a different item will be found. This is handled by falling back to iteration over items in flash.
Each node in hash list contains a 24-bit hash and 8-bit item index. Hash is calculated based on item namespace, key name and ChunkIndex. CRC32 is used for calculation, result is truncated to 24 bits. To reduce overhead of storing 32-bit entries in a linked list, list is implemented as a doubly-linked list of arrays. Each array holds 29 entries, for the total size of 128 bytes, together with linked list pointers and 32-bit count field. Minimal amount of extra RAM useage per page is therefore 128 bytes, maximum is 640 bytes.
Each node in hash list contains a 24-bit hash and 8-bit item index. Hash is calculated based on item namespace, key name and ChunkIndex. CRC32 is used for calculation, result is truncated to 24 bits. To reduce overhead of storing 32-bit entries in a linked list, list is implemented as a doubly-linked list of arrays. Each array holds 29 entries, for the total size of 128 bytes, together with linked list pointers and 32-bit count field. Minimal amount of extra RAM usage per page is therefore 128 bytes, maximum is 640 bytes.
.. _nvs_encryption:
@ -269,7 +269,7 @@ An application requiring NVS encryption support needs to be compiled with a key-
This partition can be generated using `nvs partition generator` utility and flashed onto the device. Since the partition is marked `encrypted` and :doc:`Flash Encryption <../../security/flash-encryption>` is enabled, bootloader will encrypt this partition using flash encryption key on first boot. Alternatively, the keys can be generated after startup using ``nvs_flash_generate_keys`` API provided by ``nvs_flash.h``, which will then write those keys onto the key-partition in encrypted form.
It is possible for an application to use different keys for different NVS partitions and thereby have multiple key-partitions. However, it is a responsibilty of the application to provide correct key-partition/keys for the purpose of encryption/decryption.
It is possible for an application to use different keys for different NVS partitions and thereby have multiple key-partitions. However, it is a responsibility of the application to provide correct key-partition/keys for the purpose of encryption/decryption.
Encrypted Read/Write
^^^^^^^^^^^^^^^^^^^^
@ -284,3 +284,8 @@ Applications are expected to follow the following steps in order to perform NVS
4. Open a namespace using ``nvs_open`` or ``nvs_open_from_part`` APIs
5. Perform NVS read/write operations using ``nvs_read_*`` or ``nvs_write_*``
6. Deinitialise NVS partition using ``nvs_flash_deinit``.
NVS iterators
^^^^^^^^^^^^^
Iterators allow to list key-value pairs stored in NVS based on specified partition name, namespace and data type. ``nvs_entry_find`` returns an opaque handle, which is used in subsequent calls to ``nvs_entry_next`` and ``nvs_entry_info`` function. ``nvs_entry_next`` function returns iterator to the next key-value pair. If none or no other key-value pair was found for given criteria, ``nvs_entry_find`` and ``nvs_entry_next`` functions return NULL. In that case, iterator does not have to be released. Otherwise, ``nvs_release_iterator`` function has to be used, when iterator is no longer needed. Information about each key-value pair can be obtained from ``nvs_entry_info`` function.

View file

@ -77,20 +77,39 @@ typedef enum {
*/
typedef nvs_open_mode_t nvs_open_mode IDF_DEPRECATED("Replace with nvs_open_mode_t");
/**
* @brief Types of variables
*
*/
typedef enum {
NVS_TYPE_U8 = 0x01,
NVS_TYPE_I8 = 0x11,
NVS_TYPE_U16 = 0x02,
NVS_TYPE_I16 = 0x12,
NVS_TYPE_U32 = 0x04,
NVS_TYPE_I32 = 0x14,
NVS_TYPE_U64 = 0x08,
NVS_TYPE_I64 = 0x18,
NVS_TYPE_STR = 0x21,
NVS_TYPE_BLOB = 0x42,
NVS_TYPE_ANY = 0xff // Must be last
NVS_TYPE_U8 = 0x01, /*!< Type uint8_t */
NVS_TYPE_I8 = 0x11, /*!< Type int8_t */
NVS_TYPE_U16 = 0x02, /*!< Type uint16_t */
NVS_TYPE_I16 = 0x12, /*!< Type int16_t */
NVS_TYPE_U32 = 0x04, /*!< Type uint32_t */
NVS_TYPE_I32 = 0x14, /*!< Type int32_t */
NVS_TYPE_U64 = 0x08, /*!< Type uint64_t */
NVS_TYPE_I64 = 0x18, /*!< Type int64_t */
NVS_TYPE_STR = 0x21, /*!< Type string */
NVS_TYPE_BLOB = 0x42, /*!< Type blob */
NVS_TYPE_ANY = 0xff /*!< Must be last */
} nvs_type_t;
/**
* @brief information about entry obtained from nvs_entry_info function
*/
typedef struct {
char namespace_name[16]; /*!< Namespace to which key-value belong */
char key[16]; /*!< Key of stored key-value pair */
nvs_type_t type; /*!< Type of stored key-value pair */
} nvs_entry_info_t;
/**
* Opaque pointer type representing iterator to nvs entries
*/
typedef struct nvs_opaque_iterator_t *nvs_iterator_t;
/**
* @brief Open non-volatile storage with a given namespace from the default NVS partition
*
@ -105,7 +124,7 @@ typedef enum {
* at least 15 characters. Shouldn't be empty.
* @param[in] open_mode NVS_READWRITE or NVS_READONLY. If NVS_READONLY, will
* open a handle for reading only. All write requests will
* be rejected for this handle.
* be rejected for this handle.
* @param[out] out_handle If successful (return code is zero), handle will be
* returned in this argument.
*
@ -131,9 +150,9 @@ esp_err_t nvs_open(const char* name, nvs_open_mode_t open_mode, nvs_handle_t *ou
* @param[in] name Namespace name. Maximal length is determined by the
* underlying implementation, but is guaranteed to be
* at least 15 characters. Shouldn't be empty.
* @param[in] open_mode NVS_READWRITE or NVS_READONLY. If NVS_READONLY, will
* open a handle for reading only. All write requests will
* be rejected for this handle.
* @param[in] open_mode NVS_READWRITE or NVS_READONLY. If NVS_READONLY, will
* open a handle for reading only. All write requests will
* be rejected for this handle.
* @param[out] out_handle If successful (return code is zero), handle will be
* returned in this argument.
*
@ -276,7 +295,7 @@ esp_err_t nvs_get_u64 (nvs_handle_t handle, const char* key, uint64_t* out_value
*
* All functions expect out_value to be a pointer to an already allocated variable
* of the given type.
*
*
* nvs_get_str and nvs_get_blob functions support WinAPI-style length queries.
* To get the size necessary to store the value, call nvs_get_str or nvs_get_blob
* with zero out_value and non-zero pointer to length. Variable pointed to
@ -436,7 +455,7 @@ typedef struct {
* Return param nvs_stats will be filled not with correct values because
* not all pages will be counted. Counting will be interrupted at the first INVALID page.
*/
esp_err_t nvs_get_stats(const char* part_name, nvs_stats_t* nvs_stats);
esp_err_t nvs_get_stats(const char *part_name, nvs_stats_t *nvs_stats);
/**
* @brief Calculate all entries in a namespace.
@ -476,6 +495,98 @@ esp_err_t nvs_get_stats(const char* part_name, nvs_stats_t* nvs_stats);
*/
esp_err_t nvs_get_used_entry_count(nvs_handle_t handle, size_t* used_entries);
/**
* @brief Create an iterator to enumerate NVS entries based on one or more parameters
*
* \code{c}
* // Example of listing all the key-value pairs of any type under specified partition and namespace
* nvs_iterator_t it = nvs_entry_find(partition, namespace, NVS_TYPE_ANY);
* while (it != NULL) {
* nvs_entry_info_t info;
* nvs_entry_info(it, &info);
* it = nvs_entry_next(it);
* printf("key '%s', type '%d' \n", info.key, info.type);
* };
* // Note: no need to release iterator obtained from nvs_entry_find function when
* // nvs_entry_find or nvs_entry_next function return NULL, indicating no other
* // element for specified criteria was found.
* }
* \endcode
*
* @param[in] part_name Partition name
*
* @param[in] namespace_name Set this value if looking for entries with
* a specific namespace. Pass NULL otherwise.
*
* @param[in] type One of nvs_type_t values.
*
* @return
* Iterator used to enumerate all the entries found,
* or NULL if no entry satisfying criteria was found.
* Iterator obtained through this function has to be released
* using nvs_release_iterator when not used any more.
*/
nvs_iterator_t nvs_entry_find(const char *part_name, const char *namespace_name, nvs_type_t type);
/**
* @brief Returns next item matching the iterator criteria, NULL if no such item exists.
*
* Note that any copies of the iterator will be invalid after this call.
*
* @param[in] iterator Iterator obtained from nvs_entry_find function. Must be non-NULL.
*
* @return
* NULL if no entry was found, valid nvs_iterator_t otherwise.
*/
nvs_iterator_t nvs_entry_next(nvs_iterator_t iterator);
/**
* @brief Fills nvs_entry_info_t structure with information about entry pointed to by the iterator.
*
* @param[in] iterator Iterator obtained from nvs_entry_find or nvs_entry_next function. Must be non-NULL.
*
* @param[out] out_info Structure to which entry information is copied.
*/
void nvs_entry_info(nvs_iterator_t iterator, nvs_entry_info_t *out_info);
/**
* @brief Release iterator
*
* @param[in] iterator Release iterator obtained from nvs_entry_find function. NULL argument is allowed.
*
*/
void nvs_release_iterator(nvs_iterator_t iterator);
/**
* @brief Returns next item matching the iterator criteria, NULL if no such item exists.
*
* Note that any copies of the iterator will be invalid after this call.
*
* @param[in] iterator Iterator obtained from nvs_entry_find function. Must be non-NULL.
*
* @return
* NULL if no entry was found, valid nvs_iterator_t otherwise.
*/
nvs_iterator_t nvs_entry_next(nvs_iterator_t iterator);
/**
* @brief Fills nvs_entry_info_t structure with information about entry pointed to by the iterator.
*
* @param[in] iterator Iterator obtained from nvs_entry_find or nvs_entry_next function. Must be non-NULL.
*
* @param[out] out_info Structure to which entry information is copied.
*/
void nvs_entry_info(nvs_iterator_t iterator, nvs_entry_info_t *out_info);
/**
* @brief Release iterator
*
* @param[in] iterator Release iterator obtained from nvs_entry_find function. NULL argument is allowed.
*
*/
void nvs_release_iterator(nvs_iterator_t iterator);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -688,4 +688,66 @@ extern "C" esp_err_t nvs_flash_read_security_cfg(const esp_partition_t* partitio
return ESP_OK;
}
#endif
static nvs_iterator_t create_iterator(nvs::Storage *storage, nvs_type_t type)
{
nvs_iterator_t it = (nvs_iterator_t)calloc(1, sizeof(nvs_opaque_iterator_t));
if (it == NULL) {
return NULL;
}
it->storage = storage;
it->type = type;
return it;
}
extern "C" nvs_iterator_t nvs_entry_find(const char *part_name, const char *namespace_name, nvs_type_t type)
{
Lock lock;
nvs::Storage *pStorage;
pStorage = lookup_storage_from_name(part_name);
if (pStorage == NULL) {
return NULL;
}
nvs_iterator_t it = create_iterator(pStorage, type);
if (it == NULL) {
return NULL;
}
bool entryFound = pStorage->findEntry(it, namespace_name);
if (!entryFound) {
free(it);
return NULL;
}
return it;
}
extern "C" nvs_iterator_t nvs_entry_next(nvs_iterator_t it)
{
Lock lock;
assert(it);
bool entryFound = it->storage->nextEntry(it);
if (!entryFound) {
free(it);
return NULL;
}
return it;
}
extern "C" void nvs_entry_info(nvs_iterator_t it, nvs_entry_info_t *out_info)
{
*out_info = it->entry_info;
}
extern "C" void nvs_release_iterator(nvs_iterator_t it)
{
free(it);
}

View file

@ -704,4 +704,67 @@ esp_err_t Storage::calcEntriesInNamespace(uint8_t nsIndex, size_t& usedEntries)
return ESP_OK;
}
void Storage::fillEntryInfo(Item &item, nvs_entry_info_t &info)
{
info.type = static_cast<nvs_type_t>(item.datatype);
strncpy(info.key, item.key, sizeof(info.key));
for (auto &name : mNamespaces) {
if(item.nsIndex == name.mIndex) {
strncpy(info.namespace_name, name.mName, sizeof(info.namespace_name));
break;
}
}
}
bool Storage::findEntry(nvs_opaque_iterator_t* it, const char* namespace_name)
{
it->entryIndex = 0;
it->nsIndex = Page::NS_ANY;
it->page = mPageManager.begin();
if (namespace_name != nullptr) {
if(createOrOpenNamespace(namespace_name, false, it->nsIndex) != ESP_OK) {
return false;
}
}
return nextEntry(it);
}
inline bool isIterableItem(Item& item)
{
return (item.nsIndex != 0 &&
item.datatype != ItemType::BLOB &&
item.datatype != ItemType::BLOB_IDX);
}
inline bool isMultipageBlob(Item& item)
{
return (item.datatype == ItemType::BLOB_DATA && item.chunkIndex != 0);
}
bool Storage::nextEntry(nvs_opaque_iterator_t* it)
{
Item item;
esp_err_t err;
for (auto page = it->page; page != mPageManager.end(); ++page) {
do {
err = page->findItem(it->nsIndex, (ItemType)it->type, nullptr, it->entryIndex, item);
it->entryIndex += item.span;
if(err == ESP_OK && isIterableItem(item) && !isMultipageBlob(item)) {
fillEntryInfo(item, it->entry_info);
it->page = page;
return true;
}
} while (err != ESP_ERR_NVS_NOT_FOUND);
it->entryIndex = 0;
}
return false;
}
}

View file

@ -121,6 +121,10 @@ public:
esp_err_t calcEntriesInNamespace(uint8_t nsIndex, size_t& usedEntries);
bool findEntry(nvs_opaque_iterator_t*, const char* name);
bool nextEntry(nvs_opaque_iterator_t* it);
protected:
Page& getCurrentPage()
@ -134,6 +138,7 @@ protected:
void eraseOrphanDataBlobs(TBlobIndexList&);
void fillEntryInfo(Item &item, nvs_entry_info_t &info);
esp_err_t findItem(uint8_t nsIndex, ItemType datatype, const char* key, Page* &page, Item& item, uint8_t chunkIdx = Page::CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY);
@ -148,6 +153,14 @@ protected:
} // namespace nvs
struct nvs_opaque_iterator_t
{
nvs_type_t type;
uint8_t nsIndex;
size_t entryIndex;
nvs::Storage *storage;
intrusive_list<nvs::Page>::iterator page;
nvs_entry_info_t entry_info;
};
#endif /* nvs_storage_hpp */

View file

@ -27,19 +27,19 @@ namespace nvs
{
enum class ItemType : uint8_t {
U8 = 0x01,
I8 = 0x11,
U16 = 0x02,
I16 = 0x12,
U32 = 0x04,
I32 = 0x14,
U64 = 0x08,
I64 = 0x18,
SZ = 0x21,
U8 = NVS_TYPE_U8,
I8 = NVS_TYPE_I8,
U16 = NVS_TYPE_U16,
I16 = NVS_TYPE_I16,
U32 = NVS_TYPE_U32,
I32 = NVS_TYPE_I32,
U64 = NVS_TYPE_U64,
I64 = NVS_TYPE_I64,
SZ = NVS_TYPE_STR,
BLOB = 0x41,
BLOB_DATA = 0x42,
BLOB_DATA = NVS_TYPE_BLOB,
BLOB_IDX = 0x48,
ANY = 0xff
ANY = NVS_TYPE_ANY
};
enum class VerOffset: uint8_t {

View file

@ -25,6 +25,7 @@
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <string>
#define TEST_ESP_ERR(rc, res) CHECK((rc) == (res))
#define TEST_ESP_OK(rc) CHECK((rc) == ESP_OK)
@ -609,6 +610,168 @@ TEST_CASE("nvs api tests", "[nvs]")
nvs_close(handle_2);
}
TEST_CASE("nvs iterators tests", "[nvs]")
{
SpiFlashEmulator emu(5);
const uint32_t NVS_FLASH_SECTOR = 0;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 5;
emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
for (uint16_t i = NVS_FLASH_SECTOR; i < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; ++i) {
spi_flash_erase_sector(i);
}
TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN));
nvs_iterator_t it;
nvs_entry_info_t info;
nvs_handle handle_1;
nvs_handle handle_2;
const uint32_t blob = 0x11223344;
const char *name_1 = "namespace1";
const char *name_2 = "namespace2";
TEST_ESP_OK(nvs_open(name_1, NVS_READWRITE, &handle_1));
TEST_ESP_OK(nvs_open(name_2, NVS_READWRITE, &handle_2));
TEST_ESP_OK(nvs_set_i8(handle_1, "value1", -11));
TEST_ESP_OK(nvs_set_u8(handle_1, "value2", 11));
TEST_ESP_OK(nvs_set_i16(handle_1, "value3", 1234));
TEST_ESP_OK(nvs_set_u16(handle_1, "value4", -1234));
TEST_ESP_OK(nvs_set_i32(handle_1, "value5", -222));
TEST_ESP_OK(nvs_set_i32(handle_1, "value6", -222));
TEST_ESP_OK(nvs_set_i32(handle_1, "value7", -222));
TEST_ESP_OK(nvs_set_u32(handle_1, "value8", 222));
TEST_ESP_OK(nvs_set_u32(handle_1, "value9", 222));
TEST_ESP_OK(nvs_set_str(handle_1, "value10", "foo"));
TEST_ESP_OK(nvs_set_blob(handle_1, "value11", &blob, sizeof(blob)));
TEST_ESP_OK(nvs_set_i32(handle_2, "value1", -111));
TEST_ESP_OK(nvs_set_i32(handle_2, "value2", -111));
TEST_ESP_OK(nvs_set_i64(handle_2, "value3", -555));
TEST_ESP_OK(nvs_set_u64(handle_2, "value4", 555));
auto entry_count = [](const char *part, const char *name, nvs_type_t type)-> int {
int count;
nvs_iterator_t it = nvs_entry_find(part, name, type);
for (count = 0; it != nullptr; count++) {
it = nvs_entry_next(it);
}
return count;
};
SECTION("Number of entries found for specified namespace and type is correct")
{
CHECK(nvs_entry_find("", NULL, NVS_TYPE_ANY) == NULL);
CHECK(entry_count(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_ANY) == 15);
CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_ANY) == 11);
CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_I32) == 3);
CHECK(entry_count(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_I32) == 5);
CHECK(entry_count(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_U64) == 1);
}
SECTION("New entry is not created when existing key-value pair is set")
{
CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_2, NVS_TYPE_ANY) == 4);
TEST_ESP_OK(nvs_set_i32(handle_2, "value1", -222));
CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_2, NVS_TYPE_ANY) == 4);
}
SECTION("Number of entries found decrease when entry is erased")
{
CHECK(entry_count(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_U64) == 1);
TEST_ESP_OK(nvs_erase_key(handle_2, "value4"));
CHECK(entry_count(NVS_DEFAULT_PART_NAME, "", NVS_TYPE_U64) == 0);
}
SECTION("All fields of nvs_entry_info_t structure are correct")
{
it = nvs_entry_find(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_I32);
CHECK(it != nullptr);
string key = "value5";
do {
nvs_entry_info(it, &info);
CHECK(string(name_1) == info.namespace_name);
CHECK(key == info.key);
CHECK(info.type == NVS_TYPE_I32);
it = nvs_entry_next(it);
key[5]++;
} while (it != NULL);
nvs_release_iterator(it);
}
SECTION("Entry info is not affected by subsequent erase")
{
nvs_entry_info_t info_after_erase;
it = nvs_entry_find(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_ANY);
nvs_entry_info(it, &info);
TEST_ESP_OK(nvs_erase_key(handle_1, "value1"));
nvs_entry_info(it, &info_after_erase);
CHECK(memcmp(&info, &info_after_erase, sizeof(info)) == 0);
nvs_release_iterator(it);
}
SECTION("Entry info is not affected by subsequent set")
{
nvs_entry_info_t info_after_set;
it = nvs_entry_find(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_ANY);
nvs_entry_info(it, &info);
TEST_ESP_OK(nvs_set_u8(handle_1, info.key, 44));
nvs_entry_info(it, &info_after_set);
CHECK(memcmp(&info, &info_after_set, sizeof(info)) == 0);
nvs_release_iterator(it);
}
SECTION("Iterating over multiple pages works correctly")
{
nvs_handle handle_3;
const char *name_3 = "namespace3";
const int entries_created = 250;
TEST_ESP_OK(nvs_open(name_3, NVS_READWRITE, &handle_3));
for (size_t i = 0; i < entries_created; i++) {
TEST_ESP_OK(nvs_set_u8(handle_3, to_string(i).c_str(), 123));
}
int entries_found = 0;
it = nvs_entry_find(NVS_DEFAULT_PART_NAME, name_3, NVS_TYPE_ANY);
while(it != nullptr) {
entries_found++;
it = nvs_entry_next(it);
}
CHECK(entries_created == entries_found);
nvs_release_iterator(it);
nvs_close(handle_3);
}
SECTION("Iterating over multi-page blob works correctly")
{
nvs_handle handle_3;
const char *name_3 = "namespace3";
const uint8_t multipage_blob[4096 * 2] = { 0 };
const int NUMBER_OF_ENTRIES_PER_PAGE = 125;
size_t occupied_entries;
TEST_ESP_OK(nvs_open(name_3, NVS_READWRITE, &handle_3));
nvs_set_blob(handle_3, "blob", multipage_blob, sizeof(multipage_blob));
TEST_ESP_OK(nvs_get_used_entry_count(handle_3, &occupied_entries));
CHECK(occupied_entries > NUMBER_OF_ENTRIES_PER_PAGE * 2);
CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_3, NVS_TYPE_BLOB) == 1);
nvs_close(handle_3);
}
nvs_close(handle_1);
nvs_close(handle_2);
}
TEST_CASE("wifi test", "[nvs]")
{
SpiFlashEmulator emu(10);

View file

@ -11,6 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <functional>
#include "catch.hpp"
#include "esp_spi_flash.h"
#include "spi_flash_emulation.h"

View file

@ -73,6 +73,13 @@ static struct {
struct arg_end *end;
} namespace_args;
static struct {
struct arg_str *partition;
struct arg_str *namespace;
struct arg_str *type;
struct arg_end *end;
} list_args;
static nvs_type_t str_to_type(const char *type)
{
@ -86,6 +93,18 @@ static nvs_type_t str_to_type(const char *type)
return NVS_TYPE_ANY;
}
static const char *type_to_str(nvs_type_t type)
{
for (int i = 0; i < TYPE_STR_PAIR_SIZE; i++) {
const type_str_pair_t *p = &type_str_pair[i];
if (p->type == type) {
return p->str;
}
}
return "Unknown";
}
static esp_err_t store_blob(nvs_handle_t nvs, const char *key, const char *str_values)
{
uint8_t value;
@ -141,6 +160,7 @@ static void print_blob(const char *blob, size_t len)
printf("\n");
}
static esp_err_t set_value_in_nvs(const char *key, const char *str_type, const char *str_value)
{
esp_err_t err;
@ -150,6 +170,7 @@ static esp_err_t set_value_in_nvs(const char *key, const char *str_type, const c
nvs_type_t type = str_to_type(str_type);
if (type == NVS_TYPE_ANY) {
ESP_LOGE(TAG, "Type '%s' is undefined", str_type);
return ESP_ERR_NVS_TYPE_MISMATCH;
}
@ -236,6 +257,7 @@ static esp_err_t get_value_from_nvs(const char *key, const char *str_type)
nvs_type_t type = str_to_type(str_type);
if (type == NVS_TYPE_ANY) {
ESP_LOGE(TAG, "Type '%s' is undefined", str_type);
return ESP_ERR_NVS_TYPE_MISMATCH;
}
@ -248,51 +270,51 @@ static esp_err_t get_value_from_nvs(const char *key, const char *str_type)
int8_t value;
err = nvs_get_i8(nvs, key, &value);
if (err == ESP_OK) {
printf("Value associated with key '%s' is %d \n", key, value);
printf("%d\n", value);
}
} else if (type == NVS_TYPE_U8) {
uint8_t value;
err = nvs_get_u8(nvs, key, &value);
if (err == ESP_OK) {
printf("Value associated with key '%s' is %u \n", key, value);
printf("%u\n", value);
}
} else if (type == NVS_TYPE_I16) {
int16_t value;
err = nvs_get_i16(nvs, key, &value);
if (err == ESP_OK) {
printf("Value associated with key '%s' is %d \n", key, value);
printf("%u\n", value);
}
} else if (type == NVS_TYPE_U16) {
uint16_t value;
if ((err = nvs_get_u16(nvs, key, &value)) == ESP_OK) {
printf("Value associated with key '%s' is %u", key, value);
printf("%u\n", value);
}
} else if (type == NVS_TYPE_I32) {
int32_t value;
if ((err = nvs_get_i32(nvs, key, &value)) == ESP_OK) {
printf("Value associated with key '%s' is %d \n", key, value);
printf("%d\n", value);
}
} else if (type == NVS_TYPE_U32) {
uint32_t value;
if ((err = nvs_get_u32(nvs, key, &value)) == ESP_OK) {
printf("Value associated with key '%s' is %u \n", key, value);
printf("%u\n", value);
}
} else if (type == NVS_TYPE_I64) {
int64_t value;
if ((err = nvs_get_i64(nvs, key, &value)) == ESP_OK) {
printf("Value associated with key '%s' is %lld \n", key, value);
printf("%lld\n", value);
}
} else if (type == NVS_TYPE_U64) {
uint64_t value;
if ( (err = nvs_get_u64(nvs, key, &value)) == ESP_OK) {
printf("Value associated with key '%s' is %llu \n", key, value);
printf("%llu\n", value);
}
} else if (type == NVS_TYPE_STR) {
size_t len;
if ( (err = nvs_get_str(nvs, key, NULL, &len)) == ESP_OK) {
char *str = (char *)malloc(len);
if ( (err = nvs_get_str(nvs, key, str, &len)) == ESP_OK) {
printf("String associated with key '%s' is %s \n", key, str);
printf("%s\n", str);
}
free(str);
}
@ -301,7 +323,6 @@ static esp_err_t get_value_from_nvs(const char *key, const char *str_type)
if ( (err = nvs_get_blob(nvs, key, NULL, &len)) == ESP_OK) {
char *blob = (char *)malloc(len);
if ( (err = nvs_get_blob(nvs, key, blob, &len)) == ESP_OK) {
printf("Blob associated with key '%s' is %d bytes long: \n", key, len);
print_blob(blob, len);
}
free(blob);
@ -335,7 +356,7 @@ static esp_err_t erase_all(const char *name)
{
nvs_handle_t nvs;
esp_err_t err = nvs_open(current_namespace, NVS_READWRITE, &nvs);
esp_err_t err = nvs_open(name, NVS_READWRITE, &nvs);
if (err == ESP_OK) {
err = nvs_erase_all(nvs);
if (err == ESP_OK) {
@ -344,10 +365,33 @@ static esp_err_t erase_all(const char *name)
}
ESP_LOGI(TAG, "Namespace '%s' was %s erased", name, (err == ESP_OK) ? "" : "not");
nvs_close(nvs);
return ESP_OK;
}
static int list(const char *part, const char *name, const char *str_type)
{
nvs_type_t type = str_to_type(str_type);
nvs_iterator_t it = nvs_entry_find(part, NULL, type);
if (it == NULL) {
ESP_LOGE(TAG, "No such enty was found");
return 1;
}
do {
nvs_entry_info_t info;
nvs_entry_info(it, &info);
it = nvs_entry_next(it);
printf("namespace '%s', key '%s', type '%s' \n",
info.namespace_name, info.key, type_to_str(info.type));
} while (it != NULL);
return 0;
}
static int set_value(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &set_args);
@ -368,7 +412,6 @@ static int set_value(int argc, char **argv)
}
return 0;
}
static int get_value(int argc, char **argv)
@ -445,10 +488,30 @@ static int set_namespace(int argc, char **argv)
return 0;
}
static int list_entries(int argc, char **argv)
{
list_args.partition->sval[0] = "";
list_args.namespace->sval[0] = "";
list_args.type->sval[0] = "";
int nerrors = arg_parse(argc, argv, (void **) &list_args);
if (nerrors != 0) {
arg_print_errors(stderr, list_args.end, argv[0]);
return 1;
}
const char *part = list_args.partition->sval[0];
const char *name = list_args.namespace->sval[0];
const char *type = list_args.type->sval[0];
return list(part, name, type);
}
void register_nvs()
{
set_args.key = arg_str1(NULL, NULL, "<key>", "key of the value to be set");
set_args.type = arg_str1(NULL, NULL, "<type>", ARG_TYPE_STR);
set_args.value = arg_str1("v", "value", "<value>", "value to be stored");
set_args.end = arg_end(2);
@ -465,9 +528,14 @@ void register_nvs()
namespace_args.namespace = arg_str1(NULL, NULL, "<namespace>", "namespace of the partition to be selected");
namespace_args.end = arg_end(2);
list_args.partition = arg_str1(NULL, NULL, "<partition>", "partition name");
list_args.namespace = arg_str0("n", "namespace", "<namespace>", "namespace name");
list_args.type = arg_str0("t", "type", "<type>", ARG_TYPE_STR);
list_args.end = arg_end(2);
const esp_console_cmd_t set_cmd = {
.command = "nvs_set",
.help = "Set variable in selected namespace. Blob type must be comma separated list of hex values. \n"
.help = "Set key-value pair in selected namespace.\n"
"Examples:\n"
" nvs_set VarName i32 -v 123 \n"
" nvs_set VarName srt -v YourString \n"
@ -479,7 +547,7 @@ void register_nvs()
const esp_console_cmd_t get_cmd = {
.command = "nvs_get",
.help = "Get variable from selected namespace. \n"
.help = "Get key-value pair from selected namespace. \n"
"Example: nvs_get VarName i32",
.hint = NULL,
.func = &get_value,
@ -488,7 +556,7 @@ void register_nvs()
const esp_console_cmd_t erase_cmd = {
.command = "nvs_erase",
.help = "Erase variable from current namespace",
.help = "Erase key-value pair from current namespace",
.hint = NULL,
.func = &erase_value,
.argtable = &erase_args
@ -510,9 +578,21 @@ void register_nvs()
.argtable = &namespace_args
};
const esp_console_cmd_t list_entries_cmd = {
.command = "nvs_list",
.help = "List stored key-value pairs stored in NVS."
"Namespace and type can be specified to print only those key-value pairs.\n"
"Following command list variables stored inside 'nvs' partition, under namespace 'storage' with type uint32_t"
"Example: nvs_list nvs -n storage -t u32 \n",
.hint = NULL,
.func = &list_entries,
.argtable = &list_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&set_cmd));
ESP_ERROR_CHECK(esp_console_cmd_register(&get_cmd));
ESP_ERROR_CHECK(esp_console_cmd_register(&erase_cmd));
ESP_ERROR_CHECK(esp_console_cmd_register(&namespace_cmd));
ESP_ERROR_CHECK(esp_console_cmd_register(&list_entries_cmd));
ESP_ERROR_CHECK(esp_console_cmd_register(&erase_namespace_cmd));
}