Bring in EFR32 platform files
diff --git a/src/platform/EFR32/EFR32Config.cpp b/src/platform/EFR32/EFR32Config.cpp new file mode 100644 index 0000000..5b61bf7 --- /dev/null +++ b/src/platform/EFR32/EFR32Config.cpp
@@ -0,0 +1,641 @@ +/* + * + * Copyright (c) 2019 Nest Labs, Inc. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * Utilities for accessing persisted device configuration on + * platforms based on the Silicon Labs SDK. + */ + +#include <Weave/DeviceLayer/internal/WeaveDeviceLayerInternal.h> +#include <Weave/DeviceLayer/EFR32/EFR32Config.h> +#include <Weave/Core/WeaveEncoding.h> +#include <Weave/DeviceLayer/internal/testing/ConfigUnitTest.h> + +#include "nvm3.h" +#include "nvm3_hal_flash.h" +#include "FreeRTOS.h" + +namespace nl { +namespace Weave { +namespace DeviceLayer { +namespace Internal { + +// Two macros are provided to support the creation of the Silicon Labs NVM3 area and +// initialization data- NVM3_DEFINE_SECTION_STATIC_DATA() and NVM3_DEFINE_SECTION_INIT_DATA(). +// A linker section called 'name'_section is defined by NVM3_DEFINE_SECTION_STATIC_DATA(). +// The NVM3 area is placed at the top of the device FLASH section by the linker +// script file: openweave-efr32-bringup-MG12P.ld. An error is returned +// by nvm3_open() on alignment or size violation. + +// Local version of SDK macro (avoids uninitialized var compile error). +#define WEAVE_NVM3_DEFINE_SECTION_STATIC_DATA(name, nvmSize, cacheSize) \ + static nvm3_CacheEntry_t name##_cache[cacheSize]; \ + static uint8_t name##_nvm[nvmSize] SL_ATTRIBUTE_SECTION(STRINGIZE(name##_section)) + +// Local version of SDK macro (allows Weave to configure the maximum nvm3 object size and headroom). +#define WEAVE_NVM3_DEFINE_SECTION_INIT_DATA(name, maxObjectSize, repackHeadroom) \ + static nvm3_Init_t name = { \ + (nvm3_HalPtr_t)name##_nvm, \ + sizeof(name##_nvm), \ + name##_cache, \ + sizeof(name##_cache) / sizeof(nvm3_CacheEntry_t), \ + maxObjectSize, \ + repackHeadroom, \ + &nvm3_halFlashHandle, \ + } + +#define WEAVE_NVM3_REPACK_HEADROOM 64 // Threshold for User non-forced nvm3 flash repacking. + +static nvm3_Handle_t handle; + +// Declare NVM3 data area and cache. + +WEAVE_NVM3_DEFINE_SECTION_STATIC_DATA(weaveNvm3, + WEAVE_DEVICE_CONFIG_NVM3_NUM_FLASH_PAGES_FOR_STORAGE *FLASH_PAGE_SIZE, + WEAVE_DEVICE_CONFIG_NVM3_MAX_NUM_OBJECTS); + +WEAVE_NVM3_DEFINE_SECTION_INIT_DATA(weaveNvm3, WEAVE_DEVICE_CONFIG_NVM3_MAX_OBJECT_SIZE, WEAVE_NVM3_REPACK_HEADROOM); + +WEAVE_ERROR EFR32Config::Init() +{ + WEAVE_ERROR err; + bool needClose = false; + + err = MapNvm3Error(nvm3_open(&handle, &weaveNvm3)); + SuccessOrExit(err); + needClose = true; + +exit: + if (needClose) + { + nvm3_close(&handle); + } + + return err; +} + +WEAVE_ERROR EFR32Config::ReadConfigValue(Key key, bool &val) +{ + WEAVE_ERROR err; + bool needClose = false; + uint32_t objectType; + size_t dataLen; + bool tmpVal; + + VerifyOrExit(ValidConfigKey(key), err = WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND); // Verify key id. + + err = MapNvm3Error(nvm3_open(&handle, &weaveNvm3)); + SuccessOrExit(err); + needClose = true; + + // Get nvm3 object info. + err = MapNvm3Error(nvm3_getObjectInfo(&handle, key, &objectType, &dataLen)); + SuccessOrExit(err); + + // Read nvm3 bytes into tmp. + err = MapNvm3Error(nvm3_readData(&handle, key, &tmpVal, dataLen)); + SuccessOrExit(err); + val = tmpVal; + +exit: + if (needClose) + { + nvm3_close(&handle); + } + return err; +} + +WEAVE_ERROR EFR32Config::ReadConfigValue(Key key, uint32_t &val) +{ + WEAVE_ERROR err; + bool needClose = false; + uint32_t objectType; + size_t dataLen; + uint32_t tmpVal; + + VerifyOrExit(ValidConfigKey(key), err = WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND); // Verify key id. + + err = MapNvm3Error(nvm3_open(&handle, &weaveNvm3)); + SuccessOrExit(err); + needClose = true; + + // Get nvm3 object info. + err = MapNvm3Error(nvm3_getObjectInfo(&handle, key, &objectType, &dataLen)); + SuccessOrExit(err); + + // Read nvm3 bytes into tmp. + err = MapNvm3Error(nvm3_readData(&handle, key, &tmpVal, dataLen)); + SuccessOrExit(err); + val = tmpVal; + +exit: + if (needClose) + { + nvm3_close(&handle); + } + return err; +} + +WEAVE_ERROR EFR32Config::ReadConfigValue(Key key, uint64_t &val) +{ + WEAVE_ERROR err; + bool needClose = false; + uint32_t objectType; + size_t dataLen; + uint64_t tmpVal; + + VerifyOrExit(ValidConfigKey(key), err = WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND); // Verify key id. + + err = MapNvm3Error(nvm3_open(&handle, &weaveNvm3)); + SuccessOrExit(err); + needClose = true; + + // Get nvm3 object info. + err = MapNvm3Error(nvm3_getObjectInfo(&handle, key, &objectType, &dataLen)); + SuccessOrExit(err); + + // Read nvm3 bytes into tmp. + err = MapNvm3Error(nvm3_readData(&handle, key, &tmpVal, dataLen)); + SuccessOrExit(err); + val = tmpVal; + +exit: + if (needClose) + { + nvm3_close(&handle); + } + return err; +} + +WEAVE_ERROR EFR32Config::ReadConfigValueStr(Key key, char *buf, size_t bufSize, size_t &outLen) +{ + WEAVE_ERROR err; + bool needClose = false; + uint32_t objectType; + size_t dataLen; + + outLen = 0; + + VerifyOrExit(ValidConfigKey(key), err = WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND); // Verify key id. + + err = MapNvm3Error(nvm3_open(&handle, &weaveNvm3)); + SuccessOrExit(err); + needClose = true; + + // Get nvm3 object info. + err = MapNvm3Error(nvm3_getObjectInfo(&handle, key, &objectType, &dataLen)); + SuccessOrExit(err); + VerifyOrExit(dataLen > 0, err = WEAVE_ERROR_INVALID_STRING_LENGTH); + + if (buf != NULL) + { + // Read nvm3 bytes directly into the output buffer- check buffer is + // long enough to take the string (nvm3 string does not include the + // terminator char). + VerifyOrExit((bufSize > dataLen), err = WEAVE_ERROR_BUFFER_TOO_SMALL); + + err = MapNvm3Error(nvm3_readData(&handle, key, buf, dataLen)); + SuccessOrExit(err); + + outLen = ((dataLen == 1) && (buf[0] == 0)) ? 0 : dataLen; + buf[outLen] = 0; // Add the terminator char. + } + else + { + if (dataLen > 1) + { + outLen = dataLen; + } + else + { + // Read the first byte of the nvm3 string into a tmp var. + char firstByte; + err = MapNvm3Error(nvm3_readData(&handle, key, &firstByte, 1)); + SuccessOrExit(err); + + outLen = (firstByte == 0) ? 0 : dataLen; + } + } + +exit: + if (needClose) + { + nvm3_close(&handle); + } + return err; +} + +WEAVE_ERROR EFR32Config::ReadConfigValueBin(Key key, uint8_t *buf, size_t bufSize, size_t &outLen) +{ + WEAVE_ERROR err; + bool needClose = false; + uint32_t objectType; + size_t dataLen; + + outLen = 0; + + VerifyOrExit(ValidConfigKey(key), err = WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND); // Verify key id. + + err = MapNvm3Error(nvm3_open(&handle, &weaveNvm3)); + SuccessOrExit(err); + needClose = true; + + // Get nvm3 object info. + err = MapNvm3Error(nvm3_getObjectInfo(&handle, key, &objectType, &dataLen)); + SuccessOrExit(err); + VerifyOrExit(dataLen > 0, err = WEAVE_ERROR_INVALID_STRING_LENGTH); + + if (buf != NULL) + { + // Read nvm3 bytes directly into output buffer- check buffer is long + // enough to take the data. + VerifyOrExit((bufSize >= dataLen), err = WEAVE_ERROR_BUFFER_TOO_SMALL); + + err = MapNvm3Error(nvm3_readData(&handle, key, buf, dataLen)); + SuccessOrExit(err); + } + + outLen = dataLen; + +exit: + if (needClose) + { + nvm3_close(&handle); + } + return err; +} + +WEAVE_ERROR EFR32Config::ReadConfigValueCounter(uint8_t counterIdx, uint32_t &val) +{ + WEAVE_ERROR err; + bool needClose = false; + uint32_t tmpVal; + + Key key = kMinConfigKey_WeaveCounter + counterIdx; + VerifyOrExit(ValidConfigKey(key), err = WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND); // Verify key id. + + err = MapNvm3Error(nvm3_open(&handle, &weaveNvm3)); + SuccessOrExit(err); + needClose = true; + + // Read bytes into tmp. + err = MapNvm3Error(nvm3_readCounter(&handle, key, &tmpVal)); + SuccessOrExit(err); + val = tmpVal; + +exit: + if (needClose) + { + nvm3_close(&handle); + } + return err; +} + +WEAVE_ERROR EFR32Config::WriteConfigValue(Key key, bool val) +{ + WEAVE_ERROR err; + bool needClose = false; + + VerifyOrExit(ValidConfigKey(key), err = WEAVE_ERROR_INVALID_ARGUMENT); // Verify key id. + + err = MapNvm3Error(nvm3_open(&handle, &weaveNvm3)); + SuccessOrExit(err); + needClose = true; + + err = MapNvm3Error(nvm3_writeData(&handle, key, &val, sizeof(val))); + SuccessOrExit(err); + +exit: + if (needClose) + { + nvm3_close(&handle); + } + return err; +} + +WEAVE_ERROR EFR32Config::WriteConfigValue(Key key, uint32_t val) +{ + WEAVE_ERROR err; + bool needClose = false; + + VerifyOrExit(ValidConfigKey(key), err = WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND); // Verify key id. + + err = MapNvm3Error(nvm3_open(&handle, &weaveNvm3)); + SuccessOrExit(err); + needClose = true; + + err = MapNvm3Error(nvm3_writeData(&handle, key, &val, sizeof(val))); + SuccessOrExit(err); + +exit: + if (needClose) + { + nvm3_close(&handle); + } + return err; +} + +WEAVE_ERROR EFR32Config::WriteConfigValue(Key key, uint64_t val) +{ + WEAVE_ERROR err; + bool needClose = false; + + VerifyOrExit(ValidConfigKey(key), err = WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND); // Verify key id. + + err = MapNvm3Error(nvm3_open(&handle, &weaveNvm3)); + SuccessOrExit(err); + needClose = true; + + err = MapNvm3Error(nvm3_writeData(&handle, key, &val, sizeof(val))); + SuccessOrExit(err); + +exit: + if (needClose) + { + nvm3_close(&handle); + } + return err; +} + +WEAVE_ERROR EFR32Config::WriteConfigValueStr(Key key, const char *str) +{ + return WriteConfigValueStr(key, str, (str != NULL) ? strlen(str) : 0); +} + +WEAVE_ERROR EFR32Config::WriteConfigValueStr(Key key, const char *str, size_t strLen) +{ + WEAVE_ERROR err; + bool needClose = false; + + VerifyOrExit(ValidConfigKey(key), err = WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND); // Verify key id. + + err = MapNvm3Error(nvm3_open(&handle, &weaveNvm3)); + SuccessOrExit(err); + needClose = true; + + if (str != NULL) + { + // Write the string to nvm3 without the terminator char (apart from + // empty strings where only the terminator char is stored in nvm3). + err = MapNvm3Error(nvm3_writeData(&handle, key, str, (strLen > 0) ? strLen : 1)); + SuccessOrExit(err); + } + else + { + nvm3_deleteObject(&handle, key); // no error checking here. + } + +exit: + if (needClose) + { + nvm3_close(&handle); + } + + return err; +} + +WEAVE_ERROR EFR32Config::WriteConfigValueBin(Key key, const uint8_t *data, size_t dataLen) +{ + WEAVE_ERROR err; + bool needClose = false; + + VerifyOrExit(ValidConfigKey(key), err = WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND); // Verify key id. + + err = MapNvm3Error(nvm3_open(&handle, &weaveNvm3)); + SuccessOrExit(err); + needClose = true; + + if (data != NULL) + { + if (dataLen > 0) + { + // Write the binary data to nvm3. + err = MapNvm3Error(nvm3_writeData(&handle, key, data, dataLen)); + SuccessOrExit(err); + } + } + else + { + nvm3_deleteObject(&handle, key); // no error checking here. + } + +exit: + if (needClose) + { + nvm3_close(&handle); + } + return err; +} + +WEAVE_ERROR EFR32Config::WriteConfigValueCounter(uint8_t counterIdx, uint32_t val) +{ + WEAVE_ERROR err; + bool needClose = false; + + Key key = kMinConfigKey_WeaveCounter + counterIdx; + VerifyOrExit(ValidConfigKey(key), err = WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND); // Verify key id. + + err = MapNvm3Error(nvm3_open(&handle, &weaveNvm3)); + SuccessOrExit(err); + needClose = true; + + err = MapNvm3Error(nvm3_writeCounter(&handle, key, val)); + SuccessOrExit(err); + +exit: + if (needClose) + { + nvm3_close(&handle); + } + return err; +} + +WEAVE_ERROR EFR32Config::ClearConfigValue(Key key) +{ + WEAVE_ERROR err; + bool needClose = false; + + err = MapNvm3Error(nvm3_open(&handle, &weaveNvm3)); + SuccessOrExit(err); + needClose = true; + + // Delete the nvm3 object with the given key id. + err = MapNvm3Error(nvm3_deleteObject(&handle, key)); + SuccessOrExit(err); + +exit: + if (needClose) + { + nvm3_close(&handle); + } + return err; +} + +bool EFR32Config::ConfigValueExists(Key key) +{ + WEAVE_ERROR err; + bool needClose = false; + uint32_t objectType; + size_t dataLen; + + err = MapNvm3Error(nvm3_open(&handle, &weaveNvm3)); + SuccessOrExit(err); + needClose = true; + + // Find object with key id. + err = MapNvm3Error(nvm3_getObjectInfo(&handle, key, &objectType, &dataLen)); + +exit: + if (needClose) + { + nvm3_close(&handle); + } + return (err == WEAVE_NO_ERROR); +} + +WEAVE_ERROR EFR32Config::FactoryResetConfig(void) +{ + // Deletes all nvm3 'Config' type objects. + // Note- 'Factory' and 'Counter' type nvm3 objects are NOT deleted. + + WEAVE_ERROR err; + + // Iterate over all the Weave Config nvm3 records and delete each one... + err = ForEachRecord(kMinConfigKey_WeaveConfig, kMaxConfigKey_WeaveConfig, false, + [](const Key &nvm3Key, const size_t &length) -> WEAVE_ERROR { + WEAVE_ERROR err2; + + err2 = ClearConfigValue(nvm3Key); + SuccessOrExit(err2); + + exit: + return err2; + }); + + // Return success at end of iterations. + if (err == WEAVE_END_OF_INPUT) + { + err = WEAVE_NO_ERROR; + } + + return err; +} + +WEAVE_ERROR EFR32Config::MapNvm3Error(Ecode_t nvm3Res) +{ + WEAVE_ERROR err; + + switch (nvm3Res) + { + case ECODE_NVM3_OK: + err = WEAVE_NO_ERROR; + break; + case ECODE_NVM3_ERR_KEY_NOT_FOUND: + err = WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND; + break; + default: + err = (nvm3Res & 0xFF) + WEAVE_DEVICE_CONFIG_EFR32_NVM3_ERROR_MIN; + break; + } + + return err; +} + +WEAVE_ERROR EFR32Config::ForEachRecord(Key firstNvm3Key, Key lastNvm3Key, bool addNewRecord, ForEachRecordFunct funct) +{ + // Iterates through the specified range of nvm3 object key ids. + // Invokes the callers CB function when appropriate. + + WEAVE_ERROR err = WEAVE_NO_ERROR; + + for (Key nvm3Key = firstNvm3Key; nvm3Key <= lastNvm3Key; ++nvm3Key) + { + Ecode_t nvm3Res; + uint32_t objectType; + size_t dataLen; + + // Open nvm3 handle for reading on each iteration. + err = MapNvm3Error(nvm3_open(&handle, &weaveNvm3)); + SuccessOrExit(err); + + // Find nvm3 object with current nvm3 iteration key. + nvm3Res = nvm3_getObjectInfo(&handle, nvm3Key, &objectType, &dataLen); + switch (nvm3Res) + { + case ECODE_NVM3_OK: + if (!addNewRecord) + { + // Invoke the caller's function + // (for retrieve,store,delete,enumerate GroupKey operations). + err = funct(nvm3Key, dataLen); + } + break; + case ECODE_NVM3_ERR_KEY_NOT_FOUND: + if (addNewRecord) + { + // Invoke caller's function + // (for add GroupKey operation). + err = funct(nvm3Key, dataLen); + } + break; + default: + err = MapNvm3Error(nvm3Res); + break; + } + + SuccessOrExit(err); + } + +exit: + // Always close handle. + nvm3_close(&handle); + + return err; +} + +bool EFR32Config::ValidConfigKey(Key key) +{ + // Returns true if the key is in the valid Weave Config nvm3 key range. + + if ((key >= kMinConfigKey_WeaveFactory) && (key <= kMaxConfigKey_WeaveCounter)) + { + return true; + } + + return false; +} + +void EFR32Config::RunConfigUnitTest() +{ + // Run common unit test. + ::nl::Weave::DeviceLayer::Internal::RunConfigUnitTest<EFR32Config>(); +} + +void EFR32Config::RepackNvm3Flash(void) +{ + // Repack nvm3 flash if nvm3 space < headroom threshold. + // Note- checking periodically during idle periods should prevent + // forced repack events on any write operation. + nvm3_repack(&handle); +} + +} // namespace Internal +} // namespace DeviceLayer +} // namespace Weave +} // namespace nl