diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp index 605d1debaa1f..ae2622e696cc 100644 --- a/src/lib/spinel/radio_spinel.cpp +++ b/src/lib/spinel/radio_spinel.cpp @@ -1771,6 +1771,12 @@ void RadioSpinel::SetDiagOutputCallback(otPlatDiagOutputCallback aCallback, void mOutputContext = aContext; } +void RadioSpinel::GetDiagOutputCallback(otPlatDiagOutputCallback &aCallback, void *&aContext) +{ + aCallback = mOutputCallback; + aContext = mOutputContext; +} + otError RadioSpinel::PlatDiagProcess(const char *aString) { return Set(SPINEL_PROP_NEST_STREAM_MFG, SPINEL_DATATYPE_UTF8_S, aString); diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index 4d30ff893fce..62027c2d8869 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -695,6 +695,15 @@ class RadioSpinel : private Logger * */ void SetDiagOutputCallback(otPlatDiagOutputCallback aCallback, void *aContext); + + /** + * Gets the diag output callback. + * + * @param[out] aCallback A reference to a function that is called on outputting diag messages. + * @param[out] aContext A reference to the user context. + * + */ + void GetDiagOutputCallback(otPlatDiagOutputCallback &aCallback, void *&aContext); #endif /** diff --git a/src/posix/platform/README_RCP_CAPS_DIAG.md b/src/posix/platform/README_RCP_CAPS_DIAG.md index faf9909e88d7..d920792cb396 100644 --- a/src/posix/platform/README_RCP_CAPS_DIAG.md +++ b/src/posix/platform/README_RCP_CAPS_DIAG.md @@ -8,6 +8,7 @@ This module provides diag commands for checking RCP capabilities. - [capflags](#capflags) - [spinel](#spinel) +- [spinelspeed](#spinelspeed) - [srcmatchtable](#srcmatchtable) ## Command Details @@ -114,6 +115,16 @@ PROP_VALUE_SET RADIO_COEX_ENABLE -------------------------- OK Done ``` +### spinelspeed + +Check the speed of Spinel interface. + +```bash +> diag rcpcaps spinelspeed +SpinelSpeed ----------------------------------------------- 34414843 bps +Done +``` + ### srcmatchtable Check the source match table size supported by the RCP. diff --git a/src/posix/platform/rcp_caps_diag.cpp b/src/posix/platform/rcp_caps_diag.cpp index e972f0e48941..41762203058d 100644 --- a/src/posix/platform/rcp_caps_diag.cpp +++ b/src/posix/platform/rcp_caps_diag.cpp @@ -28,6 +28,8 @@ #include "rcp_caps_diag.hpp" +#include "lib/utils/math.hpp" + #if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE namespace ot { namespace Posix { @@ -465,6 +467,10 @@ otError RcpCapsDiag::DiagProcess(char *aArgs[], uint8_t aArgsLength) { ProcessSpinel(); } + else if (strcmp(aArgs[1], "spinelspeed") == 0) + { + ProcessSpinelSpeed(); + } else { error = OT_ERROR_INVALID_COMMAND; @@ -680,6 +686,87 @@ void RcpCapsDiag::OutputExtendedSrcMatchTableSize(void) OutputFormat("ExtendedSrcMatchTableSize", num); } +void RcpCapsDiag::HandleDiagOutput(const char *aFormat, va_list aArguments, void *aContext) +{ + static_cast(aContext)->HandleDiagOutput(aFormat, aArguments); +} + +void RcpCapsDiag::HandleDiagOutput(const char *aFormat, va_list aArguments) +{ + int rval; + + VerifyOrExit(mDiagOutput != nullptr && mDiagOutputLength != 0); + rval = vsnprintf(mDiagOutput, mDiagOutputLength, aFormat, aArguments); + VerifyOrExit(rval >= 0); + + rval = (rval > mDiagOutputLength) ? mDiagOutputLength : rval; + mDiagOutput += rval; + mDiagOutputLength -= rval; + +exit: + return; +} + +void RcpCapsDiag::ProcessSpinelSpeed(void) +{ + static constexpr uint32_t kUsPerSec = 1000000; + static constexpr uint8_t kBitsPerByte = 8; + static constexpr uint8_t kSpinelHeaderSize = 4; + static constexpr uint8_t kZeroTerminatorSize = 1; + static constexpr char kEchoCmd[] = "echo "; + static constexpr uint8_t kEchoPayloadLength = 200; + static constexpr uint8_t kNumTests = 100; + + otError error = OT_ERROR_NONE; + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE] = {0}; + char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; + uint16_t echoPayloadLength; + uint64_t startTimestamp; + uint64_t endTimestamp; + uint64_t sumTime = 0; + uint64_t sumLength = 0; + uint64_t speed; + otPlatDiagOutputCallback callback; + void *context; + + mRadioSpinel.GetDiagOutputCallback(callback, context); + mRadioSpinel.SetDiagOutputCallback(HandleDiagOutput, this); + + strncpy(cmd, kEchoCmd, sizeof(cmd) - 1); + echoPayloadLength = sizeof(cmd) - strlen(cmd) - 1; + echoPayloadLength = Lib::Utils::Min(kEchoPayloadLength, echoPayloadLength); + memset(cmd + strlen(cmd), '1', echoPayloadLength); + + for (uint16_t i = 0; i < kNumTests; i++) + { + output[0] = '\0'; + mDiagOutput = output; + mDiagOutputLength = sizeof(output); + startTimestamp = otPlatTimeGet(); + + SuccessOrExit(error = mRadioSpinel.PlatDiagProcess(cmd)); + + endTimestamp = otPlatTimeGet(); + sumTime += endTimestamp - startTimestamp; + sumLength += kSpinelHeaderSize + strlen(cmd) + kZeroTerminatorSize + kSpinelHeaderSize + strlen(output) + + kZeroTerminatorSize; + } + + mRadioSpinel.SetDiagOutputCallback(callback, context); + +exit: + if (error == OT_ERROR_NONE) + { + speed = (sumLength * kBitsPerByte * kUsPerSec) / sumTime; + snprintf(output, sizeof(output), "%lu bps", ToUlong(speed)); + OutputFormat("SpinelSpeed", output); + } + else + { + Output("Failed to test the Spinel speed: %s", otThreadErrorToString(error)); + } +} + void RcpCapsDiag::OutputFormat(const char *aName, const char *aValue) { static constexpr uint8_t kMaxNameLength = 56; diff --git a/src/posix/platform/rcp_caps_diag.hpp b/src/posix/platform/rcp_caps_diag.hpp index 0431f6167d7f..59719b4506b3 100644 --- a/src/posix/platform/rcp_caps_diag.hpp +++ b/src/posix/platform/rcp_caps_diag.hpp @@ -62,6 +62,8 @@ class RcpCapsDiag : mRadioSpinel(aRadioSpinel) , mOutputCallback(nullptr) , mOutputContext(nullptr) + , mDiagOutput(nullptr) + , mDiagOutputLength(0) { } @@ -111,6 +113,7 @@ class RcpCapsDiag static constexpr uint16_t kMaxNumChildren = 512; void ProcessSpinel(void); + void ProcessSpinelSpeed(void); void ProcessCapabilityFlags(void); void ProcessSrcMatchTable(void); void TestSpinelCommands(Category aCategory); @@ -125,6 +128,10 @@ class RcpCapsDiag bool IsSpinelCapabilitySupported(const uint8_t *aCapsData, spinel_size_t aCapsLength, uint32_t aCapability); void OutputExtendedSrcMatchTableSize(void); void OutputShortSrcMatchTableSize(void); + + static void HandleDiagOutput(const char *aFormat, va_list aArguments, void *aContext); + void HandleDiagOutput(const char *aFormat, va_list aArguments); + void OutputFormat(const char *aName, const char *aValue); void OutputFormat(const char *aName, uint32_t aValue); void OutputResult(const SpinelEntry &aEntry, otError error); @@ -139,6 +146,8 @@ class RcpCapsDiag Spinel::RadioSpinel &mRadioSpinel; otPlatDiagOutputCallback mOutputCallback; void *mOutputContext; + char *mDiagOutput; + uint16_t mDiagOutputLength; }; } // namespace Posix