NVS: expose C++ API

Closes https://github.com/espressif/esp-idf/issues/3540
This commit is contained in:
Jakob Hasse 2019-11-25 11:32:40 +08:00
parent 6655fa8a93
commit d286876d34
26 changed files with 1674 additions and 227 deletions

View File

@ -1,9 +1,13 @@
set(srcs "src/nvs_api.cpp"
"src/nvs_cxx_api.cpp"
"src/nvs_item_hash_list.cpp"
"src/nvs_ops.cpp"
"src/nvs_page.cpp"
"src/nvs_pagemanager.cpp"
"src/nvs_storage.cpp"
"src/nvs_handle_simple.cpp"
"src/nvs_handle_locked.cpp"
"src/nvs_partition_manager.cpp"
"src/nvs_types.cpp")
if(CONFIG_NVS_ENCRYPTION)
list(APPEND srcs "src/nvs_encr.cpp")

View File

@ -47,7 +47,7 @@ Data type check is also performed when reading a value. An error is returned if
Namespaces
^^^^^^^^^^
To mitigate potential conflicts in key names between different components, NVS assigns each key-value pair to one of namespaces. Namespace names follow the same rules as key names, i.e., the maximum length is 15 characters. Namespace name is specified in the ``nvs_open`` or ``nvs_open_from_part`` call. This call returns an opaque handle, which is used in subsequent calls to the ``nvs_read_*``, ``nvs_write_*``, and ``nvs_commit`` functions. This way, a handle is associated with a namespace, and key names will not collide with same names in other namespaces.
To mitigate potential conflicts in key names between different components, NVS assigns each key-value pair to one of namespaces. Namespace names follow the same rules as key names, i.e., the maximum length is 15 characters. Namespace name is specified in the ``nvs_open`` or ``nvs_open_from_part`` call. This call returns an opaque handle, which is used in subsequent calls to the ``nvs_get_*``, ``nvs_set_*``, and ``nvs_commit`` functions. This way, a handle is associated with a namespace, and key names will not collide with same names in other namespaces.
Please note that the namespaces with the same name in different NVS partitions are considered as separate namespaces.
@ -282,7 +282,7 @@ It is possible for an application to use different keys for different NVS partit
Encrypted Read/Write
^^^^^^^^^^^^^^^^^^^^
The same NVS API functions ``nvs_read_*`` or ``nvs_write_*`` can be used for reading of, and writing to an encrypted nvs partition as well. However, the API functions for initialising NVS partitions are different: ``nvs_flash_secure_init`` and ``nvs_flash_secure_init_partition`` instead of ``nvs_flash_init`` and ``nvs_flash_init_partition`` respectively. The ``nvs_sec_cfg_t`` structure required for these API functions can be populated using ``nvs_flash_generate_keys`` or ``nvs_flash_read_security_cfg``.
The same NVS API functions ``nvs_get_*`` or ``nvs_set_*`` can be used for reading of, and writing to an encrypted nvs partition as well. However, the API functions for initialising NVS partitions are different: ``nvs_flash_secure_init`` and ``nvs_flash_secure_init_partition`` instead of ``nvs_flash_init`` and ``nvs_flash_init_partition`` respectively. The ``nvs_sec_cfg_t`` structure required for these API functions can be populated using ``nvs_flash_generate_keys`` or ``nvs_flash_read_security_cfg``.
Applications are expected to follow the steps below in order to perform NVS read/write operations with encryption enabled.
@ -290,7 +290,7 @@ Applications are expected to follow the steps below in order to perform NVS read
2. Populate the ``nvs_sec_cfg_t`` struct using the ``nvs_flash_read_security_cfg`` or ``nvs_flash_generate_keys`` API functions.
3. Initialise NVS flash partition using the ``nvs_flash_secure_init`` or ``nvs_flash_secure_init_partition`` API functions.
4. Open a namespace using the ``nvs_open`` or ``nvs_open_from_part`` API functions.
5. Perform NVS read/write operations using ``nvs_read_*`` or ``nvs_write_*``.
5. Perform NVS read/write operations using ``nvs_get_*`` or ``nvs_set_*``.
6. Deinitialise an NVS partition using ``nvs_flash_deinit``.
NVS iterators

View File

@ -46,7 +46,7 @@ NVS 的操作对象为键值对,其中键是 ASCII 字符串,当前支持最
命名空间
^^^^^^^^^^
为了减少不同组件之间键名的潜在冲突NVS 将每个键值对分配给一个命名空间。命名空间的命名规则遵循键名的命名规则,即最多可占 15 个字符。命名空间的名称在调用 ``nvs_open````nvs_open_from_part`` 中指定,调用后将返回一个不透明句柄,用于后续调用 ``nvs_read_*````nvs_write_*````nvs_commit`` 函数。这样,一个句柄关联一个命名空间,键名便不会与其他命名空间中相同键名冲突。请注意,不同 NVS 分区中具有相同名称的命名空间将被视为不同的命名空间。
为了减少不同组件之间键名的潜在冲突NVS 将每个键值对分配给一个命名空间。命名空间的命名规则遵循键名的命名规则,即最多可占 15 个字符。命名空间的名称在调用 ``nvs_open````nvs_open_from_part`` 中指定,调用后将返回一个不透明句柄,用于后续调用 ``nvs_get_*````nvs_set_*````nvs_commit`` 函数。这样,一个句柄关联一个命名空间,键名便不会与其他命名空间中相同键名冲突。请注意,不同 NVS 分区中具有相同名称的命名空间将被视为不同的命名空间。
安全性、篡改性及鲁棒性
@ -277,7 +277,7 @@ NVS 密钥分区
加密读取/写入
^^^^^^^^^^^^^^^^^^^^
``nvs_read_*````nvs_write_*`` 等 NVS API 函数同样可以对 NVS 加密分区执行读写操作。但用于初始化 NVS 非加密分区和加密分区的 API 则有所不同:初始化 NVS 非加密分区可以使用 ``nvs_flash_init````nvs_flash_init_partition``,但初始化 NVS 加密分区则需调用 ``nvs_flash_secure_init````nvs_flash_secure_init_partition``。上述 API 函数所需的 ``nvs_sec_cfg_t`` 结构可使用 ``nvs_flash_generate_keys`` 或者 ``nvs_flash_read_security_cfg`` 进行填充。
``nvs_get_*````nvs_set_*`` 等 NVS API 函数同样可以对 NVS 加密分区执行读写操作。但用于初始化 NVS 非加密分区和加密分区的 API 则有所不同:初始化 NVS 非加密分区可以使用 ``nvs_flash_init````nvs_flash_init_partition``,但初始化 NVS 加密分区则需调用 ``nvs_flash_secure_init````nvs_flash_secure_init_partition``。上述 API 函数所需的 ``nvs_sec_cfg_t`` 结构可使用 ``nvs_flash_generate_keys`` 或者 ``nvs_flash_read_security_cfg`` 进行填充。
应用程序如需在加密状态下执行 NVS 读写操作,应遵循以下步骤:
@ -285,7 +285,7 @@ NVS 密钥分区
2. 使用 ``nvs_flash_read_security_cfg````nvs_flash_generate_keys`` API 填充 ``nvs_sec_cfg_t`` 结构;
3. 使用 ``nvs_flash_secure_init````nvs_flash_secure_init_partition`` API 初始化 NVS flash 分区;
4. 使用 ``nvs_open````nvs_open_from_part`` API 打开命名空间;
5. 使用 ``nvs_read_*````nvs_write_*`` API 执行 NVS 读取/写入操作;
5. 使用 ``nvs_get_*````nvs_set_*`` API 执行 NVS 读取/写入操作;
6. 使用 ``nvs_flash_deinit`` API 释放已初始化的 NVS 分区。
NVS 迭代器

View File

@ -289,7 +289,7 @@ esp_err_t nvs_get_u64 (nvs_handle_t handle, const char* key, uint64_t* out_value
/**
* @brief get value for given key
*
* These functions retrieve value for the key, given its name. If key does not
* These functions retrieve the data of an entry, given its key. If key does not
* exist, or the requested variable type doesn't match the type which was used
* when setting a value, an error is returned.
*
@ -491,7 +491,7 @@ esp_err_t nvs_get_stats(const char *part_name, nvs_stats_t *nvs_stats);
* Return param used_entries will be filled 0.
* - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL.
* Return param used_entries will be filled 0.
* - ESP_ERR_INVALID_ARG if nvs_stats equal to NULL.
* - ESP_ERR_INVALID_ARG if used_entries equal to NULL.
* - Other error codes from the underlying storage driver.
* Return param used_entries will be filled 0.
*/

View File

@ -0,0 +1,262 @@
#ifndef NVS_HANDLE_HPP_
#define NVS_HANDLE_HPP_
#include <string>
#include <memory>
#include <type_traits>
#include "nvs.h"
namespace nvs {
/**
* The possible blob types. This is a helper definition for template functions.
*/
enum class ItemType : uint8_t {
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 = NVS_TYPE_BLOB,
BLOB_IDX = 0x48,
ANY = NVS_TYPE_ANY
};
/**
* @brief A handle allowing nvs-entry related operations on the NVS.
*
* @note The scope of this handle may vary depending on the implementation, but normally would be the namespace of
* a particular partition. Outside that scope, nvs entries can't be accessed/altered.
*/
class NVSHandle {
public:
virtual ~NVSHandle() { }
/**
* @brief set value for given key
*
* Sets value for key. Note that physical storage will not be updated until nvs_commit function is called.
*
* @param[in] key Key name. Maximal length is determined by the underlying
* implementation, but is guaranteed to be at least
* 15 characters. Shouldn't be empty.
* @param[in] value The value to set. Allowed types are the ones declared in ItemType.
* For strings, the maximum length (including null character) is
* 4000 bytes.
*
* @return
* - ESP_OK if value was set successfully
* - ESP_ERR_NVS_READ_ONLY if storage handle was opened as read only
* - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints
* - ESP_ERR_NVS_NOT_ENOUGH_SPACE if there is not enough space in the
* underlying storage to save the value
* - ESP_ERR_NVS_REMOVE_FAILED if the value wasn't updated because flash
* write operation has failed. The value was written however, and
* update will be finished after re-initialization of nvs, provided that
* flash operation doesn't fail again.
* - ESP_ERR_NVS_VALUE_TOO_LONG if the string value is too long
*/
template<typename T>
esp_err_t set_item(const char *key, T value);
virtual
esp_err_t set_string(const char *key, const char* value) = 0;
/**
* @brief get value for given key
*
* These functions retrieve value for the key, given its name. If key does not
* exist, or the requested variable type doesn't match the type which was used
* when setting a value, an error is returned.
*
* In case of any error, out_value is not modified.
*
* @param[in] key Key name. Maximal length is determined by the underlying
* implementation, but is guaranteed to be at least
* 15 characters. Shouldn't be empty.
* @param value The output value.
*
* @return
* - ESP_OK if the value was retrieved successfully
* - ESP_ERR_NVS_NOT_FOUND if the requested key doesn't exist
* - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints
* - ESP_ERR_NVS_INVALID_LENGTH if length is not sufficient to store data
*/
template<typename T>
esp_err_t get_item(const char *key, T &value);
/**
* @brief set variable length binary value for given key
*
* This family of functions set value for the key, given its name. Note that
* actual storage will not be updated until nvs_commit function is called.
*
* @param[in] key Key name. Maximal length is 15 characters. Shouldn't be empty.
* @param[in] blob The blob value to set.
* @param[in] len length of binary value to set, in bytes; Maximum length is
* 508000 bytes or (97.6% of the partition size - 4000) bytes
* whichever is lower.
*
* @return
* - ESP_OK if value was set successfully
* - ESP_ERR_NVS_READ_ONLY if storage handle was opened as read only
* - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints
* - ESP_ERR_NVS_NOT_ENOUGH_SPACE if there is not enough space in the
* underlying storage to save the value
* - ESP_ERR_NVS_REMOVE_FAILED if the value wasn't updated because flash
* write operation has failed. The value was written however, and
* update will be finished after re-initialization of nvs, provided that
* flash operation doesn't fail again.
* - ESP_ERR_NVS_VALUE_TOO_LONG if the value is too long
*
* @note compare to \ref nvs_set_blob in nvs.h
*/
virtual esp_err_t set_blob(const char *key, const void* blob, size_t len) = 0;
/**
* @brief get value for given key
*
* These functions retrieve the data of an entry, given its key. If key does not
* exist, or the requested variable type doesn't match the type which was used
* when setting a value, an error is returned.
*
* In case of any error, out_value is not modified.
*
* Both functions expect out_value to be a pointer to an already allocated variable
* of the given type.
*
* It is suggested that nvs_get/set_str is used for zero-terminated C strings, and
* nvs_get/set_blob used for arbitrary data structures.
*
* @param[in] key Key name. Maximal length is determined by the underlying
* implementation, but is guaranteed to be at least
* 15 characters. Shouldn't be empty.
* @param out_str/ Pointer to the output value.
* out_blob
* @param[inout] length A non-zero pointer to the variable holding the length of out_value.
* It will be set to the actual length of the value
* written. For nvs_get_str this includes the zero terminator.
*
* @return
* - ESP_OK if the value was retrieved successfully
* - ESP_ERR_NVS_NOT_FOUND if the requested key doesn't exist
* - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints
* - ESP_ERR_NVS_INVALID_LENGTH if length is not sufficient to store data
*/
virtual esp_err_t get_string(const char *key, char* out_str, size_t len) = 0;
virtual esp_err_t get_blob(const char *key, void* out_blob, size_t len) = 0;
/**
* @brief Looks up the size of an entry's data.
*
* For strings, this size includes the zero terminator.
*/
virtual esp_err_t get_item_size(ItemType datatype, const char *key, size_t &size) = 0;
/**
* @brief Erases an entry.
*/
virtual esp_err_t erase_item(const char* key) = 0;
/**
* Erases all entries in the scope of this handle. The scope may vary, depending on the implementation.
*
* @not If you want to erase the whole nvs flash (partition), refer to \ref
*/
virtual esp_err_t erase_all() = 0;
/**
* Commits all changes done through this handle so far.
*/
virtual esp_err_t commit() = 0;
/**
* @brief Calculate all entries in the scope of the handle.
*
* @param[out] used_entries Returns amount of used entries from a namespace on success.
*
*
* @return
* - ESP_OK if the changes have been written successfully.
* Return param used_entries will be filled valid value.
* - ESP_ERR_NVS_NOT_INITIALIZED if the storage driver is not initialized.
* Return param used_entries will be filled 0.
* - ESP_ERR_INVALID_ARG if nvs_stats equal to NULL.
* - Other error codes from the underlying storage driver.
* Return param used_entries will be filled 0.
*/
virtual esp_err_t get_used_entry_count(size_t& usedEntries) = 0;
protected:
virtual esp_err_t set_typed_item(ItemType datatype, const char *key, const void* data, size_t dataSize) = 0;
virtual esp_err_t get_typed_item(ItemType datatype, const char *key, void* data, size_t dataSize) = 0;
};
/**
* @brief Opens non-volatile storage and returns a handle object.
*
* The handle is automatically closed on desctruction. The scope of the handle is the namespace ns_name
* in a particular partition partition_name.
* The parameters partition_name, ns_name and open_mode have the same meaning and restrictions as the parameters
* part_name, name and open_mode in \ref nvs_open_from_partition, respectively.
*
* @param err an optional pointer to an esp_err_t result of the open operation, having the same meaning as the return
* value in \ref nvs_open_from_partition:
* - ESP_OK if storage handle was opened successfully
* - ESP_ERR_NVS_NOT_INITIALIZED if the storage driver is not initialized
* - ESP_ERR_NVS_PART_NOT_FOUND if the partition with label "nvs" is not found
* - ESP_ERR_NVS_NOT_FOUND id namespace doesn't exist yet and
* mode is NVS_READONLY
* - ESP_ERR_NVS_INVALID_NAME if namespace name doesn't satisfy constraints
* - other error codes from the underlying storage driver
*
* @return shared pointer of an nvs handle on success, an empty shared pointer otherwise
*/
std::unique_ptr<NVSHandle> open_nvs_handle_from_partition(const char *partition_name,
const char *ns_name,
nvs_open_mode_t open_mode,
esp_err_t *err = nullptr);
/**
* @brief This function does the same as \ref open_nvs_handle_from_partition but uses the default nvs partition
* instead of a partition_name parameter.
*/
std::unique_ptr<NVSHandle> open_nvs_handle(const char *ns_name,
nvs_open_mode_t open_mode,
esp_err_t *err = nullptr);
// Helper functions for template usage
template<typename T, typename std::enable_if<std::is_integral<T>::value, void*>::type = nullptr>
constexpr ItemType itemTypeOf()
{
return static_cast<ItemType>(((std::is_signed<T>::value)?0x10:0x00) | sizeof(T));
}
template<typename T>
constexpr ItemType itemTypeOf(const T&)
{
return itemTypeOf<T>();
}
// Template Implementations
template<typename T>
esp_err_t NVSHandle::set_item(const char *key, T value) {
return set_typed_item(itemTypeOf(value), key, &value, sizeof(value));
}
template<typename T>
esp_err_t NVSHandle::get_item(const char *key, T &value) {
return get_typed_item(itemTypeOf(value), key, &value, sizeof(value));
}
} // nvs
#endif // NVS_HANDLE_HPP_

View File

@ -16,8 +16,10 @@
#include "nvs_storage.hpp"
#include "intrusive_list.h"
#include "nvs_platform.hpp"
#include "nvs_partition_manager.hpp"
#include "esp_partition.h"
#include "sdkconfig.h"
#include "nvs_handle_simple.hpp"
#ifdef CONFIG_NVS_ENCRYPTION
#include "nvs_encr.hpp"
#endif
@ -34,6 +36,26 @@ static const char* TAG = "nvs";
#define ESP_LOGD(...)
#endif
class NVSHandleEntry : public intrusive_list_node<NVSHandleEntry> {
public:
NVSHandleEntry(nvs::NVSHandleSimple *handle, const char* part_name)
: nvs_handle(handle),
mHandle(++s_nvs_next_handle),
handle_part_name(part_name) { }
~NVSHandleEntry() {
delete nvs_handle;
}
nvs::NVSHandleSimple *nvs_handle;
nvs_handle_t mHandle;
const char* handle_part_name;
private:
static uint32_t s_nvs_next_handle;
};
uint32_t NVSHandleEntry::s_nvs_next_handle;
extern "C" void nvs_dump(const char *partName);
extern "C" esp_err_t nvs_flash_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount);
@ -41,26 +63,6 @@ extern "C" esp_err_t nvs_flash_init_custom(const char *partName, uint32_t baseSe
extern "C" esp_err_t nvs_flash_secure_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount, nvs_sec_cfg_t* cfg);
#endif
class HandleEntry : public intrusive_list_node<HandleEntry>
{
static uint32_t s_nvs_next_handle;
public:
HandleEntry() {}
HandleEntry(bool readOnly, uint8_t nsIndex, nvs::Storage* StoragePtr) :
mHandle(++s_nvs_next_handle), // Begin the handle value with 1
mReadOnly(readOnly),
mNsIndex(nsIndex),
mStoragePtr(StoragePtr)
{
}
nvs_handle_t mHandle;
uint8_t mReadOnly;
uint8_t mNsIndex;
nvs::Storage* mStoragePtr;
};
#ifdef ESP_PLATFORM
SemaphoreHandle_t nvs::Lock::mSemaphore = NULL;
#endif
@ -68,20 +70,11 @@ SemaphoreHandle_t nvs::Lock::mSemaphore = NULL;
using namespace std;
using namespace nvs;
static intrusive_list<HandleEntry> s_nvs_handles;
uint32_t HandleEntry::s_nvs_next_handle;
static intrusive_list<nvs::Storage> s_nvs_storage_list;
static intrusive_list<NVSHandleEntry> s_nvs_handles;
static nvs::Storage* lookup_storage_from_name(const char *name)
{
auto it = find_if(begin(s_nvs_storage_list), end(s_nvs_storage_list), [=](Storage& e) -> bool {
return (strcmp(e.getPartName(), name) == 0);
});
if (it == end(s_nvs_storage_list)) {
return NULL;
}
return it;
return NVSPartitionManager::get_instance()->lookup_storage_from_name(name);
}
extern "C" void nvs_dump(const char *partName)
@ -102,24 +95,7 @@ extern "C" esp_err_t nvs_flash_init_custom(const char *partName, uint32_t baseSe
{
ESP_LOGD(TAG, "nvs_flash_init_custom partition=%s start=%d count=%d", partName, baseSector, sectorCount);
if (strlen(partName) > NVS_PART_NAME_MAX_SIZE) return ESP_ERR_INVALID_ARG;
nvs::Storage* new_storage = NULL;
nvs::Storage* storage = lookup_storage_from_name(partName);
if (storage == NULL) {
new_storage = new nvs::Storage((const char *)partName);
storage = new_storage;
}
esp_err_t err = storage->init(baseSector, sectorCount);
if (new_storage != NULL) {
if (err == ESP_OK) {
s_nvs_storage_list.push_back(new_storage);
} else {
delete new_storage;
}
}
return err;
return nvs::NVSPartitionManager::get_instance()->init_custom(partName, baseSector, sectorCount);
}
#ifdef CONFIG_NVS_ENCRYPTION
@ -145,21 +121,8 @@ extern "C" esp_err_t nvs_flash_init_partition(const char *part_name)
{
Lock::init();
Lock lock;
nvs::Storage* mStorage;
mStorage = lookup_storage_from_name(part_name);
if (mStorage) {
return ESP_OK;
}
const esp_partition_t* partition = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, part_name);
if (partition == NULL) {
return ESP_ERR_NOT_FOUND;
}
return nvs_flash_init_custom(part_name, partition->address / SPI_FLASH_SEC_SIZE,
partition->size / SPI_FLASH_SEC_SIZE);
return nvs::NVSPartitionManager::get_instance()->init_partition(part_name);
}
extern "C" esp_err_t nvs_flash_init(void)
@ -217,37 +180,11 @@ extern "C" esp_err_t nvs_flash_deinit_partition(const char* partition_name)
Lock::init();
Lock lock;
nvs::Storage* storage = lookup_storage_from_name(partition_name);
if (!storage) {
return ESP_ERR_NVS_NOT_INITIALIZED;
}
// Delete all corresponding open handles
s_nvs_handles.clearAndFreeNodes();
#ifdef CONFIG_NVS_ENCRYPTION
if(EncrMgr::isEncrActive()) {
auto encrMgr = EncrMgr::getInstance();
encrMgr->removeSecurityContext(storage->getBaseSector());
}
#endif
/* Clean up handles related to the storage being deinitialized */
auto it = s_nvs_handles.begin();
auto next = it;
while(it != s_nvs_handles.end()) {
next++;
if (it->mStoragePtr == storage) {
ESP_LOGD(TAG, "Deleting handle %d (ns=%d) related to partition \"%s\" (missing call to nvs_close?)",
it->mHandle, it->mNsIndex, partition_name);
s_nvs_handles.erase(it);
delete static_cast<HandleEntry*>(it);
}
it = next;
}
/* Finally delete the storage itself */
s_nvs_storage_list.erase(storage);
delete storage;
return ESP_OK;
// Deinit partition
return nvs::NVSPartitionManager::get_instance()->deinit_partition(partition_name);
}
extern "C" esp_err_t nvs_flash_deinit(void)
@ -255,15 +192,15 @@ extern "C" esp_err_t nvs_flash_deinit(void)
return nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME);
}
static esp_err_t nvs_find_ns_handle(nvs_handle_t handle, HandleEntry& entry)
static esp_err_t nvs_find_ns_handle(nvs_handle_t c_handle, NVSHandleSimple** handle)
{
auto it = find_if(begin(s_nvs_handles), end(s_nvs_handles), [=](HandleEntry& e) -> bool {
return e.mHandle == handle;
auto it = find_if(begin(s_nvs_handles), end(s_nvs_handles), [=](NVSHandleEntry& e) -> bool {
return e.mHandle == c_handle;
});
if (it == end(s_nvs_handles)) {
return ESP_ERR_NVS_INVALID_HANDLE;
}
entry = *it;
*handle = it->nvs_handle;
return ESP_OK;
}
@ -271,33 +208,25 @@ extern "C" esp_err_t nvs_open_from_partition(const char *part_name, const char*
{
Lock lock;
ESP_LOGD(TAG, "%s %s %d", __func__, name, open_mode);
uint8_t nsIndex;
nvs::Storage* sHandle;
sHandle = lookup_storage_from_name(part_name);
if (sHandle == NULL) {
return ESP_ERR_NVS_PART_NOT_FOUND;
NVSHandleSimple *handle;
esp_err_t result = nvs::NVSPartitionManager::get_instance()->open_handle(part_name, name, open_mode, &handle);
if (result == ESP_OK) {
NVSHandleEntry *entry = new NVSHandleEntry(handle, part_name);
if (entry) {
s_nvs_handles.push_back(entry);
*out_handle = entry->mHandle;
} else {
delete handle;
return ESP_ERR_NO_MEM;
}
}
esp_err_t err = sHandle->createOrOpenNamespace(name, open_mode == NVS_READWRITE, nsIndex);
if (err != ESP_OK) {
return err;
}
HandleEntry *handle_entry = new HandleEntry(open_mode==NVS_READONLY, nsIndex, sHandle);
s_nvs_handles.push_back(handle_entry);
*out_handle = handle_entry->mHandle;
return ESP_OK;
return result;
}
extern "C" esp_err_t nvs_open(const char* name, nvs_open_mode_t open_mode, nvs_handle_t *out_handle)
{
if (s_nvs_storage_list.size() == 0) {
return ESP_ERR_NVS_NOT_INITIALIZED;
}
return nvs_open_from_partition(NVS_DEFAULT_PART_NAME, name, open_mode, out_handle);
}
@ -305,60 +234,54 @@ extern "C" void nvs_close(nvs_handle_t handle)
{
Lock lock;
ESP_LOGD(TAG, "%s %d", __func__, handle);
auto it = find_if(begin(s_nvs_handles), end(s_nvs_handles), [=](HandleEntry& e) -> bool {
auto it = find_if(begin(s_nvs_handles), end(s_nvs_handles), [=](NVSHandleEntry& e) -> bool {
return e.mHandle == handle;
});
if (it == end(s_nvs_handles)) {
return;
}
s_nvs_handles.erase(it);
delete static_cast<HandleEntry*>(it);
delete static_cast<NVSHandleEntry*>(it);
}
extern "C" esp_err_t nvs_erase_key(nvs_handle_t handle, const char* key)
extern "C" esp_err_t nvs_erase_key(nvs_handle_t c_handle, const char* key)
{
Lock lock;
ESP_LOGD(TAG, "%s %s\r\n", __func__, key);
HandleEntry entry;
auto err = nvs_find_ns_handle(handle, entry);
NVSHandleSimple *handle;
auto err = nvs_find_ns_handle(c_handle, &handle);
if (err != ESP_OK) {
return err;
}
if (entry.mReadOnly) {
return ESP_ERR_NVS_READ_ONLY;
}
return entry.mStoragePtr->eraseItem(entry.mNsIndex, key);
return handle->erase_item(key);
}
extern "C" esp_err_t nvs_erase_all(nvs_handle_t handle)
extern "C" esp_err_t nvs_erase_all(nvs_handle_t c_handle)
{
Lock lock;
ESP_LOGD(TAG, "%s\r\n", __func__);
HandleEntry entry;
auto err = nvs_find_ns_handle(handle, entry);
NVSHandleSimple *handle;
auto err = nvs_find_ns_handle(c_handle, &handle);
if (err != ESP_OK) {
return err;
}
if (entry.mReadOnly) {
return ESP_ERR_NVS_READ_ONLY;
}
return entry.mStoragePtr->eraseNamespace(entry.mNsIndex);
return handle->erase_all();
}
template<typename T>
static esp_err_t nvs_set(nvs_handle_t handle, const char* key, T value)
static esp_err_t nvs_set(nvs_handle_t c_handle, const char* key, T value)
{
Lock lock;
ESP_LOGD(TAG, "%s %s %d %d", __func__, key, sizeof(T), (uint32_t) value);
HandleEntry entry;
auto err = nvs_find_ns_handle(handle, entry);
NVSHandleSimple *handle;
auto err = nvs_find_ns_handle(c_handle, &handle);
if (err != ESP_OK) {
return err;
}
if (entry.mReadOnly) {
return ESP_ERR_NVS_READ_ONLY;
}
return entry.mStoragePtr->writeItem(entry.mNsIndex, key, value);
return handle->set_item(key, value);
}
extern "C" esp_err_t nvs_set_i8 (nvs_handle_t handle, const char* key, int8_t value)
@ -401,110 +324,108 @@ extern "C" esp_err_t nvs_set_u64 (nvs_handle_t handle, const char* key, uint64_t
return nvs_set(handle, key, value);
}
extern "C" esp_err_t nvs_commit(nvs_handle_t handle)
extern "C" esp_err_t nvs_commit(nvs_handle_t c_handle)
{
Lock lock;
// no-op for now, to be used when intermediate cache is added
HandleEntry entry;
return nvs_find_ns_handle(handle, entry);
NVSHandleSimple *handle;
auto err = nvs_find_ns_handle(c_handle, &handle);
if (err != ESP_OK) {
return err;
}
return handle->commit();
}
extern "C" esp_err_t nvs_set_str(nvs_handle_t handle, const char* key, const char* value)
extern "C" esp_err_t nvs_set_str(nvs_handle_t c_handle, const char* key, const char* value)
{
Lock lock;
ESP_LOGD(TAG, "%s %s %s", __func__, key, value);
HandleEntry entry;
auto err = nvs_find_ns_handle(handle, entry);
NVSHandleSimple *handle;
auto err = nvs_find_ns_handle(c_handle, &handle);
if (err != ESP_OK) {
return err;
}
if (entry.mReadOnly) {
return ESP_ERR_NVS_READ_ONLY;
}
return entry.mStoragePtr->writeItem(entry.mNsIndex, nvs::ItemType::SZ, key, value, strlen(value) + 1);
return handle->set_string(key, value);
}
extern "C" esp_err_t nvs_set_blob(nvs_handle_t handle, const char* key, const void* value, size_t length)
extern "C" esp_err_t nvs_set_blob(nvs_handle_t c_handle, const char* key, const void* value, size_t length)
{
Lock lock;
ESP_LOGD(TAG, "%s %s %d", __func__, key, length);
HandleEntry entry;
auto err = nvs_find_ns_handle(handle, entry);
NVSHandleSimple *handle;
auto err = nvs_find_ns_handle(c_handle, &handle);
if (err != ESP_OK) {
return err;
}
if (entry.mReadOnly) {
return ESP_ERR_NVS_READ_ONLY;
}
return entry.mStoragePtr->writeItem(entry.mNsIndex, nvs::ItemType::BLOB, key, value, length);
return handle->set_blob(key, value, length);
}
template<typename T>
static esp_err_t nvs_get(nvs_handle_t handle, const char* key, T* out_value)
static esp_err_t nvs_get(nvs_handle_t c_handle, const char* key, T* out_value)
{
Lock lock;
ESP_LOGD(TAG, "%s %s %d", __func__, key, sizeof(T));
HandleEntry entry;
auto err = nvs_find_ns_handle(handle, entry);
NVSHandleSimple *handle;
auto err = nvs_find_ns_handle(c_handle, &handle);
if (err != ESP_OK) {
return err;
}
return entry.mStoragePtr->readItem(entry.mNsIndex, key, *out_value);
return handle->get_item(key, *out_value);
}
extern "C" esp_err_t nvs_get_i8 (nvs_handle_t handle, const char* key, int8_t* out_value)
extern "C" esp_err_t nvs_get_i8 (nvs_handle_t c_handle, const char* key, int8_t* out_value)
{
return nvs_get(handle, key, out_value);
return nvs_get(c_handle, key, out_value);
}
extern "C" esp_err_t nvs_get_u8 (nvs_handle_t handle, const char* key, uint8_t* out_value)
extern "C" esp_err_t nvs_get_u8 (nvs_handle_t c_handle, const char* key, uint8_t* out_value)
{
return nvs_get(handle, key, out_value);
return nvs_get(c_handle, key, out_value);
}
extern "C" esp_err_t nvs_get_i16 (nvs_handle_t handle, const char* key, int16_t* out_value)
extern "C" esp_err_t nvs_get_i16 (nvs_handle_t c_handle, const char* key, int16_t* out_value)
{
return nvs_get(handle, key, out_value);
return nvs_get(c_handle, key, out_value);
}
extern "C" esp_err_t nvs_get_u16 (nvs_handle_t handle, const char* key, uint16_t* out_value)
extern "C" esp_err_t nvs_get_u16 (nvs_handle_t c_handle, const char* key, uint16_t* out_value)
{
return nvs_get(handle, key, out_value);
return nvs_get(c_handle, key, out_value);
}
extern "C" esp_err_t nvs_get_i32 (nvs_handle_t handle, const char* key, int32_t* out_value)
extern "C" esp_err_t nvs_get_i32 (nvs_handle_t c_handle, const char* key, int32_t* out_value)
{
return nvs_get(handle, key, out_value);
return nvs_get(c_handle, key, out_value);
}
extern "C" esp_err_t nvs_get_u32 (nvs_handle_t handle, const char* key, uint32_t* out_value)
extern "C" esp_err_t nvs_get_u32 (nvs_handle_t c_handle, const char* key, uint32_t* out_value)
{
return nvs_get(handle, key, out_value);
return nvs_get(c_handle, key, out_value);
}
extern "C" esp_err_t nvs_get_i64 (nvs_handle_t handle, const char* key, int64_t* out_value)
extern "C" esp_err_t nvs_get_i64 (nvs_handle_t c_handle, const char* key, int64_t* out_value)
{
return nvs_get(handle, key, out_value);
return nvs_get(c_handle, key, out_value);
}
extern "C" esp_err_t nvs_get_u64 (nvs_handle_t handle, const char* key, uint64_t* out_value)
extern "C" esp_err_t nvs_get_u64 (nvs_handle_t c_handle, const char* key, uint64_t* out_value)
{
return nvs_get(handle, key, out_value);
return nvs_get(c_handle, key, out_value);
}
static esp_err_t nvs_get_str_or_blob(nvs_handle_t handle, nvs::ItemType type, const char* key, void* out_value, size_t* length)
static esp_err_t nvs_get_str_or_blob(nvs_handle_t c_handle, nvs::ItemType type, const char* key, void* out_value, size_t* length)
{
Lock lock;
ESP_LOGD(TAG, "%s %s", __func__, key);
HandleEntry entry;
auto err = nvs_find_ns_handle(handle, entry);
NVSHandleSimple *handle;
auto err = nvs_find_ns_handle(c_handle, &handle);
if (err != ESP_OK) {
return err;
}
size_t dataSize;
err = entry.mStoragePtr->getItemDataSize(entry.mNsIndex, type, key, dataSize);
err = handle->get_item_size(type, key, dataSize);
if (err != ESP_OK) {
return err;
}
@ -520,17 +441,17 @@ static esp_err_t nvs_get_str_or_blob(nvs_handle_t handle, nvs::ItemType type, co
}
*length = dataSize;
return entry.mStoragePtr->readItem(entry.mNsIndex, type, key, out_value, dataSize);
return handle->get_typed_item(type, key, out_value, dataSize);
}
extern "C" esp_err_t nvs_get_str(nvs_handle_t handle, const char* key, char* out_value, size_t* length)
extern "C" esp_err_t nvs_get_str(nvs_handle_t c_handle, const char* key, char* out_value, size_t* length)
{
return nvs_get_str_or_blob(handle, nvs::ItemType::SZ, key, out_value, length);
return nvs_get_str_or_blob(c_handle, nvs::ItemType::SZ, key, out_value, length);
}
extern "C" esp_err_t nvs_get_blob(nvs_handle_t handle, const char* key, void* out_value, size_t* length)
extern "C" esp_err_t nvs_get_blob(nvs_handle_t c_handle, const char* key, void* out_value, size_t* length)
{
return nvs_get_str_or_blob(handle, nvs::ItemType::BLOB, key, out_value, length);
return nvs_get_str_or_blob(c_handle, nvs::ItemType::BLOB, key, out_value, length);
}
extern "C" esp_err_t nvs_get_stats(const char* part_name, nvs_stats_t* nvs_stats)
@ -558,7 +479,7 @@ extern "C" esp_err_t nvs_get_stats(const char* part_name, nvs_stats_t* nvs_stats
return pStorage->fillStats(*nvs_stats);
}
extern "C" esp_err_t nvs_get_used_entry_count(nvs_handle_t handle, size_t* used_entries)
extern "C" esp_err_t nvs_get_used_entry_count(nvs_handle_t c_handle, size_t* used_entries)
{
Lock lock;
if(used_entries == NULL){
@ -566,14 +487,14 @@ extern "C" esp_err_t nvs_get_used_entry_count(nvs_handle_t handle, size_t* used_
}
*used_entries = 0;
HandleEntry entry;
auto err = nvs_find_ns_handle(handle, entry);
NVSHandleSimple *handle;
auto err = nvs_find_ns_handle(c_handle, &handle);
if (err != ESP_OK) {
return err;
}
size_t used_entry_count;
err = entry.mStoragePtr->calcEntriesInNamespace(entry.mNsIndex, used_entry_count);
err = handle->get_used_entry_count(used_entry_count);
if(err == ESP_OK){
*used_entries = used_entry_count;
}

View File

@ -0,0 +1,68 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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 "nvs_partition_manager.hpp"
#include "nvs_handle.hpp"
#include "nvs_handle_simple.hpp"
#include "nvs_handle_locked.hpp"
#include "nvs_platform.hpp"
namespace nvs {
std::unique_ptr<NVSHandle> open_nvs_handle_from_partition(const char *partition_name,
const char *ns_name,
nvs_open_mode_t open_mode,
esp_err_t *err)
{
if (partition_name == nullptr || ns_name == nullptr) {
if (err) {
*err = ESP_ERR_INVALID_ARG;
}
return nullptr;
}
Lock lock;
NVSHandleSimple *handle_simple;
esp_err_t result = nvs::NVSPartitionManager::get_instance()->
open_handle(partition_name, ns_name, open_mode, &handle_simple);
if (err) {
*err = result;
}
if (result != ESP_OK) {
return nullptr;
}
NVSHandleLocked *locked_handle = new (nothrow) NVSHandleLocked(handle_simple);
if (!locked_handle) {
if (err) {
*err = ESP_ERR_NO_MEM;
}
delete handle_simple;
return nullptr;
}
return std::unique_ptr<NVSHandleLocked>(locked_handle);
}
std::unique_ptr<NVSHandle> open_nvs_handle(const char *ns_name,
nvs_open_mode_t open_mode,
esp_err_t *err)
{
return open_nvs_handle_from_partition(NVS_DEFAULT_PART_NAME, ns_name, open_mode, err);
}
} // namespace nvs

View File

@ -0,0 +1,83 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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 "nvs_handle_locked.hpp"
namespace nvs {
NVSHandleLocked::NVSHandleLocked(NVSHandleSimple *handle) : handle(handle) {
Lock::init();
}
NVSHandleLocked::~NVSHandleLocked() {
Lock lock;
delete handle;
}
esp_err_t NVSHandleLocked::set_string(const char *key, const char* str) {
Lock lock;
return handle->set_string(key, str);
}
esp_err_t NVSHandleLocked::set_blob(const char *key, const void* blob, size_t len) {
Lock lock;
return handle->set_blob(key, blob, len);
}
esp_err_t NVSHandleLocked::get_string(const char *key, char* out_str, size_t len) {
Lock lock;
return handle->get_string(key, out_str, len);
}
esp_err_t NVSHandleLocked::get_blob(const char *key, void* out_blob, size_t len) {
Lock lock;
return handle->get_blob(key, out_blob, len);
}
esp_err_t NVSHandleLocked::get_item_size(ItemType datatype, const char *key, size_t &size) {
Lock lock;
return handle->get_item_size(datatype, key, size);
}
esp_err_t NVSHandleLocked::erase_item(const char* key) {
Lock lock;
return handle->erase_item(key);
}
esp_err_t NVSHandleLocked::erase_all() {
Lock lock;
return handle->erase_all();
}
esp_err_t NVSHandleLocked::commit() {
Lock lock;
return handle->commit();
}
esp_err_t NVSHandleLocked::get_used_entry_count(size_t& usedEntries) {
Lock lock;
return handle->get_used_entry_count(usedEntries);
}
esp_err_t NVSHandleLocked::set_typed_item(ItemType datatype, const char *key, const void* data, size_t dataSize) {
Lock lock;
return handle->set_typed_item(datatype, key, data, dataSize);
}
esp_err_t NVSHandleLocked::get_typed_item(ItemType datatype, const char *key, void* data, size_t dataSize) {
Lock lock;
return handle->get_typed_item(datatype, key, data, dataSize);
}
} // namespace nvs

View File

@ -0,0 +1,67 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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.
#ifndef NVS_HANDLE_LOCKED_HPP_
#define NVS_HANDLE_LOCKED_HPP_
#include "nvs_handle_simple.hpp"
namespace nvs {
/**
* @brief A class which behaves the same as NVSHandleSimple, except that all public member functions are locked.
*
* This class follows the decorator design pattern. The reason why we don't want locks in NVSHandleSimple is that
* NVSHandleSimple can also be used by the C-API which locks its public functions already.
* Thus, we avoid double-locking.
*
* @note this class becomes responsible for its internal NVSHandleSimple object, i.e. it deletes the handle object on
* destruction
*/
class NVSHandleLocked : public NVSHandle {
public:
NVSHandleLocked(NVSHandleSimple *handle);
virtual ~NVSHandleLocked();
esp_err_t set_string(const char *key, const char* str) override;
esp_err_t set_blob(const char *key, const void* blob, size_t len) override;
esp_err_t get_string(const char *key, char* out_str, size_t len) override;
esp_err_t get_blob(const char *key, void* out_blob, size_t len) override;
esp_err_t get_item_size(ItemType datatype, const char *key, size_t &size) override;
esp_err_t erase_item(const char* key) override;
esp_err_t erase_all() override;
esp_err_t commit() override;
esp_err_t get_used_entry_count(size_t& usedEntries) override;
protected:
esp_err_t set_typed_item(ItemType datatype, const char *key, const void* data, size_t dataSize) override;
esp_err_t get_typed_item(ItemType datatype, const char *key, void* data, size_t dataSize) override;
private:
NVSHandleSimple *handle;
};
} // namespace nvs
#endif // NVS_HANDLE_LOCKED_HPP_

View File

@ -0,0 +1,133 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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 <cstdlib>
#include "nvs_handle.hpp"
#include "nvs_partition_manager.hpp"
namespace nvs {
NVSHandleSimple::~NVSHandleSimple() {
NVSPartitionManager::get_instance()->close_handle(this);
}
esp_err_t NVSHandleSimple::set_typed_item(ItemType datatype, const char *key, const void* data, size_t dataSize)
{
if (!valid) return ESP_ERR_NVS_INVALID_HANDLE;
if (mReadOnly) return ESP_ERR_NVS_READ_ONLY;
return mStoragePtr->writeItem(mNsIndex, datatype, key, data, dataSize);
}
esp_err_t NVSHandleSimple::get_typed_item(ItemType datatype, const char *key, void* data, size_t dataSize)
{
if (!valid) return ESP_ERR_NVS_INVALID_HANDLE;
return mStoragePtr->readItem(mNsIndex, datatype, key, data, dataSize);
}
esp_err_t NVSHandleSimple::set_string(const char *key, const char* str)
{
if (!valid) return ESP_ERR_NVS_INVALID_HANDLE;
if (mReadOnly) return ESP_ERR_NVS_READ_ONLY;
return mStoragePtr->writeItem(mNsIndex, nvs::ItemType::SZ, key, str, strlen(str) + 1);
}
esp_err_t NVSHandleSimple::set_blob(const char *key, const void* blob, size_t len)
{
if (!valid) return ESP_ERR_NVS_INVALID_HANDLE;
if (mReadOnly) return ESP_ERR_NVS_READ_ONLY;
return mStoragePtr->writeItem(mNsIndex, nvs::ItemType::BLOB, key, blob, len);
}
esp_err_t NVSHandleSimple::get_string(const char *key, char* out_str, size_t len)
{
if (!valid) return ESP_ERR_NVS_INVALID_HANDLE;
return ESP_FAIL;
}
esp_err_t NVSHandleSimple::get_blob(const char *key, void* out_blob, size_t len)
{
if (!valid) return ESP_ERR_NVS_INVALID_HANDLE;
return ESP_FAIL;
}
esp_err_t NVSHandleSimple::get_item_size(ItemType datatype, const char *key, size_t &size)
{
if (!valid) return ESP_ERR_NVS_INVALID_HANDLE;
return mStoragePtr->getItemDataSize(mNsIndex, datatype, key, size);
}
esp_err_t NVSHandleSimple::erase_item(const char* key)
{
if (!valid) return ESP_ERR_NVS_INVALID_HANDLE;
if (mReadOnly) return ESP_ERR_NVS_READ_ONLY;
return mStoragePtr->eraseItem(mNsIndex, key);
}
esp_err_t NVSHandleSimple::erase_all()
{
if (!valid) return ESP_ERR_NVS_INVALID_HANDLE;
if (mReadOnly) return ESP_ERR_NVS_READ_ONLY;
return mStoragePtr->eraseNamespace(mNsIndex);
}
esp_err_t NVSHandleSimple::commit()
{
if (!valid) return ESP_ERR_NVS_INVALID_HANDLE;
return ESP_OK;
}
esp_err_t NVSHandleSimple::get_used_entry_count(size_t& used_entries)
{
used_entries = 0;
if (!valid) return ESP_ERR_NVS_INVALID_HANDLE;
size_t used_entry_count;
esp_err_t err = mStoragePtr->calcEntriesInNamespace(mNsIndex, used_entry_count);
if(err == ESP_OK){
used_entries = used_entry_count;
}
return err;
}
void NVSHandleSimple::debugDump() {
return mStoragePtr->debugDump();
}
esp_err_t NVSHandleSimple::fillStats(nvs_stats_t& nvsStats) {
return mStoragePtr->fillStats(nvsStats);
}
esp_err_t NVSHandleSimple::calcEntriesInNamespace(size_t& usedEntries) {
return mStoragePtr->calcEntriesInNamespace(mNsIndex, usedEntries);
}
bool NVSHandleSimple::findEntry(nvs_opaque_iterator_t* it, const char* name) {
return mStoragePtr->findEntry(it, name);
}
bool NVSHandleSimple::nextEntry(nvs_opaque_iterator_t* it) {
return mStoragePtr->nextEntry(it);
}
}

View File

@ -0,0 +1,106 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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.
#ifndef NVS_HANDLE_SIMPLE_HPP_
#define NVS_HANDLE_SIMPLE_HPP_
#include "intrusive_list.h"
#include "nvs_storage.hpp"
#include "nvs_platform.hpp"
#include "nvs_handle.hpp"
namespace nvs {
/**
* @brief This class implements NVSHandle according to the ESP32's flash and partitioning scheme.
*
* It is used by both the C API and the C++ API. The main responsibility is to check whether the handle is valid
* and in the right read/write mode and then forward the calls to the storage object.
*
* For more details about the general member functions, see nvs_handle.hpp.
*/
class NVSHandleSimple : public intrusive_list_node<NVSHandleSimple>, public NVSHandle {
friend class NVSPartitionManager;
public:
NVSHandleSimple(bool readOnly, uint8_t nsIndex, Storage *StoragePtr) :
mStoragePtr(StoragePtr),
mNsIndex(nsIndex),
mReadOnly(readOnly),
valid(1)
{ }
~NVSHandleSimple();
esp_err_t set_typed_item(ItemType datatype, const char *key, const void *data, size_t dataSize) override;
esp_err_t get_typed_item(ItemType datatype, const char *key, void *data, size_t dataSize) override;
esp_err_t set_string(const char *key, const char *str) override;
esp_err_t set_blob(const char *key, const void *blob, size_t len) override;
esp_err_t get_string(const char *key, char *out_str, size_t len) override;
esp_err_t get_blob(const char *key, void *out_blob, size_t len) override;
esp_err_t get_item_size(ItemType datatype, const char *key, size_t &size) override;
esp_err_t erase_item(const char *key) override;
esp_err_t erase_all() override;
esp_err_t commit() override;
esp_err_t get_used_entry_count(size_t &usedEntries) override;
esp_err_t getItemDataSize(ItemType datatype, const char *key, size_t &dataSize);
void debugDump();
esp_err_t fillStats(nvs_stats_t &nvsStats);
esp_err_t calcEntriesInNamespace(size_t &usedEntries);
bool findEntry(nvs_opaque_iterator_t *it, const char *name);
bool nextEntry(nvs_opaque_iterator_t *it);
private:
/**
* The underlying storage's object.
*/
Storage *mStoragePtr;
/**
* Numeric representation of the namespace as it is saved in flash (see README.rst for further details).
*/
uint8_t mNsIndex;
/**
* Whether this handle is marked as read-only or read-write.
* 0 indicates read-only, any other value read-write.
*/
uint8_t mReadOnly;
/**
* Indicates the validity of this handle.
* Upon opening, a handle is valid. It becomes invalid if the underlying storage is de-initialized.
*/
uint8_t valid;
};
} // nvs
#endif // NVS_HANDLE_SIMPLE_HPP_

View File

@ -0,0 +1,197 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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 "esp_partition.h"
#include "nvs_partition_manager.hpp"
namespace nvs {
NVSPartitionManager* NVSPartitionManager::instance = nullptr;
NVSPartitionManager* NVSPartitionManager::get_instance()
{
if (!instance) {
instance = new NVSPartitionManager();
}
return instance;
}
#ifdef ESP_PLATFORM
esp_err_t NVSPartitionManager::init_partition(const char *partition_label)
{
Storage* mStorage;
mStorage = lookup_storage_from_name(partition_label);
if (mStorage) {
return ESP_OK;
}
assert(SPI_FLASH_SEC_SIZE != 0);
const esp_partition_t* partition = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, partition_label);
if (partition == nullptr) {
return ESP_ERR_NOT_FOUND;
}
return init_custom(partition_label, partition->address / SPI_FLASH_SEC_SIZE,
partition->size / SPI_FLASH_SEC_SIZE);
}
#endif // ESP_PLATFORM
esp_err_t NVSPartitionManager::init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount)
{
if (strlen(partName) > NVS_PART_NAME_MAX_SIZE) return ESP_ERR_INVALID_ARG;
Storage* new_storage = NULL;
Storage* storage = lookup_storage_from_name(partName);
if (storage == NULL) {
new_storage = new Storage((const char *)partName);
storage = new_storage;
}
esp_err_t err = storage->init(baseSector, sectorCount);
if (new_storage != NULL) {
if (err == ESP_OK) {
nvs_storage_list.push_back(new_storage);
} else {
delete new_storage;
}
}
return err;
}
#ifdef ESP_PLATFORM
#ifdef CONFIG_NVS_ENCRYPTION
esp_err_t NVSPartitionManager::secure_init_partition(const char *part_name, nvs_sec_cfg_t* cfg)
{
Storage* mStorage;
mStorage = lookup_storage_from_name(part_name);
if (mStorage) {
return ESP_OK;
}
const esp_partition_t* partition = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, part_name);
if (partition == NULL) {
return ESP_ERR_NOT_FOUND;
}
return secure_init_custom(part_name, partition->address / SPI_FLASH_SEC_SIZE,
partition->size / SPI_FLASH_SEC_SIZE, cfg);
}
esp_err_t NVSPartitionManager::secure_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount, nvs_sec_cfg_t* cfg)
{
if(cfg) {
auto encrMgr = EncrMgr::getInstance();
auto err = encrMgr->setSecurityContext(baseSector, sectorCount, cfg);
if(err != ESP_OK) {
return err;
}
}
return init_custom(partName, baseSector, sectorCount);
}
#endif // CONFIG_NVS_ENCRYPTION
#endif // ESP_PLATFORM
esp_err_t NVSPartitionManager::deinit_partition(const char *partition_label)
{
Storage* storage = lookup_storage_from_name(partition_label);
if (!storage) {
return ESP_ERR_NVS_NOT_INITIALIZED;
}
#ifdef CONFIG_NVS_ENCRYPTION
if(EncrMgr::isEncrActive()) {
auto encrMgr = EncrMgr::getInstance();
encrMgr->removeSecurityContext(storage->getBaseSector());
}
#endif
/* Clean up handles related to the storage being deinitialized */
for (auto it = nvs_handles.begin(); it != nvs_handles.end(); ++it) {
if (it->mStoragePtr == storage) {
it->valid = false;
nvs_handles.erase(it);
}
}
/* Finally delete the storage itself */
nvs_storage_list.erase(storage);
delete storage;
return ESP_OK;
}
esp_err_t NVSPartitionManager::open_handle(const char *part_name,
const char *ns_name,
nvs_open_mode_t open_mode,
NVSHandleSimple** handle)
{
uint8_t nsIndex;
Storage* sHandle;
if (nvs_storage_list.size() == 0) {
return ESP_ERR_NVS_NOT_INITIALIZED;
}
sHandle = lookup_storage_from_name(part_name);
if (sHandle == NULL) {
return ESP_ERR_NVS_PART_NOT_FOUND;
}
esp_err_t err = sHandle->createOrOpenNamespace(ns_name, open_mode == NVS_READWRITE, nsIndex);
if (err != ESP_OK) {
return err;
}
*handle = new NVSHandleSimple(open_mode==NVS_READONLY, nsIndex, sHandle);
nvs_handles.push_back(*handle);
return ESP_OK;
}
esp_err_t NVSPartitionManager::close_handle(NVSHandleSimple* handle) {
for (auto it = nvs_handles.begin(); it != nvs_handles.end(); ++it) {
if (it == intrusive_list<NVSHandleSimple>::iterator(handle)) {
nvs_handles.erase(it);
return ESP_OK;
}
}
return ESP_ERR_NVS_INVALID_HANDLE;
}
size_t NVSPartitionManager::open_handles_size()
{
return nvs_handles.size();
}
Storage* NVSPartitionManager::lookup_storage_from_name(const char* name)
{
auto it = find_if(begin(nvs_storage_list), end(nvs_storage_list), [=](Storage& e) -> bool {
return (strcmp(e.getPartName(), name) == 0);
});
if (it == end(nvs_storage_list)) {
return NULL;
}
return it;
}
} // nvs

View File

@ -0,0 +1,65 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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.
#ifndef NVS_PARTITION_MANAGER_HPP_
#define NVS_PARTITION_MANAGER_HPP_
#include "nvs_handle_simple.hpp"
#include "nvs_storage.hpp"
#ifdef CONFIG_NVS_ENCRYPTION
#include "nvs_encr.hpp"
#endif
namespace nvs {
class NVSPartitionManager {
public:
virtual ~NVSPartitionManager() { }
static NVSPartitionManager* get_instance();
esp_err_t init_partition(const char *partition_label);
esp_err_t init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount);
#ifdef CONFIG_NVS_ENCRYPTION
esp_err_t secure_init_partition(const char *part_name, nvs_sec_cfg_t* cfg);
esp_err_t secure_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount, nvs_sec_cfg_t* cfg);
#endif
esp_err_t deinit_partition(const char *partition_label);
Storage* lookup_storage_from_name(const char* name);
esp_err_t open_handle(const char *part_name, const char *ns_name, nvs_open_mode_t open_mode, NVSHandleSimple** handle);
esp_err_t close_handle(NVSHandleSimple* handle);
size_t open_handles_size();
protected:
NVSPartitionManager() { }
static NVSPartitionManager* instance;
intrusive_list<NVSHandleSimple> nvs_handles;
intrusive_list<nvs::Storage> nvs_storage_list;
};
} // nvs
#endif // NVS_PARTITION_MANAGER_HPP_

View File

@ -15,11 +15,11 @@
#define nvs_types_h
#include <cstdint>
#include <type_traits>
#include <cstring>
#include <cassert>
#include <algorithm>
#include "nvs.h"
#include "nvs_handle.hpp"
#include "compressed_enum_table.hpp"
#include "string.h"
@ -28,40 +28,12 @@ using namespace std;
namespace nvs
{
enum class ItemType : uint8_t {
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 = NVS_TYPE_BLOB,
BLOB_IDX = 0x48,
ANY = NVS_TYPE_ANY
};
enum class VerOffset: uint8_t {
VER_0_OFFSET = 0x0,
VER_1_OFFSET = 0x80,
VER_ANY = 0xff,
};
template<typename T, typename std::enable_if<std::is_integral<T>::value, void*>::type = nullptr>
constexpr ItemType itemTypeOf()
{
return static_cast<ItemType>(((std::is_signed<T>::value)?0x10:0x00) | sizeof(T));
}
template<typename T>
constexpr ItemType itemTypeOf(const T&)
{
return itemTypeOf<T>();
}
inline bool isVariableLengthType(ItemType type)
{
return (type == ItemType::BLOB ||

View File

@ -18,6 +18,25 @@
static const char* TAG = "test_nvs";
// test could have different output on host tests
TEST_CASE("nvs deinit with open handle", "[nvs]")
{
nvs_handle_t handle_1;
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_LOGW(TAG, "nvs_flash_init failed (0x%x), erasing partition and retrying", err);
const esp_partition_t* nvs_partition = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL);
assert(nvs_partition && "partition table must have an NVS partition");
ESP_ERROR_CHECK( esp_partition_erase_range(nvs_partition, 0, nvs_partition->size) );
err = nvs_flash_init();
}
ESP_ERROR_CHECK( err );
TEST_ESP_OK(nvs_open("deinit_ns", NVS_READWRITE, &handle_1));
nvs_flash_deinit();
}
TEST_CASE("various nvs tests", "[nvs]")
{
nvs_handle_t handle_1;

View File

@ -12,12 +12,19 @@ SOURCE_FILES = \
nvs_item_hash_list.cpp \
nvs_encr.cpp \
nvs_ops.cpp \
nvs_handle_simple.cpp \
nvs_handle_locked.cpp \
nvs_partition_manager.cpp \
nvs_cxx_api.cpp \
) \
spi_flash_emulation.cpp \
test_compressed_enum_table.cpp \
test_spi_flash_emulation.cpp \
test_intrusive_list.cpp \
test_nvs.cpp \
test_partition_manager.cpp \
test_nvs_handle.cpp \
test_nvs_cxx_api.cpp \
crc.cpp \
main.cpp
@ -66,5 +73,11 @@ clean:
rm -f ../nvs_partition_generator/partition_encrypted.bin
rm -f ../nvs_partition_generator/partition_encrypted_using_keygen.bin
rm -f ../nvs_partition_generator/partition_encrypted_using_keyfile.bin
rm -f ../nvs_partition_generator/Test-1-partition-encrypted.bin
rm -f ../nvs_partition_generator/Test-1-partition.bin
rm -f ../../../tools/mass_mfg/samples/sample_values_multipage_blob_created.csv
rm -f ../../../tools/mass_mfg/samples/sample_values_singlepage_blob_created.csv
.PHONY: clean all test long-test

View File

@ -0,0 +1,22 @@
# Build
```bash
make -j 6
```
# Run
* Run particular test case:
```bash
./test_nvs "<particular test case>"
```
* Run all quick tests:
```bash
./test_nvs -d yes exclude:[long]
```
* Run all tests (takes several hours)
```bash
./test_nvs -d yes
```

View File

@ -0,0 +1,132 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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 "catch.hpp"
#include <algorithm>
#include <cstring>
#include "nvs_test_api.h"
#include "nvs_handle_simple.hpp"
#include "nvs_partition_manager.hpp"
#include "spi_flash_emulation.h"
#include <iostream>
using namespace std;
TEST_CASE("NVSHandleSimple CXX api open invalid arguments", "[nvs cxx]")
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
esp_err_t result;
shared_ptr<nvs::NVSHandle> handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->
init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK);
handle = nvs::open_nvs_handle_from_partition(nullptr, "ns_1", NVS_READWRITE, &result);
CHECK(result == ESP_ERR_INVALID_ARG);
CHECK(!handle);
handle = nvs::open_nvs_handle_from_partition("test", nullptr, NVS_READWRITE, &result);
CHECK(result == ESP_ERR_INVALID_ARG);
CHECK(!handle);
nvs::NVSPartitionManager::get_instance()->deinit_partition("test");
}
TEST_CASE("NVSHandleSimple CXX api open partition uninitialized", "[nvs cxx]")
{
SpiFlashEmulator emu(10);
esp_err_t result;
shared_ptr<nvs::NVSHandle> handle;
handle = nvs::open_nvs_handle_from_partition("test", "ns_1", NVS_READWRITE, &result);
bool result_expected = result == ESP_ERR_NVS_NOT_INITIALIZED || result == ESP_ERR_NVS_PART_NOT_FOUND;
CHECK(result_expected);
CHECK(!handle);
}
TEST_CASE("NVSHandleSimple CXX api open successful", "[nvs cxx]")
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
esp_err_t result;
shared_ptr<nvs::NVSHandle> handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
== ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
handle = nvs::open_nvs_handle_from_partition("test", "ns_1", NVS_READWRITE, &result);
CHECK(result == ESP_OK);
CHECK(handle);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 1);
handle.reset();
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
nvs::NVSPartitionManager::get_instance()->deinit_partition("test");
}
TEST_CASE("NVSHandleSimple CXX api open default part successful", "[nvs cxx]")
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
esp_err_t result;
shared_ptr<nvs::NVSHandle> handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom("nvs", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
== ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
handle = nvs::open_nvs_handle("ns_1", NVS_READWRITE, &result);
CHECK(result == ESP_OK);
CHECK(handle);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 1);
handle.reset();
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
nvs::NVSPartitionManager::get_instance()->deinit_partition("nvs");
}
TEST_CASE("NVSHandleSimple CXX api open default part ns NULL", "[nvs cxx]")
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
esp_err_t result;
shared_ptr<nvs::NVSHandle> handle;
REQUIRE(nvs::NVSPartitionManager::get_instance()->init_custom("nvs", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
== ESP_OK);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
handle = nvs::open_nvs_handle(nullptr, NVS_READWRITE, &result);
CHECK(result == ESP_ERR_INVALID_ARG);
CHECK(!handle);
CHECK(nvs::NVSPartitionManager::get_instance()->open_handles_size() == 0);
nvs::NVSPartitionManager::get_instance()->deinit_partition("nvs");
}

View File

@ -0,0 +1,116 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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 "catch.hpp"
#include <algorithm>
#include <cstring>
#include "nvs_test_api.h"
#include "nvs_handle_simple.hpp"
#include "nvs_partition_manager.hpp"
#include "spi_flash_emulation.h"
#include <iostream>
using namespace std;
using namespace nvs;
TEST_CASE("NVSHandleSimple closes its reference in PartitionManager", "[partition_mgr]")
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
REQUIRE(NVSPartitionManager::get_instance()->init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
== ESP_OK);
CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0);
NVSHandleSimple *handle;
REQUIRE(NVSPartitionManager::get_instance()->open_handle("test", "ns_1", NVS_READWRITE, &handle) == ESP_OK);
CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 1);
delete handle;
CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0);
REQUIRE(NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK);
}
TEST_CASE("NVSHandleSimple multiple open and closes with PartitionManager", "[partition_mgr]")
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
REQUIRE(NVSPartitionManager::get_instance()->init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
== ESP_OK);
CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0);
NVSHandleSimple *handle1;
NVSHandleSimple *handle2;
REQUIRE(NVSPartitionManager::get_instance()->open_handle("test", "ns_1", NVS_READWRITE, &handle1) == ESP_OK);
CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 1);
REQUIRE(NVSPartitionManager::get_instance()->open_handle("test", "ns_1", NVS_READWRITE, &handle2) == ESP_OK);
CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 2);
delete handle1;
CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 1);
delete handle2;
CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0);
REQUIRE(NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK);
}
TEST_CASE("nvshandle readonly fails", "[partition_mgr]")
{
SpiFlashEmulator emu(10);
NVSPartitionManager::get_instance()->deinit_partition(NVS_DEFAULT_PART_NAME);
NVSHandleSimple *handle_1;
NVSHandleSimple *handle_2;
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
CHECK(NVSPartitionManager::get_instance()->init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK);
CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0);
// first, creating namespace...
REQUIRE(NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READWRITE, &handle_1) == ESP_OK);
CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 1);
delete handle_1;
CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0);
REQUIRE(NVSPartitionManager::get_instance()->open_handle(NVS_DEFAULT_PART_NAME, "ns_1", NVS_READONLY, &handle_2) == ESP_OK);
CHECK(handle_2->set_item("key", 47) == ESP_ERR_NVS_READ_ONLY);
CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 1);
delete handle_2;
CHECK(NVSPartitionManager::get_instance()->open_handles_size() == 0);
// without deinit it affects "nvs api tests"
CHECK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME) == ESP_OK);
}

View File

@ -0,0 +1,85 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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 "catch.hpp"
#include <algorithm>
#include <cstring>
#include "nvs_test_api.h"
#include "nvs_handle_simple.hpp"
#include "nvs_partition_manager.hpp"
#include "spi_flash_emulation.h"
#include "nvs_test_api.h"
using namespace nvs;
TEST_CASE("Partition manager initializes storage", "[partition_mgr]")
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
REQUIRE(NVSPartitionManager::get_instance()->init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK);
CHECK(NVSPartitionManager::get_instance()->lookup_storage_from_name("test") != nullptr);
}
TEST_CASE("Partition manager de-initializes storage", "[partition_mgr]")
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
REQUIRE(NVSPartitionManager::get_instance()->init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK);
CHECK(NVSPartitionManager::get_instance()->lookup_storage_from_name("test") != nullptr);
CHECK(NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK);
CHECK(NVSPartitionManager::get_instance()->lookup_storage_from_name("test") == nullptr);
}
TEST_CASE("Partition manager initializes multiple partitions", "[partition_mgr]")
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
REQUIRE(NVSPartitionManager::get_instance()->init_custom("test1", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
== ESP_OK);
// TODO: why does this work, actually? same sectors used as above
REQUIRE(NVSPartitionManager::get_instance()->init_custom("test2", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
== ESP_OK);
Storage *storage1 = NVSPartitionManager::get_instance()->lookup_storage_from_name("test1");
REQUIRE(storage1 != nullptr);
Storage *storage2 = NVSPartitionManager::get_instance()->lookup_storage_from_name("test2");
REQUIRE(storage2 != nullptr);
CHECK(storage1 != storage2);
}
TEST_CASE("Partition manager invalidates handle on partition de-init", "[partition_mgr]")
{
const uint32_t NVS_FLASH_SECTOR = 6;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
SpiFlashEmulator emu(10);
REQUIRE(NVSPartitionManager::get_instance()->init_custom("test", NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)
== ESP_OK);
NVSHandleSimple *handle;
REQUIRE(NVSPartitionManager::get_instance()->open_handle("test", "ns_1", NVS_READWRITE, &handle) == ESP_OK);
CHECK(handle->erase_all() == ESP_OK);
REQUIRE(NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK);
CHECK(handle->erase_all() == ESP_ERR_NVS_INVALID_HANDLE);
delete handle;
}

View File

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(nvs-rw-value-cxx)

View File

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := nvs-rw-value-cxx
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,76 @@
# Non-Volatile Storage (NVS) C++ Read and Write Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example demonstrates how to read and write a single integer value using NVS.
It is essentially the same as the nvs_rw_value example. The only difference is that it uses the C++ NVS handle API.
Please see [nvs_rw_value README](../nvs_rw_value/README.md) for more details about this example.
## How to use example
### Hardware required
This example does not require any special hardware, and can be run on any common development board.
### Build and flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
First run:
```
Opening Non-Volatile Storage (NVS) handle... Done
Reading restart counter from NVS ... The value is not initialized yet!
Updating restart counter in NVS ... Done
Committing updates in NVS ... Done
Restarting in 10 seconds...
Restarting in 9 seconds...
Restarting in 8 seconds...
Restarting in 7 seconds...
Restarting in 6 seconds...
Restarting in 5 seconds...
Restarting in 4 seconds...
Restarting in 3 seconds...
Restarting in 2 seconds...
Restarting in 1 seconds...
Restarting in 0 seconds...
Restarting now.
```
Subsequent runs:
```
Opening Non-Volatile Storage (NVS) handle... Done
Reading restart counter from NVS ... Done
Restart counter = 1
Updating restart counter in NVS ... Done
Committing updates in NVS ... Done
Restarting in 10 seconds...
Restarting in 9 seconds...
Restarting in 8 seconds...
Restarting in 7 seconds...
Restarting in 6 seconds...
Restarting in 5 seconds...
Restarting in 4 seconds...
Restarting in 3 seconds...
Restarting in 2 seconds...
Restarting in 1 seconds...
Restarting in 0 seconds...
Restarting now.
```
Restart counter will increment on each run.
To reset the counter, erase the contents of flash memory using `idf.py erase_flash`, then upload the program again as described above.

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "nvs_value_example_main.cpp"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -0,0 +1,84 @@
/* Non-Volatile Storage (NVS) Read and Write a Value - Example
For other examples please check:
https://github.com/espressif/esp-idf/tree/master/examples
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "nvs_flash.h"
#include "nvs.h"
#include "nvs_handle.hpp"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
extern "C" void app_main(void)
{
// Initialize NVS
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
// NVS partition was truncated and needs to be erased
// Retry nvs_flash_init
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK( err );
// Open
printf("\n");
printf("Opening Non-Volatile Storage (NVS) handle... ");
esp_err_t result;
// Handle will automatically close when going out of scope or when it's reset.
std::shared_ptr<nvs::NVSHandle> handle = nvs::open_nvs_handle("storage", NVS_READWRITE, &result);
if (err != ESP_OK) {
printf("Error (%s) opening NVS handle!\n", esp_err_to_name(err));
} else {
printf("Done\n");
// Read
printf("Reading restart counter from NVS ... ");
int32_t restart_counter = 0; // value will default to 0, if not set yet in NVS
err = handle->get_item("restart_counter", restart_counter);
switch (err) {
case ESP_OK:
printf("Done\n");
printf("Restart counter = %d\n", restart_counter);
break;
case ESP_ERR_NVS_NOT_FOUND:
printf("The value is not initialized yet!\n");
break;
default :
printf("Error (%s) reading!\n", esp_err_to_name(err));
}
// Write
printf("Updating restart counter in NVS ... ");
restart_counter++;
err = handle->set_item("restart_counter", restart_counter);
printf((err != ESP_OK) ? "Failed!\n" : "Done\n");
// Commit written value.
// After setting any values, nvs_commit() must be called to ensure changes are written
// to flash storage. Implementations may write to storage at other times,
// but this is not guaranteed.
printf("Committing updates in NVS ... ");
err = handle->commit();
printf((err != ESP_OK) ? "Failed!\n" : "Done\n");
}
printf("\n");
// Restart module
for (int i = 10; i >= 0; i--) {
printf("Restarting in %d seconds...\n", i);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
printf("Restarting now.\n");
fflush(stdout);
esp_restart();
}