diff --git a/etc/visual-studio/UnitTests.vcxproj b/etc/visual-studio/UnitTests.vcxproj
index 6777a4dcb..3f686dcfe 100644
--- a/etc/visual-studio/UnitTests.vcxproj
+++ b/etc/visual-studio/UnitTests.vcxproj
@@ -170,6 +170,7 @@
+
@@ -195,4 +196,4 @@
-
\ No newline at end of file
+
diff --git a/etc/visual-studio/UnitTests.vcxproj.filters b/etc/visual-studio/UnitTests.vcxproj.filters
index 5dbcb99fc..6f88bbc98 100644
--- a/etc/visual-studio/UnitTests.vcxproj.filters
+++ b/etc/visual-studio/UnitTests.vcxproj.filters
@@ -36,6 +36,9 @@
Source Files
+
+ Source Files
+
Source Files
@@ -66,4 +69,4 @@
Header Files
-
\ No newline at end of file
+
diff --git a/etc/visual-studio/libopenthread.vcxproj b/etc/visual-studio/libopenthread.vcxproj
index be9d951d7..51e996e5c 100644
--- a/etc/visual-studio/libopenthread.vcxproj
+++ b/etc/visual-studio/libopenthread.vcxproj
@@ -24,6 +24,7 @@
+
@@ -276,4 +277,4 @@
-
\ No newline at end of file
+
diff --git a/etc/visual-studio/libopenthread.vcxproj.filters b/etc/visual-studio/libopenthread.vcxproj.filters
index 2ab491035..b3b11b03a 100644
--- a/etc/visual-studio/libopenthread.vcxproj.filters
+++ b/etc/visual-studio/libopenthread.vcxproj.filters
@@ -69,6 +69,9 @@
Source Files\common
+
+ Source Files\common
+
Source Files\common
@@ -420,4 +423,4 @@
Header Files\thread
-
\ No newline at end of file
+
diff --git a/etc/visual-studio/libopenthread_k.vcxproj b/etc/visual-studio/libopenthread_k.vcxproj
index e3381bc38..0bb07e985 100644
--- a/etc/visual-studio/libopenthread_k.vcxproj
+++ b/etc/visual-studio/libopenthread_k.vcxproj
@@ -257,4 +257,4 @@
-
\ No newline at end of file
+
diff --git a/etc/visual-studio/libopenthread_k.vcxproj.filters b/etc/visual-studio/libopenthread_k.vcxproj.filters
index 3776cd43a..c632bf051 100644
--- a/etc/visual-studio/libopenthread_k.vcxproj.filters
+++ b/etc/visual-studio/libopenthread_k.vcxproj.filters
@@ -69,6 +69,9 @@
Source Files\common
+
+ Source Files\common
+
Source Files\common
@@ -417,4 +420,4 @@
Header Files\meshcop
-
\ No newline at end of file
+
diff --git a/examples/platforms/cc2538/flash.c b/examples/platforms/cc2538/flash.c
index 332030152..4c7758e34 100644
--- a/examples/platforms/cc2538/flash.c
+++ b/examples/platforms/cc2538/flash.c
@@ -120,21 +120,31 @@ uint32_t otPlatFlashWrite(uint32_t aAddress, uint8_t *aData, uint32_t aSize)
{
int32_t status;
uint32_t busy = 1;
+ uint32_t *data;
+ uint32_t size = 0;
VerifyOrExit(((aAddress + aSize) < otPlatFlashGetSize()) &&
(!(aAddress & 3)) && (!(aSize & 3)), aSize = 0);
- status = ROM_ProgramFlash((uint32_t *)aData, aAddress + FLASH_BASE, aSize);
+ data = (uint32_t *)(aData);
- while (busy)
+ while (size < aSize)
{
- busy = HWREG(FLASH_CTRL_FCTL) & FLASH_CTRL_FCTL_BUSY;
+ status = ROM_ProgramFlash(data, aAddress + FLASH_BASE, 4);
+
+ while (busy)
+ {
+ busy = HWREG(FLASH_CTRL_FCTL) & FLASH_CTRL_FCTL_BUSY;
+ }
+
+ VerifyOrExit(romStatusToThread(status) == kThreadError_None, ;);
+ size += 4;
+ data++;
+ aAddress += 4;
}
- VerifyOrExit(romStatusToThread(status) == kThreadError_None, aSize = 0);
-
exit:
- return aSize;
+ return size;
}
uint32_t otPlatFlashRead(uint32_t aAddress, uint8_t *aData, uint32_t aSize)
@@ -143,7 +153,7 @@ uint32_t otPlatFlashRead(uint32_t aAddress, uint8_t *aData, uint32_t aSize)
VerifyOrExit((aAddress + aSize) < otPlatFlashGetSize(), ;);
- while (size <= aSize)
+ while (size < aSize)
{
uint32_t reg = HWREG(aAddress + FLASH_BASE);
uint8_t *byte = (uint8_t *)®
diff --git a/include/platform/settings.h b/include/platform/settings.h
index 432bc8adf..1da0a0a51 100644
--- a/include/platform/settings.h
+++ b/include/platform/settings.h
@@ -43,8 +43,12 @@ extern "C" {
/**
* Performs any initialization for the settings subsystem, if necessary.
+ *
+ * @param[in] aInstance
+ * The OpenThread instance structure.
+ *
*/
-void otPlatSettingsInit(void);
+void otPlatSettingsInit(otInstance *aInstance);
/// Begin atomic change set
/** This function is called at the start of a sequence of changes
@@ -59,18 +63,24 @@ void otPlatSettingsInit(void);
* The implementation of this function is optional. If not
* implemented, it should return kThreadError_None.
*
+ * @param[in] aInstance
+ * The OpenThread instance structure.
+ *
* @retval kThreadError_None The settings commit lock has been set.
* @retval kThreadError_Already The commit lock is already set.
*
* @sa otPlatSettingsCommitChange(), otPlatSettingsAbandonChange()
*/
-ThreadError otPlatSettingsBeginChange(void);
+ThreadError otPlatSettingsBeginChange(otInstance *aInstance);
/// Commit all settings changes since previous call to otPlatSettingsBeginChange()
/** This function is called at the end of a sequence of changes.
* The implementation of this function is optional. If not
* implemented, it should return kThreadError_NotImplemented.
*
+ * @param[in] aInstance
+ * The OpenThread instance structure.
+ *
* @retval kThreadError_None
* The changes made since the last call to
* otPlatSettingsBeginChange() have been successfully
@@ -82,7 +92,7 @@ ThreadError otPlatSettingsBeginChange(void);
*
* @sa otPlatSettingsBeginChange(), otPlatSettingsAbandonChange()
*/
-ThreadError otPlatSettingsCommitChange(void);
+ThreadError otPlatSettingsCommitChange(otInstance *aInstance);
/// Abandon all settings changes since previous call to otPlatSettingsBeginChange()
/** This function may be called at the end of a sequence of changes.
@@ -91,7 +101,10 @@ ThreadError otPlatSettingsCommitChange(void);
* rolled back and abandoned.
*
* The implementation of this function is optional. If not
- * implemented, it should return kThreadError_NotImplemented..
+ * implemented, it should return kThreadError_NotImplemented.
+ *
+ * @param[in] aInstance
+ * The OpenThread instance structure.
*
* @retval kThreadError_None
* The changes made since the last call to
@@ -104,7 +117,7 @@ ThreadError otPlatSettingsCommitChange(void);
*
* @sa otPlatSettingsBeginChange(), otPlatSettingsCommitChange()
*/
-ThreadError otPlatSettingsAbandonChange(void);
+ThreadError otPlatSettingsAbandonChange(otInstance *aInstance);
/// Fetches the value of a setting
/** This function fetches the value of the setting identified
@@ -124,6 +137,8 @@ ThreadError otPlatSettingsAbandonChange(void);
* values. The order of such values MAY change after ANY
* write operation to the store.
*
+ * @param[in] aInstance
+ * The OpenThread instance structure.
* @param[in] aKey
* The key associated with the requested setting.
* @param[in] aIndex
@@ -147,7 +162,7 @@ ThreadError otPlatSettingsAbandonChange(void);
* @retval kThreadError_NotImplemented
* This function is not implemented on this platform.
*/
-ThreadError otPlatSettingsGet(uint16_t aKey, int aIndex, uint8_t *aValue, int *aValueLength);
+ThreadError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, int *aValueLength);
/// Sets or replaces the value of a setting
/** This function sets or replaces the value of a setting
@@ -158,6 +173,8 @@ ThreadError otPlatSettingsGet(uint16_t aKey, int aIndex, uint8_t *aValue, int *a
* Calling this function successfully may cause unrelated
* settings with multiple values to be reordered.
*
+ * @param[in] aInstance
+ * The OpenThread instance structure.
* @param[in] aKey
* The key associated with the setting to change.
* @param[out] aValue
@@ -173,7 +190,7 @@ ThreadError otPlatSettingsGet(uint16_t aKey, int aIndex, uint8_t *aValue, int *a
* @retval kThreadError_NotImplemented
* This function is not implemented on this platform.
*/
-ThreadError otPlatSettingsSet(uint16_t aKey, const uint8_t *aValue, int aValueLength);
+ThreadError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, int aValueLength);
/// Adds a value to a setting
/** This function adds the value to a setting
@@ -189,6 +206,8 @@ ThreadError otPlatSettingsSet(uint16_t aKey, const uint8_t *aValue, int aValueLe
* Calling this function successfully may cause unrelated
* settings with multiple values to be reordered.
*
+ * @param[in] aInstance
+ * The OpenThread instance structure.
* @param[in] aKey
* The key associated with the setting to change.
* @param[out] aValue
@@ -204,7 +223,7 @@ ThreadError otPlatSettingsSet(uint16_t aKey, const uint8_t *aValue, int aValueLe
* @retval kThreadError_NotImplemented
* This function is not implemented on this platform.
*/
-ThreadError otPlatSettingsAdd(uint16_t aKey, const uint8_t *aValue, int aValueLength);
+ThreadError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, int aValueLength);
/// Removes a setting from the setting store
/** This function deletes a specific value from the
@@ -216,6 +235,8 @@ ThreadError otPlatSettingsAdd(uint16_t aKey, const uint8_t *aValue, int aValueLe
* items ordered (A, B, C) and you delete B, the resulting order
* is guaranteed to be (A, C).
*
+ * @param[in] aInstance
+ * The OpenThread instance structure.
* @param[in] aKey
* The key associated with the requested setting.
* @param[in] aIndex
@@ -229,13 +250,16 @@ ThreadError otPlatSettingsAdd(uint16_t aKey, const uint8_t *aValue, int aValueLe
* @retval kThreadError_NotImplemented
* This function is not implemented on this platform.
*/
-ThreadError otPlatSettingsDelete(uint16_t aKey, int index);
+ThreadError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex);
/// Removes all settings from the setting store
/** This function deletes all settings from the settings
* store, resetting it to its initial factory state.
+ *
+ * @param[in] aInstance
+ * The OpenThread instance structure.
*/
-void otPlatSettingsWipe(void);
+void otPlatSettingsWipe(otInstance *aInstance);
#ifdef __cplusplus
} // extern "C"
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index 56205e630..f4cf51d4c 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -44,6 +44,7 @@ libopenthread_a_SOURCES = \
common/message.cpp \
common/tasklet.cpp \
common/timer.cpp \
+ common/settings.cpp \
common/trickle_timer.cpp \
crypto/aes_ccm.cpp \
crypto/aes_ecb.cpp \
diff --git a/src/core/common/settings.cpp b/src/core/common/settings.cpp
new file mode 100644
index 000000000..3d3fc49f6
--- /dev/null
+++ b/src/core/common/settings.cpp
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+/**
+ * @file
+ * This file implements the OpenThread platform abstraction for non-volatile storage of settings.
+ *
+ */
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum
+{
+ kBlockAddBeginFlag = 0x1,
+ kBlockAddCompleteFlag = 0x02,
+ kBlockDeleteFlag = 0x04,
+ kBlockIndex0Flag = 0x08,
+};
+
+enum
+{
+ kSettingsFlagSize = 4,
+ kSettingsBlockDataSize = 255,
+
+ kSettingsInSwap = 0xbe5cc5ef,
+ kSettingsInUse = 0xbe5cc5ee,
+ kSettingsNotUse = 0xbe5cc5ec,
+};
+
+OT_TOOL_PACKED_BEGIN
+struct settingsBlock
+{
+ uint16_t key;
+ uint16_t flag;
+ uint16_t length;
+ uint16_t reserved;
+} OT_TOOL_PACKED_END;
+
+static void setSettingsFlag(uint32_t aBase, uint32_t aFlag)
+{
+ otPlatFlashWrite(aBase, reinterpret_cast(&aFlag), sizeof(aFlag));
+}
+
+static void initSettings(uint32_t aBase, uint32_t aFlag)
+{
+ uint32_t address = aBase;
+ uint32_t settingsSize = OPENTHREAD_CONFIG_SETTINGS_PAGE_NUM > 1 ?
+ OPENTHREAD_CONFIG_SETTINGS_PAGE_SIZE * OPENTHREAD_CONFIG_SETTINGS_PAGE_NUM / 2 :
+ OPENTHREAD_CONFIG_SETTINGS_PAGE_SIZE;
+
+ while (address < (aBase + settingsSize))
+ {
+ otPlatFlashErasePage(address);
+ otPlatFlashStatusWait(1000);
+ address += OPENTHREAD_CONFIG_SETTINGS_PAGE_SIZE;
+ }
+
+ setSettingsFlag(aBase, aFlag);
+}
+
+static uint32_t swapSettingsBlock(otInstance *aInstance)
+{
+ uint32_t oldBase = aInstance->mSettingsBaseAddress;
+ uint32_t swapAddress = oldBase;
+ uint32_t usedSize = aInstance->mSettingsUsedSize;
+ uint8_t pageNum = OPENTHREAD_CONFIG_SETTINGS_PAGE_NUM;
+ uint32_t settingsSize = pageNum > 1 ? OPENTHREAD_CONFIG_SETTINGS_PAGE_SIZE * pageNum / 2 :
+ OPENTHREAD_CONFIG_SETTINGS_PAGE_SIZE;
+
+ VerifyOrExit(pageNum > 1, ;);
+
+ aInstance->mSettingsBaseAddress = (swapAddress == OPENTHREAD_CONFIG_SETTINGS_BASE_ADDRESS) ?
+ (swapAddress + settingsSize) :
+ OPENTHREAD_CONFIG_SETTINGS_BASE_ADDRESS;
+
+ initSettings(aInstance->mSettingsBaseAddress, static_cast(kSettingsInSwap));
+ aInstance->mSettingsUsedSize = kSettingsFlagSize;
+ swapAddress += kSettingsFlagSize;
+
+ while (swapAddress < (oldBase + usedSize))
+ {
+ OT_TOOL_PACKED_BEGIN
+ struct addSettingsBlock
+ {
+ struct settingsBlock block;
+ uint8_t data[kSettingsBlockDataSize];
+ } OT_TOOL_PACKED_END addBlock;
+ bool valid = true;
+
+ otPlatFlashRead(swapAddress, reinterpret_cast(&addBlock.block), sizeof(struct settingsBlock));
+ swapAddress += sizeof(struct settingsBlock);
+
+ if (!(addBlock.block.flag & kBlockAddCompleteFlag) && (addBlock.block.flag & kBlockDeleteFlag))
+ {
+ uint32_t address = swapAddress + addBlock.block.length;
+
+ while (address < (oldBase + usedSize))
+ {
+ struct settingsBlock block;
+
+ otPlatFlashRead(address, reinterpret_cast(&block), sizeof(block));
+
+ if (!(block.flag & kBlockAddCompleteFlag) && (block.flag & kBlockDeleteFlag) &&
+ !(block.flag & kBlockIndex0Flag) && (block.key == addBlock.block.key))
+ {
+ valid = false;
+ break;
+ }
+
+ address += (block.length + sizeof(struct settingsBlock));
+ }
+
+ if (valid)
+ {
+ otPlatFlashRead(swapAddress, addBlock.data, addBlock.block.length);
+ otPlatFlashWrite(aInstance->mSettingsBaseAddress + aInstance->mSettingsUsedSize,
+ reinterpret_cast(&addBlock),
+ addBlock.block.length + sizeof(struct settingsBlock));
+ aInstance->mSettingsUsedSize += (sizeof(struct settingsBlock) + addBlock.block.length);
+ }
+ }
+ else if (addBlock.block.flag == 0xff)
+ {
+ break;
+ }
+
+ swapAddress += addBlock.block.length;
+ }
+
+ setSettingsFlag(aInstance->mSettingsBaseAddress, static_cast(kSettingsInUse));
+ setSettingsFlag(oldBase, static_cast(kSettingsNotUse));
+
+exit:
+ return settingsSize - aInstance->mSettingsUsedSize;
+}
+
+static ThreadError addSetting(otInstance *aInstance, uint16_t aKey, bool aIndex0, const uint8_t *aValue,
+ int aValueLength)
+{
+ ThreadError error = kThreadError_None;
+ OT_TOOL_PACKED_BEGIN
+ struct addSettingsBlock
+ {
+ struct settingsBlock block;
+ uint8_t data[kSettingsBlockDataSize];
+ } OT_TOOL_PACKED_END addBlock;
+ uint32_t settingsSize = OPENTHREAD_CONFIG_SETTINGS_PAGE_NUM > 1 ?
+ OPENTHREAD_CONFIG_SETTINGS_PAGE_SIZE * OPENTHREAD_CONFIG_SETTINGS_PAGE_NUM / 2 :
+ OPENTHREAD_CONFIG_SETTINGS_PAGE_SIZE;
+
+ addBlock.block.flag = 0xff;
+ addBlock.block.key = aKey;
+
+ if (aIndex0)
+ {
+ addBlock.block.flag &= (~kBlockIndex0Flag);
+ }
+
+ addBlock.block.flag &= (~kBlockAddBeginFlag);
+ addBlock.block.length = static_cast(aValueLength);
+
+ if ((aInstance->mSettingsUsedSize + addBlock.block.length + sizeof(struct settingsBlock)) >= settingsSize)
+ {
+ VerifyOrExit(swapSettingsBlock(aInstance) >= (addBlock.block.length + sizeof(struct settingsBlock)),
+ error = kThreadError_NoBufs);
+ }
+
+ otPlatFlashWrite(aInstance->mSettingsBaseAddress + aInstance->mSettingsUsedSize,
+ reinterpret_cast(&addBlock.block),
+ sizeof(struct settingsBlock));
+
+ memcpy(addBlock.data, aValue, addBlock.block.length);
+
+ // padding
+ while (addBlock.block.length & 3)
+ {
+ addBlock.data[addBlock.block.length++] = 0xff;
+ }
+
+ otPlatFlashWrite(aInstance->mSettingsBaseAddress + aInstance->mSettingsUsedSize + sizeof(struct settingsBlock),
+ reinterpret_cast(addBlock.data), addBlock.block.length);
+
+ addBlock.block.flag &= (~kBlockAddCompleteFlag);
+ otPlatFlashWrite(aInstance->mSettingsBaseAddress + aInstance->mSettingsUsedSize,
+ reinterpret_cast(&addBlock.block),
+ sizeof(struct settingsBlock));
+ aInstance->mSettingsUsedSize += (sizeof(struct settingsBlock) + addBlock.block.length);
+
+exit:
+ return error;
+}
+
+// settings API
+void otPlatSettingsInit(otInstance *aInstance)
+{
+ uint8_t index;
+ uint32_t settingsSize = OPENTHREAD_CONFIG_SETTINGS_PAGE_NUM > 1 ?
+ OPENTHREAD_CONFIG_SETTINGS_PAGE_SIZE * OPENTHREAD_CONFIG_SETTINGS_PAGE_NUM / 2 :
+ OPENTHREAD_CONFIG_SETTINGS_PAGE_SIZE;
+ aInstance->mSettingsBaseAddress = OPENTHREAD_CONFIG_SETTINGS_BASE_ADDRESS;
+
+ for (index = 0; index < 2; index++)
+ {
+ uint32_t blockFlag;
+
+ aInstance->mSettingsBaseAddress += settingsSize * index;
+ otPlatFlashRead(aInstance->mSettingsBaseAddress, reinterpret_cast(&blockFlag), sizeof(blockFlag));
+
+ if (blockFlag == kSettingsInUse)
+ {
+ break;
+ }
+ }
+
+ if (index == 2)
+ {
+ initSettings(aInstance->mSettingsBaseAddress, static_cast(kSettingsInUse));
+ }
+
+ aInstance->mSettingsUsedSize = kSettingsFlagSize;
+
+ while (aInstance->mSettingsUsedSize < settingsSize)
+ {
+ struct settingsBlock block;
+
+ otPlatFlashRead(aInstance->mSettingsBaseAddress + aInstance->mSettingsUsedSize,
+ reinterpret_cast(&block), sizeof(block));
+
+ if (!(block.flag & kBlockAddBeginFlag))
+ {
+ aInstance->mSettingsUsedSize += (block.length + sizeof(struct settingsBlock));
+ }
+ else
+ {
+ break;
+ }
+ }
+}
+
+ThreadError otPlatSettingsBeginChange(otInstance *aInstance)
+{
+ (void)aInstance;
+ return kThreadError_None;
+}
+
+ThreadError otPlatSettingsCommitChange(otInstance *aInstance)
+{
+ (void)aInstance;
+ return kThreadError_None;
+}
+
+ThreadError otPlatSettingsAbandonChange(otInstance *aInstance)
+{
+ (void)aInstance;
+ return kThreadError_None;
+}
+
+ThreadError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, int *aValueLength)
+{
+ ThreadError error = kThreadError_NotFound;
+ uint32_t address = aInstance->mSettingsBaseAddress + kSettingsFlagSize;
+ int index = 0;
+
+ while (address < (aInstance->mSettingsBaseAddress + aInstance->mSettingsUsedSize))
+ {
+ struct settingsBlock block;
+
+ otPlatFlashRead(address, reinterpret_cast(&block), sizeof(block));
+
+ if (block.key == aKey)
+ {
+ if (!(block.flag & kBlockIndex0Flag))
+ {
+ index = 0;
+ }
+
+ if (!(block.flag & kBlockAddCompleteFlag) && (block.flag & kBlockDeleteFlag))
+ {
+ if (index == aIndex)
+ {
+ error = kThreadError_None;
+
+ if (aValueLength)
+ {
+ *aValueLength = block.length;
+ }
+
+ if (aValue)
+ {
+ VerifyOrExit(aValueLength, error = kThreadError_InvalidArgs);
+ otPlatFlashRead(address + sizeof(struct settingsBlock), aValue, block.length);
+ }
+ }
+
+ index++;
+ }
+ }
+
+ address += (block.length + sizeof(struct settingsBlock));
+ }
+
+exit:
+ return error;
+}
+
+ThreadError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, int aValueLength)
+{
+ return addSetting(aInstance, aKey, true, aValue, aValueLength);
+}
+
+ThreadError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, int aValueLength)
+{
+ int length;
+ bool index0;
+
+ index0 = (otPlatSettingsGet(aInstance, aKey, 0, NULL, &length) == kThreadError_NotFound ? true : false);
+ return addSetting(aInstance, aKey, index0, aValue, aValueLength);
+}
+
+ThreadError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex)
+{
+ ThreadError error = kThreadError_NotFound;
+ uint32_t address = aInstance->mSettingsBaseAddress + kSettingsFlagSize;
+ int index = 0;
+
+ while (address < (aInstance->mSettingsBaseAddress + aInstance->mSettingsUsedSize))
+ {
+ struct settingsBlock block;
+
+ otPlatFlashRead(address, reinterpret_cast(&block), sizeof(block));
+
+ if (block.key == aKey)
+ {
+ if (!(block.flag & kBlockIndex0Flag))
+ {
+ index = 0;
+ }
+
+ if (!(block.flag & kBlockAddCompleteFlag) && (block.flag & kBlockDeleteFlag))
+ {
+ if (aIndex == index || aIndex == -1)
+ {
+ error = kThreadError_None;
+ block.flag &= (~kBlockDeleteFlag);
+ otPlatFlashWrite(address, reinterpret_cast(&block), sizeof(block));
+ }
+
+ if (index == 1 && aIndex == 0)
+ {
+ block.flag &= (~kBlockIndex0Flag);
+ otPlatFlashWrite(address, reinterpret_cast(&block), sizeof(block));
+ }
+
+ index++;
+ }
+ }
+
+ address += (block.length + sizeof(struct settingsBlock));
+ }
+
+ return error;
+}
+
+void otPlatSettingsWipe(otInstance *aInstance)
+{
+ initSettings(aInstance->mSettingsBaseAddress, static_cast(kSettingsInUse));
+ otPlatSettingsInit(aInstance);
+}
+
+#ifdef __cplusplus
+};
+#endif
diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h
index 0a52a099a..c001013a6 100644
--- a/src/core/openthread-core-default-config.h
+++ b/src/core/openthread-core-default-config.h
@@ -279,5 +279,35 @@
*/
#define OPENTHREAD_CONFIG_LOG_NETDIAG
+/**
+ * @def OPENTHREAD_CONFIG_SETTINGS_BASE_ADDRESS
+ *
+ * The base address of settings.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_SETTINGS_BASE_ADDRESS
+#define OPENTHREAD_CONFIG_SETTINGS_BASE_ADDRESS 0x39000
+#endif // OPENTHREAD_CONFIG_SETTINGS_BASE_ADDRESS
+
+/**
+ * @def OPENTHREAD_CONFIG_SETTINGS_PAGE_SIZE
+ *
+ * The page size of settings.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_SETTINGS_PAGE_SIZE
+#define OPENTHREAD_CONFIG_SETTINGS_PAGE_SIZE 0x800
+#endif // OPENTHREAD_CONFIG_SETTINGS_PAGE_SIZE
+
+/**
+ * @def OPENTHREAD_CONFIG_SETTINGS_PAGE_NUM
+ *
+ * The page number of settings.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_SETTINGS_PAGE_NUM
+#define OPENTHREAD_CONFIG_SETTINGS_PAGE_NUM 2
+#endif // OPENTHREAD_CONFIG_SETTINGS_PAGE_NUM
+
#endif // OPENTHREAD_CORE_DEFAULT_CONFIG_H_
diff --git a/src/core/openthread-instance.h b/src/core/openthread-instance.h
index 96a460926..48057a8c8 100644
--- a/src/core/openthread-instance.h
+++ b/src/core/openthread-instance.h
@@ -77,6 +77,13 @@ typedef struct otInstance
Thread::Ip6::Ip6 mIp6;
Thread::ThreadNetif mThreadNetif;
+ //
+ // Platform specific variables
+ //
+
+ uint32_t mSettingsBaseAddress;
+ uint32_t mSettingsUsedSize;
+
// Constructor
otInstance(void);
diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am
index 77c4a676e..c7b2a9b2f 100644
--- a/tests/unit/Makefile.am
+++ b/tests/unit/Makefile.am
@@ -70,6 +70,7 @@ check_PROGRAMS = \
test-link-quality \
test-mac-frame \
test-message \
+ test-settings \
test-timer \
test-toolchain \
$(NULL)
@@ -128,6 +129,9 @@ test_message_SOURCES = test_platform.cpp test_message.cpp
test_ncp_buffer_LDADD = $(COMMON_LDADD)
test_ncp_buffer_SOURCES = test_platform.cpp test_ncp_buffer.cpp
+test_settings_LDADD = $(COMMON_LDADD)
+test_settings_SOURCES = test_platform.cpp test_settings.cpp
+
test_timer_LDADD = $(COMMON_LDADD)
test_timer_SOURCES = test_platform.cpp test_timer.cpp
diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp
index 38359bc75..e77e5ef86 100644
--- a/tests/unit/test_platform.cpp
+++ b/tests/unit/test_platform.cpp
@@ -41,6 +41,7 @@
#include
#include
+#include
enum
{
@@ -59,6 +60,14 @@ uint32_t sCallCount[kCallCountIndexMax];
bool sDiagMode = false;
+enum
+{
+ kFlashSize = 0x40000,
+ kFlashPageSize = 0x800,
+};
+
+uint8_t sFlashBuffer[kFlashSize];
+
extern "C" {
void otSignalTaskletPending(otInstance *)
@@ -296,4 +305,72 @@ exit:
return kPlatResetReason_PowerOn;
}
+ //
+ // Flash
+ //
+ ThreadError otPlatFlashInit(void)
+ {
+ memset(sFlashBuffer, 0xff, kFlashSize);
+ return kThreadError_None;
+ }
+
+ uint32_t otPlatFlashGetSize(void)
+ {
+ return kFlashSize;
+ }
+
+ ThreadError otPlatFlashErasePage(uint32_t aAddress)
+ {
+ ThreadError error = kThreadError_None;
+ uint32_t address;
+
+ VerifyOrExit(aAddress < kFlashSize, error = kThreadError_InvalidArgs);
+
+ // Get start address of the flash page that includes aAddress
+ address = aAddress & (~(uint32_t)(kFlashPageSize - 1));
+ memset(sFlashBuffer + address, 0xff, kFlashPageSize);
+
+exit:
+ return error;
+ }
+
+ ThreadError otPlatFlashStatusWait(uint32_t aTimeout)
+ {
+ (void)aTimeout;
+ return kThreadError_None;
+ }
+
+ uint32_t otPlatFlashWrite(uint32_t aAddress, uint8_t *aData, uint32_t aSize)
+ {
+ uint32_t ret = 0;
+ uint8_t byte;
+
+ VerifyOrExit(aAddress < kFlashSize, ;);
+
+ for (uint32_t index = 0; index < aSize; index++)
+ {
+ byte = sFlashBuffer[aAddress + index];
+ byte &= aData[index];
+ sFlashBuffer[aAddress + index] = byte;
+ }
+
+ ret = aSize;
+
+exit:
+ return ret;
+ }
+
+ uint32_t otPlatFlashRead(uint32_t aAddress, uint8_t *aData, uint32_t aSize)
+ {
+ uint32_t ret = 0;
+
+ VerifyOrExit(aAddress < kFlashSize, ;);
+
+ memcpy(aData, sFlashBuffer + aAddress, aSize);
+ ret = aSize;
+
+exit:
+ return ret;
+ }
+
} // extern "C"
diff --git a/tests/unit/test_settings.cpp b/tests/unit/test_settings.cpp
new file mode 100644
index 000000000..c08219d04
--- /dev/null
+++ b/tests/unit/test_settings.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2016, 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 "test_util.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+enum
+{
+ kMaxStageDataLen = 32,
+};
+
+static uint8_t sWriteBuffer[kMaxStageDataLen];
+static int sWriteBufferLength;
+static otInstance sInstance;
+
+void TestSettingsInit(void)
+{
+ uint8_t index;
+
+ otPlatFlashInit();
+ otPlatSettingsInit(&sInstance);
+ otPlatSettingsWipe(&sInstance);
+
+ for (index = 0; index < kMaxStageDataLen; index++)
+ {
+ sWriteBuffer[index] = index;
+ }
+
+ sWriteBufferLength = index;
+}
+
+void TestSettingsAdd(void)
+{
+ uint8_t key = 7;
+ uint8_t readBuffer[kMaxStageDataLen];
+ int readBufferLength;
+
+ VerifyOrQuit(otPlatSettingsAdd(&sInstance, key, sWriteBuffer, sWriteBufferLength) == kThreadError_None,
+ "Settings::Add::Add Fail\n");
+ VerifyOrQuit(otPlatSettingsGet(&sInstance, key, 0, readBuffer, &readBufferLength) == kThreadError_None,
+ "Settings::Add::Get Fail\n");
+ VerifyOrQuit(!memcmp(readBuffer, sWriteBuffer, static_cast(sWriteBufferLength)),
+ "Settings::Add::Add Check Fail\n");
+}
+
+void TestSettingsDelete(void)
+{
+ uint8_t key = 8;
+ uint8_t readBuffer[kMaxStageDataLen];
+ int readBufferLength;
+
+ VerifyOrQuit(otPlatSettingsAdd(&sInstance, key, sWriteBuffer, sWriteBufferLength) == kThreadError_None,
+ "Settings::Delete::Add Fail\n");
+ VerifyOrQuit(otPlatSettingsGet(&sInstance, key, 0, readBuffer, &readBufferLength) == kThreadError_None,
+ "Settings::Delete::Get Fail\n");
+ VerifyOrQuit(!memcmp(readBuffer, sWriteBuffer, static_cast(sWriteBufferLength)),
+ "Settings::Delete::Add Check Fail\n");
+ VerifyOrQuit(otPlatSettingsDelete(&sInstance, key, -1) == kThreadError_None, "Settings::Delete::Delete Fail\n");
+ VerifyOrQuit(otPlatSettingsGet(&sInstance, key, 0, readBuffer, &readBufferLength) == kThreadError_NotFound,
+ "Settings::Delete::Get Fail\n");
+}
+
+void TestSettingsSet(void)
+{
+ uint8_t key = 9;
+ uint8_t readBuffer[kMaxStageDataLen];
+ int readBufferLength;
+
+ for (uint8_t index = 0; index < 2; index++)
+ {
+ VerifyOrQuit(otPlatSettingsAdd(&sInstance, key, sWriteBuffer, sWriteBufferLength) == kThreadError_None,
+ "Settings::Set::Add Fail\n");
+ }
+
+ VerifyOrQuit(otPlatSettingsSet(&sInstance, key, sWriteBuffer, sWriteBufferLength) == kThreadError_None,
+ "Settings::Set::Set Fail\n");
+ VerifyOrQuit(otPlatSettingsGet(&sInstance, key, 0, readBuffer, &readBufferLength) == kThreadError_None,
+ "Settings::Set::Get Fail\n");
+ VerifyOrQuit(!memcmp(readBuffer, sWriteBuffer, static_cast(sWriteBufferLength)),
+ "Settings::Set::Set Check Fail\n");
+}
+
+void TestSettingsSwap(void)
+{
+ ThreadError error = kThreadError_None;
+ uint8_t key = 11;
+ uint16_t index = 0;
+ uint8_t readBuffer[kMaxStageDataLen];
+ int readBufferLength = kMaxStageDataLen;
+
+ while (true)
+ {
+ error = otPlatSettingsAdd(&sInstance, key, sWriteBuffer, sWriteBufferLength);
+ VerifyOrQuit(error == kThreadError_None || error == kThreadError_NoBufs, "Settings::Swap::Add Fail\n");
+
+ if (error == kThreadError_NoBufs)
+ {
+ break;
+ }
+
+ index++;
+ }
+
+ VerifyOrQuit(otPlatSettingsDelete(&sInstance, key, 0) == kThreadError_None, "Settings::Swap::Delete Fail\n");
+ VerifyOrQuit(otPlatSettingsAdd(&sInstance, key, sWriteBuffer, sWriteBufferLength) == kThreadError_None,
+ "Settings::Swap::Add Fail after swap\n");
+ VerifyOrQuit(otPlatSettingsGet(&sInstance, key, index - 1, readBuffer, &readBufferLength) == kThreadError_None,
+ "Settings::Swap::Get Fail\n");
+ VerifyOrQuit(!memcmp(readBuffer, sWriteBuffer, static_cast(sWriteBufferLength)),
+ "Settings::Swap::Add and Swap Check Fail\n");
+}
+
+void RunSettingsTests(void)
+{
+ TestSettingsInit();
+ TestSettingsAdd();
+ TestSettingsDelete();
+ TestSettingsSet();
+ TestSettingsSwap();
+}
+
+#ifdef ENABLE_TEST_MAIN
+int main(void)
+{
+ RunSettingsTests();
+ printf("All tests passed\n");
+ return 0;
+}
+#endif
diff --git a/tests/unit/test_windows.cpp b/tests/unit/test_windows.cpp
index 94aa0d610..f2c18e38f 100644
--- a/tests/unit/test_windows.cpp
+++ b/tests/unit/test_windows.cpp
@@ -80,6 +80,9 @@ void test_packed2();
void test_packed_union();
void test_packed_enum();
+// test_settings.cpp
+void RunSettingsTests();
+
#pragma endregion
utAssertTrue s_AssertTrue;
@@ -131,5 +134,8 @@ namespace Thread
TEST_METHOD(test_packed2) { ::test_packed2(); }
TEST_METHOD(test_packed_union) { ::test_packed_union(); }
TEST_METHOD(test_packed_enum) { ::test_packed_enum(); }
+
+ // test_settings.cpp
+ TEST_METHOD(RunSettingsTests) { ::RunSettingsTests(); }
};
-}
\ No newline at end of file
+}