blob: 09e3393e07b1b14b32a3c569fa74953f0246cd8a [file] [log] [blame]
/*
*
* Copyright (c) 2020-2022 Project CHIP Authors
* Copyright (c) 2019-2020 Google LLC.
* Copyright (c) 2018 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 interacting with multiple file partitions and maps
* key-value config calls to the correct partition.
*/
#include <platform/internal/CHIPDeviceLayerInternal.h>
#include <platform/internal/testing/ConfigUnitTest.h>
#include <lib/core/CHIPEncoding.h>
#include <lib/support/CodeUtils.h>
#include <platform/cc13x2_26x2/CC13X2_26X2Config.h>
#include <ti/common/nv/nvintf.h>
#include <ti/common/nv/nvocmp.h>
namespace chip {
namespace DeviceLayer {
namespace Internal {
// *** CAUTION ***: Changing the names or namespaces of these values will *break* existing devices.
/* itemID and subID are limited to 10 bits, even though their types are uint16_t */
// Keys stored in the Chip-factory namespace
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_SerialNum = { { .systemID = kCC13X2_26X2ChipFactory_Sysid,
.itemID = 0x0001 } };
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_MfrDeviceId = { { .systemID = kCC13X2_26X2ChipFactory_Sysid,
.itemID = 0x0002 } };
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_MfrDeviceCert = { { .systemID = kCC13X2_26X2ChipFactory_Sysid,
.itemID = 0x0003 } };
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_MfrDeviceICACerts = { { .systemID = kCC13X2_26X2ChipFactory_Sysid,
.itemID = 0x0004 } };
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_MfrDevicePrivateKey = { { .systemID = kCC13X2_26X2ChipFactory_Sysid,
.itemID = 0x0005 } };
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_HardwareVersion = { { .systemID = kCC13X2_26X2ChipFactory_Sysid,
.itemID = 0x0006 } };
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_ManufacturingDate = { { .systemID = kCC13X2_26X2ChipFactory_Sysid,
.itemID = 0x0007 } };
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_SetupPinCode = { { .systemID = kCC13X2_26X2ChipFactory_Sysid,
.itemID = 0x0008 } };
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_SetupDiscriminator = { { .systemID = kCC13X2_26X2ChipFactory_Sysid,
.itemID = 0x0009 } };
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_Spake2pIterationCount = { { .systemID = kCC13X2_26X2ChipFactory_Sysid,
.itemID = 0x000a } };
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_Spake2pSalt = { { .systemID = kCC13X2_26X2ChipFactory_Sysid,
.itemID = 0x000b } };
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_Spake2pVerifier = { { .systemID = kCC13X2_26X2ChipFactory_Sysid,
.itemID = 0x000c } };
// Keys stored in the Chip-config namespace
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_ServiceConfig = { { .systemID = kCC13X2_26X2ChipConfig_Sysid,
.itemID = 0x0012 } };
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_PairedAccountId = { { .systemID = kCC13X2_26X2ChipConfig_Sysid,
.itemID = 0x0013 } };
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_ServiceId = { { .systemID = kCC13X2_26X2ChipConfig_Sysid,
.itemID = 0x0014 } };
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_LastUsedEpochKeyId = { { .systemID = kCC13X2_26X2ChipConfig_Sysid,
.itemID = 0x0017 } };
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_FailSafeArmed = { { .systemID = kCC13X2_26X2ChipConfig_Sysid,
.itemID = 0x0018 } };
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_WiFiStationSecType = { { .systemID = kCC13X2_26X2ChipConfig_Sysid,
.itemID = 0x0019 } };
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_RegulatoryLocation = { { .systemID = kCC13X2_26X2ChipConfig_Sysid,
.itemID = 0x001a } };
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_CountryCode = { { .systemID = kCC13X2_26X2ChipConfig_Sysid,
.itemID = 0x001b } };
// itemID 0x001c is unused (used to be breadcrumb).
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_UniqueId = { { .systemID = kCC13X2_26X2ChipConfig_Sysid,
.itemID = 0x001d } };
/* Internal for the KVS interface. */
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_KVS_key = { { .systemID = kCC13X2_26X2ChipFactory_Sysid,
.itemID = 0x001d } };
const CC13X2_26X2Config::Key CC13X2_26X2Config::kConfigKey_KVS_value = { { .systemID = kCC13X2_26X2ChipFactory_Sysid,
.itemID = 0x001e } };
/* Static local variables */
static NVINTF_nvFuncts_t sNvoctpFps = { 0 };
CHIP_ERROR CC13X2_26X2Config::Init()
{
CHIP_ERROR err = CHIP_NO_ERROR;
/* Load NVOCMP function pointers, extended API */
NVOCMP_loadApiPtrsExt(&sNvoctpFps);
/* Initialize NVOCMP */
sNvoctpFps.initNV(NULL);
return err;
}
CHIP_ERROR CC13X2_26X2Config::ReadConfigValue(Key key, bool & val)
{
CHIP_ERROR ret;
size_t ignore;
uint8_t localVal;
ret = ReadConfigValueBin(key, &localVal, sizeof(localVal), ignore);
// reference CC13X2_26X2Config::WriteConfigValue(Key key, bool val) for storage of boolean values
val = (localVal != 0);
return ret;
}
CHIP_ERROR CC13X2_26X2Config::ReadConfigValue(Key key, uint32_t & val)
{
size_t ignore;
return ReadConfigValueBin(key, (uint8_t *) &val, sizeof(val), ignore);
}
CHIP_ERROR CC13X2_26X2Config::ReadConfigValue(Key key, uint64_t & val)
{
size_t ignore;
return ReadConfigValueBin(key, (uint8_t *) &val, sizeof(val), ignore);
}
CHIP_ERROR CC13X2_26X2Config::ReadConfigValueStr(Key key, char * buf, size_t bufSize, size_t & outLen)
{
return ReadConfigValueBin(key, (uint8_t *) buf, bufSize, outLen);
}
CHIP_ERROR CC13X2_26X2Config::ReadConfigValueBin(Key key, uint8_t * buf, size_t bufSize, size_t & outLen)
{
CHIP_ERROR err = CHIP_NO_ERROR;
size_t len;
len = sNvoctpFps.getItemLen(key.nvID);
VerifyOrExit(len > 0, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); // key not found
VerifyOrExit(len <= bufSize, err = CHIP_ERROR_BUFFER_TOO_SMALL);
VerifyOrExit(sNvoctpFps.readItem(key.nvID, 0, (uint16_t) len, buf) == NVINTF_SUCCESS,
err = CHIP_ERROR_PERSISTED_STORAGE_FAILED);
outLen = len;
exit:
return err;
}
/* Iterate through the key range to find a key that matches. */
static uint8_t FindKVSSubID(const char * key, uint16_t & subID)
{
char key_scratch[32]; // 32 characters seems large enough for a key
NVINTF_nvProxy_t nvProxy = { 0 };
uint8_t status = NVINTF_SUCCESS;
nvProxy.sysid = CC13X2_26X2Config::kConfigKey_KVS_key.nvID.systemID;
nvProxy.itemid = CC13X2_26X2Config::kConfigKey_KVS_key.nvID.itemID;
nvProxy.buffer = key_scratch;
nvProxy.len = sizeof(key_scratch);
nvProxy.flag = NVINTF_DOSTART | NVINTF_DOITMID | NVINTF_DOREAD;
intptr_t lock_key = sNvoctpFps.lockNV();
do
{
memset(key_scratch, 0, sizeof(key_scratch));
status = sNvoctpFps.doNext(&nvProxy);
if (NVINTF_SUCCESS != status)
{
break;
}
if (0 == strcmp(key, (char *) nvProxy.buffer))
{
subID = nvProxy.subid;
break;
}
} while (NVINTF_SUCCESS == status);
sNvoctpFps.unlockNV(lock_key);
return status;
}
CHIP_ERROR CC13X2_26X2Config::ReadKVS(const char * key, void * value, size_t value_size, size_t * read_bytes_size,
size_t offset_bytes)
{
CHIP_ERROR err = CHIP_NO_ERROR;
NVINTF_itemID_t val_item = CC13X2_26X2Config::kConfigKey_KVS_value.nvID;
uint16_t subID;
size_t len;
uint16_t read_len;
VerifyOrExit(FindKVSSubID(key, subID) == NVINTF_SUCCESS, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
val_item.subID = subID;
len = sNvoctpFps.getItemLen(val_item);
VerifyOrExit(len > 0, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND); // key not found
if ((offset_bytes + value_size) > len)
{
// trying to read up to the end of the element
read_len = len - offset_bytes;
}
else
{
read_len = value_size;
}
VerifyOrExit(sNvoctpFps.readItem(val_item, (uint16_t) offset_bytes, read_len, value) == NVINTF_SUCCESS,
err = CHIP_ERROR_PERSISTED_STORAGE_FAILED);
if (read_bytes_size)
{
*read_bytes_size = read_len;
}
exit:
return err;
}
CHIP_ERROR CC13X2_26X2Config::WriteConfigValue(Key key, bool val)
{
uint8_t localVal = val ? 1 : 0;
return WriteConfigValueBin(key, (const uint8_t *) &localVal, sizeof(localVal));
}
CHIP_ERROR CC13X2_26X2Config::WriteConfigValue(Key key, uint32_t val)
{
return WriteConfigValueBin(key, (const uint8_t *) &val, sizeof(val));
}
CHIP_ERROR CC13X2_26X2Config::WriteConfigValue(Key key, uint64_t val)
{
return WriteConfigValueBin(key, (const uint8_t *) &val, sizeof(val));
}
CHIP_ERROR CC13X2_26X2Config::WriteConfigValueStr(Key key, const char * str)
{
size_t strLen = strlen(str);
return WriteConfigValueBin(key, (const uint8_t *) str, strLen);
}
CHIP_ERROR CC13X2_26X2Config::WriteConfigValueStr(Key key, const char * str, size_t strLen)
{
return WriteConfigValueBin(key, (const uint8_t *) str, strLen);
}
CHIP_ERROR CC13X2_26X2Config::WriteKVS(const char * key, const void * value, size_t value_size)
{
CHIP_ERROR err = CHIP_NO_ERROR;
uint16_t subID;
if (FindKVSSubID(key, subID) == NVINTF_SUCCESS)
{
NVINTF_itemID_t val_item = CC13X2_26X2Config::kConfigKey_KVS_value.nvID;
// key already exists, update value
val_item.subID = subID;
VerifyOrExit(sNvoctpFps.updateItem(val_item, (uint16_t) value_size, (void *) value) == NVINTF_SUCCESS,
err = CHIP_ERROR_PERSISTED_STORAGE_FAILED);
}
else
{
// key does not exist, likely case
intptr_t lock_key = sNvoctpFps.lockNV();
NVINTF_itemID_t key_item = CC13X2_26X2Config::kConfigKey_KVS_key.nvID;
NVINTF_itemID_t val_item = CC13X2_26X2Config::kConfigKey_KVS_value.nvID;
/* Iterate through the subID range to find an unused subID in the
* keyspace. SubID is a 10 bit value, reference
* `<simplelink_sdk>/source/ti/common/nv/nvocmp.c:MVOCMP_MAXSUBID`.
*/
for (uint16_t i = 0; i < 0x3FF; i++)
{
key_item.subID = i;
if (sNvoctpFps.getItemLen(key_item) == 0U)
{
val_item.subID = i;
break;
}
}
// write they key item
if (sNvoctpFps.writeItem(key_item, (uint16_t) strlen(key), (void *) key) == NVINTF_SUCCESS)
{
if (sNvoctpFps.writeItem(val_item, (uint16_t) value_size, (void *) value) != NVINTF_SUCCESS)
{
// try to delete the key item
sNvoctpFps.deleteItem(key_item);
err = CHIP_ERROR_PERSISTED_STORAGE_FAILED;
}
}
else
{
err = CHIP_ERROR_PERSISTED_STORAGE_FAILED;
}
sNvoctpFps.unlockNV(lock_key);
}
exit:
return err;
}
CHIP_ERROR CC13X2_26X2Config::WriteConfigValueBin(Key key, const uint8_t * data, size_t dataLen)
{
CHIP_ERROR err = CHIP_NO_ERROR;
VerifyOrExit(sNvoctpFps.writeItem(key.nvID, (uint16_t) dataLen, (void *) data) == NVINTF_SUCCESS,
err = CHIP_ERROR_PERSISTED_STORAGE_FAILED);
exit:
return err;
}
CHIP_ERROR CC13X2_26X2Config::ClearKVS(const char * key)
{
CHIP_ERROR err = CHIP_NO_ERROR;
uint16_t subID;
NVINTF_itemID_t key_item = CC13X2_26X2Config::kConfigKey_KVS_key.nvID;
NVINTF_itemID_t val_item = CC13X2_26X2Config::kConfigKey_KVS_value.nvID;
if (FindKVSSubID(key, subID) == NVINTF_SUCCESS)
{
key_item.subID = subID;
val_item.subID = subID;
// delete the value item
if (sNvoctpFps.deleteItem(val_item) != NVINTF_SUCCESS)
{
err = CHIP_ERROR_PERSISTED_STORAGE_FAILED;
}
// delete the key item
if (sNvoctpFps.deleteItem(key_item) != NVINTF_SUCCESS)
{
err = CHIP_ERROR_PERSISTED_STORAGE_FAILED;
}
}
return err;
}
CHIP_ERROR CC13X2_26X2Config::ClearConfigValue(Key key)
{
CHIP_ERROR err = CHIP_NO_ERROR;
VerifyOrExit(sNvoctpFps.deleteItem(key.nvID) == NVINTF_SUCCESS, err = CHIP_ERROR_PERSISTED_STORAGE_FAILED);
exit:
return err;
}
bool CC13X2_26X2Config::ConfigValueExists(Key key)
{
/* 0 is an invalid length for an item, getting a length of 0 means there is no item */
return (0 != sNvoctpFps.getItemLen(key.nvID));
}
CHIP_ERROR CC13X2_26X2Config::FactoryResetConfig(void)
{
CHIP_ERROR err = CHIP_NO_ERROR;
NVINTF_nvProxy_t nvProxy = { 0 };
uint8_t status = NVINTF_SUCCESS;
intptr_t key = sNvoctpFps.lockNV();
/* Setup doNext call */
nvProxy.sysid = kCC13X2_26X2ChipConfig_Sysid;
nvProxy.flag = NVINTF_DOSTART | NVINTF_DOSYSID | NVINTF_DODELETE;
/* Lock and wipe all items with sysid TIOP */
do
{
status = sNvoctpFps.doNext(&nvProxy);
} while (NVINTF_SUCCESS == status);
/* check we ran out of elements */
VerifyOrExit(status != NVINTF_NOTFOUND, err = CHIP_ERROR_PERSISTED_STORAGE_FAILED);
/* Setup doNext call. Sysid is the same here, but it doesn't necessarily have to be. Matching POSIX impl */
nvProxy.sysid = kCC13X2_26X2ChipFactory_Sysid;
nvProxy.flag = NVINTF_DOSTART | NVINTF_DOSYSID | NVINTF_DODELETE;
/* Lock and wipe all items with sysid TIOP */
do
{
status = sNvoctpFps.doNext(&nvProxy);
} while (NVINTF_SUCCESS == status);
/* check we ran out of elements */
VerifyOrExit(status != NVINTF_NOTFOUND, err = CHIP_ERROR_PERSISTED_STORAGE_FAILED);
/* Setup doNext call. Sysid is the same here, but it doesn't necessarily have to be. Matching POSIX impl */
nvProxy.sysid = kCC13X2_26X2ChipCounters_Sysid;
nvProxy.flag = NVINTF_DOSTART | NVINTF_DOSYSID | NVINTF_DODELETE;
/* Lock and wipe all items with sysid TIOP */
do
{
status = sNvoctpFps.doNext(&nvProxy);
} while (NVINTF_SUCCESS == status);
/* check we ran out of elements */
VerifyOrExit(status != NVINTF_NOTFOUND, err = CHIP_ERROR_PERSISTED_STORAGE_FAILED);
exit:
sNvoctpFps.unlockNV(key);
if (err == CHIP_NO_ERROR)
{
/* force compaction */
sNvoctpFps.compactNV(0);
}
return err;
}
void CC13X2_26X2Config::RunConfigUnitTest()
{
// Run common unit test.
::chip::DeviceLayer::Internal::RunConfigUnitTest<CC13X2_26X2Config>();
}
} // namespace Internal
} // namespace DeviceLayer
} // namespace chip