blob: 8ef49f8f2c981372a616a55573f4097113df8963 [file] [log] [blame]
/*
* 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 <openthread/config.h>
#include "cli.hpp"
#ifdef OTDLL
#include <assert.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include "utils/wrap_string.h"
#include <openthread/openthread.h>
#include <openthread/commissioner.h>
#include <openthread/icmp6.h>
#include <openthread/joiner.h>
#if OPENTHREAD_FTD
#include <openthread/dataset_ftd.h>
#include <openthread/thread_ftd.h>
#endif
#if OPENTHREAD_ENABLE_BORDER_ROUTER
#include <openthread/border_router.h>
#endif
#ifndef OTDLL
#include <openthread/dhcp6_client.h>
#include <openthread/dhcp6_server.h>
#include <openthread/diag.h>
#include <openthread/icmp6.h>
#include <openthread/platform/uart.h>
#include "openthread-instance.h"
#include "common/new.hpp"
#include "net/ip6.hpp"
#endif
#include "cli_dataset.hpp"
#include "cli_uart.hpp"
#if OPENTHREAD_ENABLE_APPLICATION_COAP
#include "cli_coap.hpp"
#endif
#include "common/encoding.hpp"
using ot::Encoding::BigEndian::HostSwap16;
using ot::Encoding::BigEndian::HostSwap32;
namespace ot {
namespace Cli {
const struct Command Interpreter::sCommands[] =
{
{ "help", &Interpreter::ProcessHelp },
{ "autostart", &Interpreter::ProcessAutoStart },
{ "blacklist", &Interpreter::ProcessBlacklist },
{ "bufferinfo", &Interpreter::ProcessBufferInfo },
{ "channel", &Interpreter::ProcessChannel },
#if OPENTHREAD_FTD
{ "child", &Interpreter::ProcessChild },
{ "childmax", &Interpreter::ProcessChildMax },
#endif
{ "childtimeout", &Interpreter::ProcessChildTimeout },
#if OPENTHREAD_ENABLE_APPLICATION_COAP
{ "coap", &Interpreter::ProcessCoap },
#endif
#if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
{ "commissioner", &Interpreter::ProcessCommissioner },
#endif
#if OPENTHREAD_FTD
{ "contextreusedelay", &Interpreter::ProcessContextIdReuseDelay },
#endif
{ "counter", &Interpreter::ProcessCounters },
{ "dataset", &Interpreter::ProcessDataset },
#if OPENTHREAD_FTD
{ "delaytimermin", &Interpreter::ProcessDelayTimerMin},
#endif
#if OPENTHREAD_ENABLE_DIAG
{ "diag", &Interpreter::ProcessDiag },
#endif
{ "discover", &Interpreter::ProcessDiscover },
#if OPENTHREAD_ENABLE_DNS_CLIENT
{ "dns", &Interpreter::ProcessDns },
#endif
#if OPENTHREAD_FTD
{ "eidcache", &Interpreter::ProcessEidCache },
#endif
{ "eui64", &Interpreter::ProcessEui64 },
#ifdef OPENTHREAD_EXAMPLES_POSIX
{ "exit", &Interpreter::ProcessExit },
#endif
{ "extaddr", &Interpreter::ProcessExtAddress },
{ "extpanid", &Interpreter::ProcessExtPanId },
{ "factoryreset", &Interpreter::ProcessFactoryReset },
{ "hashmacaddr", &Interpreter::ProcessHashMacAddress },
{ "ifconfig", &Interpreter::ProcessIfconfig },
#ifdef OTDLL
{ "instance", &Interpreter::ProcessInstance },
{ "instancelist", &Interpreter::ProcessInstanceList },
#endif
{ "ipaddr", &Interpreter::ProcessIpAddr },
#ifndef OTDLL
{ "ipmaddr", &Interpreter::ProcessIpMulticastAddr },
#endif
#if OPENTHREAD_ENABLE_JOINER
{ "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
{ "linkquality", &Interpreter::ProcessLinkQuality },
{ "masterkey", &Interpreter::ProcessMasterKey },
{ "mode", &Interpreter::ProcessMode },
#if OPENTHREAD_ENABLE_BORDER_ROUTER
{ "netdataregister", &Interpreter::ProcessNetworkDataRegister },
#endif
#if OPENTHREAD_FTD || OPENTHREAD_ENABLE_MTD_NETWORK_DIAGNOSTIC
{ "networkdiagnostic", &Interpreter::ProcessNetworkDiagnostic },
#endif // OPENTHREAD_FTD || OPENTHREAD_ENABLE_MTD_NETWORK_DIAGNOSTIC
#if OPENTHREAD_FTD
{ "networkidtimeout", &Interpreter::ProcessNetworkIdTimeout },
#endif
{ "networkname", &Interpreter::ProcessNetworkName },
{ "panid", &Interpreter::ProcessPanId },
{ "parent", &Interpreter::ProcessParent },
#if OPENTHREAD_FTD
{ "parentpriority", &Interpreter::ProcessParentPriority },
#endif
#ifndef OTDLL
{ "ping", &Interpreter::ProcessPing },
#endif
{ "pollperiod", &Interpreter::ProcessPollPeriod },
#ifndef OTDLL
{ "promiscuous", &Interpreter::ProcessPromiscuous },
#endif
#if OPENTHREAD_ENABLE_BORDER_ROUTER
{ "prefix", &Interpreter::ProcessPrefix },
#endif
#if OPENTHREAD_FTD
{ "pskc", &Interpreter::ProcessPSKc },
{ "releaserouterid", &Interpreter::ProcessReleaseRouterId },
#endif
{ "reset", &Interpreter::ProcessReset },
{ "rloc16", &Interpreter::ProcessRloc16 },
#if OPENTHREAD_ENABLE_BORDER_ROUTER
{ "route", &Interpreter::ProcessRoute },
#endif
#if OPENTHREAD_FTD
{ "router", &Interpreter::ProcessRouter },
{ "routerdowngradethreshold", &Interpreter::ProcessRouterDowngradeThreshold },
{ "routerrole", &Interpreter::ProcessRouterRole },
{ "routerselectionjitter", &Interpreter::ProcessRouterSelectionJitter },
{ "routerupgradethreshold", &Interpreter::ProcessRouterUpgradeThreshold },
#endif
{ "scan", &Interpreter::ProcessScan },
{ "singleton", &Interpreter::ProcessSingleton },
{ "state", &Interpreter::ProcessState },
{ "thread", &Interpreter::ProcessThread },
{ "txpowermax", &Interpreter::ProcessTxPowerMax },
{ "version", &Interpreter::ProcessVersion },
{ "whitelist", &Interpreter::ProcessWhitelist },
};
#ifdef OTDLL
uint32_t otPlatRandomGet(void)
{
return (uint32_t)rand();
}
#else
void otFreeMemory(const void *)
{
// No-op on systems running OpenThread in-proc
}
#endif
template <class T> class otPtr
{
T *ptr;
public:
otPtr(T *_ptr) : ptr(_ptr) { }
~otPtr() { if (ptr) { otFreeMemory(ptr); } }
T *get() const { return ptr; }
operator T *() const { return ptr; }
T *operator->() const { return ptr; }
};
typedef otPtr<const otMacCounters> otMacCountersPtr;
typedef otPtr<const otNetifAddress> otNetifAddressPtr;
typedef otPtr<const uint8_t> otBufferPtr;
typedef otPtr<const char> otStringPtr;
Interpreter::Interpreter(otInstance *aInstance):
#if OPENTHREAD_ENABLE_APPLICATION_COAP
mCoap(*this),
#endif
mServer(NULL),
#ifdef OTDLL
mApiInstance(otApiInit()),
mInstanceIndex(0),
#else
mLength(8),
mCount(1),
mInterval(1000),
mPingTimer(aInstance->mIp6.mTimerScheduler, &Interpreter::s_HandlePingTimer, this),
#if OPENTHREAD_ENABLE_DNS_CLIENT
mResolvingInProgress(0),
#endif
#endif
mInstance(aInstance)
{
#ifdef OTDLL
assert(mApiInstance);
CacheInstances();
#else
memset(mSlaacAddresses, 0, sizeof(mSlaacAddresses));
otSetStateChangedCallback(mInstance, &Interpreter::s_HandleNetifStateChanged, this);
#if OPENTHREAD_FTD || OPENTHREAD_ENABLE_MTD_NETWORK_DIAGNOSTIC
otThreadSetReceiveDiagnosticGetCallback(mInstance, &Interpreter::s_HandleDiagnosticGetResponse, this);
#endif
mIcmpHandler.mReceiveCallback = Interpreter::s_HandleIcmpReceive;
mIcmpHandler.mContext = this;
otIcmp6RegisterHandler(mInstance, &mIcmpHandler);
#if OPENTHREAD_ENABLE_DHCP6_CLIENT
memset(mDhcpAddresses, 0, sizeof(mDhcpAddresses));
#endif // OPENTHREAD_ENABLE_DHCP6_CLIENT
#if OPENTHREAD_ENABLE_DNS_CLIENT
memset(mResolvingHostname, 0, sizeof(mResolvingHostname));
#endif // OPENTHREAD_ENABLE_DNS_CLIENT
#endif
}
int Interpreter::Hex2Bin(const char *aHex, uint8_t *aBin, uint16_t aBinLength)
{
size_t hexLength = strlen(aHex);
const char *hexEnd = aHex + hexLength;
uint8_t *cur = aBin;
uint8_t numChars = hexLength & 1;
uint8_t byte = 0;
if ((hexLength + 1) / 2 > aBinLength)
{
return -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
{
return -1;
}
aHex++;
numChars++;
if (numChars >= 2)
{
numChars = 0;
*cur++ = byte;
byte = 0;
}
else
{
byte <<= 4;
}
}
return static_cast<int>(cur - aBin);
}
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]);
}
}
otError Interpreter::ParseLong(char *argv, long &value)
{
char *endptr;
value = strtol(argv, &endptr, 0);
return (*endptr == '\0') ? OT_ERROR_NONE : OT_ERROR_PARSE;
}
otError Interpreter::ParseUnsignedLong(char *argv, unsigned long &value)
{
char *endptr;
value = strtoul(argv, &endptr, 0);
return (*endptr == '\0') ? OT_ERROR_NONE : OT_ERROR_PARSE;
}
void Interpreter::ProcessHelp(int argc, char *argv[])
{
for (unsigned int i = 0; i < sizeof(sCommands) / sizeof(sCommands[0]); i++)
{
mServer->OutputFormat("%s\r\n", sCommands[i].mName);
}
OT_UNUSED_VARIABLE(argc);
OT_UNUSED_VARIABLE(argv);
}
void Interpreter::ProcessAutoStart(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
if (argc == 0)
{
if (otThreadGetAutoStart(mInstance))
{
mServer->OutputFormat("true\r\n");
}
else
{
mServer->OutputFormat("false\r\n");
}
}
else if (strcmp(argv[0], "true") == 0)
{
error = otThreadSetAutoStart(mInstance, true);
}
else if (strcmp(argv[0], "false") == 0)
{
error = otThreadSetAutoStart(mInstance, false);
}
else
{
error = OT_ERROR_INVALID_ARGS;
}
AppendResult(error);
}
void Interpreter::ProcessBlacklist(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
otMacBlacklistEntry entry;
int argcur = 0;
uint8_t extAddr[8];
if (argcur >= argc)
{
if (otLinkIsBlacklistEnabled(mInstance))
{
mServer->OutputFormat("Enabled\r\n");
}
else
{
mServer->OutputFormat("Disabled\r\n");
}
for (uint8_t i = 0; ; i++)
{
if (otLinkGetBlacklistEntry(mInstance, i, &entry) != OT_ERROR_NONE)
{
break;
}
if (entry.mValid == false)
{
continue;
}
OutputBytes(entry.mExtAddress.m8, OT_EXT_ADDRESS_SIZE);
mServer->OutputFormat("\r\n");
}
}
else if (strcmp(argv[argcur], "add") == 0)
{
VerifyOrExit(++argcur < argc, error = OT_ERROR_PARSE);
VerifyOrExit(Hex2Bin(argv[argcur], extAddr, sizeof(extAddr)) == sizeof(extAddr), error = OT_ERROR_PARSE);
otLinkAddBlacklist(mInstance, extAddr);
VerifyOrExit(otLinkAddBlacklist(mInstance, extAddr) == OT_ERROR_NONE, error = OT_ERROR_PARSE);
}
else if (strcmp(argv[argcur], "clear") == 0)
{
otLinkClearBlacklist(mInstance);
}
else if (strcmp(argv[argcur], "disable") == 0)
{
otLinkSetBlacklistEnabled(mInstance, false);
}
else if (strcmp(argv[argcur], "enable") == 0)
{
otLinkSetBlacklistEnabled(mInstance, true);
}
else if (strcmp(argv[argcur], "remove") == 0)
{
VerifyOrExit(++argcur < argc, error = OT_ERROR_PARSE);
VerifyOrExit(Hex2Bin(argv[argcur], extAddr, sizeof(extAddr)) == sizeof(extAddr), error = OT_ERROR_PARSE);
otLinkRemoveBlacklist(mInstance, extAddr);
}
exit:
AppendResult(error);
}
void Interpreter::ProcessBufferInfo(int argc, char *argv[])
{
otBufferInfo bufferInfo;
OT_UNUSED_VARIABLE(argc);
OT_UNUSED_VARIABLE(argv);
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);
AppendResult(OT_ERROR_NONE);
}
void Interpreter::ProcessChannel(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
long value;
if (argc == 0)
{
mServer->OutputFormat("%d\r\n", otLinkGetChannel(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(argv[0], value));
error = otLinkSetChannel(mInstance, static_cast<uint8_t>(value));
}
exit:
AppendResult(error);
}
#if OPENTHREAD_FTD
void Interpreter::ProcessChild(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
otChildInfo childInfo;
uint8_t maxChildren;
long value;
bool isTable = false;
VerifyOrExit(argc > 0, error = OT_ERROR_PARSE);
if (strcmp(argv[0], "list") == 0 || (isTable = (strcmp(argv[0], "table") == 0)))
{
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 (uint8_t i = 0; i < maxChildren ; i++)
{
switch (otThreadGetChildInfoByIndex(mInstance, i, &childInfo))
{
case OT_ERROR_NONE:
break;
case OT_ERROR_NOT_FOUND:
continue;
default:
mServer->OutputFormat("\r\n");
ExitNow();
}
if (childInfo.mTimeout > 0)
{
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.mFullFunction);
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);
}
}
}
ExitNow();
}
SuccessOrExit(error = ParseLong(argv[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.mFullFunction)
{
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::ProcessChildMax(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
long value;
if (argc == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetMaxAllowedChildren(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(argv[0], value));
SuccessOrExit(error = otThreadSetMaxAllowedChildren(mInstance, static_cast<uint8_t>(value)));
}
exit:
AppendResult(error);
}
#endif // OPENTHREAD_FTD
void Interpreter::ProcessChildTimeout(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
long value;
if (argc == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetChildTimeout(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(argv[0], value));
otThreadSetChildTimeout(mInstance, static_cast<uint32_t>(value));
}
exit:
AppendResult(error);
}
#if OPENTHREAD_ENABLE_APPLICATION_COAP
void Interpreter::ProcessCoap(int argc, char *argv[])
{
otError error;
error = mCoap.Process(argc, argv);
AppendResult(error);
}
#endif // OPENTHREAD_ENABLE_APPLICATION_COAP
#if OPENTHREAD_FTD
void Interpreter::ProcessContextIdReuseDelay(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
long value;
if (argc == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetContextIdReuseDelay(mInstance));
}
else
{
SuccessOrExit(ParseLong(argv[0], value));
otThreadSetContextIdReuseDelay(mInstance, static_cast<uint32_t>(value));
}
exit:
AppendResult(error);
}
#endif // OPENTHREAD_FTD
void Interpreter::ProcessCounters(int argc, char *argv[])
{
if (argc == 0)
{
mServer->OutputFormat("mac\r\n");
mServer->OutputFormat("Done\r\n");
}
else
{
if (strcmp(argv[0], "mac") == 0)
{
otMacCountersPtr counters(otLinkGetCounters(mInstance));
mServer->OutputFormat("TxTotal: %d\r\n", counters->mTxTotal);
mServer->OutputFormat(" TxUnicast: %d\r\n", counters->mTxUnicast);
mServer->OutputFormat(" TxBroadcast: %d\r\n", counters->mTxBroadcast);
mServer->OutputFormat(" TxAckRequested: %d\r\n", counters->mTxAckRequested);
mServer->OutputFormat(" TxAcked: %d\r\n", counters->mTxAcked);
mServer->OutputFormat(" TxNoAckRequested: %d\r\n", counters->mTxNoAckRequested);
mServer->OutputFormat(" TxData: %d\r\n", counters->mTxData);
mServer->OutputFormat(" TxDataPoll: %d\r\n", counters->mTxDataPoll);
mServer->OutputFormat(" TxBeacon: %d\r\n", counters->mTxBeacon);
mServer->OutputFormat(" TxBeaconRequest: %d\r\n", counters->mTxBeaconRequest);
mServer->OutputFormat(" TxOther: %d\r\n", counters->mTxOther);
mServer->OutputFormat(" TxRetry: %d\r\n", counters->mTxRetry);
mServer->OutputFormat(" TxErrCca: %d\r\n", counters->mTxErrCca);
mServer->OutputFormat("RxTotal: %d\r\n", counters->mRxTotal);
mServer->OutputFormat(" RxUnicast: %d\r\n", counters->mRxUnicast);
mServer->OutputFormat(" RxBroadcast: %d\r\n", counters->mRxBroadcast);
mServer->OutputFormat(" RxData: %d\r\n", counters->mRxData);
mServer->OutputFormat(" RxDataPoll: %d\r\n", counters->mRxDataPoll);
mServer->OutputFormat(" RxBeacon: %d\r\n", counters->mRxBeacon);
mServer->OutputFormat(" RxBeaconRequest: %d\r\n", counters->mRxBeaconRequest);
mServer->OutputFormat(" RxOther: %d\r\n", counters->mRxOther);
mServer->OutputFormat(" RxWhitelistFiltered: %d\r\n", counters->mRxWhitelistFiltered);
mServer->OutputFormat(" RxDestAddrFiltered: %d\r\n", counters->mRxDestAddrFiltered);
mServer->OutputFormat(" RxDuplicated: %d\r\n", counters->mRxDuplicated);
mServer->OutputFormat(" RxErrNoFrame: %d\r\n", counters->mRxErrNoFrame);
mServer->OutputFormat(" RxErrNoUnknownNeighbor: %d\r\n", counters->mRxErrUnknownNeighbor);
mServer->OutputFormat(" RxErrInvalidSrcAddr: %d\r\n", counters->mRxErrInvalidSrcAddr);
mServer->OutputFormat(" RxErrSec: %d\r\n", counters->mRxErrSec);
mServer->OutputFormat(" RxErrFcs: %d\r\n", counters->mRxErrFcs);
mServer->OutputFormat(" RxErrOther: %d\r\n", counters->mRxErrOther);
}
}
}
void Interpreter::ProcessDataset(int argc, char *argv[])
{
otError error;
error = Dataset::Process(mInstance, argc, argv, *mServer);
AppendResult(error);
}
#if OPENTHREAD_FTD
void Interpreter::ProcessDelayTimerMin(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
if (argc == 0)
{
mServer->OutputFormat("%d\r\n", (otDatasetGetDelayTimerMinimal(mInstance) / 1000));
}
else if (argc == 1)
{
unsigned long value;
SuccessOrExit(error = ParseUnsignedLong(argv[0], value));
SuccessOrExit(error = otDatasetSetDelayTimerMinimal(mInstance, static_cast<uint32_t>(value * 1000)));
}
else
{
error = OT_ERROR_INVALID_ARGS;
}
exit:
AppendResult(error);
}
#endif
void Interpreter::ProcessDiscover(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
uint32_t scanChannels = 0;
long value;
if (argc > 0)
{
SuccessOrExit(error = ParseLong(argv[0], value));
scanChannels = 1 << value;
}
SuccessOrExit(error = otThreadDiscover(mInstance, scanChannels, OT_PANID_BROADCAST, false, false,
&Interpreter::s_HandleActiveScanResult, this));
mServer->OutputFormat("| J | Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI |\r\n");
mServer->OutputFormat("+---+------------------+------------------+------+------------------+----+-----+-----+\r\n");
return;
exit:
AppendResult(error);
}
#if OPENTHREAD_ENABLE_DNS_CLIENT
void Interpreter::ProcessDns(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
long port = OT_DNS_DEFAULT_DNS_SERVER_PORT;
Ip6::MessageInfo messageInfo;
otDnsQuery query;
VerifyOrExit(argc > 0, error = OT_ERROR_INVALID_ARGS);
if (strcmp(argv[0], "resolve") == 0)
{
VerifyOrExit(!mResolvingInProgress, error = OT_ERROR_BUSY);
VerifyOrExit(argc > 1, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(strlen(argv[1]) < OT_DNS_MAX_HOSTNAME_LENGTH, error = OT_ERROR_INVALID_ARGS);
strcpy(mResolvingHostname, argv[1]);
memset(&messageInfo, 0, sizeof(messageInfo));
messageInfo.mInterfaceId = OT_NETIF_INTERFACE_ID_THREAD;
if (argc > 2)
{
SuccessOrExit(error = messageInfo.GetPeerAddr().FromString(argv[2]));
}
else
{
// Use IPv6 address of default DNS server.
SuccessOrExit(error = messageInfo.GetPeerAddr().FromString(OT_DNS_DEFAULT_DNS_SERVER_IP));
}
if (argc > 3)
{
SuccessOrExit(error = ParseLong(argv[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::s_HandleDnsResponse, this));
mResolvingInProgress = true;
return;
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
exit:
AppendResult(error);
}
void Interpreter::s_HandleDnsResponse(void *aContext, const char *aHostname, otIp6Address *aAddress,
uint32_t aTtl, otError aResult)
{
static_cast<Interpreter *>(aContext)->HandleDnsResponse(aHostname,
*static_cast<Ip6::Address *>(aAddress),
aTtl, aResult);
}
void Interpreter::HandleDnsResponse(const char *aHostname, Ip6::Address &aAddress, uint32_t aTtl, otError aResult)
{
mServer->OutputFormat("DNS response for %s - ", aHostname);
if (aResult == OT_ERROR_NONE)
{
mServer->OutputFormat("[%x:%x:%x:%x:%x:%x:%x:%x] TTL: %d\r\n",
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]),
aTtl);
}
else
{
AppendResult(aResult);
}
mResolvingInProgress = false;
}
#endif
#if OPENTHREAD_FTD
void Interpreter::ProcessEidCache(int argc, char *argv[])
{
otEidCacheEntry entry;
for (uint8_t i = 0; ; i++)
{
SuccessOrExit(otThreadGetEidCacheEntry(mInstance, i, &entry));
if (entry.mValid == false)
{
continue;
}
mServer->OutputFormat("%x:%x:%x:%x:%x:%x:%x:%x %04x\r\n",
HostSwap16(entry.mTarget.mFields.m16[0]),
HostSwap16(entry.mTarget.mFields.m16[1]),
HostSwap16(entry.mTarget.mFields.m16[2]),
HostSwap16(entry.mTarget.mFields.m16[3]),
HostSwap16(entry.mTarget.mFields.m16[4]),
HostSwap16(entry.mTarget.mFields.m16[5]),
HostSwap16(entry.mTarget.mFields.m16[6]),
HostSwap16(entry.mTarget.mFields.m16[7]),
entry.mRloc16);
}
exit:
OT_UNUSED_VARIABLE(argc);
OT_UNUSED_VARIABLE(argv);
AppendResult(OT_ERROR_NONE);
}
#endif // OPENTHREAD_FTD
void Interpreter::ProcessEui64(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
otExtAddress extAddress;
VerifyOrExit(argc == 0, error = OT_ERROR_PARSE);
otLinkGetFactoryAssignedIeeeEui64(mInstance, &extAddress);
OutputBytes(extAddress.m8, OT_EXT_ADDRESS_SIZE);
mServer->OutputFormat("\r\n");
exit:
OT_UNUSED_VARIABLE(argv);
AppendResult(error);
}
void Interpreter::ProcessExtAddress(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
if (argc == 0)
{
otBufferPtr extAddress(otLinkGetExtendedAddress(mInstance));
OutputBytes(extAddress, OT_EXT_ADDRESS_SIZE);
mServer->OutputFormat("\r\n");
}
else
{
otExtAddress extAddress;
VerifyOrExit(Hex2Bin(argv[0], extAddress.m8, sizeof(otExtAddress)) >= 0, error = OT_ERROR_PARSE);
error = otLinkSetExtendedAddress(mInstance, &extAddress);
}
exit:
AppendResult(error);
}
#ifdef OPENTHREAD_EXAMPLES_POSIX
void Interpreter::ProcessExit(int argc, char *argv[])
{
exit(0);
OT_UNUSED_VARIABLE(argc);
OT_UNUSED_VARIABLE(argv);
}
#endif
void Interpreter::ProcessExtPanId(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
if (argc == 0)
{
otBufferPtr extPanId(otThreadGetExtendedPanId(mInstance));
OutputBytes(extPanId, OT_EXT_PAN_ID_SIZE);
mServer->OutputFormat("\r\n");
}
else
{
uint8_t extPanId[8];
VerifyOrExit(Hex2Bin(argv[0], extPanId, sizeof(extPanId)) >= 0, error = OT_ERROR_PARSE);
error = otThreadSetExtendedPanId(mInstance, extPanId);
}
exit:
AppendResult(error);
}
void Interpreter::ProcessFactoryReset(int argc, char *argv[])
{
otInstanceFactoryReset(mInstance);
OT_UNUSED_VARIABLE(argc);
OT_UNUSED_VARIABLE(argv);
}
void Interpreter::ProcessHashMacAddress(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
otExtAddress hashMacAddress;
VerifyOrExit(argc == 0, error = OT_ERROR_PARSE);
otLinkGetJoinerId(mInstance, &hashMacAddress);
OutputBytes(hashMacAddress.m8, OT_EXT_ADDRESS_SIZE);
mServer->OutputFormat("\r\n");
exit:
OT_UNUSED_VARIABLE(argv);
AppendResult(error);
}
void Interpreter::ProcessIfconfig(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
if (argc == 0)
{
if (otIp6IsEnabled(mInstance))
{
mServer->OutputFormat("up\r\n");
}
else
{
mServer->OutputFormat("down\r\n");
}
}
else if (strcmp(argv[0], "up") == 0)
{
SuccessOrExit(error = otIp6SetEnabled(mInstance, true));
}
else if (strcmp(argv[0], "down") == 0)
{
SuccessOrExit(error = otIp6SetEnabled(mInstance, false));
}
exit:
AppendResult(error);
}
otError Interpreter::ProcessIpAddrAdd(int argc, char *argv[])
{
otError error;
otNetifAddress aAddress;
VerifyOrExit(argc > 0, error = OT_ERROR_PARSE);
SuccessOrExit(error = otIp6AddressFromString(argv[0], &aAddress.mAddress));
aAddress.mPrefixLength = 64;
aAddress.mPreferred = true;
aAddress.mValid = true;
error = otIp6AddUnicastAddress(mInstance, &aAddress);
exit:
return error;
}
otError Interpreter::ProcessIpAddrDel(int argc, char *argv[])
{
otError error;
struct otIp6Address address;
VerifyOrExit(argc > 0, error = OT_ERROR_PARSE);
SuccessOrExit(error = otIp6AddressFromString(argv[0], &address));
error = otIp6RemoveUnicastAddress(mInstance, &address);
exit:
return error;
}
void Interpreter::ProcessIpAddr(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
if (argc == 0)
{
otNetifAddressPtr unicastAddrs(otIp6GetUnicastAddresses(mInstance));
for (const otNetifAddress *addr = unicastAddrs; addr; addr = addr->mNext)
{
mServer->OutputFormat("%x:%x:%x:%x:%x:%x:%x:%x\r\n",
HostSwap16(addr->mAddress.mFields.m16[0]),
HostSwap16(addr->mAddress.mFields.m16[1]),
HostSwap16(addr->mAddress.mFields.m16[2]),
HostSwap16(addr->mAddress.mFields.m16[3]),
HostSwap16(addr->mAddress.mFields.m16[4]),
HostSwap16(addr->mAddress.mFields.m16[5]),
HostSwap16(addr->mAddress.mFields.m16[6]),
HostSwap16(addr->mAddress.mFields.m16[7]));
}
}
else
{
if (strcmp(argv[0], "add") == 0)
{
SuccessOrExit(error = ProcessIpAddrAdd(argc - 1, argv + 1));
}
else if (strcmp(argv[0], "del") == 0)
{
SuccessOrExit(error = ProcessIpAddrDel(argc - 1, argv + 1));
}
}
exit:
AppendResult(error);
}
#ifndef OTDLL
otError Interpreter::ProcessIpMulticastAddrAdd(int argc, char *argv[])
{
otError error;
struct otIp6Address address;
VerifyOrExit(argc > 0, error = OT_ERROR_PARSE);
SuccessOrExit(error = otIp6AddressFromString(argv[0], &address));
error = otIp6SubscribeMulticastAddress(mInstance, &address);
exit:
return error;
}
otError Interpreter::ProcessIpMulticastAddrDel(int argc, char *argv[])
{
otError error;
struct otIp6Address address;
VerifyOrExit(argc > 0, error = OT_ERROR_PARSE);
SuccessOrExit(error = otIp6AddressFromString(argv[0], &address));
error = otIp6UnsubscribeMulticastAddress(mInstance, &address);
exit:
return error;
}
otError Interpreter::ProcessMulticastPromiscuous(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
if (argc == 0)
{
if (otIp6IsMulticastPromiscuousEnabled(mInstance))
{
mServer->OutputFormat("Enabled\r\n");
}
else
{
mServer->OutputFormat("Disabled\r\n");
}
}
else
{
if (strcmp(argv[0], "enable") == 0)
{
otIp6SetMulticastPromiscuousEnabled(mInstance, true);
}
else if (strcmp(argv[0], "disable") == 0)
{
otIp6SetMulticastPromiscuousEnabled(mInstance, false);
}
else
{
ExitNow(error = OT_ERROR_PARSE);
}
}
exit:
return error;
}
void Interpreter::ProcessIpMulticastAddr(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
if (argc == 0)
{
for (const otNetifMulticastAddress *addr = otIp6GetMulticastAddresses(mInstance); addr; addr = addr->mNext)
{
mServer->OutputFormat("%x:%x:%x:%x:%x:%x:%x:%x\r\n",
HostSwap16(addr->mAddress.mFields.m16[0]),
HostSwap16(addr->mAddress.mFields.m16[1]),
HostSwap16(addr->mAddress.mFields.m16[2]),
HostSwap16(addr->mAddress.mFields.m16[3]),
HostSwap16(addr->mAddress.mFields.m16[4]),
HostSwap16(addr->mAddress.mFields.m16[5]),
HostSwap16(addr->mAddress.mFields.m16[6]),
HostSwap16(addr->mAddress.mFields.m16[7]));
}
}
else
{
if (strcmp(argv[0], "add") == 0)
{
SuccessOrExit(error = ProcessIpMulticastAddrAdd(argc - 1, argv + 1));
}
else if (strcmp(argv[0], "del") == 0)
{
SuccessOrExit(error = ProcessIpMulticastAddrDel(argc - 1, argv + 1));
}
else if (strcmp(argv[0], "promiscuous") == 0)
{
SuccessOrExit(error = ProcessMulticastPromiscuous(argc - 1, argv + 1));
}
}
exit:
AppendResult(error);
}
#endif
void Interpreter::ProcessKeySequence(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
long value;
VerifyOrExit(argc == 1 || argc == 2, error = OT_ERROR_PARSE);
if (strcmp(argv[0], "counter") == 0)
{
if (argc == 1)
{
mServer->OutputFormat("%d\r\n", otThreadGetKeySequenceCounter(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(argv[1], value));
otThreadSetKeySequenceCounter(mInstance, static_cast<uint32_t>(value));
}
}
else if (strcmp(argv[0], "guardtime") == 0)
{
if (argc == 1)
{
mServer->OutputFormat("%d\r\n", otThreadGetKeySwitchGuardTime(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(argv[1], value));
otThreadSetKeySwitchGuardTime(mInstance, static_cast<uint32_t>(value));
}
}
exit:
AppendResult(error);
}
void Interpreter::ProcessLeaderData(int argc, char *argv[])
{
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:
OT_UNUSED_VARIABLE(argc);
OT_UNUSED_VARIABLE(argv);
AppendResult(error);
}
#if OPENTHREAD_FTD
void Interpreter::ProcessLeaderPartitionId(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
unsigned long value;
if (argc == 0)
{
mServer->OutputFormat("%u\r\n", otThreadGetLocalLeaderPartitionId(mInstance));
}
else
{
SuccessOrExit(error = ParseUnsignedLong(argv[0], value));
otThreadSetLocalLeaderPartitionId(mInstance, static_cast<uint32_t>(value));
}
exit:
AppendResult(error);
}
void Interpreter::ProcessLeaderWeight(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
long value;
if (argc == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetLocalLeaderWeight(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(argv[0], value));
otThreadSetLocalLeaderWeight(mInstance, static_cast<uint8_t>(value));
}
exit:
AppendResult(error);
}
#endif // OPENTHREAD_FTD
void Interpreter::ProcessLinkQuality(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
uint8_t extAddress[8];
uint8_t linkQuality;
long value;
VerifyOrExit(argc > 0, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(Hex2Bin(argv[0], extAddress, OT_EXT_ADDRESS_SIZE) >= 0, error = OT_ERROR_PARSE);
if (argc == 1)
{
VerifyOrExit(otLinkGetAssignLinkQuality(mInstance, extAddress, &linkQuality) == OT_ERROR_NONE,
error = OT_ERROR_INVALID_ARGS);
mServer->OutputFormat("%d\r\n", linkQuality);
}
else
{
SuccessOrExit(error = ParseLong(argv[1], value));
otLinkSetAssignLinkQuality(mInstance, extAddress, static_cast<uint8_t>(value));
}
exit:
AppendResult(error);
}
#if OPENTHREAD_FTD
void Interpreter::ProcessPSKc(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
if (argc == 0)
{
const uint8_t *currentPSKc = otThreadGetPSKc(mInstance);
for (int i = 0; i < OT_PSKC_MAX_SIZE; i++)
{
mServer->OutputFormat("%02x", currentPSKc[i]);
}
mServer->OutputFormat("\r\n");
}
else
{
uint8_t newPSKc[OT_PSKC_MAX_SIZE];
VerifyOrExit(Hex2Bin(argv[0], newPSKc, sizeof(newPSKc)) == OT_PSKC_MAX_SIZE, error = OT_ERROR_PARSE);
SuccessOrExit(error = otThreadSetPSKc(mInstance, newPSKc));
}
exit:
AppendResult(error);
}
#endif
void Interpreter::ProcessMasterKey(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
if (argc == 0)
{
otBufferPtr 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(argv[0], key.m8, sizeof(key.m8)) == OT_MASTER_KEY_SIZE, error = OT_ERROR_PARSE);
SuccessOrExit(error = otThreadSetMasterKey(mInstance, &key));
}
exit:
AppendResult(error);
}
void Interpreter::ProcessMode(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
otLinkModeConfig linkMode;
memset(&linkMode, 0, sizeof(otLinkModeConfig));
if (argc == 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 = argv[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_PARSE);
}
}
SuccessOrExit(error = otThreadSetLinkMode(mInstance, linkMode));
}
exit:
AppendResult(error);
}
#if OPENTHREAD_ENABLE_BORDER_ROUTER
void Interpreter::ProcessNetworkDataRegister(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
SuccessOrExit(error = otBorderRouterRegister(mInstance));
exit:
OT_UNUSED_VARIABLE(argc);
OT_UNUSED_VARIABLE(argv);
AppendResult(error);
}
#endif // OPENTHREAD_ENABLE_BORDER_ROUTER
#if OPENTHREAD_FTD
void Interpreter::ProcessNetworkIdTimeout(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
long value;
if (argc == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetNetworkIdTimeout(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(argv[0], value));
otThreadSetNetworkIdTimeout(mInstance, static_cast<uint8_t>(value));
}
exit:
AppendResult(error);
}
#endif // OPENTHREAD_FTD
void Interpreter::ProcessNetworkName(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
if (argc == 0)
{
otStringPtr networkName(otThreadGetNetworkName(mInstance));
mServer->OutputFormat("%.*s\r\n", OT_NETWORK_NAME_MAX_SIZE, (const char *)networkName);
}
else
{
SuccessOrExit(error = otThreadSetNetworkName(mInstance, argv[0]));
}
exit:
AppendResult(error);
}
void Interpreter::ProcessPanId(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
long value;
if (argc == 0)
{
mServer->OutputFormat("%04x\r\n", otLinkGetPanId(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(argv[0], value));
error = otLinkSetPanId(mInstance, static_cast<otPanId>(value));
}
exit:
AppendResult(error);
}
void Interpreter::ProcessParent(int argc, char *argv[])
{
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:
OT_UNUSED_VARIABLE(argc);
OT_UNUSED_VARIABLE(argv);
AppendResult(error);
}
#if OPENTHREAD_FTD
void Interpreter::ProcessParentPriority(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
long value;
if (argc == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetParentPriority(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(argv[0], value));
error = otThreadSetParentPriority(mInstance, static_cast<int8_t>(value));
}
exit:
AppendResult(error);
}
#endif
#ifndef OTDLL
void Interpreter::s_HandleIcmpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo,
const otIcmp6Header *aIcmpHeader)
{
static_cast<Interpreter *>(aContext)->HandleIcmpReceive(*static_cast<Message *>(aMessage),
*static_cast<const Ip6::MessageInfo *>(aMessageInfo),
*static_cast<const Ip6::IcmpHeader *>(aIcmpHeader));
}
void Interpreter::HandleIcmpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo,
const otIcmp6Header &aIcmpHeader)
{
uint32_t timestamp = 0;
VerifyOrExit(aIcmpHeader.mType == OT_ICMP6_TYPE_ECHO_REPLY);
mServer->OutputFormat("%d bytes from ", aMessage.GetLength() - aMessage.GetOffset());
mServer->OutputFormat("%x:%x:%x:%x:%x:%x:%x:%x",
HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[0]),
HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[1]),
HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[2]),
HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[3]),
HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[4]),
HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[5]),
HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[6]),
HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[7]));
mServer->OutputFormat(": icmp_seq=%d hlim=%d", HostSwap16(aIcmpHeader.mData.m16[1]), aMessageInfo.mHopLimit);
if (aMessage.Read(aMessage.GetOffset(), sizeof(uint32_t), &timestamp) >=
static_cast<int>(sizeof(uint32_t)))
{
mServer->OutputFormat(" time=%dms", Timer::GetNow() - HostSwap32(timestamp));
}
mServer->OutputFormat("\r\n");
exit:
return;
}
void Interpreter::ProcessPing(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
uint8_t index = 1;
long value;
VerifyOrExit(argc > 0, error = OT_ERROR_PARSE);
if (strcmp(argv[0], "stop") == 0)
{
if (!mPingTimer.IsRunning())
{
error = OT_ERROR_INVALID_STATE;
}
else
{
mPingTimer.Stop();
}
ExitNow();
}
VerifyOrExit(!mPingTimer.IsRunning(), error = OT_ERROR_BUSY);
memset(&mMessageInfo, 0, sizeof(mMessageInfo));
SuccessOrExit(error = mMessageInfo.GetPeerAddr().FromString(argv[0]));
mMessageInfo.mInterfaceId = OT_NETIF_INTERFACE_ID_THREAD;
mLength = 8;
mCount = 1;
mInterval = 1000;
while (index < argc)
{
SuccessOrExit(error = ParseLong(argv[index], value));
switch (index)
{
case 1:
mLength = (uint16_t)value;
break;
case 2:
mCount = (uint16_t)value;
break;
case 3:
mInterval = (uint32_t)value;
mInterval = mInterval * 1000;
break;
default:
ExitNow(error = OT_ERROR_PARSE);
}
index++;
}
HandlePingTimer();
return;
exit:
AppendResult(error);
}
void Interpreter::s_HandlePingTimer(Timer &aTimer)
{
GetOwner(aTimer).HandlePingTimer();
}
void Interpreter::HandlePingTimer()
{
otError error = OT_ERROR_NONE;
uint32_t timestamp = HostSwap32(Timer::GetNow());
otMessage *message;
const otMessageInfo *messageInfo = static_cast<const otMessageInfo *>(&mMessageInfo);
VerifyOrExit((message = otIp6NewMessage(mInstance, true)) != NULL, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = otMessageAppend(message, &timestamp, sizeof(timestamp)));
SuccessOrExit(error = otMessageSetLength(message, mLength));
SuccessOrExit(error = otIcmp6SendEchoRequest(mInstance, message, messageInfo, 1));
mCount--;
exit:
if (error != OT_ERROR_NONE && message != NULL)
{
otMessageFree(message);
}
if (mCount)
{
mPingTimer.Start(mInterval);
}
}
#endif
void Interpreter::ProcessPollPeriod(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
long value;
if (argc == 0)
{
mServer->OutputFormat("%d\r\n", (otLinkGetPollPeriod(mInstance) / 1000)); // ms->s
}
else
{
SuccessOrExit(error = ParseLong(argv[0], value));
otLinkSetPollPeriod(mInstance, static_cast<uint32_t>(value * 1000)); // s->ms
}
exit:
AppendResult(error);
}
#ifndef OTDLL
void Interpreter::ProcessPromiscuous(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
if (argc == 0)
{
if (otLinkIsPromiscuous(mInstance) && otPlatRadioGetPromiscuous(mInstance))
{
mServer->OutputFormat("Enabled\r\n");
}
else
{
mServer->OutputFormat("Disabled\r\n");
}
}
else
{
if (strcmp(argv[0], "enable") == 0)
{
otLinkSetPcapCallback(mInstance, &s_HandleLinkPcapReceive, this);
SuccessOrExit(error = otLinkSetPromiscuous(mInstance, true));
}
else if (strcmp(argv[0], "disable") == 0)
{
otLinkSetPcapCallback(mInstance, NULL, NULL);
SuccessOrExit(error = otLinkSetPromiscuous(mInstance, false));
}
}
exit:
AppendResult(error);
}
void Interpreter::s_HandleLinkPcapReceive(const otRadioFrame *aFrame, void *aContext)
{
static_cast<Interpreter *>(aContext)->HandleLinkPcapReceive(aFrame);
}
void Interpreter::HandleLinkPcapReceive(const otRadioFrame *aFrame)
{
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");
}
#endif
#if OPENTHREAD_ENABLE_BORDER_ROUTER
otError Interpreter::ProcessPrefixAdd(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
otBorderRouterConfig config;
int argcur = 0;
memset(&config, 0, sizeof(otBorderRouterConfig));
char *prefixLengthStr;
char *endptr;
if ((prefixLengthStr = strchr(argv[argcur], '/')) == NULL)
{
ExitNow();
}
*prefixLengthStr++ = '\0';
SuccessOrExit(error = otIp6AddressFromString(argv[argcur], &config.mPrefix.mPrefix));
config.mPrefix.mLength = static_cast<uint8_t>(strtol(prefixLengthStr, &endptr, 0));
if (*endptr != '\0')
{
ExitNow(error = OT_ERROR_PARSE);
}
argcur++;
for (; argcur < argc; argcur++)
{
if (strcmp(argv[argcur], "high") == 0)
{
config.mPreference = OT_ROUTE_PREFERENCE_HIGH;
}
else if (strcmp(argv[argcur], "med") == 0)
{
config.mPreference = OT_ROUTE_PREFERENCE_MED;
}
else if (strcmp(argv[argcur], "low") == 0)
{
config.mPreference = OT_ROUTE_PREFERENCE_LOW;
}
else
{
for (char *arg = argv[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_PARSE);
}
}
}
}
error = otBorderRouterAddOnMeshPrefix(mInstance, &config);
exit:
return error;
}
otError Interpreter::ProcessPrefixRemove(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
struct otIp6Prefix prefix;
int argcur = 0;
memset(&prefix, 0, sizeof(otIp6Prefix));
char *prefixLengthStr;
char *endptr;
if ((prefixLengthStr = strchr(argv[argcur], '/')) == NULL)
{
ExitNow();
}
*prefixLengthStr++ = '\0';
SuccessOrExit(error = otIp6AddressFromString(argv[argcur], &prefix.mPrefix));
prefix.mLength = static_cast<uint8_t>(strtol(prefixLengthStr, &endptr, 0));
if (*endptr != '\0')
{
ExitNow(error = OT_ERROR_PARSE);
}
error = otBorderRouterRemoveOnMeshPrefix(mInstance, &prefix);
exit:
OT_UNUSED_VARIABLE(argc);
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(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
if (argc == 0)
{
SuccessOrExit(error = ProcessPrefixList());
}
else if (strcmp(argv[0], "add") == 0)
{
SuccessOrExit(error = ProcessPrefixAdd(argc - 1, argv + 1));
}
else if (strcmp(argv[0], "remove") == 0)
{
SuccessOrExit(error = ProcessPrefixRemove(argc - 1, argv + 1));
}
else
{
ExitNow(error = OT_ERROR_PARSE);
}
exit:
AppendResult(error);
}
#endif // OPENTHREAD_ENABLE_BORDER_ROUTER
#if OPENTHREAD_FTD
void Interpreter::ProcessReleaseRouterId(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
long value;
VerifyOrExit(argc > 0, error = OT_ERROR_PARSE);
SuccessOrExit(error = ParseLong(argv[0], value));
SuccessOrExit(error = otThreadReleaseRouterId(mInstance, static_cast<uint8_t>(value)));
exit:
AppendResult(error);
}
#endif // OPENTHREAD_FTD
void Interpreter::ProcessReset(int argc, char *argv[])
{
otInstanceReset(mInstance);
OT_UNUSED_VARIABLE(argc);
OT_UNUSED_VARIABLE(argv);
}
void Interpreter::ProcessRloc16(int argc, char *argv[])
{
mServer->OutputFormat("%04x\r\n", otThreadGetRloc16(mInstance));
mServer->OutputFormat("Done\r\n");
OT_UNUSED_VARIABLE(argc);
OT_UNUSED_VARIABLE(argv);
}
#if OPENTHREAD_ENABLE_BORDER_ROUTER
otError Interpreter::ProcessRouteAdd(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
otExternalRouteConfig config;
int argcur = 0;
memset(&config, 0, sizeof(otExternalRouteConfig));
char *prefixLengthStr;
char *endptr;
VerifyOrExit(argc > 0, error = OT_ERROR_PARSE);
if ((prefixLengthStr = strchr(argv[argcur], '/')) == NULL)
{
ExitNow();
}
*prefixLengthStr++ = '\0';
SuccessOrExit(error = otIp6AddressFromString(argv[argcur], &config.mPrefix.mPrefix));
config.mPrefix.mLength = static_cast<uint8_t>(strtol(prefixLengthStr, &endptr, 0));
if (*endptr != '\0')
{
ExitNow(error = OT_ERROR_PARSE);
}
argcur++;
for (; argcur < argc; argcur++)
{
if (strcmp(argv[argcur], "s") == 0)
{
config.mStable = true;
}
else if (strcmp(argv[argcur], "high") == 0)
{
config.mPreference = OT_ROUTE_PREFERENCE_HIGH;
}
else if (strcmp(argv[argcur], "med") == 0)
{
config.mPreference = OT_ROUTE_PREFERENCE_MED;
}
else if (strcmp(argv[argcur], "low") == 0)
{
config.mPreference = OT_ROUTE_PREFERENCE_LOW;
}
else
{
ExitNow(error = OT_ERROR_PARSE);
}
}
error = otBorderRouterAddRoute(mInstance, &config);
exit:
return error;
}
otError Interpreter::ProcessRouteRemove(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
struct otIp6Prefix prefix;
int argcur = 0;
memset(&prefix, 0, sizeof(struct otIp6Prefix));
char *prefixLengthStr;
char *endptr;
VerifyOrExit(argc > 0, error = OT_ERROR_PARSE);
if ((prefixLengthStr = strchr(argv[argcur], '/')) == NULL)
{
ExitNow();
}
*prefixLengthStr++ = '\0';
SuccessOrExit(error = otIp6AddressFromString(argv[argcur], &prefix.mPrefix));
prefix.mLength = static_cast<uint8_t>(strtol(prefixLengthStr, &endptr, 0));
if (*endptr != '\0')
{
ExitNow(error = OT_ERROR_PARSE);
}
error = otBorderRouterRemoveRoute(mInstance, &prefix);
exit:
return error;
}
void Interpreter::ProcessRoute(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
VerifyOrExit(argc > 0, error = OT_ERROR_PARSE);
if (strcmp(argv[0], "add") == 0)
{
SuccessOrExit(error = ProcessRouteAdd(argc - 1, argv + 1));
}
else if (strcmp(argv[0], "remove") == 0)
{
SuccessOrExit(error = ProcessRouteRemove(argc - 1, argv + 1));
}
else
{
ExitNow(error = OT_ERROR_PARSE);
}
exit:
AppendResult(error);
}
#endif // OPENTHREAD_ENABLE_BORDER_ROUTER
#if OPENTHREAD_FTD
void Interpreter::ProcessRouter(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
otRouterInfo routerInfo;
long value;
bool isTable = false;
VerifyOrExit(argc > 0, error = OT_ERROR_PARSE);
if (strcmp(argv[0], "list") == 0 || (isTable = (strcmp(argv[0], "table") == 0)))
{
if (isTable)
{
mServer->OutputFormat("| ID | RLOC16 | Next Hop | Path Cost | LQ In | LQ Out | Age | Extended MAC |\r\n");
mServer->OutputFormat("+----+--------+----------+-----------+-------+--------+-----+------------------+\r\n");
}
for (uint8_t i = 0; ; i++)
{
if (otThreadGetRouterInfo(mInstance, i, &routerInfo) != OT_ERROR_NONE)
{
mServer->OutputFormat("\r\n");
ExitNow();
}
if (routerInfo.mAllocated)
{
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);
}
}
}
}
SuccessOrExit(error = ParseLong(argv[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(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
long value;
if (argc == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetRouterDowngradeThreshold(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(argv[0], value));
otThreadSetRouterDowngradeThreshold(mInstance, static_cast<uint8_t>(value));
}
exit:
AppendResult(error);
}
void Interpreter::ProcessRouterRole(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
if (argc == 0)
{
if (otThreadIsRouterRoleEnabled(mInstance))
{
mServer->OutputFormat("Enabled\r\n");
}
else
{
mServer->OutputFormat("Disabled\r\n");
}
}
else if (strcmp(argv[0], "enable") == 0)
{
otThreadSetRouterRoleEnabled(mInstance, true);
}
else if (strcmp(argv[0], "disable") == 0)
{
otThreadSetRouterRoleEnabled(mInstance, false);
}
else
{
ExitNow(error = OT_ERROR_PARSE);
}
exit:
AppendResult(error);
}
void Interpreter::ProcessRouterSelectionJitter(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
long value;
if (argc == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetRouterSelectionJitter(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(argv[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(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
long value;
if (argc == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetRouterUpgradeThreshold(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(argv[0], value));
otThreadSetRouterUpgradeThreshold(mInstance, static_cast<uint8_t>(value));
}
exit:
AppendResult(error);
}
#endif // OPENTHREAD_FTD
void Interpreter::ProcessScan(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
uint32_t scanChannels = 0;
long value;
if (argc > 0)
{
SuccessOrExit(error = ParseLong(argv[0], value));
scanChannels = 1 << value;
}
mServer->OutputFormat("| J | Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI |\r\n");
mServer->OutputFormat("+---+------------------+------------------+------+------------------+----+-----+-----+\r\n");
SuccessOrExit(error = otLinkActiveScan(mInstance, scanChannels, 0, &Interpreter::s_HandleActiveScanResult, this));
return;
exit:
AppendResult(error);
}
void OTCALL Interpreter::s_HandleActiveScanResult(otActiveScanResult *aResult, void *aContext)
{
static_cast<Interpreter *>(aContext)->HandleActiveScanResult(aResult);
}
void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult)
{
if (aResult == NULL)
{
mServer->OutputFormat("Done\r\n");
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::ProcessSingleton(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
if (otThreadIsSingleton(mInstance))
{
mServer->OutputFormat("true\r\n");
}
else
{
mServer->OutputFormat("false\r\n");
}
OT_UNUSED_VARIABLE(argc);
OT_UNUSED_VARIABLE(argv);
AppendResult(error);
}
void Interpreter::ProcessState(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
if (argc == 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(argv[0], "detached") == 0)
{
SuccessOrExit(error = otThreadBecomeDetached(mInstance));
}
else if (strcmp(argv[0], "child") == 0)
{
SuccessOrExit(error = otThreadBecomeChild(mInstance));
}
#if OPENTHREAD_FTD
else if (strcmp(argv[0], "router") == 0)
{
SuccessOrExit(error = otThreadBecomeRouter(mInstance));
}
else if (strcmp(argv[0], "leader") == 0)
{
SuccessOrExit(error = otThreadBecomeLeader(mInstance));
}
#endif // OPENTHREAD_FTD
else
{
ExitNow(error = OT_ERROR_PARSE);
}
}
exit:
AppendResult(error);
}
void Interpreter::ProcessThread(int argc, char *argv[])
{
otError error = OT_ERROR_PARSE;
VerifyOrExit(argc > 0, error = OT_ERROR_PARSE);
if (strcmp(argv[0], "start") == 0)
{
SuccessOrExit(error = otThreadSetEnabled(mInstance, true));
}
else if (strcmp(argv[0], "stop") == 0)
{
SuccessOrExit(error = otThreadSetEnabled(mInstance, false));
}
exit:
OT_UNUSED_VARIABLE(argc);
OT_UNUSED_VARIABLE(argv);
AppendResult(error);
}
void Interpreter::ProcessTxPowerMax(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
long value;
if (argc == 0)
{
mServer->OutputFormat("%d dBm\r\n", otLinkGetMaxTransmitPower(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(argv[0], value));
otLinkSetMaxTransmitPower(mInstance, static_cast<int8_t>(value));
}
exit:
AppendResult(error);
}
void Interpreter::ProcessVersion(int argc, char *argv[])
{
otStringPtr version(otGetVersionString());
mServer->OutputFormat("%s\r\n", (const char *)version);
AppendResult(OT_ERROR_NONE);
OT_UNUSED_VARIABLE(argc);
OT_UNUSED_VARIABLE(argv);
}
#if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
void Interpreter::ProcessCommissioner(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
VerifyOrExit(argc > 0, error = OT_ERROR_PARSE);
if (strcmp(argv[0], "start") == 0)
{
SuccessOrExit(error = otCommissionerStart(mInstance));
}
else if (strcmp(argv[0], "stop") == 0)
{
SuccessOrExit(error = otCommissionerStop(mInstance));
}
else if (strcmp(argv[0], "joiner") == 0)
{
otExtAddress addr;
const otExtAddress *addrPtr;
VerifyOrExit(argc > 2, error = OT_ERROR_PARSE);
if (strcmp(argv[2], "*") == 0)
{
addrPtr = NULL;
}
else
{
VerifyOrExit(Hex2Bin(argv[2], addr.m8, sizeof(addr)) == sizeof(addr), error = OT_ERROR_PARSE);
addrPtr = &addr;
}
if (strcmp(argv[1], "add") == 0)
{
VerifyOrExit(argc > 3, error = OT_ERROR_PARSE);
// Timeout parameter is optional - if not specified, use default value.
unsigned long timeout = kDefaultJoinerTimeout;
if (argc > 4)
{
SuccessOrExit(error = ParseUnsignedLong(argv[4], timeout));
}
SuccessOrExit(error = otCommissionerAddJoiner(mInstance, addrPtr, argv[3], static_cast<uint32_t>(timeout)));
}
else if (strcmp(argv[1], "remove") == 0)
{
SuccessOrExit(error = otCommissionerRemoveJoiner(mInstance, addrPtr));
}
}
else if (strcmp(argv[0], "provisioningurl") == 0)
{
SuccessOrExit(error = otCommissionerSetProvisioningUrl(mInstance, (argc > 1) ? argv[1] : NULL));
}
else if (strcmp(argv[0], "announce") == 0)
{
long mask;
long count;
long period;
otIp6Address address;
VerifyOrExit(argc > 4, error = OT_ERROR_PARSE);
// mask
SuccessOrExit(error = ParseLong(argv[1], mask));
// count
SuccessOrExit(error = ParseLong(argv[2], count));
// period
SuccessOrExit(error = ParseLong(argv[3], period));
// destination
SuccessOrExit(error = otIp6AddressFromString(argv[4], &address));
SuccessOrExit(error = otCommissionerAnnounceBegin(mInstance,
static_cast<uint32_t>(mask),
static_cast<uint8_t>(count),
static_cast<uint16_t>(period),
&address));
}
else if (strcmp(argv[0], "energy") == 0)
{
long mask;
long count;
long period;
long scanDuration;
otIp6Address address;
VerifyOrExit(argc > 5, error = OT_ERROR_PARSE);
// mask
SuccessOrExit(error = ParseLong(argv[1], mask));
// count
SuccessOrExit(error = ParseLong(argv[2], count));
// period
SuccessOrExit(error = ParseLong(argv[3], period));
// scan duration
SuccessOrExit(error = ParseLong(argv[4], scanDuration));
// destination
SuccessOrExit(error = otIp6AddressFromString(argv[5], &address));
SuccessOrExit(error = otCommissionerEnergyScan(mInstance,
static_cast<uint32_t>(mask),
static_cast<uint8_t>(count),
static_cast<uint16_t>(period),
static_cast<uint16_t>(scanDuration),
&address,
Interpreter::s_HandleEnergyReport,
this));
}
else if (strcmp(argv[0], "panid") == 0)
{
long panid;
long mask;
otIp6Address address;
VerifyOrExit(argc > 3, error = OT_ERROR_PARSE);
// panid
SuccessOrExit(error = ParseLong(argv[1], panid));
// mask
SuccessOrExit(error = ParseLong(argv[2], mask));
// destination
SuccessOrExit(error = otIp6AddressFromString(argv[3], &address));
SuccessOrExit(error = otCommissionerPanIdQuery(mInstance,
static_cast<uint16_t>(panid),
static_cast<uint32_t>(mask),
&address,
Interpreter::s_HandlePanIdConflict,
this));
}
else if (strcmp(argv[0], "mgmtget") == 0)
{
uint8_t tlvs[32];
long value;
int length = 0;
for (uint8_t index = 1; index < argc; index++)
{
VerifyOrExit(static_cast<size_t>(length) < sizeof(tlvs), error = OT_ERROR_NO_BUFS);
if (strcmp(argv[index], "locator") == 0)
{
tlvs[length++] = OT_MESHCOP_TLV_BORDER_AGENT_RLOC;
}
else if (strcmp(argv[index], "sessionid") == 0)
{
tlvs[length++] = OT_MESHCOP_TLV_COMM_SESSION_ID;
}
else if (strcmp(argv[index], "steeringdata") == 0)
{
tlvs[length++] = OT_MESHCOP_TLV_STEERING_DATA;
}
else if (strcmp(argv[index], "joinerudpport") == 0)
{
tlvs[length++] = OT_MESHCOP_TLV_JOINER_UDP_PORT;
}
else if (strcmp(argv[index], "binary") == 0)
{
VerifyOrExit(++index < argc, error = OT_ERROR_PARSE);
value = static_cast<long>(strlen(argv[index]) + 1) / 2;
VerifyOrExit(static_cast<size_t>(value) <= (sizeof(tlvs) - static_cast<size_t>(length)), error = OT_ERROR_NO_BUFS);
VerifyOrExit(Interpreter::Hex2Bin(argv[index], tlvs + length, static_cast<uint16_t>(value)) >= 0,
error = OT_ERROR_PARSE);
length += value;
}
else
{
ExitNow(error = OT_ERROR_PARSE);
}
}
SuccessOrExit(error = otCommissionerSendMgmtGet(mInstance, tlvs, static_cast<uint8_t>(length)));
}
else if (strcmp(argv[0], "mgmtset") == 0)
{
otCommissioningDataset dataset;
uint8_t tlvs[32];
long value;
int length = 0;
VerifyOrExit(argc > 0, error = OT_ERROR_PARSE);
memset(&dataset, 0, sizeof(dataset));
for (uint8_t index = 1; index < argc; index++)
{
VerifyOrExit(static_cast<size_t>(length) < sizeof(tlvs), error = OT_ERROR_NO_BUFS);
if (strcmp(argv[index], "locator") == 0)
{
VerifyOrExit(++index < argc, error = OT_ERROR_PARSE);
dataset.mIsLocatorSet = true;
SuccessOrExit(error = Interpreter::ParseLong(argv[index], value));
dataset.mLocator = static_cast<uint16_t>(value);
}
else if (strcmp(argv[index], "sessionid") == 0)
{
VerifyOrExit(++index < argc, error = OT_ERROR_PARSE);
dataset.mIsSessionIdSet = true;
SuccessOrExit(error = Interpreter::ParseLong(argv[index], value));
dataset.mSessionId = static_cast<uint16_t>(value);
}
else if (strcmp(argv[index], "steeringdata") == 0)
{
VerifyOrExit(++index < argc, error = OT_ERROR_PARSE);
dataset.mIsSteeringDataSet = true;
length = static_cast<int>((strlen(argv[index]) + 1) / 2);
VerifyOrExit(static_cast<size_t>(length) <= OT_STEERING_DATA_MAX_LENGTH, error = OT_ERROR_NO_BUFS);
VerifyOrExit(Interpreter::Hex2Bin(argv[index], dataset.mSteeringData.m8, static_cast<uint16_t>(length)) >= 0,
error = OT_ERROR_PARSE);
dataset.mSteeringData.mLength = static_cast<uint8_t>(length);
length = 0;
}
else if (strcmp(argv[index], "joinerudpport") == 0)
{
VerifyOrExit(++index < argc, error = OT_ERROR_PARSE);
dataset.mIsJoinerUdpPortSet = true;
SuccessOrExit(error = Interpreter::ParseLong(argv[index], value));
dataset.mJoinerUdpPort = static_cast<uint16_t>(value);
}
else if (strcmp(argv[index], "binary") == 0)
{
VerifyOrExit(++index < argc, error = OT_ERROR_PARSE);
length = static_cast<int>((strlen(argv[index]) + 1) / 2);
VerifyOrExit(static_cast<size_t>(length) <= sizeof(tlvs), error = OT_ERROR_NO_BUFS);
VerifyOrExit(Interpreter::Hex2Bin(argv[index], tlvs, static_cast<uint16_t>(length)) >= 0,
error = OT_ERROR_PARSE);
}
else
{
ExitNow(error = OT_ERROR_PARSE);
}
}
SuccessOrExit(error = otCommissionerSendMgmtSet(mInstance, &dataset, tlvs, static_cast<uint8_t>(length)));
}
else if (strcmp(argv[0], "sessionid") == 0)
{
mServer->OutputFormat("%d\r\n", otCommissionerGetSessionId(mInstance));
}
exit:
AppendResult(error);
}
void OTCALL Interpreter::s_HandleEnergyReport(uint32_t aChannelMask, const uint8_t *aEnergyList,
uint8_t aEnergyListLength,
void *aContext)
{
static_cast<Interpreter *>(aContext)->HandleEnergyReport(aChannelMask, aEnergyList, aEnergyListLength);
}
void Interpreter::HandleEnergyReport(uint32_t aChannelMask, const uint8_t *aEnergyList, uint8_t aEnergyListLength)
{
mServer->OutputFormat("Energy: %08x ", aChannelMask);
for (uint8_t i = 0; i < aEnergyListLength; i++)
{
mServer->OutputFormat("%d ", aEnergyList[i]);
}
mServer->OutputFormat("\r\n");
}
void OTCALL Interpreter::s_HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask, void *aContext)
{
static_cast<Interpreter *>(aContext)->HandlePanIdConflict(aPanId, aChannelMask);
}
void Interpreter::HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask)
{
mServer->OutputFormat("Conflict: %04x, %08x\r\n", aPanId, aChannelMask);
}
#endif // OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
#if OPENTHREAD_ENABLE_JOINER
void Interpreter::ProcessJoiner(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
VerifyOrExit(argc > 0, error = OT_ERROR_PARSE);
if (strcmp(argv[0], "start") == 0)
{
const char *provisioningUrl;
VerifyOrExit(argc > 1, error = OT_ERROR_PARSE);
provisioningUrl = (argc > 2) ? argv[2] : NULL;
otJoinerStart(mInstance, argv[1], provisioningUrl,
PACKAGE_NAME, PLATFORM_INFO, PACKAGE_VERSION, NULL,
&Interpreter::s_HandleJoinerCallback, this);
}
else if (strcmp(argv[0], "stop") == 0)
{
otJoinerStop(mInstance);
}
exit:
AppendResult(error);
}
#endif // OPENTHREAD_ENABLE_JOINER
void OTCALL Interpreter::s_HandleJoinerCallback(otError aError, void *aContext)
{
static_cast<Interpreter *>(aContext)->HandleJoinerCallback(aError);
}
void Interpreter::HandleJoinerCallback(otError aError)
{
switch (aError)
{
case OT_ERROR_NONE:
mServer->OutputFormat("Join success\r\n");
break;
default:
mServer->OutputFormat("Join failed [%s]\r\n", otThreadErrorToString(aError));
break;
}
}
#if OPENTHREAD_FTD
void Interpreter::ProcessJoinerPort(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
long value;
if (argc == 0)
{
mServer->OutputFormat("%d\r\n", otThreadGetJoinerUdpPort(mInstance));
}
else
{
SuccessOrExit(error = ParseLong(argv[0], value));
error = otThreadSetJoinerUdpPort(mInstance, static_cast<uint16_t>(value));
}
exit:
AppendResult(error);
}
#endif
void Interpreter::ProcessWhitelist(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
otMacWhitelistEntry entry;
int argcur = 0;
uint8_t extAddr[8];
int8_t rssi;
if (argcur >= argc)
{
if (otLinkIsWhitelistEnabled(mInstance))
{
mServer->OutputFormat("Enabled\r\n");
}
else
{
mServer->OutputFormat("Disabled\r\n");
}
for (uint8_t i = 0; ; i++)
{
if (otLinkGetWhitelistEntry(mInstance, i, &entry) != OT_ERROR_NONE)
{
break;
}
if (entry.mValid == false)
{
continue;
}
OutputBytes(entry.mExtAddress.m8, OT_EXT_ADDRESS_SIZE);
if (entry.mFixedRssi)
{
mServer->OutputFormat(" %d", entry.mRssi);
}
mServer->OutputFormat("\r\n");
}
}
else if (strcmp(argv[argcur], "add") == 0)
{
VerifyOrExit(++argcur < argc, error = OT_ERROR_PARSE);
VerifyOrExit(Hex2Bin(argv[argcur], extAddr, sizeof(extAddr)) == sizeof(extAddr), error = OT_ERROR_PARSE);
if (++argcur < argc)
{
rssi = static_cast<int8_t>(strtol(argv[argcur], NULL, 0));
VerifyOrExit(otLinkAddWhitelistRssi(mInstance, extAddr, rssi) == OT_ERROR_NONE, error = OT_ERROR_PARSE);
}
else
{
otLinkAddWhitelist(mInstance, extAddr);
VerifyOrExit(otLinkAddWhitelist(mInstance, extAddr) == OT_ERROR_NONE, error = OT_ERROR_PARSE);
}
}
else if (strcmp(argv[argcur], "clear") == 0)
{
otLinkClearWhitelist(mInstance);
}
else if (strcmp(argv[argcur], "disable") == 0)
{
otLinkSetWhitelistEnabled(mInstance, false);
}
else if (strcmp(argv[argcur], "enable") == 0)
{
otLinkSetWhitelistEnabled(mInstance, true);
}
else if (strcmp(argv[argcur], "remove") == 0)
{
VerifyOrExit(++argcur < argc, error = OT_ERROR_PARSE);
VerifyOrExit(Hex2Bin(argv[argcur], extAddr, sizeof(extAddr)) == sizeof(extAddr), error = OT_ERROR_PARSE);
otLinkRemoveWhitelist(mInstance, extAddr);
}
exit:
AppendResult(error);
}
#if OPENTHREAD_ENABLE_DIAG
void Interpreter::ProcessDiag(int argc, char *argv[])
{
// all diagnostics related features are processed within diagnostics module
mServer->OutputFormat("%s\r\n", otDiagProcessCmd(argc, argv));
}
#endif
void Interpreter::ProcessLine(char *aBuf, uint16_t aBufLength, Server &aServer)
{
char *argv[kMaxArgs];
char *cmd;
uint8_t argc = 0, i = 0;
mServer = &aServer;
VerifyOrExit(aBuf != NULL);
for (; *aBuf == ' '; aBuf++, aBufLength--);
for (cmd = aBuf + 1; (cmd < aBuf + aBufLength) && (cmd != NULL); ++cmd)
{
VerifyOrExit(argc < kMaxArgs,
mServer->OutputFormat("Error: too many args (max %d)\r\n", kMaxArgs));
if (*cmd == ' ' || *cmd == '\r' || *cmd == '\n')
{
*cmd = '\0';
}
if (*(cmd - 1) == '\0' && *cmd != ' ')
{
argv[argc++] = cmd;
}
}
cmd = aBuf;
#if OPENTHREAD_ENABLE_DIAG
VerifyOrExit((!otDiagIsEnabled() || (strcmp(cmd, "diag") == 0)),
mServer->OutputFormat("under diagnostics mode, execute 'diag stop' before running any other commands.\r\n"));
#endif
for (i = 0; i < sizeof(sCommands) / sizeof(sCommands[0]); i++)
{
if (strcmp(cmd, sCommands[i].mName) == 0)
{
(this->*sCommands[i].mCommand)(argc, argv);
break;
}
}
// Error prompt for unsupported commands
if (i == sizeof(sCommands) / sizeof(sCommands[0]))
{
AppendResult(OT_ERROR_PARSE);
}
exit:
return;
}
void OTCALL Interpreter::s_HandleNetifStateChanged(uint32_t aFlags, void *aContext)
{
#ifdef OTDLL
otCliContext *cliContext = static_cast<otCliContext *>(aContext);
cliContext->mInterpreter->HandleNetifStateChanged(cliContext->mInstance, aFlags);
#else
static_cast<Interpreter *>(aContext)->HandleNetifStateChanged(aFlags);
#endif
}
#ifdef OTDLL
void Interpreter::HandleNetifStateChanged(otInstance *mInstance, uint32_t aFlags)
#else
void Interpreter::HandleNetifStateChanged(uint32_t aFlags)
#endif
{
VerifyOrExit((aFlags & OT_CHANGED_THREAD_NETDATA) != 0);
#ifndef OTDLL
otIp6SlaacUpdate(mInstance, mSlaacAddresses, sizeof(mSlaacAddresses) / sizeof(mSlaacAddresses[0]),
otIp6CreateRandomIid, NULL);
#if OPENTHREAD_ENABLE_DHCP6_SERVER
otDhcp6ServerUpdate(mInstance);
#endif // OPENTHREAD_ENABLE_DHCP6_SERVER
#if OPENTHREAD_ENABLE_DHCP6_CLIENT
otDhcp6ClientUpdate(mInstance, mDhcpAddresses, sizeof(mDhcpAddresses) / sizeof(mDhcpAddresses[0]), NULL);
#endif // OPENTHREAD_ENABLE_DHCP6_CLIENT
#endif
exit:
return;
}
#if OPENTHREAD_FTD || OPENTHREAD_ENABLE_MTD_NETWORK_DIAGNOSTIC
void Interpreter::ProcessNetworkDiagnostic(int argc, char *argv[])
{
otError error = OT_ERROR_NONE;
struct otIp6Address address;
uint8_t payload[2 + OT_NETWORK_DIAGNOSTIC_TYPELIST_MAX_ENTRIES]; // TypeList Type(1B), len(1B), type list
uint8_t payloadIndex = 0;
uint8_t paramIndex = 0;
VerifyOrExit(argc > 1 + 1, error = OT_ERROR_PARSE);
SuccessOrExit(error = otIp6AddressFromString(argv[1], &address));
payloadIndex = 2;
paramIndex = 2;
while (paramIndex < argc && payloadIndex < sizeof(payload))
{
long value;
SuccessOrExit(error = ParseLong(argv[paramIndex++], value));
payload[payloadIndex++] = static_cast<uint8_t>(value);
}
payload[0] = OT_NETWORK_DIAGNOSTIC_TYPELIST_TYPE; // TypeList TLV Type
payload[1] = payloadIndex - 2; // length
if (strcmp(argv[0], "get") == 0)
{
otThreadSendDiagnosticGet(mInstance, &address, payload, payloadIndex);
return;
}
else if (strcmp(argv[0], "reset") == 0)
{
otThreadSendDiagnosticReset(mInstance, &address, payload, payloadIndex);
}
exit:
AppendResult(error);
}
#endif // OPENTHREAD_FTD || OPENTHREAD_ENABLE_MTD_NETWORK_DIAGNOSTIC
#ifndef OTDLL
void Interpreter::s_HandleDiagnosticGetResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo,
void *aContext)
{
static_cast<Interpreter *>(aContext)->HandleDiagnosticGetResponse(*static_cast<Message *>(aMessage),
*static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}
void Interpreter::HandleDiagnosticGetResponse(Message &aMessage, const Ip6::MessageInfo &)
{
uint8_t buf[16];
uint16_t bytesToPrint;
uint16_t bytesPrinted = 0;
uint16_t length = aMessage.GetLength() - aMessage.GetOffset();
mServer->OutputFormat("DIAG_GET.rsp: ");
while (length > 0)
{
bytesToPrint = (length < sizeof(buf)) ? length : sizeof(buf);
aMessage.Read(aMessage.GetOffset() + bytesPrinted, bytesToPrint, buf);
OutputBytes(buf, static_cast<uint8_t>(bytesToPrint));
length -= bytesToPrint;
bytesPrinted += bytesToPrint;
}
mServer->OutputFormat("\r\n");
}
#endif
Interpreter &Interpreter::GetOwner(const Context &aContext)
{
#if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES
Interpreter &interpreter = *static_cast<Interpreter *>(aContext.GetContext());
#else
Interpreter &interpreter = Uart::sUartServer->GetInterpreter();
OT_UNUSED_VARIABLE(aContext);
#endif
return interpreter;
}
} // namespace Cli
} // namespace ot