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 +}