mirror of
https://github.com/espressif/openthread.git
synced 2026-08-08 03:37:46 +00:00
[flash] implement flash storage driver for settings (#4552)
This commit is contained in:
@@ -33,6 +33,7 @@ ot_platform_headers = \
|
||||
alarm-milli.h \
|
||||
ble.h \
|
||||
diag.h \
|
||||
flash.h \
|
||||
entropy.h \
|
||||
memory.h \
|
||||
misc.h \
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2020, The OpenThread Authors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef OPENTHREAD_PLATFORM_FLASH_H_
|
||||
#define OPENTHREAD_PLATFORM_FLASH_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <openthread/instance.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This function initializes the flash driver.
|
||||
*
|
||||
* @param[in] aInstance The OpenThread instance structure.
|
||||
*
|
||||
*/
|
||||
void otPlatFlashInit(otInstance *aInstance);
|
||||
|
||||
/**
|
||||
* This function gets the size of the swap space.
|
||||
*
|
||||
* @param[in] aInstance The OpenThread instance structure.
|
||||
*
|
||||
* @returns The size of the swap space in bytes.
|
||||
*
|
||||
*/
|
||||
uint32_t otPlatFlashGetSwapSize(otInstance *aInstance);
|
||||
|
||||
/**
|
||||
* This function erases the swap space indicated by @p aSwapIndex.
|
||||
*
|
||||
* @param[in] aInstance The OpenThread instance structure.
|
||||
* @param[in] aSwapIndex A value in [0, 1] that indicates the swap space.
|
||||
*
|
||||
*/
|
||||
void otPlatFlashErase(otInstance *aInstance, uint8_t aSwapIndex);
|
||||
|
||||
/**
|
||||
* This function reads @p aSize bytes into @p aData.
|
||||
*
|
||||
* @param[in] aInstance The OpenThread instance structure.
|
||||
* @param[in] aSwapIndex A value in [0, 1] that indicates the swap space.
|
||||
* @param[in] aOffset A byte offset within the swap space.
|
||||
* @param[out] aData A pointer to the data buffer for reading.
|
||||
* @param[in] aSize Number of bytes to read.
|
||||
*
|
||||
*/
|
||||
void otPlatFlashRead(otInstance *aInstance, uint8_t aSwapIndex, uint32_t aOffset, void *aData, uint32_t aSize);
|
||||
|
||||
/**
|
||||
* This function writes @p aSize bytes from @p aData.
|
||||
*
|
||||
* @param[in] aInstance The OpenThread instance structure.
|
||||
* @param[in] aSwapIndex A value in [0, 1] that indicates the swap space.
|
||||
* @param[in] aOffset A byte offset within the swap space.
|
||||
* @param[out] aData A pointer to the data to write.
|
||||
* @param[in] aSize Number of bytes to write.
|
||||
*
|
||||
*
|
||||
*/
|
||||
void otPlatFlashWrite(otInstance *aInstance, uint8_t aSwapIndex, uint32_t aOffset, const void *aData, uint32_t aSize);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // OPENTHREAD_PLATFORM_FLASH_H_
|
||||
@@ -195,6 +195,7 @@ set(COMMON_SOURCES
|
||||
utils/channel_manager.cpp
|
||||
utils/channel_monitor.cpp
|
||||
utils/child_supervision.cpp
|
||||
utils/flash.cpp
|
||||
utils/heap.cpp
|
||||
utils/jam_detector.cpp
|
||||
utils/parse_cmdline.cpp
|
||||
|
||||
@@ -233,6 +233,7 @@ SOURCES_COMMON = \
|
||||
utils/channel_manager.cpp \
|
||||
utils/channel_monitor.cpp \
|
||||
utils/child_supervision.cpp \
|
||||
utils/flash.cpp \
|
||||
utils/heap.cpp \
|
||||
utils/jam_detector.cpp \
|
||||
utils/parse_cmdline.cpp \
|
||||
@@ -435,6 +436,7 @@ HEADERS_COMMON = \
|
||||
utils/channel_manager.hpp \
|
||||
utils/channel_monitor.hpp \
|
||||
utils/child_supervision.hpp \
|
||||
utils/flash.hpp \
|
||||
utils/heap.hpp \
|
||||
utils/jam_detector.hpp \
|
||||
utils/parse_cmdline.hpp \
|
||||
|
||||
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* Copyright (c) 2020, The OpenThread Authors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "flash.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <openthread/platform/flash.h>
|
||||
|
||||
#include "common/code_utils.hpp"
|
||||
#include "common/instance.hpp"
|
||||
|
||||
namespace ot {
|
||||
|
||||
const uint32_t ot::Flash::sSwapActive;
|
||||
const uint32_t ot::Flash::sSwapInactive;
|
||||
|
||||
void Flash::Init(void)
|
||||
{
|
||||
RecordHeader record;
|
||||
|
||||
otPlatFlashInit(&GetInstance());
|
||||
|
||||
mSwapSize = otPlatFlashGetSwapSize(&GetInstance());
|
||||
|
||||
for (mSwapIndex = 0;; mSwapIndex++)
|
||||
{
|
||||
uint32_t swapMarker;
|
||||
|
||||
if (mSwapIndex >= 2)
|
||||
{
|
||||
Wipe();
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
otPlatFlashRead(&GetInstance(), mSwapIndex, 0, &swapMarker, sizeof(swapMarker));
|
||||
|
||||
if (swapMarker == sSwapActive)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (mSwapUsed = kSwapMarkerSize; mSwapUsed <= mSwapSize - sizeof(record); mSwapUsed += record.GetSize())
|
||||
{
|
||||
otPlatFlashRead(&GetInstance(), mSwapIndex, mSwapUsed, &record, sizeof(record));
|
||||
VerifyOrExit(record.IsAddBeginSet());
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
otError Flash::Get(uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength) const
|
||||
{
|
||||
otError error = OT_ERROR_NOT_FOUND;
|
||||
uint16_t valueLength = 0;
|
||||
int index = 0;
|
||||
uint32_t offset;
|
||||
RecordHeader record;
|
||||
|
||||
for (offset = kSwapMarkerSize; offset < mSwapUsed; offset += record.GetSize())
|
||||
{
|
||||
otPlatFlashRead(&GetInstance(), mSwapIndex, offset, &record, sizeof(record));
|
||||
|
||||
if ((record.GetKey() != aKey) || !record.IsValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (record.IsFirst())
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
|
||||
if (index == aIndex)
|
||||
{
|
||||
if (aValue && aValueLength)
|
||||
{
|
||||
uint16_t readLength = *aValueLength;
|
||||
|
||||
if (readLength > record.GetLength())
|
||||
{
|
||||
readLength = record.GetLength();
|
||||
}
|
||||
|
||||
otPlatFlashRead(&GetInstance(), mSwapIndex, offset + sizeof(record), aValue, readLength);
|
||||
}
|
||||
|
||||
valueLength = record.GetLength();
|
||||
error = OT_ERROR_NONE;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
if (aValueLength)
|
||||
{
|
||||
*aValueLength = valueLength;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
otError Flash::Set(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
|
||||
{
|
||||
return Add(aKey, true, aValue, aValueLength);
|
||||
}
|
||||
|
||||
otError Flash::Add(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
|
||||
{
|
||||
bool first = (Get(aKey, 0, NULL, NULL) == OT_ERROR_NOT_FOUND);
|
||||
|
||||
return Add(aKey, first, aValue, aValueLength);
|
||||
}
|
||||
|
||||
otError Flash::Add(uint16_t aKey, bool aFirst, const uint8_t *aValue, uint16_t aValueLength)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
Record record;
|
||||
|
||||
record.Init(aKey, aFirst);
|
||||
record.SetData(aValue, aValueLength);
|
||||
|
||||
if ((mSwapSize - record.GetSize()) < mSwapUsed)
|
||||
{
|
||||
Swap();
|
||||
VerifyOrExit((mSwapSize - record.GetSize()) >= mSwapUsed, error = OT_ERROR_NO_BUFS);
|
||||
}
|
||||
|
||||
otPlatFlashWrite(&GetInstance(), mSwapIndex, mSwapUsed, &record, record.GetSize());
|
||||
|
||||
record.SetAddCompleteFlag();
|
||||
otPlatFlashWrite(&GetInstance(), mSwapIndex, mSwapUsed, &record, sizeof(RecordHeader));
|
||||
|
||||
mSwapUsed += record.GetSize();
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
bool Flash::DoesValidRecordExist(uint32_t aOffset, uint16_t aKey) const
|
||||
{
|
||||
RecordHeader record;
|
||||
bool rval = false;
|
||||
|
||||
for (; aOffset < mSwapUsed; aOffset += record.GetSize())
|
||||
{
|
||||
otPlatFlashRead(&GetInstance(), mSwapIndex, aOffset, &record, sizeof(record));
|
||||
|
||||
if (record.IsValid() && record.IsFirst() && (record.GetKey() == aKey))
|
||||
{
|
||||
ExitNow(rval = true);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return rval;
|
||||
}
|
||||
|
||||
void Flash::Swap(void)
|
||||
{
|
||||
uint8_t dstIndex = !mSwapIndex;
|
||||
uint32_t dstOffset = kSwapMarkerSize;
|
||||
Record record;
|
||||
|
||||
otPlatFlashErase(&GetInstance(), dstIndex);
|
||||
|
||||
for (uint32_t srcOffset = kSwapMarkerSize; srcOffset < mSwapUsed; srcOffset += record.GetSize())
|
||||
{
|
||||
otPlatFlashRead(&GetInstance(), mSwapIndex, srcOffset, &record, sizeof(RecordHeader));
|
||||
|
||||
VerifyOrExit(record.IsAddBeginSet());
|
||||
|
||||
if (!record.IsValid() || DoesValidRecordExist(srcOffset + record.GetSize(), record.GetKey()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
otPlatFlashRead(&GetInstance(), mSwapIndex, srcOffset, &record, record.GetSize());
|
||||
otPlatFlashWrite(&GetInstance(), dstIndex, dstOffset, &record, record.GetSize());
|
||||
dstOffset += record.GetSize();
|
||||
}
|
||||
|
||||
exit:
|
||||
otPlatFlashWrite(&GetInstance(), dstIndex, 0, &sSwapActive, sizeof(sSwapActive));
|
||||
otPlatFlashWrite(&GetInstance(), mSwapIndex, 0, &sSwapInactive, sizeof(sSwapInactive));
|
||||
|
||||
mSwapIndex = dstIndex;
|
||||
mSwapUsed = dstOffset;
|
||||
}
|
||||
|
||||
otError Flash::Delete(uint16_t aKey, int aIndex)
|
||||
{
|
||||
otError error = OT_ERROR_NOT_FOUND;
|
||||
int index = 0;
|
||||
RecordHeader record;
|
||||
|
||||
for (uint32_t offset = kSwapMarkerSize; offset < mSwapUsed; offset += record.GetSize())
|
||||
{
|
||||
otPlatFlashRead(&GetInstance(), mSwapIndex, offset, &record, sizeof(record));
|
||||
|
||||
if ((record.GetKey() != aKey) || !record.IsValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (record.IsFirst())
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
|
||||
if ((aIndex == index) || (aIndex == -1))
|
||||
{
|
||||
record.SetDeleted();
|
||||
otPlatFlashWrite(&GetInstance(), mSwapIndex, offset, &record, sizeof(record));
|
||||
error = OT_ERROR_NONE;
|
||||
}
|
||||
|
||||
if ((index == 1) && (aIndex == 0))
|
||||
{
|
||||
record.SetFirst();
|
||||
otPlatFlashWrite(&GetInstance(), mSwapIndex, offset, &record, sizeof(record));
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void Flash::Wipe(void)
|
||||
{
|
||||
otPlatFlashErase(&GetInstance(), 0);
|
||||
otPlatFlashWrite(&GetInstance(), 0, 0, &sSwapActive, sizeof(sSwapActive));
|
||||
|
||||
mSwapIndex = 0;
|
||||
mSwapUsed = sizeof(sSwapActive);
|
||||
}
|
||||
|
||||
} // namespace ot
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright (c) 2020, The OpenThread Authors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef FLASH_HPP_
|
||||
#define FLASH_HPP_
|
||||
|
||||
#include "openthread-core-config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <openthread/error.h>
|
||||
#include <openthread/platform/toolchain.h>
|
||||
|
||||
#include "common/debug.hpp"
|
||||
#include "common/locator.hpp"
|
||||
|
||||
namespace ot {
|
||||
|
||||
/**
|
||||
* This class implements the flash storage driver.
|
||||
*
|
||||
*/
|
||||
class Flash : public InstanceLocator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
*/
|
||||
Flash(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* This method initializes the flash storage driver.
|
||||
*
|
||||
*/
|
||||
void Init(void);
|
||||
|
||||
/**
|
||||
* This method fetches the value identified by @p aKey.
|
||||
*
|
||||
* @param[in] aKey The key associated with the requested value.
|
||||
* @param[in] aIndex The index of the specific item to get.
|
||||
* @param[out] aValue A pointer to where the value of the setting should be written.
|
||||
* May be NULL if just testing for the presence or length of a key.
|
||||
* @param[inout] aValueLength A pointer to the length of the value.
|
||||
* When called, this should point to an integer containing the maximum bytes that
|
||||
* can be written to @p aValue.
|
||||
* At return, the actual length of the setting is written.
|
||||
* May be NULL if performing a presence check.
|
||||
*
|
||||
* @retval OT_ERROR_NONE The value was fetched successfully.
|
||||
* @retval OT_ERROR_NOT_FOUND The key was not found.
|
||||
*
|
||||
*/
|
||||
otError Get(uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength) const;
|
||||
|
||||
/**
|
||||
* This method sets or replaces the value identified by @p aKey.
|
||||
*
|
||||
* If there was more than one value previously associated with @p aKey, then they are all deleted and replaced with
|
||||
* this single entry.
|
||||
*
|
||||
* @param[in] aKey The key associated with the value.
|
||||
* @param[in] aValue A pointer to where the new value of the setting should be read from.
|
||||
* MUST NOT be NULL if @p aValueLength is non-zero.
|
||||
* @param[in] aValueLength The length of the data pointed to by @p aValue. May be zero.
|
||||
*
|
||||
* @retval OT_ERROR_NONE The value was changed.
|
||||
* @retval OT_ERROR_NO_BUFS Not enough space to store the value.
|
||||
*
|
||||
*/
|
||||
otError Set(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength);
|
||||
|
||||
/**
|
||||
* This method adds a value to @p aKey.
|
||||
*
|
||||
* @param[in] aKey The key associated with the value.
|
||||
* @param[in] aValue A pointer to where the new value of the setting should be read from.
|
||||
* MUST NOT be NULL if @p aValueLength is non-zero.
|
||||
* @param[in] aValueLength The length of the data pointed to by @p aValue. May be zero.
|
||||
*
|
||||
* @retval OT_ERROR_NONE The value was added.
|
||||
* @retval OT_ERROR_NO_BUFS Not enough space to store the value.
|
||||
*
|
||||
*/
|
||||
otError Add(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength);
|
||||
|
||||
/**
|
||||
* This method removes a value from @p aKey.
|
||||
*
|
||||
*
|
||||
* @param[in] aKey The key associated with the value.
|
||||
* @param[in] aIndex The index of the value to be removed.
|
||||
* If set to -1, all values for @p aKey will be removed.
|
||||
*
|
||||
* @retval OT_ERROR_NONE The given key and index was found and removed successfully.
|
||||
* @retval OT_ERROR_NOT_FOUND The given key or index was not found.
|
||||
*
|
||||
*/
|
||||
otError Delete(uint16_t aKey, int aIndex);
|
||||
|
||||
/**
|
||||
* This method removes all values.
|
||||
*
|
||||
*/
|
||||
void Wipe(void);
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
kSwapMarkerSize = 4, // in bytes
|
||||
};
|
||||
|
||||
static const uint32_t sSwapActive = 0xbe5cc5ee;
|
||||
static const uint32_t sSwapInactive = 0xbe5cc5ec;
|
||||
|
||||
OT_TOOL_PACKED_BEGIN
|
||||
class RecordHeader
|
||||
{
|
||||
public:
|
||||
void Init(uint16_t aKey, bool aFirst)
|
||||
{
|
||||
mKey = aKey;
|
||||
mFlags = kFlagsInit & ~kFlagAddBegin;
|
||||
|
||||
if (aFirst)
|
||||
{
|
||||
mFlags &= ~kFlagFirst;
|
||||
}
|
||||
|
||||
mLength = 0;
|
||||
mReserved = 0xffff;
|
||||
};
|
||||
|
||||
uint16_t GetKey(void) const { return mKey; }
|
||||
void SetKey(uint16_t aKey) { mKey = aKey; }
|
||||
|
||||
uint16_t GetLength(void) const { return mLength; }
|
||||
void SetLength(uint16_t aLength) { mLength = aLength; }
|
||||
|
||||
uint16_t GetSize(void) const { return sizeof(*this) + ((mLength + 3) & 0xfffc); }
|
||||
|
||||
bool IsValid(void) const { return ((mFlags & (kFlagAddComplete | kFlagDelete)) == kFlagDelete); }
|
||||
|
||||
bool IsAddBeginSet(void) const { return (mFlags & kFlagAddBegin) == 0; }
|
||||
void SetAddBeginFlag(void) { mFlags &= ~kFlagAddBegin; }
|
||||
|
||||
bool IsAddCompleteSet(void) const { return (mFlags & kFlagAddComplete) == 0; }
|
||||
void SetAddCompleteFlag(void) { mFlags &= ~kFlagAddComplete; }
|
||||
|
||||
bool IsDeleted(void) const { return (mFlags & kFlagDelete) == 0; }
|
||||
void SetDeleted(void) { mFlags &= ~kFlagDelete; }
|
||||
|
||||
bool IsFirst(void) const { return (mFlags & kFlagFirst) == 0; }
|
||||
void SetFirst(void) { mFlags &= ~kFlagFirst; }
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
kFlagsInit = 0xffff, ///< Flags initialize to all-ones.
|
||||
kFlagAddBegin = 1 << 0, ///< 0 indicates record write has started, 1 otherwise.
|
||||
kFlagAddComplete = 1 << 1, ///< 0 indicates record write has completed, 1 otherwise.
|
||||
kFlagDelete = 1 << 2, ///< 0 indicates record was deleted, 1 otherwise.
|
||||
kFlagFirst = 1 << 3, ///< 0 indicates first record for key, 1 otherwise.
|
||||
};
|
||||
|
||||
uint16_t mKey;
|
||||
uint16_t mFlags;
|
||||
uint16_t mLength;
|
||||
uint16_t mReserved;
|
||||
} OT_TOOL_PACKED_END;
|
||||
|
||||
OT_TOOL_PACKED_BEGIN
|
||||
class Record : public RecordHeader
|
||||
{
|
||||
public:
|
||||
const uint8_t *GetData(void) const { return mData; }
|
||||
void SetData(const uint8_t *aData, uint16_t aDataLength)
|
||||
{
|
||||
OT_ASSERT(aDataLength <= kMaxDataSize);
|
||||
memcpy(mData, aData, aDataLength);
|
||||
SetLength(aDataLength);
|
||||
}
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
kMaxDataSize = 255,
|
||||
};
|
||||
|
||||
uint8_t mData[kMaxDataSize];
|
||||
} OT_TOOL_PACKED_END;
|
||||
|
||||
otError Add(uint16_t aKey, bool aFirst, const uint8_t *aValue, uint16_t aValueLength);
|
||||
bool DoesValidRecordExist(uint32_t aOffset, uint16_t aKey) const;
|
||||
void Swap(void);
|
||||
|
||||
uint32_t mSwapSize;
|
||||
uint32_t mSwapUsed;
|
||||
uint8_t mSwapIndex;
|
||||
};
|
||||
|
||||
} // namespace ot
|
||||
|
||||
#endif // FLASH_HPP_
|
||||
@@ -109,6 +109,7 @@ check_PROGRAMS += \
|
||||
test-aes \
|
||||
test-child \
|
||||
test-child-table \
|
||||
test-flash \
|
||||
test-heap \
|
||||
test-hmac-sha256 \
|
||||
test-ip6-address \
|
||||
@@ -173,6 +174,9 @@ test_child_SOURCES = $(COMMON_SOURCES) test_child.cpp
|
||||
test_child_table_LDADD = $(COMMON_LDADD)
|
||||
test_child_table_SOURCES = $(COMMON_SOURCES) test_child_table.cpp
|
||||
|
||||
test_flash_LDADD = $(COMMON_LDADD)
|
||||
test_flash_SOURCES = $(COMMON_SOURCES) test_flash.cpp
|
||||
|
||||
test_hdlc_LDADD = $(COMMON_LDADD)
|
||||
test_hdlc_SOURCES = $(COMMON_SOURCES) test_hdlc.cpp
|
||||
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (c) 2020, The OpenThread Authors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <openthread/platform/flash.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "utils/flash.hpp"
|
||||
|
||||
#include "test_platform.h"
|
||||
#include "test_util.h"
|
||||
|
||||
namespace ot {
|
||||
|
||||
void TestFlash(void)
|
||||
{
|
||||
uint8_t readBuffer[256];
|
||||
uint8_t writeBuffer[256];
|
||||
|
||||
Instance *instance = testInitInstance();
|
||||
Flash flash(*instance);
|
||||
|
||||
for (uint32_t i = 0; i < sizeof(readBuffer); i++)
|
||||
{
|
||||
readBuffer[i] = i & 0xff;
|
||||
}
|
||||
|
||||
flash.Init();
|
||||
|
||||
// No records in settings
|
||||
|
||||
VerifyOrQuit(flash.Delete(0, 0) == OT_ERROR_NOT_FOUND, "Delete() failed");
|
||||
VerifyOrQuit(flash.Get(0, 0, NULL, NULL) == OT_ERROR_NOT_FOUND, "Get() failed");
|
||||
|
||||
// Multiple records with different keys
|
||||
|
||||
for (uint16_t key = 0; key < 16; key++)
|
||||
{
|
||||
uint16_t length = key;
|
||||
|
||||
SuccessOrQuit(flash.Add(key, writeBuffer, length), "Add() failed");
|
||||
}
|
||||
|
||||
for (uint16_t key = 0; key < 16; key++)
|
||||
{
|
||||
uint16_t length = key;
|
||||
|
||||
SuccessOrQuit(flash.Get(key, 0, readBuffer, &length), "Get() failed");
|
||||
VerifyOrQuit(length == key, "Get() did not return expected length");
|
||||
VerifyOrQuit(memcmp(readBuffer, writeBuffer, length) == 0, "Get() did not return expected value");
|
||||
}
|
||||
|
||||
for (uint16_t key = 0; key < 16; key++)
|
||||
{
|
||||
SuccessOrQuit(flash.Delete(key, 0), "Delete() failed");
|
||||
}
|
||||
|
||||
for (uint16_t key = 0; key < 16; key++)
|
||||
{
|
||||
VerifyOrQuit(flash.Delete(key, 0) == OT_ERROR_NOT_FOUND, "Delete() failed");
|
||||
VerifyOrQuit(flash.Get(key, 0, NULL, NULL) == OT_ERROR_NOT_FOUND, "Get() failed");
|
||||
}
|
||||
|
||||
// Multiple records with the same key
|
||||
|
||||
for (uint16_t index = 0; index < 16; index++)
|
||||
{
|
||||
uint16_t length = index;
|
||||
|
||||
SuccessOrQuit(flash.Add(0, writeBuffer, length), "Add() failed");
|
||||
}
|
||||
|
||||
for (uint16_t index = 0; index < 16; index++)
|
||||
{
|
||||
uint16_t length = index;
|
||||
|
||||
SuccessOrQuit(flash.Get(0, index, readBuffer, &length), "Get() failed");
|
||||
VerifyOrQuit(length == index, "Get() did not return expected length");
|
||||
VerifyOrQuit(memcmp(readBuffer, writeBuffer, length) == 0, "Get() did not return expected value");
|
||||
}
|
||||
|
||||
for (uint16_t index = 0; index < 16; index++)
|
||||
{
|
||||
SuccessOrQuit(flash.Delete(0, 0), "Delete() failed");
|
||||
}
|
||||
|
||||
VerifyOrQuit(flash.Delete(0, 0) == OT_ERROR_NOT_FOUND, "Delete() failed");
|
||||
VerifyOrQuit(flash.Get(0, 0, NULL, NULL) == OT_ERROR_NOT_FOUND, "Get() failed");
|
||||
|
||||
// Multiple records with the same key
|
||||
|
||||
for (uint16_t index = 0; index < 16; index++)
|
||||
{
|
||||
uint16_t length = index;
|
||||
|
||||
if ((index % 4) == 0)
|
||||
{
|
||||
SuccessOrQuit(flash.Set(0, writeBuffer, length), "Add() failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
SuccessOrQuit(flash.Add(0, writeBuffer, length), "Add() failed");
|
||||
}
|
||||
}
|
||||
|
||||
for (uint16_t index = 0; index < 4; index++)
|
||||
{
|
||||
uint16_t length = index + 12;
|
||||
|
||||
SuccessOrQuit(flash.Get(0, index, readBuffer, &length), "Get() failed");
|
||||
VerifyOrQuit(length == (index + 12), "Get() did not return expected length");
|
||||
VerifyOrQuit(memcmp(readBuffer, writeBuffer, length) == 0, "Get() did not return expected value");
|
||||
}
|
||||
|
||||
for (uint16_t index = 0; index < 4; index++)
|
||||
{
|
||||
SuccessOrQuit(flash.Delete(0, 0), "Delete() failed");
|
||||
}
|
||||
|
||||
VerifyOrQuit(flash.Delete(0, 0) == OT_ERROR_NOT_FOUND, "Delete() failed");
|
||||
VerifyOrQuit(flash.Get(0, 0, NULL, NULL) == OT_ERROR_NOT_FOUND, "Get() failed");
|
||||
|
||||
// Wipe()
|
||||
|
||||
for (uint16_t key = 0; key < 16; key++)
|
||||
{
|
||||
uint16_t length = key;
|
||||
|
||||
SuccessOrQuit(flash.Add(key, writeBuffer, length), "Add() failed");
|
||||
}
|
||||
|
||||
flash.Wipe();
|
||||
|
||||
for (uint16_t key = 0; key < 16; key++)
|
||||
{
|
||||
VerifyOrQuit(flash.Delete(key, 0) == OT_ERROR_NOT_FOUND, "Delete() failed");
|
||||
VerifyOrQuit(flash.Get(key, 0, NULL, NULL) == OT_ERROR_NOT_FOUND, "Get() failed");
|
||||
}
|
||||
|
||||
// Test swap
|
||||
|
||||
for (uint16_t index = 0; index < 4096; index++)
|
||||
{
|
||||
uint16_t key = index & 0xf;
|
||||
uint16_t length = index & 0xf;
|
||||
|
||||
SuccessOrQuit(flash.Set(key, writeBuffer, length), "Set() failed");
|
||||
}
|
||||
|
||||
for (uint16_t key = 0; key < 16; key++)
|
||||
{
|
||||
uint16_t length = key;
|
||||
|
||||
SuccessOrQuit(flash.Get(key, 0, readBuffer, &length), "Get() failed");
|
||||
VerifyOrQuit(length == key, "Get() did not return expected length");
|
||||
VerifyOrQuit(memcmp(readBuffer, writeBuffer, length) == 0, "Get() did not return expected value");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ot
|
||||
|
||||
int main(void)
|
||||
{
|
||||
ot::TestFlash();
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -47,6 +47,14 @@ testPlatRadioReceive g_testPlatRadioReceive = NULL;
|
||||
testPlatRadioTransmit g_testPlatRadioTransmit = NULL;
|
||||
testPlatRadioGetTransmitBuffer g_testPlatRadioGetTransmitBuffer = NULL;
|
||||
|
||||
enum
|
||||
{
|
||||
FLASH_SWAP_SIZE = 2048,
|
||||
FLASH_SWAP_NUM = 2,
|
||||
};
|
||||
|
||||
uint8_t g_flash[FLASH_SWAP_SIZE * FLASH_SWAP_NUM];
|
||||
|
||||
void testPlatResetToDefaults(void)
|
||||
{
|
||||
g_testPlatAlarmSet = false;
|
||||
@@ -543,6 +551,66 @@ void otPlatSettingsWipe(otInstance *aInstance)
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
}
|
||||
|
||||
void otPlatFlashInit(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
|
||||
memset(g_flash, 0xff, sizeof(g_flash));
|
||||
}
|
||||
|
||||
uint32_t otPlatFlashGetSwapSize(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
|
||||
return FLASH_SWAP_SIZE;
|
||||
}
|
||||
|
||||
void otPlatFlashErase(otInstance *aInstance, uint8_t aSwapIndex)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
|
||||
uint32_t address;
|
||||
|
||||
VerifyOrQuit(aSwapIndex < FLASH_SWAP_NUM, "aSwapIndex invalid");
|
||||
|
||||
address = aSwapIndex ? FLASH_SWAP_SIZE : 0;
|
||||
|
||||
memset(g_flash + address, 0xff, FLASH_SWAP_SIZE);
|
||||
}
|
||||
|
||||
void otPlatFlashRead(otInstance *aInstance, uint8_t aSwapIndex, uint32_t aOffset, void *aData, uint32_t aSize)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
|
||||
uint32_t address;
|
||||
|
||||
VerifyOrQuit(aSwapIndex < FLASH_SWAP_NUM, "aSwapIndex invalid");
|
||||
VerifyOrQuit(aSize <= FLASH_SWAP_SIZE, "aSize invalid");
|
||||
VerifyOrQuit(aOffset <= (FLASH_SWAP_SIZE - aSize), "aOffset + aSize invalid");
|
||||
|
||||
address = aSwapIndex ? FLASH_SWAP_SIZE : 0;
|
||||
|
||||
memcpy(aData, g_flash + address + aOffset, aSize);
|
||||
}
|
||||
|
||||
void otPlatFlashWrite(otInstance *aInstance, uint8_t aSwapIndex, uint32_t aOffset, const void *aData, uint32_t aSize)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
|
||||
uint32_t address;
|
||||
|
||||
VerifyOrQuit(aSwapIndex < FLASH_SWAP_NUM, "aSwapIndex invalid");
|
||||
VerifyOrQuit(aSize <= FLASH_SWAP_SIZE, "aSize invalid");
|
||||
VerifyOrQuit(aOffset <= (FLASH_SWAP_SIZE - aSize), "aOffset + aSize invalid");
|
||||
|
||||
address = aSwapIndex ? FLASH_SWAP_SIZE : 0;
|
||||
|
||||
for (uint32_t index = 0; index < aSize; index++)
|
||||
{
|
||||
g_flash[address + aOffset + index] &= ((uint8_t *)aData)[index];
|
||||
}
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
|
||||
uint64_t otPlatTimeGet(void)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user