Files
openthread/src/cli/cli.cpp
T
Rongli Sun 77ea86302e [cli] add childip max command (#4759)
Thread 1.2 test harness requires the ability to set the number of IP
address the parent could store per child to test the reaction of the
Child DUT. The value intentionally may not conform to the Conformance
Specification. Since it is only used for reference device, this commit
wraps relevant APIs in the OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
macro.
2020-03-31 07:58:51 -07:00

4241 lines
126 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 "mac/channel_mask.hpp"
#include "utils/parse_cmdline.hpp"
#include <openthread/icmp6.h>
#include <openthread/link.h>
#include <openthread/ncp.h>
#include <openthread/netdata.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
#include <openthread/diag.h>
#include <openthread/icmp6.h>
#include <openthread/platform/uart.h>
#include "common/new.hpp"
#include "net/ip6.hpp"
#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
#endif
#include "cli_dataset.hpp"
#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
#include "cli_server.hpp"
#include "common/encoding.hpp"
#include "common/string.hpp"
using ot::Encoding::BigEndian::HostSwap16;
using ot::Encoding::BigEndian::HostSwap32;
#define INDENT_SIZE (4)
namespace ot {
namespace Cli {
const struct Command Interpreter::sCommands[] = {
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
{"bbr", &Interpreter::ProcessBackboneRouter},
#endif
{"bufferinfo", &Interpreter::ProcessBufferInfo},
{"channel", &Interpreter::ProcessChannel},
#if OPENTHREAD_FTD
{"child", &Interpreter::ProcessChild},
{"childip", &Interpreter::ProcessChildIp},
{"childmax", &Interpreter::ProcessChildMax},
#endif
{"childtimeout", &Interpreter::ProcessChildTimeout},
#if OPENTHREAD_CONFIG_COAP_API_ENABLE
{"coap", &Interpreter::ProcessCoap},
#endif
#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
{"coaps", &Interpreter::ProcessCoapSecure},
#endif
#if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
{"coex", &Interpreter::ProcessCoexMetrics},
#endif
#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
{"commissioner", &Interpreter::ProcessCommissioner},
#endif
#if OPENTHREAD_FTD
{"contextreusedelay", &Interpreter::ProcessContextIdReuseDelay},
#endif
{"counters", &Interpreter::ProcessCounters},
{"dataset", &Interpreter::ProcessDataset},
#if OPENTHREAD_FTD
{"delaytimermin", &Interpreter::ProcessDelayTimerMin},
#endif
#if OPENTHREAD_CONFIG_DIAG_ENABLE
{"diag", &Interpreter::ProcessDiag},
#endif
{"discover", &Interpreter::ProcessDiscover},
#if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
{"dns", &Interpreter::ProcessDns},
#endif
#if OPENTHREAD_FTD
{"eidcache", &Interpreter::ProcessEidCache},
#endif
{"eui64", &Interpreter::ProcessEui64},
#if OPENTHREAD_POSIX
{"exit", &Interpreter::ProcessExit},
#endif
#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX
{"logfilename", &Interpreter::ProcessLogFilename},
#endif
{"extaddr", &Interpreter::ProcessExtAddress},
{"extpanid", &Interpreter::ProcessExtPanId},
{"factoryreset", &Interpreter::ProcessFactoryReset},
{"help", &Interpreter::ProcessHelp},
{"ifconfig", &Interpreter::ProcessIfconfig},
{"ipaddr", &Interpreter::ProcessIpAddr},
{"ipmaddr", &Interpreter::ProcessIpMulticastAddr},
#if OPENTHREAD_CONFIG_JOINER_ENABLE
{"joiner", &Interpreter::ProcessJoiner},
#endif
#if OPENTHREAD_FTD
{"joinerport", &Interpreter::ProcessJoinerPort},
#endif
{"keysequence", &Interpreter::ProcessKeySequence},
{"leaderdata", &Interpreter::ProcessLeaderData},
#if OPENTHREAD_FTD
{"leaderpartitionid", &Interpreter::ProcessLeaderPartitionId},
{"leaderweight", &Interpreter::ProcessLeaderWeight},
#endif
{"mac", &Interpreter::ProcessMac},
#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
{"macfilter", &Interpreter::ProcessMacFilter},
#endif
{"masterkey", &Interpreter::ProcessMasterKey},
{"mode", &Interpreter::ProcessMode},
#if OPENTHREAD_FTD
{"neighbor", &Interpreter::ProcessNeighbor},
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
{"netdataregister", &Interpreter::ProcessNetworkDataRegister},
#endif
{"netdatashow", &Interpreter::ProcessNetworkDataShow},
#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
{"networkdiagnostic", &Interpreter::ProcessNetworkDiagnostic},
#endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
#if OPENTHREAD_FTD
{"networkidtimeout", &Interpreter::ProcessNetworkIdTimeout},
#endif
{"networkname", &Interpreter::ProcessNetworkName},
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
{"networktime", &Interpreter::ProcessNetworkTime},
#endif
{"panid", &Interpreter::ProcessPanId},
{"parent", &Interpreter::ProcessParent},
#if OPENTHREAD_FTD
{"parentpriority", &Interpreter::ProcessParentPriority},
#endif
{"ping", &Interpreter::ProcessPing},
{"pollperiod", &Interpreter::ProcessPollPeriod},
{"promiscuous", &Interpreter::ProcessPromiscuous},
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
{"prefix", &Interpreter::ProcessPrefix},
#endif
#if OPENTHREAD_FTD
{"preferrouterid", &Interpreter::ProcessPreferRouterId},
{"pskc", &Interpreter::ProcessPskc},
{"releaserouterid", &Interpreter::ProcessReleaseRouterId},
#endif
{"reset", &Interpreter::ProcessReset},
{"rloc16", &Interpreter::ProcessRloc16},
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
{"route", &Interpreter::ProcessRoute},
#endif
#if OPENTHREAD_FTD
{"router", &Interpreter::ProcessRouter},
{"routerdowngradethreshold", &Interpreter::ProcessRouterDowngradeThreshold},
{"routereligible", &Interpreter::ProcessRouterEligible},
{"routerselectionjitter", &Interpreter::ProcessRouterSelectionJitter},
{"routerupgradethreshold", &Interpreter::ProcessRouterUpgradeThreshold},
#endif
{"scan", &Interpreter::ProcessScan},
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
{"service", &Interpreter::ProcessService},
#endif
{"singleton", &Interpreter::ProcessSingleton},
#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
{"sntp", &Interpreter::ProcessSntp},
#endif
{"state", &Interpreter::ProcessState},
{"thread", &Interpreter::ProcessThread},
{"txpower", &Interpreter::ProcessTxPower},
{"udp", &Interpreter::ProcessUdp},
{"version", &Interpreter::ProcessVersion},
};
Interpreter::Interpreter(Instance *aInstance)
: mUserCommands(NULL)
, mUserCommandsLength(0)
, mServer(NULL)
, mPingLength(kDefaultPingLength)
, mPingCount(kDefaultPingCount)
, mPingInterval(kDefaultPingInterval)
, mPingHopLimit(0)
, mPingAllowZeroHopLimit(false)
, mPingIdentifier(0)
, mPingTimer(*aInstance, &Interpreter::HandlePingTimer, this)
#if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
, mResolvingInProgress(0)
#endif
#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
, mSntpQueryingInProgress(false)
#endif
, mUdp(*this)
, mDataset(*this)
#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
, mInstance(aInstance)
{
#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
otThreadSetReceiveDiagnosticGetCallback(mInstance, &Interpreter::HandleDiagnosticGetResponse, this);
#endif
mIcmpHandler.mReceiveCallback = Interpreter::HandleIcmpReceive;
mIcmpHandler.mContext = this;
otIcmp6RegisterHandler(mInstance, &mIcmpHandler);
#if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
memset(mResolvingHostname, 0, sizeof(mResolvingHostname));
#endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
}
int Interpreter::Hex2Bin(const char *aHex, uint8_t *aBin, uint16_t aBinLength, bool aAllowTruncate)
{
size_t hexLength = strlen(aHex);
const char *hexEnd = aHex + hexLength;
uint8_t * cur = aBin;
uint8_t numChars = hexLength & 1;
uint8_t byte = 0;
int len = 0;
int rval;
if (!aAllowTruncate)
{
VerifyOrExit((hexLength + 1) / 2 <= aBinLength, rval = -1);
}
while (aHex < hexEnd)
{
if ('A' <= *aHex && *aHex <= 'F')
{
byte |= 10 + (*aHex - 'A');
}
else if ('a' <= *aHex && *aHex <= 'f')
{
byte |= 10 + (*aHex - 'a');
}
else if ('0' <= *aHex && *aHex <= '9')
{
byte |= *aHex - '0';
}
else
{
ExitNow(rval = -1);
}
aHex++;
numChars++;
if (numChars >= 2)
{
numChars = 0;
*cur++ = byte;
byte = 0;
len++;
if (len == aBinLength)
{
ExitNow(rval = len);
}
}
else
{
byte <<= 4;
}
}
rval = len;
exit:
return rval;
}
void Interpreter::AppendResult(otError aError) const
{
if (aError == OT_ERROR_NONE)
{
mServer->OutputFormat("Done\r\n");
}
else
{
mServer->OutputFormat("Error %d: %s\r\n", aError, otThreadErrorToString(aError));
}
}
void Interpreter::OutputBytes(const uint8_t *aBytes, uint8_t aLength) const
{
for (int i = 0; i < aLength; i++)
{
mServer->OutputFormat("%02x", aBytes[i]);
}
}
void Interpreter::OutputIp6Address(const otIp6Address &aAddress) const
{
mServer->OutputFormat(
"%x:%x:%x:%x:%x:%x:%x:%x", HostSwap16(aAddress.mFields.m16[0]), HostSwap16(aAddress.mFields.m16[1]),
HostSwap16(aAddress.mFields.m16[2]), HostSwap16(aAddress.mFields.m16[3]), HostSwap16(aAddress.mFields.m16[4]),
HostSwap16(aAddress.mFields.m16[5]), HostSwap16(aAddress.mFields.m16[6]), HostSwap16(aAddress.mFields.m16[7]));
}
otError Interpreter::ParseLong(char *aString, long &aLong)
{
char *endptr;
aLong = strtol(aString, &endptr, 0);
return (*endptr == '\0') ? OT_ERROR_NONE : OT_ERROR_INVALID_ARGS;
}
otError Interpreter::ParseUnsignedLong(char *aString, unsigned long &aUnsignedLong)
{
char *endptr;
aUnsignedLong = strtoul(aString, &endptr, 0);
return (*endptr == '\0') ? OT_ERROR_NONE : OT_ERROR_INVALID_ARGS;
}
otError Interpreter::ParsePingInterval(const char *aString, uint32_t &aInterval)
{
otError error = OT_ERROR_NONE;
const uint32_t msFactor = 1000;
uint32_t factor = msFactor;
aInterval = 0;
while (*aString)
{
if ('0' <= *aString && *aString <= '9')
{
// In the case of seconds, change the base of already calculated value.
if (factor == msFactor)
{
aInterval *= 10;
}
aInterval += static_cast<uint32_t>(*aString - '0') * factor;
// In the case of milliseconds, change the multiplier factor.
if (factor != msFactor)
{
factor /= 10;
}
}
else if (*aString == '.')
{
// 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);
}
aString++;
}
exit:
return error;
}
void Interpreter::ProcessHelp(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgsLength);
OT_UNUSED_VARIABLE(aArgs);
for (unsigned int i = 0; i < OT_ARRAY_LENGTH(sCommands); i++)
{
mServer->OutputFormat("%s\r\n", sCommands[i].mName);
}
for (uint8_t i = 0; i < mUserCommandsLength; i++)
{
mServer->OutputFormat("%s\r\n", mUserCommands[i].mName);
}
AppendResult(OT_ERROR_NONE);
}
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
void Interpreter::ProcessBackboneRouter(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgs);
otError error = OT_ERROR_INVALID_COMMAND;
otBackboneRouterConfig config;
if (aArgsLength == 0)
{
if (otBackboneRouterGetPrimary(mInstance, &config) == OT_ERROR_NONE)
{
mServer->OutputFormat("BBR Primary:\r\n");
mServer->OutputFormat("server16: 0x%04X\r\n", config.mServer16);
mServer->OutputFormat("seqno: %d\r\n", config.mSequenceNumber);
mServer->OutputFormat("delay: %d secs\r\n", config.mReregistrationDelay);
mServer->OutputFormat("timeout: %d secs\r\n", config.mMlrTimeout);
}
else
{
mServer->OutputFormat("BBR Primary: None\r\n");
}
error = OT_ERROR_NONE;
}
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
else
{
error = ProcessBackboneRouterLocal(aArgsLength, aArgs);
}
#endif
AppendResult(error);
}
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
otError Interpreter::ProcessBackboneRouterLocal(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
;
otBackboneRouterConfig config;
unsigned long value;
if (strcmp(aArgs[0], "disable") == 0)
{
otBackboneRouterSetEnabled(mInstance, false);
}
else if (strcmp(aArgs[0], "enable") == 0)
{
otBackboneRouterSetEnabled(mInstance, true);
}
else if (strcmp(aArgs[0], "jitter") == 0)
{
if (aArgsLength == 1)
{
mServer->OutputFormat("%d\r\n", otBackboneRouterGetRegistrationJitter(mInstance));
}
else if (aArgsLength == 2)
{
SuccessOrExit(error = ParseUnsignedLong(aArgs[1], value));
otBackboneRouterSetRegistrationJitter(mInstance, static_cast<uint8_t>(value));
}
}
else if (strcmp(aArgs[0], "register") == 0)
{
SuccessOrExit(error = otBackboneRouterRegister(mInstance));
}
else if (strcmp(aArgs[0], "state") == 0)
{
switch (otBackboneRouterGetState(mInstance))
{
case OT_BACKBONE_ROUTER_STATE_DISABLED:
mServer->OutputFormat("Disabled\r\n");
break;
case OT_BACKBONE_ROUTER_STATE_SECONDARY:
mServer->OutputFormat("Secondary\r\n");
break;
case OT_BACKBONE_ROUTER_STATE_PRIMARY:
mServer->OutputFormat("Primary\r\n");
break;
}
}
else if (strcmp(aArgs[0], "config") == 0)
{
otBackboneRouterGetConfig(mInstance, &config);
if (aArgsLength == 1)
{
mServer->OutputFormat("seqno: %d\r\n", config.mSequenceNumber);
mServer->OutputFormat("delay: %d secs\r\n", config.mReregistrationDelay);
mServer->OutputFormat("timeout: %d secs\r\n", config.mMlrTimeout);
}
else
{
// Set local Backbone Router configuration.
for (int argCur = 1; argCur < aArgsLength; argCur++)
{
VerifyOrExit(argCur + 1 < aArgsLength, error = OT_ERROR_INVALID_ARGS);
if (strcmp(aArgs[argCur], "seqno") == 0)
{
SuccessOrExit(error = ParseUnsignedLong(aArgs[++argCur], value));
config.mSequenceNumber = static_cast<uint8_t>(value);
}
else if (strcmp(aArgs[argCur], "delay") == 0)
{
SuccessOrExit(error = ParseUnsignedLong(aArgs[++argCur], value));
config.mReregistrationDelay = static_cast<uint16_t>(value);
}
else if (strcmp(aArgs[argCur], "timeout") == 0)
{
SuccessOrExit(error = ParseUnsignedLong(aArgs[++argCur], value));
config.mMlrTimeout = static_cast<uint32_t>(value);
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
otBackboneRouterSetConfig(mInstance, &config);
}
}
else
{
error = OT_ERROR_INVALID_COMMAND;
}
exit:
return error;
}
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
void Interpreter::ProcessBufferInfo(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgsLength);
OT_UNUSED_VARIABLE(aArgs);
otBufferInfo bufferInfo;
otMessageGetBufferInfo(mInstance, &bufferInfo);
mServer->OutputFormat("total: %d\r\n", bufferInfo.mTotalBuffers);
mServer->OutputFormat("free: %d\r\n", bufferInfo.mFreeBuffers);
mServer->OutputFormat("6lo send: %d %d\r\n", bufferInfo.m6loSendMessages, bufferInfo.m6loSendBuffers);
mServer->OutputFormat("6lo reas: %d %d\r\n", bufferInfo.m6loReassemblyMessages, bufferInfo.m6loReassemblyBuffers);
mServer->OutputFormat("ip6: %d %d\r\n", bufferInfo.mIp6Messages, bufferInfo.mIp6Buffers);
mServer->OutputFormat("mpl: %d %d\r\n", bufferInfo.mMplMessages, bufferInfo.mMplBuffers);
mServer->OutputFormat("mle: %d %d\r\n", bufferInfo.mMleMessages, bufferInfo.mMleBuffers);
mServer->OutputFormat("arp: %d %d\r\n", bufferInfo.mArpMessages, bufferInfo.mArpBuffers);
mServer->OutputFormat("coap: %d %d\r\n", bufferInfo.mCoapMessages, bufferInfo.mCoapBuffers);
mServer->OutputFormat("coap secure: %d %d\r\n", bufferInfo.mCoapSecureMessages, bufferInfo.mCoapSecureBuffers);
mServer->OutputFormat("application coap: %d %d\r\n", bufferInfo.mApplicationCoapMessages,
bufferInfo.mApplicationCoapBuffers);
AppendResult(OT_ERROR_NONE);
}
void Interpreter::ProcessChannel(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
long value;
if (aArgsLength == 0)
{
mServer->OutputFormat("%d\r\n", otLinkGetChannel(mInstance));
}
else if (strcmp(aArgs[0], "supported") == 0)
{
mServer->OutputFormat("0x%x\r\n", otPlatRadioGetSupportedChannelMask(mInstance));
}
else if (strcmp(aArgs[0], "preferred") == 0)
{
mServer->OutputFormat("0x%x\r\n", otPlatRadioGetPreferredChannelMask(mInstance));
}
#if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
else if (strcmp(aArgs[0], "monitor") == 0)
{
if (aArgsLength == 1)
{
mServer->OutputFormat("enabled: %d\r\n", otChannelMonitorIsEnabled(mInstance));
if (otChannelMonitorIsEnabled(mInstance))
{
uint32_t channelMask = otLinkGetSupportedChannelMask(mInstance);
uint8_t channelNum = sizeof(channelMask) * CHAR_BIT;
mServer->OutputFormat("interval: %u\r\n", otChannelMonitorGetSampleInterval(mInstance));
mServer->OutputFormat("threshold: %d\r\n", otChannelMonitorGetRssiThreshold(mInstance));
mServer->OutputFormat("window: %u\r\n", otChannelMonitorGetSampleWindow(mInstance));
mServer->OutputFormat("count: %u\r\n", otChannelMonitorGetSampleCount(mInstance));
mServer->OutputFormat("occupancies:\r\n");
for (uint8_t channel = 0; channel < channelNum; channel++)
{
uint32_t occupancy = 0;
if (!((1UL << channel) & channelMask))
{
continue;
}
occupancy = otChannelMonitorGetChannelOccupancy(mInstance, channel);
mServer->OutputFormat("ch %d (0x%04x) ", channel, occupancy);
occupancy = (occupancy * 10000) / 0xffff;
mServer->OutputFormat("%2d.%02d%% busy\r\n", occupancy / 100, occupancy % 100);
}
mServer->OutputFormat("\r\n");
}
}
else if (strcmp(aArgs[1], "start") == 0)
{
error = otChannelMonitorSetEnabled(mInstance, true);
}
else if (strcmp(aArgs[1], "stop") == 0)
{
error = otChannelMonitorSetEnabled(mInstance, false);
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
#endif
#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
else if (strcmp(aArgs[0], "manager") == 0)
{
if (aArgsLength == 1)
{
mServer->OutputFormat("channel: %d\r\n", otChannelManagerGetRequestedChannel(mInstance));
mServer->OutputFormat("auto: %d\r\n", otChannelManagerGetAutoChannelSelectionEnabled(mInstance));
if (otChannelManagerGetAutoChannelSelectionEnabled(mInstance))
{
Mac::ChannelMask supportedMask(otChannelManagerGetSupportedChannels(mInstance));
Mac::ChannelMask favoredMask(otChannelManagerGetFavoredChannels(mInstance));
mServer->OutputFormat("delay: %d\r\n", otChannelManagerGetDelay(mInstance));
mServer->OutputFormat("interval: %lu\r\n", otChannelManagerGetAutoChannelSelectionInterval(mInstance));
mServer->OutputFormat("supported: %s\r\n", supportedMask.ToString().AsCString());
mServer->OutputFormat("favored: %s\r\n", supportedMask.ToString().AsCString());
}
}
else if (strcmp(aArgs[1], "change") == 0)
{
VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseLong(aArgs[2], value));
otChannelManagerRequestChannelChange(mInstance, static_cast<uint8_t>(value));
}
#if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
else if (strcmp(aArgs[1], "select") == 0)
{
VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseLong(aArgs[2], value));
error = otChannelManagerRequestChannelSelect(mInstance, (value != 0) ? true : false);
}
#endif
else if (strcmp(aArgs[1], "auto") == 0)
{
VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseLong(aArgs[2], value));
otChannelManagerSetAutoChannelSelectionEnabled(mInstance, (value != 0) ? true : false);
}
else if (strcmp(aArgs[1], "delay") == 0)
{
VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseLong(aArgs[2], value));
error = otChannelManagerSetDelay(mInstance, static_cast<uint8_t>(value));
}
else if (strcmp(aArgs[1], "interval") == 0)
{
VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseLong(aArgs[2], value));
error = otChannelManagerSetAutoChannelSelectionInterval(mInstance, static_cast<uint32_t>(value));
}
else if (strcmp(aArgs[1], "supported") == 0)
{
VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseLong(aArgs[2], value));
otChannelManagerSetSupportedChannels(mInstance, static_cast<uint32_t>(value));
}
else if (strcmp(aArgs[1], "favored") == 0)
{
VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseLong(aArgs[2], value));
otChannelManagerSetFavoredChannels(mInstance, static_cast<uint32_t>(value));
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
#endif
else
{
SuccessOrExit(error = ParseLong(aArgs[0], value));
error = otLinkSetChannel(mInstance, static_cast<uint8_t>(value));
}
exit:
AppendResult(error);
}
#if OPENTHREAD_FTD
void Interpreter::ProcessChild(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
otChildInfo childInfo;
long value;
bool isTable;
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
isTable = (strcmp(aArgs[0], "table") == 0);
if (isTable || strcmp(aArgs[0], "list") == 0)
{
uint16_t maxChildren;
if (isTable)
{
mServer->OutputFormat(
"| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|S|D|N| Extended MAC |\r\n");
mServer->OutputFormat(
"+-----+--------+------------+------------+-------+------+-+-+-+-+------------------+\r\n");
}
maxChildren = otThreadGetMaxAllowedChildren(mInstance);
for (uint16_t i = 0; i < maxChildren; i++)
{
if ((otThreadGetChildInfoByIndex(mInstance, i, &childInfo) != OT_ERROR_NONE) || childInfo.mIsStateRestoring)
{
continue;
}
if (isTable)
{
mServer->OutputFormat("| %3d ", childInfo.mChildId);
mServer->OutputFormat("| 0x%04x ", childInfo.mRloc16);
mServer->OutputFormat("| %10d ", childInfo.mTimeout);
mServer->OutputFormat("| %10d ", childInfo.mAge);
mServer->OutputFormat("| %5d ", childInfo.mLinkQualityIn);
mServer->OutputFormat("| %4d ", childInfo.mNetworkDataVersion);
mServer->OutputFormat("|%1d", childInfo.mRxOnWhenIdle);
mServer->OutputFormat("|%1d", childInfo.mSecureDataRequest);
mServer->OutputFormat("|%1d", childInfo.mFullThreadDevice);
mServer->OutputFormat("|%1d", childInfo.mFullNetworkData);
mServer->OutputFormat("| ");
for (size_t j = 0; j < sizeof(childInfo.mExtAddress); j++)
{
mServer->OutputFormat("%02x", childInfo.mExtAddress.m8[j]);
}
mServer->OutputFormat(" |\r\n");
}
else
{
mServer->OutputFormat("%d ", childInfo.mChildId);
}
}
mServer->OutputFormat("\r\n");
ExitNow();
}
SuccessOrExit(error = ParseLong(aArgs[0], value));
SuccessOrExit(error = otThreadGetChildInfoById(mInstance, static_cast<uint16_t>(value), &childInfo));
mServer->OutputFormat("Child ID: %d\r\n", childInfo.mChildId);
mServer->OutputFormat("Rloc: %04x\r\n", childInfo.mRloc16);
mServer->OutputFormat("Ext Addr: ");
for (size_t j = 0; j < sizeof(childInfo.mExtAddress); j++)
{
mServer->OutputFormat("%02x", childInfo.mExtAddress.m8[j]);
}
mServer->OutputFormat("\r\n");
mServer->OutputFormat("Mode: ");
if (childInfo.mRxOnWhenIdle)
{
mServer->OutputFormat("r");
}
if (childInfo.mSecureDataRequest)
{
mServer->OutputFormat("s");
}
if (childInfo.mFullThreadDevice)
{
mServer->OutputFormat("d");
}
if (childInfo.mFullNetworkData)
{
mServer->OutputFormat("n");
}
mServer->OutputFormat("\r\n");
mServer->OutputFormat("Net Data: %d\r\n", childInfo.mNetworkDataVersion);
mServer->OutputFormat("Timeout: %d\r\n", childInfo.mTimeout);
mServer->OutputFormat("Age: %d\r\n", childInfo.mAge);
mServer->OutputFormat("Link Quality In: %d\r\n", childInfo.mLinkQualityIn);
mServer->OutputFormat("RSSI: %d\r\n", childInfo.mAverageRssi);
exit:
AppendResult(error);
}
void Interpreter::ProcessChildIp(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgs);
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
uint16_t maxChildren = otThreadGetMaxAllowedChildren(mInstance);
for (uint16_t childIndex = 0; childIndex < maxChildren; childIndex++)
{
otChildIp6AddressIterator iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT;
otIp6Address ip6Address;
otChildInfo childInfo;
if ((otThreadGetChildInfoByIndex(mInstance, childIndex, &childInfo) != OT_ERROR_NONE) ||
childInfo.mIsStateRestoring)
{
continue;
}
iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT;
while (otThreadGetChildNextIp6Address(mInstance, childIndex, &iterator, &ip6Address) == OT_ERROR_NONE)
{
mServer->OutputFormat("%04x: ", childInfo.mRloc16);
OutputIp6Address(ip6Address);
mServer->OutputFormat("\r\n");
}
}
}
else if (strcmp(aArgs[0], "max") == 0)
{
if (aArgsLength == 1)
{
mServer->OutputFormat("%d\r\n", otThreadGetMaxChildIpAddresses(mInstance));
}
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
else if (aArgsLength == 2)
{
unsigned long value;
SuccessOrExit(error = ParseUnsignedLong(aArgs[1], value));
SuccessOrExit(error = otThreadSetMaxChildIpAddresses(mInstance, static_cast<uint8_t>(value)));
}
#endif
else
{
error = OT_ERROR_INVALID_ARGS;
}
}
else
{
error = OT_ERROR_INVALID_COMMAND;
}
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
exit:
#endif
AppendResult(error);
}
void Interpreter::ProcessChildMax(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
long value;
if (aArgsLength == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetMaxAllowedChildren(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(aArgs[0], value));
SuccessOrExit(error = otThreadSetMaxAllowedChildren(mInstance, static_cast<uint16_t>(value)));
}
exit:
AppendResult(error);
}
#endif // OPENTHREAD_FTD
void Interpreter::ProcessChildTimeout(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
long value;
if (aArgsLength == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetChildTimeout(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(aArgs[0], value));
otThreadSetChildTimeout(mInstance, static_cast<uint32_t>(value));
}
exit:
AppendResult(error);
}
#if OPENTHREAD_CONFIG_COAP_API_ENABLE
void Interpreter::ProcessCoap(uint8_t aArgsLength, char *aArgs[])
{
otError error;
error = mCoap.Process(aArgsLength, aArgs);
AppendResult(error);
}
#endif // OPENTHREAD_CONFIG_COAP_API_ENABLE
#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
void Interpreter::ProcessCoapSecure(uint8_t aArgsLength, char *aArgs[])
{
otError error;
error = mCoapSecure.Process(aArgsLength, aArgs);
AppendResult(error);
}
#endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
#if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
void Interpreter::ProcessCoexMetrics(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
mServer->OutputFormat("%s\r\n", otPlatRadioIsCoexEnabled(mInstance) ? "Enabled" : "Disabled");
}
else if (strcmp(aArgs[0], "enable") == 0)
{
error = otPlatRadioSetCoexEnabled(mInstance, true);
}
else if (strcmp(aArgs[0], "disable") == 0)
{
error = otPlatRadioSetCoexEnabled(mInstance, false);
}
else if (strcmp(aArgs[0], "metrics") == 0)
{
otRadioCoexMetrics metrics;
SuccessOrExit(error = otPlatRadioGetCoexMetrics(mInstance, &metrics));
mServer->OutputFormat("Stopped: %s\r\n", metrics.mStopped ? "true" : "false");
mServer->OutputFormat("Grant Glitch: %u\r\n", metrics.mNumGrantGlitch);
mServer->OutputFormat("Transmit metrics\r\n");
mServer->OutputFormat(" Request: %u\r\n", metrics.mNumTxRequest);
mServer->OutputFormat(" Grant Immediate: %u\r\n", metrics.mNumTxGrantImmediate);
mServer->OutputFormat(" Grant Wait: %u\r\n", metrics.mNumTxGrantWait);
mServer->OutputFormat(" Grant Wait Activated: %u\r\n", metrics.mNumTxGrantWaitActivated);
mServer->OutputFormat(" Grant Wait Timeout: %u\r\n", metrics.mNumTxGrantWaitTimeout);
mServer->OutputFormat(" Grant Deactivated During Request: %u\r\n",
metrics.mNumTxGrantDeactivatedDuringRequest);
mServer->OutputFormat(" Delayed Grant: %u\r\n", metrics.mNumTxDelayedGrant);
mServer->OutputFormat(" Average Request To Grant Time: %u\r\n", metrics.mAvgTxRequestToGrantTime);
mServer->OutputFormat("Receive metrics\r\n");
mServer->OutputFormat(" Request: %u\r\n", metrics.mNumRxRequest);
mServer->OutputFormat(" Grant Immediate: %u\r\n", metrics.mNumRxGrantImmediate);
mServer->OutputFormat(" Grant Wait: %u\r\n", metrics.mNumRxGrantWait);
mServer->OutputFormat(" Grant Wait Activated: %u\r\n", metrics.mNumRxGrantWaitActivated);
mServer->OutputFormat(" Grant Wait Timeout: %u\r\n", metrics.mNumRxGrantWaitTimeout);
mServer->OutputFormat(" Grant Deactivated During Request: %u\r\n",
metrics.mNumRxGrantDeactivatedDuringRequest);
mServer->OutputFormat(" Delayed Grant: %u\r\n", metrics.mNumRxDelayedGrant);
mServer->OutputFormat(" Average Request To Grant Time: %u\r\n", metrics.mAvgRxRequestToGrantTime);
mServer->OutputFormat(" Grant None: %u\r\n", metrics.mNumRxGrantNone);
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
exit:
AppendResult(error);
}
#endif // OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
#if OPENTHREAD_FTD
void Interpreter::ProcessContextIdReuseDelay(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
long value;
if (aArgsLength == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetContextIdReuseDelay(mInstance));
}
else
{
SuccessOrExit(ParseLong(aArgs[0], value));
otThreadSetContextIdReuseDelay(mInstance, static_cast<uint32_t>(value));
}
exit:
AppendResult(error);
}
#endif // OPENTHREAD_FTD
void Interpreter::ProcessCounters(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
mServer->OutputFormat("mac\r\n");
mServer->OutputFormat("mle\r\n");
}
else if (strcmp(aArgs[0], "mac") == 0)
{
if (aArgsLength == 1)
{
const otMacCounters *macCounters = otLinkGetCounters(mInstance);
mServer->OutputFormat("TxTotal: %d\r\n", macCounters->mTxTotal);
mServer->OutputFormat(" TxUnicast: %d\r\n", macCounters->mTxUnicast);
mServer->OutputFormat(" TxBroadcast: %d\r\n", macCounters->mTxBroadcast);
mServer->OutputFormat(" TxAckRequested: %d\r\n", macCounters->mTxAckRequested);
mServer->OutputFormat(" TxAcked: %d\r\n", macCounters->mTxAcked);
mServer->OutputFormat(" TxNoAckRequested: %d\r\n", macCounters->mTxNoAckRequested);
mServer->OutputFormat(" TxData: %d\r\n", macCounters->mTxData);
mServer->OutputFormat(" TxDataPoll: %d\r\n", macCounters->mTxDataPoll);
mServer->OutputFormat(" TxBeacon: %d\r\n", macCounters->mTxBeacon);
mServer->OutputFormat(" TxBeaconRequest: %d\r\n", macCounters->mTxBeaconRequest);
mServer->OutputFormat(" TxOther: %d\r\n", macCounters->mTxOther);
mServer->OutputFormat(" TxRetry: %d\r\n", macCounters->mTxRetry);
mServer->OutputFormat(" TxErrCca: %d\r\n", macCounters->mTxErrCca);
mServer->OutputFormat(" TxErrBusyChannel: %d\r\n", macCounters->mTxErrBusyChannel);
mServer->OutputFormat("RxTotal: %d\r\n", macCounters->mRxTotal);
mServer->OutputFormat(" RxUnicast: %d\r\n", macCounters->mRxUnicast);
mServer->OutputFormat(" RxBroadcast: %d\r\n", macCounters->mRxBroadcast);
mServer->OutputFormat(" RxData: %d\r\n", macCounters->mRxData);
mServer->OutputFormat(" RxDataPoll: %d\r\n", macCounters->mRxDataPoll);
mServer->OutputFormat(" RxBeacon: %d\r\n", macCounters->mRxBeacon);
mServer->OutputFormat(" RxBeaconRequest: %d\r\n", macCounters->mRxBeaconRequest);
mServer->OutputFormat(" RxOther: %d\r\n", macCounters->mRxOther);
mServer->OutputFormat(" RxAddressFiltered: %d\r\n", macCounters->mRxAddressFiltered);
mServer->OutputFormat(" RxDestAddrFiltered: %d\r\n", macCounters->mRxDestAddrFiltered);
mServer->OutputFormat(" RxDuplicated: %d\r\n", macCounters->mRxDuplicated);
mServer->OutputFormat(" RxErrNoFrame: %d\r\n", macCounters->mRxErrNoFrame);
mServer->OutputFormat(" RxErrNoUnknownNeighbor: %d\r\n", macCounters->mRxErrUnknownNeighbor);
mServer->OutputFormat(" RxErrInvalidSrcAddr: %d\r\n", macCounters->mRxErrInvalidSrcAddr);
mServer->OutputFormat(" RxErrSec: %d\r\n", macCounters->mRxErrSec);
mServer->OutputFormat(" RxErrFcs: %d\r\n", macCounters->mRxErrFcs);
mServer->OutputFormat(" RxErrOther: %d\r\n", macCounters->mRxErrOther);
}
else if ((aArgsLength == 2) && (strcmp(aArgs[0], "reset") == 0))
{
otLinkResetCounters(mInstance);
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
else if (strcmp(aArgs[0], "mle") == 0)
{
if (aArgsLength == 1)
{
const otMleCounters *mleCounters = otThreadGetMleCounters(mInstance);
mServer->OutputFormat("Role Disabled: %d\r\n", mleCounters->mDisabledRole);
mServer->OutputFormat("Role Detached: %d\r\n", mleCounters->mDetachedRole);
mServer->OutputFormat("Role Child: %d\r\n", mleCounters->mChildRole);
mServer->OutputFormat("Role Router: %d\r\n", mleCounters->mRouterRole);
mServer->OutputFormat("Role Leader: %d\r\n", mleCounters->mLeaderRole);
mServer->OutputFormat("Attach Attempts: %d\r\n", mleCounters->mAttachAttempts);
mServer->OutputFormat("Partition Id Changes: %d\r\n", mleCounters->mPartitionIdChanges);
mServer->OutputFormat("Better Partition Attach Attempts: %d\r\n",
mleCounters->mBetterPartitionAttachAttempts);
mServer->OutputFormat("Parent Changes: %d\r\n", mleCounters->mParentChanges);
}
else if ((aArgsLength == 2) && (strcmp(aArgs[0], "reset") == 0))
{
otThreadResetMleCounters(mInstance);
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
exit:
AppendResult(error);
}
#if OPENTHREAD_FTD
void Interpreter::ProcessDelayTimerMin(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
mServer->OutputFormat("%d\r\n", (otDatasetGetDelayTimerMinimal(mInstance) / 1000));
}
else if (aArgsLength == 1)
{
unsigned long value;
SuccessOrExit(error = ParseUnsignedLong(aArgs[0], value));
SuccessOrExit(error = otDatasetSetDelayTimerMinimal(mInstance, static_cast<uint32_t>(value * 1000)));
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
exit:
AppendResult(error);
}
#endif
void Interpreter::ProcessDiscover(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
uint32_t scanChannels = 0;
long value;
if (aArgsLength > 0)
{
SuccessOrExit(error = ParseLong(aArgs[0], value));
VerifyOrExit((0 <= value) && (value < static_cast<long>(sizeof(scanChannels) * CHAR_BIT)),
error = OT_ERROR_INVALID_ARGS);
scanChannels = 1 << value;
}
SuccessOrExit(error = otThreadDiscover(mInstance, scanChannels, OT_PANID_BROADCAST, false, false,
&Interpreter::HandleActiveScanResult, this));
mServer->OutputFormat("| J | Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI |\r\n");
mServer->OutputFormat("+---+------------------+------------------+------+------------------+----+-----+-----+\r\n");
exit:
if (error != OT_ERROR_NONE)
{
AppendResult(error);
}
}
#if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
void Interpreter::ProcessDns(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
long port = OT_DNS_DEFAULT_SERVER_PORT;
Ip6::MessageInfo messageInfo;
otDnsQuery query;
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
if (strcmp(aArgs[0], "resolve") == 0)
{
VerifyOrExit(!mResolvingInProgress, error = OT_ERROR_BUSY);
VerifyOrExit(aArgsLength > 1, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(strlen(aArgs[1]) < OT_DNS_MAX_HOSTNAME_LENGTH, error = OT_ERROR_INVALID_ARGS);
strcpy(mResolvingHostname, aArgs[1]);
if (aArgsLength > 2)
{
SuccessOrExit(error = messageInfo.GetPeerAddr().FromString(aArgs[2]));
}
else
{
// Use IPv6 address of default DNS server.
SuccessOrExit(error = messageInfo.GetPeerAddr().FromString(OT_DNS_DEFAULT_SERVER_IP));
}
if (aArgsLength > 3)
{
SuccessOrExit(error = ParseLong(aArgs[3], port));
}
messageInfo.SetPeerPort(static_cast<uint16_t>(port));
query.mHostname = mResolvingHostname;
query.mMessageInfo = static_cast<const otMessageInfo *>(&messageInfo);
query.mNoRecursion = false;
SuccessOrExit(error = otDnsClientQuery(mInstance, &query, &Interpreter::HandleDnsResponse, this));
mResolvingInProgress = true;
}
else
{
ExitNow(error = OT_ERROR_INVALID_COMMAND);
}
exit:
if (error != OT_ERROR_NONE)
{
AppendResult(error);
}
}
void Interpreter::HandleDnsResponse(void * aContext,
const char * aHostname,
const otIp6Address *aAddress,
uint32_t aTtl,
otError aResult)
{
static_cast<Interpreter *>(aContext)->HandleDnsResponse(aHostname, static_cast<const Ip6::Address *>(aAddress),
aTtl, aResult);
}
void Interpreter::HandleDnsResponse(const char *aHostname, const Ip6::Address *aAddress, uint32_t aTtl, otError aResult)
{
mServer->OutputFormat("DNS response for %s - ", aHostname);
if (aResult == OT_ERROR_NONE)
{
if (aAddress != NULL)
{
OutputIp6Address(*aAddress);
}
mServer->OutputFormat(" TTL: %d\r\n", aTtl);
}
AppendResult(aResult);
mResolvingInProgress = false;
}
#endif
#if OPENTHREAD_FTD
void Interpreter::ProcessEidCache(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgsLength);
OT_UNUSED_VARIABLE(aArgs);
otEidCacheEntry entry;
for (uint8_t i = 0;; i++)
{
SuccessOrExit(otThreadGetEidCacheEntry(mInstance, i, &entry));
if (!entry.mValid)
{
continue;
}
OutputIp6Address(entry.mTarget);
mServer->OutputFormat(" %04x\r\n", entry.mRloc16);
}
exit:
AppendResult(OT_ERROR_NONE);
}
#endif // OPENTHREAD_FTD
void Interpreter::ProcessEui64(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgs);
otError error = OT_ERROR_NONE;
otExtAddress extAddress;
VerifyOrExit(aArgsLength == 0, error = OT_ERROR_INVALID_ARGS);
otLinkGetFactoryAssignedIeeeEui64(mInstance, &extAddress);
OutputBytes(extAddress.m8, OT_EXT_ADDRESS_SIZE);
mServer->OutputFormat("\r\n");
exit:
AppendResult(error);
}
void Interpreter::ProcessExtAddress(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
const uint8_t *extAddress = reinterpret_cast<const uint8_t *>(otLinkGetExtendedAddress(mInstance));
OutputBytes(extAddress, OT_EXT_ADDRESS_SIZE);
mServer->OutputFormat("\r\n");
}
else
{
otExtAddress extAddress;
VerifyOrExit(Hex2Bin(aArgs[0], extAddress.m8, sizeof(otExtAddress)) >= 0, error = OT_ERROR_INVALID_ARGS);
error = otLinkSetExtendedAddress(mInstance, &extAddress);
}
exit:
AppendResult(error);
}
#if OPENTHREAD_POSIX
void Interpreter::ProcessExit(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgsLength);
OT_UNUSED_VARIABLE(aArgs);
exit(EXIT_SUCCESS);
}
#endif
#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX
void Interpreter::ProcessLogFilename(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = otPlatDebugUart_logfile(aArgs[0]));
exit:
AppendResult(error);
}
#endif
void Interpreter::ProcessExtPanId(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
const uint8_t *extPanId = reinterpret_cast<const uint8_t *>(otThreadGetExtendedPanId(mInstance));
OutputBytes(extPanId, OT_EXT_PAN_ID_SIZE);
mServer->OutputFormat("\r\n");
}
else
{
otExtendedPanId extPanId;
VerifyOrExit(Hex2Bin(aArgs[0], extPanId.m8, sizeof(extPanId)) >= 0, error = OT_ERROR_INVALID_ARGS);
error = otThreadSetExtendedPanId(mInstance, &extPanId);
}
exit:
AppendResult(error);
}
void Interpreter::ProcessFactoryReset(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgsLength);
OT_UNUSED_VARIABLE(aArgs);
otInstanceFactoryReset(mInstance);
}
void Interpreter::ProcessIfconfig(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
if (otIp6IsEnabled(mInstance))
{
mServer->OutputFormat("up\r\n");
}
else
{
mServer->OutputFormat("down\r\n");
}
}
else if (strcmp(aArgs[0], "up") == 0)
{
SuccessOrExit(error = otIp6SetEnabled(mInstance, true));
}
else if (strcmp(aArgs[0], "down") == 0)
{
SuccessOrExit(error = otIp6SetEnabled(mInstance, false));
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
exit:
AppendResult(error);
}
otError Interpreter::ProcessIpAddrAdd(uint8_t aArgsLength, char *aArgs[])
{
otError error;
otNetifAddress aAddress;
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = otIp6AddressFromString(aArgs[0], &aAddress.mAddress));
aAddress.mPrefixLength = 64;
aAddress.mPreferred = true;
aAddress.mValid = true;
error = otIp6AddUnicastAddress(mInstance, &aAddress);
exit:
return error;
}
otError Interpreter::ProcessIpAddrDel(uint8_t aArgsLength, char *aArgs[])
{
otError error;
struct otIp6Address address;
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = otIp6AddressFromString(aArgs[0], &address));
error = otIp6RemoveUnicastAddress(mInstance, &address);
exit:
return error;
}
void Interpreter::ProcessIpAddr(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
const otNetifAddress *unicastAddrs = otIp6GetUnicastAddresses(mInstance);
for (const otNetifAddress *addr = unicastAddrs; addr; addr = addr->mNext)
{
OutputIp6Address(addr->mAddress);
mServer->OutputFormat("\r\n");
}
}
else
{
if (strcmp(aArgs[0], "add") == 0)
{
SuccessOrExit(error = ProcessIpAddrAdd(aArgsLength - 1, aArgs + 1));
}
else if (strcmp(aArgs[0], "del") == 0)
{
SuccessOrExit(error = ProcessIpAddrDel(aArgsLength - 1, aArgs + 1));
}
else if (strcmp(aArgs[0], "linklocal") == 0)
{
OutputIp6Address(*otThreadGetLinkLocalIp6Address(mInstance));
mServer->OutputFormat("\r\n");
}
else if (strcmp(aArgs[0], "rloc") == 0)
{
OutputIp6Address(*otThreadGetRloc(mInstance));
mServer->OutputFormat("\r\n");
}
else if (strcmp(aArgs[0], "mleid") == 0)
{
OutputIp6Address(*otThreadGetMeshLocalEid(mInstance));
mServer->OutputFormat("\r\n");
}
else
{
ExitNow(error = OT_ERROR_INVALID_COMMAND);
}
}
exit:
AppendResult(error);
}
otError Interpreter::ProcessIpMulticastAddrAdd(uint8_t aArgsLength, char *aArgs[])
{
otError error;
struct otIp6Address address;
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = otIp6AddressFromString(aArgs[0], &address));
error = otIp6SubscribeMulticastAddress(mInstance, &address);
exit:
return error;
}
otError Interpreter::ProcessIpMulticastAddrDel(uint8_t aArgsLength, char *aArgs[])
{
otError error;
struct otIp6Address address;
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = otIp6AddressFromString(aArgs[0], &address));
error = otIp6UnsubscribeMulticastAddress(mInstance, &address);
exit:
return error;
}
otError Interpreter::ProcessMulticastPromiscuous(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
if (otIp6IsMulticastPromiscuousEnabled(mInstance))
{
mServer->OutputFormat("Enabled\r\n");
}
else
{
mServer->OutputFormat("Disabled\r\n");
}
}
else
{
if (strcmp(aArgs[0], "enable") == 0)
{
otIp6SetMulticastPromiscuousEnabled(mInstance, true);
}
else if (strcmp(aArgs[0], "disable") == 0)
{
otIp6SetMulticastPromiscuousEnabled(mInstance, false);
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
exit:
return error;
}
void Interpreter::ProcessIpMulticastAddr(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
for (const otNetifMulticastAddress *addr = otIp6GetMulticastAddresses(mInstance); addr; addr = addr->mNext)
{
OutputIp6Address(addr->mAddress);
mServer->OutputFormat("\r\n");
}
}
else
{
if (strcmp(aArgs[0], "add") == 0)
{
SuccessOrExit(error = ProcessIpMulticastAddrAdd(aArgsLength - 1, aArgs + 1));
}
else if (strcmp(aArgs[0], "del") == 0)
{
SuccessOrExit(error = ProcessIpMulticastAddrDel(aArgsLength - 1, aArgs + 1));
}
else if (strcmp(aArgs[0], "promiscuous") == 0)
{
SuccessOrExit(error = ProcessMulticastPromiscuous(aArgsLength - 1, aArgs + 1));
}
else
{
ExitNow(error = OT_ERROR_INVALID_COMMAND);
}
}
exit:
AppendResult(error);
}
void Interpreter::ProcessKeySequence(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
long value;
VerifyOrExit(aArgsLength == 1 || aArgsLength == 2, error = OT_ERROR_INVALID_ARGS);
if (strcmp(aArgs[0], "counter") == 0)
{
if (aArgsLength == 1)
{
mServer->OutputFormat("%d\r\n", otThreadGetKeySequenceCounter(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(aArgs[1], value));
otThreadSetKeySequenceCounter(mInstance, static_cast<uint32_t>(value));
}
}
else if (strcmp(aArgs[0], "guardtime") == 0)
{
if (aArgsLength == 1)
{
mServer->OutputFormat("%d\r\n", otThreadGetKeySwitchGuardTime(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(aArgs[1], value));
otThreadSetKeySwitchGuardTime(mInstance, static_cast<uint32_t>(value));
}
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
exit:
AppendResult(error);
}
void Interpreter::ProcessLeaderData(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgsLength);
OT_UNUSED_VARIABLE(aArgs);
otError error;
otLeaderData leaderData;
SuccessOrExit(error = otThreadGetLeaderData(mInstance, &leaderData));
mServer->OutputFormat("Partition ID: %u\r\n", leaderData.mPartitionId);
mServer->OutputFormat("Weighting: %d\r\n", leaderData.mWeighting);
mServer->OutputFormat("Data Version: %d\r\n", leaderData.mDataVersion);
mServer->OutputFormat("Stable Data Version: %d\r\n", leaderData.mStableDataVersion);
mServer->OutputFormat("Leader Router ID: %d\r\n", leaderData.mLeaderRouterId);
exit:
AppendResult(error);
}
#if OPENTHREAD_FTD
void Interpreter::ProcessLeaderPartitionId(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
unsigned long value;
if (aArgsLength == 0)
{
mServer->OutputFormat("%u\r\n", otThreadGetLocalLeaderPartitionId(mInstance));
}
else
{
SuccessOrExit(error = ParseUnsignedLong(aArgs[0], value));
otThreadSetLocalLeaderPartitionId(mInstance, static_cast<uint32_t>(value));
}
exit:
AppendResult(error);
}
void Interpreter::ProcessLeaderWeight(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
long value;
if (aArgsLength == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetLocalLeaderWeight(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(aArgs[0], value));
otThreadSetLocalLeaderWeight(mInstance, static_cast<uint8_t>(value));
}
exit:
AppendResult(error);
}
#endif // OPENTHREAD_FTD
#if OPENTHREAD_FTD
void Interpreter::ProcessPskc(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
const otPskc *pskc = otThreadGetPskc(mInstance);
for (int i = 0; i < OT_PSKC_MAX_SIZE; i++)
{
mServer->OutputFormat("%02x", pskc->m8[i]);
}
mServer->OutputFormat("\r\n");
}
else
{
otPskc pskc;
VerifyOrExit(Hex2Bin(aArgs[0], pskc.m8, sizeof(pskc)) == OT_PSKC_MAX_SIZE, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = otThreadSetPskc(mInstance, &pskc));
}
exit:
AppendResult(error);
}
#endif
void Interpreter::ProcessMasterKey(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
const uint8_t *key = reinterpret_cast<const uint8_t *>(otThreadGetMasterKey(mInstance));
for (int i = 0; i < OT_MASTER_KEY_SIZE; i++)
{
mServer->OutputFormat("%02x", key[i]);
}
mServer->OutputFormat("\r\n");
}
else
{
otMasterKey key;
VerifyOrExit(Hex2Bin(aArgs[0], key.m8, sizeof(key.m8)) == OT_MASTER_KEY_SIZE, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = otThreadSetMasterKey(mInstance, &key));
}
exit:
AppendResult(error);
}
void Interpreter::ProcessMode(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
otLinkModeConfig linkMode;
memset(&linkMode, 0, sizeof(otLinkModeConfig));
if (aArgsLength == 0)
{
linkMode = otThreadGetLinkMode(mInstance);
if (linkMode.mRxOnWhenIdle)
{
mServer->OutputFormat("r");
}
if (linkMode.mSecureDataRequests)
{
mServer->OutputFormat("s");
}
if (linkMode.mDeviceType)
{
mServer->OutputFormat("d");
}
if (linkMode.mNetworkData)
{
mServer->OutputFormat("n");
}
mServer->OutputFormat("\r\n");
}
else
{
for (char *arg = aArgs[0]; *arg != '\0'; arg++)
{
switch (*arg)
{
case 'r':
linkMode.mRxOnWhenIdle = 1;
break;
case 's':
linkMode.mSecureDataRequests = 1;
break;
case 'd':
linkMode.mDeviceType = 1;
break;
case 'n':
linkMode.mNetworkData = 1;
break;
default:
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
SuccessOrExit(error = otThreadSetLinkMode(mInstance, linkMode));
}
exit:
AppendResult(error);
}
#if OPENTHREAD_FTD
void Interpreter::ProcessNeighbor(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
otNeighborInfo neighborInfo;
bool isTable;
otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT;
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
isTable = (strcmp(aArgs[0], "table") == 0);
if (isTable || strcmp(aArgs[0], "list") == 0)
{
if (isTable)
{
mServer->OutputFormat("| Role | RLOC16 | Age | Avg RSSI | Last RSSI |R|S|D|N| Extended MAC |\r\n");
mServer->OutputFormat("+------+--------+-----+----------+-----------+-+-+-+-+------------------+\r\n");
}
while (otThreadGetNextNeighborInfo(mInstance, &iterator, &neighborInfo) == OT_ERROR_NONE)
{
if (isTable)
{
mServer->OutputFormat("| %3c ", neighborInfo.mIsChild ? 'C' : 'R');
mServer->OutputFormat("| 0x%04x ", neighborInfo.mRloc16);
mServer->OutputFormat("| %3d ", neighborInfo.mAge);
mServer->OutputFormat("| %8d ", neighborInfo.mAverageRssi);
mServer->OutputFormat("| %9d ", neighborInfo.mLastRssi);
mServer->OutputFormat("|%1d", neighborInfo.mRxOnWhenIdle);
mServer->OutputFormat("|%1d", neighborInfo.mSecureDataRequest);
mServer->OutputFormat("|%1d", neighborInfo.mFullThreadDevice);
mServer->OutputFormat("|%1d", neighborInfo.mFullNetworkData);
mServer->OutputFormat("| ");
for (size_t j = 0; j < sizeof(neighborInfo.mExtAddress); j++)
{
mServer->OutputFormat("%02x", neighborInfo.mExtAddress.m8[j]);
}
mServer->OutputFormat(" |\r\n");
}
else
{
mServer->OutputFormat("0x%04x ", neighborInfo.mRloc16);
}
}
mServer->OutputFormat("\r\n");
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
exit:
AppendResult(error);
}
#endif
void Interpreter::ProcessNetworkDataShow(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgsLength);
OT_UNUSED_VARIABLE(aArgs);
otError error = OT_ERROR_NONE;
uint8_t data[255];
uint8_t len = sizeof(data);
SuccessOrExit(error = otNetDataGet(mInstance, false, data, &len));
OutputBytes(data, static_cast<uint8_t>(len));
mServer->OutputFormat("\r\n");
exit:
AppendResult(error);
}
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
void Interpreter::ProcessService(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
if (strcmp(aArgs[0], "add") == 0)
{
otServiceConfig cfg;
long enterpriseNumber;
size_t length;
VerifyOrExit(aArgsLength > 3, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseLong(aArgs[1], enterpriseNumber));
cfg.mEnterpriseNumber = static_cast<uint32_t>(enterpriseNumber);
length = strlen(aArgs[2]);
VerifyOrExit(length <= sizeof(cfg.mServiceData), error = OT_ERROR_NO_BUFS);
cfg.mServiceDataLength = static_cast<uint8_t>(length);
memcpy(cfg.mServiceData, aArgs[2], cfg.mServiceDataLength);
length = strlen(aArgs[3]);
VerifyOrExit(length <= sizeof(cfg.mServerConfig.mServerData), error = OT_ERROR_NO_BUFS);
cfg.mServerConfig.mServerDataLength = static_cast<uint8_t>(length);
memcpy(cfg.mServerConfig.mServerData, aArgs[3], cfg.mServerConfig.mServerDataLength);
cfg.mServerConfig.mStable = true;
SuccessOrExit(error = otServerAddService(mInstance, &cfg));
}
else if (strcmp(aArgs[0], "remove") == 0)
{
long enterpriseNumber = 0;
VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseLong(aArgs[1], enterpriseNumber));
SuccessOrExit(error = otServerRemoveService(mInstance, static_cast<uint32_t>(enterpriseNumber),
reinterpret_cast<uint8_t *>(aArgs[2]),
static_cast<uint8_t>(strlen(aArgs[2]))));
}
else
{
ExitNow(error = OT_ERROR_INVALID_COMMAND);
}
exit:
AppendResult(error);
}
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
void Interpreter::ProcessNetworkDataRegister(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgsLength);
OT_UNUSED_VARIABLE(aArgs);
otError error = OT_ERROR_NONE;
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
SuccessOrExit(error = otBorderRouterRegister(mInstance));
#else
SuccessOrExit(error = otServerRegister(mInstance));
#endif
exit:
AppendResult(error);
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
#if OPENTHREAD_FTD
void Interpreter::ProcessNetworkIdTimeout(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
long value;
if (aArgsLength == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetNetworkIdTimeout(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(aArgs[0], value));
otThreadSetNetworkIdTimeout(mInstance, static_cast<uint8_t>(value));
}
exit:
AppendResult(error);
}
#endif // OPENTHREAD_FTD
void Interpreter::ProcessNetworkName(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
const char *networkName = otThreadGetNetworkName(mInstance);
mServer->OutputFormat("%.*s\r\n", OT_NETWORK_NAME_MAX_SIZE, static_cast<const char *>(networkName));
}
else
{
SuccessOrExit(error = otThreadSetNetworkName(mInstance, aArgs[0]));
}
exit:
AppendResult(error);
}
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
void Interpreter::ProcessNetworkTime(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
long value;
if (aArgsLength == 0)
{
uint64_t time;
otNetworkTimeStatus networkTimeStatus;
networkTimeStatus = otNetworkTimeGet(mInstance, &time);
mServer->OutputFormat("Network Time: %luus", time);
switch (networkTimeStatus)
{
case OT_NETWORK_TIME_UNSYNCHRONIZED:
mServer->OutputFormat(" (unsynchronized)\r\n");
break;
case OT_NETWORK_TIME_RESYNC_NEEDED:
mServer->OutputFormat(" (resync needed)\r\n");
break;
case OT_NETWORK_TIME_SYNCHRONIZED:
mServer->OutputFormat(" (synchronized)\r\n");
break;
default:
break;
}
mServer->OutputFormat("Time Sync Period: %ds\r\n", otNetworkTimeGetSyncPeriod(mInstance));
mServer->OutputFormat("XTAL Threshold: %dppm\r\n", otNetworkTimeGetXtalThreshold(mInstance));
}
else if (aArgsLength == 2)
{
SuccessOrExit(error = ParseLong(aArgs[0], value));
SuccessOrExit(error = otNetworkTimeSetSyncPeriod(mInstance, static_cast<uint16_t>(value)));
SuccessOrExit(error = ParseLong(aArgs[1], value));
SuccessOrExit(error = otNetworkTimeSetXtalThreshold(mInstance, static_cast<uint16_t>(value)));
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
exit:
AppendResult(error);
}
#endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
void Interpreter::ProcessPanId(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
long value;
if (aArgsLength == 0)
{
mServer->OutputFormat("0x%04x\r\n", otLinkGetPanId(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(aArgs[0], value));
error = otLinkSetPanId(mInstance, static_cast<otPanId>(value));
}
exit:
AppendResult(error);
}
void Interpreter::ProcessParent(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgsLength);
OT_UNUSED_VARIABLE(aArgs);
otError error = OT_ERROR_NONE;
otRouterInfo parentInfo;
SuccessOrExit(error = otThreadGetParentInfo(mInstance, &parentInfo));
mServer->OutputFormat("Ext Addr: ");
for (size_t i = 0; i < sizeof(parentInfo.mExtAddress); i++)
{
mServer->OutputFormat("%02x", parentInfo.mExtAddress.m8[i]);
}
mServer->OutputFormat("\r\n");
mServer->OutputFormat("Rloc: %x\r\n", parentInfo.mRloc16);
mServer->OutputFormat("Link Quality In: %d\r\n", parentInfo.mLinkQualityIn);
mServer->OutputFormat("Link Quality Out: %d\r\n", parentInfo.mLinkQualityOut);
mServer->OutputFormat("Age: %d\r\n", parentInfo.mAge);
exit:
AppendResult(error);
}
#if OPENTHREAD_FTD
void Interpreter::ProcessParentPriority(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
long value;
if (aArgsLength == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetParentPriority(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(aArgs[0], value));
error = otThreadSetParentPriority(mInstance, static_cast<int8_t>(value));
}
exit:
AppendResult(error);
}
#endif
void Interpreter::HandleIcmpReceive(void * aContext,
otMessage * aMessage,
const otMessageInfo *aMessageInfo,
const otIcmp6Header *aIcmpHeader)
{
static_cast<Interpreter *>(aContext)->HandleIcmpReceive(aMessage, aMessageInfo, aIcmpHeader);
}
void Interpreter::HandleIcmpReceive(otMessage * aMessage,
const otMessageInfo *aMessageInfo,
const otIcmp6Header *aIcmpHeader)
{
uint32_t timestamp;
VerifyOrExit(aIcmpHeader->mType == OT_ICMP6_TYPE_ECHO_REPLY);
VerifyOrExit((mPingIdentifier != 0) && (mPingIdentifier == HostSwap16(aIcmpHeader->mData.m16[0])));
mServer->OutputFormat("%u bytes from ", otMessageGetLength(aMessage) - otMessageGetOffset(aMessage) +
static_cast<uint16_t>(sizeof(otIcmp6Header)));
OutputIp6Address(aMessageInfo->mPeerAddr);
mServer->OutputFormat(": icmp_seq=%d hlim=%d", HostSwap16(aIcmpHeader->mData.m16[1]), aMessageInfo->mHopLimit);
if (otMessageRead(aMessage, otMessageGetOffset(aMessage), &timestamp, sizeof(uint32_t)) == sizeof(uint32_t))
{
mServer->OutputFormat(" time=%dms", TimerMilli::GetNow().GetValue() - HostSwap32(timestamp));
}
mServer->OutputFormat("\r\n");
exit:
return;
}
void Interpreter::ProcessPing(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
uint8_t index = 1;
long value;
uint32_t interval;
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
if (strcmp(aArgs[0], "stop") == 0)
{
mPingIdentifier = 0;
VerifyOrExit(mPingTimer.IsRunning(), error = OT_ERROR_INVALID_STATE);
mPingTimer.Stop();
ExitNow();
}
VerifyOrExit(!mPingTimer.IsRunning(), error = OT_ERROR_BUSY);
SuccessOrExit(error = otIp6AddressFromString(aArgs[0], &mPingDestAddress));
mPingLength = kDefaultPingLength;
mPingCount = kDefaultPingCount;
mPingInterval = kDefaultPingInterval;
mPingHopLimit = 0;
mPingAllowZeroHopLimit = false;
while (index < aArgsLength)
{
switch (index)
{
case 1:
SuccessOrExit(error = ParseLong(aArgs[index], value));
mPingLength = static_cast<uint16_t>(value);
break;
case 2:
SuccessOrExit(error = ParseLong(aArgs[index], value));
mPingCount = static_cast<uint16_t>(value);
break;
case 3:
SuccessOrExit(error = ParsePingInterval(aArgs[index], interval));
VerifyOrExit(0 < interval && interval <= Timer::kMaxDelay, error = OT_ERROR_INVALID_ARGS);
mPingInterval = interval;
break;
case 4:
SuccessOrExit(error = ParseLong(aArgs[index], value));
VerifyOrExit(0 <= value && value <= 255, error = OT_ERROR_INVALID_ARGS);
mPingHopLimit = static_cast<uint8_t>(value);
mPingAllowZeroHopLimit = (mPingHopLimit == 0);
break;
default:
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
index++;
}
mPingIdentifier++;
if (mPingIdentifier == 0)
{
mPingIdentifier++;
}
SendPing();
exit:
AppendResult(error);
}
void Interpreter::HandlePingTimer(Timer &aTimer)
{
GetOwner(aTimer).SendPing();
}
void Interpreter::SendPing(void)
{
uint32_t timestamp = HostSwap32(TimerMilli::GetNow().GetValue());
otMessage * message = NULL;
otMessageInfo messageInfo;
memset(&messageInfo, 0, sizeof(messageInfo));
messageInfo.mPeerAddr = mPingDestAddress;
messageInfo.mHopLimit = mPingHopLimit;
messageInfo.mAllowZeroHopLimit = mPingAllowZeroHopLimit;
message = otIp6NewMessage(mInstance, NULL);
VerifyOrExit(message != NULL);
SuccessOrExit(otMessageAppend(message, &timestamp, sizeof(timestamp)));
SuccessOrExit(otMessageSetLength(message, mPingLength));
SuccessOrExit(otIcmp6SendEchoRequest(mInstance, message, &messageInfo, mPingIdentifier));
message = NULL;
exit:
if (message != NULL)
{
otMessageFree(message);
}
if (--mPingCount)
{
mPingTimer.Start(mPingInterval);
}
}
void Interpreter::ProcessPollPeriod(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
long value;
if (aArgsLength == 0)
{
mServer->OutputFormat("%d\r\n", otLinkGetPollPeriod(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(aArgs[0], value));
error = otLinkSetPollPeriod(mInstance, static_cast<uint32_t>(value));
}
exit:
AppendResult(error);
}
void Interpreter::ProcessPromiscuous(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
if (otLinkIsPromiscuous(mInstance) && otPlatRadioGetPromiscuous(mInstance))
{
mServer->OutputFormat("Enabled\r\n");
}
else
{
mServer->OutputFormat("Disabled\r\n");
}
}
else
{
if (strcmp(aArgs[0], "enable") == 0)
{
otLinkSetPcapCallback(mInstance, &HandleLinkPcapReceive, this);
SuccessOrExit(error = otLinkSetPromiscuous(mInstance, true));
}
else if (strcmp(aArgs[0], "disable") == 0)
{
otLinkSetPcapCallback(mInstance, NULL, NULL);
SuccessOrExit(error = otLinkSetPromiscuous(mInstance, false));
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
exit:
AppendResult(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);
mServer->OutputFormat("\r\n");
for (size_t i = 0; i < 44; i++)
{
mServer->OutputFormat("=");
}
mServer->OutputFormat("[len = %3u]", aFrame->mLength);
for (size_t i = 0; i < 28; i++)
{
mServer->OutputFormat("=");
}
mServer->OutputFormat("\r\n");
for (size_t i = 0; i < aFrame->mLength; i += 16)
{
mServer->OutputFormat("|");
for (size_t j = 0; j < 16; j++)
{
if (i + j < aFrame->mLength)
{
mServer->OutputFormat(" %02X", aFrame->mPsdu[i + j]);
}
else
{
mServer->OutputFormat(" ..");
}
}
mServer->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)
{
mServer->OutputFormat(" %c", aFrame->mPsdu[i + j]);
}
else
{
mServer->OutputFormat(" ?");
}
}
else
{
mServer->OutputFormat(" .");
}
}
mServer->OutputFormat("|\r\n");
}
for (size_t i = 0; i < 83; i++)
{
mServer->OutputFormat("-");
}
mServer->OutputFormat("\r\n");
}
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
otError Interpreter::ProcessPrefixAdd(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
otBorderRouterConfig config;
uint8_t argcur = 0;
char * prefixLengthStr;
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
memset(&config, 0, sizeof(otBorderRouterConfig));
if ((prefixLengthStr = strchr(aArgs[argcur], '/')) == NULL)
{
ExitNow();
}
*prefixLengthStr++ = '\0';
SuccessOrExit(error = otIp6AddressFromString(aArgs[argcur], &config.mPrefix.mPrefix));
{
unsigned long length;
SuccessOrExit(error = ParseUnsignedLong(prefixLengthStr, length));
config.mPrefix.mLength = static_cast<uint8_t>(length);
}
argcur++;
for (; argcur < aArgsLength; argcur++)
{
if (strcmp(aArgs[argcur], "high") == 0)
{
config.mPreference = OT_ROUTE_PREFERENCE_HIGH;
}
else if (strcmp(aArgs[argcur], "med") == 0)
{
config.mPreference = OT_ROUTE_PREFERENCE_MED;
}
else if (strcmp(aArgs[argcur], "low") == 0)
{
config.mPreference = OT_ROUTE_PREFERENCE_LOW;
}
else
{
for (char *arg = aArgs[argcur]; *arg != '\0'; arg++)
{
switch (*arg)
{
case 'p':
config.mPreferred = true;
break;
case 'a':
config.mSlaac = true;
break;
case 'd':
config.mDhcp = true;
break;
case 'c':
config.mConfigure = true;
break;
case 'r':
config.mDefaultRoute = true;
break;
case 'o':
config.mOnMesh = true;
break;
case 's':
config.mStable = true;
break;
default:
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
}
}
error = otBorderRouterAddOnMeshPrefix(mInstance, &config);
exit:
return error;
}
otError Interpreter::ProcessPrefixRemove(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgsLength);
otError error = OT_ERROR_NONE;
struct otIp6Prefix prefix;
uint8_t argcur = 0;
char * prefixLengthStr;
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
memset(&prefix, 0, sizeof(otIp6Prefix));
if ((prefixLengthStr = strchr(aArgs[argcur], '/')) == NULL)
{
ExitNow();
}
*prefixLengthStr++ = '\0';
SuccessOrExit(error = otIp6AddressFromString(aArgs[argcur], &prefix.mPrefix));
{
unsigned long length;
SuccessOrExit(error = ParseUnsignedLong(prefixLengthStr, length));
prefix.mLength = static_cast<uint8_t>(length);
}
error = otBorderRouterRemoveOnMeshPrefix(mInstance, &prefix);
exit:
return error;
}
otError Interpreter::ProcessPrefixList(void)
{
otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
otBorderRouterConfig config;
while (otBorderRouterGetNextOnMeshPrefix(mInstance, &iterator, &config) == OT_ERROR_NONE)
{
mServer->OutputFormat("%x:%x:%x:%x::/%d ", HostSwap16(config.mPrefix.mPrefix.mFields.m16[0]),
HostSwap16(config.mPrefix.mPrefix.mFields.m16[1]),
HostSwap16(config.mPrefix.mPrefix.mFields.m16[2]),
HostSwap16(config.mPrefix.mPrefix.mFields.m16[3]), config.mPrefix.mLength);
if (config.mPreferred)
{
mServer->OutputFormat("p");
}
if (config.mSlaac)
{
mServer->OutputFormat("a");
}
if (config.mDhcp)
{
mServer->OutputFormat("d");
}
if (config.mConfigure)
{
mServer->OutputFormat("c");
}
if (config.mDefaultRoute)
{
mServer->OutputFormat("r");
}
if (config.mOnMesh)
{
mServer->OutputFormat("o");
}
if (config.mStable)
{
mServer->OutputFormat("s");
}
switch (config.mPreference)
{
case OT_ROUTE_PREFERENCE_LOW:
mServer->OutputFormat(" low\r\n");
break;
case OT_ROUTE_PREFERENCE_MED:
mServer->OutputFormat(" med\r\n");
break;
case OT_ROUTE_PREFERENCE_HIGH:
mServer->OutputFormat(" high\r\n");
break;
}
}
return OT_ERROR_NONE;
}
void Interpreter::ProcessPrefix(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
SuccessOrExit(error = ProcessPrefixList());
}
else if (strcmp(aArgs[0], "add") == 0)
{
SuccessOrExit(error = ProcessPrefixAdd(aArgsLength - 1, aArgs + 1));
}
else if (strcmp(aArgs[0], "remove") == 0)
{
SuccessOrExit(error = ProcessPrefixRemove(aArgsLength - 1, aArgs + 1));
}
else
{
ExitNow(error = OT_ERROR_INVALID_COMMAND);
}
exit:
AppendResult(error);
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
#if OPENTHREAD_FTD
void Interpreter::ProcessPreferRouterId(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
unsigned long value;
VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseUnsignedLong(aArgs[0], value));
error = otThreadSetPreferredRouterId(mInstance, static_cast<uint8_t>(value));
exit:
AppendResult(error);
}
void Interpreter::ProcessReleaseRouterId(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
long value;
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseLong(aArgs[0], value));
SuccessOrExit(error = otThreadReleaseRouterId(mInstance, static_cast<uint8_t>(value)));
exit:
AppendResult(error);
}
#endif // OPENTHREAD_FTD
void Interpreter::ProcessReset(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgsLength);
OT_UNUSED_VARIABLE(aArgs);
otInstanceReset(mInstance);
}
void Interpreter::ProcessRloc16(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgsLength);
OT_UNUSED_VARIABLE(aArgs);
mServer->OutputFormat("%04x\r\n", otThreadGetRloc16(mInstance));
AppendResult(OT_ERROR_NONE);
}
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
otError Interpreter::ProcessRouteAdd(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
otExternalRouteConfig config;
uint8_t argcur = 0;
char * prefixLengthStr;
memset(&config, 0, sizeof(otExternalRouteConfig));
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
if ((prefixLengthStr = strchr(aArgs[argcur], '/')) == NULL)
{
ExitNow();
}
*prefixLengthStr++ = '\0';
SuccessOrExit(error = otIp6AddressFromString(aArgs[argcur], &config.mPrefix.mPrefix));
{
unsigned long length;
SuccessOrExit(error = ParseUnsignedLong(prefixLengthStr, length));
config.mPrefix.mLength = static_cast<uint8_t>(length);
}
argcur++;
for (; argcur < aArgsLength; argcur++)
{
if (strcmp(aArgs[argcur], "s") == 0)
{
config.mStable = true;
}
else if (strcmp(aArgs[argcur], "high") == 0)
{
config.mPreference = OT_ROUTE_PREFERENCE_HIGH;
}
else if (strcmp(aArgs[argcur], "med") == 0)
{
config.mPreference = OT_ROUTE_PREFERENCE_MED;
}
else if (strcmp(aArgs[argcur], "low") == 0)
{
config.mPreference = OT_ROUTE_PREFERENCE_LOW;
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
error = otBorderRouterAddRoute(mInstance, &config);
exit:
return error;
}
otError Interpreter::ProcessRouteRemove(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
struct otIp6Prefix prefix;
uint8_t argcur = 0;
char * prefixLengthStr;
memset(&prefix, 0, sizeof(struct otIp6Prefix));
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
if ((prefixLengthStr = strchr(aArgs[argcur], '/')) == NULL)
{
ExitNow();
}
*prefixLengthStr++ = '\0';
SuccessOrExit(error = otIp6AddressFromString(aArgs[argcur], &prefix.mPrefix));
{
unsigned long length;
SuccessOrExit(error = ParseUnsignedLong(prefixLengthStr, length));
prefix.mLength = static_cast<uint8_t>(length);
}
error = otBorderRouterRemoveRoute(mInstance, &prefix);
exit:
return error;
}
otError Interpreter::ProcessRouteList(void)
{
otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
otExternalRouteConfig config;
while (otBorderRouterGetNextRoute(mInstance, &iterator, &config) == OT_ERROR_NONE)
{
mServer->OutputFormat("%x:%x:%x:%x::/%d ", HostSwap16(config.mPrefix.mPrefix.mFields.m16[0]),
HostSwap16(config.mPrefix.mPrefix.mFields.m16[1]),
HostSwap16(config.mPrefix.mPrefix.mFields.m16[2]),
HostSwap16(config.mPrefix.mPrefix.mFields.m16[3]), config.mPrefix.mLength);
if (config.mStable)
{
mServer->OutputFormat("s");
}
switch (config.mPreference)
{
case OT_ROUTE_PREFERENCE_LOW:
mServer->OutputFormat(" low\r\n");
break;
case OT_ROUTE_PREFERENCE_MED:
mServer->OutputFormat(" med\r\n");
break;
case OT_ROUTE_PREFERENCE_HIGH:
mServer->OutputFormat(" high\r\n");
break;
}
}
return OT_ERROR_NONE;
}
void Interpreter::ProcessRoute(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
SuccessOrExit(error = ProcessRouteList());
}
else if (strcmp(aArgs[0], "add") == 0)
{
SuccessOrExit(error = ProcessRouteAdd(aArgsLength - 1, aArgs + 1));
}
else if (strcmp(aArgs[0], "remove") == 0)
{
SuccessOrExit(error = ProcessRouteRemove(aArgsLength - 1, aArgs + 1));
}
else
{
ExitNow(error = OT_ERROR_INVALID_COMMAND);
}
exit:
AppendResult(error);
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
#if OPENTHREAD_FTD
void Interpreter::ProcessRouter(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
otRouterInfo routerInfo;
long value;
bool isTable;
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
isTable = (strcmp(aArgs[0], "table") == 0);
if (isTable || strcmp(aArgs[0], "list") == 0)
{
uint8_t maxRouterId;
if (isTable)
{
mServer->OutputFormat(
"| ID | RLOC16 | Next Hop | Path Cost | LQ In | LQ Out | Age | Extended MAC |\r\n");
mServer->OutputFormat(
"+----+--------+----------+-----------+-------+--------+-----+------------------+\r\n");
}
maxRouterId = otThreadGetMaxRouterId(mInstance);
for (uint8_t i = 0; i <= maxRouterId; i++)
{
if (otThreadGetRouterInfo(mInstance, i, &routerInfo) != OT_ERROR_NONE)
{
continue;
}
if (isTable)
{
mServer->OutputFormat("| %2d ", routerInfo.mRouterId);
mServer->OutputFormat("| 0x%04x ", routerInfo.mRloc16);
mServer->OutputFormat("| %8d ", routerInfo.mNextHop);
mServer->OutputFormat("| %9d ", routerInfo.mPathCost);
mServer->OutputFormat("| %5d ", routerInfo.mLinkQualityIn);
mServer->OutputFormat("| %6d ", routerInfo.mLinkQualityOut);
mServer->OutputFormat("| %3d ", routerInfo.mAge);
mServer->OutputFormat("| ");
for (size_t j = 0; j < sizeof(routerInfo.mExtAddress); j++)
{
mServer->OutputFormat("%02x", routerInfo.mExtAddress.m8[j]);
}
mServer->OutputFormat(" |\r\n");
}
else
{
mServer->OutputFormat("%d ", i);
}
}
mServer->OutputFormat("\r\n");
ExitNow();
}
SuccessOrExit(error = ParseLong(aArgs[0], value));
SuccessOrExit(error = otThreadGetRouterInfo(mInstance, static_cast<uint16_t>(value), &routerInfo));
mServer->OutputFormat("Alloc: %d\r\n", routerInfo.mAllocated);
if (routerInfo.mAllocated)
{
mServer->OutputFormat("Router ID: %d\r\n", routerInfo.mRouterId);
mServer->OutputFormat("Rloc: %04x\r\n", routerInfo.mRloc16);
mServer->OutputFormat("Next Hop: %04x\r\n", static_cast<uint16_t>(routerInfo.mNextHop) << 10);
mServer->OutputFormat("Link: %d\r\n", routerInfo.mLinkEstablished);
if (routerInfo.mLinkEstablished)
{
mServer->OutputFormat("Ext Addr: ");
for (size_t j = 0; j < sizeof(routerInfo.mExtAddress); j++)
{
mServer->OutputFormat("%02x", routerInfo.mExtAddress.m8[j]);
}
mServer->OutputFormat("\r\n");
mServer->OutputFormat("Cost: %d\r\n", routerInfo.mPathCost);
mServer->OutputFormat("Link Quality In: %d\r\n", routerInfo.mLinkQualityIn);
mServer->OutputFormat("Link Quality Out: %d\r\n", routerInfo.mLinkQualityOut);
mServer->OutputFormat("Age: %d\r\n", routerInfo.mAge);
}
}
exit:
AppendResult(error);
}
void Interpreter::ProcessRouterDowngradeThreshold(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
long value;
if (aArgsLength == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetRouterDowngradeThreshold(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(aArgs[0], value));
otThreadSetRouterDowngradeThreshold(mInstance, static_cast<uint8_t>(value));
}
exit:
AppendResult(error);
}
void Interpreter::ProcessRouterEligible(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
if (otThreadIsRouterEligible(mInstance))
{
mServer->OutputFormat("Enabled\r\n");
}
else
{
mServer->OutputFormat("Disabled\r\n");
}
}
else if (strcmp(aArgs[0], "enable") == 0)
{
error = otThreadSetRouterEligible(mInstance, true);
}
else if (strcmp(aArgs[0], "disable") == 0)
{
error = otThreadSetRouterEligible(mInstance, false);
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
exit:
AppendResult(error);
}
void Interpreter::ProcessRouterSelectionJitter(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
long value;
if (aArgsLength == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetRouterSelectionJitter(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(aArgs[0], value));
VerifyOrExit(0 < value && value < 256, error = OT_ERROR_INVALID_ARGS);
otThreadSetRouterSelectionJitter(mInstance, static_cast<uint8_t>(value));
}
exit:
AppendResult(error);
}
void Interpreter::ProcessRouterUpgradeThreshold(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
long value;
if (aArgsLength == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetRouterUpgradeThreshold(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(aArgs[0], value));
otThreadSetRouterUpgradeThreshold(mInstance, static_cast<uint8_t>(value));
}
exit:
AppendResult(error);
}
#endif // OPENTHREAD_FTD
void Interpreter::ProcessScan(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
uint32_t scanChannels = 0;
uint16_t scanDuration = 0;
bool energyScan = false;
long value;
if (aArgsLength > 0)
{
if (strcmp(aArgs[0], "energy") == 0)
{
energyScan = true;
if (aArgsLength > 1)
{
SuccessOrExit(error = ParseLong(aArgs[1], value));
scanDuration = static_cast<uint16_t>(value);
}
}
else
{
SuccessOrExit(error = ParseLong(aArgs[0], value));
VerifyOrExit((0 <= value) && (value < static_cast<long>(sizeof(scanChannels) * CHAR_BIT)),
error = OT_ERROR_INVALID_ARGS);
scanChannels = 1 << value;
}
}
if (energyScan)
{
mServer->OutputFormat("| Ch | RSSI |\r\n");
mServer->OutputFormat("+----+------+\r\n");
SuccessOrExit(error = otLinkEnergyScan(mInstance, scanChannels, scanDuration,
&Interpreter::HandleEnergyScanResult, this));
}
else
{
mServer->OutputFormat(
"| J | Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI |\r\n");
mServer->OutputFormat(
"+---+------------------+------------------+------+------------------+----+-----+-----+\r\n");
SuccessOrExit(error = otLinkActiveScan(mInstance, scanChannels, scanDuration,
&Interpreter::HandleActiveScanResult, this));
}
exit:
if (error != OT_ERROR_NONE)
{
AppendResult(error);
}
}
void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult, void *aContext)
{
static_cast<Interpreter *>(aContext)->HandleActiveScanResult(aResult);
}
void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult)
{
if (aResult == NULL)
{
AppendResult(OT_ERROR_NONE);
ExitNow();
}
mServer->OutputFormat("| %d ", aResult->mIsJoinable);
mServer->OutputFormat("| %-16s ", aResult->mNetworkName.m8);
mServer->OutputFormat("| ");
OutputBytes(aResult->mExtendedPanId.m8, OT_EXT_PAN_ID_SIZE);
mServer->OutputFormat(" ");
mServer->OutputFormat("| %04x | ", aResult->mPanId);
OutputBytes(aResult->mExtAddress.m8, OT_EXT_ADDRESS_SIZE);
mServer->OutputFormat(" | %2d ", aResult->mChannel);
mServer->OutputFormat("| %3d ", aResult->mRssi);
mServer->OutputFormat("| %3d |\r\n", aResult->mLqi);
exit:
return;
}
void Interpreter::HandleEnergyScanResult(otEnergyScanResult *aResult, void *aContext)
{
static_cast<Interpreter *>(aContext)->HandleEnergyScanResult(aResult);
}
void Interpreter::HandleEnergyScanResult(otEnergyScanResult *aResult)
{
if (aResult == NULL)
{
AppendResult(OT_ERROR_NONE);
ExitNow();
}
mServer->OutputFormat("| %2d | %4d |\r\n", aResult->mChannel, aResult->mMaxRssi);
exit:
return;
}
void Interpreter::ProcessSingleton(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgsLength);
OT_UNUSED_VARIABLE(aArgs);
otError error = OT_ERROR_NONE;
if (otThreadIsSingleton(mInstance))
{
mServer->OutputFormat("true\r\n");
}
else
{
mServer->OutputFormat("false\r\n");
}
AppendResult(error);
}
#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
void Interpreter::ProcessSntp(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
long port = OT_SNTP_DEFAULT_SERVER_PORT;
Ip6::MessageInfo messageInfo;
otSntpQuery query;
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
if (strcmp(aArgs[0], "query") == 0)
{
VerifyOrExit(!mSntpQueryingInProgress, error = OT_ERROR_BUSY);
if (aArgsLength > 1)
{
SuccessOrExit(error = messageInfo.GetPeerAddr().FromString(aArgs[1]));
}
else
{
// Use IPv6 address of default SNTP server.
SuccessOrExit(error = messageInfo.GetPeerAddr().FromString(OT_SNTP_DEFAULT_SERVER_IP));
}
if (aArgsLength > 2)
{
SuccessOrExit(error = ParseLong(aArgs[2], port));
}
messageInfo.SetPeerPort(static_cast<uint16_t>(port));
query.mMessageInfo = static_cast<const otMessageInfo *>(&messageInfo);
SuccessOrExit(error = otSntpClientQuery(mInstance, &query, &Interpreter::HandleSntpResponse, this));
mSntpQueryingInProgress = true;
}
else
{
ExitNow(error = OT_ERROR_INVALID_COMMAND);
}
exit:
if (error != OT_ERROR_NONE)
{
AppendResult(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.
mServer->OutputFormat("SNTP response - Unix time: %u (era: %u)\r\n", static_cast<uint32_t>(aTime),
static_cast<uint32_t>(aTime >> 32));
}
else
{
mServer->OutputFormat("SNTP error - %s\r\n", otThreadErrorToString(aResult));
}
mSntpQueryingInProgress = false;
AppendResult(OT_ERROR_NONE);
}
#endif
void Interpreter::ProcessState(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
switch (otThreadGetDeviceRole(mInstance))
{
case OT_DEVICE_ROLE_DISABLED:
mServer->OutputFormat("disabled\r\n");
break;
case OT_DEVICE_ROLE_DETACHED:
mServer->OutputFormat("detached\r\n");
break;
case OT_DEVICE_ROLE_CHILD:
mServer->OutputFormat("child\r\n");
break;
#if OPENTHREAD_FTD
case OT_DEVICE_ROLE_ROUTER:
mServer->OutputFormat("router\r\n");
break;
case OT_DEVICE_ROLE_LEADER:
mServer->OutputFormat("leader\r\n");
break;
#endif // OPENTHREAD_FTD
default:
mServer->OutputFormat("invalid state\r\n");
break;
}
}
else
{
if (strcmp(aArgs[0], "detached") == 0)
{
SuccessOrExit(error = otThreadBecomeDetached(mInstance));
}
else if (strcmp(aArgs[0], "child") == 0)
{
SuccessOrExit(error = otThreadBecomeChild(mInstance));
}
#if OPENTHREAD_FTD
else if (strcmp(aArgs[0], "router") == 0)
{
SuccessOrExit(error = otThreadBecomeRouter(mInstance));
}
else if (strcmp(aArgs[0], "leader") == 0)
{
SuccessOrExit(error = otThreadBecomeLeader(mInstance));
}
#endif // OPENTHREAD_FTD
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
exit:
AppendResult(error);
}
void Interpreter::ProcessThread(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgsLength);
OT_UNUSED_VARIABLE(aArgs);
otError error = OT_ERROR_NONE;
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
if (strcmp(aArgs[0], "start") == 0)
{
SuccessOrExit(error = otThreadSetEnabled(mInstance, true));
}
else if (strcmp(aArgs[0], "stop") == 0)
{
SuccessOrExit(error = otThreadSetEnabled(mInstance, false));
}
else if (strcmp(aArgs[0], "version") == 0)
{
mServer->OutputFormat("%u\r\n", otThreadGetVersion());
}
else
{
ExitNow(error = OT_ERROR_INVALID_COMMAND);
}
exit:
AppendResult(error);
}
void Interpreter::ProcessDataset(uint8_t aArgsLength, char *aArgs[])
{
otError error;
error = mDataset.Process(aArgsLength, aArgs);
AppendResult(error);
}
void Interpreter::ProcessTxPower(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
int8_t power;
SuccessOrExit(error = otPlatRadioGetTransmitPower(mInstance, &power));
mServer->OutputFormat("%d dBm\r\n", power);
}
else
{
long value;
SuccessOrExit(error = ParseLong(aArgs[0], value));
SuccessOrExit(error = otPlatRadioSetTransmitPower(mInstance, static_cast<int8_t>(value)));
}
exit:
AppendResult(error);
}
void Interpreter::ProcessUdp(uint8_t aArgsLength, char *aArgs[])
{
otError error;
error = mUdp.Process(aArgsLength, aArgs);
AppendResult(error);
}
void Interpreter::ProcessVersion(uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aArgsLength);
OT_UNUSED_VARIABLE(aArgs);
const char *version = otGetVersionString();
mServer->OutputFormat("%s\r\n", static_cast<const char *>(version));
AppendResult(OT_ERROR_NONE);
}
#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
void Interpreter::ProcessCommissioner(uint8_t aArgsLength, char *aArgs[])
{
otError error;
error = mCommissioner.Process(aArgsLength, aArgs);
AppendResult(error);
}
#endif
#if OPENTHREAD_CONFIG_JOINER_ENABLE
void Interpreter::ProcessJoiner(uint8_t aArgsLength, char *aArgs[])
{
otError error;
error = mJoiner.Process(aArgsLength, aArgs);
AppendResult(error);
}
#endif
#if OPENTHREAD_FTD
void Interpreter::ProcessJoinerPort(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
long value;
if (aArgsLength == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetJoinerUdpPort(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(aArgs[0], value));
error = otThreadSetJoinerUdpPort(mInstance, static_cast<uint16_t>(value));
}
exit:
AppendResult(error);
}
#endif
#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
void Interpreter::ProcessMacFilter(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgsLength == 0)
{
PrintMacFilter();
}
else
{
if (strcmp(aArgs[0], "addr") == 0)
{
error = ProcessMacFilterAddress(aArgsLength - 1, aArgs + 1);
}
else if (strcmp(aArgs[0], "rss") == 0)
{
error = ProcessMacFilterRss(aArgsLength - 1, aArgs + 1);
}
else
{
error = OT_ERROR_INVALID_COMMAND;
}
}
AppendResult(error);
}
void Interpreter::PrintMacFilter(void)
{
otMacFilterEntry entry;
otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT;
otMacFilterAddressMode mode = otLinkFilterGetAddressMode(mInstance);
if (mode == OT_MAC_FILTER_ADDRESS_MODE_DISABLED)
{
mServer->OutputFormat("Address Mode: Disabled\r\n");
}
else if (mode == OT_MAC_FILTER_ADDRESS_MODE_WHITELIST)
{
mServer->OutputFormat("Address Mode: Whitelist\r\n");
}
else if (mode == OT_MAC_FILTER_ADDRESS_MODE_BLACKLIST)
{
mServer->OutputFormat("Address Mode: Blacklist\r\n");
}
while (otLinkFilterGetNextAddress(mInstance, &iterator, &entry) == OT_ERROR_NONE)
{
OutputBytes(entry.mExtAddress.m8, OT_EXT_ADDRESS_SIZE);
if (entry.mRssIn != OT_MAC_FILTER_FIXED_RSS_DISABLED)
{
mServer->OutputFormat(" : rss %d (lqi %d)", entry.mRssIn,
otLinkConvertRssToLinkQuality(mInstance, entry.mRssIn));
}
mServer->OutputFormat("\r\n");
}
iterator = OT_MAC_FILTER_ITERATOR_INIT;
mServer->OutputFormat("RssIn List:\r\n");
while (otLinkFilterGetNextRssIn(mInstance, &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)
{
mServer->OutputFormat("Default rss : %d (lqi %d)\r\n", entry.mRssIn,
otLinkConvertRssToLinkQuality(mInstance, entry.mRssIn));
}
else
{
OutputBytes(entry.mExtAddress.m8, OT_EXT_ADDRESS_SIZE);
mServer->OutputFormat(" : rss %d (lqi %d)\r\n", entry.mRssIn,
otLinkConvertRssToLinkQuality(mInstance, entry.mRssIn));
}
}
}
otError Interpreter::ProcessMacFilterAddress(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
otExtAddress extAddr;
otMacFilterEntry entry;
otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT;
otMacFilterAddressMode mode = otLinkFilterGetAddressMode(mInstance);
long value;
if (aArgsLength == 0)
{
if (mode == OT_MAC_FILTER_ADDRESS_MODE_DISABLED)
{
mServer->OutputFormat("Disabled\r\n");
}
else if (mode == OT_MAC_FILTER_ADDRESS_MODE_WHITELIST)
{
mServer->OutputFormat("Whitelist\r\n");
}
else if (mode == OT_MAC_FILTER_ADDRESS_MODE_BLACKLIST)
{
mServer->OutputFormat("Blacklist\r\n");
}
while (otLinkFilterGetNextAddress(mInstance, &iterator, &entry) == OT_ERROR_NONE)
{
OutputBytes(entry.mExtAddress.m8, OT_EXT_ADDRESS_SIZE);
if (entry.mRssIn != OT_MAC_FILTER_FIXED_RSS_DISABLED)
{
mServer->OutputFormat(" : rss %d (lqi %d)", entry.mRssIn,
otLinkConvertRssToLinkQuality(mInstance, entry.mRssIn));
}
mServer->OutputFormat("\r\n");
}
}
else
{
if (strcmp(aArgs[0], "disable") == 0)
{
VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = otLinkFilterSetAddressMode(mInstance, OT_MAC_FILTER_ADDRESS_MODE_DISABLED));
}
else if (strcmp(aArgs[0], "whitelist") == 0)
{
VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = otLinkFilterSetAddressMode(mInstance, OT_MAC_FILTER_ADDRESS_MODE_WHITELIST));
}
else if (strcmp(aArgs[0], "blacklist") == 0)
{
VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = otLinkFilterSetAddressMode(mInstance, OT_MAC_FILTER_ADDRESS_MODE_BLACKLIST));
}
else if (strcmp(aArgs[0], "add") == 0)
{
VerifyOrExit(aArgsLength >= 2, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(Hex2Bin(aArgs[1], extAddr.m8, OT_EXT_ADDRESS_SIZE) == OT_EXT_ADDRESS_SIZE,
error = OT_ERROR_INVALID_ARGS);
error = otLinkFilterAddAddress(mInstance, &extAddr);
VerifyOrExit(error == OT_ERROR_NONE || error == OT_ERROR_ALREADY);
if (aArgsLength > 2)
{
int8_t rss = 0;
VerifyOrExit(aArgsLength == 3, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseLong(aArgs[2], value));
rss = static_cast<int8_t>(value);
SuccessOrExit(error = otLinkFilterAddRssIn(mInstance, &extAddr, rss));
}
}
else if (strcmp(aArgs[0], "remove") == 0)
{
VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(Hex2Bin(aArgs[1], extAddr.m8, OT_EXT_ADDRESS_SIZE) == OT_EXT_ADDRESS_SIZE,
error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = otLinkFilterRemoveAddress(mInstance, &extAddr));
}
else if (strcmp(aArgs[0], "clear") == 0)
{
VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS);
otLinkFilterClearAddresses(mInstance);
}
else
{
error = OT_ERROR_INVALID_COMMAND;
}
}
exit:
return error;
}
otError Interpreter::ProcessMacFilterRss(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
otMacFilterEntry entry;
otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT;
otExtAddress extAddr;
long value;
int8_t rss;
if (aArgsLength == 0)
{
while (otLinkFilterGetNextRssIn(mInstance, &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)
{
mServer->OutputFormat("Default rss: %d (lqi %d)\r\n", entry.mRssIn,
otLinkConvertRssToLinkQuality(mInstance, entry.mRssIn));
}
else
{
OutputBytes(entry.mExtAddress.m8, OT_EXT_ADDRESS_SIZE);
mServer->OutputFormat(" : rss %d (lqi %d)\r\n", entry.mRssIn,
otLinkConvertRssToLinkQuality(mInstance, entry.mRssIn));
}
}
}
else
{
if (strcmp(aArgs[0], "add-lqi") == 0)
{
uint8_t linkquality = 0;
VerifyOrExit(aArgsLength == 3, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseLong(aArgs[2], value));
linkquality = static_cast<uint8_t>(value);
VerifyOrExit(linkquality <= 3, error = OT_ERROR_INVALID_ARGS);
rss = otLinkConvertLinkQualityToRss(mInstance, linkquality);
if (strcmp(aArgs[1], "*") == 0)
{
SuccessOrExit(error = otLinkFilterAddRssIn(mInstance, NULL, rss));
}
else
{
VerifyOrExit(Hex2Bin(aArgs[1], extAddr.m8, OT_EXT_ADDRESS_SIZE) == OT_EXT_ADDRESS_SIZE,
error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = otLinkFilterAddRssIn(mInstance, &extAddr, rss));
}
}
else if (strcmp(aArgs[0], "add") == 0)
{
VerifyOrExit(aArgsLength == 3, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = ParseLong(aArgs[2], value));
rss = static_cast<int8_t>(value);
if (strcmp(aArgs[1], "*") == 0)
{
SuccessOrExit(error = otLinkFilterAddRssIn(mInstance, NULL, rss));
}
else
{
VerifyOrExit(Hex2Bin(aArgs[1], extAddr.m8, OT_EXT_ADDRESS_SIZE) == OT_EXT_ADDRESS_SIZE,
error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = otLinkFilterAddRssIn(mInstance, &extAddr, rss));
}
}
else if (strcmp(aArgs[0], "remove") == 0)
{
VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS);
if (strcmp(aArgs[1], "*") == 0)
{
SuccessOrExit(error = otLinkFilterRemoveRssIn(mInstance, NULL));
}
else
{
VerifyOrExit(Hex2Bin(aArgs[1], extAddr.m8, OT_EXT_ADDRESS_SIZE) == OT_EXT_ADDRESS_SIZE,
error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = otLinkFilterRemoveRssIn(mInstance, &extAddr));
}
}
else if (strcmp(aArgs[0], "clear") == 0)
{
otLinkFilterClearRssIn(mInstance);
}
else
{
error = OT_ERROR_INVALID_COMMAND;
}
}
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
void Interpreter::ProcessMac(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS);
if (strcmp(aArgs[0], "retries") == 0)
{
error = ProcessMacRetries(aArgsLength - 1, aArgs + 1);
}
else
{
error = OT_ERROR_INVALID_COMMAND;
}
exit:
AppendResult(error);
}
otError Interpreter::ProcessMacRetries(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aArgsLength > 0 && aArgsLength <= 2, error = OT_ERROR_INVALID_ARGS);
if (strcmp(aArgs[0], "direct") == 0)
{
if (aArgsLength == 1)
{
mServer->OutputFormat("%d\r\n", otLinkGetMaxFrameRetriesDirect(mInstance));
}
else
{
unsigned long value;
SuccessOrExit(error = ParseUnsignedLong(aArgs[1], value));
VerifyOrExit(value <= 0xff, error = OT_ERROR_INVALID_ARGS);
otLinkSetMaxFrameRetriesDirect(mInstance, static_cast<uint8_t>(value));
}
}
#if OPENTHREAD_FTD
else if (strcmp(aArgs[0], "indirect") == 0)
{
if (aArgsLength == 1)
{
mServer->OutputFormat("%d\r\n", otLinkGetMaxFrameRetriesIndirect(mInstance));
}
else
{
unsigned long value;
SuccessOrExit(error = ParseUnsignedLong(aArgs[1], value));
VerifyOrExit(value <= 0xff, error = OT_ERROR_INVALID_ARGS);
otLinkSetMaxFrameRetriesIndirect(mInstance, static_cast<uint8_t>(value));
}
}
#endif
else
{
error = OT_ERROR_INVALID_ARGS;
}
exit:
return error;
}
#if OPENTHREAD_CONFIG_DIAG_ENABLE
void Interpreter::ProcessDiag(uint8_t aArgsLength, char *aArgs[])
{
otError error;
char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE];
// all diagnostics related features are processed within diagnostics module
output[0] = '\0';
output[sizeof(output) - 1] = '\0';
error = otDiagProcessCmd(mInstance, aArgsLength, aArgs, output, sizeof(output) - 1);
mServer->Output(output, static_cast<uint16_t>(strlen(output)));
AppendResult(error);
}
#endif
void Interpreter::ProcessLine(char *aBuf, uint16_t aBufLength, Server &aServer)
{
char * aArgs[kMaxArgs] = {NULL};
char * cmd;
uint8_t aArgsLength = 0, i = 0;
mServer = &aServer;
VerifyOrExit(aBuf != NULL && StringLength(aBuf, aBufLength + 1) <= aBufLength);
VerifyOrExit(Utils::CmdLineParser::ParseCmd(aBuf, aArgsLength, aArgs, kMaxArgs) == OT_ERROR_NONE,
mServer->OutputFormat("Error: too many args (max %d)\r\n", kMaxArgs));
VerifyOrExit(aArgsLength >= 1, mServer->OutputFormat("Error: no given command.\r\n"));
cmd = aArgs[0];
#if OPENTHREAD_CONFIG_DIAG_ENABLE
VerifyOrExit(
(!otDiagIsEnabled(mInstance) || (strcmp(cmd, "diag") == 0)),
mServer->OutputFormat("under diagnostics mode, execute 'diag stop' before running any other commands.\r\n"));
#endif
for (i = 0; i < OT_ARRAY_LENGTH(sCommands); i++)
{
if (strcmp(cmd, sCommands[i].mName) == 0)
{
(this->*sCommands[i].mCommand)(aArgsLength - 1, &aArgs[1]);
break;
}
}
// Check user defined commands if built-in command
// has not been found
if (i == OT_ARRAY_LENGTH(sCommands))
{
for (i = 0; i < mUserCommandsLength; i++)
{
if (strcmp(cmd, mUserCommands[i].mName) == 0)
{
mUserCommands[i].mCommand(aArgsLength - 1, &aArgs[1]);
break;
}
}
if (i == mUserCommandsLength)
{
AppendResult(OT_ERROR_INVALID_COMMAND);
}
}
exit:
return;
}
#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
void Interpreter::ProcessNetworkDiagnostic(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_NONE;
struct otIp6Address address;
uint8_t tlvTypes[OT_NETWORK_DIAGNOSTIC_TYPELIST_MAX_ENTRIES];
uint8_t count = 0;
uint8_t argsIndex = 0;
// Include operation, address and type tlv list.
VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = otIp6AddressFromString(aArgs[1], &address));
argsIndex = 2;
while (argsIndex < aArgsLength && count < sizeof(tlvTypes))
{
long value;
SuccessOrExit(error = ParseLong(aArgs[argsIndex++], value));
tlvTypes[count++] = static_cast<uint8_t>(value);
}
if (strcmp(aArgs[0], "get") == 0)
{
otThreadSendDiagnosticGet(mInstance, &address, tlvTypes, count);
ExitNow();
}
else if (strcmp(aArgs[0], "reset") == 0)
{
otThreadSendDiagnosticReset(mInstance, &address, tlvTypes, count);
AppendResult(OT_ERROR_NONE);
}
else
{
ExitNow(error = OT_ERROR_INVALID_COMMAND);
}
exit:
if (error != OT_ERROR_NONE)
{
AppendResult(error);
}
}
void Interpreter::HandleDiagnosticGetResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, void *aContext)
{
static_cast<Interpreter *>(aContext)->HandleDiagnosticGetResponse(
*aMessage, *static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}
void Interpreter::HandleDiagnosticGetResponse(const otMessage &aMessage, const Ip6::MessageInfo &)
{
uint8_t buf[16];
uint16_t bytesToPrint;
uint16_t bytesPrinted = 0;
uint16_t length = otMessageGetLength(&aMessage) - otMessageGetOffset(&aMessage);
otNetworkDiagTlv diagTlv;
otNetworkDiagIterator iterator = OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT;
otError error = OT_ERROR_NONE;
mServer->OutputFormat("DIAG_GET.rsp/ans: ");
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;
}
mServer->OutputFormat("\r\n");
// Output Network Diagnostic TLV values in standard YAML format.
while ((error = otThreadGetNextDiagnosticTlv(&aMessage, &iterator, &diagTlv)) == OT_ERROR_NONE)
{
uint16_t column = 0;
switch (diagTlv.mType)
{
case OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS:
mServer->OutputFormat("Ext Address: '");
OutputBytes(diagTlv.mData.mExtAddress.m8, sizeof(diagTlv.mData.mExtAddress.m8));
mServer->OutputFormat("'\r\n");
break;
case OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS:
mServer->OutputFormat("Rloc16: 0x%04x\r\n", diagTlv.mData.mAddr16);
break;
case OT_NETWORK_DIAGNOSTIC_TLV_MODE:
mServer->OutputFormat("Mode:\r\n");
OutputMode(diagTlv.mData.mMode, column + INDENT_SIZE);
break;
case OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT:
mServer->OutputFormat("Timeout: %u\r\n", diagTlv.mData.mTimeout);
break;
case OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY:
mServer->OutputFormat("Connectivity:\r\n");
OutputConnectivity(diagTlv.mData.mConnectivity, column + INDENT_SIZE);
break;
case OT_NETWORK_DIAGNOSTIC_TLV_ROUTE:
mServer->OutputFormat("Route:\r\n");
OutputRoute(diagTlv.mData.mRoute, column + INDENT_SIZE);
break;
case OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA:
mServer->OutputFormat("Leader Data:\r\n");
OutputLeaderData(diagTlv.mData.mLeaderData, column + INDENT_SIZE);
break;
case OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA:
mServer->OutputFormat("Network Data: '");
OutputBytes(diagTlv.mData.mNetworkData.m8, diagTlv.mData.mNetworkData.mCount);
mServer->OutputFormat("'\r\n");
break;
case OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST:
mServer->OutputFormat("IP6 Address List:\r\n");
for (uint16_t i = 0; i < diagTlv.mData.mIp6AddrList.mCount; ++i)
{
OutputSpaces(column + INDENT_SIZE);
mServer->OutputFormat("- ");
OutputIp6Address(diagTlv.mData.mIp6AddrList.mList[i]);
mServer->OutputFormat("\r\n");
}
break;
case OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS:
mServer->OutputFormat("MAC Counters:\r\n");
OutputNetworkDiagMacCounters(diagTlv.mData.mMacCounters, column + INDENT_SIZE);
break;
case OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL:
mServer->OutputFormat("Battery Level: %u%%\r\n", diagTlv.mData.mBatteryLevel);
break;
case OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE:
mServer->OutputFormat("Supply Voltage: %umV\r\n", diagTlv.mData.mSupplyVoltage);
break;
case OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE:
mServer->OutputFormat("Child Table:\r\n");
for (uint16_t i = 0; i < diagTlv.mData.mChildTable.mCount; ++i)
{
OutputSpaces(column + INDENT_SIZE);
mServer->OutputFormat("- ");
OutputChildTableEntry(diagTlv.mData.mChildTable.mTable[i], column + INDENT_SIZE + 2);
}
break;
case OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES:
mServer->OutputFormat("Channel Pages: '");
OutputBytes(diagTlv.mData.mChannelPages.m8, diagTlv.mData.mChannelPages.mCount);
mServer->OutputFormat("'\r\n");
break;
case OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT:
mServer->OutputFormat("Max Child Timeout: %u\r\n", diagTlv.mData.mMaxChildTimeout);
break;
}
}
AppendResult(error == OT_ERROR_NOT_FOUND ? OT_ERROR_NONE : error);
}
void Interpreter::OutputSpaces(uint16_t aCount)
{
static const uint16_t kSpaceStrLen = 16;
char spaceStr[kSpaceStrLen + 1];
memset(spaceStr, ' ', kSpaceStrLen);
spaceStr[kSpaceStrLen] = '\0';
for (uint16_t i = 0; i < aCount; i += kSpaceStrLen)
{
uint16_t idx = (i + kSpaceStrLen <= aCount) ? 0 : (i + kSpaceStrLen - aCount);
mServer->OutputFormat(&spaceStr[idx]);
}
}
void Interpreter::OutputMode(const otLinkModeConfig &aMode, uint16_t aColumn)
{
OutputSpaces(aColumn);
mServer->OutputFormat("RxOnWhenIdle: %d\r\n", aMode.mRxOnWhenIdle);
OutputSpaces(aColumn);
mServer->OutputFormat("SecureDataRequests: %d\r\n", aMode.mSecureDataRequests);
OutputSpaces(aColumn);
mServer->OutputFormat("DeviceType: %d\r\n", aMode.mDeviceType);
OutputSpaces(aColumn);
mServer->OutputFormat("NetworkData: %d\r\n", aMode.mNetworkData);
}
void Interpreter::OutputConnectivity(const otNetworkDiagConnectivity &aConnectivity, uint16_t aColumn)
{
OutputSpaces(aColumn);
mServer->OutputFormat("ParentPriority: %d\r\n", aConnectivity.mParentPriority);
OutputSpaces(aColumn);
mServer->OutputFormat("LinkQuality3: %u\r\n", aConnectivity.mLinkQuality3);
OutputSpaces(aColumn);
mServer->OutputFormat("LinkQuality2: %u\r\n", aConnectivity.mLinkQuality2);
OutputSpaces(aColumn);
mServer->OutputFormat("LinkQuality1: %u\r\n", aConnectivity.mLinkQuality1);
OutputSpaces(aColumn);
mServer->OutputFormat("LeaderCost: %u\r\n", aConnectivity.mLeaderCost);
OutputSpaces(aColumn);
mServer->OutputFormat("IdSequence: %u\r\n", aConnectivity.mIdSequence);
OutputSpaces(aColumn);
mServer->OutputFormat("ActiveRouters: %u\r\n", aConnectivity.mActiveRouters);
OutputSpaces(aColumn);
mServer->OutputFormat("SedBufferSize: %u\r\n", aConnectivity.mSedBufferSize);
OutputSpaces(aColumn);
mServer->OutputFormat("SedDatagramCount: %u\r\n", aConnectivity.mSedDatagramCount);
}
void Interpreter::OutputRoute(const otNetworkDiagRoute &aRoute, uint16_t aColumn)
{
OutputSpaces(aColumn);
mServer->OutputFormat("IdSequence: %u\r\n", aRoute.mIdSequence);
OutputSpaces(aColumn);
mServer->OutputFormat("RouteData:\r\n");
aColumn += INDENT_SIZE;
for (uint16_t i = 0; i < aRoute.mRouteCount; ++i)
{
OutputSpaces(aColumn);
mServer->OutputFormat("- ");
OutputRouteData(aRoute.mRouteData[i], aColumn + 2);
}
}
void Interpreter::OutputRouteData(const otNetworkDiagRouteData &aRouteData, uint16_t aColumn)
{
mServer->OutputFormat("RouteId: 0x%02x\r\n", aRouteData.mRouterId);
OutputSpaces(aColumn);
mServer->OutputFormat("LinkQualityOut: %u\r\n", aRouteData.mLinkQualityOut);
OutputSpaces(aColumn);
mServer->OutputFormat("LinkQualityIn: %u\r\n", aRouteData.mLinkQualityIn);
OutputSpaces(aColumn);
mServer->OutputFormat("RouteCost: %u\r\n", aRouteData.mRouteCost);
}
void Interpreter::OutputLeaderData(const otLeaderData &aLeaderData, uint16_t aColumn)
{
OutputSpaces(aColumn);
mServer->OutputFormat("PartitionId: 0x%08x\r\n", aLeaderData.mPartitionId);
OutputSpaces(aColumn);
mServer->OutputFormat("Weighting: %u\r\n", aLeaderData.mWeighting);
OutputSpaces(aColumn);
mServer->OutputFormat("DataVersion: %u\r\n", aLeaderData.mDataVersion);
OutputSpaces(aColumn);
mServer->OutputFormat("StableDataVersion: %u\r\n", aLeaderData.mStableDataVersion);
OutputSpaces(aColumn);
mServer->OutputFormat("LeaderRouterId: 0x%02x\r\n", aLeaderData.mLeaderRouterId);
}
void Interpreter::OutputNetworkDiagMacCounters(const otNetworkDiagMacCounters &aMacCounters, uint16_t aColumn)
{
OutputSpaces(aColumn);
mServer->OutputFormat("IfInUnknownProtos: %u\r\n", aMacCounters.mIfInUnknownProtos);
OutputSpaces(aColumn);
mServer->OutputFormat("IfInErrors: %u\r\n", aMacCounters.mIfInErrors);
OutputSpaces(aColumn);
mServer->OutputFormat("IfOutErrors: %u\r\n", aMacCounters.mIfOutErrors);
OutputSpaces(aColumn);
mServer->OutputFormat("IfInUcastPkts: %u\r\n", aMacCounters.mIfInUcastPkts);
OutputSpaces(aColumn);
mServer->OutputFormat("IfInBroadcastPkts: %u\r\n", aMacCounters.mIfInBroadcastPkts);
OutputSpaces(aColumn);
mServer->OutputFormat("IfInDiscards: %u\r\n", aMacCounters.mIfInDiscards);
OutputSpaces(aColumn);
mServer->OutputFormat("IfOutUcastPkts: %u\r\n", aMacCounters.mIfOutUcastPkts);
OutputSpaces(aColumn);
mServer->OutputFormat("IfOutBroadcastPkts: %u\r\n", aMacCounters.mIfOutBroadcastPkts);
OutputSpaces(aColumn);
mServer->OutputFormat("IfOutDiscards: %u\r\n", aMacCounters.mIfOutDiscards);
}
void Interpreter::OutputChildTableEntry(const otNetworkDiagChildEntry &aChildEntry, uint16_t aColumn)
{
mServer->OutputFormat("ChildId: 0x%04x\r\n", aChildEntry.mChildId);
OutputSpaces(aColumn);
mServer->OutputFormat("Timeout: %u\r\n", aChildEntry.mTimeout);
OutputSpaces(aColumn);
mServer->OutputFormat("Mode:\r\n");
OutputMode(aChildEntry.mMode, aColumn + INDENT_SIZE);
}
#endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
void Interpreter::SetUserCommands(const otCliCommand *aCommands, uint8_t aLength)
{
mUserCommands = aCommands;
mUserCommandsLength = aLength;
}
Interpreter &Interpreter::GetOwner(OwnerLocator &aOwnerLocator)
{
#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
Interpreter &interpreter = (aOwnerLocator.GetOwner<Interpreter>());
#else
OT_UNUSED_VARIABLE(aOwnerLocator);
Interpreter &interpreter = Server::sServer->GetInterpreter();
#endif
return interpreter;
}
extern "C" void otCliSetUserCommands(const otCliCommand *aUserCommands, uint8_t aLength)
{
Server::sServer->GetInterpreter().SetUserCommands(aUserCommands, aLength);
}
extern "C" void otCliOutputBytes(const uint8_t *aBytes, uint8_t aLength)
{
Server::sServer->GetInterpreter().OutputBytes(aBytes, aLength);
}
extern "C" void otCliOutputFormat(const char *aFmt, ...)
{
va_list aAp;
va_start(aAp, aFmt);
Server::sServer->OutputFormatV(aFmt, aAp);
va_end(aAp);
}
extern "C" void otCliOutput(const char *aString, uint16_t aLength)
{
Server::sServer->Output(aString, aLength);
}
extern "C" void otCliAppendResult(otError aError)
{
Server::sServer->GetInterpreter().AppendResult(aError);
}
extern "C" void otCliPlatLogv(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, va_list aArgs)
{
OT_UNUSED_VARIABLE(aLogLevel);
OT_UNUSED_VARIABLE(aLogRegion);
VerifyOrExit(Server::sServer != NULL);
Server::sServer->OutputFormatV(aFormat, aArgs);
Server::sServer->OutputFormat("\r\n");
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 // OPENTHREAD_CONFIG_LEGACY_ENABLE