5310 lines
155 KiB
C++
5310 lines
155 KiB
C++
/*
|
|
* Copyright (c) 2016, 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 implements the CLI interpreter.
|
|
*/
|
|
|
|
#include "cli.hpp"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <openthread/diag.h>
|
|
#include <openthread/dns.h>
|
|
#include <openthread/icmp6.h>
|
|
#include <openthread/link.h>
|
|
#include <openthread/logging.h>
|
|
#include <openthread/ncp.h>
|
|
#include <openthread/thread.h>
|
|
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
|
|
#include <openthread/network_time.h>
|
|
#endif
|
|
#if OPENTHREAD_FTD
|
|
#include <openthread/dataset_ftd.h>
|
|
#include <openthread/thread_ftd.h>
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
|
|
#include <openthread/border_router.h>
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
|
|
#include <openthread/server.h>
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
|
|
#include <openthread/child_supervision.h>
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
|
|
#include <openthread/platform/misc.h>
|
|
#endif
|
|
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
|
|
#include <openthread/backbone_router.h>
|
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
|
|
#include <openthread/backbone_router_ftd.h>
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
|
|
#include <openthread/link_metrics.h>
|
|
#endif
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
|
|
#include <openthread/channel_manager.h>
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
|
|
#include <openthread/channel_monitor.h>
|
|
#endif
|
|
#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX
|
|
#include <openthread/platform/debug_uart.h>
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
|
|
#include <openthread/trel.h>
|
|
#endif
|
|
|
|
#include "common/new.hpp"
|
|
#include "common/string.hpp"
|
|
#include "mac/channel_mask.hpp"
|
|
|
|
namespace ot {
|
|
namespace Cli {
|
|
|
|
Interpreter *Interpreter::sInterpreter = nullptr;
|
|
static OT_DEFINE_ALIGNED_VAR(sInterpreterRaw, sizeof(Interpreter), uint64_t);
|
|
|
|
Interpreter::Interpreter(Instance *aInstance, otCliOutputCallback aCallback, void *aContext)
|
|
: Output(aInstance, aCallback, aContext)
|
|
, mUserCommands(nullptr)
|
|
, mUserCommandsLength(0)
|
|
, mCommandIsPending(false)
|
|
, mTimer(*aInstance, HandleTimer, this)
|
|
#if OPENTHREAD_FTD || OPENTHREAD_MTD
|
|
#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
|
|
, mSntpQueryingInProgress(false)
|
|
#endif
|
|
, mDataset(*this)
|
|
, mNetworkData(*this)
|
|
, mUdp(*this)
|
|
#if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
|
|
, mTcp(*this)
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_COAP_API_ENABLE
|
|
, mCoap(*this)
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
|
|
, mCoapSecure(*this)
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
|
|
, mCommissioner(*this)
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_JOINER_ENABLE
|
|
, mJoiner(*this)
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
|
|
, mSrpClient(*this)
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
|
|
, mSrpServer(*this)
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
|
|
, mHistory(*this)
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
|
|
, mLocateInProgress(false)
|
|
#endif
|
|
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
|
|
{
|
|
#if OPENTHREAD_FTD
|
|
otThreadSetDiscoveryRequestCallback(GetInstancePtr(), &Interpreter::HandleDiscoveryRequest, this);
|
|
#endif
|
|
|
|
OutputPrompt();
|
|
}
|
|
|
|
void Interpreter::OutputResult(otError aError)
|
|
{
|
|
OT_ASSERT(mCommandIsPending);
|
|
|
|
VerifyOrExit(aError != OT_ERROR_PENDING);
|
|
|
|
if (aError == OT_ERROR_NONE)
|
|
{
|
|
OutputLine("Done");
|
|
}
|
|
else
|
|
{
|
|
OutputLine("Error %d: %s", aError, otThreadErrorToString(aError));
|
|
}
|
|
|
|
mCommandIsPending = false;
|
|
mTimer.Stop();
|
|
OutputPrompt();
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
const char *Interpreter::LinkModeToString(const otLinkModeConfig &aLinkMode, char (&aStringBuffer)[kLinkModeStringSize])
|
|
{
|
|
char *flagsPtr = &aStringBuffer[0];
|
|
|
|
if (aLinkMode.mRxOnWhenIdle)
|
|
{
|
|
*flagsPtr++ = 'r';
|
|
}
|
|
|
|
if (aLinkMode.mDeviceType)
|
|
{
|
|
*flagsPtr++ = 'd';
|
|
}
|
|
|
|
if (aLinkMode.mNetworkData)
|
|
{
|
|
*flagsPtr++ = 'n';
|
|
}
|
|
|
|
if (flagsPtr == &aStringBuffer[0])
|
|
{
|
|
*flagsPtr++ = '-';
|
|
}
|
|
|
|
*flagsPtr = '\0';
|
|
|
|
return aStringBuffer;
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_DIAG_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("diag")>(Arg aArgs[])
|
|
{
|
|
otError error;
|
|
char * args[kMaxArgs];
|
|
char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE];
|
|
|
|
// all diagnostics related features are processed within diagnostics module
|
|
Arg::CopyArgsToStringArray(aArgs, args);
|
|
|
|
error = otDiagProcessCmd(GetInstancePtr(), Arg::GetArgsLength(aArgs), args, output, sizeof(output));
|
|
|
|
OutputFormat("%s", output);
|
|
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
template <> otError Interpreter::Process<Cmd("version")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputLine("%s", otGetVersionString());
|
|
}
|
|
else if (aArgs[0] == "api")
|
|
{
|
|
OutputLine("%d", OPENTHREAD_API_VERSION);
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("reset")>(Arg aArgs[])
|
|
{
|
|
OT_UNUSED_VARIABLE(aArgs);
|
|
|
|
otInstanceReset(GetInstancePtr());
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
void Interpreter::ProcessLine(char *aBuf)
|
|
{
|
|
Arg args[kMaxArgs + 1];
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
OT_ASSERT(aBuf != nullptr);
|
|
|
|
// Ignore the command if another command is pending.
|
|
VerifyOrExit(!mCommandIsPending, args[0].Clear());
|
|
mCommandIsPending = true;
|
|
|
|
VerifyOrExit(StringLength(aBuf, kMaxLineLength) <= kMaxLineLength - 1, error = OT_ERROR_PARSE);
|
|
|
|
SuccessOrExit(error = Utils::CmdLineParser::ParseCmd(aBuf, args, kMaxArgs));
|
|
VerifyOrExit(!args[0].IsEmpty(), mCommandIsPending = false);
|
|
|
|
LogInput(args);
|
|
|
|
#if OPENTHREAD_CONFIG_DIAG_ENABLE
|
|
if (otDiagIsEnabled(GetInstancePtr()) && (args[0] != "diag") && (args[0] != "factoryreset"))
|
|
{
|
|
OutputLine("under diagnostics mode, execute 'diag stop' before running any other commands.");
|
|
ExitNow(error = OT_ERROR_INVALID_STATE);
|
|
}
|
|
#endif
|
|
|
|
error = ProcessCommand(args);
|
|
|
|
exit:
|
|
if ((error != OT_ERROR_NONE) || !args[0].IsEmpty())
|
|
{
|
|
OutputResult(error);
|
|
}
|
|
else if (!mCommandIsPending)
|
|
{
|
|
OutputPrompt();
|
|
}
|
|
}
|
|
|
|
otError Interpreter::ProcessUserCommands(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_INVALID_COMMAND;
|
|
|
|
for (uint8_t i = 0; i < mUserCommandsLength; i++)
|
|
{
|
|
if (aArgs[0] == mUserCommands[i].mName)
|
|
{
|
|
char *args[kMaxArgs];
|
|
|
|
Arg::CopyArgsToStringArray(aArgs, args);
|
|
mUserCommands[i].mCommand(mUserCommandsContext, Arg::GetArgsLength(aArgs) - 1, args + 1);
|
|
error = OT_ERROR_NONE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
void Interpreter::SetUserCommands(const otCliCommand *aCommands, uint8_t aLength, void *aContext)
|
|
{
|
|
mUserCommands = aCommands;
|
|
mUserCommandsLength = aLength;
|
|
mUserCommandsContext = aContext;
|
|
}
|
|
|
|
#if OPENTHREAD_FTD || OPENTHREAD_MTD
|
|
otError Interpreter::ParseEnableOrDisable(const Arg &aArg, bool &aEnable)
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArg == "enable")
|
|
{
|
|
aEnable = true;
|
|
}
|
|
else if (aArg == "disable")
|
|
{
|
|
aEnable = false;
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
otError Interpreter::ParseJoinerDiscerner(Arg &aArg, otJoinerDiscerner &aDiscerner)
|
|
{
|
|
otError error;
|
|
char * separator;
|
|
|
|
VerifyOrExit(!aArg.IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
|
|
separator = strstr(aArg.GetCString(), "/");
|
|
|
|
VerifyOrExit(separator != nullptr, error = OT_ERROR_NOT_FOUND);
|
|
|
|
SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(separator + 1, aDiscerner.mLength));
|
|
VerifyOrExit(aDiscerner.mLength > 0 && aDiscerner.mLength <= 64, error = OT_ERROR_INVALID_ARGS);
|
|
*separator = '\0';
|
|
error = aArg.ParseAsUint64(aDiscerner.mValue);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
|
|
|
|
otError Interpreter::ParsePingInterval(const Arg &aArg, uint32_t &aInterval)
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
const char * string = aArg.GetCString();
|
|
const uint32_t msFactor = 1000;
|
|
uint32_t factor = msFactor;
|
|
|
|
aInterval = 0;
|
|
|
|
while (*string)
|
|
{
|
|
if ('0' <= *string && *string <= '9')
|
|
{
|
|
// In the case of seconds, change the base of already calculated value.
|
|
if (factor == msFactor)
|
|
{
|
|
aInterval *= 10;
|
|
}
|
|
|
|
aInterval += static_cast<uint32_t>(*string - '0') * factor;
|
|
|
|
// In the case of milliseconds, change the multiplier factor.
|
|
if (factor != msFactor)
|
|
{
|
|
factor /= 10;
|
|
}
|
|
}
|
|
else if (*string == '.')
|
|
{
|
|
// Accept only one dot character.
|
|
VerifyOrExit(factor == msFactor, error = OT_ERROR_INVALID_ARGS);
|
|
|
|
// Start analyzing hundreds of milliseconds.
|
|
factor /= 10;
|
|
}
|
|
else
|
|
{
|
|
ExitNow(error = OT_ERROR_INVALID_ARGS);
|
|
}
|
|
|
|
string++;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#endif // OPENTHREAD_CONFIG_PING_SENDER_ENABLE
|
|
|
|
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("history")>(Arg aArgs[])
|
|
{
|
|
return mHistory.Process(aArgs);
|
|
}
|
|
#endif
|
|
|
|
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("ba")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0] == "port")
|
|
{
|
|
OutputLine("%hu", otBorderAgentGetUdpPort(GetInstancePtr()));
|
|
}
|
|
else if (aArgs[0] == "state")
|
|
{
|
|
static const char *const kStateStrings[] = {
|
|
"Stopped" // (0) OT_BORDER_AGENT_STATE_STOPPED
|
|
"Started", // (1) OT_BORDER_AGENT_STATE_STARTED
|
|
"Active", // (2) OT_BORDER_AGENT_STATE_ACTIVE
|
|
};
|
|
|
|
static_assert(0 == OT_BORDER_AGENT_STATE_STOPPED, "OT_BORDER_AGENT_STATE_STOPPED value is incorrect");
|
|
static_assert(1 == OT_BORDER_AGENT_STATE_STARTED, "OT_BORDER_AGENT_STATE_STARTED value is incorrect");
|
|
static_assert(2 == OT_BORDER_AGENT_STATE_ACTIVE, "OT_BORDER_AGENT_STATE_ACTIVE value is incorrect");
|
|
|
|
OutputLine("%s", Stringify(otBorderAgentGetState(GetInstancePtr()), kStateStrings));
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
|
|
|
|
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("br")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
bool enable;
|
|
|
|
if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
|
|
{
|
|
SuccessOrExit(error = otBorderRoutingSetEnabled(GetInstancePtr(), enable));
|
|
}
|
|
else if (aArgs[0] == "omrprefix")
|
|
{
|
|
otIp6Prefix omrPrefix;
|
|
|
|
SuccessOrExit(error = otBorderRoutingGetOmrPrefix(GetInstancePtr(), &omrPrefix));
|
|
OutputIp6PrefixLine(omrPrefix);
|
|
}
|
|
else if (aArgs[0] == "onlinkprefix")
|
|
{
|
|
otIp6Prefix onLinkPrefix;
|
|
|
|
SuccessOrExit(error = otBorderRoutingGetOnLinkPrefix(GetInstancePtr(), &onLinkPrefix));
|
|
OutputIp6PrefixLine(onLinkPrefix);
|
|
}
|
|
#if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
|
|
else if (aArgs[0] == "nat64prefix")
|
|
{
|
|
otIp6Prefix nat64Prefix;
|
|
|
|
SuccessOrExit(error = otBorderRoutingGetNat64Prefix(GetInstancePtr(), &nat64Prefix));
|
|
OutputIp6PrefixLine(nat64Prefix);
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
|
|
|
|
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
|
|
template <> otError Interpreter::Process<Cmd("bbr")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_INVALID_COMMAND;
|
|
otBackboneRouterConfig config;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
if (otBackboneRouterGetPrimary(GetInstancePtr(), &config) == OT_ERROR_NONE)
|
|
{
|
|
OutputLine("BBR Primary:");
|
|
OutputLine("server16: 0x%04X", config.mServer16);
|
|
OutputLine("seqno: %d", config.mSequenceNumber);
|
|
OutputLine("delay: %d secs", config.mReregistrationDelay);
|
|
OutputLine("timeout: %d secs", config.mMlrTimeout);
|
|
}
|
|
else
|
|
{
|
|
OutputLine("BBR Primary: None");
|
|
}
|
|
|
|
error = OT_ERROR_NONE;
|
|
}
|
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
|
|
else
|
|
{
|
|
if (aArgs[0] == "mgmt")
|
|
{
|
|
if (aArgs[1].IsEmpty())
|
|
{
|
|
ExitNow(error = OT_ERROR_INVALID_COMMAND);
|
|
}
|
|
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
else if (aArgs[1] == "dua")
|
|
{
|
|
uint8_t status;
|
|
otIp6InterfaceIdentifier *mlIid = nullptr;
|
|
otIp6InterfaceIdentifier iid;
|
|
|
|
SuccessOrExit(error = aArgs[2].ParseAsUint8(status));
|
|
|
|
if (!aArgs[3].IsEmpty())
|
|
{
|
|
SuccessOrExit(error = aArgs[3].ParseAsHexString(iid.mFields.m8));
|
|
mlIid = &iid;
|
|
VerifyOrExit(aArgs[4].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
}
|
|
|
|
otBackboneRouterConfigNextDuaRegistrationResponse(GetInstancePtr(), mlIid, status);
|
|
ExitNow();
|
|
}
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
|
|
else if (aArgs[1] == "mlr")
|
|
{
|
|
error = ProcessBackboneRouterMgmtMlr(aArgs + 2);
|
|
ExitNow();
|
|
}
|
|
#endif
|
|
}
|
|
SuccessOrExit(error = ProcessBackboneRouterLocal(aArgs));
|
|
}
|
|
|
|
exit:
|
|
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
|
|
|
|
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
|
|
otError Interpreter::ProcessBackboneRouterMgmtMlr(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_INVALID_COMMAND;
|
|
|
|
if (aArgs[0] == "listener")
|
|
{
|
|
if (aArgs[1].IsEmpty())
|
|
{
|
|
PrintMulticastListenersTable();
|
|
ExitNow(error = OT_ERROR_NONE);
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
if (aArgs[1] == "clear")
|
|
{
|
|
otBackboneRouterMulticastListenerClear(GetInstancePtr());
|
|
error = OT_ERROR_NONE;
|
|
}
|
|
else if (aArgs[1] == "add")
|
|
{
|
|
otIp6Address address;
|
|
uint32_t timeout = 0;
|
|
|
|
SuccessOrExit(error = aArgs[2].ParseAsIp6Address(address));
|
|
|
|
if (!aArgs[3].IsEmpty())
|
|
{
|
|
SuccessOrExit(error = aArgs[3].ParseAsUint32(timeout));
|
|
VerifyOrExit(aArgs[4].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
}
|
|
|
|
error = otBackboneRouterMulticastListenerAdd(GetInstancePtr(), &address, timeout);
|
|
}
|
|
}
|
|
else if (aArgs[0] == "response")
|
|
{
|
|
error = ProcessSet(aArgs + 1, otBackboneRouterConfigNextMulticastListenerRegistrationResponse);
|
|
#endif
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
void Interpreter::PrintMulticastListenersTable(void)
|
|
{
|
|
otBackboneRouterMulticastListenerIterator iter = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT;
|
|
otBackboneRouterMulticastListenerInfo listenerInfo;
|
|
|
|
while (otBackboneRouterMulticastListenerGetNext(GetInstancePtr(), &iter, &listenerInfo) == OT_ERROR_NONE)
|
|
{
|
|
OutputIp6Address(listenerInfo.mAddress);
|
|
OutputLine(" %u", listenerInfo.mTimeout);
|
|
}
|
|
}
|
|
|
|
#endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
|
|
|
|
otError Interpreter::ProcessBackboneRouterLocal(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
otBackboneRouterConfig config;
|
|
bool enable;
|
|
|
|
if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
|
|
{
|
|
otBackboneRouterSetEnabled(GetInstancePtr(), enable);
|
|
}
|
|
else if (aArgs[0] == "jitter")
|
|
{
|
|
error = ProcessGetSet(aArgs + 1, otBackboneRouterGetRegistrationJitter, otBackboneRouterSetRegistrationJitter);
|
|
}
|
|
else if (aArgs[0] == "register")
|
|
{
|
|
SuccessOrExit(error = otBackboneRouterRegister(GetInstancePtr()));
|
|
}
|
|
else if (aArgs[0] == "state")
|
|
{
|
|
static const char *const kStateStrings[] = {
|
|
"Disabled", // (0) OT_BACKBONE_ROUTER_STATE_DISABLED
|
|
"Secondary", // (1) OT_BACKBONE_ROUTER_STATE_SECONDARY
|
|
"Primary", // (2) OT_BACKBONE_ROUTER_STATE_PRIMARY
|
|
};
|
|
|
|
static_assert(0 == OT_BACKBONE_ROUTER_STATE_DISABLED, "OT_BACKBONE_ROUTER_STATE_DISABLED value is incorrect");
|
|
static_assert(1 == OT_BACKBONE_ROUTER_STATE_SECONDARY, "OT_BACKBONE_ROUTER_STATE_SECONDARY value is incorrect");
|
|
static_assert(2 == OT_BACKBONE_ROUTER_STATE_PRIMARY, "OT_BACKBONE_ROUTER_STATE_PRIMARY value is incorrect");
|
|
|
|
OutputLine("%s", Stringify(otBackboneRouterGetState(GetInstancePtr()), kStateStrings));
|
|
}
|
|
else if (aArgs[0] == "config")
|
|
{
|
|
otBackboneRouterGetConfig(GetInstancePtr(), &config);
|
|
|
|
if (aArgs[1].IsEmpty())
|
|
{
|
|
OutputLine("seqno: %d", config.mSequenceNumber);
|
|
OutputLine("delay: %d secs", config.mReregistrationDelay);
|
|
OutputLine("timeout: %d secs", config.mMlrTimeout);
|
|
}
|
|
else
|
|
{
|
|
// Set local Backbone Router configuration.
|
|
for (Arg *arg = &aArgs[1]; !arg->IsEmpty(); arg++)
|
|
{
|
|
if (*arg == "seqno")
|
|
{
|
|
arg++;
|
|
SuccessOrExit(error = arg->ParseAsUint8(config.mSequenceNumber));
|
|
}
|
|
else if (*arg == "delay")
|
|
{
|
|
arg++;
|
|
SuccessOrExit(error = arg->ParseAsUint16(config.mReregistrationDelay));
|
|
}
|
|
else if (*arg == "timeout")
|
|
{
|
|
arg++;
|
|
SuccessOrExit(error = arg->ParseAsUint32(config.mMlrTimeout));
|
|
}
|
|
else
|
|
{
|
|
ExitNow(error = OT_ERROR_INVALID_ARGS);
|
|
}
|
|
}
|
|
|
|
SuccessOrExit(error = otBackboneRouterSetConfig(GetInstancePtr(), &config));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
|
|
|
|
template <> otError Interpreter::Process<Cmd("domainname")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputLine("%s", otThreadGetDomainName(GetInstancePtr()));
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = otThreadSetDomainName(GetInstancePtr(), aArgs[0].GetCString()));
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_DUA_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("dua")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0] == "iid")
|
|
{
|
|
if (aArgs[1].IsEmpty())
|
|
{
|
|
const otIp6InterfaceIdentifier *iid = otThreadGetFixedDuaInterfaceIdentifier(GetInstancePtr());
|
|
|
|
if (iid != nullptr)
|
|
{
|
|
OutputBytesLine(iid->mFields.m8);
|
|
}
|
|
}
|
|
else if (aArgs[1] == "clear")
|
|
{
|
|
error = otThreadSetFixedDuaInterfaceIdentifier(GetInstancePtr(), nullptr);
|
|
}
|
|
else
|
|
{
|
|
otIp6InterfaceIdentifier iid;
|
|
|
|
SuccessOrExit(error = aArgs[1].ParseAsHexString(iid.mFields.m8));
|
|
error = otThreadSetFixedDuaInterfaceIdentifier(GetInstancePtr(), &iid);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_DUA_ENABLE
|
|
|
|
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
|
|
|
|
template <> otError Interpreter::Process<Cmd("bufferinfo")>(Arg aArgs[])
|
|
{
|
|
OT_UNUSED_VARIABLE(aArgs);
|
|
|
|
struct BufferInfoName
|
|
{
|
|
const otMessageQueueInfo otBufferInfo::*mQueuePtr;
|
|
const char * mName;
|
|
};
|
|
|
|
static const BufferInfoName kBufferInfoNames[] = {
|
|
{&otBufferInfo::m6loSendQueue, "6lo send"},
|
|
{&otBufferInfo::m6loReassemblyQueue, "6lo reas"},
|
|
{&otBufferInfo::mIp6Queue, "ip6"},
|
|
{&otBufferInfo::mMplQueue, "mpl"},
|
|
{&otBufferInfo::mMleQueue, "mle"},
|
|
{&otBufferInfo::mCoapQueue, "coap"},
|
|
{&otBufferInfo::mCoapSecureQueue, "coap secure"},
|
|
{&otBufferInfo::mApplicationCoapQueue, "application coap"},
|
|
};
|
|
|
|
otBufferInfo bufferInfo;
|
|
|
|
otMessageGetBufferInfo(GetInstancePtr(), &bufferInfo);
|
|
|
|
OutputLine("total: %d", bufferInfo.mTotalBuffers);
|
|
OutputLine("free: %d", bufferInfo.mFreeBuffers);
|
|
|
|
for (const BufferInfoName &info : kBufferInfoNames)
|
|
{
|
|
OutputLine("%s: %u %u %u", info.mName, (bufferInfo.*info.mQueuePtr).mNumMessages,
|
|
(bufferInfo.*info.mQueuePtr).mNumBuffers, (bufferInfo.*info.mQueuePtr).mTotalBytes);
|
|
}
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("ccathreshold")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
int8_t cca;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
SuccessOrExit(error = otPlatRadioGetCcaEnergyDetectThreshold(GetInstancePtr(), &cca));
|
|
OutputLine("%d dBm", cca);
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = aArgs[0].ParseAsInt8(cca));
|
|
error = otPlatRadioSetCcaEnergyDetectThreshold(GetInstancePtr(), cca);
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("ccm")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
bool enable;
|
|
|
|
VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_COMMAND);
|
|
|
|
SuccessOrExit(error = ParseEnableOrDisable(aArgs[0], enable));
|
|
otThreadSetCcmEnabled(GetInstancePtr(), enable);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("tvcheck")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
bool enable;
|
|
|
|
VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_COMMAND);
|
|
|
|
SuccessOrExit(error = ParseEnableOrDisable(aArgs[0], enable));
|
|
otThreadSetThreadVersionCheckEnabled(GetInstancePtr(), enable);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#endif
|
|
|
|
template <> otError Interpreter::Process<Cmd("channel")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0] == "supported")
|
|
{
|
|
OutputLine("0x%x", otPlatRadioGetSupportedChannelMask(GetInstancePtr()));
|
|
}
|
|
else if (aArgs[0] == "preferred")
|
|
{
|
|
OutputLine("0x%x", otPlatRadioGetPreferredChannelMask(GetInstancePtr()));
|
|
}
|
|
#if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
|
|
else if (aArgs[0] == "monitor")
|
|
{
|
|
if (aArgs[1].IsEmpty())
|
|
{
|
|
OutputLine("enabled: %d", otChannelMonitorIsEnabled(GetInstancePtr()));
|
|
if (otChannelMonitorIsEnabled(GetInstancePtr()))
|
|
{
|
|
uint32_t channelMask = otLinkGetSupportedChannelMask(GetInstancePtr());
|
|
uint8_t channelNum = sizeof(channelMask) * CHAR_BIT;
|
|
|
|
OutputLine("interval: %u", otChannelMonitorGetSampleInterval(GetInstancePtr()));
|
|
OutputLine("threshold: %d", otChannelMonitorGetRssiThreshold(GetInstancePtr()));
|
|
OutputLine("window: %u", otChannelMonitorGetSampleWindow(GetInstancePtr()));
|
|
OutputLine("count: %u", otChannelMonitorGetSampleCount(GetInstancePtr()));
|
|
|
|
OutputLine("occupancies:");
|
|
for (uint8_t channel = 0; channel < channelNum; channel++)
|
|
{
|
|
uint32_t occupancy = 0;
|
|
|
|
if (!((1UL << channel) & channelMask))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
occupancy = otChannelMonitorGetChannelOccupancy(GetInstancePtr(), channel);
|
|
|
|
OutputFormat("ch %d (0x%04x) ", channel, occupancy);
|
|
occupancy = (occupancy * 10000) / 0xffff;
|
|
OutputLine("%2d.%02d%% busy", occupancy / 100, occupancy % 100);
|
|
}
|
|
OutputLine("");
|
|
}
|
|
}
|
|
else if (aArgs[1] == "start")
|
|
{
|
|
error = otChannelMonitorSetEnabled(GetInstancePtr(), true);
|
|
}
|
|
else if (aArgs[1] == "stop")
|
|
{
|
|
error = otChannelMonitorSetEnabled(GetInstancePtr(), false);
|
|
}
|
|
else
|
|
{
|
|
ExitNow(error = OT_ERROR_INVALID_ARGS);
|
|
}
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
|
|
#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
|
|
else if (aArgs[0] == "manager")
|
|
{
|
|
if (aArgs[1].IsEmpty())
|
|
{
|
|
OutputLine("channel: %d", otChannelManagerGetRequestedChannel(GetInstancePtr()));
|
|
OutputLine("auto: %d", otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr()));
|
|
|
|
if (otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr()))
|
|
{
|
|
Mac::ChannelMask supportedMask(otChannelManagerGetSupportedChannels(GetInstancePtr()));
|
|
Mac::ChannelMask favoredMask(otChannelManagerGetFavoredChannels(GetInstancePtr()));
|
|
|
|
OutputLine("delay: %d", otChannelManagerGetDelay(GetInstancePtr()));
|
|
OutputLine("interval: %u", otChannelManagerGetAutoChannelSelectionInterval(GetInstancePtr()));
|
|
OutputLine("cca threshold: 0x%04x", otChannelManagerGetCcaFailureRateThreshold(GetInstancePtr()));
|
|
OutputLine("supported: %s", supportedMask.ToString().AsCString());
|
|
OutputLine("favored: %s", supportedMask.ToString().AsCString());
|
|
}
|
|
}
|
|
else if (aArgs[1] == "change")
|
|
{
|
|
error = ProcessSet(aArgs + 2, otChannelManagerRequestChannelChange);
|
|
}
|
|
#if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
|
|
else if (aArgs[1] == "select")
|
|
{
|
|
bool enable;
|
|
|
|
SuccessOrExit(error = aArgs[2].ParseAsBool(enable));
|
|
error = otChannelManagerRequestChannelSelect(GetInstancePtr(), enable);
|
|
}
|
|
#endif
|
|
else if (aArgs[1] == "auto")
|
|
{
|
|
bool enable;
|
|
|
|
SuccessOrExit(error = aArgs[2].ParseAsBool(enable));
|
|
otChannelManagerSetAutoChannelSelectionEnabled(GetInstancePtr(), enable);
|
|
}
|
|
else if (aArgs[1] == "delay")
|
|
{
|
|
error = ProcessSet(aArgs + 2, otChannelManagerSetDelay);
|
|
}
|
|
else if (aArgs[1] == "interval")
|
|
{
|
|
error = ProcessSet(aArgs + 2, otChannelManagerSetAutoChannelSelectionInterval);
|
|
}
|
|
else if (aArgs[1] == "supported")
|
|
{
|
|
error = ProcessSet(aArgs + 2, otChannelManagerSetSupportedChannels);
|
|
}
|
|
else if (aArgs[1] == "favored")
|
|
{
|
|
error = ProcessSet(aArgs + 2, otChannelManagerSetFavoredChannels);
|
|
}
|
|
else if (aArgs[1] == "threshold")
|
|
{
|
|
error = ProcessSet(aArgs + 2, otChannelManagerSetCcaFailureRateThreshold);
|
|
}
|
|
else
|
|
{
|
|
ExitNow(error = OT_ERROR_INVALID_ARGS);
|
|
}
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
|
|
else
|
|
{
|
|
ExitNow(error = ProcessGetSet(aArgs, otLinkGetChannel, otLinkSetChannel));
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_FTD
|
|
template <> otError Interpreter::Process<Cmd("child")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
otChildInfo childInfo;
|
|
uint16_t childId;
|
|
bool isTable;
|
|
otLinkModeConfig linkMode;
|
|
char linkModeString[kLinkModeStringSize];
|
|
|
|
isTable = (aArgs[0] == "table");
|
|
|
|
if (isTable || (aArgs[0] == "list"))
|
|
{
|
|
uint16_t maxChildren;
|
|
|
|
if (isTable)
|
|
{
|
|
static const char *const kChildTableTitles[] = {
|
|
"ID", "RLOC16", "Timeout", "Age", "LQ In", "C_VN", "R",
|
|
"D", "N", "Ver", "CSL", "QMsgCnt", "Extended MAC",
|
|
};
|
|
|
|
static const uint8_t kChildTableColumnWidths[] = {
|
|
5, 8, 12, 12, 7, 6, 1, 1, 1, 3, 3, 7, 18,
|
|
};
|
|
|
|
OutputTableHeader(kChildTableTitles, kChildTableColumnWidths);
|
|
}
|
|
|
|
maxChildren = otThreadGetMaxAllowedChildren(GetInstancePtr());
|
|
|
|
for (uint16_t i = 0; i < maxChildren; i++)
|
|
{
|
|
if ((otThreadGetChildInfoByIndex(GetInstancePtr(), i, &childInfo) != OT_ERROR_NONE) ||
|
|
childInfo.mIsStateRestoring)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (isTable)
|
|
{
|
|
OutputFormat("| %3d ", childInfo.mChildId);
|
|
OutputFormat("| 0x%04x ", childInfo.mRloc16);
|
|
OutputFormat("| %10d ", childInfo.mTimeout);
|
|
OutputFormat("| %10d ", childInfo.mAge);
|
|
OutputFormat("| %5d ", childInfo.mLinkQualityIn);
|
|
OutputFormat("| %4d ", childInfo.mNetworkDataVersion);
|
|
OutputFormat("|%1d", childInfo.mRxOnWhenIdle);
|
|
OutputFormat("|%1d", childInfo.mFullThreadDevice);
|
|
OutputFormat("|%1d", childInfo.mFullNetworkData);
|
|
OutputFormat("|%3d", childInfo.mVersion);
|
|
OutputFormat("| %1d ", childInfo.mIsCslSynced);
|
|
OutputFormat("| %5d ", childInfo.mQueuedMessageCnt);
|
|
OutputFormat("| ");
|
|
OutputExtAddress(childInfo.mExtAddress);
|
|
OutputLine(" |");
|
|
}
|
|
else
|
|
{
|
|
OutputFormat("%d ", childInfo.mChildId);
|
|
}
|
|
}
|
|
|
|
OutputLine("");
|
|
ExitNow();
|
|
}
|
|
|
|
SuccessOrExit(error = aArgs[0].ParseAsUint16(childId));
|
|
SuccessOrExit(error = otThreadGetChildInfoById(GetInstancePtr(), childId, &childInfo));
|
|
|
|
OutputLine("Child ID: %d", childInfo.mChildId);
|
|
OutputLine("Rloc: %04x", childInfo.mRloc16);
|
|
OutputFormat("Ext Addr: ");
|
|
OutputExtAddressLine(childInfo.mExtAddress);
|
|
linkMode.mRxOnWhenIdle = childInfo.mRxOnWhenIdle;
|
|
linkMode.mDeviceType = childInfo.mFullThreadDevice;
|
|
linkMode.mNetworkData = childInfo.mFullThreadDevice;
|
|
OutputLine("Mode: %s", LinkModeToString(linkMode, linkModeString));
|
|
OutputLine("Net Data: %d", childInfo.mNetworkDataVersion);
|
|
OutputLine("Timeout: %d", childInfo.mTimeout);
|
|
OutputLine("Age: %d", childInfo.mAge);
|
|
OutputLine("Link Quality In: %d", childInfo.mLinkQualityIn);
|
|
OutputLine("RSSI: %d", childInfo.mAverageRssi);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("childip")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
uint16_t maxChildren = otThreadGetMaxAllowedChildren(GetInstancePtr());
|
|
|
|
for (uint16_t childIndex = 0; childIndex < maxChildren; childIndex++)
|
|
{
|
|
otChildIp6AddressIterator iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT;
|
|
otIp6Address ip6Address;
|
|
otChildInfo childInfo;
|
|
|
|
if ((otThreadGetChildInfoByIndex(GetInstancePtr(), childIndex, &childInfo) != OT_ERROR_NONE) ||
|
|
childInfo.mIsStateRestoring)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT;
|
|
|
|
while (otThreadGetChildNextIp6Address(GetInstancePtr(), childIndex, &iterator, &ip6Address) ==
|
|
OT_ERROR_NONE)
|
|
{
|
|
OutputFormat("%04x: ", childInfo.mRloc16);
|
|
OutputIp6AddressLine(ip6Address);
|
|
}
|
|
}
|
|
}
|
|
else if (aArgs[0] == "max")
|
|
{
|
|
#if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
error = ProcessGet(aArgs + 1, otThreadGetMaxChildIpAddresses);
|
|
#else
|
|
error = ProcessGetSet(aArgs + 1, otThreadGetMaxChildIpAddresses, otThreadSetMaxChildIpAddresses);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("childmax")>(Arg aArgs[])
|
|
{
|
|
return ProcessGetSet(aArgs, otThreadGetMaxAllowedChildren, otThreadSetMaxAllowedChildren);
|
|
}
|
|
#endif // OPENTHREAD_FTD
|
|
|
|
#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("childsupervision")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_INVALID_ARGS;
|
|
|
|
if (aArgs[0] == "checktimeout")
|
|
{
|
|
error = ProcessGetSet(aArgs + 1, otChildSupervisionGetCheckTimeout, otChildSupervisionSetCheckTimeout);
|
|
}
|
|
#if OPENTHREAD_FTD
|
|
else if (aArgs[0] == "interval")
|
|
{
|
|
error = ProcessGetSet(aArgs + 1, otChildSupervisionGetInterval, otChildSupervisionSetInterval);
|
|
}
|
|
#endif
|
|
|
|
return error;
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
|
|
|
|
template <> otError Interpreter::Process<Cmd("childtimeout")>(Arg aArgs[])
|
|
{
|
|
return ProcessGetSet(aArgs, otThreadGetChildTimeout, otThreadSetChildTimeout);
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_COAP_API_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("coap")>(Arg aArgs[])
|
|
{
|
|
return mCoap.Process(aArgs);
|
|
}
|
|
#endif
|
|
|
|
#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("coaps")>(Arg aArgs[])
|
|
{
|
|
return mCoapSecure.Process(aArgs);
|
|
}
|
|
#endif
|
|
|
|
#if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("coex")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
bool enable;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputEnabledDisabledStatus(otPlatRadioIsCoexEnabled(GetInstancePtr()));
|
|
}
|
|
else if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
|
|
{
|
|
error = otPlatRadioSetCoexEnabled(GetInstancePtr(), enable);
|
|
}
|
|
else if (aArgs[0] == "metrics")
|
|
{
|
|
struct RadioCoexMetricName
|
|
{
|
|
const uint32_t otRadioCoexMetrics::*mValuePtr;
|
|
const char * mName;
|
|
};
|
|
|
|
static const RadioCoexMetricName kTxMetricNames[] = {
|
|
{&otRadioCoexMetrics::mNumTxRequest, "Request"},
|
|
{&otRadioCoexMetrics::mNumTxGrantImmediate, "Grant Immediate"},
|
|
{&otRadioCoexMetrics::mNumTxGrantWait, "Grant Wait"},
|
|
{&otRadioCoexMetrics::mNumTxGrantWaitActivated, "Grant Wait Activated"},
|
|
{&otRadioCoexMetrics::mNumTxGrantWaitTimeout, "Grant Wait Timeout"},
|
|
{&otRadioCoexMetrics::mNumTxGrantDeactivatedDuringRequest, "Grant Deactivated During Request"},
|
|
{&otRadioCoexMetrics::mNumTxDelayedGrant, "Delayed Grant"},
|
|
{&otRadioCoexMetrics::mAvgTxRequestToGrantTime, "Average Request To Grant Time"},
|
|
};
|
|
|
|
static const RadioCoexMetricName kRxMetricNames[] = {
|
|
{&otRadioCoexMetrics::mNumRxRequest, "Request"},
|
|
{&otRadioCoexMetrics::mNumRxGrantImmediate, "Grant Immediate"},
|
|
{&otRadioCoexMetrics::mNumRxGrantWait, "Grant Wait"},
|
|
{&otRadioCoexMetrics::mNumRxGrantWaitActivated, "Grant Wait Activated"},
|
|
{&otRadioCoexMetrics::mNumRxGrantWaitTimeout, "Grant Wait Timeout"},
|
|
{&otRadioCoexMetrics::mNumRxGrantDeactivatedDuringRequest, "Grant Deactivated During Request"},
|
|
{&otRadioCoexMetrics::mNumRxDelayedGrant, "Delayed Grant"},
|
|
{&otRadioCoexMetrics::mAvgRxRequestToGrantTime, "Average Request To Grant Time"},
|
|
{&otRadioCoexMetrics::mNumRxGrantNone, "Grant None"},
|
|
};
|
|
|
|
otRadioCoexMetrics metrics;
|
|
|
|
SuccessOrExit(error = otPlatRadioGetCoexMetrics(GetInstancePtr(), &metrics));
|
|
|
|
OutputLine("Stopped: %s", metrics.mStopped ? "true" : "false");
|
|
OutputLine("Grant Glitch: %u", metrics.mNumGrantGlitch);
|
|
OutputLine("Transmit metrics");
|
|
|
|
for (const RadioCoexMetricName &metric : kTxMetricNames)
|
|
{
|
|
OutputLine(kIndentSize, "%s: %u", metric.mName, metrics.*metric.mValuePtr);
|
|
}
|
|
|
|
OutputLine("Receive metrics");
|
|
|
|
for (const RadioCoexMetricName &metric : kRxMetricNames)
|
|
{
|
|
OutputLine(kIndentSize, "%s: %u", metric.mName, metrics.*metric.mValuePtr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
|
|
|
|
#if OPENTHREAD_FTD
|
|
template <> otError Interpreter::Process<Cmd("contextreusedelay")>(Arg aArgs[])
|
|
{
|
|
return ProcessGetSet(aArgs, otThreadGetContextIdReuseDelay, otThreadSetContextIdReuseDelay);
|
|
}
|
|
#endif
|
|
|
|
template <> otError Interpreter::Process<Cmd("counters")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputLine("ip");
|
|
OutputLine("mac");
|
|
OutputLine("mle");
|
|
}
|
|
else if (aArgs[0] == "mac")
|
|
{
|
|
if (aArgs[1].IsEmpty())
|
|
{
|
|
struct MacCounterName
|
|
{
|
|
const uint32_t otMacCounters::*mValuePtr;
|
|
const char * mName;
|
|
};
|
|
|
|
static const MacCounterName kTxCounterNames[] = {
|
|
{&otMacCounters::mTxUnicast, "TxUnicast"},
|
|
{&otMacCounters::mTxBroadcast, "TxBroadcast"},
|
|
{&otMacCounters::mTxAckRequested, "TxAckRequested"},
|
|
{&otMacCounters::mTxAcked, "TxAcked"},
|
|
{&otMacCounters::mTxNoAckRequested, "TxNoAckRequested"},
|
|
{&otMacCounters::mTxData, "TxData"},
|
|
{&otMacCounters::mTxDataPoll, "TxDataPoll"},
|
|
{&otMacCounters::mTxBeacon, "TxBeacon"},
|
|
{&otMacCounters::mTxBeaconRequest, "TxBeaconRequest"},
|
|
{&otMacCounters::mTxOther, "TxOther"},
|
|
{&otMacCounters::mTxRetry, "TxRetry"},
|
|
{&otMacCounters::mTxErrCca, "TxErrCca"},
|
|
{&otMacCounters::mTxErrBusyChannel, "TxErrBusyChannel"},
|
|
};
|
|
|
|
static const MacCounterName kRxCounterNames[] = {
|
|
{&otMacCounters::mRxUnicast, "RxUnicast"},
|
|
{&otMacCounters::mRxBroadcast, "RxBroadcast"},
|
|
{&otMacCounters::mRxData, "RxData"},
|
|
{&otMacCounters::mRxDataPoll, "RxDataPoll"},
|
|
{&otMacCounters::mRxBeacon, "RxBeacon"},
|
|
{&otMacCounters::mRxBeaconRequest, "RxBeaconRequest"},
|
|
{&otMacCounters::mRxOther, "RxOther"},
|
|
{&otMacCounters::mRxAddressFiltered, "RxAddressFiltered"},
|
|
{&otMacCounters::mRxDestAddrFiltered, "RxDestAddrFiltered"},
|
|
{&otMacCounters::mRxDuplicated, "RxDuplicated"},
|
|
{&otMacCounters::mRxErrNoFrame, "RxErrNoFrame"},
|
|
{&otMacCounters::mRxErrUnknownNeighbor, "RxErrNoUnknownNeighbor"},
|
|
{&otMacCounters::mRxErrInvalidSrcAddr, "RxErrInvalidSrcAddr"},
|
|
{&otMacCounters::mRxErrSec, "RxErrSec"},
|
|
{&otMacCounters::mRxErrFcs, "RxErrFcs"},
|
|
{&otMacCounters::mRxErrOther, "RxErrOther"},
|
|
};
|
|
|
|
const otMacCounters *macCounters = otLinkGetCounters(GetInstancePtr());
|
|
|
|
OutputLine("TxTotal: %d", macCounters->mTxTotal);
|
|
|
|
for (const MacCounterName &counter : kTxCounterNames)
|
|
{
|
|
OutputLine(kIndentSize, "%s: %u", counter.mName, macCounters->*counter.mValuePtr);
|
|
}
|
|
|
|
OutputLine("RxTotal: %d", macCounters->mRxTotal);
|
|
|
|
for (const MacCounterName &counter : kRxCounterNames)
|
|
{
|
|
OutputLine(kIndentSize, "%s: %u", counter.mName, macCounters->*counter.mValuePtr);
|
|
}
|
|
}
|
|
else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty())
|
|
{
|
|
otLinkResetCounters(GetInstancePtr());
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
}
|
|
else if (aArgs[0] == "mle")
|
|
{
|
|
if (aArgs[1].IsEmpty())
|
|
{
|
|
struct MleCounterName
|
|
{
|
|
const uint16_t otMleCounters::*mValuePtr;
|
|
const char * mName;
|
|
};
|
|
|
|
static const MleCounterName kCounterNames[] = {
|
|
{&otMleCounters::mDisabledRole, "Role Disabled"},
|
|
{&otMleCounters::mDetachedRole, "Role Detached"},
|
|
{&otMleCounters::mChildRole, "Role Child"},
|
|
{&otMleCounters::mRouterRole, "Role Router"},
|
|
{&otMleCounters::mLeaderRole, "Role Leader"},
|
|
{&otMleCounters::mAttachAttempts, "Attach Attempts"},
|
|
{&otMleCounters::mPartitionIdChanges, "Partition Id Changes"},
|
|
{&otMleCounters::mBetterPartitionAttachAttempts, "Better Partition Attach Attempts"},
|
|
{&otMleCounters::mParentChanges, "Parent Changes"},
|
|
};
|
|
|
|
const otMleCounters *mleCounters = otThreadGetMleCounters(GetInstancePtr());
|
|
|
|
for (const MleCounterName &counter : kCounterNames)
|
|
{
|
|
OutputLine("%s: %d", counter.mName, mleCounters->*counter.mValuePtr);
|
|
}
|
|
}
|
|
else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty())
|
|
{
|
|
otThreadResetMleCounters(GetInstancePtr());
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
}
|
|
else if (aArgs[0] == "ip")
|
|
{
|
|
if (aArgs[1].IsEmpty())
|
|
{
|
|
struct IpCounterName
|
|
{
|
|
const uint32_t otIpCounters::*mValuePtr;
|
|
const char * mName;
|
|
};
|
|
|
|
static const IpCounterName kCounterNames[] = {
|
|
{&otIpCounters::mTxSuccess, "TxSuccess"},
|
|
{&otIpCounters::mTxFailure, "TxFailed"},
|
|
{&otIpCounters::mRxSuccess, "RxSuccess"},
|
|
{&otIpCounters::mRxFailure, "RxFailed"},
|
|
};
|
|
|
|
const otIpCounters *ipCounters = otThreadGetIp6Counters(GetInstancePtr());
|
|
|
|
for (const IpCounterName &counter : kCounterNames)
|
|
{
|
|
OutputLine("%s: %d", counter.mName, ipCounters->*counter.mValuePtr);
|
|
}
|
|
}
|
|
else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty())
|
|
{
|
|
otThreadResetIp6Counters(GetInstancePtr());
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("csl")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputLine("Channel: %u", otLinkCslGetChannel(GetInstancePtr()));
|
|
OutputLine("Period: %u(in units of 10 symbols), %ums", otLinkCslGetPeriod(GetInstancePtr()),
|
|
otLinkCslGetPeriod(GetInstancePtr()) * kUsPerTenSymbols / 1000);
|
|
OutputLine("Timeout: %us", otLinkCslGetTimeout(GetInstancePtr()));
|
|
}
|
|
else if (aArgs[0] == "channel")
|
|
{
|
|
error = ProcessSet(aArgs + 1, otLinkCslSetChannel);
|
|
}
|
|
else if (aArgs[0] == "period")
|
|
{
|
|
error = ProcessSet(aArgs + 1, otLinkCslSetPeriod);
|
|
}
|
|
else if (aArgs[0] == "timeout")
|
|
{
|
|
error = ProcessSet(aArgs + 1, otLinkCslSetTimeout);
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
|
|
|
#if OPENTHREAD_FTD
|
|
template <> otError Interpreter::Process<Cmd("delaytimermin")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputLine("%d", (otDatasetGetDelayTimerMinimal(GetInstancePtr()) / 1000));
|
|
}
|
|
else if (aArgs[1].IsEmpty())
|
|
{
|
|
uint32_t delay;
|
|
SuccessOrExit(error = aArgs[0].ParseAsUint32(delay));
|
|
SuccessOrExit(error = otDatasetSetDelayTimerMinimal(GetInstancePtr(), static_cast<uint32_t>(delay * 1000)));
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
template <> otError Interpreter::Process<Cmd("detach")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0] == "async")
|
|
{
|
|
SuccessOrExit(error = otThreadDetachGracefully(GetInstancePtr(), nullptr, nullptr));
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error =
|
|
otThreadDetachGracefully(GetInstancePtr(), &Interpreter::HandleDetachGracefullyResult, this));
|
|
error = OT_ERROR_PENDING;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("discover")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
uint32_t scanChannels = 0;
|
|
|
|
if (!aArgs[0].IsEmpty())
|
|
{
|
|
uint8_t channel;
|
|
|
|
SuccessOrExit(error = aArgs[0].ParseAsUint8(channel));
|
|
VerifyOrExit(channel < sizeof(scanChannels) * CHAR_BIT, error = OT_ERROR_INVALID_ARGS);
|
|
scanChannels = 1 << channel;
|
|
}
|
|
|
|
SuccessOrExit(error = otThreadDiscover(GetInstancePtr(), scanChannels, OT_PANID_BROADCAST, false, false,
|
|
&Interpreter::HandleActiveScanResult, this));
|
|
|
|
static const char *const kScanTableTitles[] = {
|
|
"Network Name", "Extended PAN", "PAN", "MAC Address", "Ch", "dBm", "LQI",
|
|
};
|
|
|
|
static const uint8_t kScanTableColumnWidths[] = {
|
|
18, 18, 6, 18, 4, 5, 5,
|
|
};
|
|
|
|
OutputTableHeader(kScanTableTitles, kScanTableColumnWidths);
|
|
|
|
error = OT_ERROR_PENDING;
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("dns")>(Arg aArgs[])
|
|
{
|
|
OT_UNUSED_VARIABLE(aArgs);
|
|
|
|
otError error = OT_ERROR_NONE;
|
|
#if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
|
|
otDnsQueryConfig queryConfig;
|
|
otDnsQueryConfig *config = &queryConfig;
|
|
#endif
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
else if (aArgs[0] == "compression")
|
|
{
|
|
if (aArgs[1].IsEmpty())
|
|
{
|
|
OutputEnabledDisabledStatus(otDnsIsNameCompressionEnabled());
|
|
}
|
|
else
|
|
{
|
|
bool enable;
|
|
|
|
SuccessOrExit(error = ParseEnableOrDisable(aArgs[1], enable));
|
|
otDnsSetNameCompressionEnabled(enable);
|
|
}
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
#if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
|
|
else if (aArgs[0] == "config")
|
|
{
|
|
if (aArgs[1].IsEmpty())
|
|
{
|
|
const otDnsQueryConfig *defaultConfig = otDnsClientGetDefaultConfig(GetInstancePtr());
|
|
|
|
OutputFormat("Server: ");
|
|
OutputSockAddrLine(defaultConfig->mServerSockAddr);
|
|
OutputLine("ResponseTimeout: %u ms", defaultConfig->mResponseTimeout);
|
|
OutputLine("MaxTxAttempts: %u", defaultConfig->mMaxTxAttempts);
|
|
OutputLine("RecursionDesired: %s",
|
|
(defaultConfig->mRecursionFlag == OT_DNS_FLAG_RECURSION_DESIRED) ? "yes" : "no");
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = GetDnsConfig(aArgs + 1, config));
|
|
otDnsClientSetDefaultConfig(GetInstancePtr(), config);
|
|
}
|
|
}
|
|
else if (aArgs[0] == "resolve")
|
|
{
|
|
VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
SuccessOrExit(error = GetDnsConfig(aArgs + 2, config));
|
|
SuccessOrExit(error = otDnsClientResolveAddress(GetInstancePtr(), aArgs[1].GetCString(),
|
|
&Interpreter::HandleDnsAddressResponse, this, config));
|
|
error = OT_ERROR_PENDING;
|
|
}
|
|
#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
|
|
else if (aArgs[0] == "resolve4")
|
|
{
|
|
VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
SuccessOrExit(error = GetDnsConfig(aArgs + 2, config));
|
|
SuccessOrExit(error = otDnsClientResolveIp4Address(GetInstancePtr(), aArgs[1].GetCString(),
|
|
&Interpreter::HandleDnsAddressResponse, this, config));
|
|
error = OT_ERROR_PENDING;
|
|
}
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
|
|
else if (aArgs[0] == "browse")
|
|
{
|
|
VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
SuccessOrExit(error = GetDnsConfig(aArgs + 2, config));
|
|
SuccessOrExit(error = otDnsClientBrowse(GetInstancePtr(), aArgs[1].GetCString(),
|
|
&Interpreter::HandleDnsBrowseResponse, this, config));
|
|
error = OT_ERROR_PENDING;
|
|
}
|
|
else if (aArgs[0] == "service")
|
|
{
|
|
VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
SuccessOrExit(error = GetDnsConfig(aArgs + 3, config));
|
|
SuccessOrExit(error = otDnsClientResolveService(GetInstancePtr(), aArgs[1].GetCString(), aArgs[2].GetCString(),
|
|
&Interpreter::HandleDnsServiceResponse, this, config));
|
|
error = OT_ERROR_PENDING;
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
|
|
#endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
|
|
else
|
|
{
|
|
ExitNow(error = OT_ERROR_INVALID_COMMAND);
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
|
|
|
|
otError Interpreter::GetDnsConfig(Arg aArgs[], otDnsQueryConfig *&aConfig)
|
|
{
|
|
// This method gets the optional DNS config from `aArgs[]`.
|
|
// The format: `[server IPv6 address] [server port] [timeout]
|
|
// [max tx attempt] [recursion desired]`.
|
|
|
|
otError error = OT_ERROR_NONE;
|
|
bool recursionDesired;
|
|
|
|
memset(aConfig, 0, sizeof(otDnsQueryConfig));
|
|
|
|
VerifyOrExit(!aArgs[0].IsEmpty(), aConfig = nullptr);
|
|
|
|
SuccessOrExit(error = aArgs[0].ParseAsIp6Address(aConfig->mServerSockAddr.mAddress));
|
|
|
|
VerifyOrExit(!aArgs[1].IsEmpty());
|
|
SuccessOrExit(error = aArgs[1].ParseAsUint16(aConfig->mServerSockAddr.mPort));
|
|
|
|
VerifyOrExit(!aArgs[2].IsEmpty());
|
|
SuccessOrExit(error = aArgs[2].ParseAsUint32(aConfig->mResponseTimeout));
|
|
|
|
VerifyOrExit(!aArgs[3].IsEmpty());
|
|
SuccessOrExit(error = aArgs[3].ParseAsUint8(aConfig->mMaxTxAttempts));
|
|
|
|
VerifyOrExit(!aArgs[4].IsEmpty());
|
|
SuccessOrExit(error = aArgs[4].ParseAsBool(recursionDesired));
|
|
aConfig->mRecursionFlag = recursionDesired ? OT_DNS_FLAG_RECURSION_DESIRED : OT_DNS_FLAG_NO_RECURSION;
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
void Interpreter::HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse, void *aContext)
|
|
{
|
|
static_cast<Interpreter *>(aContext)->HandleDnsAddressResponse(aError, aResponse);
|
|
}
|
|
|
|
void Interpreter::HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse)
|
|
{
|
|
char hostName[OT_DNS_MAX_NAME_SIZE];
|
|
otIp6Address address;
|
|
uint32_t ttl;
|
|
|
|
IgnoreError(otDnsAddressResponseGetHostName(aResponse, hostName, sizeof(hostName)));
|
|
|
|
OutputFormat("DNS response for %s - ", hostName);
|
|
|
|
if (aError == OT_ERROR_NONE)
|
|
{
|
|
uint16_t index = 0;
|
|
|
|
while (otDnsAddressResponseGetAddress(aResponse, index, &address, &ttl) == OT_ERROR_NONE)
|
|
{
|
|
OutputIp6Address(address);
|
|
OutputFormat(" TTL:%u ", ttl);
|
|
index++;
|
|
}
|
|
}
|
|
|
|
OutputLine("");
|
|
OutputResult(aError);
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
|
|
|
|
void Interpreter::OutputDnsServiceInfo(uint8_t aIndentSize, const otDnsServiceInfo &aServiceInfo)
|
|
{
|
|
OutputLine(aIndentSize, "Port:%d, Priority:%d, Weight:%d, TTL:%u", aServiceInfo.mPort, aServiceInfo.mPriority,
|
|
aServiceInfo.mWeight, aServiceInfo.mTtl);
|
|
OutputLine(aIndentSize, "Host:%s", aServiceInfo.mHostNameBuffer);
|
|
OutputFormat(aIndentSize, "HostAddress:");
|
|
OutputIp6Address(aServiceInfo.mHostAddress);
|
|
OutputLine(" TTL:%u", aServiceInfo.mHostAddressTtl);
|
|
OutputFormat(aIndentSize, "TXT:");
|
|
OutputDnsTxtData(aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize);
|
|
OutputLine(" TTL:%u", aServiceInfo.mTxtDataTtl);
|
|
}
|
|
|
|
void Interpreter::HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse, void *aContext)
|
|
{
|
|
static_cast<Interpreter *>(aContext)->HandleDnsBrowseResponse(aError, aResponse);
|
|
}
|
|
|
|
void Interpreter::HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse)
|
|
{
|
|
char name[OT_DNS_MAX_NAME_SIZE];
|
|
char label[OT_DNS_MAX_LABEL_SIZE];
|
|
uint8_t txtBuffer[255];
|
|
otDnsServiceInfo serviceInfo;
|
|
|
|
IgnoreError(otDnsBrowseResponseGetServiceName(aResponse, name, sizeof(name)));
|
|
|
|
OutputLine("DNS browse response for %s", name);
|
|
|
|
if (aError == OT_ERROR_NONE)
|
|
{
|
|
uint16_t index = 0;
|
|
|
|
while (otDnsBrowseResponseGetServiceInstance(aResponse, index, label, sizeof(label)) == OT_ERROR_NONE)
|
|
{
|
|
OutputLine("%s", label);
|
|
index++;
|
|
|
|
serviceInfo.mHostNameBuffer = name;
|
|
serviceInfo.mHostNameBufferSize = sizeof(name);
|
|
serviceInfo.mTxtData = txtBuffer;
|
|
serviceInfo.mTxtDataSize = sizeof(txtBuffer);
|
|
|
|
if (otDnsBrowseResponseGetServiceInfo(aResponse, label, &serviceInfo) == OT_ERROR_NONE)
|
|
{
|
|
OutputDnsServiceInfo(kIndentSize, serviceInfo);
|
|
}
|
|
|
|
OutputLine("");
|
|
}
|
|
}
|
|
|
|
OutputResult(aError);
|
|
}
|
|
|
|
void Interpreter::HandleDnsServiceResponse(otError aError, const otDnsServiceResponse *aResponse, void *aContext)
|
|
{
|
|
static_cast<Interpreter *>(aContext)->HandleDnsServiceResponse(aError, aResponse);
|
|
}
|
|
|
|
void Interpreter::HandleDnsServiceResponse(otError aError, const otDnsServiceResponse *aResponse)
|
|
{
|
|
char name[OT_DNS_MAX_NAME_SIZE];
|
|
char label[OT_DNS_MAX_LABEL_SIZE];
|
|
uint8_t txtBuffer[255];
|
|
otDnsServiceInfo serviceInfo;
|
|
|
|
IgnoreError(otDnsServiceResponseGetServiceName(aResponse, label, sizeof(label), name, sizeof(name)));
|
|
|
|
OutputLine("DNS service resolution response for %s for service %s", label, name);
|
|
|
|
if (aError == OT_ERROR_NONE)
|
|
{
|
|
serviceInfo.mHostNameBuffer = name;
|
|
serviceInfo.mHostNameBufferSize = sizeof(name);
|
|
serviceInfo.mTxtData = txtBuffer;
|
|
serviceInfo.mTxtDataSize = sizeof(txtBuffer);
|
|
|
|
if (otDnsServiceResponseGetServiceInfo(aResponse, &serviceInfo) == OT_ERROR_NONE)
|
|
{
|
|
OutputDnsServiceInfo(/* aIndetSize */ 0, serviceInfo);
|
|
OutputLine("");
|
|
}
|
|
}
|
|
|
|
OutputResult(aError);
|
|
}
|
|
|
|
#endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
|
|
#endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
|
|
|
|
#if OPENTHREAD_FTD
|
|
const char *EidCacheStateToString(otCacheEntryState aState)
|
|
{
|
|
static const char *const kStateStrings[4] = {
|
|
"cache",
|
|
"snoop",
|
|
"query",
|
|
"retry",
|
|
};
|
|
|
|
return Interpreter::Stringify(aState, kStateStrings);
|
|
}
|
|
|
|
void Interpreter::OutputEidCacheEntry(const otCacheEntryInfo &aEntry)
|
|
{
|
|
OutputIp6Address(aEntry.mTarget);
|
|
OutputFormat(" %04x", aEntry.mRloc16);
|
|
OutputFormat(" %s", EidCacheStateToString(aEntry.mState));
|
|
OutputFormat(" canEvict=%d", aEntry.mCanEvict);
|
|
|
|
if (aEntry.mState == OT_CACHE_ENTRY_STATE_CACHED)
|
|
{
|
|
if (aEntry.mValidLastTrans)
|
|
{
|
|
OutputFormat(" transTime=%u eid=", aEntry.mLastTransTime);
|
|
OutputIp6Address(aEntry.mMeshLocalEid);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OutputFormat(" timeout=%u", aEntry.mTimeout);
|
|
}
|
|
|
|
if (aEntry.mState == OT_CACHE_ENTRY_STATE_RETRY_QUERY)
|
|
{
|
|
OutputFormat(" retryDelay=%u", aEntry.mRetryDelay);
|
|
}
|
|
|
|
OutputLine("");
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("eidcache")>(Arg aArgs[])
|
|
{
|
|
OT_UNUSED_VARIABLE(aArgs);
|
|
|
|
otCacheEntryIterator iterator;
|
|
otCacheEntryInfo entry;
|
|
|
|
memset(&iterator, 0, sizeof(iterator));
|
|
|
|
for (uint8_t i = 0;; i++)
|
|
{
|
|
SuccessOrExit(otThreadGetNextCacheEntry(GetInstancePtr(), &entry, &iterator));
|
|
OutputEidCacheEntry(entry);
|
|
}
|
|
|
|
exit:
|
|
return OT_ERROR_NONE;
|
|
}
|
|
#endif
|
|
|
|
template <> otError Interpreter::Process<Cmd("eui64")>(Arg aArgs[])
|
|
{
|
|
OT_UNUSED_VARIABLE(aArgs);
|
|
|
|
otError error = OT_ERROR_NONE;
|
|
otExtAddress extAddress;
|
|
|
|
VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
|
|
otLinkGetFactoryAssignedIeeeEui64(GetInstancePtr(), &extAddress);
|
|
OutputExtAddressLine(extAddress);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("extaddr")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputExtAddressLine(*otLinkGetExtendedAddress(GetInstancePtr()));
|
|
}
|
|
else
|
|
{
|
|
otExtAddress extAddress;
|
|
|
|
SuccessOrExit(error = aArgs[0].ParseAsHexString(extAddress.m8));
|
|
error = otLinkSetExtendedAddress(GetInstancePtr(), &extAddress);
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("log")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0] == "level")
|
|
{
|
|
if (aArgs[1].IsEmpty())
|
|
{
|
|
OutputLine("%d", otLoggingGetLevel());
|
|
}
|
|
else
|
|
{
|
|
#if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
|
|
uint8_t level;
|
|
|
|
VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
SuccessOrExit(error = aArgs[1].ParseAsUint8(level));
|
|
error = otLoggingSetLevel(static_cast<otLogLevel>(level));
|
|
#else
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
#endif
|
|
}
|
|
}
|
|
#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX
|
|
else if (aArgs[0] == "filename")
|
|
{
|
|
VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
SuccessOrExit(error = otPlatDebugUart_logfile(aArgs[1].GetCString()));
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
ExitNow(error = OT_ERROR_INVALID_ARGS);
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("extpanid")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputBytesLine(otThreadGetExtendedPanId(GetInstancePtr())->m8);
|
|
}
|
|
else
|
|
{
|
|
otExtendedPanId extPanId;
|
|
|
|
SuccessOrExit(error = aArgs[0].ParseAsHexString(extPanId.m8));
|
|
error = otThreadSetExtendedPanId(GetInstancePtr(), &extPanId);
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("factoryreset")>(Arg aArgs[])
|
|
{
|
|
OT_UNUSED_VARIABLE(aArgs);
|
|
|
|
otInstanceFactoryReset(GetInstancePtr());
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("fake")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_INVALID_COMMAND;
|
|
|
|
if (aArgs[0] == "/a/an")
|
|
{
|
|
otIp6Address destination, target;
|
|
otIp6InterfaceIdentifier mlIid;
|
|
|
|
SuccessOrExit(error = aArgs[1].ParseAsIp6Address(destination));
|
|
SuccessOrExit(error = aArgs[2].ParseAsIp6Address(target));
|
|
SuccessOrExit(error = aArgs[3].ParseAsHexString(mlIid.mFields.m8));
|
|
otThreadSendAddressNotification(GetInstancePtr(), &destination, &target, &mlIid);
|
|
}
|
|
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
|
|
else if (aArgs[0] == "/b/ba")
|
|
{
|
|
otIp6Address target;
|
|
otIp6InterfaceIdentifier mlIid;
|
|
uint32_t timeSinceLastTransaction;
|
|
|
|
SuccessOrExit(error = aArgs[1].ParseAsIp6Address(target));
|
|
SuccessOrExit(error = aArgs[2].ParseAsHexString(mlIid.mFields.m8));
|
|
SuccessOrExit(error = aArgs[3].ParseAsUint32(timeSinceLastTransaction));
|
|
|
|
error = otThreadSendProactiveBackboneNotification(GetInstancePtr(), &target, &mlIid, timeSinceLastTransaction);
|
|
}
|
|
#endif
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
template <> otError Interpreter::Process<Cmd("fem")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
int8_t lnaGain;
|
|
|
|
SuccessOrExit(error = otPlatRadioGetFemLnaGain(GetInstancePtr(), &lnaGain));
|
|
OutputLine("LNA gain %d dBm", lnaGain);
|
|
}
|
|
else if (aArgs[0] == "lnagain")
|
|
{
|
|
if (aArgs[1].IsEmpty())
|
|
{
|
|
int8_t lnaGain;
|
|
|
|
SuccessOrExit(error = otPlatRadioGetFemLnaGain(GetInstancePtr(), &lnaGain));
|
|
OutputLine("%d", lnaGain);
|
|
}
|
|
else
|
|
{
|
|
int8_t lnaGain;
|
|
|
|
SuccessOrExit(error = aArgs[1].ParseAsInt8(lnaGain));
|
|
SuccessOrExit(error = otPlatRadioSetFemLnaGain(GetInstancePtr(), lnaGain));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("ifconfig")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
if (otIp6IsEnabled(GetInstancePtr()))
|
|
{
|
|
OutputLine("up");
|
|
}
|
|
else
|
|
{
|
|
OutputLine("down");
|
|
}
|
|
}
|
|
else if (aArgs[0] == "up")
|
|
{
|
|
SuccessOrExit(error = otIp6SetEnabled(GetInstancePtr(), true));
|
|
}
|
|
else if (aArgs[0] == "down")
|
|
{
|
|
SuccessOrExit(error = otIp6SetEnabled(GetInstancePtr(), false));
|
|
}
|
|
else
|
|
{
|
|
ExitNow(error = OT_ERROR_INVALID_ARGS);
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
const char *Interpreter::AddressOriginToString(uint8_t aOrigin)
|
|
{
|
|
static const char *const kOriginStrings[4] = {
|
|
"thread", // 0, OT_ADDRESS_ORIGIN_THREAD
|
|
"slaac", // 1, OT_ADDRESS_ORIGIN_SLAAC
|
|
"dhcp6", // 2, OT_ADDRESS_ORIGIN_DHCPV6
|
|
"manual", // 3, OT_ADDRESS_ORIGIN_MANUAL
|
|
};
|
|
|
|
static_assert(0 == OT_ADDRESS_ORIGIN_THREAD, "OT_ADDRESS_ORIGIN_THREAD value is incorrect");
|
|
static_assert(1 == OT_ADDRESS_ORIGIN_SLAAC, "OT_ADDRESS_ORIGIN_SLAAC value is incorrect");
|
|
static_assert(2 == OT_ADDRESS_ORIGIN_DHCPV6, "OT_ADDRESS_ORIGIN_DHCPV6 value is incorrect");
|
|
static_assert(3 == OT_ADDRESS_ORIGIN_MANUAL, "OT_ADDRESS_ORIGIN_MANUAL value is incorrect");
|
|
|
|
return Stringify(aOrigin, kOriginStrings);
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("ipaddr")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
bool verbose = false;
|
|
|
|
if (aArgs[0] == "-v")
|
|
{
|
|
aArgs++;
|
|
verbose = true;
|
|
}
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
const otNetifAddress *unicastAddrs = otIp6GetUnicastAddresses(GetInstancePtr());
|
|
|
|
for (const otNetifAddress *addr = unicastAddrs; addr; addr = addr->mNext)
|
|
{
|
|
OutputIp6Address(addr->mAddress);
|
|
|
|
if (verbose)
|
|
{
|
|
OutputFormat(" origin:%s", AddressOriginToString(addr->mAddressOrigin));
|
|
}
|
|
|
|
OutputLine("");
|
|
}
|
|
}
|
|
else if (aArgs[0] == "add")
|
|
{
|
|
otNetifAddress address;
|
|
|
|
SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address.mAddress));
|
|
address.mPrefixLength = 64;
|
|
address.mPreferred = true;
|
|
address.mValid = true;
|
|
address.mAddressOrigin = OT_ADDRESS_ORIGIN_MANUAL;
|
|
|
|
error = otIp6AddUnicastAddress(GetInstancePtr(), &address);
|
|
}
|
|
else if (aArgs[0] == "del")
|
|
{
|
|
otIp6Address address;
|
|
|
|
SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
|
|
error = otIp6RemoveUnicastAddress(GetInstancePtr(), &address);
|
|
}
|
|
else if (aArgs[0] == "linklocal")
|
|
{
|
|
OutputIp6AddressLine(*otThreadGetLinkLocalIp6Address(GetInstancePtr()));
|
|
}
|
|
else if (aArgs[0] == "rloc")
|
|
{
|
|
OutputIp6AddressLine(*otThreadGetRloc(GetInstancePtr()));
|
|
}
|
|
else if (aArgs[0] == "mleid")
|
|
{
|
|
OutputIp6AddressLine(*otThreadGetMeshLocalEid(GetInstancePtr()));
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("ipmaddr")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
for (const otNetifMulticastAddress *addr = otIp6GetMulticastAddresses(GetInstancePtr()); addr;
|
|
addr = addr->mNext)
|
|
{
|
|
OutputIp6AddressLine(addr->mAddress);
|
|
}
|
|
}
|
|
else if (aArgs[0] == "add")
|
|
{
|
|
otIp6Address address;
|
|
|
|
aArgs++;
|
|
|
|
do
|
|
{
|
|
SuccessOrExit(error = aArgs->ParseAsIp6Address(address));
|
|
SuccessOrExit(error = otIp6SubscribeMulticastAddress(GetInstancePtr(), &address));
|
|
}
|
|
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
while (!(++aArgs)->IsEmpty());
|
|
#else
|
|
while (false);
|
|
#endif
|
|
}
|
|
else if (aArgs[0] == "del")
|
|
{
|
|
otIp6Address address;
|
|
|
|
SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
|
|
error = otIp6UnsubscribeMulticastAddress(GetInstancePtr(), &address);
|
|
}
|
|
else if (aArgs[0] == "promiscuous")
|
|
{
|
|
if (aArgs[1].IsEmpty())
|
|
{
|
|
OutputEnabledDisabledStatus(otIp6IsMulticastPromiscuousEnabled(GetInstancePtr()));
|
|
}
|
|
else
|
|
{
|
|
bool enable;
|
|
|
|
SuccessOrExit(error = ParseEnableOrDisable(aArgs[1], enable));
|
|
otIp6SetMulticastPromiscuousEnabled(GetInstancePtr(), enable);
|
|
}
|
|
}
|
|
else if (aArgs[0] == "llatn")
|
|
{
|
|
OutputIp6AddressLine(*otThreadGetLinkLocalAllThreadNodesMulticastAddress(GetInstancePtr()));
|
|
}
|
|
else if (aArgs[0] == "rlatn")
|
|
{
|
|
OutputIp6AddressLine(*otThreadGetRealmLocalAllThreadNodesMulticastAddress(GetInstancePtr()));
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("keysequence")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_INVALID_ARGS;
|
|
|
|
if (aArgs[0] == "counter")
|
|
{
|
|
error = ProcessGetSet(aArgs + 1, otThreadGetKeySequenceCounter, otThreadSetKeySequenceCounter);
|
|
}
|
|
else if (aArgs[0] == "guardtime")
|
|
{
|
|
error = ProcessGetSet(aArgs + 1, otThreadGetKeySwitchGuardTime, otThreadSetKeySwitchGuardTime);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("leaderdata")>(Arg aArgs[])
|
|
{
|
|
OT_UNUSED_VARIABLE(aArgs);
|
|
|
|
otError error;
|
|
otLeaderData leaderData;
|
|
|
|
SuccessOrExit(error = otThreadGetLeaderData(GetInstancePtr(), &leaderData));
|
|
|
|
OutputLine("Partition ID: %u", leaderData.mPartitionId);
|
|
OutputLine("Weighting: %d", leaderData.mWeighting);
|
|
OutputLine("Data Version: %d", leaderData.mDataVersion);
|
|
OutputLine("Stable Data Version: %d", leaderData.mStableDataVersion);
|
|
OutputLine("Leader Router ID: %d", leaderData.mLeaderRouterId);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_FTD
|
|
template <> otError Interpreter::Process<Cmd("partitionid")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_INVALID_COMMAND;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputLine("%u", otThreadGetPartitionId(GetInstancePtr()));
|
|
error = OT_ERROR_NONE;
|
|
}
|
|
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
else if (aArgs[0] == "preferred")
|
|
{
|
|
error = ProcessGetSet(aArgs + 1, otThreadGetPreferredLeaderPartitionId, otThreadSetPreferredLeaderPartitionId);
|
|
}
|
|
#endif
|
|
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("leaderweight")>(Arg aArgs[])
|
|
{
|
|
return ProcessGetSet(aArgs, otThreadGetLocalLeaderWeight, otThreadSetLocalLeaderWeight);
|
|
}
|
|
#endif // OPENTHREAD_FTD
|
|
|
|
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
|
|
void Interpreter::HandleLinkMetricsReport(const otIp6Address * aAddress,
|
|
const otLinkMetricsValues *aMetricsValues,
|
|
uint8_t aStatus,
|
|
void * aContext)
|
|
{
|
|
static_cast<Interpreter *>(aContext)->HandleLinkMetricsReport(aAddress, aMetricsValues, aStatus);
|
|
}
|
|
|
|
void Interpreter::PrintLinkMetricsValue(const otLinkMetricsValues *aMetricsValues)
|
|
{
|
|
const char kLinkMetricsTypeCount[] = "(Count/Summation)";
|
|
const char kLinkMetricsTypeAverage[] = "(Exponential Moving Average)";
|
|
|
|
if (aMetricsValues->mMetrics.mPduCount)
|
|
{
|
|
OutputLine(" - PDU Counter: %d %s", aMetricsValues->mPduCountValue, kLinkMetricsTypeCount);
|
|
}
|
|
|
|
if (aMetricsValues->mMetrics.mLqi)
|
|
{
|
|
OutputLine(" - LQI: %d %s", aMetricsValues->mLqiValue, kLinkMetricsTypeAverage);
|
|
}
|
|
|
|
if (aMetricsValues->mMetrics.mLinkMargin)
|
|
{
|
|
OutputLine(" - Margin: %d (dB) %s", aMetricsValues->mLinkMarginValue, kLinkMetricsTypeAverage);
|
|
}
|
|
|
|
if (aMetricsValues->mMetrics.mRssi)
|
|
{
|
|
OutputLine(" - RSSI: %d (dBm) %s", aMetricsValues->mRssiValue, kLinkMetricsTypeAverage);
|
|
}
|
|
}
|
|
|
|
void Interpreter::HandleLinkMetricsReport(const otIp6Address * aAddress,
|
|
const otLinkMetricsValues *aMetricsValues,
|
|
uint8_t aStatus)
|
|
{
|
|
OutputFormat("Received Link Metrics Report from: ");
|
|
OutputIp6AddressLine(*aAddress);
|
|
|
|
if (aMetricsValues != nullptr)
|
|
{
|
|
PrintLinkMetricsValue(aMetricsValues);
|
|
}
|
|
else
|
|
{
|
|
OutputLine("Link Metrics Report, status: %s", LinkMetricsStatusToStr(aStatus));
|
|
}
|
|
}
|
|
|
|
void Interpreter::HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, uint8_t aStatus, void *aContext)
|
|
{
|
|
static_cast<Interpreter *>(aContext)->HandleLinkMetricsMgmtResponse(aAddress, aStatus);
|
|
}
|
|
|
|
void Interpreter::HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, uint8_t aStatus)
|
|
{
|
|
OutputFormat("Received Link Metrics Management Response from: ");
|
|
OutputIp6AddressLine(*aAddress);
|
|
|
|
OutputLine("Status: %s", LinkMetricsStatusToStr(aStatus));
|
|
}
|
|
|
|
void Interpreter::HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress,
|
|
const otExtAddress * aExtAddress,
|
|
const otLinkMetricsValues *aMetricsValues,
|
|
void * aContext)
|
|
{
|
|
static_cast<Interpreter *>(aContext)->HandleLinkMetricsEnhAckProbingIe(aShortAddress, aExtAddress, aMetricsValues);
|
|
}
|
|
|
|
void Interpreter::HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress,
|
|
const otExtAddress * aExtAddress,
|
|
const otLinkMetricsValues *aMetricsValues)
|
|
{
|
|
OutputFormat("Received Link Metrics data in Enh Ack from neighbor, short address:0x%02x , extended address:",
|
|
aShortAddress);
|
|
OutputExtAddressLine(*aExtAddress);
|
|
|
|
if (aMetricsValues != nullptr)
|
|
{
|
|
PrintLinkMetricsValue(aMetricsValues);
|
|
}
|
|
}
|
|
|
|
const char *Interpreter::LinkMetricsStatusToStr(uint8_t aStatus)
|
|
{
|
|
static const char *const kStatusStrings[] = {
|
|
"Success", // (0) OT_LINK_METRICS_STATUS_SUCCESS
|
|
"Cannot support new series", // (1) OT_LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES
|
|
"Series ID already registered", // (2) OT_LINK_METRICS_STATUS_SERIESID_ALREADY_REGISTERED
|
|
"Series ID not recognized", // (3) OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED
|
|
"No matching series ID", // (4) OT_LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED
|
|
};
|
|
|
|
const char *str = "Unknown error";
|
|
|
|
static_assert(0 == OT_LINK_METRICS_STATUS_SUCCESS, "STATUS_SUCCESS is incorrect");
|
|
static_assert(1 == OT_LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES, "CANNOT_SUPPORT_NEW_SERIES is incorrect");
|
|
static_assert(2 == OT_LINK_METRICS_STATUS_SERIESID_ALREADY_REGISTERED, "SERIESID_ALREADY_REGISTERED is incorrect");
|
|
static_assert(3 == OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED, "SERIESID_NOT_RECOGNIZED is incorrect");
|
|
static_assert(4 == OT_LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED, "NO_MATCHING_FRAMES_RECEIVED is incorrect");
|
|
|
|
if (aStatus < OT_ARRAY_LENGTH(kStatusStrings))
|
|
{
|
|
str = kStatusStrings[aStatus];
|
|
}
|
|
else if (aStatus == OT_LINK_METRICS_STATUS_OTHER_ERROR)
|
|
{
|
|
str = "Other error";
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("linkmetrics")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_INVALID_COMMAND;
|
|
|
|
if (aArgs[0] == "query")
|
|
{
|
|
otIp6Address address;
|
|
otLinkMetrics linkMetrics;
|
|
|
|
SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
|
|
|
|
if (aArgs[2] == "single")
|
|
{
|
|
VerifyOrExit(!aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[3]));
|
|
error = otLinkMetricsQuery(GetInstancePtr(), &address, /* aSeriesId */ 0, &linkMetrics,
|
|
&Interpreter::HandleLinkMetricsReport, this);
|
|
}
|
|
else if (aArgs[2] == "forward")
|
|
{
|
|
uint8_t seriesId;
|
|
|
|
SuccessOrExit(error = aArgs[3].ParseAsUint8(seriesId));
|
|
error = otLinkMetricsQuery(GetInstancePtr(), &address, seriesId, nullptr,
|
|
&Interpreter::HandleLinkMetricsReport, this);
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
}
|
|
else if (aArgs[0] == "mgmt")
|
|
{
|
|
error = ProcessLinkMetricsMgmt(aArgs + 1);
|
|
}
|
|
else if (aArgs[0] == "probe")
|
|
{
|
|
otIp6Address address;
|
|
uint8_t seriesId;
|
|
uint8_t length;
|
|
|
|
SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
|
|
SuccessOrExit(error = aArgs[2].ParseAsUint8(seriesId));
|
|
SuccessOrExit(error = aArgs[3].ParseAsUint8(length));
|
|
|
|
error = otLinkMetricsSendLinkProbe(GetInstancePtr(), &address, seriesId, length);
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
otError Interpreter::ParseLinkMetricsFlags(otLinkMetrics &aLinkMetrics, const Arg &aFlags)
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
memset(&aLinkMetrics, 0, sizeof(aLinkMetrics));
|
|
|
|
for (const char *arg = aFlags.GetCString(); *arg != '\0'; arg++)
|
|
{
|
|
switch (*arg)
|
|
{
|
|
case 'p':
|
|
aLinkMetrics.mPduCount = true;
|
|
break;
|
|
|
|
case 'q':
|
|
aLinkMetrics.mLqi = true;
|
|
break;
|
|
|
|
case 'm':
|
|
aLinkMetrics.mLinkMargin = true;
|
|
break;
|
|
|
|
case 'r':
|
|
aLinkMetrics.mRssi = true;
|
|
break;
|
|
|
|
default:
|
|
ExitNow(error = OT_ERROR_INVALID_ARGS);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
otError Interpreter::ProcessLinkMetricsMgmt(Arg aArgs[])
|
|
{
|
|
otError error;
|
|
otIp6Address address;
|
|
otLinkMetricsSeriesFlags seriesFlags;
|
|
bool clear = false;
|
|
|
|
SuccessOrExit(error = aArgs[0].ParseAsIp6Address(address));
|
|
|
|
memset(&seriesFlags, 0, sizeof(otLinkMetricsSeriesFlags));
|
|
|
|
if (aArgs[1] == "forward")
|
|
{
|
|
uint8_t seriesId;
|
|
otLinkMetrics linkMetrics;
|
|
|
|
memset(&linkMetrics, 0, sizeof(otLinkMetrics));
|
|
SuccessOrExit(error = aArgs[2].ParseAsUint8(seriesId));
|
|
VerifyOrExit(!aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
|
|
for (const char *arg = aArgs[3].GetCString(); *arg != '\0'; arg++)
|
|
{
|
|
switch (*arg)
|
|
{
|
|
case 'l':
|
|
seriesFlags.mLinkProbe = true;
|
|
break;
|
|
|
|
case 'd':
|
|
seriesFlags.mMacData = true;
|
|
break;
|
|
|
|
case 'r':
|
|
seriesFlags.mMacDataRequest = true;
|
|
break;
|
|
|
|
case 'a':
|
|
seriesFlags.mMacAck = true;
|
|
break;
|
|
|
|
case 'X':
|
|
VerifyOrExit(arg == aArgs[3].GetCString() && *(arg + 1) == '\0' && aArgs[4].IsEmpty(),
|
|
error = OT_ERROR_INVALID_ARGS); // Ensure the flags only contain 'X'
|
|
clear = true;
|
|
break;
|
|
|
|
default:
|
|
ExitNow(error = OT_ERROR_INVALID_ARGS);
|
|
}
|
|
}
|
|
|
|
if (!clear)
|
|
{
|
|
VerifyOrExit(!aArgs[4].IsEmpty() && aArgs[5].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[4]));
|
|
}
|
|
|
|
error = otLinkMetricsConfigForwardTrackingSeries(GetInstancePtr(), &address, seriesId, seriesFlags,
|
|
clear ? nullptr : &linkMetrics,
|
|
&Interpreter::HandleLinkMetricsMgmtResponse, this);
|
|
}
|
|
else if (aArgs[1] == "enhanced-ack")
|
|
{
|
|
otLinkMetricsEnhAckFlags enhAckFlags;
|
|
otLinkMetrics linkMetrics;
|
|
otLinkMetrics * pLinkMetrics = &linkMetrics;
|
|
|
|
if (aArgs[2] == "clear")
|
|
{
|
|
enhAckFlags = OT_LINK_METRICS_ENH_ACK_CLEAR;
|
|
pLinkMetrics = nullptr;
|
|
}
|
|
else if (aArgs[2] == "register")
|
|
{
|
|
enhAckFlags = OT_LINK_METRICS_ENH_ACK_REGISTER;
|
|
VerifyOrExit(!aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[3]));
|
|
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
if (aArgs[4] == "r")
|
|
{
|
|
linkMetrics.mReserved = true;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
ExitNow(error = OT_ERROR_INVALID_ARGS);
|
|
}
|
|
|
|
error = otLinkMetricsConfigEnhAckProbing(GetInstancePtr(), &address, enhAckFlags, pLinkMetrics,
|
|
&Interpreter::HandleLinkMetricsMgmtResponse, this,
|
|
&Interpreter::HandleLinkMetricsEnhAckProbingIe, this);
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
|
|
|
|
#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
|
|
|
|
template <> otError Interpreter::Process<Cmd("locate")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_INVALID_ARGS;
|
|
otIp6Address anycastAddress;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputLine(otThreadIsAnycastLocateInProgress(GetInstancePtr()) ? "In Progress" : "Idle");
|
|
ExitNow(error = OT_ERROR_NONE);
|
|
}
|
|
|
|
SuccessOrExit(error = aArgs[0].ParseAsIp6Address(anycastAddress));
|
|
SuccessOrExit(error =
|
|
otThreadLocateAnycastDestination(GetInstancePtr(), &anycastAddress, HandleLocateResult, this));
|
|
SetCommandTimeout(kLocateTimeoutMsecs);
|
|
mLocateInProgress = true;
|
|
error = OT_ERROR_PENDING;
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
void Interpreter::HandleLocateResult(void * aContext,
|
|
otError aError,
|
|
const otIp6Address *aMeshLocalAddress,
|
|
uint16_t aRloc16)
|
|
{
|
|
static_cast<Interpreter *>(aContext)->HandleLocateResult(aError, aMeshLocalAddress, aRloc16);
|
|
}
|
|
|
|
void Interpreter::HandleLocateResult(otError aError, const otIp6Address *aMeshLocalAddress, uint16_t aRloc16)
|
|
{
|
|
VerifyOrExit(mLocateInProgress);
|
|
|
|
mLocateInProgress = false;
|
|
|
|
if (aError == OT_ERROR_NONE)
|
|
{
|
|
OutputIp6Address(*aMeshLocalAddress);
|
|
OutputLine(" 0x%04x", aRloc16);
|
|
}
|
|
|
|
OutputResult(aError);
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
#endif // OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
|
|
|
|
#if OPENTHREAD_FTD
|
|
template <> otError Interpreter::Process<Cmd("pskc")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
otPskc pskc;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
otThreadGetPskc(GetInstancePtr(), &pskc);
|
|
OutputBytesLine(pskc.m8);
|
|
}
|
|
else
|
|
{
|
|
if (aArgs[1].IsEmpty())
|
|
{
|
|
SuccessOrExit(error = aArgs[0].ParseAsHexString(pskc.m8));
|
|
}
|
|
else if (aArgs[0] == "-p")
|
|
{
|
|
SuccessOrExit(error = otDatasetGeneratePskc(
|
|
aArgs[1].GetCString(),
|
|
reinterpret_cast<const otNetworkName *>(otThreadGetNetworkName(GetInstancePtr())),
|
|
otThreadGetExtendedPanId(GetInstancePtr()), &pskc));
|
|
}
|
|
else
|
|
{
|
|
ExitNow(error = OT_ERROR_INVALID_ARGS);
|
|
}
|
|
|
|
error = otThreadSetPskc(GetInstancePtr(), &pskc);
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("pskcref")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputLine("0x%04x", otThreadGetPskcRef(GetInstancePtr()));
|
|
}
|
|
else
|
|
{
|
|
otPskcRef pskcRef;
|
|
|
|
if (aArgs[1].IsEmpty())
|
|
{
|
|
SuccessOrExit(error = aArgs[0].ParseAsUint32(pskcRef));
|
|
}
|
|
else
|
|
{
|
|
ExitNow(error = OT_ERROR_INVALID_ARGS);
|
|
}
|
|
|
|
error = otThreadSetPskcRef(GetInstancePtr(), pskcRef);
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
#endif
|
|
#endif // OPENTHREAD_FTD
|
|
|
|
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("mliid")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
otIp6InterfaceIdentifier iid;
|
|
|
|
VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
|
|
SuccessOrExit(error = aArgs[0].ParseAsHexString(iid.mFields.m8));
|
|
SuccessOrExit(error = otIp6SetMeshLocalIid(GetInstancePtr(), &iid));
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
|
|
|
template <> otError Interpreter::Process<Cmd("mlr")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_INVALID_COMMAND;
|
|
|
|
if (aArgs[0] == "reg")
|
|
{
|
|
otIp6Address addresses[kIp6AddressesNumMax];
|
|
uint32_t timeout;
|
|
bool hasTimeout = false;
|
|
uint8_t numAddresses = 0;
|
|
|
|
aArgs++;
|
|
|
|
while (aArgs->ParseAsIp6Address(addresses[numAddresses]) == OT_ERROR_NONE)
|
|
{
|
|
aArgs++;
|
|
numAddresses++;
|
|
|
|
if (numAddresses == OT_ARRAY_LENGTH(addresses))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (aArgs->ParseAsUint32(timeout) == OT_ERROR_NONE)
|
|
{
|
|
aArgs++;
|
|
hasTimeout = true;
|
|
}
|
|
|
|
VerifyOrExit(aArgs->IsEmpty() && (numAddresses > 0), error = OT_ERROR_INVALID_ARGS);
|
|
|
|
SuccessOrExit(error = otIp6RegisterMulticastListeners(GetInstancePtr(), addresses, numAddresses,
|
|
hasTimeout ? &timeout : nullptr,
|
|
Interpreter::HandleMlrRegResult, this));
|
|
|
|
error = OT_ERROR_PENDING;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
void Interpreter::HandleMlrRegResult(void * aContext,
|
|
otError aError,
|
|
uint8_t aMlrStatus,
|
|
const otIp6Address *aFailedAddresses,
|
|
uint8_t aFailedAddressNum)
|
|
{
|
|
static_cast<Interpreter *>(aContext)->HandleMlrRegResult(aError, aMlrStatus, aFailedAddresses, aFailedAddressNum);
|
|
}
|
|
|
|
void Interpreter::HandleMlrRegResult(otError aError,
|
|
uint8_t aMlrStatus,
|
|
const otIp6Address *aFailedAddresses,
|
|
uint8_t aFailedAddressNum)
|
|
{
|
|
if (aError == OT_ERROR_NONE)
|
|
{
|
|
OutputLine("status %d, %d failed", aMlrStatus, aFailedAddressNum);
|
|
|
|
for (uint8_t i = 0; i < aFailedAddressNum; i++)
|
|
{
|
|
OutputIp6AddressLine(aFailedAddresses[i]);
|
|
}
|
|
}
|
|
|
|
OutputResult(aError);
|
|
}
|
|
|
|
#endif // (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
|
|
|
template <> otError Interpreter::Process<Cmd("mode")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
otLinkModeConfig linkMode;
|
|
|
|
memset(&linkMode, 0, sizeof(otLinkModeConfig));
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
char linkModeString[kLinkModeStringSize];
|
|
|
|
OutputLine("%s", LinkModeToString(otThreadGetLinkMode(GetInstancePtr()), linkModeString));
|
|
ExitNow();
|
|
}
|
|
|
|
if (aArgs[0] != "-")
|
|
{
|
|
for (const char *arg = aArgs[0].GetCString(); *arg != '\0'; arg++)
|
|
{
|
|
switch (*arg)
|
|
{
|
|
case 'r':
|
|
linkMode.mRxOnWhenIdle = true;
|
|
break;
|
|
|
|
case 'd':
|
|
linkMode.mDeviceType = true;
|
|
break;
|
|
|
|
case 'n':
|
|
linkMode.mNetworkData = true;
|
|
break;
|
|
|
|
default:
|
|
ExitNow(error = OT_ERROR_INVALID_ARGS);
|
|
}
|
|
}
|
|
}
|
|
|
|
error = otThreadSetLinkMode(GetInstancePtr(), linkMode);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("multiradio")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
OT_UNUSED_VARIABLE(aArgs);
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
bool isFirst = true;
|
|
|
|
OutputFormat("[");
|
|
#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
|
|
OutputFormat("15.4");
|
|
isFirst = false;
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
|
|
OutputFormat("%sTREL", isFirst ? "" : ", ");
|
|
#endif
|
|
OutputLine("]");
|
|
|
|
OT_UNUSED_VARIABLE(isFirst);
|
|
}
|
|
#if OPENTHREAD_CONFIG_MULTI_RADIO
|
|
else if (aArgs[0] == "neighbor")
|
|
{
|
|
otMultiRadioNeighborInfo multiRadioInfo;
|
|
|
|
if (aArgs[1] == "list")
|
|
{
|
|
otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT;
|
|
otNeighborInfo neighInfo;
|
|
|
|
while (otThreadGetNextNeighborInfo(GetInstancePtr(), &iterator, &neighInfo) == OT_ERROR_NONE)
|
|
{
|
|
if (otMultiRadioGetNeighborInfo(GetInstancePtr(), &neighInfo.mExtAddress, &multiRadioInfo) !=
|
|
OT_ERROR_NONE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
OutputFormat("ExtAddr:");
|
|
OutputExtAddress(neighInfo.mExtAddress);
|
|
OutputFormat(", RLOC16:0x%04x, Radios:", neighInfo.mRloc16);
|
|
OutputMultiRadioInfo(multiRadioInfo);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
otExtAddress extAddress;
|
|
|
|
SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddress.m8));
|
|
SuccessOrExit(error = otMultiRadioGetNeighborInfo(GetInstancePtr(), &extAddress, &multiRadioInfo));
|
|
OutputMultiRadioInfo(multiRadioInfo);
|
|
}
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_MULTI_RADIO
|
|
else
|
|
{
|
|
ExitNow(error = OT_ERROR_INVALID_COMMAND);
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_MULTI_RADIO
|
|
void Interpreter::OutputMultiRadioInfo(const otMultiRadioNeighborInfo &aMultiRadioInfo)
|
|
{
|
|
bool isFirst = true;
|
|
|
|
OutputFormat("[");
|
|
|
|
if (aMultiRadioInfo.mSupportsIeee802154)
|
|
{
|
|
OutputFormat("15.4(%d)", aMultiRadioInfo.mIeee802154Info.mPreference);
|
|
isFirst = false;
|
|
}
|
|
|
|
if (aMultiRadioInfo.mSupportsTrelUdp6)
|
|
{
|
|
OutputFormat("%sTREL(%d)", isFirst ? "" : ", ", aMultiRadioInfo.mTrelUdp6Info.mPreference);
|
|
}
|
|
|
|
OutputLine("]");
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_MULTI_RADIO
|
|
|
|
#if OPENTHREAD_FTD
|
|
template <> otError Interpreter::Process<Cmd("neighbor")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
otNeighborInfo neighborInfo;
|
|
bool isTable;
|
|
otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT;
|
|
|
|
isTable = (aArgs[0] == "table");
|
|
|
|
if (isTable || (aArgs[0] == "list"))
|
|
{
|
|
if (isTable)
|
|
{
|
|
static const char *const kNeighborTableTitles[] = {
|
|
"Role", "RLOC16", "Age", "Avg RSSI", "Last RSSI", "R", "D", "N", "Extended MAC",
|
|
};
|
|
|
|
static const uint8_t kNeighborTableColumnWidths[] = {
|
|
6, 8, 5, 10, 11, 1, 1, 1, 18,
|
|
};
|
|
|
|
OutputTableHeader(kNeighborTableTitles, kNeighborTableColumnWidths);
|
|
}
|
|
|
|
while (otThreadGetNextNeighborInfo(GetInstancePtr(), &iterator, &neighborInfo) == OT_ERROR_NONE)
|
|
{
|
|
if (isTable)
|
|
{
|
|
OutputFormat("| %3c ", neighborInfo.mIsChild ? 'C' : 'R');
|
|
OutputFormat("| 0x%04x ", neighborInfo.mRloc16);
|
|
OutputFormat("| %3d ", neighborInfo.mAge);
|
|
OutputFormat("| %8d ", neighborInfo.mAverageRssi);
|
|
OutputFormat("| %9d ", neighborInfo.mLastRssi);
|
|
OutputFormat("|%1d", neighborInfo.mRxOnWhenIdle);
|
|
OutputFormat("|%1d", neighborInfo.mFullThreadDevice);
|
|
OutputFormat("|%1d", neighborInfo.mFullNetworkData);
|
|
OutputFormat("| ");
|
|
OutputExtAddress(neighborInfo.mExtAddress);
|
|
OutputLine(" |");
|
|
}
|
|
else
|
|
{
|
|
OutputFormat("0x%04x ", neighborInfo.mRloc16);
|
|
}
|
|
}
|
|
|
|
OutputLine("");
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
#endif // OPENTHREAD_FTD
|
|
|
|
template <> otError Interpreter::Process<Cmd("netstat")>(Arg aArgs[])
|
|
{
|
|
OT_UNUSED_VARIABLE(aArgs);
|
|
|
|
static const char *const kNetstatTableTitles[] = {"Local Address", "Peer Address"};
|
|
static const uint8_t kNetstatTableColumnWidths[] = {49, 49};
|
|
|
|
char string[OT_IP6_SOCK_ADDR_STRING_SIZE];
|
|
|
|
OutputTableHeader(kNetstatTableTitles, kNetstatTableColumnWidths);
|
|
|
|
for (const otUdpSocket *socket = otUdpGetSockets(GetInstancePtr()); socket != nullptr; socket = socket->mNext)
|
|
{
|
|
otIp6SockAddrToString(&socket->mSockName, string, sizeof(string));
|
|
OutputFormat("| %-47s ", string);
|
|
otIp6SockAddrToString(&socket->mPeerName, string, sizeof(string));
|
|
OutputLine("| %-47s |", string);
|
|
}
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("service")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_INVALID_COMMAND;
|
|
otServiceConfig cfg;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
|
|
otServiceConfig config;
|
|
|
|
while (otServerGetNextService(GetInstancePtr(), &iterator, &config) == OT_ERROR_NONE)
|
|
{
|
|
mNetworkData.OutputService(config);
|
|
}
|
|
|
|
error = OT_ERROR_NONE;
|
|
}
|
|
else
|
|
{
|
|
uint16_t length;
|
|
|
|
SuccessOrExit(error = aArgs[1].ParseAsUint32(cfg.mEnterpriseNumber));
|
|
|
|
length = sizeof(cfg.mServiceData);
|
|
SuccessOrExit(error = aArgs[2].ParseAsHexString(length, cfg.mServiceData));
|
|
VerifyOrExit(length > 0, error = OT_ERROR_INVALID_ARGS);
|
|
cfg.mServiceDataLength = static_cast<uint8_t>(length);
|
|
|
|
if (aArgs[0] == "add")
|
|
{
|
|
length = sizeof(cfg.mServerConfig.mServerData);
|
|
SuccessOrExit(error = aArgs[3].ParseAsHexString(length, cfg.mServerConfig.mServerData));
|
|
VerifyOrExit(length > 0, error = OT_ERROR_INVALID_ARGS);
|
|
cfg.mServerConfig.mServerDataLength = static_cast<uint8_t>(length);
|
|
|
|
cfg.mServerConfig.mStable = true;
|
|
|
|
error = otServerAddService(GetInstancePtr(), &cfg);
|
|
}
|
|
else if (aArgs[0] == "remove")
|
|
{
|
|
error = otServerRemoveService(GetInstancePtr(), cfg.mEnterpriseNumber, cfg.mServiceData,
|
|
cfg.mServiceDataLength);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
|
|
|
|
template <> otError Interpreter::Process<Cmd("netdata")>(Arg aArgs[])
|
|
{
|
|
return mNetworkData.Process(aArgs);
|
|
}
|
|
|
|
#if OPENTHREAD_FTD
|
|
template <> otError Interpreter::Process<Cmd("networkidtimeout")>(Arg aArgs[])
|
|
{
|
|
return ProcessGetSet(aArgs, otThreadGetNetworkIdTimeout, otThreadSetNetworkIdTimeout);
|
|
}
|
|
#endif
|
|
|
|
template <> otError Interpreter::Process<Cmd("networkkey")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
otNetworkKey networkKey;
|
|
|
|
otThreadGetNetworkKey(GetInstancePtr(), &networkKey);
|
|
OutputBytesLine(networkKey.m8);
|
|
}
|
|
else
|
|
{
|
|
otNetworkKey key;
|
|
|
|
SuccessOrExit(error = aArgs[0].ParseAsHexString(key.m8));
|
|
SuccessOrExit(error = otThreadSetNetworkKey(GetInstancePtr(), &key));
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("networkkeyref")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputLine("0x%04x", otThreadGetNetworkKeyRef(GetInstancePtr()));
|
|
}
|
|
else
|
|
{
|
|
otNetworkKeyRef keyRef;
|
|
|
|
SuccessOrExit(error = aArgs[0].ParseAsUint32(keyRef));
|
|
SuccessOrExit(error = otThreadSetNetworkKeyRef(GetInstancePtr(), keyRef));
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
template <> otError Interpreter::Process<Cmd("networkname")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputLine("%s", otThreadGetNetworkName(GetInstancePtr()));
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = otThreadSetNetworkName(GetInstancePtr(), aArgs[0].GetCString()));
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("networktime")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
uint64_t time;
|
|
otNetworkTimeStatus networkTimeStatus;
|
|
|
|
networkTimeStatus = otNetworkTimeGet(GetInstancePtr(), &time);
|
|
|
|
OutputFormat("Network Time: %luus", time);
|
|
|
|
switch (networkTimeStatus)
|
|
{
|
|
case OT_NETWORK_TIME_UNSYNCHRONIZED:
|
|
OutputLine(" (unsynchronized)");
|
|
break;
|
|
|
|
case OT_NETWORK_TIME_RESYNC_NEEDED:
|
|
OutputLine(" (resync needed)");
|
|
break;
|
|
|
|
case OT_NETWORK_TIME_SYNCHRONIZED:
|
|
OutputLine(" (synchronized)");
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
OutputLine("Time Sync Period: %ds", otNetworkTimeGetSyncPeriod(GetInstancePtr()));
|
|
OutputLine("XTAL Threshold: %dppm", otNetworkTimeGetXtalThreshold(GetInstancePtr()));
|
|
}
|
|
else
|
|
{
|
|
uint16_t period;
|
|
uint16_t xtalThreshold;
|
|
|
|
SuccessOrExit(error = aArgs[0].ParseAsUint16(period));
|
|
SuccessOrExit(error = aArgs[1].ParseAsUint16(xtalThreshold));
|
|
SuccessOrExit(error = otNetworkTimeSetSyncPeriod(GetInstancePtr(), period));
|
|
SuccessOrExit(error = otNetworkTimeSetXtalThreshold(GetInstancePtr(), xtalThreshold));
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
|
|
|
|
template <> otError Interpreter::Process<Cmd("panid")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputLine("0x%04x", otLinkGetPanId(GetInstancePtr()));
|
|
}
|
|
else
|
|
{
|
|
error = ProcessSet(aArgs, otLinkSetPanId);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("parent")>(Arg aArgs[])
|
|
{
|
|
OT_UNUSED_VARIABLE(aArgs);
|
|
|
|
otError error = OT_ERROR_NONE;
|
|
otRouterInfo parentInfo;
|
|
|
|
SuccessOrExit(error = otThreadGetParentInfo(GetInstancePtr(), &parentInfo));
|
|
OutputFormat("Ext Addr: ");
|
|
OutputExtAddressLine(parentInfo.mExtAddress);
|
|
OutputLine("Rloc: %x", parentInfo.mRloc16);
|
|
OutputLine("Link Quality In: %d", parentInfo.mLinkQualityIn);
|
|
OutputLine("Link Quality Out: %d", parentInfo.mLinkQualityOut);
|
|
OutputLine("Age: %d", parentInfo.mAge);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_FTD
|
|
template <> otError Interpreter::Process<Cmd("parentpriority")>(Arg aArgs[])
|
|
{
|
|
return ProcessGetSet(aArgs, otThreadGetParentPriority, otThreadSetParentPriority);
|
|
}
|
|
#endif
|
|
|
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("routeridrange")>(Arg *aArgs)
|
|
{
|
|
uint8_t minRouterId;
|
|
uint8_t maxRouterId;
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
otThreadGetRouterIdRange(GetInstancePtr(), &minRouterId, &maxRouterId);
|
|
OutputLine("%d %d", minRouterId, maxRouterId);
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = aArgs[0].ParseAsUint8(minRouterId));
|
|
SuccessOrExit(error = aArgs[1].ParseAsUint8(maxRouterId));
|
|
VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
error = otThreadSetRouterIdRange(GetInstancePtr(), minRouterId, maxRouterId);
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
|
|
|
|
void Interpreter::HandlePingReply(const otPingSenderReply *aReply, void *aContext)
|
|
{
|
|
static_cast<Interpreter *>(aContext)->HandlePingReply(aReply);
|
|
}
|
|
|
|
void Interpreter::HandlePingReply(const otPingSenderReply *aReply)
|
|
{
|
|
OutputFormat("%u bytes from ", static_cast<uint16_t>(aReply->mSize + sizeof(otIcmp6Header)));
|
|
OutputIp6Address(aReply->mSenderAddress);
|
|
OutputLine(": icmp_seq=%d hlim=%d time=%dms", aReply->mSequenceNumber, aReply->mHopLimit, aReply->mRoundTripTime);
|
|
}
|
|
|
|
void Interpreter::HandlePingStatistics(const otPingSenderStatistics *aStatistics, void *aContext)
|
|
{
|
|
static_cast<Interpreter *>(aContext)->HandlePingStatistics(aStatistics);
|
|
}
|
|
|
|
void Interpreter::HandlePingStatistics(const otPingSenderStatistics *aStatistics)
|
|
{
|
|
OutputFormat("%u packets transmitted, %u packets received.", aStatistics->mSentCount, aStatistics->mReceivedCount);
|
|
|
|
if ((aStatistics->mSentCount != 0) && !aStatistics->mIsMulticast &&
|
|
aStatistics->mReceivedCount <= aStatistics->mSentCount)
|
|
{
|
|
uint32_t packetLossRate =
|
|
1000 * (aStatistics->mSentCount - aStatistics->mReceivedCount) / aStatistics->mSentCount;
|
|
OutputFormat(" Packet loss = %u.%u%%.", packetLossRate / 10, packetLossRate % 10);
|
|
}
|
|
|
|
if (aStatistics->mReceivedCount != 0)
|
|
{
|
|
uint32_t avgRoundTripTime = 1000 * aStatistics->mTotalRoundTripTime / aStatistics->mReceivedCount;
|
|
OutputFormat(" Round-trip min/avg/max = %u/%u.%u/%u ms.", aStatistics->mMinRoundTripTime,
|
|
avgRoundTripTime / 1000, avgRoundTripTime % 1000, aStatistics->mMaxRoundTripTime);
|
|
}
|
|
|
|
OutputLine("");
|
|
|
|
if (!mPingIsAsync)
|
|
{
|
|
OutputResult(OT_ERROR_NONE);
|
|
}
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("ping")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
otPingSenderConfig config;
|
|
bool async = false;
|
|
|
|
if (aArgs[0] == "stop")
|
|
{
|
|
otPingSenderStop(GetInstancePtr());
|
|
ExitNow();
|
|
}
|
|
else if (aArgs[0] == "async")
|
|
{
|
|
async = true;
|
|
aArgs++;
|
|
}
|
|
|
|
memset(&config, 0, sizeof(config));
|
|
|
|
if (aArgs[0] == "-I")
|
|
{
|
|
SuccessOrExit(error = aArgs[1].ParseAsIp6Address(config.mSource));
|
|
|
|
#if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
{
|
|
bool valid = false;
|
|
const otNetifAddress *unicastAddrs = otIp6GetUnicastAddresses(GetInstancePtr());
|
|
|
|
for (const otNetifAddress *addr = unicastAddrs; addr; addr = addr->mNext)
|
|
{
|
|
if (otIp6IsAddressEqual(&addr->mAddress, &config.mSource))
|
|
{
|
|
valid = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
VerifyOrExit(valid, error = OT_ERROR_INVALID_ARGS);
|
|
}
|
|
#endif
|
|
|
|
aArgs += 2;
|
|
}
|
|
|
|
SuccessOrExit(error = aArgs[0].ParseAsIp6Address(config.mDestination));
|
|
|
|
if (!aArgs[1].IsEmpty())
|
|
{
|
|
SuccessOrExit(error = aArgs[1].ParseAsUint16(config.mSize));
|
|
}
|
|
|
|
if (!aArgs[2].IsEmpty())
|
|
{
|
|
SuccessOrExit(error = aArgs[2].ParseAsUint16(config.mCount));
|
|
}
|
|
|
|
if (!aArgs[3].IsEmpty())
|
|
{
|
|
SuccessOrExit(error = ParsePingInterval(aArgs[3], config.mInterval));
|
|
}
|
|
|
|
if (!aArgs[4].IsEmpty())
|
|
{
|
|
SuccessOrExit(error = aArgs[4].ParseAsUint8(config.mHopLimit));
|
|
config.mAllowZeroHopLimit = (config.mHopLimit == 0);
|
|
}
|
|
|
|
if (!aArgs[5].IsEmpty())
|
|
{
|
|
uint32_t timeout;
|
|
|
|
SuccessOrExit(error = ParsePingInterval(aArgs[5], timeout));
|
|
VerifyOrExit(timeout <= NumericLimits<uint16_t>::kMax, error = OT_ERROR_INVALID_ARGS);
|
|
config.mTimeout = static_cast<uint16_t>(timeout);
|
|
}
|
|
|
|
VerifyOrExit(aArgs[6].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
|
|
config.mReplyCallback = Interpreter::HandlePingReply;
|
|
config.mStatisticsCallback = Interpreter::HandlePingStatistics;
|
|
config.mCallbackContext = this;
|
|
|
|
SuccessOrExit(error = otPingSenderPing(GetInstancePtr(), &config));
|
|
|
|
mPingIsAsync = async;
|
|
|
|
if (!async)
|
|
{
|
|
error = OT_ERROR_PENDING;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#endif // OPENTHREAD_CONFIG_PING_SENDER_ENABLE
|
|
|
|
template <> otError Interpreter::Process<Cmd("pollperiod")>(Arg aArgs[])
|
|
{
|
|
return ProcessGetSet(aArgs, otLinkGetPollPeriod, otLinkSetPollPeriod);
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("promiscuous")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputEnabledDisabledStatus(otLinkIsPromiscuous(GetInstancePtr()) &&
|
|
otPlatRadioGetPromiscuous(GetInstancePtr()));
|
|
}
|
|
else
|
|
{
|
|
bool enable;
|
|
|
|
SuccessOrExit(error = ParseEnableOrDisable(aArgs[0], enable));
|
|
|
|
if (!enable)
|
|
{
|
|
otLinkSetPcapCallback(GetInstancePtr(), nullptr, nullptr);
|
|
}
|
|
|
|
SuccessOrExit(error = otLinkSetPromiscuous(GetInstancePtr(), enable));
|
|
|
|
if (enable)
|
|
{
|
|
otLinkSetPcapCallback(GetInstancePtr(), &HandleLinkPcapReceive, this);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
void Interpreter::HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx, void *aContext)
|
|
{
|
|
static_cast<Interpreter *>(aContext)->HandleLinkPcapReceive(aFrame, aIsTx);
|
|
}
|
|
|
|
void Interpreter::HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx)
|
|
{
|
|
OT_UNUSED_VARIABLE(aIsTx);
|
|
|
|
OutputLine("");
|
|
|
|
for (size_t i = 0; i < 44; i++)
|
|
{
|
|
OutputFormat("=");
|
|
}
|
|
|
|
OutputFormat("[len = %3u]", aFrame->mLength);
|
|
|
|
for (size_t i = 0; i < 28; i++)
|
|
{
|
|
OutputFormat("=");
|
|
}
|
|
|
|
OutputLine("");
|
|
|
|
for (size_t i = 0; i < aFrame->mLength; i += 16)
|
|
{
|
|
OutputFormat("|");
|
|
|
|
for (size_t j = 0; j < 16; j++)
|
|
{
|
|
if (i + j < aFrame->mLength)
|
|
{
|
|
OutputFormat(" %02X", aFrame->mPsdu[i + j]);
|
|
}
|
|
else
|
|
{
|
|
OutputFormat(" ..");
|
|
}
|
|
}
|
|
|
|
OutputFormat("|");
|
|
|
|
for (size_t j = 0; j < 16; j++)
|
|
{
|
|
if (i + j < aFrame->mLength)
|
|
{
|
|
if (31 < aFrame->mPsdu[i + j] && aFrame->mPsdu[i + j] < 127)
|
|
{
|
|
OutputFormat(" %c", aFrame->mPsdu[i + j]);
|
|
}
|
|
else
|
|
{
|
|
OutputFormat(" ?");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OutputFormat(" .");
|
|
}
|
|
}
|
|
|
|
OutputLine("|");
|
|
}
|
|
|
|
for (size_t i = 0; i < 83; i++)
|
|
{
|
|
OutputFormat("-");
|
|
}
|
|
|
|
OutputLine("");
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
|
|
otError Interpreter::ParsePrefix(Arg aArgs[], otBorderRouterConfig &aConfig)
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
memset(&aConfig, 0, sizeof(otBorderRouterConfig));
|
|
|
|
SuccessOrExit(error = aArgs[0].ParseAsIp6Prefix(aConfig.mPrefix));
|
|
aArgs++;
|
|
|
|
for (; !aArgs->IsEmpty(); aArgs++)
|
|
{
|
|
if (*aArgs == "high")
|
|
{
|
|
aConfig.mPreference = OT_ROUTE_PREFERENCE_HIGH;
|
|
}
|
|
else if (*aArgs == "med")
|
|
{
|
|
aConfig.mPreference = OT_ROUTE_PREFERENCE_MED;
|
|
}
|
|
else if (*aArgs == "low")
|
|
{
|
|
aConfig.mPreference = OT_ROUTE_PREFERENCE_LOW;
|
|
}
|
|
else
|
|
{
|
|
for (char *arg = aArgs->GetCString(); *arg != '\0'; arg++)
|
|
{
|
|
switch (*arg)
|
|
{
|
|
case 'p':
|
|
aConfig.mPreferred = true;
|
|
break;
|
|
|
|
case 'a':
|
|
aConfig.mSlaac = true;
|
|
break;
|
|
|
|
case 'd':
|
|
aConfig.mDhcp = true;
|
|
break;
|
|
|
|
case 'c':
|
|
aConfig.mConfigure = true;
|
|
break;
|
|
|
|
case 'r':
|
|
aConfig.mDefaultRoute = true;
|
|
break;
|
|
|
|
case 'o':
|
|
aConfig.mOnMesh = true;
|
|
break;
|
|
|
|
case 's':
|
|
aConfig.mStable = true;
|
|
break;
|
|
|
|
case 'n':
|
|
aConfig.mNdDns = true;
|
|
break;
|
|
|
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
|
|
case 'D':
|
|
aConfig.mDp = true;
|
|
break;
|
|
#endif
|
|
default:
|
|
ExitNow(error = OT_ERROR_INVALID_ARGS);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("prefix")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
|
|
otBorderRouterConfig config;
|
|
|
|
while (otBorderRouterGetNextOnMeshPrefix(GetInstancePtr(), &iterator, &config) == OT_ERROR_NONE)
|
|
{
|
|
mNetworkData.OutputPrefix(config);
|
|
}
|
|
|
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
|
|
if (otBackboneRouterGetState(GetInstancePtr()) == OT_BACKBONE_ROUTER_STATE_DISABLED)
|
|
{
|
|
SuccessOrExit(otBackboneRouterGetDomainPrefix(GetInstancePtr(), &config));
|
|
OutputFormat("- ");
|
|
mNetworkData.OutputPrefix(config);
|
|
}
|
|
#endif
|
|
}
|
|
else if (aArgs[0] == "add")
|
|
{
|
|
otBorderRouterConfig config;
|
|
|
|
SuccessOrExit(error = ParsePrefix(aArgs + 1, config));
|
|
error = otBorderRouterAddOnMeshPrefix(GetInstancePtr(), &config);
|
|
}
|
|
else if (aArgs[0] == "remove")
|
|
{
|
|
otIp6Prefix prefix;
|
|
|
|
SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix));
|
|
error = otBorderRouterRemoveOnMeshPrefix(GetInstancePtr(), &prefix);
|
|
}
|
|
else if (aArgs[0] == "meshlocal")
|
|
{
|
|
if (aArgs[1].IsEmpty())
|
|
{
|
|
OutputIp6PrefixLine(*otThreadGetMeshLocalPrefix(GetInstancePtr()));
|
|
}
|
|
else
|
|
{
|
|
otIp6Prefix prefix;
|
|
|
|
SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix));
|
|
VerifyOrExit(prefix.mLength == OT_IP6_PREFIX_BITSIZE, error = OT_ERROR_INVALID_ARGS);
|
|
error =
|
|
otThreadSetMeshLocalPrefix(GetInstancePtr(), reinterpret_cast<otMeshLocalPrefix *>(&prefix.mPrefix));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
|
|
|
|
#if OPENTHREAD_FTD
|
|
template <> otError Interpreter::Process<Cmd("preferrouterid")>(Arg aArgs[])
|
|
{
|
|
return ProcessSet(aArgs, otThreadSetPreferredRouterId);
|
|
}
|
|
#endif
|
|
|
|
#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("radiofilter")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputEnabledDisabledStatus(otLinkIsRadioFilterEnabled(GetInstancePtr()));
|
|
}
|
|
else
|
|
{
|
|
bool enable;
|
|
|
|
SuccessOrExit(error = ParseEnableOrDisable(aArgs[0], enable));
|
|
otLinkSetRadioFilterEnabled(GetInstancePtr(), enable);
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
template <> otError Interpreter::Process<Cmd("rcp")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
const char *version = otPlatRadioGetVersionString(GetInstancePtr());
|
|
|
|
VerifyOrExit(version != otGetVersionString(), error = OT_ERROR_NOT_IMPLEMENTED);
|
|
|
|
if (aArgs[0] == "version")
|
|
{
|
|
OutputLine("%s", version);
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("region")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
uint16_t regionCode;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
SuccessOrExit(error = otPlatRadioGetRegion(GetInstancePtr(), ®ionCode));
|
|
OutputLine("%c%c", regionCode >> 8, regionCode & 0xff);
|
|
}
|
|
else
|
|
{
|
|
VerifyOrExit(aArgs[0].GetLength() == 2, error = OT_ERROR_INVALID_ARGS);
|
|
|
|
regionCode = static_cast<uint16_t>(static_cast<uint16_t>(aArgs[0].GetCString()[0]) << 8) +
|
|
static_cast<uint16_t>(aArgs[0].GetCString()[1]);
|
|
error = otPlatRadioSetRegion(GetInstancePtr(), regionCode);
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_FTD
|
|
template <> otError Interpreter::Process<Cmd("releaserouterid")>(Arg aArgs[])
|
|
{
|
|
return ProcessSet(aArgs, otThreadReleaseRouterId);
|
|
}
|
|
#endif
|
|
|
|
template <> otError Interpreter::Process<Cmd("rloc16")>(Arg aArgs[])
|
|
{
|
|
OT_UNUSED_VARIABLE(aArgs);
|
|
|
|
OutputLine("%04x", otThreadGetRloc16(GetInstancePtr()));
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
|
|
otError Interpreter::ParseRoute(Arg aArgs[], otExternalRouteConfig &aConfig)
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
memset(&aConfig, 0, sizeof(otExternalRouteConfig));
|
|
|
|
SuccessOrExit(error = aArgs[0].ParseAsIp6Prefix(aConfig.mPrefix));
|
|
aArgs++;
|
|
|
|
for (; !aArgs->IsEmpty(); aArgs++)
|
|
{
|
|
if (*aArgs == "s")
|
|
{
|
|
aConfig.mStable = true;
|
|
}
|
|
else if (*aArgs == "n")
|
|
{
|
|
aConfig.mNat64 = true;
|
|
}
|
|
else if (*aArgs == "high")
|
|
{
|
|
aConfig.mPreference = OT_ROUTE_PREFERENCE_HIGH;
|
|
}
|
|
else if (*aArgs == "med")
|
|
{
|
|
aConfig.mPreference = OT_ROUTE_PREFERENCE_MED;
|
|
}
|
|
else if (*aArgs == "low")
|
|
{
|
|
aConfig.mPreference = OT_ROUTE_PREFERENCE_LOW;
|
|
}
|
|
else
|
|
{
|
|
ExitNow(error = OT_ERROR_INVALID_ARGS);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("route")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
|
|
otExternalRouteConfig config;
|
|
|
|
while (otBorderRouterGetNextRoute(GetInstancePtr(), &iterator, &config) == OT_ERROR_NONE)
|
|
{
|
|
mNetworkData.OutputRoute(config);
|
|
}
|
|
}
|
|
else if (aArgs[0] == "add")
|
|
{
|
|
otExternalRouteConfig config;
|
|
|
|
SuccessOrExit(error = ParseRoute(aArgs + 1, config));
|
|
error = otBorderRouterAddRoute(GetInstancePtr(), &config);
|
|
}
|
|
else if (aArgs[0] == "remove")
|
|
{
|
|
otIp6Prefix prefix;
|
|
|
|
SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix));
|
|
error = otBorderRouterRemoveRoute(GetInstancePtr(), &prefix);
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
|
|
|
|
#if OPENTHREAD_FTD
|
|
template <> otError Interpreter::Process<Cmd("router")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
otRouterInfo routerInfo;
|
|
uint16_t routerId;
|
|
bool isTable;
|
|
|
|
isTable = (aArgs[0] == "table");
|
|
|
|
if (isTable || (aArgs[0] == "list"))
|
|
{
|
|
uint8_t maxRouterId;
|
|
|
|
if (isTable)
|
|
{
|
|
static const char *const kRouterTableTitles[] = {
|
|
"ID", "RLOC16", "Next Hop", "Path Cost", "LQ In", "LQ Out", "Age", "Extended MAC", "Link",
|
|
};
|
|
|
|
static const uint8_t kRouterTableColumnWidths[] = {
|
|
4, 8, 10, 11, 7, 8, 5, 18, 6,
|
|
};
|
|
|
|
OutputTableHeader(kRouterTableTitles, kRouterTableColumnWidths);
|
|
}
|
|
|
|
maxRouterId = otThreadGetMaxRouterId(GetInstancePtr());
|
|
|
|
for (uint8_t i = 0; i <= maxRouterId; i++)
|
|
{
|
|
if (otThreadGetRouterInfo(GetInstancePtr(), i, &routerInfo) != OT_ERROR_NONE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (isTable)
|
|
{
|
|
OutputFormat("| %2d ", routerInfo.mRouterId);
|
|
OutputFormat("| 0x%04x ", routerInfo.mRloc16);
|
|
OutputFormat("| %8d ", routerInfo.mNextHop);
|
|
OutputFormat("| %9d ", routerInfo.mPathCost);
|
|
OutputFormat("| %5d ", routerInfo.mLinkQualityIn);
|
|
OutputFormat("| %6d ", routerInfo.mLinkQualityOut);
|
|
OutputFormat("| %3d ", routerInfo.mAge);
|
|
OutputFormat("| ");
|
|
OutputExtAddress(routerInfo.mExtAddress);
|
|
OutputLine(" | %4d |", routerInfo.mLinkEstablished);
|
|
}
|
|
else
|
|
{
|
|
OutputFormat("%d ", i);
|
|
}
|
|
}
|
|
|
|
OutputLine("");
|
|
ExitNow();
|
|
}
|
|
|
|
SuccessOrExit(error = aArgs[0].ParseAsUint16(routerId));
|
|
SuccessOrExit(error = otThreadGetRouterInfo(GetInstancePtr(), routerId, &routerInfo));
|
|
|
|
OutputLine("Alloc: %d", routerInfo.mAllocated);
|
|
|
|
if (routerInfo.mAllocated)
|
|
{
|
|
OutputLine("Router ID: %d", routerInfo.mRouterId);
|
|
OutputLine("Rloc: %04x", routerInfo.mRloc16);
|
|
OutputLine("Next Hop: %04x", static_cast<uint16_t>(routerInfo.mNextHop) << 10);
|
|
OutputLine("Link: %d", routerInfo.mLinkEstablished);
|
|
|
|
if (routerInfo.mLinkEstablished)
|
|
{
|
|
OutputFormat("Ext Addr: ");
|
|
OutputExtAddressLine(routerInfo.mExtAddress);
|
|
OutputLine("Cost: %d", routerInfo.mPathCost);
|
|
OutputLine("Link Quality In: %d", routerInfo.mLinkQualityIn);
|
|
OutputLine("Link Quality Out: %d", routerInfo.mLinkQualityOut);
|
|
OutputLine("Age: %d", routerInfo.mAge);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("routerdowngradethreshold")>(Arg aArgs[])
|
|
{
|
|
return ProcessGetSet(aArgs, otThreadGetRouterDowngradeThreshold, otThreadSetRouterDowngradeThreshold);
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("routereligible")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputEnabledDisabledStatus(otThreadIsRouterEligible(GetInstancePtr()));
|
|
}
|
|
else
|
|
{
|
|
bool enable;
|
|
|
|
SuccessOrExit(error = ParseEnableOrDisable(aArgs[0], enable));
|
|
error = otThreadSetRouterEligible(GetInstancePtr(), enable);
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("routerselectionjitter")>(Arg aArgs[])
|
|
{
|
|
return ProcessGetSet(aArgs, otThreadGetRouterSelectionJitter, otThreadSetRouterSelectionJitter);
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("routerupgradethreshold")>(Arg aArgs[])
|
|
{
|
|
return ProcessGetSet(aArgs, otThreadGetRouterUpgradeThreshold, otThreadSetRouterUpgradeThreshold);
|
|
}
|
|
#endif // OPENTHREAD_FTD
|
|
|
|
template <> otError Interpreter::Process<Cmd("scan")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
uint32_t scanChannels = 0;
|
|
uint16_t scanDuration = 0;
|
|
bool energyScan = false;
|
|
|
|
if (aArgs[0] == "energy")
|
|
{
|
|
energyScan = true;
|
|
aArgs++;
|
|
|
|
if (!aArgs->IsEmpty())
|
|
{
|
|
SuccessOrExit(error = aArgs->ParseAsUint16(scanDuration));
|
|
aArgs++;
|
|
}
|
|
}
|
|
|
|
if (!aArgs->IsEmpty())
|
|
{
|
|
uint8_t channel;
|
|
|
|
SuccessOrExit(error = aArgs->ParseAsUint8(channel));
|
|
VerifyOrExit(channel < sizeof(scanChannels) * CHAR_BIT, error = OT_ERROR_INVALID_ARGS);
|
|
scanChannels = 1 << channel;
|
|
}
|
|
|
|
if (energyScan)
|
|
{
|
|
static const char *const kEnergyScanTableTitles[] = {"Ch", "RSSI"};
|
|
static const uint8_t kEnergyScanTableColumnWidths[] = {4, 6};
|
|
|
|
OutputTableHeader(kEnergyScanTableTitles, kEnergyScanTableColumnWidths);
|
|
SuccessOrExit(error = otLinkEnergyScan(GetInstancePtr(), scanChannels, scanDuration,
|
|
&Interpreter::HandleEnergyScanResult, this));
|
|
}
|
|
else
|
|
{
|
|
static const char *const kScanTableTitles[] = {"PAN", "MAC Address", "Ch", "dBm", "LQI"};
|
|
static const uint8_t kScanTableColumnWidths[] = {6, 18, 4, 5, 5};
|
|
|
|
OutputTableHeader(kScanTableTitles, kScanTableColumnWidths);
|
|
|
|
SuccessOrExit(error = otLinkActiveScan(GetInstancePtr(), scanChannels, scanDuration,
|
|
&Interpreter::HandleActiveScanResult, this));
|
|
}
|
|
|
|
error = OT_ERROR_PENDING;
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult, void *aContext)
|
|
{
|
|
static_cast<Interpreter *>(aContext)->HandleActiveScanResult(aResult);
|
|
}
|
|
|
|
void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult)
|
|
{
|
|
if (aResult == nullptr)
|
|
{
|
|
OutputResult(OT_ERROR_NONE);
|
|
ExitNow();
|
|
}
|
|
|
|
if (aResult->mDiscover)
|
|
{
|
|
OutputFormat("| %-16s ", aResult->mNetworkName.m8);
|
|
|
|
OutputFormat("| ");
|
|
OutputBytes(aResult->mExtendedPanId.m8);
|
|
OutputFormat(" ");
|
|
}
|
|
|
|
OutputFormat("| %04x | ", aResult->mPanId);
|
|
OutputExtAddress(aResult->mExtAddress);
|
|
OutputFormat(" | %2d ", aResult->mChannel);
|
|
OutputFormat("| %3d ", aResult->mRssi);
|
|
OutputLine("| %3d |", aResult->mLqi);
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void Interpreter::HandleEnergyScanResult(otEnergyScanResult *aResult, void *aContext)
|
|
{
|
|
static_cast<Interpreter *>(aContext)->HandleEnergyScanResult(aResult);
|
|
}
|
|
|
|
void Interpreter::HandleEnergyScanResult(otEnergyScanResult *aResult)
|
|
{
|
|
if (aResult == nullptr)
|
|
{
|
|
OutputResult(OT_ERROR_NONE);
|
|
ExitNow();
|
|
}
|
|
|
|
OutputLine("| %2d | %4d |", aResult->mChannel, aResult->mMaxRssi);
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("singleton")>(Arg aArgs[])
|
|
{
|
|
OT_UNUSED_VARIABLE(aArgs);
|
|
|
|
OutputLine(otThreadIsSingleton(GetInstancePtr()) ? "true" : "false");
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("sntp")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
uint16_t port = OT_SNTP_DEFAULT_SERVER_PORT;
|
|
Ip6::MessageInfo messageInfo;
|
|
otSntpQuery query;
|
|
|
|
if (aArgs[0] == "query")
|
|
{
|
|
VerifyOrExit(!mSntpQueryingInProgress, error = OT_ERROR_BUSY);
|
|
|
|
if (!aArgs[1].IsEmpty())
|
|
{
|
|
SuccessOrExit(error = aArgs[1].ParseAsIp6Address(messageInfo.GetPeerAddr()));
|
|
}
|
|
else
|
|
{
|
|
// Use IPv6 address of default SNTP server.
|
|
SuccessOrExit(error = messageInfo.GetPeerAddr().FromString(OT_SNTP_DEFAULT_SERVER_IP));
|
|
}
|
|
|
|
if (!aArgs[2].IsEmpty())
|
|
{
|
|
SuccessOrExit(error = aArgs[2].ParseAsUint16(port));
|
|
}
|
|
|
|
messageInfo.SetPeerPort(port);
|
|
|
|
query.mMessageInfo = static_cast<const otMessageInfo *>(&messageInfo);
|
|
|
|
SuccessOrExit(error = otSntpClientQuery(GetInstancePtr(), &query, &Interpreter::HandleSntpResponse, this));
|
|
|
|
mSntpQueryingInProgress = true;
|
|
error = OT_ERROR_PENDING;
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
void Interpreter::HandleSntpResponse(void *aContext, uint64_t aTime, otError aResult)
|
|
{
|
|
static_cast<Interpreter *>(aContext)->HandleSntpResponse(aTime, aResult);
|
|
}
|
|
|
|
void Interpreter::HandleSntpResponse(uint64_t aTime, otError aResult)
|
|
{
|
|
if (aResult == OT_ERROR_NONE)
|
|
{
|
|
// Some Embedded C libraries do not support printing of 64-bit unsigned integers.
|
|
// To simplify, unix epoch time and era number are printed separately.
|
|
OutputLine("SNTP response - Unix time: %u (era: %u)", static_cast<uint32_t>(aTime),
|
|
static_cast<uint32_t>(aTime >> 32));
|
|
}
|
|
else
|
|
{
|
|
OutputLine("SNTP error - %s", otThreadErrorToString(aResult));
|
|
}
|
|
|
|
mSntpQueryingInProgress = false;
|
|
|
|
OutputResult(OT_ERROR_NONE);
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
|
|
|
|
#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("srp")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
|
|
OutputLine("client");
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
|
|
OutputLine("server");
|
|
#endif
|
|
ExitNow();
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
|
|
if (aArgs[0] == "client")
|
|
{
|
|
ExitNow(error = mSrpClient.Process(aArgs + 1));
|
|
}
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
|
|
if (aArgs[0] == "server")
|
|
{
|
|
ExitNow(error = mSrpServer.Process(aArgs + 1));
|
|
}
|
|
#endif
|
|
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
|
|
|
|
template <> otError Interpreter::Process<Cmd("state")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputLine("%s", otThreadDeviceRoleToString(otThreadGetDeviceRole(GetInstancePtr())));
|
|
}
|
|
else if (aArgs[0] == "detached")
|
|
{
|
|
error = otThreadBecomeDetached(GetInstancePtr());
|
|
}
|
|
else if (aArgs[0] == "child")
|
|
{
|
|
error = otThreadBecomeChild(GetInstancePtr());
|
|
}
|
|
#if OPENTHREAD_FTD
|
|
else if (aArgs[0] == "router")
|
|
{
|
|
error = otThreadBecomeRouter(GetInstancePtr());
|
|
}
|
|
else if (aArgs[0] == "leader")
|
|
{
|
|
error = otThreadBecomeLeader(GetInstancePtr());
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("thread")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0] == "start")
|
|
{
|
|
error = otThreadSetEnabled(GetInstancePtr(), true);
|
|
}
|
|
else if (aArgs[0] == "stop")
|
|
{
|
|
error = otThreadSetEnabled(GetInstancePtr(), false);
|
|
}
|
|
else if (aArgs[0] == "version")
|
|
{
|
|
OutputLine("%u", otThreadGetVersion());
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("dataset")>(Arg aArgs[])
|
|
{
|
|
return mDataset.Process(aArgs);
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("txpower")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
int8_t power;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
SuccessOrExit(error = otPlatRadioGetTransmitPower(GetInstancePtr(), &power));
|
|
OutputLine("%d dBm", power);
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = aArgs[0].ParseAsInt8(power));
|
|
error = otPlatRadioSetTransmitPower(GetInstancePtr(), power);
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("tcp")>(Arg aArgs[])
|
|
{
|
|
return mTcp.Process(aArgs);
|
|
}
|
|
#endif
|
|
|
|
template <> otError Interpreter::Process<Cmd("udp")>(Arg aArgs[])
|
|
{
|
|
return mUdp.Process(aArgs);
|
|
}
|
|
|
|
template <> otError Interpreter::Process<Cmd("unsecureport")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0] == "add")
|
|
{
|
|
error = ProcessSet(aArgs + 1, otIp6AddUnsecurePort);
|
|
}
|
|
else if (aArgs[0] == "remove")
|
|
{
|
|
if (aArgs[1] == "all")
|
|
{
|
|
otIp6RemoveAllUnsecurePorts(GetInstancePtr());
|
|
}
|
|
else
|
|
{
|
|
error = ProcessSet(aArgs + 1, otIp6RemoveUnsecurePort);
|
|
}
|
|
}
|
|
else if (aArgs[0] == "get")
|
|
{
|
|
const uint16_t *ports;
|
|
uint8_t numPorts;
|
|
|
|
ports = otIp6GetUnsecurePorts(GetInstancePtr(), &numPorts);
|
|
|
|
if (ports != nullptr)
|
|
{
|
|
for (uint8_t i = 0; i < numPorts; i++)
|
|
{
|
|
OutputFormat("%d ", ports[i]);
|
|
}
|
|
}
|
|
|
|
OutputLine("");
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_UPTIME_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("uptime")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
char string[OT_UPTIME_STRING_SIZE];
|
|
|
|
otInstanceGetUptimeAsString(GetInstancePtr(), string, sizeof(string));
|
|
OutputLine("%s", string);
|
|
}
|
|
else if (aArgs[0] == "ms")
|
|
{
|
|
OutputLine("%lu", otInstanceGetUptime(GetInstancePtr()));
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
|
|
template <> otError Interpreter::Process<Cmd("commissioner")>(Arg aArgs[])
|
|
{
|
|
return mCommissioner.Process(aArgs);
|
|
}
|
|
#endif
|
|
|
|
#if OPENTHREAD_CONFIG_JOINER_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("joiner")>(Arg aArgs[])
|
|
{
|
|
return mJoiner.Process(aArgs);
|
|
}
|
|
#endif
|
|
|
|
#if OPENTHREAD_FTD
|
|
template <> otError Interpreter::Process<Cmd("joinerport")>(Arg aArgs[])
|
|
{
|
|
return ProcessGetSet(aArgs, otThreadGetJoinerUdpPort, otThreadSetJoinerUdpPort);
|
|
}
|
|
#endif
|
|
|
|
#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("macfilter")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
PrintMacFilter();
|
|
}
|
|
else if (aArgs[0] == "addr")
|
|
{
|
|
error = ProcessMacFilterAddress(aArgs + 1);
|
|
}
|
|
else if (aArgs[0] == "rss")
|
|
{
|
|
error = ProcessMacFilterRss(aArgs + 1);
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
void Interpreter::PrintMacFilter(void)
|
|
{
|
|
otMacFilterEntry entry;
|
|
otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT;
|
|
|
|
OutputLine("Address Mode: %s", MacFilterAddressModeToString(otLinkFilterGetAddressMode(GetInstancePtr())));
|
|
|
|
while (otLinkFilterGetNextAddress(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE)
|
|
{
|
|
OutputMacFilterEntry(entry);
|
|
}
|
|
|
|
iterator = OT_MAC_FILTER_ITERATOR_INIT;
|
|
OutputLine("RssIn List:");
|
|
|
|
while (otLinkFilterGetNextRssIn(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE)
|
|
{
|
|
uint8_t i = 0;
|
|
|
|
for (; i < OT_EXT_ADDRESS_SIZE; i++)
|
|
{
|
|
if (entry.mExtAddress.m8[i] != 0xff)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == OT_EXT_ADDRESS_SIZE)
|
|
{
|
|
OutputLine("Default rss : %d (lqi %d)", entry.mRssIn,
|
|
otLinkConvertRssToLinkQuality(GetInstancePtr(), entry.mRssIn));
|
|
}
|
|
else
|
|
{
|
|
OutputMacFilterEntry(entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
otError Interpreter::ProcessMacFilterAddress(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
otExtAddress extAddr;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT;
|
|
otMacFilterEntry entry;
|
|
|
|
OutputLine("%s", MacFilterAddressModeToString(otLinkFilterGetAddressMode(GetInstancePtr())));
|
|
|
|
while (otLinkFilterGetNextAddress(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE)
|
|
{
|
|
OutputMacFilterEntry(entry);
|
|
}
|
|
}
|
|
else if (aArgs[0] == "disable")
|
|
{
|
|
VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
otLinkFilterSetAddressMode(GetInstancePtr(), OT_MAC_FILTER_ADDRESS_MODE_DISABLED);
|
|
}
|
|
else if (aArgs[0] == "allowlist")
|
|
{
|
|
VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
otLinkFilterSetAddressMode(GetInstancePtr(), OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST);
|
|
}
|
|
else if (aArgs[0] == "denylist")
|
|
{
|
|
VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
otLinkFilterSetAddressMode(GetInstancePtr(), OT_MAC_FILTER_ADDRESS_MODE_DENYLIST);
|
|
}
|
|
else if (aArgs[0] == "add")
|
|
{
|
|
SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
|
|
error = otLinkFilterAddAddress(GetInstancePtr(), &extAddr);
|
|
|
|
VerifyOrExit(error == OT_ERROR_NONE || error == OT_ERROR_ALREADY);
|
|
|
|
if (!aArgs[2].IsEmpty())
|
|
{
|
|
int8_t rss;
|
|
|
|
SuccessOrExit(error = aArgs[2].ParseAsInt8(rss));
|
|
SuccessOrExit(error = otLinkFilterAddRssIn(GetInstancePtr(), &extAddr, rss));
|
|
}
|
|
}
|
|
else if (aArgs[0] == "remove")
|
|
{
|
|
SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
|
|
otLinkFilterRemoveAddress(GetInstancePtr(), &extAddr);
|
|
}
|
|
else if (aArgs[0] == "clear")
|
|
{
|
|
otLinkFilterClearAddresses(GetInstancePtr());
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
otError Interpreter::ProcessMacFilterRss(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
otMacFilterEntry entry;
|
|
otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT;
|
|
otExtAddress extAddr;
|
|
int8_t rss;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
while (otLinkFilterGetNextRssIn(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE)
|
|
{
|
|
uint8_t i = 0;
|
|
|
|
for (; i < OT_EXT_ADDRESS_SIZE; i++)
|
|
{
|
|
if (entry.mExtAddress.m8[i] != 0xff)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == OT_EXT_ADDRESS_SIZE)
|
|
{
|
|
OutputLine("Default rss: %d (lqi %d)", entry.mRssIn,
|
|
otLinkConvertRssToLinkQuality(GetInstancePtr(), entry.mRssIn));
|
|
}
|
|
else
|
|
{
|
|
OutputMacFilterEntry(entry);
|
|
}
|
|
}
|
|
}
|
|
else if (aArgs[0] == "add-lqi")
|
|
{
|
|
uint8_t linkQuality;
|
|
|
|
SuccessOrExit(error = aArgs[2].ParseAsUint8(linkQuality));
|
|
VerifyOrExit(linkQuality <= 3, error = OT_ERROR_INVALID_ARGS);
|
|
rss = otLinkConvertLinkQualityToRss(GetInstancePtr(), linkQuality);
|
|
|
|
if (aArgs[1] == "*")
|
|
{
|
|
otLinkFilterSetDefaultRssIn(GetInstancePtr(), rss);
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
|
|
error = otLinkFilterAddRssIn(GetInstancePtr(), &extAddr, rss);
|
|
}
|
|
}
|
|
else if (aArgs[0] == "add")
|
|
{
|
|
SuccessOrExit(error = aArgs[2].ParseAsInt8(rss));
|
|
|
|
if (aArgs[1] == "*")
|
|
{
|
|
otLinkFilterSetDefaultRssIn(GetInstancePtr(), rss);
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
|
|
error = otLinkFilterAddRssIn(GetInstancePtr(), &extAddr, rss);
|
|
}
|
|
}
|
|
else if (aArgs[0] == "remove")
|
|
{
|
|
if (aArgs[1] == "*")
|
|
{
|
|
otLinkFilterClearDefaultRssIn(GetInstancePtr());
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
|
|
otLinkFilterRemoveRssIn(GetInstancePtr(), &extAddr);
|
|
}
|
|
}
|
|
else if (aArgs[0] == "clear")
|
|
{
|
|
otLinkFilterClearAllRssIn(GetInstancePtr());
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
void Interpreter::OutputMacFilterEntry(const otMacFilterEntry &aEntry)
|
|
{
|
|
OutputExtAddress(aEntry.mExtAddress);
|
|
|
|
if (aEntry.mRssIn != OT_MAC_FILTER_FIXED_RSS_DISABLED)
|
|
{
|
|
OutputFormat(" : rss %d (lqi %d)", aEntry.mRssIn,
|
|
otLinkConvertRssToLinkQuality(GetInstancePtr(), aEntry.mRssIn));
|
|
}
|
|
|
|
OutputLine("");
|
|
}
|
|
|
|
const char *Interpreter::MacFilterAddressModeToString(otMacFilterAddressMode aMode)
|
|
{
|
|
static const char *const kModeStrings[] = {
|
|
"Disabled", // (0) OT_MAC_FILTER_ADDRESS_MODE_DISABLED
|
|
"Allowlist", // (1) OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST
|
|
"Denylist", // (2) OT_MAC_FILTER_ADDRESS_MODE_DENYLIST
|
|
};
|
|
|
|
static_assert(0 == OT_MAC_FILTER_ADDRESS_MODE_DISABLED, "OT_MAC_FILTER_ADDRESS_MODE_DISABLED value is incorrect");
|
|
static_assert(1 == OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST, "OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST value is incorrect");
|
|
static_assert(2 == OT_MAC_FILTER_ADDRESS_MODE_DENYLIST, "OT_MAC_FILTER_ADDRESS_MODE_DENYLIST value is incorrect");
|
|
|
|
return Stringify(aMode, kModeStrings);
|
|
}
|
|
|
|
#endif // OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
|
|
|
|
template <> otError Interpreter::Process<Cmd("mac")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (aArgs[0] == "retries")
|
|
{
|
|
if (aArgs[1] == "direct")
|
|
{
|
|
error = ProcessGetSet(aArgs + 2, otLinkGetMaxFrameRetriesDirect, otLinkSetMaxFrameRetriesDirect);
|
|
}
|
|
#if OPENTHREAD_FTD
|
|
else if (aArgs[1] == "indirect")
|
|
{
|
|
error = ProcessGetSet(aArgs + 2, otLinkGetMaxFrameRetriesIndirect, otLinkSetMaxFrameRetriesIndirect);
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
}
|
|
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
else if (aArgs[0] == "send")
|
|
{
|
|
VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
|
|
|
if (aArgs[1] == "datarequest")
|
|
{
|
|
error = otLinkSendDataRequest(GetInstancePtr());
|
|
}
|
|
else if (aArgs[1] == "emptydata")
|
|
{
|
|
error = otLinkSendEmptyData(GetInstancePtr());
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
ExitNow(); // To silence unused `exit` label warning when `REFERENCE_DEVICE_ENABLE` is not enabled.
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("trel")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
bool enable;
|
|
|
|
if (aArgs[0].IsEmpty())
|
|
{
|
|
OutputEnabledDisabledStatus(otTrelIsEnabled(GetInstancePtr()));
|
|
}
|
|
else if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
|
|
{
|
|
if (enable)
|
|
{
|
|
otTrelEnable(GetInstancePtr());
|
|
}
|
|
else
|
|
{
|
|
otTrelDisable(GetInstancePtr());
|
|
}
|
|
}
|
|
else if (aArgs[0] == "filter")
|
|
{
|
|
if (aArgs[1].IsEmpty())
|
|
{
|
|
OutputEnabledDisabledStatus(otTrelIsFilterEnabled(GetInstancePtr()));
|
|
}
|
|
else
|
|
{
|
|
SuccessOrExit(error = ParseEnableOrDisable(aArgs[1], enable));
|
|
otTrelSetFilterEnabled(GetInstancePtr(), enable);
|
|
}
|
|
}
|
|
else if (aArgs[0] == "peers")
|
|
{
|
|
uint16_t index = 0;
|
|
otTrelPeerIterator iterator;
|
|
const otTrelPeer * peer;
|
|
bool isTable = true;
|
|
|
|
if (aArgs[1] == "list")
|
|
{
|
|
isTable = false;
|
|
}
|
|
else
|
|
{
|
|
VerifyOrExit(aArgs[1].IsEmpty(), error = kErrorInvalidArgs);
|
|
}
|
|
|
|
if (isTable)
|
|
{
|
|
static const char *const kTrelPeerTableTitles[] = {"No", "Ext MAC Address", "Ext PAN Id",
|
|
"IPv6 Socket Address"};
|
|
|
|
static const uint8_t kTrelPeerTableColumnWidths[] = {5, 18, 18, 50};
|
|
|
|
OutputTableHeader(kTrelPeerTableTitles, kTrelPeerTableColumnWidths);
|
|
}
|
|
|
|
otTrelInitPeerIterator(GetInstancePtr(), &iterator);
|
|
|
|
while ((peer = otTrelGetNextPeer(GetInstancePtr(), &iterator)) != nullptr)
|
|
{
|
|
if (!isTable)
|
|
{
|
|
OutputFormat("%03u ExtAddr:", ++index);
|
|
OutputExtAddress(peer->mExtAddress);
|
|
OutputFormat(" ExtPanId:");
|
|
OutputBytes(peer->mExtPanId.m8);
|
|
OutputFormat(" SockAddr:");
|
|
OutputSockAddrLine(peer->mSockAddr);
|
|
}
|
|
else
|
|
{
|
|
char string[OT_IP6_SOCK_ADDR_STRING_SIZE];
|
|
|
|
OutputFormat("| %3u | ", ++index);
|
|
OutputExtAddress(peer->mExtAddress);
|
|
OutputFormat(" | ");
|
|
OutputBytes(peer->mExtPanId.m8);
|
|
otIp6SockAddrToString(&peer->mSockAddr, string, sizeof(string));
|
|
OutputLine(" | %-48s |", string);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_ARGS;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
|
|
template <> otError Interpreter::Process<Cmd("networkdiagnostic")>(Arg aArgs[])
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
otIp6Address address;
|
|
uint8_t tlvTypes[OT_NETWORK_DIAGNOSTIC_TYPELIST_MAX_ENTRIES];
|
|
uint8_t count = 0;
|
|
|
|
SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
|
|
|
|
for (Arg *arg = &aArgs[2]; !arg->IsEmpty(); arg++)
|
|
{
|
|
VerifyOrExit(count < sizeof(tlvTypes), error = OT_ERROR_INVALID_ARGS);
|
|
SuccessOrExit(error = arg->ParseAsUint8(tlvTypes[count++]));
|
|
}
|
|
|
|
if (aArgs[0] == "get")
|
|
{
|
|
SuccessOrExit(error = otThreadSendDiagnosticGet(GetInstancePtr(), &address, tlvTypes, count,
|
|
&Interpreter::HandleDiagnosticGetResponse, this));
|
|
SetCommandTimeout(kNetworkDiagnosticTimeoutMsecs);
|
|
error = OT_ERROR_PENDING;
|
|
}
|
|
else if (aArgs[0] == "reset")
|
|
{
|
|
IgnoreError(otThreadSendDiagnosticReset(GetInstancePtr(), &address, tlvTypes, count));
|
|
}
|
|
else
|
|
{
|
|
error = OT_ERROR_INVALID_COMMAND;
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
void Interpreter::HandleDiagnosticGetResponse(otError aError,
|
|
otMessage * aMessage,
|
|
const otMessageInfo *aMessageInfo,
|
|
void * aContext)
|
|
{
|
|
static_cast<Interpreter *>(aContext)->HandleDiagnosticGetResponse(
|
|
aError, aMessage, static_cast<const Ip6::MessageInfo *>(aMessageInfo));
|
|
}
|
|
|
|
void Interpreter::HandleDiagnosticGetResponse(otError aError,
|
|
const otMessage * aMessage,
|
|
const Ip6::MessageInfo *aMessageInfo)
|
|
{
|
|
uint8_t buf[16];
|
|
uint16_t bytesToPrint;
|
|
uint16_t bytesPrinted = 0;
|
|
uint16_t length;
|
|
otNetworkDiagTlv diagTlv;
|
|
otNetworkDiagIterator iterator = OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT;
|
|
|
|
SuccessOrExit(aError);
|
|
|
|
OutputFormat("DIAG_GET.rsp/ans from ");
|
|
OutputIp6Address(aMessageInfo->mPeerAddr);
|
|
OutputFormat(": ");
|
|
|
|
length = otMessageGetLength(aMessage) - otMessageGetOffset(aMessage);
|
|
|
|
while (length > 0)
|
|
{
|
|
bytesToPrint = (length < sizeof(buf)) ? length : sizeof(buf);
|
|
otMessageRead(aMessage, otMessageGetOffset(aMessage) + bytesPrinted, buf, bytesToPrint);
|
|
|
|
OutputBytes(buf, static_cast<uint8_t>(bytesToPrint));
|
|
|
|
length -= bytesToPrint;
|
|
bytesPrinted += bytesToPrint;
|
|
}
|
|
|
|
OutputLine("");
|
|
|
|
// Output Network Diagnostic TLV values in standard YAML format.
|
|
while (otThreadGetNextDiagnosticTlv(aMessage, &iterator, &diagTlv) == OT_ERROR_NONE)
|
|
{
|
|
switch (diagTlv.mType)
|
|
{
|
|
case OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS:
|
|
OutputFormat("Ext Address: '");
|
|
OutputExtAddressLine(diagTlv.mData.mExtAddress);
|
|
break;
|
|
case OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS:
|
|
OutputLine("Rloc16: 0x%04x", diagTlv.mData.mAddr16);
|
|
break;
|
|
case OT_NETWORK_DIAGNOSTIC_TLV_MODE:
|
|
OutputLine("Mode:");
|
|
OutputMode(kIndentSize, diagTlv.mData.mMode);
|
|
break;
|
|
case OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT:
|
|
OutputLine("Timeout: %u", diagTlv.mData.mTimeout);
|
|
break;
|
|
case OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY:
|
|
OutputLine("Connectivity:");
|
|
OutputConnectivity(kIndentSize, diagTlv.mData.mConnectivity);
|
|
break;
|
|
case OT_NETWORK_DIAGNOSTIC_TLV_ROUTE:
|
|
OutputLine("Route:");
|
|
OutputRoute(kIndentSize, diagTlv.mData.mRoute);
|
|
break;
|
|
case OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA:
|
|
OutputLine("Leader Data:");
|
|
OutputLeaderData(kIndentSize, diagTlv.mData.mLeaderData);
|
|
break;
|
|
case OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA:
|
|
OutputFormat("Network Data: '");
|
|
OutputBytesLine(diagTlv.mData.mNetworkData.m8, diagTlv.mData.mNetworkData.mCount);
|
|
break;
|
|
case OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST:
|
|
OutputLine("IP6 Address List:");
|
|
for (uint16_t i = 0; i < diagTlv.mData.mIp6AddrList.mCount; ++i)
|
|
{
|
|
OutputFormat(kIndentSize, "- ");
|
|
OutputIp6AddressLine(diagTlv.mData.mIp6AddrList.mList[i]);
|
|
}
|
|
break;
|
|
case OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS:
|
|
OutputLine("MAC Counters:");
|
|
OutputNetworkDiagMacCounters(kIndentSize, diagTlv.mData.mMacCounters);
|
|
break;
|
|
case OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL:
|
|
OutputLine("Battery Level: %u%%", diagTlv.mData.mBatteryLevel);
|
|
break;
|
|
case OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE:
|
|
OutputLine("Supply Voltage: %umV", diagTlv.mData.mSupplyVoltage);
|
|
break;
|
|
case OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE:
|
|
OutputLine("Child Table:");
|
|
for (uint16_t i = 0; i < diagTlv.mData.mChildTable.mCount; ++i)
|
|
{
|
|
OutputFormat(kIndentSize, "- ");
|
|
OutputChildTableEntry(kIndentSize + 2, diagTlv.mData.mChildTable.mTable[i]);
|
|
}
|
|
break;
|
|
case OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES:
|
|
OutputFormat("Channel Pages: '");
|
|
OutputBytes(diagTlv.mData.mChannelPages.m8, diagTlv.mData.mChannelPages.mCount);
|
|
OutputLine("'");
|
|
break;
|
|
case OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT:
|
|
OutputLine("Max Child Timeout: %u", diagTlv.mData.mMaxChildTimeout);
|
|
break;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void Interpreter::OutputMode(uint8_t aIndentSize, const otLinkModeConfig &aMode)
|
|
{
|
|
OutputLine(aIndentSize, "RxOnWhenIdle: %d", aMode.mRxOnWhenIdle);
|
|
OutputLine(aIndentSize, "DeviceType: %d", aMode.mDeviceType);
|
|
OutputLine(aIndentSize, "NetworkData: %d", aMode.mNetworkData);
|
|
}
|
|
|
|
void Interpreter::OutputConnectivity(uint8_t aIndentSize, const otNetworkDiagConnectivity &aConnectivity)
|
|
{
|
|
OutputLine(aIndentSize, "ParentPriority: %d", aConnectivity.mParentPriority);
|
|
OutputLine(aIndentSize, "LinkQuality3: %u", aConnectivity.mLinkQuality3);
|
|
OutputLine(aIndentSize, "LinkQuality2: %u", aConnectivity.mLinkQuality2);
|
|
OutputLine(aIndentSize, "LinkQuality1: %u", aConnectivity.mLinkQuality1);
|
|
OutputLine(aIndentSize, "LeaderCost: %u", aConnectivity.mLeaderCost);
|
|
OutputLine(aIndentSize, "IdSequence: %u", aConnectivity.mIdSequence);
|
|
OutputLine(aIndentSize, "ActiveRouters: %u", aConnectivity.mActiveRouters);
|
|
OutputLine(aIndentSize, "SedBufferSize: %u", aConnectivity.mSedBufferSize);
|
|
OutputLine(aIndentSize, "SedDatagramCount: %u", aConnectivity.mSedDatagramCount);
|
|
}
|
|
void Interpreter::OutputRoute(uint8_t aIndentSize, const otNetworkDiagRoute &aRoute)
|
|
{
|
|
OutputLine(aIndentSize, "IdSequence: %u", aRoute.mIdSequence);
|
|
OutputLine(aIndentSize, "RouteData:");
|
|
|
|
aIndentSize += kIndentSize;
|
|
for (uint16_t i = 0; i < aRoute.mRouteCount; ++i)
|
|
{
|
|
OutputFormat(aIndentSize, "- ");
|
|
OutputRouteData(aIndentSize + 2, aRoute.mRouteData[i]);
|
|
}
|
|
}
|
|
|
|
void Interpreter::OutputRouteData(uint8_t aIndentSize, const otNetworkDiagRouteData &aRouteData)
|
|
{
|
|
OutputLine("RouteId: 0x%02x", aRouteData.mRouterId);
|
|
|
|
OutputLine(aIndentSize, "LinkQualityOut: %u", aRouteData.mLinkQualityOut);
|
|
OutputLine(aIndentSize, "LinkQualityIn: %u", aRouteData.mLinkQualityIn);
|
|
OutputLine(aIndentSize, "RouteCost: %u", aRouteData.mRouteCost);
|
|
}
|
|
|
|
void Interpreter::OutputLeaderData(uint8_t aIndentSize, const otLeaderData &aLeaderData)
|
|
{
|
|
OutputLine(aIndentSize, "PartitionId: 0x%08x", aLeaderData.mPartitionId);
|
|
OutputLine(aIndentSize, "Weighting: %u", aLeaderData.mWeighting);
|
|
OutputLine(aIndentSize, "DataVersion: %u", aLeaderData.mDataVersion);
|
|
OutputLine(aIndentSize, "StableDataVersion: %u", aLeaderData.mStableDataVersion);
|
|
OutputLine(aIndentSize, "LeaderRouterId: 0x%02x", aLeaderData.mLeaderRouterId);
|
|
}
|
|
|
|
void Interpreter::OutputNetworkDiagMacCounters(uint8_t aIndentSize, const otNetworkDiagMacCounters &aMacCounters)
|
|
{
|
|
OutputLine(aIndentSize, "IfInUnknownProtos: %u", aMacCounters.mIfInUnknownProtos);
|
|
OutputLine(aIndentSize, "IfInErrors: %u", aMacCounters.mIfInErrors);
|
|
OutputLine(aIndentSize, "IfOutErrors: %u", aMacCounters.mIfOutErrors);
|
|
OutputLine(aIndentSize, "IfInUcastPkts: %u", aMacCounters.mIfInUcastPkts);
|
|
OutputLine(aIndentSize, "IfInBroadcastPkts: %u", aMacCounters.mIfInBroadcastPkts);
|
|
OutputLine(aIndentSize, "IfInDiscards: %u", aMacCounters.mIfInDiscards);
|
|
OutputLine(aIndentSize, "IfOutUcastPkts: %u", aMacCounters.mIfOutUcastPkts);
|
|
OutputLine(aIndentSize, "IfOutBroadcastPkts: %u", aMacCounters.mIfOutBroadcastPkts);
|
|
OutputLine(aIndentSize, "IfOutDiscards: %u", aMacCounters.mIfOutDiscards);
|
|
}
|
|
|
|
void Interpreter::OutputChildTableEntry(uint8_t aIndentSize, const otNetworkDiagChildEntry &aChildEntry)
|
|
{
|
|
OutputLine("ChildId: 0x%04x", aChildEntry.mChildId);
|
|
|
|
OutputLine(aIndentSize, "Timeout: %u", aChildEntry.mTimeout);
|
|
OutputLine(aIndentSize, "Mode:");
|
|
OutputMode(aIndentSize + kIndentSize, aChildEntry.mMode);
|
|
}
|
|
#endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
|
|
|
|
void Interpreter::HandleDetachGracefullyResult(void *aContext)
|
|
{
|
|
static_cast<Interpreter *>(aContext)->HandleDetachGracefullyResult();
|
|
}
|
|
|
|
void Interpreter::HandleDetachGracefullyResult(void)
|
|
{
|
|
OutputLine("Finished detaching");
|
|
OutputResult(OT_ERROR_NONE);
|
|
}
|
|
|
|
void Interpreter::HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo &aInfo)
|
|
{
|
|
OutputFormat("~ Discovery Request from ");
|
|
OutputExtAddress(aInfo.mExtAddress);
|
|
OutputLine(": version=%u,joiner=%d", aInfo.mVersion, aInfo.mIsJoiner);
|
|
}
|
|
|
|
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
|
|
|
|
void Interpreter::Initialize(otInstance *aInstance, otCliOutputCallback aCallback, void *aContext)
|
|
{
|
|
Instance *instance = static_cast<Instance *>(aInstance);
|
|
|
|
Interpreter::sInterpreter = new (&sInterpreterRaw) Interpreter(instance, aCallback, aContext);
|
|
}
|
|
|
|
void Interpreter::OutputPrompt(void)
|
|
{
|
|
#if OPENTHREAD_CONFIG_CLI_PROMPT_ENABLE
|
|
static const char sPrompt[] = "> ";
|
|
|
|
// The `OutputFormat()` below is adding the prompt which is not
|
|
// part of any command output, so we set the `EmittingCommandOutput`
|
|
// flag to false to avoid it being included in the command output
|
|
// log (under `OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE`).
|
|
|
|
SetEmittingCommandOutput(false);
|
|
OutputFormat("%s", sPrompt);
|
|
SetEmittingCommandOutput(true);
|
|
#endif // OPENTHREAD_CONFIG_CLI_PROMPT_ENABLE
|
|
}
|
|
|
|
void Interpreter::HandleTimer(Timer &aTimer)
|
|
{
|
|
static_cast<Interpreter *>(static_cast<TimerMilliContext &>(aTimer).GetContext())->HandleTimer();
|
|
}
|
|
|
|
void Interpreter::HandleTimer(void)
|
|
{
|
|
#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
|
|
if (mLocateInProgress)
|
|
{
|
|
mLocateInProgress = false;
|
|
OutputResult(OT_ERROR_RESPONSE_TIMEOUT);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
OutputResult(OT_ERROR_NONE);
|
|
}
|
|
}
|
|
|
|
void Interpreter::SetCommandTimeout(uint32_t aTimeoutMilli)
|
|
{
|
|
OT_ASSERT(mCommandIsPending);
|
|
mTimer.Start(aTimeoutMilli);
|
|
}
|
|
|
|
otError Interpreter::ProcessCommand(Arg aArgs[])
|
|
{
|
|
#define CmdEntry(aCommandString) \
|
|
{ \
|
|
aCommandString, &Interpreter::Process<Cmd(aCommandString)> \
|
|
}
|
|
|
|
static constexpr Command kCommands[] = {
|
|
#if OPENTHREAD_FTD || OPENTHREAD_MTD
|
|
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
|
|
CmdEntry("ba"),
|
|
#endif
|
|
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
|
|
CmdEntry("bbr"),
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
|
|
CmdEntry("br"),
|
|
#endif
|
|
CmdEntry("bufferinfo"),
|
|
CmdEntry("ccathreshold"),
|
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
CmdEntry("ccm"),
|
|
#endif
|
|
CmdEntry("channel"),
|
|
#if OPENTHREAD_FTD
|
|
CmdEntry("child"),
|
|
CmdEntry("childip"),
|
|
CmdEntry("childmax"),
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
|
|
CmdEntry("childsupervision"),
|
|
#endif
|
|
CmdEntry("childtimeout"),
|
|
#if OPENTHREAD_CONFIG_COAP_API_ENABLE
|
|
CmdEntry("coap"),
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
|
|
CmdEntry("coaps"),
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
|
|
CmdEntry("coex"),
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
|
|
CmdEntry("commissioner"),
|
|
#endif
|
|
#if OPENTHREAD_FTD
|
|
CmdEntry("contextreusedelay"),
|
|
#endif
|
|
CmdEntry("counters"),
|
|
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
|
CmdEntry("csl"),
|
|
#endif
|
|
CmdEntry("dataset"),
|
|
#if OPENTHREAD_FTD
|
|
CmdEntry("delaytimermin"),
|
|
#endif
|
|
CmdEntry("detach"),
|
|
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
|
|
#if OPENTHREAD_CONFIG_DIAG_ENABLE
|
|
CmdEntry("diag"),
|
|
#endif
|
|
#if OPENTHREAD_FTD || OPENTHREAD_MTD
|
|
CmdEntry("discover"),
|
|
CmdEntry("dns"),
|
|
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
|
|
CmdEntry("domainname"),
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_DUA_ENABLE
|
|
CmdEntry("dua"),
|
|
#endif
|
|
#if OPENTHREAD_FTD
|
|
CmdEntry("eidcache"),
|
|
#endif
|
|
CmdEntry("eui64"),
|
|
CmdEntry("extaddr"),
|
|
CmdEntry("extpanid"),
|
|
CmdEntry("factoryreset"),
|
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
CmdEntry("fake"),
|
|
#endif
|
|
CmdEntry("fem"),
|
|
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
|
|
#if OPENTHREAD_FTD || OPENTHREAD_MTD
|
|
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
|
|
CmdEntry("history"),
|
|
#endif
|
|
CmdEntry("ifconfig"),
|
|
CmdEntry("ipaddr"),
|
|
CmdEntry("ipmaddr"),
|
|
#if OPENTHREAD_CONFIG_JOINER_ENABLE
|
|
CmdEntry("joiner"),
|
|
#endif
|
|
#if OPENTHREAD_FTD
|
|
CmdEntry("joinerport"),
|
|
#endif
|
|
CmdEntry("keysequence"),
|
|
CmdEntry("leaderdata"),
|
|
#if OPENTHREAD_FTD
|
|
CmdEntry("leaderweight"),
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
|
|
CmdEntry("linkmetrics"),
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
|
|
CmdEntry("locate"),
|
|
#endif
|
|
CmdEntry("log"),
|
|
CmdEntry("mac"),
|
|
#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
|
|
CmdEntry("macfilter"),
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
CmdEntry("mliid"),
|
|
#endif
|
|
#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
|
CmdEntry("mlr"),
|
|
#endif
|
|
CmdEntry("mode"),
|
|
CmdEntry("multiradio"),
|
|
#if OPENTHREAD_FTD
|
|
CmdEntry("neighbor"),
|
|
#endif
|
|
CmdEntry("netdata"),
|
|
CmdEntry("netstat"),
|
|
#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
|
|
CmdEntry("networkdiagnostic"),
|
|
#endif
|
|
#if OPENTHREAD_FTD
|
|
CmdEntry("networkidtimeout"),
|
|
#endif
|
|
CmdEntry("networkkey"),
|
|
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
|
|
CmdEntry("networkkeyref"),
|
|
#endif
|
|
CmdEntry("networkname"),
|
|
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
|
|
CmdEntry("networktime"),
|
|
#endif
|
|
CmdEntry("panid"),
|
|
CmdEntry("parent"),
|
|
#if OPENTHREAD_FTD
|
|
CmdEntry("parentpriority"),
|
|
CmdEntry("partitionid"),
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
|
|
CmdEntry("ping"),
|
|
#endif
|
|
CmdEntry("pollperiod"),
|
|
#if OPENTHREAD_FTD
|
|
CmdEntry("preferrouterid"),
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
|
|
CmdEntry("prefix"),
|
|
#endif
|
|
CmdEntry("promiscuous"),
|
|
#if OPENTHREAD_FTD
|
|
CmdEntry("pskc"),
|
|
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
|
|
CmdEntry("pskcref"),
|
|
#endif
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
|
|
CmdEntry("radiofilter"),
|
|
#endif
|
|
CmdEntry("rcp"),
|
|
CmdEntry("region"),
|
|
#if OPENTHREAD_FTD
|
|
CmdEntry("releaserouterid"),
|
|
#endif
|
|
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
|
|
CmdEntry("reset"),
|
|
#if OPENTHREAD_FTD || OPENTHREAD_MTD
|
|
CmdEntry("rloc16"),
|
|
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
|
|
CmdEntry("route"),
|
|
#endif
|
|
#if OPENTHREAD_FTD
|
|
CmdEntry("router"),
|
|
CmdEntry("routerdowngradethreshold"),
|
|
CmdEntry("routereligible"),
|
|
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
CmdEntry("routeridrange"),
|
|
#endif
|
|
CmdEntry("routerselectionjitter"),
|
|
CmdEntry("routerupgradethreshold"),
|
|
#endif
|
|
CmdEntry("scan"),
|
|
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
|
|
CmdEntry("service"),
|
|
#endif
|
|
CmdEntry("singleton"),
|
|
#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
|
|
CmdEntry("sntp"),
|
|
#endif
|
|
#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
|
|
CmdEntry("srp"),
|
|
#endif
|
|
CmdEntry("state"),
|
|
#if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
|
|
CmdEntry("tcp"),
|
|
#endif
|
|
CmdEntry("thread"),
|
|
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
|
|
CmdEntry("trel"),
|
|
#endif
|
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
|
CmdEntry("tvcheck"),
|
|
#endif
|
|
CmdEntry("txpower"),
|
|
CmdEntry("udp"),
|
|
CmdEntry("unsecureport"),
|
|
#if OPENTHREAD_CONFIG_UPTIME_ENABLE
|
|
CmdEntry("uptime"),
|
|
#endif
|
|
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
|
|
CmdEntry("version"),
|
|
};
|
|
|
|
#undef CmdEntry
|
|
|
|
static_assert(BinarySearch::IsSorted(kCommands), "Command Table is not sorted");
|
|
|
|
otError error = OT_ERROR_NONE;
|
|
const Command *command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
|
|
|
|
if (command != nullptr)
|
|
{
|
|
error = (this->*command->mHandler)(aArgs + 1);
|
|
}
|
|
else if (aArgs[0] == "help")
|
|
{
|
|
OutputCommandTable(kCommands);
|
|
|
|
for (uint8_t i = 0; i < mUserCommandsLength; i++)
|
|
{
|
|
OutputLine("%s", mUserCommands[i].mName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = ProcessUserCommands(aArgs);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
extern "C" void otCliInit(otInstance *aInstance, otCliOutputCallback aCallback, void *aContext)
|
|
{
|
|
Interpreter::Initialize(aInstance, aCallback, aContext);
|
|
}
|
|
|
|
extern "C" void otCliInputLine(char *aBuf)
|
|
{
|
|
Interpreter::GetInterpreter().ProcessLine(aBuf);
|
|
}
|
|
|
|
extern "C" void otCliSetUserCommands(const otCliCommand *aUserCommands, uint8_t aLength, void *aContext)
|
|
{
|
|
Interpreter::GetInterpreter().SetUserCommands(aUserCommands, aLength, aContext);
|
|
}
|
|
|
|
extern "C" void otCliOutputBytes(const uint8_t *aBytes, uint8_t aLength)
|
|
{
|
|
Interpreter::GetInterpreter().OutputBytes(aBytes, aLength);
|
|
}
|
|
|
|
extern "C" void otCliOutputFormat(const char *aFmt, ...)
|
|
{
|
|
va_list aAp;
|
|
va_start(aAp, aFmt);
|
|
Interpreter::GetInterpreter().OutputFormatV(aFmt, aAp);
|
|
va_end(aAp);
|
|
}
|
|
|
|
extern "C" void otCliAppendResult(otError aError)
|
|
{
|
|
Interpreter::GetInterpreter().OutputResult(aError);
|
|
}
|
|
|
|
extern "C" void otCliPlatLogv(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, va_list aArgs)
|
|
{
|
|
OT_UNUSED_VARIABLE(aLogLevel);
|
|
OT_UNUSED_VARIABLE(aLogRegion);
|
|
|
|
VerifyOrExit(Interpreter::IsInitialized());
|
|
|
|
// CLI output is being used for logging, so we set the flag
|
|
// `EmittingCommandOutput` to false indicate this.
|
|
Interpreter::GetInterpreter().SetEmittingCommandOutput(false);
|
|
Interpreter::GetInterpreter().OutputFormatV(aFormat, aArgs);
|
|
Interpreter::GetInterpreter().OutputLine("");
|
|
Interpreter::GetInterpreter().SetEmittingCommandOutput(true);
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
} // namespace Cli
|
|
} // namespace ot
|
|
|
|
#if OPENTHREAD_CONFIG_LEGACY_ENABLE
|
|
OT_TOOL_WEAK void otNcpRegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers)
|
|
{
|
|
OT_UNUSED_VARIABLE(aHandlers);
|
|
}
|
|
|
|
OT_TOOL_WEAK void otNcpHandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix)
|
|
{
|
|
OT_UNUSED_VARIABLE(aUlaPrefix);
|
|
}
|
|
|
|
OT_TOOL_WEAK void otNcpHandleLegacyNodeDidJoin(const otExtAddress *aExtAddr)
|
|
{
|
|
OT_UNUSED_VARIABLE(aExtAddr);
|
|
}
|
|
#endif
|