[flash] implement flash storage driver for settings (#4552)

This commit is contained in:
Jonathan Hui
2020-03-18 20:51:41 -07:00
parent 4ee8010558
commit b3d05af6ff
9 changed files with 866 additions and 0 deletions
+1
View File
@@ -33,6 +33,7 @@ ot_platform_headers = \
alarm-milli.h \
ble.h \
diag.h \
flash.h \
entropy.h \
memory.h \
misc.h \
+96
View File
@@ -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_
+1
View File
@@ -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
+2
View File
@@ -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 \
+266
View File
@@ -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
+234
View File
@@ -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_
+4
View File
@@ -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
+194
View File
@@ -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;
}
+68
View File
@@ -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)
{