mirror of
https://github.com/espressif/openthread.git
synced 2026-08-07 11:17:46 +00:00
[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:
@@ -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_
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
Reference in New Issue
Block a user