mirror of
https://github.com/espressif/openthread.git
synced 2026-07-08 05:10:25 +00:00
77ea86302e
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.
4241 lines
126 KiB
C++
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), ×tamp, 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, ×tamp, 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
|