From d286876d34720652fa66852998c5f212d72b9d7b Mon Sep 17 00:00:00 2001 From: Jakob Hasse Date: Mon, 25 Nov 2019 11:32:40 +0800 Subject: [PATCH] NVS: expose C++ API Closes https://github.com/espressif/esp-idf/issues/3540 --- components/nvs_flash/CMakeLists.txt | 4 + components/nvs_flash/README.rst | 6 +- components/nvs_flash/README_CN.rst | 6 +- components/nvs_flash/include/nvs.h | 4 +- components/nvs_flash/include/nvs_handle.hpp | 262 +++++++++++++++ components/nvs_flash/src/nvs_api.cpp | 301 +++++++----------- components/nvs_flash/src/nvs_cxx_api.cpp | 68 ++++ .../nvs_flash/src/nvs_handle_locked.cpp | 83 +++++ .../nvs_flash/src/nvs_handle_locked.hpp | 67 ++++ .../nvs_flash/src/nvs_handle_simple.cpp | 133 ++++++++ .../nvs_flash/src/nvs_handle_simple.hpp | 106 ++++++ .../nvs_flash/src/nvs_partition_manager.cpp | 197 ++++++++++++ .../nvs_flash/src/nvs_partition_manager.hpp | 65 ++++ components/nvs_flash/src/nvs_types.hpp | 30 +- components/nvs_flash/test/test_nvs.c | 19 ++ components/nvs_flash/test_nvs_host/Makefile | 13 + components/nvs_flash/test_nvs_host/README.md | 22 ++ .../test_nvs_host/test_nvs_cxx_api.cpp | 132 ++++++++ .../test_nvs_host/test_nvs_handle.cpp | 116 +++++++ .../test_nvs_host/test_partition_manager.cpp | 85 +++++ .../storage/nvs_rw_value_cxx/CMakeLists.txt | 6 + examples/storage/nvs_rw_value_cxx/Makefile | 9 + examples/storage/nvs_rw_value_cxx/README.md | 76 +++++ .../nvs_rw_value_cxx/main/CMakeLists.txt | 2 + .../nvs_rw_value_cxx/main/component.mk | 5 + .../main/nvs_value_example_main.cpp | 84 +++++ 26 files changed, 1674 insertions(+), 227 deletions(-) create mode 100644 components/nvs_flash/include/nvs_handle.hpp create mode 100644 components/nvs_flash/src/nvs_cxx_api.cpp create mode 100644 components/nvs_flash/src/nvs_handle_locked.cpp create mode 100644 components/nvs_flash/src/nvs_handle_locked.hpp create mode 100644 components/nvs_flash/src/nvs_handle_simple.cpp create mode 100644 components/nvs_flash/src/nvs_handle_simple.hpp create mode 100644 components/nvs_flash/src/nvs_partition_manager.cpp create mode 100644 components/nvs_flash/src/nvs_partition_manager.hpp create mode 100644 components/nvs_flash/test_nvs_host/README.md create mode 100644 components/nvs_flash/test_nvs_host/test_nvs_cxx_api.cpp create mode 100644 components/nvs_flash/test_nvs_host/test_nvs_handle.cpp create mode 100644 components/nvs_flash/test_nvs_host/test_partition_manager.cpp create mode 100644 examples/storage/nvs_rw_value_cxx/CMakeLists.txt create mode 100644 examples/storage/nvs_rw_value_cxx/Makefile create mode 100644 examples/storage/nvs_rw_value_cxx/README.md create mode 100644 examples/storage/nvs_rw_value_cxx/main/CMakeLists.txt create mode 100644 examples/storage/nvs_rw_value_cxx/main/component.mk create mode 100644 examples/storage/nvs_rw_value_cxx/main/nvs_value_example_main.cpp diff --git a/components/nvs_flash/CMakeLists.txt b/components/nvs_flash/CMakeLists.txt index f305dbec3a..d450d8077a 100644 --- a/components/nvs_flash/CMakeLists.txt +++ b/components/nvs_flash/CMakeLists.txt @@ -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") diff --git a/components/nvs_flash/README.rst b/components/nvs_flash/README.rst index 6cbb56985d..7e7d63e2f7 100644 --- a/components/nvs_flash/README.rst +++ b/components/nvs_flash/README.rst @@ -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 diff --git a/components/nvs_flash/README_CN.rst b/components/nvs_flash/README_CN.rst index 4025b9b5f1..2b53a4256e 100644 --- a/components/nvs_flash/README_CN.rst +++ b/components/nvs_flash/README_CN.rst @@ -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 迭代器 diff --git a/components/nvs_flash/include/nvs.h b/components/nvs_flash/include/nvs.h index 89f9ce81f9..3e877a6520 100644 --- a/components/nvs_flash/include/nvs.h +++ b/components/nvs_flash/include/nvs.h @@ -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. */ diff --git a/components/nvs_flash/include/nvs_handle.hpp b/components/nvs_flash/include/nvs_handle.hpp new file mode 100644 index 0000000000..8699974a00 --- /dev/null +++ b/components/nvs_flash/include/nvs_handle.hpp @@ -0,0 +1,262 @@ +#ifndef NVS_HANDLE_HPP_ +#define NVS_HANDLE_HPP_ + +#include +#include +#include + +#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 + 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 + 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 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 open_nvs_handle(const char *ns_name, + nvs_open_mode_t open_mode, + esp_err_t *err = nullptr); + +// Helper functions for template usage +template::value, void*>::type = nullptr> +constexpr ItemType itemTypeOf() +{ + return static_cast(((std::is_signed::value)?0x10:0x00) | sizeof(T)); +} + +template +constexpr ItemType itemTypeOf(const T&) +{ + return itemTypeOf(); +} + +// Template Implementations +template +esp_err_t NVSHandle::set_item(const char *key, T value) { + return set_typed_item(itemTypeOf(value), key, &value, sizeof(value)); +} + +template +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_ + diff --git a/components/nvs_flash/src/nvs_api.cpp b/components/nvs_flash/src/nvs_api.cpp index 778bed2609..7df027a820 100644 --- a/components/nvs_flash/src/nvs_api.cpp +++ b/components/nvs_flash/src/nvs_api.cpp @@ -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 { +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 -{ - 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 s_nvs_handles; -uint32_t HandleEntry::s_nvs_next_handle; -static intrusive_list s_nvs_storage_list; +static intrusive_list 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(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(it); + delete static_cast(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 -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 -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; } diff --git a/components/nvs_flash/src/nvs_cxx_api.cpp b/components/nvs_flash/src/nvs_cxx_api.cpp new file mode 100644 index 0000000000..65fb1128b2 --- /dev/null +++ b/components/nvs_flash/src/nvs_cxx_api.cpp @@ -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 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(locked_handle); +} + +std::unique_ptr 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 diff --git a/components/nvs_flash/src/nvs_handle_locked.cpp b/components/nvs_flash/src/nvs_handle_locked.cpp new file mode 100644 index 0000000000..dc6ab10e39 --- /dev/null +++ b/components/nvs_flash/src/nvs_handle_locked.cpp @@ -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 + diff --git a/components/nvs_flash/src/nvs_handle_locked.hpp b/components/nvs_flash/src/nvs_handle_locked.hpp new file mode 100644 index 0000000000..036e675eb6 --- /dev/null +++ b/components/nvs_flash/src/nvs_handle_locked.hpp @@ -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_ + diff --git a/components/nvs_flash/src/nvs_handle_simple.cpp b/components/nvs_flash/src/nvs_handle_simple.cpp new file mode 100644 index 0000000000..5a7b9e86e8 --- /dev/null +++ b/components/nvs_flash/src/nvs_handle_simple.cpp @@ -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 +#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); +} + +} diff --git a/components/nvs_flash/src/nvs_handle_simple.hpp b/components/nvs_flash/src/nvs_handle_simple.hpp new file mode 100644 index 0000000000..e05aaf7386 --- /dev/null +++ b/components/nvs_flash/src/nvs_handle_simple.hpp @@ -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, 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_ + diff --git a/components/nvs_flash/src/nvs_partition_manager.cpp b/components/nvs_flash/src/nvs_partition_manager.cpp new file mode 100644 index 0000000000..6719dd0986 --- /dev/null +++ b/components/nvs_flash/src/nvs_partition_manager.cpp @@ -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::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 + diff --git a/components/nvs_flash/src/nvs_partition_manager.hpp b/components/nvs_flash/src/nvs_partition_manager.hpp new file mode 100644 index 0000000000..baa0c09d10 --- /dev/null +++ b/components/nvs_flash/src/nvs_partition_manager.hpp @@ -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 nvs_handles; + + intrusive_list nvs_storage_list; +}; + +} // nvs + +#endif // NVS_PARTITION_MANAGER_HPP_ + diff --git a/components/nvs_flash/src/nvs_types.hpp b/components/nvs_flash/src/nvs_types.hpp index 74f34b5b6c..32055cdb81 100644 --- a/components/nvs_flash/src/nvs_types.hpp +++ b/components/nvs_flash/src/nvs_types.hpp @@ -15,11 +15,11 @@ #define nvs_types_h #include -#include #include #include #include #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::value, void*>::type = nullptr> -constexpr ItemType itemTypeOf() -{ - return static_cast(((std::is_signed::value)?0x10:0x00) | sizeof(T)); -} - -template -constexpr ItemType itemTypeOf(const T&) -{ - return itemTypeOf(); -} - inline bool isVariableLengthType(ItemType type) { return (type == ItemType::BLOB || diff --git a/components/nvs_flash/test/test_nvs.c b/components/nvs_flash/test/test_nvs.c index 7eaafc2951..50a297246e 100644 --- a/components/nvs_flash/test/test_nvs.c +++ b/components/nvs_flash/test/test_nvs.c @@ -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; diff --git a/components/nvs_flash/test_nvs_host/Makefile b/components/nvs_flash/test_nvs_host/Makefile index 717e2c5104..20c2966e24 100644 --- a/components/nvs_flash/test_nvs_host/Makefile +++ b/components/nvs_flash/test_nvs_host/Makefile @@ -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 diff --git a/components/nvs_flash/test_nvs_host/README.md b/components/nvs_flash/test_nvs_host/README.md new file mode 100644 index 0000000000..bad5b08cd8 --- /dev/null +++ b/components/nvs_flash/test_nvs_host/README.md @@ -0,0 +1,22 @@ +# Build + +```bash +make -j 6 +``` + +# Run +* Run particular test case: +```bash +./test_nvs "" + +``` +* Run all quick tests: +```bash +./test_nvs -d yes exclude:[long] +``` + +* Run all tests (takes several hours) +```bash +./test_nvs -d yes +``` + diff --git a/components/nvs_flash/test_nvs_host/test_nvs_cxx_api.cpp b/components/nvs_flash/test_nvs_host/test_nvs_cxx_api.cpp new file mode 100644 index 0000000000..502078ad85 --- /dev/null +++ b/components/nvs_flash/test_nvs_host/test_nvs_cxx_api.cpp @@ -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 +#include +#include "nvs_test_api.h" +#include "nvs_handle_simple.hpp" +#include "nvs_partition_manager.hpp" +#include "spi_flash_emulation.h" + +#include + +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 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 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 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 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 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"); +} diff --git a/components/nvs_flash/test_nvs_host/test_nvs_handle.cpp b/components/nvs_flash/test_nvs_host/test_nvs_handle.cpp new file mode 100644 index 0000000000..bee7d823aa --- /dev/null +++ b/components/nvs_flash/test_nvs_host/test_nvs_handle.cpp @@ -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 +#include +#include "nvs_test_api.h" +#include "nvs_handle_simple.hpp" +#include "nvs_partition_manager.hpp" +#include "spi_flash_emulation.h" + +#include + +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); +} + diff --git a/components/nvs_flash/test_nvs_host/test_partition_manager.cpp b/components/nvs_flash/test_nvs_host/test_partition_manager.cpp new file mode 100644 index 0000000000..e6326b4036 --- /dev/null +++ b/components/nvs_flash/test_nvs_host/test_partition_manager.cpp @@ -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 +#include +#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; +} + diff --git a/examples/storage/nvs_rw_value_cxx/CMakeLists.txt b/examples/storage/nvs_rw_value_cxx/CMakeLists.txt new file mode 100644 index 0000000000..ae1b325f3a --- /dev/null +++ b/examples/storage/nvs_rw_value_cxx/CMakeLists.txt @@ -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) diff --git a/examples/storage/nvs_rw_value_cxx/Makefile b/examples/storage/nvs_rw_value_cxx/Makefile new file mode 100644 index 0000000000..9dafe84362 --- /dev/null +++ b/examples/storage/nvs_rw_value_cxx/Makefile @@ -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 + diff --git a/examples/storage/nvs_rw_value_cxx/README.md b/examples/storage/nvs_rw_value_cxx/README.md new file mode 100644 index 0000000000..2adce9b494 --- /dev/null +++ b/examples/storage/nvs_rw_value_cxx/README.md @@ -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. + diff --git a/examples/storage/nvs_rw_value_cxx/main/CMakeLists.txt b/examples/storage/nvs_rw_value_cxx/main/CMakeLists.txt new file mode 100644 index 0000000000..217cab6a7e --- /dev/null +++ b/examples/storage/nvs_rw_value_cxx/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "nvs_value_example_main.cpp" + INCLUDE_DIRS ".") diff --git a/examples/storage/nvs_rw_value_cxx/main/component.mk b/examples/storage/nvs_rw_value_cxx/main/component.mk new file mode 100644 index 0000000000..0b9d7585e7 --- /dev/null +++ b/examples/storage/nvs_rw_value_cxx/main/component.mk @@ -0,0 +1,5 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + diff --git a/examples/storage/nvs_rw_value_cxx/main/nvs_value_example_main.cpp b/examples/storage/nvs_rw_value_cxx/main/nvs_value_example_main.cpp new file mode 100644 index 0000000000..7fa0891c26 --- /dev/null +++ b/examples/storage/nvs_rw_value_cxx/main/nvs_value_example_main.cpp @@ -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 +#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 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(); +}