blob: 6510c4cf92cb5f93eec241fc9ad0cf4611b9eb76 [file] [log] [blame]
/*
* Copyright (c) 2022, 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 strain 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 "configuration.hpp"
#include "platform-posix.h"
#include <openthread/platform/radio.h>
#include "lib/platform/exit_code.h"
#include "utils/parse_cmdline.hpp"
#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE && !OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE
#error \
"OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE is required for OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE"
#endif
#if OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE
namespace ot {
namespace Posix {
otError Configuration::SetRegion(uint16_t aRegionCode)
{
otError error = OT_ERROR_NONE;
Power::Domain domain;
if (GetDomain(aRegionCode, domain) != OT_ERROR_NONE)
{
// If failed to find the domain for the region, use the world wide region as the default region.
VerifyOrExit(GetDomain(kRegionCodeWorldWide, domain) == OT_ERROR_NONE, error = OT_ERROR_FAILED);
}
SuccessOrExit(error = UpdateChannelMasks(domain));
#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
SuccessOrExit(error = UpdateTargetPower(domain));
SuccessOrExit(error = UpdateCalibratedPower());
#endif
mRegionCode = aRegionCode;
exit:
if (error == OT_ERROR_NONE)
{
otLogInfoPlat("Successfully set region \"%c%c\"", (aRegionCode >> 8) & 0xff, (aRegionCode & 0xff));
}
else
{
otLogCritPlat("Failed to set region \"%c%c\": %s", (aRegionCode >> 8) & 0xff, (aRegionCode & 0xff),
otThreadErrorToString(error));
}
return error;
}
otError Configuration::GetDomain(uint16_t aRegionCode, Power::Domain &aDomain)
{
otError error = OT_ERROR_NOT_FOUND;
int iterator = 0;
char value[kMaxValueSize];
char *str;
char *psave;
while (mProductConfigFile.Get(kKeyRegionDomainMapping, iterator, value, sizeof(value)) == OT_ERROR_NONE)
{
if ((str = strtok_r(value, kCommaDelimiter, &psave)) == nullptr)
{
continue;
}
while ((str = strtok_r(nullptr, kCommaDelimiter, &psave)) != nullptr)
{
if ((strlen(str) == 2) && (StringToRegionCode(str) == aRegionCode))
{
ExitNow(error = aDomain.Set(value));
}
}
}
exit:
if (error != OT_ERROR_NONE)
{
otLogCritPlat("Failed to get power domain: %s", otThreadErrorToString(error));
}
return error;
}
otError Configuration::GetChannelMask(const char *aKey, const Power::Domain &aDomain, uint32_t &aChannelMask)
{
otError error = OT_ERROR_NOT_FOUND;
int iterator = 0;
char value[kMaxValueSize];
char *str;
Power::Domain domain;
uint32_t channelMask;
char *psave;
while (mProductConfigFile.Get(aKey, iterator, value, sizeof(value)) == OT_ERROR_NONE)
{
if (((str = strtok_r(value, kCommaDelimiter, &psave)) == nullptr) || (aDomain != str))
{
continue;
}
if ((str = strtok_r(nullptr, kCommaDelimiter, &psave)) != nullptr)
{
SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint32(str, channelMask));
aChannelMask = channelMask;
error = OT_ERROR_NONE;
break;
}
}
exit:
return error;
}
otError Configuration::UpdateChannelMasks(const Power::Domain &aDomain)
{
otError error = OT_ERROR_NONE;
if (mProductConfigFile.HasKey(kKeySupportedChannelMask))
{
SuccessOrExit(error = GetChannelMask(kKeySupportedChannelMask, aDomain, mSupportedChannelMask));
}
if (mProductConfigFile.HasKey(kKeySupportedChannelMask))
{
SuccessOrExit(error = GetChannelMask(kKeyPreferredChannelMask, aDomain, mPreferredChannelMask));
}
exit:
if (error != OT_ERROR_NONE)
{
otLogCritPlat("Failed to update channel mask: %s", otThreadErrorToString(error));
}
return error;
}
#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
otError Configuration::UpdateTargetPower(const Power::Domain &aDomain)
{
otError error = OT_ERROR_NONE;
int iterator = 0;
Power::TargetPower targetPower;
VerifyOrExit(mProductConfigFile.HasKey(kKeyTargetPower));
while (GetNextTargetPower(aDomain, iterator, targetPower) == OT_ERROR_NONE)
{
otLogInfoPlat("Update target power: %s\r\n", targetPower.ToString().AsCString());
for (uint8_t ch = targetPower.GetChannelStart(); ch <= targetPower.GetChannelEnd(); ch++)
{
SuccessOrExit(error = otPlatRadioSetChannelTargetPower(gInstance, ch, targetPower.GetTargetPower()));
}
}
exit:
if (error != OT_ERROR_NONE)
{
otLogCritPlat("Failed to update target power: %s", otThreadErrorToString(error));
}
return error;
}
otError Configuration::UpdateCalibratedPower(void)
{
otError error = OT_ERROR_NONE;
int iterator = 0;
char value[kMaxValueSize];
Power::CalibratedPower calibratedPower;
ConfigFile *calibrationFile = &mFactoryConfigFile;
// If the distribution of output power is large, the factory needs to measure the power calibration data
// for each device individually, and the power calibration data will be written to the factory config file.
// Otherwise, the power calibration data can be pre-configured in the product config file.
if (calibrationFile->Get(kKeyCalibratedPower, iterator, value, sizeof(value)) != OT_ERROR_NONE)
{
calibrationFile = &mProductConfigFile;
}
VerifyOrExit(calibrationFile->HasKey(kKeyCalibratedPower));
SuccessOrExit(error = otPlatRadioClearCalibratedPowers(gInstance));
iterator = 0;
while (calibrationFile->Get(kKeyCalibratedPower, iterator, value, sizeof(value)) == OT_ERROR_NONE)
{
SuccessOrExit(error = calibratedPower.FromString(value));
otLogInfoPlat("Update calibrated power: %s\r\n", calibratedPower.ToString().AsCString());
for (uint8_t ch = calibratedPower.GetChannelStart(); ch <= calibratedPower.GetChannelEnd(); ch++)
{
SuccessOrExit(error = otPlatRadioAddCalibratedPower(gInstance, ch, calibratedPower.GetActualPower(),
calibratedPower.GetRawPowerSetting().GetData(),
calibratedPower.GetRawPowerSetting().GetLength()));
}
}
exit:
if (error != OT_ERROR_NONE)
{
otLogCritPlat("Failed to update calibrated power table: %s", otThreadErrorToString(error));
}
return error;
}
otError Configuration::GetNextTargetPower(const Power::Domain &aDomain,
int &aIterator,
Power::TargetPower &aTargetPower)
{
otError error = OT_ERROR_NOT_FOUND;
char value[kMaxValueSize];
char *domain;
char *psave;
while (mProductConfigFile.Get(kKeyTargetPower, aIterator, value, sizeof(value)) == OT_ERROR_NONE)
{
if (((domain = strtok_r(value, kCommaDelimiter, &psave)) == nullptr) || (aDomain != domain))
{
continue;
}
if ((error = aTargetPower.FromString(psave)) != OT_ERROR_NONE)
{
otLogCritPlat("Failed to read target power: %s", otThreadErrorToString(error));
}
break;
}
return error;
}
#endif // OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
bool Configuration::IsValid(void) const
{
bool ret;
VerifyOrExit(mProductConfigFile.DoesExist(), ret = false);
ret = mProductConfigFile.HasKey(kKeySupportedChannelMask) || mProductConfigFile.HasKey(kKeyPreferredChannelMask) ||
mProductConfigFile.HasKey(kKeyRegionDomainMapping);
VerifyOrExit(!ret);
#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
ret = (mProductConfigFile.HasKey(kKeyCalibratedPower) || mProductConfigFile.HasKey(kKeyTargetPower));
#endif
exit:
return ret;
}
} // namespace Posix
} // namespace ot
#endif // OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE