From 2a502abb88ec4854c246b94b3317bd85cac1a116 Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Mon, 18 Sep 2023 22:17:54 +0800 Subject: [PATCH] [posix] add channel mask configurations to configuration file (#9391) This commit allows developers to set the preferred channel mask and the supported channel mask in the configuration file. The posix platform selects channel masks from the configuration file based on the region code when the region code is updated. --- src/posix/platform/CMakeLists.txt | 2 +- src/posix/platform/config_file.cpp | 31 ++- src/posix/platform/config_file.hpp | 27 ++- .../{power_updater.cpp => configuration.cpp} | 218 +++++++++++++----- .../{power_updater.hpp => configuration.hpp} | 69 ++++-- src/posix/platform/openthread-posix-config.h | 10 + src/posix/platform/openthread.conf.example | 11 + src/posix/platform/power.cpp | 7 +- src/posix/platform/radio.cpp | 76 ++++-- tests/scripts/expect/_common.exp | 3 + tests/scripts/expect/posix-channel-mask.exp | 64 +++++ .../scripts/expect/posix-max-power-table.exp | 9 + 12 files changed, 423 insertions(+), 104 deletions(-) rename src/posix/platform/{power_updater.cpp => configuration.cpp} (57%) rename src/posix/platform/{power_updater.hpp => configuration.hpp} (58%) create mode 100644 tests/scripts/expect/posix-channel-mask.exp diff --git a/src/posix/platform/CMakeLists.txt b/src/posix/platform/CMakeLists.txt index fe66bebca..88e8f7e73 100644 --- a/src/posix/platform/CMakeLists.txt +++ b/src/posix/platform/CMakeLists.txt @@ -105,6 +105,7 @@ add_library(openthread-posix alarm.cpp backbone.cpp backtrace.cpp + configuration.cpp config_file.cpp daemon.cpp entropy.cpp @@ -118,7 +119,6 @@ add_library(openthread-posix multicast_routing.cpp netif.cpp power.cpp - power_updater.cpp radio.cpp radio_url.cpp resolver.cpp diff --git a/src/posix/platform/config_file.cpp b/src/posix/platform/config_file.cpp index dd78c9716..c8a7dddb4 100644 --- a/src/posix/platform/config_file.cpp +++ b/src/posix/platform/config_file.cpp @@ -50,15 +50,25 @@ ConfigFile::ConfigFile(const char *aFilePath) VerifyOrDie(strlen(mFilePath) + strlen(kSwapSuffix) < kFileNameMaxSize, OT_EXIT_FAILURE); } -otError ConfigFile::Get(const char *aKey, int &aIterator, char *aValue, int aValueLength) +bool ConfigFile::HasKey(const char *aKey) const +{ + int iterator = 0; + + return (Get(aKey, iterator, nullptr, 0) == OT_ERROR_NONE); +} + +bool ConfigFile::DoesExist(void) const { return (access(mFilePath, 0) == 0); } + +otError ConfigFile::Get(const char *aKey, int &aIterator, char *aValue, int aValueLength) const { otError error = OT_ERROR_NONE; char line[kLineMaxSize + 1]; FILE *fp = nullptr; char *ret; + char *psave; long int pos; - VerifyOrExit((aKey != nullptr) && (aValue != nullptr), error = OT_ERROR_INVALID_ARGS); + VerifyOrExit(aKey != nullptr, error = OT_ERROR_INVALID_ARGS); VerifyOrExit((fp = fopen(mFilePath, "r")) != nullptr, error = OT_ERROR_NOT_FOUND); VerifyOrDie(fseek(fp, aIterator, SEEK_SET) == 0, OT_EXIT_ERROR_ERRNO); @@ -77,7 +87,7 @@ otError ConfigFile::Get(const char *aKey, int &aIterator, char *aValue, int aVal } // Remove comments - strtok(line, kCommentDelimiter); + strtok_r(line, kCommentDelimiter, &psave); if ((str = strstr(line, "=")) == nullptr) { @@ -91,11 +101,14 @@ otError ConfigFile::Get(const char *aKey, int &aIterator, char *aValue, int aVal if (strcmp(aKey, key) == 0) { - value = str + 1; - Strip(value); - aValueLength = OT_MIN(static_cast(strlen(value)), (aValueLength - 1)); - memcpy(aValue, value, static_cast(aValueLength)); - aValue[aValueLength] = '\0'; + if (aValue != nullptr) + { + value = str + 1; + Strip(value); + aValueLength = OT_MIN(static_cast(strlen(value)), (aValueLength - 1)); + memcpy(aValue, value, static_cast(aValueLength)); + aValue[aValueLength] = '\0'; + } break; } } @@ -197,7 +210,7 @@ exit: return error; } -void ConfigFile::Strip(char *aString) +void ConfigFile::Strip(char *aString) const { int count = 0; diff --git a/src/posix/platform/config_file.hpp b/src/posix/platform/config_file.hpp index 067124a7a..51d16ca0e 100644 --- a/src/posix/platform/config_file.hpp +++ b/src/posix/platform/config_file.hpp @@ -58,14 +58,15 @@ public: * @param[in,out] aIterator A reference to an iterator. MUST be initialized to 0 or the behavior is undefined. * @param[out] aValue A pointer to where the new value string of the configuration should be read from. * The @p aValue string will be terminated with `\0` if this method returns success. + * May be `nullptr` if performing a presence check. * @param[in] aValueLength The max length of the data pointed to by @p aValue. * * @retval OT_ERROR_NONE The given configuration was found and fetched successfully. * @retval OT_ERROR_NOT_FOUND The given key or iterator was not found in the configuration file. - * @retval OT_ERROR_INVALID_ARGS If @p aKey or @p aValue was NULL. + * @retval OT_ERROR_INVALID_ARGS If @p aKey was NULL. * */ - otError Get(const char *aKey, int &aIterator, char *aValue, int aValueLength); + otError Get(const char *aKey, int &aIterator, char *aValue, int aValueLength) const; /** * Adds a configuration to the configuration file. @@ -90,13 +91,33 @@ public: */ otError Clear(const char *aKey); + /** + * Indicates whether the given key exists. + * + * @param[in] aKey The key string associated with the requested configuration. + * + * @retval TRUE If the key exists in the configuration file. + * @retval FALSE If the key does not exist in the configuration file. + * + */ + bool HasKey(const char *aKey) const; + + /** + * Indicates whether the configuration file exists. + * + * @retval TRUE If the configuration file exists. + * @retval FALSE If the configuration file does not exist. + * + */ + bool DoesExist(void) const; + private: const char *kCommentDelimiter = "#"; const char *kSwapSuffix = ".swap"; static constexpr uint16_t kLineMaxSize = 512; static constexpr uint16_t kFileNameMaxSize = 255; - void Strip(char *aString); + void Strip(char *aString) const; const char *mFilePath; }; diff --git a/src/posix/platform/power_updater.cpp b/src/posix/platform/configuration.cpp similarity index 57% rename from src/posix/platform/power_updater.cpp rename to src/posix/platform/configuration.cpp index d3b45bb92..6510c4cf9 100644 --- a/src/posix/platform/power_updater.cpp +++ b/src/posix/platform/configuration.cpp @@ -26,23 +26,27 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "power_updater.hpp" +#include "configuration.hpp" #include "platform-posix.h" #include #include "lib/platform/exit_code.h" +#include "utils/parse_cmdline.hpp" -#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE +#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 PowerUpdater::SetRegion(uint16_t aRegionCode) +otError Configuration::SetRegion(uint16_t aRegionCode) { - otError error = OT_ERROR_NONE; - int iterator = 0; - Power::Domain domain; - Power::TargetPower targetPower; + otError error = OT_ERROR_NONE; + Power::Domain domain; if (GetDomain(aRegionCode, domain) != OT_ERROR_NONE) { @@ -50,7 +54,124 @@ otError PowerUpdater::SetRegion(uint16_t aRegionCode) VerifyOrExit(GetDomain(kRegionCodeWorldWide, domain) == OT_ERROR_NONE, error = OT_ERROR_FAILED); } - while (GetNextTargetPower(domain, iterator, targetPower) == OT_ERROR_NONE) + 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()); @@ -60,25 +181,16 @@ otError PowerUpdater::SetRegion(uint16_t aRegionCode) } } - SuccessOrExit(error = UpdateCalibratedPower()); - - mRegionCode = aRegionCode; - exit: - if (error == OT_ERROR_NONE) + if (error != OT_ERROR_NONE) { - otLogInfoPlat("Set region \"%c%c\" successfully", (aRegionCode >> 8) & 0xff, (aRegionCode & 0xff)); - } - else - { - otLogCritPlat("Set region \"%c%c\" failed, Error: %s", (aRegionCode >> 8) & 0xff, (aRegionCode & 0xff), - otThreadErrorToString(error)); + otLogCritPlat("Failed to update target power: %s", otThreadErrorToString(error)); } return error; } -otError PowerUpdater::UpdateCalibratedPower(void) +otError Configuration::UpdateCalibratedPower(void) { otError error = OT_ERROR_NONE; int iterator = 0; @@ -86,8 +198,6 @@ otError PowerUpdater::UpdateCalibratedPower(void) Power::CalibratedPower calibratedPower; ConfigFile *calibrationFile = &mFactoryConfigFile; - SuccessOrExit(error = otPlatRadioClearCalibratedPowers(gInstance)); - // 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. @@ -96,6 +206,9 @@ otError PowerUpdater::UpdateCalibratedPower(void) calibrationFile = &mProductConfigFile; } + VerifyOrExit(calibrationFile->HasKey(kKeyCalibratedPower)); + SuccessOrExit(error = otPlatRadioClearCalibratedPowers(gInstance)); + iterator = 0; while (calibrationFile->Get(kKeyCalibratedPower, iterator, value, sizeof(value)) == OT_ERROR_NONE) { @@ -113,45 +226,15 @@ otError PowerUpdater::UpdateCalibratedPower(void) exit: if (error != OT_ERROR_NONE) { - otLogCritPlat("Update calibrated power table failed, Error: %s", otThreadErrorToString(error)); + otLogCritPlat("Failed to update calibrated power table: %s", otThreadErrorToString(error)); } return error; } -otError PowerUpdater::GetDomain(uint16_t aRegionCode, Power::Domain &aDomain) -{ - otError error = OT_ERROR_NOT_FOUND; - int iterator = 0; - char value[kMaxValueSize]; - char *str; - - while (mProductConfigFile.Get(kKeyRegionDomainMapping, iterator, value, sizeof(value)) == OT_ERROR_NONE) - { - if ((str = strtok(value, kCommaDelimiter)) == nullptr) - { - continue; - } - - while ((str = strtok(nullptr, kCommaDelimiter)) != nullptr) - { - if ((strlen(str) == 2) && (StringToRegionCode(str) == aRegionCode)) - { - ExitNow(error = aDomain.Set(value)); - } - } - } - -exit: - if (error != OT_ERROR_NONE) - { - otLogCritPlat("Get domain failed, Error: %s", otThreadErrorToString(error)); - } - - return error; -} - -otError PowerUpdater::GetNextTargetPower(const Power::Domain &aDomain, int &aIterator, Power::TargetPower &aTargetPower) +otError Configuration::GetNextTargetPower(const Power::Domain &aDomain, + int &aIterator, + Power::TargetPower &aTargetPower) { otError error = OT_ERROR_NOT_FOUND; char value[kMaxValueSize]; @@ -167,14 +250,33 @@ otError PowerUpdater::GetNextTargetPower(const Power::Domain &aDomain, int &aIte if ((error = aTargetPower.FromString(psave)) != OT_ERROR_NONE) { - otLogCritPlat("Read target power failed, Error: %s", otThreadErrorToString(error)); + 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_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE +#endif // OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE diff --git a/src/posix/platform/power_updater.hpp b/src/posix/platform/configuration.hpp similarity index 58% rename from src/posix/platform/power_updater.hpp rename to src/posix/platform/configuration.hpp index f20e67d60..dd6c6a209 100644 --- a/src/posix/platform/power_updater.hpp +++ b/src/posix/platform/configuration.hpp @@ -26,12 +26,12 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef POSIX_PLATFORM_POWER_UPDATER_HPP_ -#define POSIX_PLATFORM_POWER_UPDATER_HPP_ +#ifndef POSIX_PLATFORM_CONFIGURATION_HPP_ +#define POSIX_PLATFORM_CONFIGURATION_HPP_ #include "openthread-posix-config.h" -#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE +#if OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE #include #include @@ -51,13 +51,15 @@ namespace Posix { * Updates the target power table and calibrated power table to the RCP. * */ -class PowerUpdater +class Configuration { public: - PowerUpdater(void) + Configuration(void) : mFactoryConfigFile(kFactoryConfigFile) , mProductConfigFile(kProductConfigFile) , mRegionCode(0) + , mSupportedChannelMask(kDefaultChannelMask) + , mPreferredChannelMask(kDefaultChannelMask) { } @@ -86,31 +88,66 @@ public: */ uint16_t GetRegion(void) const { return mRegionCode; } + /** + * Get the radio supported channel mask that the device is allowed to be on. + * + * @returns The radio supported channel mask. + * + */ + uint32_t GetSupportedChannelMask(void) const { return mSupportedChannelMask; } + + /** + * Gets the radio preferred channel mask that the device prefers to form on. + * + * @returns The radio preferred channel mask. + * + */ + uint32_t GetPreferredChannelMask(void) const { return mPreferredChannelMask; } + + /** + * Indicates whether the configuration file are valid. + * + * @retval TRUE If there are any valid configuration keys in the configuration file. + * @retval FALSE If the configuration file doesn't exist or there is no key in the configuration file. + * + */ + bool IsValid(void) const; + private: - const char *kFactoryConfigFile = OPENTHREAD_POSIX_CONFIG_FACTORY_CONFIG_FILE; - const char *kProductConfigFile = OPENTHREAD_POSIX_CONFIG_PRODUCT_CONFIG_FILE; - const char *kKeyCalibratedPower = "calibrated_power"; - const char *kKeyTargetPower = "target_power"; - const char *kKeyRegionDomainMapping = "region_domain_mapping"; - const char *kCommaDelimiter = ","; - static constexpr uint16_t kMaxValueSize = 512; - static constexpr uint16_t kRegionCodeWorldWide = 0x5757; // Region Code: "WW" + const char *kFactoryConfigFile = OPENTHREAD_POSIX_CONFIG_FACTORY_CONFIG_FILE; + const char *kProductConfigFile = OPENTHREAD_POSIX_CONFIG_PRODUCT_CONFIG_FILE; + const char *kKeyCalibratedPower = "calibrated_power"; + const char *kKeyTargetPower = "target_power"; + const char *kKeyRegionDomainMapping = "region_domain_mapping"; + const char *kKeySupportedChannelMask = "supported_channel_mask"; + const char *kKeyPreferredChannelMask = "preferred_channel_mask"; + const char *kCommaDelimiter = ","; + static constexpr uint16_t kMaxValueSize = 512; + static constexpr uint16_t kRegionCodeWorldWide = 0x5757; // Region Code: "WW" + static constexpr uint32_t kDefaultChannelMask = 0x7fff800; // Channel 11 ~ 26 uint16_t StringToRegionCode(const char *aString) const { return static_cast(((aString[0] & 0xFF) << 8) | ((aString[1] & 0xFF) << 0)); } otError GetDomain(uint16_t aRegionCode, Power::Domain &aDomain); - otError GetNextTargetPower(const Power::Domain &aDomain, int &aIterator, Power::TargetPower &aTargetPower); + otError GetChannelMask(const char *aKey, const Power::Domain &aDomain, uint32_t &aChannelMask); + otError UpdateChannelMasks(const Power::Domain &aDomain); +#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + otError UpdateTargetPower(const Power::Domain &aDomain); otError UpdateCalibratedPower(void); + otError GetNextTargetPower(const Power::Domain &aDomain, int &aIterator, Power::TargetPower &aTargetPower); +#endif ConfigFile mFactoryConfigFile; ConfigFile mProductConfigFile; uint16_t mRegionCode; + uint32_t mSupportedChannelMask; + uint32_t mPreferredChannelMask; }; } // namespace Posix } // namespace ot -#endif // OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE -#endif // POSIX_PLATFORM_POWER_UPDATER_HPP_ +#endif // OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE +#endif // POSIX_PLATFORM_CONFIGURATION_HPP_ diff --git a/src/posix/platform/openthread-posix-config.h b/src/posix/platform/openthread-posix-config.h index 2efa6ef37..eb7fbf6f7 100644 --- a/src/posix/platform/openthread-posix-config.h +++ b/src/posix/platform/openthread-posix-config.h @@ -399,6 +399,16 @@ #define OPENTHREAD_POSIX_CONFIG_PRODUCT_CONFIG_FILE "src/posix/platform/openthread.conf.example" #endif +/** + * @def OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE + * + * Define as 1 to enable the configuration file support. + * + */ +#ifndef OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE +#define OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE 1 +#endif + /** * @def OPENTHREAD_POSIX_CONFIG_RCP_TIME_SYNC_INTERVAL * diff --git a/src/posix/platform/openthread.conf.example b/src/posix/platform/openthread.conf.example index 83a2908cc..09668c119 100644 --- a/src/posix/platform/openthread.conf.example +++ b/src/posix/platform/openthread.conf.example @@ -22,3 +22,14 @@ calibrated_power=11,25,1900,112233 calibrated_power=11,25,1000,223344 calibrated_power=26,26,1500,334455 calibrated_power=26,26,700,445566 + + +# The radio supported channel mask that the device is allowed to be on. +# supported_channel_mask=, +supported_channel_mask=ETSI,0x7fff800 +supported_channel_mask=FCC,0x7fff000 + +# The radio preferred channel mask that the device prefers to form on. +# preferred_channel_mask=, +preferred_channel_mask=ETSI,0x7fff000 +preferred_channel_mask=FCC,0x7ff7000 diff --git a/src/posix/platform/power.cpp b/src/posix/platform/power.cpp index ea832a38a..d8b64a87a 100644 --- a/src/posix/platform/power.cpp +++ b/src/posix/platform/power.cpp @@ -50,14 +50,15 @@ otError TargetPower::FromString(char *aString) { otError error = OT_ERROR_NONE; char *str; + char *psave; - VerifyOrExit((str = strtok(aString, ",")) != nullptr, error = OT_ERROR_PARSE); + VerifyOrExit((str = strtok_r(aString, ",", &psave)) != nullptr, error = OT_ERROR_PARSE); SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(str, mChannelStart)); - VerifyOrExit((str = strtok(nullptr, ",")) != nullptr, error = OT_ERROR_PARSE); + VerifyOrExit((str = strtok_r(nullptr, ",", &psave)) != nullptr, error = OT_ERROR_PARSE); SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(str, mChannelEnd)); - VerifyOrExit((str = strtok(nullptr, ",")) != nullptr, error = OT_ERROR_PARSE); + VerifyOrExit((str = strtok_r(nullptr, ",", &psave)) != nullptr, error = OT_ERROR_PARSE); SuccessOrExit(error = Utils::CmdLineParser::ParseAsInt16(str, mTargetPower)); exit: diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index c96f803cf..32cd9e618 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -60,9 +60,9 @@ static ot::Spinel::RadioSpinel sRadioSpinel; "OT_POSIX_RCP_BUS_VENDOR!" #endif -#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE -#include "power_updater.hpp" -static ot::Posix::PowerUpdater sPowerUpdater; +#if OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE +#include "configuration.hpp" +static ot::Posix::Configuration sConfig; #endif namespace ot { @@ -767,13 +767,41 @@ void otPlatDiagAlarmCallback(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstan uint32_t otPlatRadioGetSupportedChannelMask(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); - return sRadioSpinel.GetRadioChannelMask(false); + + uint32_t channelMask; + +#if OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE + if (sConfig.IsValid()) + { + channelMask = sConfig.GetSupportedChannelMask(); + } + else +#endif + { + channelMask = sRadioSpinel.GetRadioChannelMask(false); + } + + return channelMask; } uint32_t otPlatRadioGetPreferredChannelMask(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); - return sRadioSpinel.GetRadioChannelMask(true); + + uint32_t channelMask; + +#if OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE + if (sConfig.IsValid()) + { + channelMask = sConfig.GetPreferredChannelMask(); + } + else +#endif + { + channelMask = sRadioSpinel.GetRadioChannelMask(true); + } + + return channelMask; } otRadioState otPlatRadioGetState(otInstance *aInstance) @@ -870,22 +898,42 @@ otError otPlatRadioSetChannelTargetPower(otInstance *aInstance, uint8_t aChannel otError otPlatRadioSetRegion(otInstance *aInstance, uint16_t aRegionCode) { OT_UNUSED_VARIABLE(aInstance); -#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE - return sPowerUpdater.SetRegion(aRegionCode); -#else - return sRadioSpinel.SetRadioRegion(aRegionCode); + + otError error; + +#if OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE + if (sConfig.IsValid()) + { + error = sConfig.SetRegion(aRegionCode); + } + else #endif + { + error = sRadioSpinel.SetRadioRegion(aRegionCode); + } + + return error; } otError otPlatRadioGetRegion(otInstance *aInstance, uint16_t *aRegionCode) { OT_UNUSED_VARIABLE(aInstance); -#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE - *aRegionCode = sPowerUpdater.GetRegion(); - return OT_ERROR_NONE; -#else - return sRadioSpinel.GetRadioRegion(aRegionCode); + + otError error; + +#if OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE + if (sConfig.IsValid()) + { + *aRegionCode = sConfig.GetRegion(); + error = OT_ERROR_NONE; + } + else #endif + { + error = sRadioSpinel.GetRadioRegion(aRegionCode); + } + + return error; } #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE diff --git a/tests/scripts/expect/_common.exp b/tests/scripts/expect/_common.exp index 4214299b7..3835de8d7 100644 --- a/tests/scripts/expect/_common.exp +++ b/tests/scripts/expect/_common.exp @@ -86,6 +86,9 @@ proc spawn_node {id {type ""} {radio_url ""}} { switch -regexp ${type} { {rcp|rcp-cli} { + # Sleep 0.2 seconds to ensure that the ot-rcp in the previous test has exited to + # avoid the error: "bind(sTxFd): Address already in use" + sleep 0.2 spawn /usr/bin/env GCOV_PREFIX=$gcov_prefix $::env(OT_POSIX_APPS)/ot-cli $radio_url send "factoryreset\n" wait_for "state" "disabled" diff --git a/tests/scripts/expect/posix-channel-mask.exp b/tests/scripts/expect/posix-channel-mask.exp new file mode 100644 index 000000000..43f78f78e --- /dev/null +++ b/tests/scripts/expect/posix-channel-mask.exp @@ -0,0 +1,64 @@ +#!/usr/bin/expect -f +# +# Copyright (c) 2023, 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. +# + +source "tests/scripts/expect/_common.exp" + +spawn_node 1 + +send "channel supported\n" +expect "0x7fff800" +expect_line "Done" + +send "channel preferred\n" +expect "0x7fff800" +expect_line "Done" + +send "region US\n" +expect_line "Done" + +send "channel supported\n" +expect "0x7fff000" +expect_line "Done" + +send "channel preferred\n" +expect "0x7ff7000" +expect_line "Done" + +send "region WW\n" +expect_line "Done" + +send "channel supported\n" +expect "0x7fff800" +expect_line "Done" + +send "channel preferred\n" +expect "0x7fff000" +expect_line "Done" + +dispose_node 1 diff --git a/tests/scripts/expect/posix-max-power-table.exp b/tests/scripts/expect/posix-max-power-table.exp index 86d3a0f14..0d4b70cd4 100755 --- a/tests/scripts/expect/posix-max-power-table.exp +++ b/tests/scripts/expect/posix-max-power-table.exp @@ -29,6 +29,10 @@ source "tests/scripts/expect/_common.exp" +# backup the configuration file +spawn mv src/posix/platform/openthread.conf.example src/posix/platform/openthread.conf.example.backup +expect eof + # allows 11-25 and forbidden 26 spawn_node 1 "rcp" "spinel+hdlc+uart://$env(OT_SIMULATION_APPS)/ncp/ot-rcp?max-power-table=11,12,13,14,15,16,17,18,19,20,21,22,23,24,-1,0x7f&forkpty-arg=1" send "channel supported\n" @@ -44,6 +48,11 @@ expect "11 dBm" expect_line "Done" send "\x04" expect eof + +# restore the configuration file +spawn mv src/posix/platform/openthread.conf.example.backup src/posix/platform/openthread.conf.example +expect eof + # allows all channels by default spawn_node 1 "rcp" "spinel+hdlc+uart://$env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=1" send "channel supported\n"