Skip to content

Commit

Permalink
[posix] add RCP capability diag module
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
zhanglongxia committed May 30, 2024
1 parent 8b04e9c commit 4eb7409
Show file tree
Hide file tree
Showing 7 changed files with 381 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/posix/platform/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
33 changes: 33 additions & 0 deletions src/posix/platform/README_RCP_CAPS_DIAG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# 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
```

9 changes: 9 additions & 0 deletions src/posix/platform/openthread-posix-config.h
Original file line number Diff line number Diff line change
Expand Up @@ -429,4 +429,13 @@
#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 1
#endif
#endif // OPENTHREAD_PLATFORM_POSIX_CONFIG_H_
14 changes: 14 additions & 0 deletions src/posix/platform/radio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
}

Expand Down Expand Up @@ -194,6 +197,10 @@ void Radio::ProcessMaxPowerTable(const RadioUrl &aRadioUrl)

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)
Expand Down Expand Up @@ -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]);
Expand Down
15 changes: 15 additions & 0 deletions src/posix/platform/radio.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -86,6 +87,16 @@ class Radio : public Logger<Radio>
*/
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);
Expand All @@ -110,6 +121,10 @@ class Radio : public Logger<Radio>
#else
Spinel::RadioSpinel mRadioSpinel;
#endif

#if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE
RcpCapsDiag mRcpCapsDiag;
#endif
};

} // namespace Posix
Expand Down
190 changes: 190 additions & 0 deletions src/posix/platform/rcp_caps_diag.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
/*
* 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++)
{
TesSpinelCommands(static_cast<Category>(i));
}
}

void RcpCapsDiag::TesSpinelCommands(Category aCategory)
{
otError error;

Output("\r\n%s :\r\n", CategoryToString(aCategory));

for (const SpinelEntry &property : sSpinelEntries)
{
if (property.mCategory != aCategory)
{
continue;
}

error = (this->*property.mHandler)();
OutputResult(property, 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 = 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
Loading

0 comments on commit 4eb7409

Please sign in to comment.