diff --git a/src/posix/platform/README_RCP_CAPS_DIAG.md b/src/posix/platform/README_RCP_CAPS_DIAG.md index 73322ad24..c4f675f6e 100644 --- a/src/posix/platform/README_RCP_CAPS_DIAG.md +++ b/src/posix/platform/README_RCP_CAPS_DIAG.md @@ -6,10 +6,49 @@ This module provides diag commands for checking RCP capabilities. ## Command List +- [capflags](#capflags) - [spinel](#spinel) ## Command Details +### capflags + +Check RCP's radio and spinel capbility flags. + +```bash +> diag rcpcaps capflags + +Radio Capbility Flags : + +Thread Version >= 1.1 : +RADIO_CAPS_ACK_TIMEOUT ------------------------------------ OK +RADIO_CAPS_TRANSMIT_RETRIES ------------------------------- OK +RADIO_CAPS_CSMA_BACKOFF ----------------------------------- OK + +Thread Version >= 1.2 : +RADIO_CAPS_TRANSMIT_SEC ----------------------------------- OK +RADIO_CAPS_TRANSMIT_TIMING -------------------------------- OK + +Utils : +RADIO_CAPS_ENERGY_SCAN ------------------------------------ OK +RADIO_CAPS_SLEEP_TO_TX ------------------------------------ NotSupported +RADIO_CAPS_RECEIVE_TIMING --------------------------------- NotSupported +RADIO_CAPS_RX_ON_WHEN_IDLE -------------------------------- NotSupported + +Spinel Capbility Flags : + +Basic : +SPINEL_CAPS_CONFIG_RADIO ---------------------------------- OK +SPINEL_CAPS_MAC_RAW --------------------------------------- OK +SPINEL_CAPS_RCP_API_VERSION ------------------------------- OK + +Utils : +SPINEL_CAPS_OPENTHREAD_LOG_METADATA ----------------------- NotSupported +SPINEL_CAPS_RCP_MIN_HOST_API_VERSION ---------------------- OK +SPINEL_CAPS_RCP_RESET_TO_BOOTLOADER ----------------------- NotSupported +Done +``` + ### spinel Check which Spinel commands RCP supports. @@ -19,14 +58,57 @@ Check which Spinel commands RCP supports. Basic : PROP_VALUE_GET CAPS --------------------------------------- OK +PROP_VALUE_GET PROTOCOL_VERSION --------------------------- OK +PROP_VALUE_GET RADIO_CAPS --------------------------------- OK +PROP_VALUE_GET RCP_API_VERSION ---------------------------- OK +PROP_VALUE_GET NCP_VERSION -------------------------------- OK Thread Version >= 1.1 : PROP_VALUE_SET PHY_CHAN ----------------------------------- OK +PROP_VALUE_SET PHY_ENABLED -------------------------------- OK +PROP_VALUE_SET MAC_15_4_PANID ----------------------------- OK +PROP_VALUE_SET MAC_15_4_LADDR ----------------------------- OK +PROP_VALUE_SET MAC_15_4_SADDR ----------------------------- OK +PROP_VALUE_SET MAC_RAW_STREAM_ENABLED --------------------- OK +PROP_VALUE_SET MAC_SCAN_MASK ------------------------------ OK +PROP_VALUE_SET MAC_SCAN_PERIOD ---------------------------- OK +PROP_VALUE_SET MAC_SCAN_STATE ----------------------------- OK +PROP_VALUE_SET MAC_SRC_MATCH_ENABLED ---------------------- OK +PROP_VALUE_SET MAC_SRC_MATCH_SHORT_ADDRESSES -------------- OK +PROP_VALUE_SET MAC_SRC_MATCH_EXTENDED_ADDRESSES ----------- OK +PROP_VALUE_GET HWADDR ------------------------------------- OK +PROP_VALUE_GET PHY_CHAN_PREFERRED ------------------------- OK +PROP_VALUE_GET PHY_CHAN_SUPPORTED ------------------------- OK +PROP_VALUE_GET PHY_RSSI ----------------------------------- OK +PROP_VALUE_GET PHY_RX_SENSITIVITY ------------------------- OK +PROP_VALUE_INSERT MAC_SRC_MATCH_SHORT_ADDRESSES ----------- OK +PROP_VALUE_INSERT MAC_SRC_MATCH_EXTENDED_ADDRESSES -------- OK +PROP_VALUE_REMOVE MAC_SRC_MATCH_SHORT_ADDRESSES ----------- OK +PROP_VALUE_REMOVE MAC_SRC_MATCH_EXTENDED_ADDRESSES -------- OK Thread Version >= 1.2 : PROP_VALUE_SET ENH_ACK_PROBING ---------------------------- NotImplemented +PROP_VALUE_SET RCP_MAC_FRAME_COUNTER ---------------------- OK +PROP_VALUE_SET RCP_MAC_KEY -------------------------------- OK +PROP_VALUE_GET CSL_ACCURACY ------------------------------- OK +PROP_VALUE_GET CSL_UNCERTAINTY ---------------------------- OK +PROP_VALUE_GET TIMESTAMP ---------------------------------- OK -Optional : +Utils : +PROP_VALUE_SET MAC_PROMISCUOUS_MODE ----------------------- OK PROP_VALUE_GET PHY_CCA_THRESHOLD -------------------------- OK +PROP_VALUE_GET PHY_FEM_LNA_GAIN --------------------------- OK +PROP_VALUE_GET PHY_REGION_CODE ---------------------------- OK +PROP_VALUE_GET PHY_TX_POWER ------------------------------- OK +PROP_VALUE_GET RADIO_COEX_ENABLE -------------------------- OK +PROP_VALUE_GET RADIO_COEX_METRICS ------------------------- OK +PROP_VALUE_GET RCP_MIN_HOST_API_VERSION ------------------- OK +PROP_VALUE_SET PHY_CCA_THRESHOLD -------------------------- OK +PROP_VALUE_SET PHY_CHAN_MAX_POWER ------------------------- OK +PROP_VALUE_SET PHY_CHAN_TARGET_POWER ---------------------- OK +PROP_VALUE_SET PHY_FEM_LNA_GAIN --------------------------- OK +PROP_VALUE_SET PHY_REGION_CODE ---------------------------- OK +PROP_VALUE_SET PHY_TX_POWER ------------------------------- OK +PROP_VALUE_SET RADIO_COEX_ENABLE -------------------------- OK Done ``` diff --git a/src/posix/platform/rcp_caps_diag.cpp b/src/posix/platform/rcp_caps_diag.cpp index b0f15e494..7a2242e38 100644 --- a/src/posix/platform/rcp_caps_diag.cpp +++ b/src/posix/platform/rcp_caps_diag.cpp @@ -453,7 +453,11 @@ otError RcpCapsDiag::DiagProcess(char *aArgs[], uint8_t aArgsLength) VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS); - if (strcmp(aArgs[1], "spinel") == 0) + if (strcmp(aArgs[1], "capflags") == 0) + { + ProcessCapabilityFlags(); + } + else if (strcmp(aArgs[1], "spinel") == 0) { ProcessSpinel(); } @@ -498,22 +502,146 @@ void RcpCapsDiag::SetDiagOutputCallback(otPlatDiagOutputCallback aCallback, void mOutputContext = aContext; } +void RcpCapsDiag::ProcessCapabilityFlags(void) +{ + TestRadioCapbilityFlags(); + TestSpinelCapbilityFlags(); +} + +void RcpCapsDiag::TestRadioCapbilityFlags(void) +{ + static constexpr uint32_t kRadioThread11Flags[] = {OT_RADIO_CAPS_ACK_TIMEOUT, OT_RADIO_CAPS_TRANSMIT_RETRIES, + OT_RADIO_CAPS_CSMA_BACKOFF}; + static constexpr uint32_t kRadioThread12Flags[] = {OT_RADIO_CAPS_TRANSMIT_SEC, OT_RADIO_CAPS_TRANSMIT_TIMING}; + static constexpr uint32_t kRadioUtilsFlags[] = {OT_RADIO_CAPS_ENERGY_SCAN, OT_RADIO_CAPS_SLEEP_TO_TX, + OT_RADIO_CAPS_RECEIVE_TIMING, OT_RADIO_CAPS_RX_ON_WHEN_IDLE}; + otError error; + unsigned int radioCaps; + + SuccessOrExit(error = mRadioSpinel.Get(SPINEL_PROP_RADIO_CAPS, SPINEL_DATATYPE_UINT_PACKED_S, &radioCaps)); + + Output("\r\nRadio Capbility Flags :\r\n"); + + OutputRadioCapFlags(kCategoryThread1_1, static_cast(radioCaps), kRadioThread11Flags, + OT_ARRAY_LENGTH(kRadioThread11Flags)); + OutputRadioCapFlags(kCategoryThread1_2, static_cast(radioCaps), kRadioThread12Flags, + OT_ARRAY_LENGTH(kRadioThread12Flags)); + OutputRadioCapFlags(kCategoryUtils, static_cast(radioCaps), kRadioUtilsFlags, + OT_ARRAY_LENGTH(kRadioUtilsFlags)); + +exit: + if (error != OT_ERROR_NONE) + { + Output("Failed to get radio capability flags: %s", otThreadErrorToString(error)); + } + + return; +} + +void RcpCapsDiag::OutputRadioCapFlags(Category aCategory, + uint32_t aRadioCaps, + const uint32_t *aFlags, + uint16_t aNumbFlags) +{ + Output("\r\n%s :\r\n", CategoryToString(aCategory)); + for (uint16_t i = 0; i < aNumbFlags; i++) + { + OutputFormat(RadioCapbilityToString(aFlags[i]), SupportToString((aRadioCaps & aFlags[i]) > 0)); + } +} + +void RcpCapsDiag::TestSpinelCapbilityFlags(void) +{ + static constexpr uint8_t kCapsBufferSize = 100; + static constexpr uint32_t kSpinelBasicFlags[] = {SPINEL_CAP_CONFIG_RADIO, SPINEL_CAP_MAC_RAW, + SPINEL_CAP_RCP_API_VERSION}; + static constexpr uint32_t kSpinelUtilsFlags[] = { + SPINEL_CAP_OPENTHREAD_LOG_METADATA, SPINEL_CAP_RCP_MIN_HOST_API_VERSION, SPINEL_CAP_RCP_RESET_TO_BOOTLOADER}; + otError error; + uint8_t capsBuffer[kCapsBufferSize]; + spinel_size_t capsLength = sizeof(capsBuffer); + + SuccessOrExit(error = mRadioSpinel.Get(SPINEL_PROP_CAPS, SPINEL_DATATYPE_DATA_S, capsBuffer, &capsLength)); + + Output("\r\nSpinel Capbility Flags :\r\n"); + + OutputSpinelCapFlags(kCategoryBasic, capsBuffer, capsLength, kSpinelBasicFlags, OT_ARRAY_LENGTH(kSpinelBasicFlags)); + OutputSpinelCapFlags(kCategoryUtils, capsBuffer, capsLength, kSpinelUtilsFlags, OT_ARRAY_LENGTH(kSpinelUtilsFlags)); + +exit: + if (error != OT_ERROR_NONE) + { + Output("Failed to get Spinel capbility flags: %s", otThreadErrorToString(error)); + } + + return; +} + +void RcpCapsDiag::OutputSpinelCapFlags(Category aCategory, + const uint8_t *aCapsData, + spinel_size_t aCapsLength, + const uint32_t *aFlags, + uint16_t aNumbFlags) +{ + static constexpr uint8_t kCapsNameSize = 40; + char capName[kCapsNameSize]; + + Output("\r\n%s :\r\n", CategoryToString(aCategory)); + + for (uint16_t i = 0; i < aNumbFlags; i++) + { + snprintf(capName, sizeof(capName), "SPINEL_CAPS_%s", spinel_capability_to_cstr(aFlags[i])); + OutputFormat(capName, SupportToString(IsSpinelCapabilitySupported(aCapsData, aCapsLength, aFlags[i]))); + } +} + +bool RcpCapsDiag::IsSpinelCapabilitySupported(const uint8_t *aCapsData, spinel_size_t aCapsLength, uint32_t aCapability) +{ + bool ret = false; + + while (aCapsLength > 0) + { + unsigned int capability; + spinel_ssize_t unpacked; + + unpacked = spinel_datatype_unpack(aCapsData, aCapsLength, SPINEL_DATATYPE_UINT_PACKED_S, &capability); + VerifyOrExit(unpacked > 0); + VerifyOrExit(capability != aCapability, ret = true); + + aCapsData += unpacked; + aCapsLength -= static_cast(unpacked); + } + +exit: + return ret; +} + +void RcpCapsDiag::OutputFormat(const char *aName, const char *aValue) +{ + static constexpr uint8_t kMaxNameLength = 56; + static const char kPadding[] = "----------------------------------------------------------"; + uint16_t actualLength = static_cast(strlen(aName)); + uint16_t paddingOffset = (actualLength > kMaxNameLength) ? kMaxNameLength : actualLength; + + static_assert(kMaxNameLength < sizeof(kPadding), "Padding bytes are too short"); + + Output("%.*s %s %s\r\n", kMaxNameLength, aName, &kPadding[paddingOffset], aValue); +} + void RcpCapsDiag::OutputResult(const SpinelEntry &aEntry, otError error) { static constexpr uint8_t kSpaceLength = 1; static constexpr uint8_t kMaxCommandStringLength = 20; static constexpr uint8_t kMaxKeyStringLength = 35; - static constexpr uint16_t kMaxLength = kMaxCommandStringLength + kMaxKeyStringLength + kSpaceLength; - static const char kPadding[] = "----------------------------------------------------------"; - const char *commandString = spinel_command_to_cstr(aEntry.mCommand); - const char *keyString = spinel_prop_key_to_cstr(aEntry.mKey); - uint16_t actualLength = static_cast(strlen(commandString) + strlen(keyString) + kSpaceLength); - uint16_t paddingOffset = (actualLength > kMaxLength) ? kMaxLength : actualLength; + static constexpr uint16_t kMaxBufferLength = + kMaxCommandStringLength + kMaxKeyStringLength + kSpaceLength + 1 /* size of '\0' */; + char buffer[kMaxBufferLength] = {0}; + const char *commandString = spinel_command_to_cstr(aEntry.mCommand); + const char *keyString = spinel_prop_key_to_cstr(aEntry.mKey); - static_assert(kMaxLength < sizeof(kPadding), "Padding bytes are too short"); - - Output("%.*s %.*s %s %s\r\n", kMaxCommandStringLength, commandString, kMaxKeyStringLength, keyString, - &kPadding[paddingOffset], otThreadErrorToString(error)); + snprintf(buffer, sizeof(buffer), "%.*s %.*s", kMaxCommandStringLength, commandString, kMaxKeyStringLength, + keyString); + OutputFormat(buffer, otThreadErrorToString(error)); } void RcpCapsDiag::Output(const char *aFormat, ...) @@ -547,6 +675,45 @@ const char *RcpCapsDiag::CategoryToString(Category aCategory) return (aCategory < OT_ARRAY_LENGTH(kCategoryStrings)) ? kCategoryStrings[aCategory] : "invalid"; } +const char *RcpCapsDiag::SupportToString(bool aSupport) { return aSupport ? "OK" : "NotSupported"; } + +const char *RcpCapsDiag::RadioCapbilityToString(uint32_t aCapability) +{ + static const char *const kCapbilityStrings[] = { + "RADIO_CAPS_ACK_TIMEOUT", // (1 << 0) OT_RADIO_CAPS_ACK_TIMEOUT + "RADIO_CAPS_ENERGY_SCAN", // (1 << 1) OT_RADIO_CAPS_ENERGY_SCAN + "RADIO_CAPS_TRANSMIT_RETRIES", // (1 << 2) OT_RADIO_CAPS_TRANSMIT_RETRIES + "RADIO_CAPS_CSMA_BACKOFF", // (1 << 3) OT_RADIO_CAPS_CSMA_BACKOFF + "RADIO_CAPS_SLEEP_TO_TX", // (1 << 4) OT_RADIO_CAPS_SLEEP_TO_TX + "RADIO_CAPS_TRANSMIT_SEC", // (1 << 5) OT_RADIO_CAPS_TRANSMIT_SEC + "RADIO_CAPS_TRANSMIT_TIMING", // (1 << 6) OT_RADIO_CAPS_TRANSMIT_TIMING + "RADIO_CAPS_RECEIVE_TIMING", // (1 << 7) OT_RADIO_CAPS_RECEIVE_TIMING + "RADIO_CAPS_RX_ON_WHEN_IDLE", // (1 << 8) OT_RADIO_CAPS_RX_ON_WHEN_IDLE + }; + const char *string = "invalid"; + uint16_t index = 0; + + static_assert(OT_RADIO_CAPS_ACK_TIMEOUT == 1 << 0, "OT_RADIO_CAPS_ACK_TIMEOUT value is incorrect"); + static_assert(OT_RADIO_CAPS_ENERGY_SCAN == 1 << 1, "OT_RADIO_CAPS_ENERGY_SCAN value is incorrect"); + static_assert(OT_RADIO_CAPS_TRANSMIT_RETRIES == 1 << 2, "OT_RADIO_CAPS_TRANSMIT_RETRIES value is incorrect"); + static_assert(OT_RADIO_CAPS_CSMA_BACKOFF == 1 << 3, "OT_RADIO_CAPS_CSMA_BACKOFF value is incorrect"); + static_assert(OT_RADIO_CAPS_SLEEP_TO_TX == 1 << 4, "OT_RADIO_CAPS_SLEEP_TO_TX value is incorrect"); + static_assert(OT_RADIO_CAPS_TRANSMIT_SEC == 1 << 5, "OT_RADIO_CAPS_TRANSMIT_SEC value is incorrect"); + static_assert(OT_RADIO_CAPS_TRANSMIT_TIMING == 1 << 6, "OT_RADIO_CAPS_TRANSMIT_TIMING value is incorrect"); + static_assert(OT_RADIO_CAPS_RECEIVE_TIMING == 1 << 7, "OT_RADIO_CAPS_RECEIVE_TIMING value is incorrect"); + static_assert(OT_RADIO_CAPS_RX_ON_WHEN_IDLE == 1 << 8, "OT_RADIO_CAPS_RX_ON_WHEN_IDLE value is incorrect"); + + for (; !(aCapability & 0x1); (aCapability >>= 1), index++) + { + VerifyOrExit(index < OT_ARRAY_LENGTH(kCapbilityStrings)); + } + + string = kCapbilityStrings[index]; + +exit: + return string; +} + } // namespace Posix } // namespace ot #endif // OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE diff --git a/src/posix/platform/rcp_caps_diag.hpp b/src/posix/platform/rcp_caps_diag.hpp index f799f8187..05e06498b 100644 --- a/src/posix/platform/rcp_caps_diag.hpp +++ b/src/posix/platform/rcp_caps_diag.hpp @@ -109,11 +109,24 @@ private: }; void ProcessSpinel(void); + void ProcessCapabilityFlags(void); void TestSpinelCommands(Category aCategory); + void TestRadioCapbilityFlags(void); + void OutputRadioCapFlags(Category aCategory, uint32_t aRadioCaps, const uint32_t *aFlags, uint16_t aNumbFlags); + void TestSpinelCapbilityFlags(void); + void OutputSpinelCapFlags(Category aCategory, + const uint8_t *aCapsData, + spinel_size_t aCapsLength, + const uint32_t *aFlags, + uint16_t aNumbFlags); + bool IsSpinelCapabilitySupported(const uint8_t *aCapsData, spinel_size_t aCapsLength, uint32_t aCapability); + void OutputFormat(const char *aName, const char *aValue); void OutputResult(const SpinelEntry &aEntry, otError error); void Output(const char *aFormat, ...); - const char *CategoryToString(Category aCategory); + static const char *SupportToString(bool aSupport); + static const char *RadioCapbilityToString(uint32_t aCapability); + static const char *CategoryToString(Category aCategory); static const struct SpinelEntry sSpinelEntries[];