[posix] add RCP capability diag module (#10335)

This commit adds a RCP capability diag module for verifying the RCP's
capability. This commit is an initial commit that only tested several
Spinel commands.
This commit is contained in:
Zhanglong Xia
2024-06-06 08:40:47 -07:00
committed by GitHub
parent d6eb56c319
commit 89b42178ad
7 changed files with 382 additions and 0 deletions
+1
View File
@@ -143,6 +143,7 @@ add_library(openthread-posix
power.cpp
radio.cpp
radio_url.cpp
rcp_caps_diag.cpp
resolver.cpp
settings.cpp
spinel_manager.cpp
@@ -0,0 +1,32 @@
# OpenThread Diagnostics - RCP Capability Diagnostics Example
This module provides diag commands for checking RCP capabilities.
`OPENTHREAD_CONFIG_DIAG_ENABLE` and `OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE` are required.
## Command List
- [spinel](#spinel)
## Command Details
### spinel
Check which Spinel commands RCP supports.
```bash
> diag rcpcaps spinel
Basic :
PROP_VALUE_GET CAPS --------------------------------------- OK
Thread Version >= 1.1 :
PROP_VALUE_SET PHY_CHAN ----------------------------------- OK
Thread Version >= 1.2 :
PROP_VALUE_SET ENH_ACK_PROBING ---------------------------- NotImplemented
Optional :
PROP_VALUE_GET PHY_CCA_THRESHOLD -------------------------- OK
Done
```
@@ -429,4 +429,14 @@
#define OPENTHREAD_POSIX_CONFIG_TREL_TX_PACKET_POOL_SIZE 5
#endif
/**
* @def OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE
*
* Define as 1 to enable RCP capability diagnostic support.
*
*/
#ifndef OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE
#define OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE OPENTHREAD_CONFIG_DIAG_ENABLE
#endif
#endif // OPENTHREAD_PLATFORM_POSIX_CONFIG_H_
+14
View File
@@ -64,6 +64,9 @@ const char Radio::kLogModuleName[] = "Radio";
Radio::Radio(void)
: mRadioUrl(nullptr)
, mRadioSpinel()
#if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE
, mRcpCapsDiag(mRadioSpinel)
#endif
{
}
@@ -194,6 +197,10 @@ exit:
ot::Spinel::RadioSpinel &GetRadioSpinel(void) { return sRadio.GetRadioSpinel(); }
#if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE
ot::Posix::RcpCapsDiag &GetRcpCapsDiag(void) { return sRadio.GetRcpCapsDiag(); }
#endif
void platformRadioDeinit(void) { GetRadioSpinel().Deinit(); }
void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64)
@@ -503,6 +510,13 @@ otError otPlatDiagProcess(otInstance *aInstance,
char *cur = cmd;
char *end = cmd + sizeof(cmd);
#if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE
if (strcmp(aArgs[0], "rcpcaps") == 0)
{
return GetRcpCapsDiag().DiagProcess(aArgs, aArgsLength, aOutput, aOutputMaxLen);
}
#endif
for (uint8_t index = 0; (index < aArgsLength) && (cur < end); index++)
{
cur += snprintf(cur, static_cast<size_t>(end - cur), "%s ", aArgs[index]);
+15
View File
@@ -32,6 +32,7 @@
#include "hdlc_interface.hpp"
#include "logger.hpp"
#include "radio_url.hpp"
#include "rcp_caps_diag.hpp"
#include "spi_interface.hpp"
#include "spinel_manager.hpp"
#include "vendor_interface.hpp"
@@ -86,6 +87,16 @@ public:
*/
Spinel::RadioSpinel &GetRadioSpinel(void) { return mRadioSpinel; }
/**
* Acts as an accessor to the RCP capability diagnostic instance used by the radio.
*
* @returns A reference to the RCP capability diagnostic instance.
*
*/
#if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE
RcpCapsDiag &GetRcpCapsDiag(void) { return mRcpCapsDiag; }
#endif
private:
void ProcessRadioUrl(const RadioUrl &aRadioUrl);
void ProcessMaxPowerTable(const RadioUrl &aRadioUrl);
@@ -110,6 +121,10 @@ private:
#else
Spinel::RadioSpinel mRadioSpinel;
#endif
#if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE
RcpCapsDiag mRcpCapsDiag;
#endif
};
} // namespace Posix
+191
View File
@@ -0,0 +1,191 @@
/*
* Copyright (c) 2024, 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.
*/
#include "rcp_caps_diag.hpp"
#if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE
namespace ot {
namespace Posix {
#define SPINEL_ENTRY(aCategory, aCommand, aKey) \
{ \
aCategory, aCommand, aKey, &RcpCapsDiag::HandleSpinelCommand<aCommand, aKey> \
}
template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_CCA_THRESHOLD>(void)
{
int8_t ccaThreshold;
return mRadioSpinel.GetCcaEnergyDetectThreshold(ccaThreshold);
}
template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_CHAN>(void)
{
static constexpr uint8_t kPhyChannel = 22;
return mRadioSpinel.Set(SPINEL_PROP_PHY_CHAN, SPINEL_DATATYPE_UINT8_S, kPhyChannel);
}
template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_CAPS>(void)
{
static constexpr uint8_t kCapsBufferSize = 100;
uint8_t capsBuffer[kCapsBufferSize];
spinel_size_t capsLength = sizeof(capsBuffer);
return mRadioSpinel.Get(SPINEL_PROP_CAPS, SPINEL_DATATYPE_DATA_S, capsBuffer, &capsLength);
}
template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_RCP_ENH_ACK_PROBING>(void)
{
uint16_t shortAddress = 0x1122;
otExtAddress extAddress = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
uint8_t flags = SPINEL_THREAD_LINK_METRIC_PDU_COUNT | SPINEL_THREAD_LINK_METRIC_LQI |
SPINEL_THREAD_LINK_METRIC_LINK_MARGIN | SPINEL_THREAD_LINK_METRIC_RSSI;
return mRadioSpinel.Set(SPINEL_PROP_RCP_ENH_ACK_PROBING,
SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_UINT8_S, shortAddress,
extAddress.m8, flags);
}
const struct RcpCapsDiag::SpinelEntry RcpCapsDiag::sSpinelEntries[] = {
// Basic Spinel commands
SPINEL_ENTRY(kCategoryBasic, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_CAPS),
// Thread Version >= 1.1
SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_CHAN),
// Thread Version >= 1.2
SPINEL_ENTRY(kCategoryThread1_2, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_RCP_ENH_ACK_PROBING),
// Optional Spinel commands
SPINEL_ENTRY(kCategoryOptional, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_CCA_THRESHOLD),
};
otError RcpCapsDiag::DiagProcess(char *aArgs[], uint8_t aArgsLength, char *aOutput, size_t aOutputMaxLen)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS);
mOutputStart = aOutput;
mOutputEnd = aOutput + aOutputMaxLen;
if (strcmp(aArgs[1], "spinel") == 0)
{
ProcessSpinel();
}
else
{
error = OT_ERROR_INVALID_COMMAND;
}
mOutputStart = nullptr;
mOutputEnd = nullptr;
exit:
return error;
}
void RcpCapsDiag::ProcessSpinel(void)
{
for (uint8_t i = 0; i < kNumCategories; i++)
{
TestSpinelCommands(static_cast<Category>(i));
}
}
void RcpCapsDiag::TestSpinelCommands(Category aCategory)
{
otError error;
Output("\r\n%s :\r\n", CategoryToString(aCategory));
for (const SpinelEntry &entry : sSpinelEntries)
{
if (entry.mCategory != aCategory)
{
continue;
}
error = (this->*entry.mHandler)();
OutputResult(entry, error);
}
}
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<uint16_t>(strlen(commandString) + strlen(keyString) + kSpaceLength);
uint16_t paddingOffset = (actualLength > kMaxLength) ? kMaxLength : actualLength;
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));
}
void RcpCapsDiag::Output(const char *aFormat, ...)
{
va_list args;
va_start(args, aFormat);
if ((mOutputStart != nullptr) && (mOutputEnd != nullptr) && (mOutputStart < mOutputEnd))
{
mOutputStart += vsnprintf(mOutputStart, static_cast<size_t>(mOutputEnd - mOutputStart), aFormat, args);
}
va_end(args);
}
const char *RcpCapsDiag::CategoryToString(Category aCategory)
{
static const char *const kCategoryStrings[] = {
"Basic", // (0) kCategoryBasic
"Thread Version >= 1.1", // (1) kCategoryThread1_1
"Thread Version >= 1.2", // (2) kCategoryThread1_2
"Optional", // (3) kCategoryOptional
};
static_assert(kCategoryBasic == 0, "kCategoryBasic value is incorrect");
static_assert(kCategoryThread1_1 == 1, "kCategoryThread1_1 value is incorrect");
static_assert(kCategoryThread1_2 == 2, "kCategoryThread1_2 value is incorrect");
static_assert(kCategoryOptional == 3, "kCategoryOptional value is incorrect");
return (aCategory < OT_ARRAY_LENGTH(kCategoryStrings)) ? kCategoryStrings[aCategory] : "invalid";
}
} // namespace Posix
} // namespace ot
#endif // OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE
+119
View File
@@ -0,0 +1,119 @@
/*
* Copyright (c) 2024, 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.
*/
/**
* @file
* This file contains definitions for the RCP capability diagnostics module.
*/
#ifndef OT_POSIX_PLATFORM_RCP_CAPS_DIAG_HPP_
#define OT_POSIX_PLATFORM_RCP_CAPS_DIAG_HPP_
#include "platform-posix.h"
#if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE
#include "lib/spinel/radio_spinel.hpp"
#include "lib/spinel/spinel.h"
#if !OPENTHREAD_CONFIG_DIAG_ENABLE
#error "OPENTHREAD_CONFIG_DIAG_ENABLE is required for OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE"
#endif
namespace ot {
namespace Posix {
class RcpCapsDiag
{
public:
/**
* Constructor initializes the object.
*
* @param[in] aRadioSpinel A reference to the Spinel::RadioSpinel instance.
*
*/
explicit RcpCapsDiag(Spinel::RadioSpinel &aRadioSpinel)
: mRadioSpinel(aRadioSpinel)
, mOutputStart(nullptr)
, mOutputEnd(nullptr)
{
}
/**
* Processes RCP capability diagnostics commands.
*
* @param[in] aArgs The arguments of diagnostics command line.
* @param[in] aArgsLength The number of arguments in @p aArgs.
* @param[out] aOutput The diagnostics execution result.
* @param[in] aOutputMaxLen The output buffer size.
*
* @retval OT_ERROR_INVALID_ARGS The command is supported but invalid arguments provided.
* @retval OT_ERROR_NONE The command is successfully processed.
* @retval OT_ERROR_INVALID_COMMAND The command is not valid or not supported.
*
*/
otError DiagProcess(char *aArgs[], uint8_t aArgsLength, char *aOutput, size_t aOutputMaxLen);
private:
template <uint32_t aCommand, spinel_prop_key_t aKey> otError HandleSpinelCommand(void);
typedef otError (RcpCapsDiag::*SpinelCommandHandler)(void);
enum Category : uint8_t
{
kCategoryBasic,
kCategoryThread1_1,
kCategoryThread1_2,
kCategoryOptional,
kNumCategories,
};
struct SpinelEntry
{
Category mCategory;
uint32_t mCommand;
spinel_prop_key_t mKey;
RcpCapsDiag::SpinelCommandHandler mHandler;
};
void ProcessSpinel(void);
void TestSpinelCommands(Category aCategory);
void OutputResult(const SpinelEntry &aEntry, otError error);
void Output(const char *aFormat, ...);
const char *CategoryToString(Category aCategory);
static const struct SpinelEntry sSpinelEntries[];
Spinel::RadioSpinel &mRadioSpinel;
char *mOutputStart;
char *mOutputEnd;
};
} // namespace Posix
} // namespace ot
#endif // OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE
#endif // OT_POSIX_PLATFORM_RCP_CAPS_DIAG_HPP_