diff --git a/examples/platforms/simulation/diag.c b/examples/platforms/simulation/diag.c index 3d3ea817f..44dc06a7d 100644 --- a/examples/platforms/simulation/diag.c +++ b/examples/platforms/simulation/diag.c @@ -55,6 +55,8 @@ enum static otGpioMode sGpioMode = OT_GPIO_MODE_INPUT; static bool sGpioValue = false; +static uint8_t sRawPowerSetting[OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE]; +static uint16_t sRawPowerSettingLength = 0; void otPlatDiagModeSet(bool aMode) { sDiagMode = aMode; } @@ -116,4 +118,54 @@ otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode) exit: return error; } + +otError otPlatDiagRadioSetRawPowerSetting(otInstance *aInstance, + const uint8_t *aRawPowerSetting, + uint16_t aRawPowerSettingLength) +{ + OT_UNUSED_VARIABLE(aInstance); + otError error = OT_ERROR_NONE; + + otEXPECT_ACTION((aRawPowerSetting != NULL) && (aRawPowerSettingLength <= sizeof(sRawPowerSetting)), + error = OT_ERROR_INVALID_ARGS); + memcpy(sRawPowerSetting, aRawPowerSetting, aRawPowerSettingLength); + sRawPowerSettingLength = aRawPowerSettingLength; + +exit: + return error; +} + +otError otPlatDiagRadioGetRawPowerSetting(otInstance *aInstance, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength) +{ + OT_UNUSED_VARIABLE(aInstance); + otError error = OT_ERROR_NONE; + + otEXPECT_ACTION((aRawPowerSetting != NULL) && (aRawPowerSettingLength != NULL), error = OT_ERROR_INVALID_ARGS); + otEXPECT_ACTION((sRawPowerSettingLength != 0), error = OT_ERROR_NOT_FOUND); + otEXPECT_ACTION((sRawPowerSettingLength <= *aRawPowerSettingLength), error = OT_ERROR_INVALID_ARGS); + + memcpy(aRawPowerSetting, sRawPowerSetting, sRawPowerSettingLength); + *aRawPowerSettingLength = sRawPowerSettingLength; + +exit: + return error; +} + +otError otPlatDiagRadioRawPowerSettingEnable(otInstance *aInstance, bool aEnable) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aEnable); + + return OT_ERROR_NONE; +} + +otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aEnable); + + return OT_ERROR_NONE; +} #endif // OPENTHREAD_CONFIG_DIAG_ENABLE diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 8086b2c29..39062bb52 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (278) +#define OPENTHREAD_API_VERSION (279) /** * @addtogroup api-instance diff --git a/include/openthread/platform/diag.h b/include/openthread/platform/diag.h index 53f6cace7..df889d277 100644 --- a/include/openthread/platform/diag.h +++ b/include/openthread/platform/diag.h @@ -200,6 +200,91 @@ otError otPlatDiagGpioSetMode(uint32_t aGpio, otGpioMode aMode); */ otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode); +/** + * Set the radio raw power setting for diagnostics module. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aRawPowerSetting A pointer to the raw power setting byte array. + * @param[in] aRawPowerSettingLength The length of the @p aRawPowerSetting. + * + * @retval OT_ERROR_NONE Successfully set the raw power setting. + * @retval OT_ERROR_INVALID_ARGS The @p aRawPowerSetting is NULL or the @p aRawPowerSettingLength is too long. + * @retval OT_ERROR_NOT_IMPLEMENTED This method is not implemented. + * + */ +otError otPlatDiagRadioSetRawPowerSetting(otInstance *aInstance, + const uint8_t *aRawPowerSetting, + uint16_t aRawPowerSettingLength); + +/** + * Get the radio raw power setting for diagnostics module. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[out] aRawPowerSetting A pointer to the raw power setting byte array. + * @param[in,out] aRawPowerSettingLength On input, a pointer to the size of @p aRawPowerSetting. + * On output, a pointer to the length of the raw power setting data. + * + * @retval OT_ERROR_NONE Successfully set the raw power setting. + * @retval OT_ERROR_INVALID_ARGS The @p aRawPowerSetting or @p aRawPowerSettingLength is NULL or + * @aRawPowerSettingLength is too short. + * @retval OT_ERROR_NOT_FOUND The raw power setting is not set. + * @retval OT_ERROR_NOT_IMPLEMENTED This method is not implemented. + * + */ +otError otPlatDiagRadioGetRawPowerSetting(otInstance *aInstance, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength); + +/** + * Enable/disable the platform layer to use the raw power setting set by `otPlatDiagRadioSetRawPowerSetting()`. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aEnable TRUE to enable or FALSE to disable the raw power setting. + * + * @retval OT_ERROR_NONE Successfully enabled/disabled the raw power setting. + * @retval OT_ERROR_NOT_IMPLEMENTED This method is not implemented. + * + */ +otError otPlatDiagRadioRawPowerSettingEnable(otInstance *aInstance, bool aEnable); + +/** + * Start/stop the platform layer to transmit continuous carrier wave. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aEnable TRUE to enable or FALSE to disable the platform layer to transmit continuous carrier wave. + * + * @retval OT_ERROR_NONE Successfully enabled/disabled . + * @retval OT_ERROR_INVALID_STATE The radio was not in the Receive state. + * @retval OT_ERROR_NOT_IMPLEMENTED This method is not implemented. + * + */ +otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable); + +/** + * Get the power settings for the given channel. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aChannel The radio channel. + * @param[out] aTargetPower The target power in 0.01 dBm. + * @param[out] aActualPower The actual power in 0.01 dBm. + * @param[out] aRawPowerSetting A pointer to the raw power setting byte array. + * @param[in,out] aRawPowerSettingLength On input, a pointer to the size of @p aRawPowerSetting. + * On output, a pointer to the length of the raw power setting data. + * + * @retval OT_ERROR_NONE Successfully got the target power. + * @retval OT_ERROR_INVALID_ARGS The @p aChannel is invalid, @aTargetPower, @p aActualPower, @p aRawPowerSetting or + * @p aRawPowerSettingLength is NULL or @aRawPowerSettingLength is too short. + * @retval OT_ERROR_NOT_FOUND The power settings for the @p aChannel was not found. + * @retval OT_ERROR_NOT_IMPLEMENTED This method is not implemented. + * + */ +otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance, + uint8_t aChannel, + int16_t *aTargetPower, + int16_t *aActualPower, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength); + /** * @} * diff --git a/src/core/diags/README.md b/src/core/diags/README.md index 62f5603d7..b137b14c7 100644 --- a/src/core/diags/README.md +++ b/src/core/diags/README.md @@ -9,10 +9,13 @@ The diagnostics module supports common diagnostics features that are listed belo - [diag](#diag) - [diag start](#diag-start) - [diag channel](#diag-channel) +- [diag cw](#diag-cw-start) - [diag power](#diag-power) +- [diag powersettings](#diag-powersettings) - [diag send](#diag-send-packets-length) - [diag repeat](#diag-repeat-delay-length) - [diag radio](#diag-radio-sleep) +- [diag rawpowersetting](#diag-rawpowersetting) - [diag stats](#diag-stats) - [diag gpio](#diag-gpio-get-gpio) - [diag stop](#diag-stop) @@ -55,6 +58,24 @@ set channel to 11 status 0x00 ``` +### diag cw start + +Start transmitting continuous carrier wave. + +```bash +> diag cw start +Done +``` + +### diag cw stop + +Stop transmitting continuous carrier wave. + +```bash +> diag cw stop +Done +``` + ### diag power Get the tx power value(dBm) for diagnostics module. @@ -74,6 +95,35 @@ set tx power to -10 dBm status 0x00 ``` +### diag powersettings + +Show the currently used power settings table. + +- Note: The unit of `TargetPower` and `ActualPower` is 0.01dBm. + +```bash +> diag powersettings +| StartCh | EndCh | TargetPower | ActualPower | RawPowerSetting | ++---------+-------+-------------+-------------+-----------------+ +| 11 | 14 | 1700 | 1000 | 223344 | +| 15 | 24 | 2000 | 1900 | 112233 | +| 25 | 25 | 1600 | 1000 | 223344 | +| 26 | 26 | 1600 | 1500 | 334455 | +Done +``` + +### diag powersettings \ + +Show the currently used power settings for the given channel. + +```bash +> diag powersettings 11 +TargetPower(0.01dBm): 1700 +ActualPower(0.01dBm): 1000 +RawPowerSetting: 223344 +Done +``` + ### diag send \ \ Transmit a fixed number of packets with fixed length. @@ -137,6 +187,43 @@ Return the state of the radio. sleep ``` +### diag rawpowersetting + +Show the raw power setting for diagnostics module. + +```bash +> diag rawpowersetting +112233 +Done +``` + +### diag rawpowersetting \ + +Set the raw power setting for diagnostics module. + +```bash +> diag rawpowersetting 112233 +Done +``` + +### diag rawpowersetting enable + +Enable the platform layer to use the value set by the command `diag rawpowersetting \`. + +```bash +> diag rawpowersetting enable +Done +``` + +### diag rawpowersetting disable + +Disable the platform layer to use the value set by the command `diag rawpowersetting \`. + +```bash +> diag rawpowersetting disable +Done +``` + ### diag stats Print statistics during diagnostics mode. diff --git a/src/core/diags/factory_diags.cpp b/src/core/diags/factory_diags.cpp index 7662a9ddf..cb6c89f9d 100644 --- a/src/core/diags/factory_diags.cpp +++ b/src/core/diags/factory_diags.cpp @@ -70,8 +70,15 @@ namespace FactoryDiags { #if OPENTHREAD_RADIO && !OPENTHREAD_RADIO_CLI const struct Diags::Command Diags::sCommands[] = { - {"channel", &Diags::ProcessChannel}, {"echo", &Diags::ProcessEcho}, {"gpio", &Diags::ProcessGpio}, - {"power", &Diags::ProcessPower}, {"start", &Diags::ProcessStart}, {"stop", &Diags::ProcessStop}, + {"channel", &Diags::ProcessChannel}, + {"cw", &Diags::ProcessContinuousWave}, + {"echo", &Diags::ProcessEcho}, + {"gpio", &Diags::ProcessGpio}, + {"power", &Diags::ProcessPower}, + {"powersettings", &Diags::ProcessPowerSettings}, + {"rawpowersetting", &Diags::ProcessRawPowerSetting}, + {"start", &Diags::ProcessStart}, + {"stop", &Diags::ProcessStop}, }; Diags::Diags(Instance &aInstance) @@ -178,9 +185,18 @@ extern "C" void otPlatDiagAlarmFired(otInstance *aInstance) { otPlatDiagAlarmCal #else // OPENTHREAD_RADIO && !OPENTHREAD_RADIO_CLI // For OPENTHREAD_FTD, OPENTHREAD_MTD, OPENTHREAD_RADIO_CLI const struct Diags::Command Diags::sCommands[] = { - {"channel", &Diags::ProcessChannel}, {"gpio", &Diags::ProcessGpio}, {"power", &Diags::ProcessPower}, - {"radio", &Diags::ProcessRadio}, {"repeat", &Diags::ProcessRepeat}, {"send", &Diags::ProcessSend}, - {"start", &Diags::ProcessStart}, {"stats", &Diags::ProcessStats}, {"stop", &Diags::ProcessStop}, + {"channel", &Diags::ProcessChannel}, + {"cw", &Diags::ProcessContinuousWave}, + {"gpio", &Diags::ProcessGpio}, + {"power", &Diags::ProcessPower}, + {"powersettings", &Diags::ProcessPowerSettings}, + {"rawpowersetting", &Diags::ProcessRawPowerSetting}, + {"radio", &Diags::ProcessRadio}, + {"repeat", &Diags::ProcessRepeat}, + {"send", &Diags::ProcessSend}, + {"start", &Diags::ProcessStart}, + {"stats", &Diags::ProcessStats}, + {"stop", &Diags::ProcessStop}, }; Diags::Diags(Instance &aInstance) @@ -537,6 +553,137 @@ exit: #endif // OPENTHREAD_RADIO +Error Diags::ProcessContinuousWave(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +{ + Error error = kErrorInvalidArgs; + + VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState); + VerifyOrExit(aArgsLength > 0, error = kErrorInvalidArgs); + + if (strcmp(aArgs[0], "start") == 0) + { + SuccessOrExit(error = otPlatDiagRadioTransmitCarrier(&GetInstance(), true)); + } + else if (strcmp(aArgs[0], "stop") == 0) + { + SuccessOrExit(error = otPlatDiagRadioTransmitCarrier(&GetInstance(), false)); + } + +exit: + AppendErrorResult(error, aOutput, aOutputMaxLen); + return error; +} + +Error Diags::GetPowerSettings(uint8_t aChannel, PowerSettings &aPowerSettings) +{ + aPowerSettings.mRawPowerSetting.mLength = RawPowerSetting::kMaxDataSize; + return otPlatDiagRadioGetPowerSettings(&GetInstance(), aChannel, &aPowerSettings.mTargetPower, + &aPowerSettings.mActualPower, aPowerSettings.mRawPowerSetting.mData, + &aPowerSettings.mRawPowerSetting.mLength); +} + +Error Diags::ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +{ + Error error = kErrorInvalidArgs; + uint8_t channel; + PowerSettings powerSettings; + + VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState); + + if (aArgsLength == 0) + { + bool isPrePowerSettingsValid = false; + uint8_t preChannel = 0; + PowerSettings prePowerSettings; + int n; + + n = snprintf(aOutput, aOutputMaxLen, + "| StartCh | EndCh | TargetPower | ActualPower | RawPowerSetting |\r\n" + "+---------+-------+-------------+-------------+-----------------+\r\n"); + VerifyOrExit((n > 0) && (n < static_cast(aOutputMaxLen)), error = kErrorNoBufs); + aOutput += n; + aOutputMaxLen -= static_cast(n); + + for (channel = Radio::kChannelMin; channel <= Radio::kChannelMax + 1; channel++) + { + error = (channel == Radio::kChannelMax + 1) ? kErrorNotFound : GetPowerSettings(channel, powerSettings); + + if (isPrePowerSettingsValid && ((powerSettings != prePowerSettings) || (error != kErrorNone))) + { + n = snprintf(aOutput, aOutputMaxLen, "| %7u | %5u | %11d | %11d | %15s |\r\n", preChannel, channel - 1, + prePowerSettings.mTargetPower, prePowerSettings.mActualPower, + prePowerSettings.mRawPowerSetting.ToString().AsCString()); + VerifyOrExit((n > 0) && (n < static_cast(aOutputMaxLen)), error = kErrorNoBufs); + aOutput += n; + aOutputMaxLen -= static_cast(n); + isPrePowerSettingsValid = false; + } + + if ((error == kErrorNone) && (!isPrePowerSettingsValid)) + { + preChannel = channel; + prePowerSettings = powerSettings; + isPrePowerSettingsValid = true; + } + } + + error = kErrorNone; + } + else if (aArgsLength == 1) + { + SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(aArgs[0], channel)); + VerifyOrExit(channel >= Radio::kChannelMin && channel <= Radio::kChannelMax, error = kErrorInvalidArgs); + + SuccessOrExit(error = GetPowerSettings(channel, powerSettings)); + snprintf(aOutput, aOutputMaxLen, + "TargetPower(0.01dBm): %d\r\nActualPower(0.01dBm): %d\r\nRawPowerSetting: %s\r\n", + powerSettings.mTargetPower, powerSettings.mActualPower, + powerSettings.mRawPowerSetting.ToString().AsCString()); + } + +exit: + AppendErrorResult(error, aOutput, aOutputMaxLen); + return error; +} + +Error Diags::GetRawPowerSetting(RawPowerSetting &aRawPowerSetting) +{ + aRawPowerSetting.mLength = RawPowerSetting::kMaxDataSize; + return otPlatDiagRadioGetRawPowerSetting(&GetInstance(), aRawPowerSetting.mData, &aRawPowerSetting.mLength); +} + +Error Diags::ProcessRawPowerSetting(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +{ + Error error = kErrorInvalidArgs; + RawPowerSetting setting; + + VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState); + + if (aArgsLength == 0) + { + SuccessOrExit(error = GetRawPowerSetting(setting)); + snprintf(aOutput, aOutputMaxLen, "%s\r\n", setting.ToString().AsCString()); + } + else if (strcmp(aArgs[0], "enable") == 0) + { + SuccessOrExit(error = otPlatDiagRadioRawPowerSettingEnable(&GetInstance(), true)); + } + else if (strcmp(aArgs[0], "disable") == 0) + { + SuccessOrExit(error = otPlatDiagRadioRawPowerSettingEnable(&GetInstance(), false)); + } + else + { + setting.mLength = RawPowerSetting::kMaxDataSize; + SuccessOrExit(error = Utils::CmdLineParser::ParseAsHexString(aArgs[0], setting.mLength, setting.mData)); + SuccessOrExit(error = otPlatDiagRadioSetRawPowerSetting(&GetInstance(), setting.mData, setting.mLength)); + } + +exit: + AppendErrorResult(error, aOutput, aOutputMaxLen); + return error; +} + Error Diags::ProcessGpio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) { Error error = kErrorInvalidArgs; @@ -755,4 +902,58 @@ OT_TOOL_WEAK otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode) return OT_ERROR_NOT_IMPLEMENTED; } +OT_TOOL_WEAK otError otPlatDiagRadioSetRawPowerSetting(otInstance *aInstance, + const uint8_t *aRawPowerSetting, + uint16_t aRawPowerSettingLength) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aRawPowerSetting); + OT_UNUSED_VARIABLE(aRawPowerSettingLength); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +OT_TOOL_WEAK otError otPlatDiagRadioGetRawPowerSetting(otInstance *aInstance, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aRawPowerSetting); + OT_UNUSED_VARIABLE(aRawPowerSettingLength); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +OT_TOOL_WEAK otError otPlatDiagRadioRawPowerSettingEnable(otInstance *aInstance, bool aEnable) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aEnable); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +OT_TOOL_WEAK otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aEnable); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +OT_TOOL_WEAK otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance, + uint8_t aChannel, + int16_t *aTargetPower, + int16_t *aActualPower, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aChannel); + OT_UNUSED_VARIABLE(aTargetPower); + OT_UNUSED_VARIABLE(aActualPower); + OT_UNUSED_VARIABLE(aRawPowerSetting); + OT_UNUSED_VARIABLE(aRawPowerSettingLength); + + return OT_ERROR_NOT_IMPLEMENTED; +} #endif // OPENTHREAD_CONFIG_DIAG_ENABLE diff --git a/src/core/diags/factory_diags.hpp b/src/core/diags/factory_diags.hpp index e3c7c2a76..9115e5150 100644 --- a/src/core/diags/factory_diags.hpp +++ b/src/core/diags/factory_diags.hpp @@ -45,6 +45,7 @@ #include "common/error.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" +#include "common/string.hpp" namespace ot { namespace FactoryDiags { @@ -142,12 +143,52 @@ private: uint8_t mLastLqi; }; + struct RawPowerSetting + { + static constexpr uint16_t kMaxDataSize = OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE; + static constexpr uint16_t kInfoStringSize = kMaxDataSize * 2 + 1; + typedef String InfoString; + + InfoString ToString(void) const + { + InfoString string; + + string.AppendHexBytes(mData, mLength); + + return string; + } + + bool operator!=(const RawPowerSetting &aOther) const + { + return (mLength != aOther.mLength) || (memcmp(mData, aOther.mData, mLength) != 0); + } + + uint8_t mData[kMaxDataSize]; + uint16_t mLength; + }; + + struct PowerSettings + { + bool operator!=(const PowerSettings &aOther) const + { + return (mTargetPower != aOther.mTargetPower) || (mActualPower != aOther.mActualPower) || + (mRawPowerSetting != aOther.mRawPowerSetting); + } + + int16_t mTargetPower; + int16_t mActualPower; + RawPowerSetting mRawPowerSetting; + }; + Error ParseCmd(char *aString, uint8_t &aArgsLength, char *aArgs[]); Error ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); + Error ProcessContinuousWave(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); Error ProcessGpio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); Error ProcessPower(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); Error ProcessRadio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); Error ProcessRepeat(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); + Error ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); + Error ProcessRawPowerSetting(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); Error ProcessSend(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); Error ProcessStart(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); Error ProcessStats(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); @@ -156,6 +197,9 @@ private: Error ProcessEcho(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); #endif + Error GetRawPowerSetting(RawPowerSetting &aRawPowerSetting); + Error GetPowerSettings(uint8_t aChannel, PowerSettings &aPowerSettings); + void TransmitPacket(void); static void AppendErrorResult(Error aError, char *aOutput, size_t aOutputMaxLen); diff --git a/src/core/utils/power_calibration.cpp b/src/core/utils/power_calibration.cpp index b23bdcf0d..0818d0440 100644 --- a/src/core/utils/power_calibration.cpp +++ b/src/core/utils/power_calibration.cpp @@ -29,6 +29,7 @@ #include "power_calibration.hpp" #if OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE +#include #include "common/as_core_type.hpp" #include "common/code_utils.hpp" @@ -131,9 +132,11 @@ exit: return error; } -Error PowerCalibration::GetRawPowerSetting(uint8_t aChannel, - uint8_t *aRawPowerSetting, - uint16_t *aRawPowerSettingLength) +Error PowerCalibration::GetPowerSettings(uint8_t aChannel, + int16_t *aTargetPower, + int16_t *aActualPower, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength) { Error error = kErrorNone; uint8_t chIndex; @@ -168,8 +171,20 @@ Error PowerCalibration::GetRawPowerSetting(uint8_t aChannel, exit: if (error == kErrorNone) { - error = mCalibratedPowerTables[mLastChannel - Radio::kChannelMin][mCalibratedPowerIndex].GetRawPowerSetting( - aRawPowerSetting, aRawPowerSettingLength); + chIndex = mLastChannel - Radio::kChannelMin; + + if (aTargetPower != nullptr) + { + *aTargetPower = mTargetPowerTable[chIndex]; + } + + if (aActualPower != nullptr) + { + *aActualPower = mCalibratedPowerTables[chIndex][mCalibratedPowerIndex].GetActualPower(); + } + + error = mCalibratedPowerTables[chIndex][mCalibratedPowerIndex].GetRawPowerSetting(aRawPowerSetting, + aRawPowerSettingLength); } return error; @@ -208,7 +223,23 @@ otError otPlatRadioGetRawPowerSetting(otInstance *aInstance, AssertPointerIsNotNull(aRawPowerSetting); AssertPointerIsNotNull(aRawPowerSettingLength); - return AsCoreType(aInstance).Get().GetRawPowerSetting(aChannel, aRawPowerSetting, - aRawPowerSettingLength); + return AsCoreType(aInstance).Get().GetPowerSettings( + aChannel, nullptr, nullptr, aRawPowerSetting, aRawPowerSettingLength); +} + +otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance, + uint8_t aChannel, + int16_t *aTargetPower, + int16_t *aActualPower, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength) +{ + OT_UNUSED_VARIABLE(aInstance); + + AssertPointerIsNotNull(aRawPowerSetting); + AssertPointerIsNotNull(aRawPowerSettingLength); + + return AsCoreType(aInstance).Get().GetPowerSettings( + aChannel, aTargetPower, aActualPower, aRawPowerSetting, aRawPowerSettingLength); } #endif // OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE diff --git a/src/core/utils/power_calibration.hpp b/src/core/utils/power_calibration.hpp index 7b1afa25f..838a55295 100644 --- a/src/core/utils/power_calibration.hpp +++ b/src/core/utils/power_calibration.hpp @@ -99,22 +99,31 @@ public: Error SetChannelTargetPower(uint8_t aChannel, int16_t aTargetPower); /** - * Get the raw power setting for the given channel. + * Get the power settings for the given channel. * * Platform radio layer should parse the raw power setting based on the radio layer defined format and set the * parameters of each radio hardware module. * * @param[in] aChannel The radio channel. + * @param[out] aTargetPower A pointer to the target power in 0.01 dBm. May be set to nullptr if + * the caller doesn't want to get the target power. + * @param[out] aActualPower A pointer to the actual power in 0.01 dBm. May be set to nullptr if + * the caller doesn't want to get the actual power. * @param[out] aRawPowerSetting A pointer to the raw power setting byte array. * @param[in,out] aRawPowerSettingLength On input, a pointer to the size of @p aRawPowerSetting. * On output, a pointer to the length of the raw power setting data. * * @retval kErrorNone Successfully got the target power. - * @retval kErrorInvalidArgs The @p aChannel is invalid or @p aRawPowerSetting is nullptr. - * @retval kErrorNotFound The raw power setting for the @p aChannel was not found. + * @retval kErrorInvalidArgs The @p aChannel is invalid, @p aRawPowerSetting or @p aRawPowerSettingLength is + * nullptr or @aRawPowerSettingLength is too short. + * @retval kErrorNotFound The power settings for the @p aChannel was not found. * */ - Error GetRawPowerSetting(uint8_t aChannel, uint8_t *aRawPowerSetting, uint16_t *aRawPowerSettingLength); + Error GetPowerSettings(uint8_t aChannel, + int16_t *aTargetPower, + int16_t *aActualPower, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength); private: class CalibratedPowerEntry diff --git a/src/posix/platform/openthread-core-posix-config.h b/src/posix/platform/openthread-core-posix-config.h index efc38c9f8..0a3cd2048 100644 --- a/src/posix/platform/openthread-core-posix-config.h +++ b/src/posix/platform/openthread-core-posix-config.h @@ -44,6 +44,16 @@ #define OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS 256 #endif +/** + * @def OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE + * + * Define OpenThread diagnostic mode output buffer size in bytes + * + */ +#ifndef OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE +#define OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE 500 +#endif + /** * @def OPENTHREAD_CONFIG_LOG_PLATFORM * diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index 815c1c46e..0134e28d2 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -41,6 +41,7 @@ #include "common/new.hpp" #include "lib/spinel/radio_spinel.hpp" #include "posix/platform/radio.hpp" +#include "utils/parse_cmdline.hpp" #if OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_UART #include "hdlc_interface.hpp" @@ -623,6 +624,117 @@ exit: return error; } +otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance, + uint8_t aChannel, + int16_t *aTargetPower, + int16_t *aActualPower, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength) +{ + OT_UNUSED_VARIABLE(aInstance); + static constexpr uint16_t kRawPowerStringSize = OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE * 2 + 1; + static constexpr uint16_t kFmtStringSize = 100; + + otError error; + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; + char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; + int targetPower; + int actualPower; + char rawPowerSetting[kRawPowerStringSize]; + char fmt[kFmtStringSize]; + + assert((aTargetPower != nullptr) && (aActualPower != nullptr) && (aRawPowerSetting != nullptr) && + (aRawPowerSettingLength != nullptr)); + + snprintf(cmd, sizeof(cmd), "powersettings %d", aChannel); + SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, output, sizeof(output))); + snprintf(fmt, sizeof(fmt), "TargetPower(0.01dBm): %%d\r\nActualPower(0.01dBm): %%d\r\nRawPowerSetting: %%%us\r\n", + kRawPowerStringSize); + VerifyOrExit(sscanf(output, fmt, &targetPower, &actualPower, rawPowerSetting) == 3, error = OT_ERROR_FAILED); + SuccessOrExit( + error = ot::Utils::CmdLineParser::ParseAsHexString(rawPowerSetting, *aRawPowerSettingLength, aRawPowerSetting)); + *aTargetPower = static_cast(targetPower); + *aActualPower = static_cast(actualPower); + +exit: + return error; +} + +otError otPlatDiagRadioSetRawPowerSetting(otInstance *aInstance, + const uint8_t *aRawPowerSetting, + uint16_t aRawPowerSettingLength) +{ + OT_UNUSED_VARIABLE(aInstance); + + otError error; + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; + int nbytes; + + assert(aRawPowerSetting != nullptr); + + nbytes = snprintf(cmd, sizeof(cmd), "rawpowersetting "); + + for (uint16_t i = 0; i < aRawPowerSettingLength; i++) + { + nbytes += snprintf(cmd + nbytes, sizeof(cmd) - static_cast(nbytes), "%02x", aRawPowerSetting[i]); + VerifyOrExit(nbytes < static_cast(sizeof(cmd)), error = OT_ERROR_INVALID_ARGS); + } + + SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, nullptr, 0)); + +exit: + return error; +} + +otError otPlatDiagRadioGetRawPowerSetting(otInstance *aInstance, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength) +{ + OT_UNUSED_VARIABLE(aInstance); + otError error; + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; + char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; + char *str; + + assert((aRawPowerSetting != nullptr) && (aRawPowerSettingLength != nullptr)); + + snprintf(cmd, sizeof(cmd), "rawpowersetting"); + SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, output, sizeof(output))); + VerifyOrExit((str = strtok(output, "\r")) != nullptr, error = OT_ERROR_FAILED); + SuccessOrExit(error = ot::Utils::CmdLineParser::ParseAsHexString(str, *aRawPowerSettingLength, aRawPowerSetting)); + +exit: + return error; +} + +otError otPlatDiagRadioRawPowerSettingEnable(otInstance *aInstance, bool aEnable) +{ + OT_UNUSED_VARIABLE(aInstance); + + otError error; + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; + + snprintf(cmd, sizeof(cmd), "rawpowersetting %s", aEnable ? "enable" : "disable"); + SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, nullptr, 0)); + +exit: + return error; +} + +otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable) +{ + OT_UNUSED_VARIABLE(aInstance); + + otError error; + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; + + snprintf(cmd, sizeof(cmd), "cw %s", aEnable ? "start" : "stop"); + SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, nullptr, 0)); + +exit: + return error; +} + void otPlatDiagRadioReceived(otInstance *aInstance, otRadioFrame *aFrame, otError aError) { OT_UNUSED_VARIABLE(aInstance); diff --git a/tests/scripts/expect/cli-diags.exp b/tests/scripts/expect/cli-diags.exp index 0c247d2ae..ab4986f8b 100755 --- a/tests/scripts/expect/cli-diags.exp +++ b/tests/scripts/expect/cli-diags.exp @@ -175,6 +175,25 @@ send "diag gpio mode 0\n" expect "out" expect_line "Done" +send "diag cw start\n" +expect_line "Done" + +send "diag cw stop\n" +expect_line "Done" + +send "diag rawpowersetting 112233\n" +expect_line "Done" + +send "diag rawpowersetting\n" +expect "112233" +expect_line "Done" + +send "diag rawpowersetting enable\n" +expect_line "Done" + +send "diag rawpowersetting disable\n" +expect_line "Done" + send "diag invalid_commad\n" expect "Error 35: InvalidCommand" diff --git a/tests/scripts/expect/posix-power-calibration.exp b/tests/scripts/expect/posix-power-calibration.exp new file mode 100755 index 000000000..a8f15fb29 --- /dev/null +++ b/tests/scripts/expect/posix-power-calibration.exp @@ -0,0 +1,74 @@ +#!/usr/bin/expect -f +# +# 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 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 "diag start\n" +expect "start diagnostics mode" +expect "status 0x00" +expect_line "Done" + +send "diag powersettings 11\n" +expect "failed" +expect "status 0x17" +expect_line "Error 23: NotFound" + +send "diag powersettings\n" +expect "| StartCh | EndCh | TargetPower | ActualPower | RawPowerSetting |" +expect "+---------+-------+-------------+-------------+-----------------+" +expect_line "Done" + +send "diag stop\n" +expect_line "Done" + +send "region US\n" +expect_line "Done" + +send "diag start\n" +expect "start diagnostics mode" +expect "status 0x00" +expect_line "Done" + +send "diag powersettings 11\n" +expect -re {TargetPower\(0\.01dBm\): -?\d+} +expect -re {ActualPower\(0\.01dBm\): -?\d+} +expect -re {RawPowerSetting: [0-9]{1,16}} +expect_line "Done" + +send "diag powersettings\n" +expect "| StartCh | EndCh | TargetPower | ActualPower | RawPowerSetting |" +expect "+---------+-------+-------------+-------------+-----------------+" +for {set i 1} {$i <= 4} {incr i} { + expect -re "\\| +\\d+ | +\\d+ | +\\d+ | +\\d+ | +\[0-9\]\{1,16\} |" +} +expect_line "Done" + +dispose_all