2016-08-17 11:08:22 -04:00
|
|
|
// 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_page_hpp
|
|
|
|
#define nvs_page_hpp
|
|
|
|
|
|
|
|
#include "nvs.h"
|
|
|
|
#include "nvs_types.hpp"
|
|
|
|
#include <cstdint>
|
|
|
|
#include <type_traits>
|
|
|
|
#include <cstring>
|
|
|
|
#include <algorithm>
|
|
|
|
#include "esp_spi_flash.h"
|
|
|
|
#include "compressed_enum_table.hpp"
|
|
|
|
#include "intrusive_list.h"
|
2016-09-20 10:40:12 -04:00
|
|
|
#include "nvs_item_hash_list.hpp"
|
2020-04-26 20:51:31 -04:00
|
|
|
#include "partition.hpp"
|
2016-08-17 11:08:22 -04:00
|
|
|
|
|
|
|
namespace nvs
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
class Page : public intrusive_list_node<Page>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
static const uint32_t PSB_INIT = 0x1;
|
|
|
|
static const uint32_t PSB_FULL = 0x2;
|
|
|
|
static const uint32_t PSB_FREEING = 0x4;
|
|
|
|
static const uint32_t PSB_CORRUPT = 0x8;
|
|
|
|
|
|
|
|
static const uint32_t ESB_WRITTEN = 0x1;
|
|
|
|
static const uint32_t ESB_ERASED = 0x2;
|
|
|
|
|
|
|
|
static const uint32_t SEC_SIZE = SPI_FLASH_SEC_SIZE;
|
|
|
|
|
|
|
|
static const size_t ENTRY_SIZE = 32;
|
|
|
|
static const size_t ENTRY_COUNT = 126;
|
|
|
|
static const uint32_t INVALID_ENTRY = 0xffffffff;
|
2018-04-02 06:44:59 -04:00
|
|
|
|
|
|
|
static const size_t CHUNK_MAX_SIZE = ENTRY_SIZE * (ENTRY_COUNT - 1);
|
2016-08-17 11:08:22 -04:00
|
|
|
|
|
|
|
static const uint8_t NS_INDEX = 0;
|
|
|
|
static const uint8_t NS_ANY = 255;
|
|
|
|
|
2018-04-02 06:44:59 -04:00
|
|
|
static const uint8_t CHUNK_ANY = Item::CHUNK_ANY;
|
|
|
|
|
2018-07-25 09:25:05 -04:00
|
|
|
static const uint8_t NVS_VERSION = 0xfe; // Decrement to upgrade
|
|
|
|
|
2016-08-17 11:08:22 -04:00
|
|
|
enum class PageState : uint32_t {
|
|
|
|
// All bits set, default state after flash erase. Page has not been initialized yet.
|
|
|
|
UNINITIALIZED = 0xffffffff,
|
|
|
|
|
|
|
|
// Page is initialized, and will accept writes.
|
|
|
|
ACTIVE = UNINITIALIZED & ~PSB_INIT,
|
|
|
|
|
|
|
|
// Page is marked as full and will not accept new writes.
|
|
|
|
FULL = ACTIVE & ~PSB_FULL,
|
|
|
|
|
|
|
|
// Data is being moved from this page to a new one.
|
|
|
|
FREEING = FULL & ~PSB_FREEING,
|
|
|
|
|
|
|
|
// Page was found to be in a corrupt and unrecoverable state.
|
|
|
|
// Instead of being erased immediately, it will be kept for diagnostics and data recovery.
|
|
|
|
// It will be erased once we run out out free pages.
|
|
|
|
CORRUPT = FREEING & ~PSB_CORRUPT,
|
|
|
|
|
|
|
|
// Page object wasn't loaded from flash memory
|
|
|
|
INVALID = 0
|
|
|
|
};
|
|
|
|
|
2020-04-26 20:51:31 -04:00
|
|
|
Page();
|
|
|
|
|
2016-08-17 11:08:22 -04:00
|
|
|
PageState state() const
|
|
|
|
{
|
|
|
|
return mState;
|
|
|
|
}
|
|
|
|
|
2020-04-26 20:51:31 -04:00
|
|
|
esp_err_t load(Partition *partition, uint32_t sectorNumber);
|
2016-08-17 11:08:22 -04:00
|
|
|
|
|
|
|
esp_err_t getSeqNumber(uint32_t& seqNumber) const;
|
|
|
|
|
|
|
|
esp_err_t setSeqNumber(uint32_t seqNumber);
|
2020-04-26 20:51:31 -04:00
|
|
|
|
2018-07-25 11:11:09 -04:00
|
|
|
esp_err_t setVersion(uint8_t version);
|
2016-08-17 11:08:22 -04:00
|
|
|
|
2018-04-02 06:44:59 -04:00
|
|
|
esp_err_t writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize, uint8_t chunkIdx = CHUNK_ANY);
|
2016-08-17 11:08:22 -04:00
|
|
|
|
2018-04-02 06:44:59 -04:00
|
|
|
esp_err_t readItem(uint8_t nsIndex, ItemType datatype, const char* key, void* data, size_t dataSize, uint8_t chunkIdx = CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY);
|
2016-08-17 11:08:22 -04:00
|
|
|
|
2019-04-15 15:06:57 -04:00
|
|
|
esp_err_t cmpItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize, uint8_t chunkIdx = CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY);
|
|
|
|
|
2018-04-02 06:44:59 -04:00
|
|
|
esp_err_t eraseItem(uint8_t nsIndex, ItemType datatype, const char* key, uint8_t chunkIdx = CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY);
|
2016-08-17 11:08:22 -04:00
|
|
|
|
2018-04-02 06:44:59 -04:00
|
|
|
esp_err_t findItem(uint8_t nsIndex, ItemType datatype, const char* key, uint8_t chunkIdx = CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY);
|
2016-08-17 11:08:22 -04:00
|
|
|
|
2018-04-02 06:44:59 -04:00
|
|
|
esp_err_t findItem(uint8_t nsIndex, ItemType datatype, const char* key, size_t &itemIndex, Item& item, uint8_t chunkIdx = CHUNK_ANY, VerOffset chunkStart = VerOffset::VER_ANY);
|
2016-08-17 11:08:22 -04:00
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
esp_err_t writeItem(uint8_t nsIndex, const char* key, const T& value)
|
|
|
|
{
|
|
|
|
return writeItem(nsIndex, itemTypeOf(value), key, &value, sizeof(value));
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
esp_err_t readItem(uint8_t nsIndex, const char* key, T& value)
|
|
|
|
{
|
|
|
|
return readItem(nsIndex, itemTypeOf(value), key, &value, sizeof(value));
|
|
|
|
}
|
|
|
|
|
2019-04-15 15:06:57 -04:00
|
|
|
template<typename T>
|
|
|
|
esp_err_t cmpItem(uint8_t nsIndex, const char* key, const T& value)
|
|
|
|
{
|
|
|
|
return cmpItem(nsIndex, itemTypeOf(value), key, &value, sizeof(value));
|
|
|
|
}
|
|
|
|
|
2016-08-17 11:08:22 -04:00
|
|
|
template<typename T>
|
|
|
|
esp_err_t eraseItem(uint8_t nsIndex, const char* key)
|
|
|
|
{
|
|
|
|
return eraseItem(nsIndex, itemTypeOf<T>(), key);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t getUsedEntryCount() const
|
|
|
|
{
|
|
|
|
return mUsedEntryCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t getErasedEntryCount() const
|
|
|
|
{
|
|
|
|
return mErasedEntryCount;
|
|
|
|
}
|
2018-04-02 06:44:59 -04:00
|
|
|
size_t getVarDataTailroom() const ;
|
2016-08-17 11:08:22 -04:00
|
|
|
|
|
|
|
esp_err_t markFull();
|
|
|
|
|
|
|
|
esp_err_t markFreeing();
|
|
|
|
|
2018-04-27 03:10:29 -04:00
|
|
|
esp_err_t copyItems(Page& other);
|
2016-08-17 11:08:22 -04:00
|
|
|
|
|
|
|
esp_err_t erase();
|
|
|
|
|
2016-08-21 22:30:35 -04:00
|
|
|
void debugDump() const;
|
2016-08-17 11:08:22 -04:00
|
|
|
|
2018-02-20 02:11:56 -05:00
|
|
|
esp_err_t calcEntries(nvs_stats_t &nvsStats);
|
|
|
|
|
2016-08-17 11:08:22 -04:00
|
|
|
protected:
|
|
|
|
|
2016-09-22 09:05:47 -04:00
|
|
|
class Header
|
|
|
|
{
|
2016-08-17 11:08:22 -04:00
|
|
|
public:
|
2016-09-22 09:05:47 -04:00
|
|
|
Header()
|
|
|
|
{
|
2018-07-25 11:11:09 -04:00
|
|
|
std::fill_n(mReserved, sizeof(mReserved)/sizeof(mReserved[0]), UINT8_MAX);
|
2016-08-17 11:08:22 -04:00
|
|
|
}
|
2016-09-22 09:05:47 -04:00
|
|
|
|
2016-08-17 11:08:22 -04:00
|
|
|
PageState mState; // page state
|
|
|
|
uint32_t mSeqNumber; // sequence number of this page
|
2018-07-25 11:11:09 -04:00
|
|
|
uint8_t mVersion; // nvs format version
|
|
|
|
uint8_t mReserved[19]; // unused, must be 0xff
|
2016-08-17 11:08:22 -04:00
|
|
|
uint32_t mCrc32; // crc of everything except mState
|
2016-09-22 09:05:47 -04:00
|
|
|
|
2016-08-17 11:08:22 -04:00
|
|
|
uint32_t calculateCrc32();
|
|
|
|
};
|
2016-09-22 09:05:47 -04:00
|
|
|
|
2016-08-17 11:08:22 -04:00
|
|
|
enum class EntryState {
|
|
|
|
EMPTY = 0x3, // 0b11, default state after flash erase
|
|
|
|
WRITTEN = EMPTY & ~ESB_WRITTEN, // entry was written
|
|
|
|
ERASED = WRITTEN & ~ESB_ERASED, // entry was written and then erased
|
2021-07-27 05:58:52 -04:00
|
|
|
ILLEGAL = 0x1, // only possible if flash is inconsistent
|
2016-08-17 11:08:22 -04:00
|
|
|
INVALID = 0x4 // entry is in inconsistent state (write started but ESB_WRITTEN has not been set yet)
|
|
|
|
};
|
|
|
|
|
|
|
|
esp_err_t mLoadEntryTable();
|
|
|
|
|
|
|
|
esp_err_t initialize();
|
|
|
|
|
|
|
|
esp_err_t alterEntryState(size_t index, EntryState state);
|
|
|
|
|
2016-09-21 23:42:55 -04:00
|
|
|
esp_err_t alterEntryRangeState(size_t begin, size_t end, EntryState state);
|
|
|
|
|
2016-08-17 11:08:22 -04:00
|
|
|
esp_err_t alterPageState(PageState state);
|
|
|
|
|
2016-08-21 22:30:35 -04:00
|
|
|
esp_err_t readEntry(size_t index, Item& dst) const;
|
2016-08-17 11:08:22 -04:00
|
|
|
|
|
|
|
esp_err_t writeEntry(const Item& item);
|
2020-04-26 20:51:31 -04:00
|
|
|
|
2016-09-21 23:42:55 -04:00
|
|
|
esp_err_t writeEntryData(const uint8_t* data, size_t size);
|
2016-08-17 11:08:22 -04:00
|
|
|
|
|
|
|
esp_err_t eraseEntryAndSpan(size_t index);
|
2016-09-22 09:05:47 -04:00
|
|
|
|
2016-08-17 11:08:22 -04:00
|
|
|
void updateFirstUsedEntry(size_t index, size_t span);
|
|
|
|
|
|
|
|
static constexpr size_t getAlignmentForType(ItemType type)
|
|
|
|
{
|
|
|
|
return static_cast<uint8_t>(type) & 0x0f;
|
|
|
|
}
|
|
|
|
|
2016-08-21 22:30:35 -04:00
|
|
|
uint32_t getEntryAddress(size_t entry) const
|
2016-08-17 11:08:22 -04:00
|
|
|
{
|
|
|
|
assert(entry < ENTRY_COUNT);
|
|
|
|
return mBaseAddress + ENTRY_DATA_OFFSET + static_cast<uint32_t>(entry) * ENTRY_SIZE;
|
|
|
|
}
|
2020-04-26 20:51:31 -04:00
|
|
|
|
2017-03-15 04:51:52 -04:00
|
|
|
static const char* pageStateToName(PageState ps);
|
2016-08-17 11:08:22 -04:00
|
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
uint32_t mBaseAddress = 0;
|
|
|
|
PageState mState = PageState::INVALID;
|
|
|
|
uint32_t mSeqNumber = UINT32_MAX;
|
2018-07-25 11:11:09 -04:00
|
|
|
uint8_t mVersion = NVS_VERSION;
|
2016-08-17 11:08:22 -04:00
|
|
|
typedef CompressedEnumTable<EntryState, 2, ENTRY_COUNT> TEntryTable;
|
|
|
|
TEntryTable mEntryTable;
|
|
|
|
size_t mNextFreeEntry = INVALID_ENTRY;
|
|
|
|
size_t mFirstUsedEntry = INVALID_ENTRY;
|
|
|
|
uint16_t mUsedEntryCount = 0;
|
|
|
|
uint16_t mErasedEntryCount = 0;
|
|
|
|
|
2020-11-06 02:54:51 -05:00
|
|
|
/**
|
|
|
|
* This hash list stores hashes of namespace index, key, and ChunkIndex for quick lookup when searching items.
|
|
|
|
*/
|
2016-09-20 10:40:12 -04:00
|
|
|
HashList mHashList;
|
2016-08-17 11:08:22 -04:00
|
|
|
|
2020-04-26 20:51:31 -04:00
|
|
|
Partition *mPartition;
|
|
|
|
|
2016-08-17 11:08:22 -04:00
|
|
|
static const uint32_t HEADER_OFFSET = 0;
|
|
|
|
static const uint32_t ENTRY_TABLE_OFFSET = HEADER_OFFSET + 32;
|
|
|
|
static const uint32_t ENTRY_DATA_OFFSET = ENTRY_TABLE_OFFSET + 32;
|
|
|
|
|
|
|
|
static_assert(sizeof(Header) == 32, "header size must be 32 bytes");
|
|
|
|
static_assert(ENTRY_TABLE_OFFSET % 32 == 0, "entry table offset should be aligned");
|
|
|
|
static_assert(ENTRY_DATA_OFFSET % 32 == 0, "entry data offset should be aligned");
|
|
|
|
|
|
|
|
}; // class Page
|
|
|
|
|
|
|
|
} // namespace nvs
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* nvs_page_hpp */
|