blob: 10a6ac88089ad0263ccb246363f85944bc8018b5 [file] [log] [blame]
/*
* Copyright (c) 2016, Nest Labs, Inc.
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openthread.h>
#include "cli.hpp"
#include <common/encoding.hpp>
#include <platform/serial.h>
using Thread::Encoding::BigEndian::HostSwap16;
namespace Thread {
namespace Cli {
const struct Command Interpreter::sCommands[] =
{
{ "help", &ProcessHelp },
{ "channel", &ProcessChannel },
{ "childtimeout", &ProcessChildTimeout },
{ "contextreusedelay", &ProcessContextIdReuseDelay },
{ "extaddr", &ProcessExtAddress },
{ "extpanid", &ProcessExtPanId },
{ "ipaddr", &ProcessIpAddr },
{ "keysequence", &ProcessKeySequence },
{ "leaderweight", &ProcessLeaderWeight },
{ "masterkey", &ProcessMasterKey },
{ "mode", &ProcessMode },
{ "netdataregister", &ProcessNetworkDataRegister },
{ "networkidtimeout", &ProcessNetworkIdTimeout },
{ "networkname", &ProcessNetworkName },
{ "panid", &ProcessPanId },
{ "ping", &ProcessPing },
{ "prefix", &ProcessPrefix },
{ "releaserouterid", &ProcessReleaseRouterId },
{ "rloc16", &ProcessRloc16 },
{ "route", &ProcessRoute },
{ "routerupgradethreshold", &ProcessRouterUpgradeThreshold },
{ "scan", &ProcessScan },
{ "shutdown", &ProcessShutdown },
{ "start", &ProcessStart },
{ "state", &ProcessState },
{ "stop", &ProcessStop },
{ "whitelist", &ProcessWhitelist },
};
ResponseBuffer Interpreter::sResponse;
otNetifAddress Interpreter::sAddress;
Ip6::IcmpEcho Interpreter::sIcmpEcho(&HandleEchoResponse, NULL);
Ip6::SockAddr Interpreter::sSockAddr;
Server *Interpreter::sServer;
uint8_t Interpreter::sEchoRequest[1500];
int Interpreter::Hex2Bin(const char *aHex, uint8_t *aBin, uint16_t aBinLength)
{
uint16_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 cur - aBin;
}
ThreadError Interpreter::ParseLong(char *argv, long &value)
{
char *endptr;
value = strtol(argv, &endptr, 0);
return (*endptr == '\0') ? kThreadError_None : kThreadError_Parse;
}
void Interpreter::ProcessHelp(int argc, char *argv[])
{
for (unsigned int i = 0; i < sizeof(sCommands) / sizeof(sCommands[0]); i++)
{
sResponse.Append("%s\r\n", sCommands[i].mName);
}
}
void Interpreter::ProcessChannel(int argc, char *argv[])
{
long value;
if (argc == 0)
{
sResponse.Append("%d\r\n", otGetChannel());
}
else
{
SuccessOrExit(ParseLong(argv[0], value));
otSetChannel(value);
}
sResponse.Append("Done\r\n");
exit:
return;
}
void Interpreter::ProcessChildTimeout(int argc, char *argv[])
{
long value;
if (argc == 0)
{
sResponse.Append("%d\r\n", otGetChildTimeout());
}
else
{
SuccessOrExit(ParseLong(argv[0], value));
otSetChildTimeout(value);
}
sResponse.Append("Done\r\n");
exit:
return;
}
void Interpreter::ProcessContextIdReuseDelay(int argc, char *argv[])
{
long value;
if (argc == 0)
{
sResponse.Append("%d\r\n", otGetContextIdReuseDelay());
}
else
{
SuccessOrExit(ParseLong(argv[0], value));
otSetContextIdReuseDelay(value);
}
sResponse.Append("Done\r\n");
exit:
return;
}
void Interpreter::ProcessExtAddress(int argc, char *argv[])
{
const uint8_t *extAddress = otGetExtendedAddress();
sResponse.Append("%02x%02x%02x%02x%02x%02x%02x%02x\r\n",
extAddress[0], extAddress[1], extAddress[2], extAddress[3],
extAddress[4], extAddress[5], extAddress[6], extAddress[7]);
sResponse.Append("Done\r\n");
}
void Interpreter::ProcessExtPanId(int argc, char *argv[])
{
if (argc == 0)
{
const uint8_t *extPanId = otGetExtendedPanId();
sResponse.Append("%02x%02x%02x%02x%02x%02x%02x%02x\r\n",
extPanId[0], extPanId[1], extPanId[2], extPanId[3],
extPanId[4], extPanId[5], extPanId[6], extPanId[7]);
}
else
{
uint8_t extPanId[8];
VerifyOrExit(Hex2Bin(argv[0], extPanId, sizeof(extPanId)) >= 0, ;);
otSetExtendedPanId(extPanId);
}
sResponse.Append("Done\r\n");
exit:
return;
}
ThreadError Interpreter::ProcessIpAddrAdd(int argc, char *argv[])
{
ThreadError error;
VerifyOrExit(argc > 0, error = kThreadError_Parse);
SuccessOrExit(error = otIp6AddressFromString(argv[0], &sAddress.mAddress));
sAddress.mPrefixLength = 64;
sAddress.mPreferredLifetime = 0xffffffff;
sAddress.mValidLifetime = 0xffffffff;
error = otAddUnicastAddress(&sAddress);
exit:
return error;
}
ThreadError Interpreter::ProcessIpAddrDel(int argc, char *argv[])
{
ThreadError error;
struct otIp6Address address;
VerifyOrExit(argc > 0, error = kThreadError_Parse);
SuccessOrExit(error = otIp6AddressFromString(argv[0], &address));
VerifyOrExit(otIsIp6AddressEqual(&address, &sAddress.mAddress), error = kThreadError_Parse);
error = otRemoveUnicastAddress(&sAddress);
exit:
return error;
}
void Interpreter::ProcessIpAddr(int argc, char *argv[])
{
if (argc == 0)
{
for (const otNetifAddress *addr = otGetUnicastAddresses(); addr; addr = addr->mNext)
{
sResponse.Append("%x:%x:%x:%x:%x:%x:%x:%x\r\n",
HostSwap16(addr->mAddress.m16[0]), HostSwap16(addr->mAddress.m16[1]),
HostSwap16(addr->mAddress.m16[2]), HostSwap16(addr->mAddress.m16[3]),
HostSwap16(addr->mAddress.m16[4]), HostSwap16(addr->mAddress.m16[5]),
HostSwap16(addr->mAddress.m16[6]), HostSwap16(addr->mAddress.m16[7]));
}
}
else
{
if (strcmp(argv[0], "add") == 0)
{
SuccessOrExit(ProcessIpAddrAdd(argc - 1, argv + 1));
}
else if (strcmp(argv[0], "del") == 0)
{
SuccessOrExit(ProcessIpAddrDel(argc - 1, argv + 1));
}
}
sResponse.Append("Done\r\n");
exit:
return;
}
void Interpreter::ProcessKeySequence(int argc, char *argv[])
{
long value;
if (argc == 0)
{
sResponse.Append("%d\r\n", otGetKeySequenceCounter());
}
else
{
SuccessOrExit(ParseLong(argv[0], value));
otSetKeySequenceCounter(value);
}
sResponse.Append("Done\r\n");
exit:
return;
}
void Interpreter::ProcessLeaderWeight(int argc, char *argv[])
{
long value;
if (argc == 0)
{
sResponse.Append("%d\r\n", otGetLocalLeaderWeight());
}
else
{
SuccessOrExit(ParseLong(argv[0], value));
otSetLocalLeaderWeight(value);
}
sResponse.Append("Done\r\n");
exit:
return;
}
void Interpreter::ProcessMasterKey(int argc, char *argv[])
{
uint8_t keyLength;
if (argc == 0)
{
const uint8_t *key = otGetMasterKey(&keyLength);
for (int i = 0; i < keyLength; i++)
{
sResponse.Append("%02x", key[i]);
}
sResponse.Append("\r\n");
}
else
{
uint8_t key[16];
VerifyOrExit((keyLength = Hex2Bin(argv[0], key, sizeof(key))) >= 0, ;);
SuccessOrExit(otSetMasterKey(key, keyLength));
}
sResponse.Append("Done\r\n");
exit:
return;
}
void Interpreter::ProcessMode(int argc, char *argv[])
{
otLinkModeConfig linkMode = {};
if (argc == 0)
{
linkMode = otGetLinkMode();
if (linkMode.mRxOnWhenIdle)
{
sResponse.Append("r");
}
if (linkMode.mSecureDataRequests)
{
sResponse.Append("s");
}
if (linkMode.mDeviceType)
{
sResponse.Append("d");
}
if (linkMode.mNetworkData)
{
sResponse.Append("n");
}
sResponse.Append("\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();
}
}
SuccessOrExit(otSetLinkMode(linkMode));
}
sResponse.Append("Done\r\n");
exit:
return;
}
void Interpreter::ProcessNetworkDataRegister(int argc, char *argv[])
{
SuccessOrExit(otSendServerData());
sResponse.Append("Done\r\n");
exit:
return;
}
void Interpreter::ProcessNetworkIdTimeout(int argc, char *argv[])
{
long value;
if (argc == 0)
{
sResponse.Append("%d\r\n", otGetNetworkIdTimeout());
}
else
{
SuccessOrExit(ParseLong(argv[0], value));
otSetNetworkIdTimeout(value);
}
sResponse.Append("Done\r\n");
exit:
return;
}
void Interpreter::ProcessNetworkName(int argc, char *argv[])
{
if (argc == 0)
{
sResponse.Append("%.*s\r\n", OT_NETWORK_NAME_SIZE, otGetNetworkName());
}
else
{
SuccessOrExit(otSetNetworkName(argv[0]));
}
sResponse.Append("Done\r\n");
exit:
return;
}
void Interpreter::ProcessPanId(int argc, char *argv[])
{
long value;
if (argc == 0)
{
sResponse.Append("%d\r\n", otGetPanId());
}
else
{
SuccessOrExit(ParseLong(argv[0], value));
otSetPanId(value);
}
sResponse.Append("Done\r\n");
exit:
return;
}
void Interpreter::HandleEchoResponse(void *aContext, Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
Ip6::IcmpHeader icmp6Header;
aMessage.Read(aMessage.GetOffset(), sizeof(icmp6Header), &icmp6Header);
sResponse.Init();
sResponse.Append("%d bytes from ", aMessage.GetLength() - aMessage.GetOffset());
sResponse.Append("%x:%x:%x:%x:%x:%x:%x:%x",
HostSwap16(aMessageInfo.GetPeerAddr().m16[0]), HostSwap16(aMessageInfo.GetPeerAddr().m16[1]),
HostSwap16(aMessageInfo.GetPeerAddr().m16[2]), HostSwap16(aMessageInfo.GetPeerAddr().m16[3]),
HostSwap16(aMessageInfo.GetPeerAddr().m16[4]), HostSwap16(aMessageInfo.GetPeerAddr().m16[5]),
HostSwap16(aMessageInfo.GetPeerAddr().m16[6]), HostSwap16(aMessageInfo.GetPeerAddr().m16[7]));
sResponse.Append(": icmp_seq=%d hlim=%d\r\n", icmp6Header.GetSequence(), aMessageInfo.mHopLimit);
sServer->Output(sResponse.GetResponse(), sResponse.GetResponseLength());
}
void Interpreter::ProcessPing(int argc, char *argv[])
{
long length = 8;
VerifyOrExit(argc > 0, ;);
memset(&sSockAddr, 0, sizeof(sSockAddr));
SuccessOrExit(sSockAddr.GetAddress().FromString(argv[0]));
sSockAddr.mScopeId = 1;
if (argc > 1)
{
SuccessOrExit(ParseLong(argv[1], length));
}
sIcmpEcho.SendEchoRequest(sSockAddr, sEchoRequest, length);
sResponse.Init();
exit:
return;
}
ThreadError Interpreter::ProcessPrefixAdd(int argc, char *argv[])
{
ThreadError error = kThreadError_None;
otBorderRouterConfig config = {};
int argcur = 0;
char *prefixLengthStr;
char *endptr;
if ((prefixLengthStr = strchr(argv[argcur], '/')) == NULL)
{
ExitNow();
}
*prefixLengthStr++ = '\0';
SuccessOrExit(error = otIp6AddressFromString(argv[argcur], &config.mPrefix.mPrefix));
config.mPrefix.mLength = strtol(prefixLengthStr, &endptr, 0);
if (*endptr != '\0')
{
ExitNow(error = kThreadError_Parse);
}
if (++argcur < argc)
{
for (char *arg = argv[argcur]; *arg != '\0'; arg++)
{
switch (*arg)
{
case 'p':
config.mSlaacPreferred = true;
break;
case 'v':
config.mSlaacValid = true;
break;
case 'd':
config.mDhcp = true;
break;
case 'c':
config.mConfigure = true;
break;
case 'r':
config.mDefaultRoute = true;
break;
case 's':
config.mStable = true;
break;
default:
ExitNow();
}
}
}
if (++argcur < argc)
{
if (strcmp(argv[argcur], "high") == 0)
{
config.mPreference = 1;
}
else if (strcmp(argv[argcur], "med") == 0)
{
config.mPreference = 0;
}
else if (strcmp(argv[argcur], "low") == 0)
{
config.mPreference = -1;
}
else
{
ExitNow(error = kThreadError_Parse);
}
}
error = otAddBorderRouter(&config);
exit:
return error;
}
ThreadError Interpreter::ProcessPrefixRemove(int argc, char *argv[])
{
ThreadError error = kThreadError_None;
struct otIp6Prefix prefix = {};
int argcur = 0;
char *prefixLengthStr;
char *endptr;
if ((prefixLengthStr = strchr(argv[argcur], '/')) == NULL)
{
ExitNow();
}
*prefixLengthStr++ = '\0';
SuccessOrExit(error = otIp6AddressFromString(argv[argcur], &prefix.mPrefix));
prefix.mLength = strtol(prefixLengthStr, &endptr, 0);
if (*endptr != '\0')
{
ExitNow(error = kThreadError_Parse);
}
error = otRemoveBorderRouter(&prefix);
exit:
return error;
}
void Interpreter::ProcessPrefix(int argc, char *argv[])
{
VerifyOrExit(argc > 0, ;);
if (strcmp(argv[0], "add") == 0)
{
SuccessOrExit(ProcessPrefixAdd(argc - 1, argv + 1));
}
else if (strcmp(argv[0], "remove") == 0)
{
SuccessOrExit(ProcessPrefixRemove(argc - 1, argv + 1));
}
else
{
ExitNow();
}
sResponse.Append("Done\r\n");
exit:
return;
}
void Interpreter::ProcessReleaseRouterId(int argc, char *argv[])
{
long value;
if (argc != 0)
{
SuccessOrExit(ParseLong(argv[0], value));
SuccessOrExit(otReleaseRouterId(value));
sResponse.Append("Done\r\n");
}
exit:
return;
}
void Interpreter::ProcessRloc16(int argc, char *argv[])
{
sResponse.Append("%04x\r\n", otGetRloc16());
sResponse.Append("Done\r\n");
}
ThreadError Interpreter::ProcessRouteAdd(int argc, char *argv[])
{
ThreadError error = kThreadError_None;
otExternalRouteConfig config = {};
int argcur = 0;
char *prefixLengthStr;
char *endptr;
VerifyOrExit(argc > 0, error = kThreadError_Parse);
if ((prefixLengthStr = strchr(argv[argcur], '/')) == NULL)
{
ExitNow();
}
*prefixLengthStr++ = '\0';
SuccessOrExit(error = otIp6AddressFromString(argv[argcur], &config.mPrefix.mPrefix));
config.mPrefix.mLength = strtol(prefixLengthStr, &endptr, 0);
if (*endptr != '\0')
{
ExitNow(error = kThreadError_Parse);
}
if (++argcur < argc)
{
if (strcmp(argv[argcur], "s") == 0)
{
config.mStable = true;
}
else if (strcmp(argv[argcur], "high") == 0)
{
config.mPreference = 1;
}
else if (strcmp(argv[argcur], "med") == 0)
{
config.mPreference = 0;
}
else if (strcmp(argv[argcur], "low") == 0)
{
config.mPreference = -1;
}
else
{
ExitNow(error = kThreadError_Parse);
}
}
error = otAddExternalRoute(&config);
exit:
return error;
}
ThreadError Interpreter::ProcessRouteRemove(int argc, char *argv[])
{
ThreadError error = kThreadError_None;
struct otIp6Prefix prefix = {};
int argcur = 0;
char *prefixLengthStr;
char *endptr;
VerifyOrExit(argc > 0, error = kThreadError_Parse);
if ((prefixLengthStr = strchr(argv[argcur], '/')) == NULL)
{
ExitNow();
}
*prefixLengthStr++ = '\0';
SuccessOrExit(error = otIp6AddressFromString(argv[argcur], &prefix.mPrefix));
prefix.mLength = strtol(prefixLengthStr, &endptr, 0);
if (*endptr != '\0')
{
ExitNow(error = kThreadError_Parse);
}
error = otRemoveExternalRoute(&prefix);
exit:
return error;
}
void Interpreter::ProcessRoute(int argc, char *argv[])
{
VerifyOrExit(argc > 0, ;);
if (strcmp(argv[0], "add") == 0)
{
SuccessOrExit(ProcessRouteAdd(argc - 1, argv + 1));
}
else if (strcmp(argv[0], "remove") == 0)
{
SuccessOrExit(ProcessRouteRemove(argc - 1, argv + 1));
}
else
{
ExitNow();
}
sResponse.Append("Done\r\n");
exit:
return;
}
void Interpreter::ProcessRouterUpgradeThreshold(int argc, char *argv[])
{
long value;
if (argc == 0)
{
sResponse.Append("%d\r\n", otGetRouterUpgradeThreshold());
}
else
{
SuccessOrExit(ParseLong(argv[0], value));
otSetRouterUpgradeThreshold(value);
}
sResponse.Append("Done\r\n");
exit:
return;
}
void Interpreter::ProcessScan(int argc, char *argv[])
{
uint16_t scanChannels = 0;
long value;
if (argc > 0)
{
SuccessOrExit(ParseLong(argv[0], value));
scanChannels = 1 << (value - kPhyMinChannel);
}
SuccessOrExit(otActiveScan(scanChannels, 0, &HandleActiveScanResult));
sResponse.Append("| J | Network Name | Extended PAN | PAN | MAC Address | Ch | dBm |\r\n");
sResponse.Append("+---+------------------+------------------+------+------------------+----+-----+\r\n");
exit:
return;
}
void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult)
{
const uint8_t *bytes;
sResponse.Init();
if (aResult == NULL)
{
sResponse.Append("Done\r\n");
ExitNow();
}
sResponse.Append("| %d ", aResult->mIsJoinable);
if (aResult->mNetworkName != NULL)
{
sResponse.Append("| %-16s ", aResult->mNetworkName);
}
else
{
sResponse.Append("| ---------------- ");
}
if (aResult->mExtPanId != NULL)
{
bytes = aResult->mExtPanId;
sResponse.Append("| %02x%02x%02x%02x%02x%02x%02x%02x ",
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]);
}
else
{
sResponse.Append("| ---------------- ");
}
sResponse.Append("| %04x ", aResult->mPanId);
bytes = aResult->mExtAddress.m8;
sResponse.Append("| %02x%02x%02x%02x%02x%02x%02x%02x ",
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]);
sResponse.Append("| %02d ", aResult->mChannel);
sResponse.Append("| %03d |\r\n", aResult->mRssi);
exit:
sServer->Output(sResponse.GetResponse(), sResponse.GetResponseLength());
}
void Interpreter::ProcessShutdown(int argc, char *argv[])
{
sResponse.Append("Done\r\n");
sServer->Output(sResponse.GetResponse(), sResponse.GetResponseLength());
otPlatSerialDisable();
exit(0);
}
void Interpreter::ProcessStart(int argc, char *argv[])
{
SuccessOrExit(otEnable());
sResponse.Append("Done\r\n");
exit:
return;
}
void Interpreter::ProcessState(int argc, char *argv[])
{
if (argc == 0)
{
switch (otGetDeviceRole())
{
case kDeviceRoleDisabled:
sResponse.Append("disabled\r\n");
break;
case kDeviceRoleDetached:
sResponse.Append("detached\r\n");
break;
case kDeviceRoleChild:
sResponse.Append("child\r\n");
break;
case kDeviceRoleRouter:
sResponse.Append("router\r\n");
break;
case kDeviceRoleLeader:
sResponse.Append("leader\r\n");
break;
}
}
else
{
if (strcmp(argv[0], "detached") == 0)
{
SuccessOrExit(otBecomeDetached());
}
else if (strcmp(argv[0], "child") == 0)
{
SuccessOrExit(otBecomeChild(kMleAttachSamePartition));
}
else if (strcmp(argv[0], "router") == 0)
{
SuccessOrExit(otBecomeRouter());
}
else if (strcmp(argv[0], "leader") == 0)
{
SuccessOrExit(otBecomeLeader());
}
else
{
ExitNow();
}
}
sResponse.Append("Done\r\n");
exit:
return;
}
void Interpreter::ProcessStop(int argc, char *argv[])
{
SuccessOrExit(otDisable());
sResponse.Append("Done\r\n");
exit:
return;
}
void Interpreter::ProcessWhitelist(int argc, char *argv[])
{
int argcur = 0;
uint8_t extAddr[8];
int8_t rssi;
if (argcur >= argc)
{
;
}
else if (strcmp(argv[argcur], "add") == 0)
{
VerifyOrExit(++argcur < argc, ;);
VerifyOrExit(Hex2Bin(argv[argcur], extAddr, sizeof(extAddr)) == sizeof(extAddr), ;);
if (++argcur < argc)
{
rssi = strtol(argv[argcur], NULL, 0);
VerifyOrExit(otAddMacWhitelistRssi(extAddr, rssi) == kThreadError_None, ;);
}
else
{
otAddMacWhitelist(extAddr);
VerifyOrExit(otAddMacWhitelist(extAddr) == kThreadError_None, ;);
}
}
else if (strcmp(argv[argcur], "clear") == 0)
{
otClearMacWhitelist();
}
else if (strcmp(argv[argcur], "disable") == 0)
{
otDisableMacWhitelist();
}
else if (strcmp(argv[argcur], "enable") == 0)
{
otEnableMacWhitelist();
}
else if (strcmp(argv[argcur], "remove") == 0)
{
VerifyOrExit(++argcur < argc, ;);
VerifyOrExit(Hex2Bin(argv[argcur], extAddr, sizeof(extAddr)) == sizeof(extAddr), ;);
otRemoveMacWhitelist(extAddr);
}
sResponse.Append("Done\r\n");
exit:
return;
}
void Interpreter::ProcessLine(char *aBuf, uint16_t aBufLength, Server &aServer)
{
char *argv[kMaxArgs];
char *cmd;
int argc;
char *last;
sServer = &aServer;
VerifyOrExit((cmd = strtok_r(aBuf, " ", &last)) != NULL, ;);
for (argc = 0; argc < kMaxArgs; argc++)
{
if ((argv[argc] = strtok_r(NULL, " ", &last)) == NULL)
{
break;
}
}
sResponse.Init();
for (unsigned int i = 0; i < sizeof(sCommands) / sizeof(sCommands[0]); i++)
{
if (strcmp(cmd, sCommands[i].mName) == 0)
{
sCommands[i].mCommand(argc, argv);
break;
}
}
if (sResponse.GetResponseLength() > 0)
{
aServer.Output(sResponse.GetResponse(), sResponse.GetResponseLength());
}
exit:
return;
}
} // namespace Cli
} // namespace Thread