| /* | |
| * Copyright (c) 2016, The OpenThread Authors. | |
| * All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions are met: | |
| * 1. Redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer. | |
| * 2. Redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution. | |
| * 3. Neither the name of the copyright holder nor the | |
| * names of its contributors may be used to endorse or promote products | |
| * derived from this software without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
| * POSSIBILITY OF SUCH DAMAGE. | |
| */ | |
| /** | |
| * @file | |
| * This file implements the CLI interpreter. | |
| */ | |
| #include "cli.hpp" | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <openthread/diag.h> | |
| #include <openthread/icmp6.h> | |
| #include <openthread/link.h> | |
| #include <openthread/logging.h> | |
| #include <openthread/ncp.h> | |
| #include <openthread/thread.h> | |
| #include <openthread/platform/uart.h> | |
| #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE | |
| #include <openthread/network_time.h> | |
| #endif | |
| #if OPENTHREAD_FTD | |
| #include <openthread/dataset_ftd.h> | |
| #include <openthread/thread_ftd.h> | |
| #endif | |
| #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE | |
| #include <openthread/border_router.h> | |
| #endif | |
| #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE | |
| #include <openthread/server.h> | |
| #endif | |
| #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE | |
| #include <openthread/child_supervision.h> | |
| #endif | |
| #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE | |
| #include <openthread/platform/misc.h> | |
| #endif | |
| #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) | |
| #include <openthread/backbone_router.h> | |
| #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE | |
| #include <openthread/backbone_router_ftd.h> | |
| #endif | |
| #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE | |
| #include <openthread/link_metrics.h> | |
| #endif | |
| #endif | |
| #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD | |
| #include <openthread/channel_manager.h> | |
| #endif | |
| #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE | |
| #include <openthread/channel_monitor.h> | |
| #endif | |
| #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX | |
| #include <openthread/platform/debug_uart.h> | |
| #endif | |
| #include "common/encoding.hpp" | |
| #include "common/new.hpp" | |
| #include "common/string.hpp" | |
| #include "mac/channel_mask.hpp" | |
| #include "net/ip6.hpp" | |
| #include "utils/otns.hpp" | |
| #include "utils/parse_cmdline.hpp" | |
| using ot::Encoding::BigEndian::HostSwap16; | |
| using ot::Encoding::BigEndian::HostSwap32; | |
| using ot::Utils::CmdLineParser::ParseAsBool; | |
| using ot::Utils::CmdLineParser::ParseAsHexString; | |
| using ot::Utils::CmdLineParser::ParseAsInt8; | |
| using ot::Utils::CmdLineParser::ParseAsIp6Address; | |
| using ot::Utils::CmdLineParser::ParseAsIp6Prefix; | |
| using ot::Utils::CmdLineParser::ParseAsUint16; | |
| using ot::Utils::CmdLineParser::ParseAsUint32; | |
| using ot::Utils::CmdLineParser::ParseAsUint64; | |
| using ot::Utils::CmdLineParser::ParseAsUint8; | |
| namespace ot { | |
| namespace Cli { | |
| constexpr Interpreter::Command Interpreter::sCommands[]; | |
| Interpreter *Interpreter::sInterpreter = nullptr; | |
| Interpreter::Interpreter(Instance *aInstance) | |
| : mInstance(aInstance) | |
| , mUserCommands(nullptr) | |
| , mUserCommandsLength(0) | |
| , mPingLength(kDefaultPingLength) | |
| , mPingCount(kDefaultPingCount) | |
| , mPingInterval(kDefaultPingInterval) | |
| , mPingHopLimit(0) | |
| , mPingAllowZeroHopLimit(false) | |
| , mPingIdentifier(0) | |
| , mPingTimer(*aInstance, Interpreter::HandlePingTimer) | |
| #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE | |
| , mResolvingInProgress(false) | |
| #endif | |
| #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE | |
| , mSntpQueryingInProgress(false) | |
| #endif | |
| , mDataset(*this) | |
| , mNetworkData(*this) | |
| , mUdp(*this) | |
| #if OPENTHREAD_CONFIG_COAP_API_ENABLE | |
| , mCoap(*this) | |
| #endif | |
| #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE | |
| , mCoapSecure(*this) | |
| #endif | |
| #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD | |
| , mCommissioner(*this) | |
| #endif | |
| #if OPENTHREAD_CONFIG_JOINER_ENABLE | |
| , mJoiner(*this) | |
| #endif | |
| #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE | |
| , mSrpClient(*this) | |
| #endif | |
| #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE | |
| , mSrpServer(*this) | |
| #endif | |
| { | |
| #if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE | |
| otThreadSetReceiveDiagnosticGetCallback(mInstance, &Interpreter::HandleDiagnosticGetResponse, this); | |
| #endif | |
| #if OPENTHREAD_FTD | |
| otThreadSetDiscoveryRequestCallback(mInstance, &Interpreter::HandleDiscoveryRequest, this); | |
| #endif | |
| mIcmpHandler.mReceiveCallback = Interpreter::HandleIcmpReceive; | |
| mIcmpHandler.mContext = this; | |
| IgnoreError(otIcmp6RegisterHandler(mInstance, &mIcmpHandler)); | |
| #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE | |
| memset(mResolvingHostname, 0, sizeof(mResolvingHostname)); | |
| #endif | |
| } | |
| void Interpreter::OutputResult(otError aError) | |
| { | |
| switch (aError) | |
| { | |
| case OT_ERROR_NONE: | |
| OutputLine("Done"); | |
| break; | |
| case OT_ERROR_PENDING: | |
| break; | |
| default: | |
| OutputLine("Error %d: %s", aError, otThreadErrorToString(aError)); | |
| } | |
| } | |
| void Interpreter::OutputBytes(const uint8_t *aBytes, uint16_t aLength) | |
| { | |
| for (uint16_t i = 0; i < aLength; i++) | |
| { | |
| OutputFormat("%02x", aBytes[i]); | |
| } | |
| } | |
| int Interpreter::OutputIp6Address(const otIp6Address &aAddress) | |
| { | |
| return OutputFormat( | |
| "%x:%x:%x:%x:%x:%x:%x:%x", HostSwap16(aAddress.mFields.m16[0]), HostSwap16(aAddress.mFields.m16[1]), | |
| HostSwap16(aAddress.mFields.m16[2]), HostSwap16(aAddress.mFields.m16[3]), HostSwap16(aAddress.mFields.m16[4]), | |
| HostSwap16(aAddress.mFields.m16[5]), HostSwap16(aAddress.mFields.m16[6]), HostSwap16(aAddress.mFields.m16[7])); | |
| } | |
| otError Interpreter::ParseJoinerDiscerner(char *aString, otJoinerDiscerner &aDiscerner) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| char * separator = strstr(aString, "/"); | |
| VerifyOrExit(separator != nullptr, error = OT_ERROR_NOT_FOUND); | |
| SuccessOrExit(error = ParseAsUint8(separator + 1, aDiscerner.mLength)); | |
| VerifyOrExit(aDiscerner.mLength > 0 && aDiscerner.mLength <= 64, error = OT_ERROR_INVALID_ARGS); | |
| *separator = '\0'; | |
| error = ParseAsUint64(aString, aDiscerner.mValue); | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ParsePingInterval(const char *aString, uint32_t &aInterval) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| const uint32_t msFactor = 1000; | |
| uint32_t factor = msFactor; | |
| aInterval = 0; | |
| while (*aString) | |
| { | |
| if ('0' <= *aString && *aString <= '9') | |
| { | |
| // In the case of seconds, change the base of already calculated value. | |
| if (factor == msFactor) | |
| { | |
| aInterval *= 10; | |
| } | |
| aInterval += static_cast<uint32_t>(*aString - '0') * factor; | |
| // In the case of milliseconds, change the multiplier factor. | |
| if (factor != msFactor) | |
| { | |
| factor /= 10; | |
| } | |
| } | |
| else if (*aString == '.') | |
| { | |
| // Accept only one dot character. | |
| VerifyOrExit(factor == msFactor, error = OT_ERROR_INVALID_ARGS); | |
| // Start analyzing hundreds of milliseconds. | |
| factor /= 10; | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| aString++; | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessHelp(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| OT_UNUSED_VARIABLE(aArgsLength); | |
| OT_UNUSED_VARIABLE(aArgs); | |
| for (const Command &command : sCommands) | |
| { | |
| OutputLine(command.mName); | |
| } | |
| for (uint8_t i = 0; i < mUserCommandsLength; i++) | |
| { | |
| OutputLine("%s", mUserCommands[i].mName); | |
| } | |
| return OT_ERROR_NONE; | |
| } | |
| #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE | |
| otError Interpreter::ProcessBorderRouting(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| bool enable = false; | |
| VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS); | |
| if (strcmp(aArgs[0], "enable") == 0) | |
| { | |
| enable = true; | |
| } | |
| else if (strcmp(aArgs[0], "disable") == 0) | |
| { | |
| enable = false; | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_COMMAND); | |
| } | |
| SuccessOrExit(error = otBorderRoutingSetEnabled(mInstance, enable)); | |
| exit: | |
| return error; | |
| } | |
| #endif | |
| #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) | |
| otError Interpreter::ProcessBackboneRouter(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| OT_UNUSED_VARIABLE(aArgs); | |
| otError error = OT_ERROR_INVALID_COMMAND; | |
| otBackboneRouterConfig config; | |
| if (aArgsLength == 0) | |
| { | |
| if (otBackboneRouterGetPrimary(mInstance, &config) == OT_ERROR_NONE) | |
| { | |
| OutputLine("BBR Primary:"); | |
| OutputLine("server16: 0x%04X", config.mServer16); | |
| OutputLine("seqno: %d", config.mSequenceNumber); | |
| OutputLine("delay: %d secs", config.mReregistrationDelay); | |
| OutputLine("timeout: %d secs", config.mMlrTimeout); | |
| } | |
| else | |
| { | |
| OutputLine("BBR Primary: None"); | |
| } | |
| error = OT_ERROR_NONE; | |
| } | |
| #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE | |
| else | |
| { | |
| #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE | |
| if (strcmp(aArgs[0], "mgmt") == 0) | |
| { | |
| if (aArgsLength < 2) | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_COMMAND); | |
| } | |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE | |
| else if (strcmp(aArgs[1], "dua") == 0) | |
| { | |
| uint8_t status; | |
| otIp6InterfaceIdentifier *mlIid = nullptr; | |
| otIp6InterfaceIdentifier iid; | |
| VerifyOrExit((aArgsLength == 3 || aArgsLength == 4), error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsUint8(aArgs[2], status)); | |
| if (aArgsLength == 4) | |
| { | |
| SuccessOrExit(error = ParseAsHexString(aArgs[3], iid.mFields.m8)); | |
| mlIid = &iid; | |
| } | |
| otBackboneRouterConfigNextDuaRegistrationResponse(mInstance, mlIid, status); | |
| ExitNow(); | |
| } | |
| #endif | |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE | |
| else if (strcmp(aArgs[1], "mlr") == 0) | |
| { | |
| error = ProcessBackboneRouterMgmtMlr(aArgsLength - 2, aArgs + 2); | |
| ExitNow(); | |
| } | |
| #endif | |
| } | |
| #endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE | |
| SuccessOrExit(error = ProcessBackboneRouterLocal(aArgsLength, aArgs)); | |
| } | |
| exit: | |
| #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE | |
| return error; | |
| } | |
| #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE | |
| #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE && OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE | |
| otError Interpreter::ProcessBackboneRouterMgmtMlr(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_INVALID_COMMAND; | |
| VerifyOrExit(aArgsLength >= 1); | |
| if (!strcmp(aArgs[0], "listener")) | |
| { | |
| if (aArgsLength == 1) | |
| { | |
| PrintMulticastListenersTable(); | |
| error = OT_ERROR_NONE; | |
| } | |
| else if (!strcmp(aArgs[1], "clear")) | |
| { | |
| otBackboneRouterMulticastListenerClear(mInstance); | |
| error = OT_ERROR_NONE; | |
| } | |
| else if (!strcmp(aArgs[1], "add")) | |
| { | |
| otIp6Address address; | |
| uint32_t timeout = 0; | |
| VerifyOrExit(aArgsLength == 3 || aArgsLength == 4, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsIp6Address(aArgs[2], address)); | |
| if (aArgsLength == 4) | |
| { | |
| SuccessOrExit(error = ParseAsUint32(aArgs[3], timeout)); | |
| } | |
| error = otBackboneRouterMulticastListenerAdd(mInstance, &address, timeout); | |
| } | |
| } | |
| else if (!strcmp(aArgs[0], "response")) | |
| { | |
| uint8_t status; | |
| VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsUint8(aArgs[1], status)); | |
| otBackboneRouterConfigNextMulticastListenerRegistrationResponse(mInstance, status); | |
| } | |
| else | |
| { | |
| error = OT_ERROR_INVALID_COMMAND; | |
| } | |
| exit: | |
| return error; | |
| } | |
| void Interpreter::PrintMulticastListenersTable(void) | |
| { | |
| otBackboneRouterMulticastListenerIterator iter = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT; | |
| otBackboneRouterMulticastListenerInfo listenerInfo; | |
| while (otBackboneRouterMulticastListenerGetNext(mInstance, &iter, &listenerInfo) == OT_ERROR_NONE) | |
| { | |
| OutputIp6Address(listenerInfo.mAddress); | |
| OutputLine(" %u", listenerInfo.mTimeout); | |
| } | |
| } | |
| #endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE && OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE | |
| otError Interpreter::ProcessBackboneRouterLocal(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| otBackboneRouterConfig config; | |
| if (strcmp(aArgs[0], "disable") == 0) | |
| { | |
| otBackboneRouterSetEnabled(mInstance, false); | |
| } | |
| else if (strcmp(aArgs[0], "enable") == 0) | |
| { | |
| otBackboneRouterSetEnabled(mInstance, true); | |
| } | |
| else if (strcmp(aArgs[0], "jitter") == 0) | |
| { | |
| if (aArgsLength == 1) | |
| { | |
| OutputLine("%d", otBackboneRouterGetRegistrationJitter(mInstance)); | |
| } | |
| else if (aArgsLength == 2) | |
| { | |
| uint8_t jitter; | |
| SuccessOrExit(error = ParseAsUint8(aArgs[1], jitter)); | |
| otBackboneRouterSetRegistrationJitter(mInstance, jitter); | |
| } | |
| } | |
| else if (strcmp(aArgs[0], "register") == 0) | |
| { | |
| SuccessOrExit(error = otBackboneRouterRegister(mInstance)); | |
| } | |
| else if (strcmp(aArgs[0], "state") == 0) | |
| { | |
| switch (otBackboneRouterGetState(mInstance)) | |
| { | |
| case OT_BACKBONE_ROUTER_STATE_DISABLED: | |
| OutputLine("Disabled"); | |
| break; | |
| case OT_BACKBONE_ROUTER_STATE_SECONDARY: | |
| OutputLine("Secondary"); | |
| break; | |
| case OT_BACKBONE_ROUTER_STATE_PRIMARY: | |
| OutputLine("Primary"); | |
| break; | |
| } | |
| } | |
| else if (strcmp(aArgs[0], "config") == 0) | |
| { | |
| otBackboneRouterGetConfig(mInstance, &config); | |
| if (aArgsLength == 1) | |
| { | |
| OutputLine("seqno: %d", config.mSequenceNumber); | |
| OutputLine("delay: %d secs", config.mReregistrationDelay); | |
| OutputLine("timeout: %d secs", config.mMlrTimeout); | |
| } | |
| else | |
| { | |
| // Set local Backbone Router configuration. | |
| for (int argCur = 1; argCur < aArgsLength; argCur++) | |
| { | |
| VerifyOrExit(argCur + 1 < aArgsLength, error = OT_ERROR_INVALID_ARGS); | |
| if (strcmp(aArgs[argCur], "seqno") == 0) | |
| { | |
| SuccessOrExit(error = ParseAsUint8(aArgs[++argCur], config.mSequenceNumber)); | |
| } | |
| else if (strcmp(aArgs[argCur], "delay") == 0) | |
| { | |
| SuccessOrExit(error = ParseAsUint16(aArgs[++argCur], config.mReregistrationDelay)); | |
| } | |
| else if (strcmp(aArgs[argCur], "timeout") == 0) | |
| { | |
| SuccessOrExit(error = ParseAsUint32(aArgs[++argCur], config.mMlrTimeout)); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| } | |
| SuccessOrExit(error = otBackboneRouterSetConfig(mInstance, &config)); | |
| } | |
| } | |
| else | |
| { | |
| error = OT_ERROR_INVALID_COMMAND; | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE | |
| otError Interpreter::ProcessDomainName(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("%s", otThreadGetDomainName(mInstance)); | |
| } | |
| else | |
| { | |
| SuccessOrExit(error = otThreadSetDomainName(mInstance, aArgs[0])); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #if OPENTHREAD_CONFIG_DUA_ENABLE | |
| otError Interpreter::ProcessDua(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| VerifyOrExit(aArgsLength >= 1 && strcmp(aArgs[0], "iid") == 0, error = OT_ERROR_INVALID_COMMAND); | |
| switch (aArgsLength) | |
| { | |
| case 1: | |
| { | |
| const otIp6InterfaceIdentifier *iid = otThreadGetFixedDuaInterfaceIdentifier(mInstance); | |
| if (iid != nullptr) | |
| { | |
| OutputBytes(iid->mFields.m8, sizeof(otIp6InterfaceIdentifier)); | |
| OutputLine(""); | |
| } | |
| break; | |
| } | |
| case 2: | |
| if (strcmp(aArgs[1], "clear") == 0) | |
| { | |
| SuccessOrExit(error = otThreadSetFixedDuaInterfaceIdentifier(mInstance, nullptr)); | |
| } | |
| else | |
| { | |
| otIp6InterfaceIdentifier iid; | |
| SuccessOrExit(error = ParseAsHexString(aArgs[1], iid.mFields.m8)); | |
| SuccessOrExit(error = otThreadSetFixedDuaInterfaceIdentifier(mInstance, &iid)); | |
| } | |
| break; | |
| default: | |
| error = OT_ERROR_INVALID_ARGS; | |
| break; | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif // OPENTHREAD_CONFIG_DUA_ENABLE | |
| #endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) | |
| otError Interpreter::ProcessBufferInfo(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| OT_UNUSED_VARIABLE(aArgsLength); | |
| OT_UNUSED_VARIABLE(aArgs); | |
| otBufferInfo bufferInfo; | |
| otMessageGetBufferInfo(mInstance, &bufferInfo); | |
| OutputLine("total: %d", bufferInfo.mTotalBuffers); | |
| OutputLine("free: %d", bufferInfo.mFreeBuffers); | |
| OutputLine("6lo send: %d %d", bufferInfo.m6loSendMessages, bufferInfo.m6loSendBuffers); | |
| OutputLine("6lo reas: %d %d", bufferInfo.m6loReassemblyMessages, bufferInfo.m6loReassemblyBuffers); | |
| OutputLine("ip6: %d %d", bufferInfo.mIp6Messages, bufferInfo.mIp6Buffers); | |
| OutputLine("mpl: %d %d", bufferInfo.mMplMessages, bufferInfo.mMplBuffers); | |
| OutputLine("mle: %d %d", bufferInfo.mMleMessages, bufferInfo.mMleBuffers); | |
| OutputLine("arp: %d %d", bufferInfo.mArpMessages, bufferInfo.mArpBuffers); | |
| OutputLine("coap: %d %d", bufferInfo.mCoapMessages, bufferInfo.mCoapBuffers); | |
| OutputLine("coap secure: %d %d", bufferInfo.mCoapSecureMessages, bufferInfo.mCoapSecureBuffers); | |
| OutputLine("application coap: %d %d", bufferInfo.mApplicationCoapMessages, bufferInfo.mApplicationCoapBuffers); | |
| return OT_ERROR_NONE; | |
| } | |
| otError Interpreter::ProcessCcaThreshold(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| int8_t cca; | |
| if (aArgsLength == 0) | |
| { | |
| SuccessOrExit(error = otPlatRadioGetCcaEnergyDetectThreshold(mInstance, &cca)); | |
| OutputLine("%d dBm", cca); | |
| } | |
| else | |
| { | |
| SuccessOrExit(error = ParseAsInt8(aArgs[0], cca)); | |
| SuccessOrExit(error = otPlatRadioSetCcaEnergyDetectThreshold(mInstance, cca)); | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessChannel(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| uint8_t channel; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("%d", otLinkGetChannel(mInstance)); | |
| } | |
| else if (strcmp(aArgs[0], "supported") == 0) | |
| { | |
| OutputLine("0x%x", otPlatRadioGetSupportedChannelMask(mInstance)); | |
| } | |
| else if (strcmp(aArgs[0], "preferred") == 0) | |
| { | |
| OutputLine("0x%x", otPlatRadioGetPreferredChannelMask(mInstance)); | |
| } | |
| #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE | |
| else if (strcmp(aArgs[0], "monitor") == 0) | |
| { | |
| if (aArgsLength == 1) | |
| { | |
| OutputLine("enabled: %d", otChannelMonitorIsEnabled(mInstance)); | |
| if (otChannelMonitorIsEnabled(mInstance)) | |
| { | |
| uint32_t channelMask = otLinkGetSupportedChannelMask(mInstance); | |
| uint8_t channelNum = sizeof(channelMask) * CHAR_BIT; | |
| OutputLine("interval: %u", otChannelMonitorGetSampleInterval(mInstance)); | |
| OutputLine("threshold: %d", otChannelMonitorGetRssiThreshold(mInstance)); | |
| OutputLine("window: %u", otChannelMonitorGetSampleWindow(mInstance)); | |
| OutputLine("count: %u", otChannelMonitorGetSampleCount(mInstance)); | |
| OutputLine("occupancies:"); | |
| for (channel = 0; channel < channelNum; channel++) | |
| { | |
| uint32_t occupancy = 0; | |
| if (!((1UL << channel) & channelMask)) | |
| { | |
| continue; | |
| } | |
| occupancy = otChannelMonitorGetChannelOccupancy(mInstance, channel); | |
| OutputFormat("ch %d (0x%04x) ", channel, occupancy); | |
| occupancy = (occupancy * 10000) / 0xffff; | |
| OutputLine("%2d.%02d%% busy", occupancy / 100, occupancy % 100); | |
| } | |
| OutputLine(""); | |
| } | |
| } | |
| else if (strcmp(aArgs[1], "start") == 0) | |
| { | |
| error = otChannelMonitorSetEnabled(mInstance, true); | |
| } | |
| else if (strcmp(aArgs[1], "stop") == 0) | |
| { | |
| error = otChannelMonitorSetEnabled(mInstance, false); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| } | |
| #endif // OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE | |
| #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD | |
| else if (strcmp(aArgs[0], "manager") == 0) | |
| { | |
| if (aArgsLength == 1) | |
| { | |
| OutputLine("channel: %d", otChannelManagerGetRequestedChannel(mInstance)); | |
| OutputLine("auto: %d", otChannelManagerGetAutoChannelSelectionEnabled(mInstance)); | |
| if (otChannelManagerGetAutoChannelSelectionEnabled(mInstance)) | |
| { | |
| Mac::ChannelMask supportedMask(otChannelManagerGetSupportedChannels(mInstance)); | |
| Mac::ChannelMask favoredMask(otChannelManagerGetFavoredChannels(mInstance)); | |
| OutputLine("delay: %d", otChannelManagerGetDelay(mInstance)); | |
| OutputLine("interval: %u", otChannelManagerGetAutoChannelSelectionInterval(mInstance)); | |
| OutputLine("supported: %s", supportedMask.ToString().AsCString()); | |
| OutputLine("favored: %s", supportedMask.ToString().AsCString()); | |
| } | |
| } | |
| else if (strcmp(aArgs[1], "change") == 0) | |
| { | |
| VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsUint8(aArgs[2], channel)); | |
| otChannelManagerRequestChannelChange(mInstance, channel); | |
| } | |
| #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE | |
| else if (strcmp(aArgs[1], "select") == 0) | |
| { | |
| bool enable; | |
| VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsBool(aArgs[2], enable)); | |
| error = otChannelManagerRequestChannelSelect(mInstance, enable); | |
| } | |
| #endif | |
| else if (strcmp(aArgs[1], "auto") == 0) | |
| { | |
| bool enable; | |
| VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsBool(aArgs[2], enable)); | |
| otChannelManagerSetAutoChannelSelectionEnabled(mInstance, enable); | |
| } | |
| else if (strcmp(aArgs[1], "delay") == 0) | |
| { | |
| uint8_t delay; | |
| VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsUint8(aArgs[2], delay)); | |
| error = otChannelManagerSetDelay(mInstance, delay); | |
| } | |
| else if (strcmp(aArgs[1], "interval") == 0) | |
| { | |
| uint32_t interval; | |
| VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsUint32(aArgs[2], interval)); | |
| error = otChannelManagerSetAutoChannelSelectionInterval(mInstance, interval); | |
| } | |
| else if (strcmp(aArgs[1], "supported") == 0) | |
| { | |
| uint32_t mask; | |
| VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsUint32(aArgs[2], mask)); | |
| otChannelManagerSetSupportedChannels(mInstance, mask); | |
| } | |
| else if (strcmp(aArgs[1], "favored") == 0) | |
| { | |
| uint32_t mask; | |
| VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsUint32(aArgs[2], mask)); | |
| otChannelManagerSetFavoredChannels(mInstance, mask); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| } | |
| #endif // OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD | |
| else | |
| { | |
| SuccessOrExit(error = ParseAsUint8(aArgs[0], channel)); | |
| error = otLinkSetChannel(mInstance, channel); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #if OPENTHREAD_FTD | |
| otError Interpreter::ProcessChild(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| otChildInfo childInfo; | |
| uint16_t childId; | |
| bool isTable; | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); | |
| isTable = (strcmp(aArgs[0], "table") == 0); | |
| if (isTable || strcmp(aArgs[0], "list") == 0) | |
| { | |
| uint16_t maxChildren; | |
| if (isTable) | |
| { | |
| OutputLine( | |
| "| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt| Extended MAC |"); | |
| OutputLine( | |
| "+-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+------------------+"); | |
| } | |
| maxChildren = otThreadGetMaxAllowedChildren(mInstance); | |
| for (uint16_t i = 0; i < maxChildren; i++) | |
| { | |
| if ((otThreadGetChildInfoByIndex(mInstance, i, &childInfo) != OT_ERROR_NONE) || childInfo.mIsStateRestoring) | |
| { | |
| continue; | |
| } | |
| if (isTable) | |
| { | |
| OutputFormat("| %3d ", childInfo.mChildId); | |
| OutputFormat("| 0x%04x ", childInfo.mRloc16); | |
| OutputFormat("| %10d ", childInfo.mTimeout); | |
| OutputFormat("| %10d ", childInfo.mAge); | |
| OutputFormat("| %5d ", childInfo.mLinkQualityIn); | |
| OutputFormat("| %4d ", childInfo.mNetworkDataVersion); | |
| OutputFormat("|%1d", childInfo.mRxOnWhenIdle); | |
| OutputFormat("|%1d", childInfo.mFullThreadDevice); | |
| OutputFormat("|%1d", childInfo.mFullNetworkData); | |
| OutputFormat("|%3d", childInfo.mVersion); | |
| OutputFormat("| %1d ", childInfo.mIsCslSynced); | |
| OutputFormat("| %5d ", childInfo.mQueuedMessageCnt); | |
| OutputFormat("| "); | |
| OutputExtAddress(childInfo.mExtAddress); | |
| OutputLine(" |"); | |
| } | |
| else | |
| { | |
| OutputFormat("%d ", childInfo.mChildId); | |
| } | |
| } | |
| OutputLine(""); | |
| ExitNow(); | |
| } | |
| SuccessOrExit(error = ParseAsUint16(aArgs[0], childId)); | |
| SuccessOrExit(error = otThreadGetChildInfoById(mInstance, childId, &childInfo)); | |
| OutputLine("Child ID: %d", childInfo.mChildId); | |
| OutputLine("Rloc: %04x", childInfo.mRloc16); | |
| OutputFormat("Ext Addr: "); | |
| OutputExtAddress(childInfo.mExtAddress); | |
| OutputLine(""); | |
| OutputFormat("Mode: "); | |
| if (!(childInfo.mRxOnWhenIdle || childInfo.mFullThreadDevice || childInfo.mFullNetworkData)) | |
| { | |
| OutputFormat("-"); | |
| } | |
| else | |
| { | |
| if (childInfo.mRxOnWhenIdle) | |
| { | |
| OutputFormat("r"); | |
| } | |
| if (childInfo.mFullThreadDevice) | |
| { | |
| OutputFormat("d"); | |
| } | |
| if (childInfo.mFullNetworkData) | |
| { | |
| OutputFormat("n"); | |
| } | |
| } | |
| OutputLine(""); | |
| OutputLine("Net Data: %d", childInfo.mNetworkDataVersion); | |
| OutputLine("Timeout: %d", childInfo.mTimeout); | |
| OutputLine("Age: %d", childInfo.mAge); | |
| OutputLine("Link Quality In: %d", childInfo.mLinkQualityIn); | |
| OutputLine("RSSI: %d", childInfo.mAverageRssi); | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessChildIp(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| uint16_t maxChildren = otThreadGetMaxAllowedChildren(mInstance); | |
| for (uint16_t childIndex = 0; childIndex < maxChildren; childIndex++) | |
| { | |
| otChildIp6AddressIterator iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT; | |
| otIp6Address ip6Address; | |
| otChildInfo childInfo; | |
| if ((otThreadGetChildInfoByIndex(mInstance, childIndex, &childInfo) != OT_ERROR_NONE) || | |
| childInfo.mIsStateRestoring) | |
| { | |
| continue; | |
| } | |
| iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT; | |
| while (otThreadGetChildNextIp6Address(mInstance, childIndex, &iterator, &ip6Address) == OT_ERROR_NONE) | |
| { | |
| OutputFormat("%04x: ", childInfo.mRloc16); | |
| OutputIp6Address(ip6Address); | |
| OutputLine(""); | |
| } | |
| } | |
| } | |
| else if (strcmp(aArgs[0], "max") == 0) | |
| { | |
| if (aArgsLength == 1) | |
| { | |
| OutputLine("%d", otThreadGetMaxChildIpAddresses(mInstance)); | |
| } | |
| #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE | |
| else if (aArgsLength == 2) | |
| { | |
| uint8_t maxIpAddresses; | |
| SuccessOrExit(error = ParseAsUint8(aArgs[1], maxIpAddresses)); | |
| SuccessOrExit(error = otThreadSetMaxChildIpAddresses(mInstance, maxIpAddresses)); | |
| } | |
| #endif | |
| else | |
| { | |
| error = OT_ERROR_INVALID_ARGS; | |
| } | |
| } | |
| else | |
| { | |
| error = OT_ERROR_INVALID_COMMAND; | |
| } | |
| #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE | |
| exit: | |
| #endif | |
| return error; | |
| } | |
| otError Interpreter::ProcessChildMax(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("%d", otThreadGetMaxAllowedChildren(mInstance)); | |
| } | |
| else | |
| { | |
| uint16_t maxChildren; | |
| SuccessOrExit(error = ParseAsUint16(aArgs[0], maxChildren)); | |
| SuccessOrExit(error = otThreadSetMaxAllowedChildren(mInstance, maxChildren)); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif // OPENTHREAD_FTD | |
| #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE | |
| otError Interpreter::ProcessChildSupervision(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| uint16_t value; | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); | |
| if (strcmp(aArgs[0], "checktimeout") == 0) | |
| { | |
| if (aArgsLength == 1) | |
| { | |
| OutputLine("%u", otChildSupervisionGetCheckTimeout(mInstance)); | |
| } | |
| else if (aArgsLength == 2) | |
| { | |
| SuccessOrExit(error = ParseAsUint16(aArgs[1], value)); | |
| otChildSupervisionSetCheckTimeout(mInstance, value); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| } | |
| #if OPENTHREAD_FTD | |
| else if (strcmp(aArgs[0], "interval") == 0) | |
| { | |
| if (aArgsLength == 1) | |
| { | |
| OutputLine("%u", otChildSupervisionGetInterval(mInstance)); | |
| } | |
| else if (aArgsLength == 2) | |
| { | |
| SuccessOrExit(error = ParseAsUint16(aArgs[1], value)); | |
| otChildSupervisionSetInterval(mInstance, value); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| } | |
| #endif | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif // OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE | |
| otError Interpreter::ProcessChildTimeout(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("%d", otThreadGetChildTimeout(mInstance)); | |
| } | |
| else | |
| { | |
| uint32_t timeout; | |
| SuccessOrExit(error = ParseAsUint32(aArgs[0], timeout)); | |
| otThreadSetChildTimeout(mInstance, timeout); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #if OPENTHREAD_CONFIG_COAP_API_ENABLE | |
| otError Interpreter::ProcessCoap(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| return mCoap.Process(aArgsLength, aArgs); | |
| } | |
| #endif | |
| #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE | |
| otError Interpreter::ProcessCoapSecure(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| return mCoapSecure.Process(aArgsLength, aArgs); | |
| } | |
| #endif | |
| #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE | |
| otError Interpreter::ProcessCoexMetrics(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine(otPlatRadioIsCoexEnabled(mInstance) ? "Enabled" : "Disabled"); | |
| } | |
| else if (strcmp(aArgs[0], "enable") == 0) | |
| { | |
| error = otPlatRadioSetCoexEnabled(mInstance, true); | |
| } | |
| else if (strcmp(aArgs[0], "disable") == 0) | |
| { | |
| error = otPlatRadioSetCoexEnabled(mInstance, false); | |
| } | |
| else if (strcmp(aArgs[0], "metrics") == 0) | |
| { | |
| otRadioCoexMetrics metrics; | |
| SuccessOrExit(error = otPlatRadioGetCoexMetrics(mInstance, &metrics)); | |
| OutputLine("Stopped: %s", metrics.mStopped ? "true" : "false"); | |
| OutputLine("Grant Glitch: %u", metrics.mNumGrantGlitch); | |
| OutputLine("Transmit metrics"); | |
| OutputLine(kIndentSize, "Request: %u", metrics.mNumTxRequest); | |
| OutputLine(kIndentSize, "Grant Immediate: %u", metrics.mNumTxGrantImmediate); | |
| OutputLine(kIndentSize, "Grant Wait: %u", metrics.mNumTxGrantWait); | |
| OutputLine(kIndentSize, "Grant Wait Activated: %u", metrics.mNumTxGrantWaitActivated); | |
| OutputLine(kIndentSize, "Grant Wait Timeout: %u", metrics.mNumTxGrantWaitTimeout); | |
| OutputLine(kIndentSize, "Grant Deactivated During Request: %u", metrics.mNumTxGrantDeactivatedDuringRequest); | |
| OutputLine(kIndentSize, "Delayed Grant: %u", metrics.mNumTxDelayedGrant); | |
| OutputLine(kIndentSize, "Average Request To Grant Time: %u", metrics.mAvgTxRequestToGrantTime); | |
| OutputLine("Receive metrics"); | |
| OutputLine(kIndentSize, "Request: %u", metrics.mNumRxRequest); | |
| OutputLine(kIndentSize, "Grant Immediate: %u", metrics.mNumRxGrantImmediate); | |
| OutputLine(kIndentSize, "Grant Wait: %u", metrics.mNumRxGrantWait); | |
| OutputLine(kIndentSize, "Grant Wait Activated: %u", metrics.mNumRxGrantWaitActivated); | |
| OutputLine(kIndentSize, "Grant Wait Timeout: %u", metrics.mNumRxGrantWaitTimeout); | |
| OutputLine(kIndentSize, "Grant Deactivated During Request: %u", metrics.mNumRxGrantDeactivatedDuringRequest); | |
| OutputLine(kIndentSize, "Delayed Grant: %u", metrics.mNumRxDelayedGrant); | |
| OutputLine(kIndentSize, "Average Request To Grant Time: %u", metrics.mAvgRxRequestToGrantTime); | |
| OutputLine(kIndentSize, "Grant None: %u", metrics.mNumRxGrantNone); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif // OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE | |
| #if OPENTHREAD_FTD | |
| otError Interpreter::ProcessContextIdReuseDelay(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("%d", otThreadGetContextIdReuseDelay(mInstance)); | |
| } | |
| else | |
| { | |
| uint32_t delay; | |
| SuccessOrExit(error = ParseAsUint32(aArgs[0], delay)); | |
| otThreadSetContextIdReuseDelay(mInstance, delay); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif | |
| otError Interpreter::ProcessCounters(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("mac"); | |
| OutputLine("mle"); | |
| } | |
| else if (strcmp(aArgs[0], "mac") == 0) | |
| { | |
| if (aArgsLength == 1) | |
| { | |
| const otMacCounters *macCounters = otLinkGetCounters(mInstance); | |
| OutputLine("TxTotal: %d", macCounters->mTxTotal); | |
| OutputLine(kIndentSize, "TxUnicast: %d", macCounters->mTxUnicast); | |
| OutputLine(kIndentSize, "TxBroadcast: %d", macCounters->mTxBroadcast); | |
| OutputLine(kIndentSize, "TxAckRequested: %d", macCounters->mTxAckRequested); | |
| OutputLine(kIndentSize, "TxAcked: %d", macCounters->mTxAcked); | |
| OutputLine(kIndentSize, "TxNoAckRequested: %d", macCounters->mTxNoAckRequested); | |
| OutputLine(kIndentSize, "TxData: %d", macCounters->mTxData); | |
| OutputLine(kIndentSize, "TxDataPoll: %d", macCounters->mTxDataPoll); | |
| OutputLine(kIndentSize, "TxBeacon: %d", macCounters->mTxBeacon); | |
| OutputLine(kIndentSize, "TxBeaconRequest: %d", macCounters->mTxBeaconRequest); | |
| OutputLine(kIndentSize, "TxOther: %d", macCounters->mTxOther); | |
| OutputLine(kIndentSize, "TxRetry: %d", macCounters->mTxRetry); | |
| OutputLine(kIndentSize, "TxErrCca: %d", macCounters->mTxErrCca); | |
| OutputLine(kIndentSize, "TxErrBusyChannel: %d", macCounters->mTxErrBusyChannel); | |
| OutputLine("RxTotal: %d", macCounters->mRxTotal); | |
| OutputLine(kIndentSize, "RxUnicast: %d", macCounters->mRxUnicast); | |
| OutputLine(kIndentSize, "RxBroadcast: %d", macCounters->mRxBroadcast); | |
| OutputLine(kIndentSize, "RxData: %d", macCounters->mRxData); | |
| OutputLine(kIndentSize, "RxDataPoll: %d", macCounters->mRxDataPoll); | |
| OutputLine(kIndentSize, "RxBeacon: %d", macCounters->mRxBeacon); | |
| OutputLine(kIndentSize, "RxBeaconRequest: %d", macCounters->mRxBeaconRequest); | |
| OutputLine(kIndentSize, "RxOther: %d", macCounters->mRxOther); | |
| OutputLine(kIndentSize, "RxAddressFiltered: %d", macCounters->mRxAddressFiltered); | |
| OutputLine(kIndentSize, "RxDestAddrFiltered: %d", macCounters->mRxDestAddrFiltered); | |
| OutputLine(kIndentSize, "RxDuplicated: %d", macCounters->mRxDuplicated); | |
| OutputLine(kIndentSize, "RxErrNoFrame: %d", macCounters->mRxErrNoFrame); | |
| OutputLine(kIndentSize, "RxErrNoUnknownNeighbor: %d", macCounters->mRxErrUnknownNeighbor); | |
| OutputLine(kIndentSize, "RxErrInvalidSrcAddr: %d", macCounters->mRxErrInvalidSrcAddr); | |
| OutputLine(kIndentSize, "RxErrSec: %d", macCounters->mRxErrSec); | |
| OutputLine(kIndentSize, "RxErrFcs: %d", macCounters->mRxErrFcs); | |
| OutputLine(kIndentSize, "RxErrOther: %d", macCounters->mRxErrOther); | |
| } | |
| else if ((aArgsLength == 2) && (strcmp(aArgs[1], "reset") == 0)) | |
| { | |
| otLinkResetCounters(mInstance); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| } | |
| else if (strcmp(aArgs[0], "mle") == 0) | |
| { | |
| if (aArgsLength == 1) | |
| { | |
| const otMleCounters *mleCounters = otThreadGetMleCounters(mInstance); | |
| OutputLine("Role Disabled: %d", mleCounters->mDisabledRole); | |
| OutputLine("Role Detached: %d", mleCounters->mDetachedRole); | |
| OutputLine("Role Child: %d", mleCounters->mChildRole); | |
| OutputLine("Role Router: %d", mleCounters->mRouterRole); | |
| OutputLine("Role Leader: %d", mleCounters->mLeaderRole); | |
| OutputLine("Attach Attempts: %d", mleCounters->mAttachAttempts); | |
| OutputLine("Partition Id Changes: %d", mleCounters->mPartitionIdChanges); | |
| OutputLine("Better Partition Attach Attempts: %d", mleCounters->mBetterPartitionAttachAttempts); | |
| OutputLine("Parent Changes: %d", mleCounters->mParentChanges); | |
| } | |
| else if ((aArgsLength == 2) && (strcmp(aArgs[1], "reset") == 0)) | |
| { | |
| otThreadResetMleCounters(mInstance); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE | |
| otError Interpreter::ProcessCsl(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_INVALID_ARGS; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("Channel: %u", otLinkCslGetChannel(mInstance)); | |
| OutputLine("Period: %u(in units of 10 symbols), %ums", otLinkCslGetPeriod(mInstance), | |
| otLinkCslGetPeriod(mInstance) * kUsPerTenSymbols / 1000); | |
| OutputLine("Timeout: %us", otLinkCslGetTimeout(mInstance)); | |
| error = OT_ERROR_NONE; | |
| } | |
| else if (aArgsLength == 2) | |
| { | |
| if (strcmp(aArgs[0], "channel") == 0) | |
| { | |
| uint8_t channel; | |
| SuccessOrExit(error = ParseAsUint8(aArgs[1], channel)); | |
| SuccessOrExit(error = otLinkCslSetChannel(mInstance, channel)); | |
| } | |
| else if (strcmp(aArgs[0], "period") == 0) | |
| { | |
| uint16_t period; | |
| SuccessOrExit(error = ParseAsUint16(aArgs[1], period)); | |
| SuccessOrExit(error = otLinkCslSetPeriod(mInstance, period)); | |
| } | |
| else if (strcmp(aArgs[0], "timeout") == 0) | |
| { | |
| uint32_t timeout; | |
| SuccessOrExit(error = ParseAsUint32(aArgs[1], timeout)); | |
| SuccessOrExit(error = otLinkCslSetTimeout(mInstance, timeout)); | |
| } | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE | |
| #if OPENTHREAD_FTD | |
| otError Interpreter::ProcessDelayTimerMin(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("%d", (otDatasetGetDelayTimerMinimal(mInstance) / 1000)); | |
| } | |
| else if (aArgsLength == 1) | |
| { | |
| uint32_t delay; | |
| SuccessOrExit(error = ParseAsUint32(aArgs[0], delay)); | |
| SuccessOrExit(error = otDatasetSetDelayTimerMinimal(mInstance, static_cast<uint32_t>(delay * 1000))); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif | |
| otError Interpreter::ProcessDiscover(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| uint32_t scanChannels = 0; | |
| if (aArgsLength > 0) | |
| { | |
| uint8_t channel; | |
| SuccessOrExit(error = ParseAsUint8(aArgs[0], channel)); | |
| VerifyOrExit(channel < sizeof(scanChannels) * CHAR_BIT, error = OT_ERROR_INVALID_ARGS); | |
| scanChannels = 1 << channel; | |
| } | |
| SuccessOrExit(error = otThreadDiscover(mInstance, scanChannels, OT_PANID_BROADCAST, false, false, | |
| &Interpreter::HandleActiveScanResult, this)); | |
| OutputLine("| J | Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI |"); | |
| OutputLine("+---+------------------+------------------+------+------------------+----+-----+-----+"); | |
| error = OT_ERROR_PENDING; | |
| exit: | |
| return error; | |
| } | |
| #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE | |
| otError Interpreter::ProcessDns(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| uint16_t port = OT_DNS_DEFAULT_SERVER_PORT; | |
| Ip6::MessageInfo messageInfo; | |
| otDnsQuery query; | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); | |
| if (strcmp(aArgs[0], "resolve") == 0) | |
| { | |
| VerifyOrExit(!mResolvingInProgress, error = OT_ERROR_BUSY); | |
| VerifyOrExit(aArgsLength > 1, error = OT_ERROR_INVALID_ARGS); | |
| VerifyOrExit(strlen(aArgs[1]) < OT_DNS_MAX_HOSTNAME_LENGTH, error = OT_ERROR_INVALID_ARGS); | |
| strcpy(mResolvingHostname, aArgs[1]); | |
| if (aArgsLength > 2) | |
| { | |
| SuccessOrExit(error = ParseAsIp6Address(aArgs[2], messageInfo.GetPeerAddr())); | |
| } | |
| else | |
| { | |
| // Use IPv6 address of default DNS server. | |
| SuccessOrExit(error = messageInfo.GetPeerAddr().FromString(OT_DNS_DEFAULT_SERVER_IP)); | |
| } | |
| if (aArgsLength > 3) | |
| { | |
| SuccessOrExit(error = ParseAsUint16(aArgs[3], port)); | |
| } | |
| messageInfo.SetPeerPort(port); | |
| query.mHostname = mResolvingHostname; | |
| query.mMessageInfo = static_cast<const otMessageInfo *>(&messageInfo); | |
| query.mNoRecursion = false; | |
| SuccessOrExit(error = otDnsClientQuery(mInstance, &query, &Interpreter::HandleDnsResponse, this)); | |
| mResolvingInProgress = true; | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_COMMAND); | |
| } | |
| error = OT_ERROR_PENDING; | |
| exit: | |
| return error; | |
| } | |
| void Interpreter::HandleDnsResponse(void * aContext, | |
| const char * aHostname, | |
| const otIp6Address *aAddress, | |
| uint32_t aTtl, | |
| otError aResult) | |
| { | |
| static_cast<Interpreter *>(aContext)->HandleDnsResponse(aHostname, static_cast<const Ip6::Address *>(aAddress), | |
| aTtl, aResult); | |
| } | |
| void Interpreter::HandleDnsResponse(const char *aHostname, const Ip6::Address *aAddress, uint32_t aTtl, otError aResult) | |
| { | |
| OutputFormat("DNS response for %s - ", aHostname); | |
| if (aResult == OT_ERROR_NONE) | |
| { | |
| if (aAddress != nullptr) | |
| { | |
| OutputIp6Address(*aAddress); | |
| } | |
| OutputLine(" TTL: %d", aTtl); | |
| } | |
| OutputResult(aResult); | |
| mResolvingInProgress = false; | |
| } | |
| #endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE | |
| #if OPENTHREAD_FTD | |
| otError Interpreter::ProcessEidCache(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| OT_UNUSED_VARIABLE(aArgsLength); | |
| OT_UNUSED_VARIABLE(aArgs); | |
| otCacheEntryIterator iterator; | |
| otCacheEntryInfo entry; | |
| memset(&iterator, 0, sizeof(iterator)); | |
| for (uint8_t i = 0;; i++) | |
| { | |
| SuccessOrExit(otThreadGetNextCacheEntry(mInstance, &entry, &iterator)); | |
| OutputIp6Address(entry.mTarget); | |
| OutputLine(" %04x", entry.mRloc16); | |
| } | |
| exit: | |
| return OT_ERROR_NONE; | |
| } | |
| #endif | |
| otError Interpreter::ProcessEui64(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| OT_UNUSED_VARIABLE(aArgs); | |
| otError error = OT_ERROR_NONE; | |
| otExtAddress extAddress; | |
| VerifyOrExit(aArgsLength == 0, error = OT_ERROR_INVALID_ARGS); | |
| otLinkGetFactoryAssignedIeeeEui64(mInstance, &extAddress); | |
| OutputBytes(extAddress.m8, OT_EXT_ADDRESS_SIZE); | |
| OutputLine(""); | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessExtAddress(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| const uint8_t *extAddress = reinterpret_cast<const uint8_t *>(otLinkGetExtendedAddress(mInstance)); | |
| OutputBytes(extAddress, OT_EXT_ADDRESS_SIZE); | |
| OutputLine(""); | |
| } | |
| else | |
| { | |
| otExtAddress extAddress; | |
| SuccessOrExit(error = ParseAsHexString(aArgs[0], extAddress.m8)); | |
| error = otLinkSetExtendedAddress(mInstance, &extAddress); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #if OPENTHREAD_POSIX | |
| otError Interpreter::ProcessExit(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| OT_UNUSED_VARIABLE(aArgsLength); | |
| OT_UNUSED_VARIABLE(aArgs); | |
| exit(EXIT_SUCCESS); | |
| return OT_ERROR_NONE; | |
| } | |
| #endif | |
| otError Interpreter::ProcessLog(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| VerifyOrExit(aArgsLength >= 1, error = OT_ERROR_INVALID_ARGS); | |
| if (!strcmp(aArgs[0], "level")) | |
| { | |
| if (aArgsLength == 1) | |
| { | |
| OutputLine("%d", otLoggingGetLevel()); | |
| } | |
| #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE | |
| else if (aArgsLength == 2) | |
| { | |
| uint8_t level; | |
| SuccessOrExit(error = ParseAsUint8(aArgs[1], level)); | |
| SuccessOrExit(error = otLoggingSetLevel(static_cast<otLogLevel>(level))); | |
| } | |
| #endif | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| } | |
| #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX | |
| else if (!strcmp(aArgs[0], "filename")) | |
| { | |
| VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = otPlatDebugUart_logfile(aArgs[1])); | |
| } | |
| #endif | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessExtPanId(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| const uint8_t *extPanId = reinterpret_cast<const uint8_t *>(otThreadGetExtendedPanId(mInstance)); | |
| OutputBytes(extPanId, OT_EXT_PAN_ID_SIZE); | |
| OutputLine(""); | |
| } | |
| else | |
| { | |
| otExtendedPanId extPanId; | |
| SuccessOrExit(error = ParseAsHexString(aArgs[0], extPanId.m8)); | |
| error = otThreadSetExtendedPanId(mInstance, &extPanId); | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessFactoryReset(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| OT_UNUSED_VARIABLE(aArgsLength); | |
| OT_UNUSED_VARIABLE(aArgs); | |
| otInstanceFactoryReset(mInstance); | |
| return OT_ERROR_NONE; | |
| } | |
| #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE | |
| otError Interpreter::ProcessFake(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_INVALID_COMMAND; | |
| VerifyOrExit(aArgsLength >= 1); | |
| if (strcmp(aArgs[0], "/a/an") == 0) | |
| { | |
| otIp6Address destination, target; | |
| otIp6InterfaceIdentifier mlIid; | |
| VerifyOrExit(aArgsLength == 4, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsIp6Address(aArgs[1], destination)); | |
| SuccessOrExit(error = ParseAsIp6Address(aArgs[2], target)); | |
| SuccessOrExit(error = ParseAsHexString(aArgs[3], mlIid.mFields.m8)); | |
| otThreadSendAddressNotification(mInstance, &destination, &target, &mlIid); | |
| } | |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE | |
| else if (strcmp(aArgs[0], "/b/ba") == 0) | |
| { | |
| otIp6Address target; | |
| otIp6InterfaceIdentifier mlIid; | |
| uint32_t timeSinceLastTransaction; | |
| VerifyOrExit(aArgsLength == 4, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsIp6Address(aArgs[1], target)); | |
| SuccessOrExit(error = ParseAsHexString(aArgs[2], mlIid.mFields.m8)); | |
| SuccessOrExit(error = ParseAsUint32(aArgs[3], timeSinceLastTransaction)); | |
| error = otThreadSendProactiveBackboneNotification(mInstance, &target, &mlIid, timeSinceLastTransaction); | |
| } | |
| #endif | |
| exit: | |
| return error; | |
| } | |
| #endif | |
| otError Interpreter::ProcessFem(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| int8_t lnaGain; | |
| SuccessOrExit(error = otPlatRadioGetFemLnaGain(mInstance, &lnaGain)); | |
| OutputLine("LNA gain %d dBm", lnaGain); | |
| } | |
| else if (strcmp(aArgs[0], "lnagain") == 0) | |
| { | |
| if (aArgsLength == 1) | |
| { | |
| int8_t lnaGain; | |
| SuccessOrExit(error = otPlatRadioGetFemLnaGain(mInstance, &lnaGain)); | |
| OutputLine("%d", lnaGain); | |
| } | |
| else | |
| { | |
| int8_t lnaGain; | |
| SuccessOrExit(error = ParseAsInt8(aArgs[1], lnaGain)); | |
| SuccessOrExit(error = otPlatRadioSetFemLnaGain(mInstance, lnaGain)); | |
| } | |
| } | |
| else | |
| { | |
| error = OT_ERROR_INVALID_ARGS; | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessIfconfig(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| if (otIp6IsEnabled(mInstance)) | |
| { | |
| OutputLine("up"); | |
| } | |
| else | |
| { | |
| OutputLine("down"); | |
| } | |
| } | |
| else if (strcmp(aArgs[0], "up") == 0) | |
| { | |
| SuccessOrExit(error = otIp6SetEnabled(mInstance, true)); | |
| } | |
| else if (strcmp(aArgs[0], "down") == 0) | |
| { | |
| SuccessOrExit(error = otIp6SetEnabled(mInstance, false)); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessIpAddrAdd(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error; | |
| otNetifAddress aAddress; | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsIp6Address(aArgs[0], aAddress.mAddress)); | |
| aAddress.mPrefixLength = 64; | |
| aAddress.mPreferred = true; | |
| aAddress.mValid = true; | |
| aAddress.mAddressOrigin = OT_ADDRESS_ORIGIN_MANUAL; | |
| error = otIp6AddUnicastAddress(mInstance, &aAddress); | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessIpAddrDel(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error; | |
| otIp6Address address; | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsIp6Address(aArgs[0], address)); | |
| error = otIp6RemoveUnicastAddress(mInstance, &address); | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessIpAddr(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| const otNetifAddress *unicastAddrs = otIp6GetUnicastAddresses(mInstance); | |
| for (const otNetifAddress *addr = unicastAddrs; addr; addr = addr->mNext) | |
| { | |
| OutputIp6Address(addr->mAddress); | |
| OutputLine(""); | |
| } | |
| } | |
| else | |
| { | |
| if (strcmp(aArgs[0], "add") == 0) | |
| { | |
| SuccessOrExit(error = ProcessIpAddrAdd(aArgsLength - 1, aArgs + 1)); | |
| } | |
| else if (strcmp(aArgs[0], "del") == 0) | |
| { | |
| SuccessOrExit(error = ProcessIpAddrDel(aArgsLength - 1, aArgs + 1)); | |
| } | |
| else if (strcmp(aArgs[0], "linklocal") == 0) | |
| { | |
| OutputIp6Address(*otThreadGetLinkLocalIp6Address(mInstance)); | |
| OutputLine(""); | |
| } | |
| else if (strcmp(aArgs[0], "rloc") == 0) | |
| { | |
| OutputIp6Address(*otThreadGetRloc(mInstance)); | |
| OutputLine(""); | |
| } | |
| else if (strcmp(aArgs[0], "mleid") == 0) | |
| { | |
| OutputIp6Address(*otThreadGetMeshLocalEid(mInstance)); | |
| OutputLine(""); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_COMMAND); | |
| } | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessIpMulticastAddrAdd(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error; | |
| otIp6Address address; | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsIp6Address(aArgs[0], address)); | |
| error = otIp6SubscribeMulticastAddress(mInstance, &address); | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessIpMulticastAddrDel(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error; | |
| otIp6Address address; | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsIp6Address(aArgs[0], address)); | |
| error = otIp6UnsubscribeMulticastAddress(mInstance, &address); | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessMulticastPromiscuous(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| if (otIp6IsMulticastPromiscuousEnabled(mInstance)) | |
| { | |
| OutputLine("Enabled"); | |
| } | |
| else | |
| { | |
| OutputLine("Disabled"); | |
| } | |
| } | |
| else | |
| { | |
| if (strcmp(aArgs[0], "enable") == 0) | |
| { | |
| otIp6SetMulticastPromiscuousEnabled(mInstance, true); | |
| } | |
| else if (strcmp(aArgs[0], "disable") == 0) | |
| { | |
| otIp6SetMulticastPromiscuousEnabled(mInstance, false); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessIpMulticastAddr(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| for (const otNetifMulticastAddress *addr = otIp6GetMulticastAddresses(mInstance); addr; addr = addr->mNext) | |
| { | |
| OutputIp6Address(addr->mAddress); | |
| OutputLine(""); | |
| } | |
| } | |
| else | |
| { | |
| if (strcmp(aArgs[0], "add") == 0) | |
| { | |
| SuccessOrExit(error = ProcessIpMulticastAddrAdd(aArgsLength - 1, aArgs + 1)); | |
| } | |
| else if (strcmp(aArgs[0], "del") == 0) | |
| { | |
| SuccessOrExit(error = ProcessIpMulticastAddrDel(aArgsLength - 1, aArgs + 1)); | |
| } | |
| else if (strcmp(aArgs[0], "promiscuous") == 0) | |
| { | |
| SuccessOrExit(error = ProcessMulticastPromiscuous(aArgsLength - 1, aArgs + 1)); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_COMMAND); | |
| } | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessKeySequence(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| VerifyOrExit(aArgsLength == 1 || aArgsLength == 2, error = OT_ERROR_INVALID_ARGS); | |
| if (strcmp(aArgs[0], "counter") == 0) | |
| { | |
| if (aArgsLength == 1) | |
| { | |
| OutputLine("%d", otThreadGetKeySequenceCounter(mInstance)); | |
| } | |
| else | |
| { | |
| uint32_t counter; | |
| SuccessOrExit(error = ParseAsUint32(aArgs[1], counter)); | |
| otThreadSetKeySequenceCounter(mInstance, counter); | |
| } | |
| } | |
| else if (strcmp(aArgs[0], "guardtime") == 0) | |
| { | |
| if (aArgsLength == 1) | |
| { | |
| OutputLine("%d", otThreadGetKeySwitchGuardTime(mInstance)); | |
| } | |
| else | |
| { | |
| uint32_t guardTime; | |
| SuccessOrExit(error = ParseAsUint32(aArgs[1], guardTime)); | |
| otThreadSetKeySwitchGuardTime(mInstance, guardTime); | |
| } | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessLeaderData(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| OT_UNUSED_VARIABLE(aArgsLength); | |
| OT_UNUSED_VARIABLE(aArgs); | |
| otError error; | |
| otLeaderData leaderData; | |
| SuccessOrExit(error = otThreadGetLeaderData(mInstance, &leaderData)); | |
| OutputLine("Partition ID: %u", leaderData.mPartitionId); | |
| OutputLine("Weighting: %d", leaderData.mWeighting); | |
| OutputLine("Data Version: %d", leaderData.mDataVersion); | |
| OutputLine("Stable Data Version: %d", leaderData.mStableDataVersion); | |
| OutputLine("Leader Router ID: %d", leaderData.mLeaderRouterId); | |
| exit: | |
| return error; | |
| } | |
| #if OPENTHREAD_FTD | |
| otError Interpreter::ProcessPartitionId(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| OT_UNUSED_VARIABLE(aArgs); | |
| otError error = OT_ERROR_INVALID_COMMAND; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("%u", otThreadGetPartitionId(mInstance)); | |
| error = OT_ERROR_NONE; | |
| } | |
| #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE | |
| else if (strcmp(aArgs[0], "preferred") == 0) | |
| { | |
| if (aArgsLength == 1) | |
| { | |
| OutputLine("%u", otThreadGetPreferredLeaderPartitionId(mInstance)); | |
| error = OT_ERROR_NONE; | |
| } | |
| else if (aArgsLength == 2) | |
| { | |
| uint32_t partitionId; | |
| SuccessOrExit(error = ParseAsUint32(aArgs[1], partitionId)); | |
| otThreadSetPreferredLeaderPartitionId(mInstance, partitionId); | |
| } | |
| } | |
| exit: | |
| #endif | |
| return error; | |
| } | |
| otError Interpreter::ProcessLeaderWeight(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("%d", otThreadGetLocalLeaderWeight(mInstance)); | |
| } | |
| else | |
| { | |
| uint8_t weight; | |
| SuccessOrExit(error = ParseAsUint8(aArgs[0], weight)); | |
| otThreadSetLocalLeaderWeight(mInstance, weight); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif // OPENTHREAD_FTD | |
| #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE | |
| void Interpreter::HandleLinkMetricsReport(const otIp6Address * aAddress, | |
| const otLinkMetricsValues *aMetricsValues, | |
| uint8_t aStatus, | |
| void * aContext) | |
| { | |
| static_cast<Interpreter *>(aContext)->HandleLinkMetricsReport(aAddress, aMetricsValues, aStatus); | |
| } | |
| void Interpreter::PrintLinkMetricsValue(const otLinkMetricsValues *aMetricsValues) | |
| { | |
| const char kLinkMetricsTypeCount[] = "(Count/Summation)"; | |
| const char kLinkMetricsTypeAverage[] = "(Exponential Moving Average)"; | |
| if (aMetricsValues->mMetrics.mPduCount) | |
| { | |
| OutputLine(" - PDU Counter: %d %s", aMetricsValues->mPduCountValue, kLinkMetricsTypeCount); | |
| } | |
| if (aMetricsValues->mMetrics.mLqi) | |
| { | |
| OutputLine(" - LQI: %d %s", aMetricsValues->mLqiValue, kLinkMetricsTypeAverage); | |
| } | |
| if (aMetricsValues->mMetrics.mLinkMargin) | |
| { | |
| OutputLine(" - Margin: %d (dB) %s", aMetricsValues->mLinkMarginValue, kLinkMetricsTypeAverage); | |
| } | |
| if (aMetricsValues->mMetrics.mRssi) | |
| { | |
| OutputLine(" - RSSI: %d (dBm) %s", aMetricsValues->mRssiValue, kLinkMetricsTypeAverage); | |
| } | |
| } | |
| void Interpreter::HandleLinkMetricsReport(const otIp6Address * aAddress, | |
| const otLinkMetricsValues *aMetricsValues, | |
| uint8_t aStatus) | |
| { | |
| OutputFormat("Received Link Metrics Report from: "); | |
| OutputIp6Address(*aAddress); | |
| OutputLine(""); | |
| if (aMetricsValues != nullptr) | |
| { | |
| PrintLinkMetricsValue(aMetricsValues); | |
| } | |
| else | |
| { | |
| OutputLine("Link Metrics Report, status: %s", LinkMetricsStatusToStr(aStatus)); | |
| } | |
| } | |
| void Interpreter::HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, uint8_t aStatus, void *aContext) | |
| { | |
| static_cast<Interpreter *>(aContext)->HandleLinkMetricsMgmtResponse(aAddress, aStatus); | |
| } | |
| void Interpreter::HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, uint8_t aStatus) | |
| { | |
| OutputFormat("Received Link Metrics Management Response from: "); | |
| OutputIp6Address(*aAddress); | |
| OutputLine(""); | |
| OutputLine("Status: %s", LinkMetricsStatusToStr(aStatus)); | |
| } | |
| void Interpreter::HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress, | |
| const otExtAddress * aExtAddress, | |
| const otLinkMetricsValues *aMetricsValues, | |
| void * aContext) | |
| { | |
| static_cast<Interpreter *>(aContext)->HandleLinkMetricsEnhAckProbingIe(aShortAddress, aExtAddress, aMetricsValues); | |
| } | |
| void Interpreter::HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress, | |
| const otExtAddress * aExtAddress, | |
| const otLinkMetricsValues *aMetricsValues) | |
| { | |
| OutputFormat("Received Link Metrics data in Enh Ack from neighbor, short address:0x%02x , extended address:", | |
| aShortAddress); | |
| OutputExtAddress(*aExtAddress); | |
| OutputLine(""); | |
| if (aMetricsValues != nullptr) | |
| { | |
| PrintLinkMetricsValue(aMetricsValues); | |
| } | |
| } | |
| const char *Interpreter::LinkMetricsStatusToStr(uint8_t aStatus) | |
| { | |
| uint8_t strIndex = 0; | |
| static const char *linkMetricsStatusText[] = { | |
| "Success", | |
| "Cannot support new series", | |
| "Series ID already registered", | |
| "Series ID not recognized", | |
| "No matching series ID", | |
| "Other error", | |
| "Unknown error", | |
| }; | |
| switch (aStatus) | |
| { | |
| case OT_LINK_METRICS_STATUS_SUCCESS: | |
| strIndex = 0; | |
| break; | |
| case OT_LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES: | |
| strIndex = 1; | |
| break; | |
| case OT_LINK_METRICS_STATUS_SERIESID_ALREADY_REGISTERED: | |
| strIndex = 2; | |
| break; | |
| case OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED: | |
| strIndex = 3; | |
| break; | |
| case OT_LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED: | |
| strIndex = 4; | |
| break; | |
| case OT_LINK_METRICS_STATUS_OTHER_ERROR: | |
| strIndex = 5; | |
| break; | |
| default: | |
| strIndex = 6; | |
| break; | |
| } | |
| return linkMetricsStatusText[strIndex]; | |
| } | |
| otError Interpreter::ProcessLinkMetrics(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_INVALID_COMMAND; | |
| VerifyOrExit(aArgsLength >= 1); | |
| if (strcmp(aArgs[0], "query") == 0) | |
| { | |
| error = ProcessLinkMetricsQuery(aArgsLength - 1, aArgs + 1); | |
| } | |
| else if (strcmp(aArgs[0], "mgmt") == 0) | |
| { | |
| error = ProcessLinkMetricsMgmt(aArgsLength - 1, aArgs + 1); | |
| } | |
| else if (strcmp(aArgs[0], "probe") == 0) | |
| { | |
| error = ProcessLinkMetricsProbe(aArgsLength - 1, aArgs + 1); | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessLinkMetricsQuery(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_INVALID_ARGS; | |
| otIp6Address address; | |
| otLinkMetrics linkMetrics; | |
| VerifyOrExit(aArgsLength == 3); | |
| SuccessOrExit(error = ParseAsIp6Address(aArgs[0], address)); | |
| if (strcmp(aArgs[1], "single") == 0) | |
| { | |
| SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[2])); | |
| error = otLinkMetricsQuery(mInstance, &address, /* aSeriesId */ 0, &linkMetrics, | |
| &Interpreter::HandleLinkMetricsReport, this); | |
| } | |
| else if (strcmp(aArgs[1], "forward") == 0) | |
| { | |
| uint8_t seriesId; | |
| SuccessOrExit(error = ParseAsUint8(aArgs[2], seriesId)); | |
| error = otLinkMetricsQuery(mInstance, &address, seriesId, nullptr, &Interpreter::HandleLinkMetricsReport, this); | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ParseLinkMetricsFlags(otLinkMetrics &aLinkMetrics, char *aFlags) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| memset(&aLinkMetrics, 0, sizeof(aLinkMetrics)); | |
| for (char *arg = aFlags; *arg != '\0'; arg++) | |
| { | |
| switch (*arg) | |
| { | |
| case 'p': | |
| aLinkMetrics.mPduCount = true; | |
| break; | |
| case 'q': | |
| aLinkMetrics.mLqi = true; | |
| break; | |
| case 'm': | |
| aLinkMetrics.mLinkMargin = true; | |
| break; | |
| case 'r': | |
| aLinkMetrics.mRssi = true; | |
| break; | |
| default: | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessLinkMetricsMgmt(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_INVALID_ARGS; | |
| otIp6Address address; | |
| otLinkMetricsSeriesFlags seriesFlags; | |
| bool clear = false; | |
| VerifyOrExit(aArgsLength >= 2); | |
| SuccessOrExit(error = ParseAsIp6Address(aArgs[0], address)); | |
| memset(&seriesFlags, 0, sizeof(otLinkMetricsSeriesFlags)); | |
| if (strcmp(aArgs[1], "forward") == 0) | |
| { | |
| uint8_t seriesId; | |
| otLinkMetrics linkMetrics; | |
| VerifyOrExit(aArgsLength >= 4); | |
| memset(&linkMetrics, 0, sizeof(otLinkMetrics)); | |
| SuccessOrExit(error = ParseAsUint8(aArgs[2], seriesId)); | |
| for (char *arg = aArgs[3]; *arg != '\0'; arg++) | |
| { | |
| switch (*arg) | |
| { | |
| case 'l': | |
| seriesFlags.mLinkProbe = true; | |
| break; | |
| case 'd': | |
| seriesFlags.mMacData = true; | |
| break; | |
| case 'r': | |
| seriesFlags.mMacDataRequest = true; | |
| break; | |
| case 'a': | |
| seriesFlags.mMacAck = true; | |
| break; | |
| case 'X': | |
| VerifyOrExit(arg == aArgs[3] && *(arg + 1) == '\0' && aArgsLength == 4, | |
| error = OT_ERROR_INVALID_ARGS); // Ensure the flags only contain 'X' | |
| clear = true; | |
| break; | |
| default: | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| } | |
| if (!clear) | |
| { | |
| VerifyOrExit(aArgsLength == 5); | |
| SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[4])); | |
| } | |
| error = otLinkMetricsConfigForwardTrackingSeries(mInstance, &address, seriesId, seriesFlags, | |
| clear ? nullptr : &linkMetrics, | |
| &Interpreter::HandleLinkMetricsMgmtResponse, this); | |
| } | |
| else if (strcmp(aArgs[1], "enhanced-ack") == 0) | |
| { | |
| otLinkMetricsEnhAckFlags enhAckFlags; | |
| otLinkMetrics linkMetrics; | |
| otLinkMetrics * pLinkMetrics = &linkMetrics; | |
| VerifyOrExit(aArgsLength >= 3); | |
| if (strcmp(aArgs[2], "clear") == 0) | |
| { | |
| enhAckFlags = OT_LINK_METRICS_ENH_ACK_CLEAR; | |
| pLinkMetrics = nullptr; | |
| } | |
| else if (strcmp(aArgs[2], "register") == 0) | |
| { | |
| enhAckFlags = OT_LINK_METRICS_ENH_ACK_REGISTER; | |
| VerifyOrExit(aArgsLength >= 4); | |
| SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[3])); | |
| #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE | |
| if (aArgsLength > 4 && strcmp(aArgs[4], "r") == 0) | |
| { | |
| linkMetrics.mReserved = true; | |
| } | |
| #endif | |
| } | |
| else | |
| { | |
| ExitNow(); | |
| } | |
| error = otLinkMetricsConfigEnhAckProbing(mInstance, &address, enhAckFlags, pLinkMetrics, | |
| &Interpreter::HandleLinkMetricsMgmtResponse, this, | |
| &Interpreter::HandleLinkMetricsEnhAckProbingIe, this); | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessLinkMetricsProbe(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_INVALID_ARGS; | |
| otIp6Address address; | |
| uint8_t seriesId = 0; | |
| uint8_t length = 0; | |
| VerifyOrExit(aArgsLength == 3); | |
| SuccessOrExit(error = ParseAsIp6Address(aArgs[0], address)); | |
| SuccessOrExit(error = ParseAsUint8(aArgs[1], seriesId)); | |
| SuccessOrExit(error = ParseAsUint8(aArgs[2], length)); | |
| error = otLinkMetricsSendLinkProbe(mInstance, &address, seriesId, length); | |
| exit: | |
| return error; | |
| } | |
| #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE | |
| #if OPENTHREAD_FTD | |
| otError Interpreter::ProcessPskc(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| const otPskc *pskc = otThreadGetPskc(mInstance); | |
| OutputBytes(pskc->m8); | |
| OutputLine(""); | |
| } | |
| else | |
| { | |
| otPskc pskc; | |
| if (aArgsLength == 1) | |
| { | |
| SuccessOrExit(error = ParseAsHexString(aArgs[0], pskc.m8)); | |
| } | |
| else if (!strcmp(aArgs[0], "-p")) | |
| { | |
| SuccessOrExit(error = otDatasetGeneratePskc( | |
| aArgs[1], reinterpret_cast<const otNetworkName *>(otThreadGetNetworkName(mInstance)), | |
| otThreadGetExtendedPanId(mInstance), &pskc)); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| SuccessOrExit(error = otThreadSetPskc(mInstance, &pskc)); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif // OPENTHREAD_FTD | |
| otError Interpreter::ProcessMasterKey(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| OutputBytes(otThreadGetMasterKey(mInstance)->m8); | |
| OutputLine(""); | |
| } | |
| else | |
| { | |
| otMasterKey key; | |
| SuccessOrExit(error = ParseAsHexString(aArgs[0], key.m8)); | |
| SuccessOrExit(error = otThreadSetMasterKey(mInstance, &key)); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE | |
| otError Interpreter::ProcessMlr(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_COMMAND); | |
| if (!strcmp(aArgs[0], "reg")) | |
| { | |
| error = ProcessMlrReg(aArgsLength - 1, aArgs + 1); | |
| } | |
| else | |
| { | |
| error = OT_ERROR_INVALID_COMMAND; | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessMlrReg(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| otIp6Address addresses[kIPv6AddressesNumMax]; | |
| uint32_t timeout; | |
| uint8_t i; | |
| VerifyOrExit(aArgsLength >= 1, error = OT_ERROR_INVALID_ARGS); | |
| VerifyOrExit(aArgsLength <= kIPv6AddressesNumMax + 1, error = OT_ERROR_INVALID_ARGS); | |
| for (i = 0; i < aArgsLength && i < kIPv6AddressesNumMax; i++) | |
| { | |
| if (ParseAsIp6Address(aArgs[i], addresses[i]) != OT_ERROR_NONE) | |
| { | |
| break; | |
| } | |
| } | |
| VerifyOrExit(i > 0 && (i == aArgsLength || i == aArgsLength - 1), error = OT_ERROR_INVALID_ARGS); | |
| if (i == aArgsLength - 1) | |
| { | |
| // Parse the last argument as a timeout in seconds | |
| SuccessOrExit(error = ParseAsUint32(aArgs[i], timeout)); | |
| } | |
| SuccessOrExit(error = otIp6RegisterMulticastListeners(mInstance, addresses, i, | |
| i == aArgsLength - 1 ? &timeout : nullptr, | |
| Interpreter::HandleMlrRegResult, this)); | |
| error = OT_ERROR_PENDING; | |
| exit: | |
| return error; | |
| } | |
| void Interpreter::HandleMlrRegResult(void * aContext, | |
| otError aError, | |
| uint8_t aMlrStatus, | |
| const otIp6Address *aFailedAddresses, | |
| uint8_t aFailedAddressNum) | |
| { | |
| static_cast<Interpreter *>(aContext)->HandleMlrRegResult(aError, aMlrStatus, aFailedAddresses, aFailedAddressNum); | |
| } | |
| void Interpreter::HandleMlrRegResult(otError aError, | |
| uint8_t aMlrStatus, | |
| const otIp6Address *aFailedAddresses, | |
| uint8_t aFailedAddressNum) | |
| { | |
| if (aError == OT_ERROR_NONE) | |
| { | |
| OutputLine("status %d, %d failed", aMlrStatus, aFailedAddressNum); | |
| for (uint8_t i = 0; i < aFailedAddressNum; i++) | |
| { | |
| OutputIp6Address(aFailedAddresses[i]); | |
| OutputLine(""); | |
| } | |
| } | |
| OutputResult(aError); | |
| } | |
| #endif // (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE | |
| otError Interpreter::ProcessMode(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| otLinkModeConfig linkMode; | |
| memset(&linkMode, 0, sizeof(otLinkModeConfig)); | |
| if (aArgsLength == 0) | |
| { | |
| linkMode = otThreadGetLinkMode(mInstance); | |
| if (!(linkMode.mRxOnWhenIdle || linkMode.mDeviceType || linkMode.mNetworkData)) | |
| { | |
| OutputFormat("-"); | |
| } | |
| else | |
| { | |
| if (linkMode.mRxOnWhenIdle) | |
| { | |
| OutputFormat("r"); | |
| } | |
| if (linkMode.mDeviceType) | |
| { | |
| OutputFormat("d"); | |
| } | |
| if (linkMode.mNetworkData) | |
| { | |
| OutputFormat("n"); | |
| } | |
| } | |
| OutputLine(""); | |
| ExitNow(); | |
| } | |
| if (strcmp(aArgs[0], "-") != 0) | |
| { | |
| for (char *arg = aArgs[0]; *arg != '\0'; arg++) | |
| { | |
| switch (*arg) | |
| { | |
| case 'r': | |
| linkMode.mRxOnWhenIdle = true; | |
| break; | |
| case 'd': | |
| linkMode.mDeviceType = true; | |
| break; | |
| case 'n': | |
| linkMode.mNetworkData = true; | |
| break; | |
| default: | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| } | |
| } | |
| error = otThreadSetLinkMode(mInstance, linkMode); | |
| exit: | |
| return error; | |
| } | |
| #if OPENTHREAD_FTD | |
| otError Interpreter::ProcessNeighbor(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| otNeighborInfo neighborInfo; | |
| bool isTable; | |
| otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT; | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); | |
| isTable = (strcmp(aArgs[0], "table") == 0); | |
| if (isTable || strcmp(aArgs[0], "list") == 0) | |
| { | |
| if (isTable) | |
| { | |
| OutputLine("| Role | RLOC16 | Age | Avg RSSI | Last RSSI |R|D|N| Extended MAC |"); | |
| OutputLine("+------+--------+-----+----------+-----------+-+-+-+------------------+"); | |
| } | |
| while (otThreadGetNextNeighborInfo(mInstance, &iterator, &neighborInfo) == OT_ERROR_NONE) | |
| { | |
| if (isTable) | |
| { | |
| OutputFormat("| %3c ", neighborInfo.mIsChild ? 'C' : 'R'); | |
| OutputFormat("| 0x%04x ", neighborInfo.mRloc16); | |
| OutputFormat("| %3d ", neighborInfo.mAge); | |
| OutputFormat("| %8d ", neighborInfo.mAverageRssi); | |
| OutputFormat("| %9d ", neighborInfo.mLastRssi); | |
| OutputFormat("|%1d", neighborInfo.mRxOnWhenIdle); | |
| OutputFormat("|%1d", neighborInfo.mFullThreadDevice); | |
| OutputFormat("|%1d", neighborInfo.mFullNetworkData); | |
| OutputFormat("| "); | |
| OutputExtAddress(neighborInfo.mExtAddress); | |
| OutputLine(" |"); | |
| } | |
| else | |
| { | |
| OutputFormat("0x%04x ", neighborInfo.mRloc16); | |
| } | |
| } | |
| OutputLine(""); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif // OPENTHREAD_FTD | |
| #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE | |
| otError Interpreter::ProcessNetif(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| OT_UNUSED_VARIABLE(aArgsLength); | |
| OT_UNUSED_VARIABLE(aArgs); | |
| otError error = OT_ERROR_NONE; | |
| const char * netif = nullptr; | |
| unsigned int netifidx = 0; | |
| SuccessOrExit(error = otPlatGetNetif(mInstance, &netif, &netifidx)); | |
| OutputLine("%s:%u", netif ? netif : "(null)", netifidx); | |
| exit: | |
| return error; | |
| } | |
| #endif | |
| otError Interpreter::ProcessNetstat(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otUdpSocket *socket = otUdpGetSockets(mInstance); | |
| OT_UNUSED_VARIABLE(aArgsLength); | |
| OT_UNUSED_VARIABLE(aArgs); | |
| OutputLine("| Local Address | Peer Address |"); | |
| OutputLine("+-----------------------------------------------+-----------------------------------------------+"); | |
| while (socket) | |
| { | |
| constexpr int kMaxOutputLength = 45; | |
| int outputLength; | |
| OutputFormat("| "); | |
| outputLength = OutputSocketAddress(socket->mSockName); | |
| for (int i = outputLength; 0 <= i && i < kMaxOutputLength; ++i) | |
| { | |
| OutputFormat(" "); | |
| } | |
| OutputFormat(" | "); | |
| outputLength = OutputSocketAddress(socket->mPeerName); | |
| for (int i = outputLength; 0 <= i && i < kMaxOutputLength; ++i) | |
| { | |
| OutputFormat(" "); | |
| } | |
| OutputLine(" |"); | |
| socket = socket->mNext; | |
| } | |
| return OT_ERROR_NONE; | |
| } | |
| int Interpreter::OutputSocketAddress(const otSockAddr &aAddress) | |
| { | |
| int outputLength; | |
| int result = 0; | |
| VerifyOrExit((outputLength = OutputIp6Address(aAddress.mAddress)) >= 0, result = -1); | |
| result += outputLength; | |
| VerifyOrExit((outputLength = OutputFormat(":")) >= 0, result = -1); | |
| result += outputLength; | |
| if (aAddress.mPort == 0) | |
| { | |
| VerifyOrExit((outputLength = OutputFormat("*")) >= 0, result = -1); | |
| result += outputLength; | |
| } | |
| else | |
| { | |
| VerifyOrExit((outputLength = OutputFormat("%d", aAddress.mPort)) >= 0, result = -1); | |
| result += outputLength; | |
| } | |
| exit: | |
| return result; | |
| } | |
| #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE | |
| otError Interpreter::ProcessServiceList(void) | |
| { | |
| otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT; | |
| otServiceConfig config; | |
| while (otServerGetNextService(mInstance, &iterator, &config) == OT_ERROR_NONE) | |
| { | |
| mNetworkData.OutputService(config); | |
| } | |
| return OT_ERROR_NONE; | |
| } | |
| otError Interpreter::ProcessService(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_INVALID_COMMAND; | |
| otServiceConfig cfg; | |
| if (aArgsLength == 0) | |
| { | |
| error = ProcessServiceList(); | |
| } | |
| else | |
| { | |
| uint16_t length; | |
| VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsUint32(aArgs[1], cfg.mEnterpriseNumber)); | |
| length = sizeof(cfg.mServiceData); | |
| SuccessOrExit(error = ParseAsHexString(aArgs[2], length, cfg.mServiceData)); | |
| VerifyOrExit(length > 0, error = OT_ERROR_INVALID_ARGS); | |
| cfg.mServiceDataLength = static_cast<uint8_t>(length); | |
| if (strcmp(aArgs[0], "add") == 0) | |
| { | |
| VerifyOrExit(aArgsLength > 3, error = OT_ERROR_INVALID_ARGS); | |
| length = sizeof(cfg.mServerConfig.mServerData); | |
| SuccessOrExit(error = ParseAsHexString(aArgs[3], length, cfg.mServerConfig.mServerData)); | |
| VerifyOrExit(length > 0, error = OT_ERROR_INVALID_ARGS); | |
| cfg.mServerConfig.mServerDataLength = static_cast<uint8_t>(length); | |
| cfg.mServerConfig.mStable = true; | |
| error = otServerAddService(mInstance, &cfg); | |
| } | |
| else if (strcmp(aArgs[0], "remove") == 0) | |
| { | |
| error = otServerRemoveService(mInstance, cfg.mEnterpriseNumber, cfg.mServiceData, cfg.mServiceDataLength); | |
| } | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE | |
| otError Interpreter::ProcessNetworkData(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| return mNetworkData.Process(aArgsLength, aArgs); | |
| } | |
| #if OPENTHREAD_FTD | |
| otError Interpreter::ProcessNetworkIdTimeout(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("%d", otThreadGetNetworkIdTimeout(mInstance)); | |
| } | |
| else | |
| { | |
| uint8_t timeout; | |
| SuccessOrExit(error = ParseAsUint8(aArgs[0], timeout)); | |
| otThreadSetNetworkIdTimeout(mInstance, timeout); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif | |
| otError Interpreter::ProcessNetworkName(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("%s", otThreadGetNetworkName(mInstance)); | |
| } | |
| else | |
| { | |
| SuccessOrExit(error = otThreadSetNetworkName(mInstance, aArgs[0])); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE | |
| otError Interpreter::ProcessNetworkTime(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| uint64_t time; | |
| otNetworkTimeStatus networkTimeStatus; | |
| networkTimeStatus = otNetworkTimeGet(mInstance, &time); | |
| OutputFormat("Network Time: %luus", time); | |
| switch (networkTimeStatus) | |
| { | |
| case OT_NETWORK_TIME_UNSYNCHRONIZED: | |
| OutputLine(" (unsynchronized)"); | |
| break; | |
| case OT_NETWORK_TIME_RESYNC_NEEDED: | |
| OutputLine(" (resync needed)"); | |
| break; | |
| case OT_NETWORK_TIME_SYNCHRONIZED: | |
| OutputLine(" (synchronized)"); | |
| break; | |
| default: | |
| break; | |
| } | |
| OutputLine("Time Sync Period: %ds", otNetworkTimeGetSyncPeriod(mInstance)); | |
| OutputLine("XTAL Threshold: %dppm", otNetworkTimeGetXtalThreshold(mInstance)); | |
| } | |
| else if (aArgsLength == 2) | |
| { | |
| uint16_t period; | |
| uint16_t xtalThreshold; | |
| SuccessOrExit(error = ParseAsUint16(aArgs[0], period)); | |
| ; | |
| SuccessOrExit(error = ParseAsUint16(aArgs[1], xtalThreshold)); | |
| SuccessOrExit(error = otNetworkTimeSetSyncPeriod(mInstance, period)); | |
| SuccessOrExit(error = otNetworkTimeSetXtalThreshold(mInstance, xtalThreshold)); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE | |
| otError Interpreter::ProcessPanId(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("0x%04x", otLinkGetPanId(mInstance)); | |
| } | |
| else | |
| { | |
| uint16_t panId; | |
| SuccessOrExit(error = ParseAsUint16(aArgs[0], panId)); | |
| error = otLinkSetPanId(mInstance, panId); | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessParent(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| OT_UNUSED_VARIABLE(aArgsLength); | |
| OT_UNUSED_VARIABLE(aArgs); | |
| otError error = OT_ERROR_NONE; | |
| otRouterInfo parentInfo; | |
| SuccessOrExit(error = otThreadGetParentInfo(mInstance, &parentInfo)); | |
| OutputFormat("Ext Addr: "); | |
| OutputExtAddress(parentInfo.mExtAddress); | |
| OutputLine(""); | |
| OutputLine("Rloc: %x", parentInfo.mRloc16); | |
| OutputLine("Link Quality In: %d", parentInfo.mLinkQualityIn); | |
| OutputLine("Link Quality Out: %d", parentInfo.mLinkQualityOut); | |
| OutputLine("Age: %d", parentInfo.mAge); | |
| exit: | |
| return error; | |
| } | |
| #if OPENTHREAD_FTD | |
| otError Interpreter::ProcessParentPriority(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("%d", otThreadGetParentPriority(mInstance)); | |
| } | |
| else | |
| { | |
| int8_t priority; | |
| SuccessOrExit(error = ParseAsInt8(aArgs[0], priority)); | |
| error = otThreadSetParentPriority(mInstance, priority); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif | |
| void Interpreter::HandleIcmpReceive(void * aContext, | |
| otMessage * aMessage, | |
| const otMessageInfo *aMessageInfo, | |
| const otIcmp6Header *aIcmpHeader) | |
| { | |
| static_cast<Interpreter *>(aContext)->HandleIcmpReceive(aMessage, aMessageInfo, aIcmpHeader); | |
| } | |
| void Interpreter::HandleIcmpReceive(otMessage * aMessage, | |
| const otMessageInfo *aMessageInfo, | |
| const otIcmp6Header *aIcmpHeader) | |
| { | |
| uint32_t timestamp = 0; | |
| uint16_t dataSize; | |
| VerifyOrExit(aIcmpHeader->mType == OT_ICMP6_TYPE_ECHO_REPLY); | |
| VerifyOrExit((mPingIdentifier != 0) && (mPingIdentifier == HostSwap16(aIcmpHeader->mData.m16[0]))); | |
| dataSize = otMessageGetLength(aMessage) - otMessageGetOffset(aMessage); | |
| OutputFormat("%u bytes from ", dataSize + static_cast<uint16_t>(sizeof(otIcmp6Header))); | |
| OutputIp6Address(aMessageInfo->mPeerAddr); | |
| OutputFormat(": icmp_seq=%d hlim=%d", HostSwap16(aIcmpHeader->mData.m16[1]), aMessageInfo->mHopLimit); | |
| if (otMessageRead(aMessage, otMessageGetOffset(aMessage), ×tamp, sizeof(uint32_t)) == sizeof(uint32_t)) | |
| { | |
| OutputFormat(" time=%dms", TimerMilli::GetNow().GetValue() - HostSwap32(timestamp)); | |
| } | |
| OutputLine(""); | |
| SignalPingReply(static_cast<const Ip6::MessageInfo *>(aMessageInfo)->GetPeerAddr(), dataSize, HostSwap32(timestamp), | |
| aMessageInfo->mHopLimit); | |
| exit: | |
| return; | |
| } | |
| otError Interpreter::ProcessPing(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| uint8_t index = 1; | |
| uint32_t interval; | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); | |
| if (strcmp(aArgs[0], "stop") == 0) | |
| { | |
| mPingIdentifier = 0; | |
| VerifyOrExit(mPingTimer.IsRunning(), error = OT_ERROR_INVALID_STATE); | |
| mPingTimer.Stop(); | |
| ExitNow(); | |
| } | |
| VerifyOrExit(!mPingTimer.IsRunning(), error = OT_ERROR_BUSY); | |
| SuccessOrExit(error = ParseAsIp6Address(aArgs[0], mPingDestAddress)); | |
| mPingLength = kDefaultPingLength; | |
| mPingCount = kDefaultPingCount; | |
| mPingInterval = kDefaultPingInterval; | |
| mPingHopLimit = 0; | |
| mPingAllowZeroHopLimit = false; | |
| while (index < aArgsLength) | |
| { | |
| switch (index) | |
| { | |
| case 1: | |
| SuccessOrExit(error = ParseAsUint16(aArgs[index], mPingLength)); | |
| break; | |
| case 2: | |
| SuccessOrExit(error = ParseAsUint16(aArgs[index], mPingCount)); | |
| break; | |
| case 3: | |
| SuccessOrExit(error = ParsePingInterval(aArgs[index], interval)); | |
| VerifyOrExit(0 < interval && interval <= Timer::kMaxDelay, error = OT_ERROR_INVALID_ARGS); | |
| mPingInterval = interval; | |
| break; | |
| case 4: | |
| SuccessOrExit(error = ParseAsUint8(aArgs[index], mPingHopLimit)); | |
| mPingAllowZeroHopLimit = (mPingHopLimit == 0); | |
| break; | |
| default: | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| index++; | |
| } | |
| mPingIdentifier++; | |
| if (mPingIdentifier == 0) | |
| { | |
| mPingIdentifier++; | |
| } | |
| SendPing(); | |
| exit: | |
| return error; | |
| } | |
| void Interpreter::HandlePingTimer(Timer &aTimer) | |
| { | |
| GetOwner(aTimer).SendPing(); | |
| } | |
| void Interpreter::SendPing(void) | |
| { | |
| uint32_t timestamp = HostSwap32(TimerMilli::GetNow().GetValue()); | |
| otMessage * message = nullptr; | |
| otMessageInfo messageInfo; | |
| memset(&messageInfo, 0, sizeof(messageInfo)); | |
| messageInfo.mPeerAddr = mPingDestAddress; | |
| messageInfo.mHopLimit = mPingHopLimit; | |
| messageInfo.mAllowZeroHopLimit = mPingAllowZeroHopLimit; | |
| message = otIp6NewMessage(mInstance, nullptr); | |
| VerifyOrExit(message != nullptr); | |
| SuccessOrExit(otMessageAppend(message, ×tamp, sizeof(timestamp))); | |
| SuccessOrExit(otMessageSetLength(message, mPingLength)); | |
| SuccessOrExit(otIcmp6SendEchoRequest(mInstance, message, &messageInfo, mPingIdentifier)); | |
| SignalPingRequest(static_cast<Ip6::MessageInfo *>(&messageInfo)->GetPeerAddr(), mPingLength, HostSwap32(timestamp), | |
| messageInfo.mHopLimit); | |
| message = nullptr; | |
| exit: | |
| if (message != nullptr) | |
| { | |
| otMessageFree(message); | |
| } | |
| if (--mPingCount) | |
| { | |
| mPingTimer.Start(mPingInterval); | |
| } | |
| } | |
| otError Interpreter::ProcessPollPeriod(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("%d", otLinkGetPollPeriod(mInstance)); | |
| } | |
| else | |
| { | |
| uint32_t pollPeriod; | |
| SuccessOrExit(error = ParseAsUint32(aArgs[0], pollPeriod)); | |
| error = otLinkSetPollPeriod(mInstance, pollPeriod); | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessPromiscuous(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| if (otLinkIsPromiscuous(mInstance) && otPlatRadioGetPromiscuous(mInstance)) | |
| { | |
| OutputLine("Enabled"); | |
| } | |
| else | |
| { | |
| OutputLine("Disabled"); | |
| } | |
| } | |
| else | |
| { | |
| if (strcmp(aArgs[0], "enable") == 0) | |
| { | |
| SuccessOrExit(error = otLinkSetPromiscuous(mInstance, true)); | |
| otLinkSetPcapCallback(mInstance, &HandleLinkPcapReceive, this); | |
| } | |
| else if (strcmp(aArgs[0], "disable") == 0) | |
| { | |
| otLinkSetPcapCallback(mInstance, nullptr, nullptr); | |
| SuccessOrExit(error = otLinkSetPromiscuous(mInstance, false)); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| } | |
| exit: | |
| return error; | |
| } | |
| void Interpreter::HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx, void *aContext) | |
| { | |
| static_cast<Interpreter *>(aContext)->HandleLinkPcapReceive(aFrame, aIsTx); | |
| } | |
| void Interpreter::HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx) | |
| { | |
| OT_UNUSED_VARIABLE(aIsTx); | |
| OutputLine(""); | |
| for (size_t i = 0; i < 44; i++) | |
| { | |
| OutputFormat("="); | |
| } | |
| OutputFormat("[len = %3u]", aFrame->mLength); | |
| for (size_t i = 0; i < 28; i++) | |
| { | |
| OutputFormat("="); | |
| } | |
| OutputLine(""); | |
| for (size_t i = 0; i < aFrame->mLength; i += 16) | |
| { | |
| OutputFormat("|"); | |
| for (size_t j = 0; j < 16; j++) | |
| { | |
| if (i + j < aFrame->mLength) | |
| { | |
| OutputFormat(" %02X", aFrame->mPsdu[i + j]); | |
| } | |
| else | |
| { | |
| OutputFormat(" .."); | |
| } | |
| } | |
| OutputFormat("|"); | |
| for (size_t j = 0; j < 16; j++) | |
| { | |
| if (i + j < aFrame->mLength) | |
| { | |
| if (31 < aFrame->mPsdu[i + j] && aFrame->mPsdu[i + j] < 127) | |
| { | |
| OutputFormat(" %c", aFrame->mPsdu[i + j]); | |
| } | |
| else | |
| { | |
| OutputFormat(" ?"); | |
| } | |
| } | |
| else | |
| { | |
| OutputFormat(" ."); | |
| } | |
| } | |
| OutputLine("|"); | |
| } | |
| for (size_t i = 0; i < 83; i++) | |
| { | |
| OutputFormat("-"); | |
| } | |
| OutputLine(""); | |
| } | |
| #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE | |
| otError Interpreter::ProcessPrefixAdd(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| otBorderRouterConfig config; | |
| uint8_t argcur = 0; | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); | |
| memset(&config, 0, sizeof(otBorderRouterConfig)); | |
| SuccessOrExit(error = ParseAsIp6Prefix(aArgs[argcur], config.mPrefix)); | |
| argcur++; | |
| for (; argcur < aArgsLength; argcur++) | |
| { | |
| if (strcmp(aArgs[argcur], "high") == 0) | |
| { | |
| config.mPreference = OT_ROUTE_PREFERENCE_HIGH; | |
| } | |
| else if (strcmp(aArgs[argcur], "med") == 0) | |
| { | |
| config.mPreference = OT_ROUTE_PREFERENCE_MED; | |
| } | |
| else if (strcmp(aArgs[argcur], "low") == 0) | |
| { | |
| config.mPreference = OT_ROUTE_PREFERENCE_LOW; | |
| } | |
| else | |
| { | |
| for (char *arg = aArgs[argcur]; *arg != '\0'; arg++) | |
| { | |
| switch (*arg) | |
| { | |
| case 'p': | |
| config.mPreferred = true; | |
| break; | |
| case 'a': | |
| config.mSlaac = true; | |
| break; | |
| case 'd': | |
| config.mDhcp = true; | |
| break; | |
| case 'c': | |
| config.mConfigure = true; | |
| break; | |
| case 'r': | |
| config.mDefaultRoute = true; | |
| break; | |
| case 'o': | |
| config.mOnMesh = true; | |
| break; | |
| case 's': | |
| config.mStable = true; | |
| break; | |
| case 'n': | |
| config.mNdDns = true; | |
| break; | |
| #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE | |
| case 'D': | |
| config.mDp = true; | |
| break; | |
| #endif | |
| default: | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| } | |
| } | |
| } | |
| error = otBorderRouterAddOnMeshPrefix(mInstance, &config); | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessPrefixRemove(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| otIp6Prefix prefix; | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsIp6Prefix(aArgs[0], prefix)); | |
| error = otBorderRouterRemoveOnMeshPrefix(mInstance, &prefix); | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessPrefixList(void) | |
| { | |
| otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT; | |
| otBorderRouterConfig config; | |
| while (otBorderRouterGetNextOnMeshPrefix(mInstance, &iterator, &config) == OT_ERROR_NONE) | |
| { | |
| mNetworkData.OutputPrefix(config); | |
| } | |
| #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE | |
| if (otBackboneRouterGetState(mInstance) == OT_BACKBONE_ROUTER_STATE_DISABLED) | |
| { | |
| SuccessOrExit(otBackboneRouterGetDomainPrefix(mInstance, &config)); | |
| OutputFormat("- "); | |
| mNetworkData.OutputPrefix(config); | |
| } | |
| // Else already printed via above while loop. | |
| exit: | |
| #endif | |
| return OT_ERROR_NONE; | |
| } | |
| otError Interpreter::ProcessPrefix(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| SuccessOrExit(error = ProcessPrefixList()); | |
| } | |
| else if (strcmp(aArgs[0], "add") == 0) | |
| { | |
| SuccessOrExit(error = ProcessPrefixAdd(aArgsLength - 1, aArgs + 1)); | |
| } | |
| else if (strcmp(aArgs[0], "remove") == 0) | |
| { | |
| SuccessOrExit(error = ProcessPrefixRemove(aArgsLength - 1, aArgs + 1)); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_COMMAND); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE | |
| #if OPENTHREAD_FTD | |
| otError Interpreter::ProcessPreferRouterId(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| uint8_t routerId; | |
| VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsUint8(aArgs[0], routerId)); | |
| error = otThreadSetPreferredRouterId(mInstance, routerId); | |
| exit: | |
| return error; | |
| } | |
| #endif | |
| otError Interpreter::ProcessRcp(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| const char *version = otPlatRadioGetVersionString(mInstance); | |
| VerifyOrExit(version != otGetVersionString(), error = OT_ERROR_NOT_IMPLEMENTED); | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); | |
| if (strcmp(aArgs[0], "version") == 0) | |
| { | |
| OutputLine("%s", version); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessRegion(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| uint16_t regionCode; | |
| if (aArgsLength == 0) | |
| { | |
| SuccessOrExit(error = otPlatRadioGetRegion(mInstance, ®ionCode)); | |
| OutputLine("%c%c", regionCode >> 8, regionCode & 0xff); | |
| } | |
| else | |
| { | |
| VerifyOrExit(strlen(aArgs[0]) == 2, error = OT_ERROR_INVALID_ARGS); | |
| regionCode = | |
| static_cast<uint16_t>(static_cast<uint16_t>(aArgs[0][0]) << 8) + static_cast<uint16_t>(aArgs[0][1]); | |
| error = otPlatRadioSetRegion(mInstance, regionCode); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #if OPENTHREAD_FTD | |
| otError Interpreter::ProcessReleaseRouterId(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| uint8_t routerId; | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsUint8(aArgs[0], routerId)); | |
| SuccessOrExit(error = otThreadReleaseRouterId(mInstance, routerId)); | |
| exit: | |
| return error; | |
| } | |
| #endif | |
| otError Interpreter::ProcessReset(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| OT_UNUSED_VARIABLE(aArgsLength); | |
| OT_UNUSED_VARIABLE(aArgs); | |
| otInstanceReset(mInstance); | |
| return OT_ERROR_NONE; | |
| } | |
| otError Interpreter::ProcessRloc16(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| OT_UNUSED_VARIABLE(aArgsLength); | |
| OT_UNUSED_VARIABLE(aArgs); | |
| OutputLine("%04x", otThreadGetRloc16(mInstance)); | |
| return OT_ERROR_NONE; | |
| } | |
| #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE | |
| otError Interpreter::ProcessRouteAdd(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| otExternalRouteConfig config; | |
| uint8_t argcur = 0; | |
| memset(&config, 0, sizeof(otExternalRouteConfig)); | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsIp6Prefix(aArgs[argcur], config.mPrefix)); | |
| argcur++; | |
| for (; argcur < aArgsLength; argcur++) | |
| { | |
| if (strcmp(aArgs[argcur], "s") == 0) | |
| { | |
| config.mStable = true; | |
| } | |
| else if (strcmp(aArgs[argcur], "high") == 0) | |
| { | |
| config.mPreference = OT_ROUTE_PREFERENCE_HIGH; | |
| } | |
| else if (strcmp(aArgs[argcur], "med") == 0) | |
| { | |
| config.mPreference = OT_ROUTE_PREFERENCE_MED; | |
| } | |
| else if (strcmp(aArgs[argcur], "low") == 0) | |
| { | |
| config.mPreference = OT_ROUTE_PREFERENCE_LOW; | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| } | |
| error = otBorderRouterAddRoute(mInstance, &config); | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessRouteRemove(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| otIp6Prefix prefix; | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsIp6Prefix(aArgs[0], prefix)); | |
| error = otBorderRouterRemoveRoute(mInstance, &prefix); | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessRouteList(void) | |
| { | |
| otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT; | |
| otExternalRouteConfig config; | |
| while (otBorderRouterGetNextRoute(mInstance, &iterator, &config) == OT_ERROR_NONE) | |
| { | |
| mNetworkData.OutputRoute(config); | |
| } | |
| return OT_ERROR_NONE; | |
| } | |
| otError Interpreter::ProcessRoute(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| SuccessOrExit(error = ProcessRouteList()); | |
| } | |
| else if (strcmp(aArgs[0], "add") == 0) | |
| { | |
| SuccessOrExit(error = ProcessRouteAdd(aArgsLength - 1, aArgs + 1)); | |
| } | |
| else if (strcmp(aArgs[0], "remove") == 0) | |
| { | |
| SuccessOrExit(error = ProcessRouteRemove(aArgsLength - 1, aArgs + 1)); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_COMMAND); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE | |
| #if OPENTHREAD_FTD | |
| otError Interpreter::ProcessRouter(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| otRouterInfo routerInfo; | |
| uint16_t routerId; | |
| bool isTable; | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); | |
| isTable = (strcmp(aArgs[0], "table") == 0); | |
| if (isTable || strcmp(aArgs[0], "list") == 0) | |
| { | |
| uint8_t maxRouterId; | |
| if (isTable) | |
| { | |
| OutputLine("| ID | RLOC16 | Next Hop | Path Cost | LQ In | LQ Out | Age | Extended MAC | Link |"); | |
| OutputLine("+----+--------+----------+-----------+-------+--------+-----+------------------+------+"); | |
| } | |
| maxRouterId = otThreadGetMaxRouterId(mInstance); | |
| for (uint8_t i = 0; i <= maxRouterId; i++) | |
| { | |
| if (otThreadGetRouterInfo(mInstance, i, &routerInfo) != OT_ERROR_NONE) | |
| { | |
| continue; | |
| } | |
| if (isTable) | |
| { | |
| OutputFormat("| %2d ", routerInfo.mRouterId); | |
| OutputFormat("| 0x%04x ", routerInfo.mRloc16); | |
| OutputFormat("| %8d ", routerInfo.mNextHop); | |
| OutputFormat("| %9d ", routerInfo.mPathCost); | |
| OutputFormat("| %5d ", routerInfo.mLinkQualityIn); | |
| OutputFormat("| %6d ", routerInfo.mLinkQualityOut); | |
| OutputFormat("| %3d ", routerInfo.mAge); | |
| OutputFormat("| "); | |
| OutputExtAddress(routerInfo.mExtAddress); | |
| OutputLine(" | %4d |", routerInfo.mLinkEstablished); | |
| } | |
| else | |
| { | |
| OutputFormat("%d ", i); | |
| } | |
| } | |
| OutputLine(""); | |
| ExitNow(); | |
| } | |
| SuccessOrExit(error = ParseAsUint16(aArgs[0], routerId)); | |
| SuccessOrExit(error = otThreadGetRouterInfo(mInstance, routerId, &routerInfo)); | |
| OutputLine("Alloc: %d", routerInfo.mAllocated); | |
| if (routerInfo.mAllocated) | |
| { | |
| OutputLine("Router ID: %d", routerInfo.mRouterId); | |
| OutputLine("Rloc: %04x", routerInfo.mRloc16); | |
| OutputLine("Next Hop: %04x", static_cast<uint16_t>(routerInfo.mNextHop) << 10); | |
| OutputLine("Link: %d", routerInfo.mLinkEstablished); | |
| if (routerInfo.mLinkEstablished) | |
| { | |
| OutputFormat("Ext Addr: "); | |
| OutputExtAddress(routerInfo.mExtAddress); | |
| OutputLine(""); | |
| OutputLine("Cost: %d", routerInfo.mPathCost); | |
| OutputLine("Link Quality In: %d", routerInfo.mLinkQualityIn); | |
| OutputLine("Link Quality Out: %d", routerInfo.mLinkQualityOut); | |
| OutputLine("Age: %d", routerInfo.mAge); | |
| } | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessRouterDowngradeThreshold(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("%d", otThreadGetRouterDowngradeThreshold(mInstance)); | |
| } | |
| else | |
| { | |
| uint8_t threshold; | |
| SuccessOrExit(error = ParseAsUint8(aArgs[0], threshold)); | |
| otThreadSetRouterDowngradeThreshold(mInstance, threshold); | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessRouterEligible(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| if (otThreadIsRouterEligible(mInstance)) | |
| { | |
| OutputLine("Enabled"); | |
| } | |
| else | |
| { | |
| OutputLine("Disabled"); | |
| } | |
| } | |
| else if (strcmp(aArgs[0], "enable") == 0) | |
| { | |
| error = otThreadSetRouterEligible(mInstance, true); | |
| } | |
| else if (strcmp(aArgs[0], "disable") == 0) | |
| { | |
| error = otThreadSetRouterEligible(mInstance, false); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessRouterSelectionJitter(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("%d", otThreadGetRouterSelectionJitter(mInstance)); | |
| } | |
| else | |
| { | |
| uint8_t jitter; | |
| SuccessOrExit(error = ParseAsUint8(aArgs[0], jitter)); | |
| otThreadSetRouterSelectionJitter(mInstance, jitter); | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessRouterUpgradeThreshold(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("%d", otThreadGetRouterUpgradeThreshold(mInstance)); | |
| } | |
| else | |
| { | |
| uint8_t threshold; | |
| SuccessOrExit(error = ParseAsUint8(aArgs[0], threshold)); | |
| otThreadSetRouterUpgradeThreshold(mInstance, threshold); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif // OPENTHREAD_FTD | |
| otError Interpreter::ProcessScan(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| uint32_t scanChannels = 0; | |
| uint16_t scanDuration = 0; | |
| bool energyScan = false; | |
| uint8_t index = 0; | |
| if (aArgsLength > 0) | |
| { | |
| if (strcmp(aArgs[index], "energy") == 0) | |
| { | |
| energyScan = true; | |
| index++; | |
| if (aArgsLength > 1) | |
| { | |
| SuccessOrExit(error = ParseAsUint16(aArgs[index++], scanDuration)); | |
| } | |
| } | |
| if (index < aArgsLength) | |
| { | |
| uint8_t channel; | |
| SuccessOrExit(error = ParseAsUint8(aArgs[index++], channel)); | |
| VerifyOrExit(channel < sizeof(scanChannels) * CHAR_BIT, error = OT_ERROR_INVALID_ARGS); | |
| scanChannels = 1 << channel; | |
| } | |
| } | |
| if (energyScan) | |
| { | |
| OutputLine("| Ch | RSSI |"); | |
| OutputLine("+----+------+"); | |
| SuccessOrExit(error = otLinkEnergyScan(mInstance, scanChannels, scanDuration, | |
| &Interpreter::HandleEnergyScanResult, this)); | |
| } | |
| else | |
| { | |
| OutputLine("| J | Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI |"); | |
| OutputLine("+---+------------------+------------------+------+------------------+----+-----+-----+"); | |
| SuccessOrExit(error = otLinkActiveScan(mInstance, scanChannels, scanDuration, | |
| &Interpreter::HandleActiveScanResult, this)); | |
| } | |
| error = OT_ERROR_PENDING; | |
| exit: | |
| return error; | |
| } | |
| void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult, void *aContext) | |
| { | |
| static_cast<Interpreter *>(aContext)->HandleActiveScanResult(aResult); | |
| } | |
| void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult) | |
| { | |
| if (aResult == nullptr) | |
| { | |
| OutputResult(OT_ERROR_NONE); | |
| ExitNow(); | |
| } | |
| OutputFormat("| %d ", aResult->mIsJoinable); | |
| OutputFormat("| %-16s ", aResult->mNetworkName.m8); | |
| OutputFormat("| "); | |
| OutputBytes(aResult->mExtendedPanId.m8); | |
| OutputFormat(" "); | |
| OutputFormat("| %04x | ", aResult->mPanId); | |
| OutputExtAddress(aResult->mExtAddress); | |
| OutputFormat(" | %2d ", aResult->mChannel); | |
| OutputFormat("| %3d ", aResult->mRssi); | |
| OutputLine("| %3d |", aResult->mLqi); | |
| exit: | |
| return; | |
| } | |
| void Interpreter::HandleEnergyScanResult(otEnergyScanResult *aResult, void *aContext) | |
| { | |
| static_cast<Interpreter *>(aContext)->HandleEnergyScanResult(aResult); | |
| } | |
| void Interpreter::HandleEnergyScanResult(otEnergyScanResult *aResult) | |
| { | |
| if (aResult == nullptr) | |
| { | |
| OutputResult(OT_ERROR_NONE); | |
| ExitNow(); | |
| } | |
| OutputLine("| %2d | %4d |", aResult->mChannel, aResult->mMaxRssi); | |
| exit: | |
| return; | |
| } | |
| otError Interpreter::ProcessSingleton(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| OT_UNUSED_VARIABLE(aArgsLength); | |
| OT_UNUSED_VARIABLE(aArgs); | |
| if (otThreadIsSingleton(mInstance)) | |
| { | |
| OutputLine("true"); | |
| } | |
| else | |
| { | |
| OutputLine("false"); | |
| } | |
| return OT_ERROR_NONE; | |
| } | |
| #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE | |
| otError Interpreter::ProcessSntp(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| uint16_t port = OT_SNTP_DEFAULT_SERVER_PORT; | |
| Ip6::MessageInfo messageInfo; | |
| otSntpQuery query; | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); | |
| if (strcmp(aArgs[0], "query") == 0) | |
| { | |
| VerifyOrExit(!mSntpQueryingInProgress, error = OT_ERROR_BUSY); | |
| if (aArgsLength > 1) | |
| { | |
| SuccessOrExit(error = ParseAsIp6Address(aArgs[1], messageInfo.GetPeerAddr())); | |
| } | |
| else | |
| { | |
| // Use IPv6 address of default SNTP server. | |
| SuccessOrExit(error = messageInfo.GetPeerAddr().FromString(OT_SNTP_DEFAULT_SERVER_IP)); | |
| } | |
| if (aArgsLength > 2) | |
| { | |
| SuccessOrExit(error = ParseAsUint16(aArgs[2], port)); | |
| } | |
| messageInfo.SetPeerPort(port); | |
| query.mMessageInfo = static_cast<const otMessageInfo *>(&messageInfo); | |
| SuccessOrExit(error = otSntpClientQuery(mInstance, &query, &Interpreter::HandleSntpResponse, this)); | |
| mSntpQueryingInProgress = true; | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_COMMAND); | |
| } | |
| error = OT_ERROR_PENDING; | |
| exit: | |
| return error; | |
| } | |
| void Interpreter::HandleSntpResponse(void *aContext, uint64_t aTime, otError aResult) | |
| { | |
| static_cast<Interpreter *>(aContext)->HandleSntpResponse(aTime, aResult); | |
| } | |
| void Interpreter::HandleSntpResponse(uint64_t aTime, otError aResult) | |
| { | |
| if (aResult == OT_ERROR_NONE) | |
| { | |
| // Some Embedded C libraries do not support printing of 64-bit unsigned integers. | |
| // To simplify, unix epoch time and era number are printed separately. | |
| OutputLine("SNTP response - Unix time: %u (era: %u)", static_cast<uint32_t>(aTime), | |
| static_cast<uint32_t>(aTime >> 32)); | |
| } | |
| else | |
| { | |
| OutputLine("SNTP error - %s", otThreadErrorToString(aResult)); | |
| } | |
| mSntpQueryingInProgress = false; | |
| OutputResult(OT_ERROR_NONE); | |
| } | |
| #endif // OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE | |
| #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE | |
| otError Interpreter::ProcessSrp(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE | |
| OutputLine("client"); | |
| #endif | |
| #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE | |
| OutputLine("server"); | |
| #endif | |
| ExitNow(); | |
| } | |
| #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE | |
| if (strcmp(aArgs[0], "client") == 0) | |
| { | |
| ExitNow(error = mSrpClient.Process(aArgsLength - 1, aArgs + 1)); | |
| } | |
| #endif | |
| #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE | |
| if (strcmp(aArgs[0], "server") == 0) | |
| { | |
| ExitNow(error = mSrpServer.Process(aArgsLength - 1, aArgs + 1)); | |
| } | |
| #endif | |
| error = OT_ERROR_INVALID_COMMAND; | |
| exit: | |
| return error; | |
| } | |
| #endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE | |
| otError Interpreter::ProcessState(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| switch (otThreadGetDeviceRole(mInstance)) | |
| { | |
| case OT_DEVICE_ROLE_DISABLED: | |
| OutputLine("disabled"); | |
| break; | |
| case OT_DEVICE_ROLE_DETACHED: | |
| OutputLine("detached"); | |
| break; | |
| case OT_DEVICE_ROLE_CHILD: | |
| OutputLine("child"); | |
| break; | |
| #if OPENTHREAD_FTD | |
| case OT_DEVICE_ROLE_ROUTER: | |
| OutputLine("router"); | |
| break; | |
| case OT_DEVICE_ROLE_LEADER: | |
| OutputLine("leader"); | |
| break; | |
| #endif | |
| default: | |
| OutputLine("invalid state"); | |
| break; | |
| } | |
| } | |
| else | |
| { | |
| if (strcmp(aArgs[0], "detached") == 0) | |
| { | |
| SuccessOrExit(error = otThreadBecomeDetached(mInstance)); | |
| } | |
| else if (strcmp(aArgs[0], "child") == 0) | |
| { | |
| SuccessOrExit(error = otThreadBecomeChild(mInstance)); | |
| } | |
| #if OPENTHREAD_FTD | |
| else if (strcmp(aArgs[0], "router") == 0) | |
| { | |
| SuccessOrExit(error = otThreadBecomeRouter(mInstance)); | |
| } | |
| else if (strcmp(aArgs[0], "leader") == 0) | |
| { | |
| SuccessOrExit(error = otThreadBecomeLeader(mInstance)); | |
| } | |
| #endif | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_ARGS); | |
| } | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessThread(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); | |
| if (strcmp(aArgs[0], "start") == 0) | |
| { | |
| SuccessOrExit(error = otThreadSetEnabled(mInstance, true)); | |
| } | |
| else if (strcmp(aArgs[0], "stop") == 0) | |
| { | |
| SuccessOrExit(error = otThreadSetEnabled(mInstance, false)); | |
| } | |
| else if (strcmp(aArgs[0], "version") == 0) | |
| { | |
| OutputLine("%u", otThreadGetVersion()); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_COMMAND); | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessDataset(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| return mDataset.Process(aArgsLength, aArgs); | |
| } | |
| otError Interpreter::ProcessTxPower(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| int8_t power; | |
| if (aArgsLength == 0) | |
| { | |
| SuccessOrExit(error = otPlatRadioGetTransmitPower(mInstance, &power)); | |
| OutputLine("%d dBm", power); | |
| } | |
| else | |
| { | |
| SuccessOrExit(error = ParseAsInt8(aArgs[0], power)); | |
| SuccessOrExit(error = otPlatRadioSetTransmitPower(mInstance, power)); | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessUdp(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| return mUdp.Process(aArgsLength, aArgs); | |
| } | |
| otError Interpreter::ProcessUnsecurePort(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| VerifyOrExit(aArgsLength >= 1, error = OT_ERROR_INVALID_ARGS); | |
| if (strcmp(aArgs[0], "add") == 0) | |
| { | |
| uint16_t port; | |
| VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsUint16(aArgs[1], port)); | |
| SuccessOrExit(error = otIp6AddUnsecurePort(mInstance, port)); | |
| } | |
| else if (strcmp(aArgs[0], "remove") == 0) | |
| { | |
| VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS); | |
| if (strcmp(aArgs[1], "all") == 0) | |
| { | |
| otIp6RemoveAllUnsecurePorts(mInstance); | |
| } | |
| else | |
| { | |
| uint16_t port; | |
| SuccessOrExit(error = ParseAsUint16(aArgs[1], port)); | |
| SuccessOrExit(error = otIp6RemoveUnsecurePort(mInstance, port)); | |
| } | |
| } | |
| else if (strcmp(aArgs[0], "get") == 0) | |
| { | |
| const uint16_t *ports; | |
| uint8_t numPorts; | |
| ports = otIp6GetUnsecurePorts(mInstance, &numPorts); | |
| if (ports != nullptr) | |
| { | |
| for (uint8_t i = 0; i < numPorts; i++) | |
| { | |
| OutputFormat("%d ", ports[i]); | |
| } | |
| } | |
| OutputLine(""); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_COMMAND); | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessVersion(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("%s", otGetVersionString()); | |
| ExitNow(); | |
| } | |
| if (strcmp(aArgs[0], "api") == 0) | |
| { | |
| OutputLine("%d", OPENTHREAD_API_VERSION); | |
| } | |
| else | |
| { | |
| ExitNow(error = OT_ERROR_INVALID_COMMAND); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD | |
| otError Interpreter::ProcessCommissioner(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| return mCommissioner.Process(aArgsLength, aArgs); | |
| } | |
| #endif | |
| #if OPENTHREAD_CONFIG_JOINER_ENABLE | |
| otError Interpreter::ProcessJoiner(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| return mJoiner.Process(aArgsLength, aArgs); | |
| } | |
| #endif | |
| #if OPENTHREAD_FTD | |
| otError Interpreter::ProcessJoinerPort(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| OutputLine("%d", otThreadGetJoinerUdpPort(mInstance)); | |
| } | |
| else | |
| { | |
| uint16_t port; | |
| SuccessOrExit(error = ParseAsUint16(aArgs[0], port)); | |
| error = otThreadSetJoinerUdpPort(mInstance, port); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif | |
| #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE | |
| otError Interpreter::ProcessMacFilter(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aArgsLength == 0) | |
| { | |
| PrintMacFilter(); | |
| } | |
| else | |
| { | |
| if (strcmp(aArgs[0], "addr") == 0) | |
| { | |
| error = ProcessMacFilterAddress(aArgsLength - 1, aArgs + 1); | |
| } | |
| else if (strcmp(aArgs[0], "rss") == 0) | |
| { | |
| error = ProcessMacFilterRss(aArgsLength - 1, aArgs + 1); | |
| } | |
| else | |
| { | |
| error = OT_ERROR_INVALID_COMMAND; | |
| } | |
| } | |
| return error; | |
| } | |
| void Interpreter::PrintMacFilter(void) | |
| { | |
| otMacFilterEntry entry; | |
| otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT; | |
| otMacFilterAddressMode mode = otLinkFilterGetAddressMode(mInstance); | |
| if (mode == OT_MAC_FILTER_ADDRESS_MODE_DISABLED) | |
| { | |
| OutputLine("Address Mode: Disabled"); | |
| } | |
| else if (mode == OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST) | |
| { | |
| OutputLine("Address Mode: Allowlist"); | |
| } | |
| else if (mode == OT_MAC_FILTER_ADDRESS_MODE_DENYLIST) | |
| { | |
| OutputLine("Address Mode: Denylist"); | |
| } | |
| while (otLinkFilterGetNextAddress(mInstance, &iterator, &entry) == OT_ERROR_NONE) | |
| { | |
| OutputExtAddress(entry.mExtAddress); | |
| if (entry.mRssIn != OT_MAC_FILTER_FIXED_RSS_DISABLED) | |
| { | |
| OutputFormat(" : rss %d (lqi %d)", entry.mRssIn, otLinkConvertRssToLinkQuality(mInstance, entry.mRssIn)); | |
| } | |
| OutputLine(""); | |
| } | |
| iterator = OT_MAC_FILTER_ITERATOR_INIT; | |
| OutputLine("RssIn List:"); | |
| while (otLinkFilterGetNextRssIn(mInstance, &iterator, &entry) == OT_ERROR_NONE) | |
| { | |
| uint8_t i = 0; | |
| for (; i < OT_EXT_ADDRESS_SIZE; i++) | |
| { | |
| if (entry.mExtAddress.m8[i] != 0xff) | |
| { | |
| break; | |
| } | |
| } | |
| if (i == OT_EXT_ADDRESS_SIZE) | |
| { | |
| OutputLine("Default rss : %d (lqi %d)", entry.mRssIn, | |
| otLinkConvertRssToLinkQuality(mInstance, entry.mRssIn)); | |
| } | |
| else | |
| { | |
| OutputExtAddress(entry.mExtAddress); | |
| OutputLine(" : rss %d (lqi %d)", entry.mRssIn, otLinkConvertRssToLinkQuality(mInstance, entry.mRssIn)); | |
| } | |
| } | |
| } | |
| otError Interpreter::ProcessMacFilterAddress(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| otExtAddress extAddr; | |
| otMacFilterEntry entry; | |
| otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT; | |
| otMacFilterAddressMode mode = otLinkFilterGetAddressMode(mInstance); | |
| if (aArgsLength == 0) | |
| { | |
| if (mode == OT_MAC_FILTER_ADDRESS_MODE_DISABLED) | |
| { | |
| OutputLine("Disabled"); | |
| } | |
| else if (mode == OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST) | |
| { | |
| OutputLine("Allowlist"); | |
| } | |
| else if (mode == OT_MAC_FILTER_ADDRESS_MODE_DENYLIST) | |
| { | |
| OutputLine("Denylist"); | |
| } | |
| while (otLinkFilterGetNextAddress(mInstance, &iterator, &entry) == OT_ERROR_NONE) | |
| { | |
| OutputExtAddress(entry.mExtAddress); | |
| if (entry.mRssIn != OT_MAC_FILTER_FIXED_RSS_DISABLED) | |
| { | |
| OutputFormat(" : rss %d (lqi %d)", entry.mRssIn, | |
| otLinkConvertRssToLinkQuality(mInstance, entry.mRssIn)); | |
| } | |
| OutputLine(""); | |
| } | |
| } | |
| else | |
| { | |
| if (strcmp(aArgs[0], "disable") == 0) | |
| { | |
| VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS); | |
| otLinkFilterSetAddressMode(mInstance, OT_MAC_FILTER_ADDRESS_MODE_DISABLED); | |
| } | |
| else if (strcmp(aArgs[0], "allowlist") == 0) | |
| { | |
| VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS); | |
| otLinkFilterSetAddressMode(mInstance, OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST); | |
| } | |
| else if (strcmp(aArgs[0], "denylist") == 0) | |
| { | |
| VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS); | |
| otLinkFilterSetAddressMode(mInstance, OT_MAC_FILTER_ADDRESS_MODE_DENYLIST); | |
| } | |
| else if (strcmp(aArgs[0], "add") == 0) | |
| { | |
| VerifyOrExit(aArgsLength >= 2, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsHexString(aArgs[1], extAddr.m8)); | |
| error = otLinkFilterAddAddress(mInstance, &extAddr); | |
| VerifyOrExit(error == OT_ERROR_NONE || error == OT_ERROR_ALREADY); | |
| if (aArgsLength > 2) | |
| { | |
| int8_t rss; | |
| VerifyOrExit(aArgsLength == 3, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsInt8(aArgs[2], rss)); | |
| SuccessOrExit(error = otLinkFilterAddRssIn(mInstance, &extAddr, rss)); | |
| } | |
| } | |
| else if (strcmp(aArgs[0], "remove") == 0) | |
| { | |
| VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsHexString(aArgs[1], extAddr.m8)); | |
| otLinkFilterRemoveAddress(mInstance, &extAddr); | |
| } | |
| else if (strcmp(aArgs[0], "clear") == 0) | |
| { | |
| VerifyOrExit(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS); | |
| otLinkFilterClearAddresses(mInstance); | |
| } | |
| else | |
| { | |
| error = OT_ERROR_INVALID_COMMAND; | |
| } | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessMacFilterRss(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| otMacFilterEntry entry; | |
| otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT; | |
| otExtAddress extAddr; | |
| int8_t rss; | |
| if (aArgsLength == 0) | |
| { | |
| while (otLinkFilterGetNextRssIn(mInstance, &iterator, &entry) == OT_ERROR_NONE) | |
| { | |
| uint8_t i = 0; | |
| for (; i < OT_EXT_ADDRESS_SIZE; i++) | |
| { | |
| if (entry.mExtAddress.m8[i] != 0xff) | |
| { | |
| break; | |
| } | |
| } | |
| if (i == OT_EXT_ADDRESS_SIZE) | |
| { | |
| OutputLine("Default rss: %d (lqi %d)", entry.mRssIn, | |
| otLinkConvertRssToLinkQuality(mInstance, entry.mRssIn)); | |
| } | |
| else | |
| { | |
| OutputExtAddress(entry.mExtAddress); | |
| OutputLine(" : rss %d (lqi %d)", entry.mRssIn, otLinkConvertRssToLinkQuality(mInstance, entry.mRssIn)); | |
| } | |
| } | |
| } | |
| else | |
| { | |
| if (strcmp(aArgs[0], "add-lqi") == 0) | |
| { | |
| uint8_t linkQuality; | |
| VerifyOrExit(aArgsLength == 3, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsUint8(aArgs[2], linkQuality)); | |
| VerifyOrExit(linkQuality <= 3, error = OT_ERROR_INVALID_ARGS); | |
| rss = otLinkConvertLinkQualityToRss(mInstance, linkQuality); | |
| if (strcmp(aArgs[1], "*") == 0) | |
| { | |
| otLinkFilterSetDefaultRssIn(mInstance, rss); | |
| } | |
| else | |
| { | |
| SuccessOrExit(error = ParseAsHexString(aArgs[1], extAddr.m8)); | |
| SuccessOrExit(error = otLinkFilterAddRssIn(mInstance, &extAddr, rss)); | |
| } | |
| } | |
| else if (strcmp(aArgs[0], "add") == 0) | |
| { | |
| VerifyOrExit(aArgsLength == 3, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsInt8(aArgs[2], rss)); | |
| if (strcmp(aArgs[1], "*") == 0) | |
| { | |
| otLinkFilterSetDefaultRssIn(mInstance, rss); | |
| } | |
| else | |
| { | |
| SuccessOrExit(error = ParseAsHexString(aArgs[1], extAddr.m8)); | |
| SuccessOrExit(error = otLinkFilterAddRssIn(mInstance, &extAddr, rss)); | |
| } | |
| } | |
| else if (strcmp(aArgs[0], "remove") == 0) | |
| { | |
| VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS); | |
| if (strcmp(aArgs[1], "*") == 0) | |
| { | |
| otLinkFilterClearDefaultRssIn(mInstance); | |
| } | |
| else | |
| { | |
| SuccessOrExit(error = ParseAsHexString(aArgs[1], extAddr.m8)); | |
| otLinkFilterRemoveRssIn(mInstance, &extAddr); | |
| } | |
| } | |
| else if (strcmp(aArgs[0], "clear") == 0) | |
| { | |
| otLinkFilterClearAllRssIn(mInstance); | |
| } | |
| else | |
| { | |
| error = OT_ERROR_INVALID_COMMAND; | |
| } | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif // OPENTHREAD_CONFIG_MAC_FILTER_ENABLE | |
| otError Interpreter::ProcessMac(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); | |
| if (strcmp(aArgs[0], "retries") == 0) | |
| { | |
| error = ProcessMacRetries(aArgsLength - 1, aArgs + 1); | |
| } | |
| #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE | |
| else if (strcmp(aArgs[0], "send") == 0) | |
| { | |
| error = ProcessMacSend(aArgsLength - 1, aArgs + 1); | |
| } | |
| #endif | |
| else | |
| { | |
| error = OT_ERROR_INVALID_COMMAND; | |
| } | |
| exit: | |
| return error; | |
| } | |
| otError Interpreter::ProcessMacRetries(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| VerifyOrExit(aArgsLength > 0 && aArgsLength <= 2, error = OT_ERROR_INVALID_ARGS); | |
| if (strcmp(aArgs[0], "direct") == 0) | |
| { | |
| if (aArgsLength == 1) | |
| { | |
| OutputLine("%d", otLinkGetMaxFrameRetriesDirect(mInstance)); | |
| } | |
| else | |
| { | |
| uint8_t retries; | |
| SuccessOrExit(error = ParseAsUint8(aArgs[1], retries)); | |
| otLinkSetMaxFrameRetriesDirect(mInstance, retries); | |
| } | |
| } | |
| #if OPENTHREAD_FTD | |
| else if (strcmp(aArgs[0], "indirect") == 0) | |
| { | |
| if (aArgsLength == 1) | |
| { | |
| OutputLine("%d", otLinkGetMaxFrameRetriesIndirect(mInstance)); | |
| } | |
| else | |
| { | |
| uint8_t retries; | |
| SuccessOrExit(error = ParseAsUint8(aArgs[1], retries)); | |
| otLinkSetMaxFrameRetriesIndirect(mInstance, retries); | |
| } | |
| } | |
| #endif | |
| else | |
| { | |
| error = OT_ERROR_INVALID_ARGS; | |
| } | |
| exit: | |
| return error; | |
| } | |
| #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE | |
| otError Interpreter::ProcessMacSend(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_INVALID_ARGS; | |
| VerifyOrExit(aArgsLength == 1); | |
| if (strcmp(aArgs[0], "datarequest") == 0) | |
| { | |
| error = otLinkSendDataRequest(mInstance); | |
| } | |
| else if (strcmp(aArgs[0], "emptydata") == 0) | |
| { | |
| error = otLinkSendEmptyData(mInstance); | |
| } | |
| exit: | |
| return error; | |
| } | |
| #endif | |
| #if OPENTHREAD_CONFIG_DIAG_ENABLE | |
| otError Interpreter::ProcessDiag(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error; | |
| char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; | |
| // all diagnostics related features are processed within diagnostics module | |
| output[0] = '\0'; | |
| output[sizeof(output) - 1] = '\0'; | |
| error = otDiagProcessCmd(mInstance, aArgsLength, aArgs, output, sizeof(output) - 1); | |
| Output(output, static_cast<uint16_t>(strlen(output))); | |
| return error; | |
| } | |
| #endif | |
| void Interpreter::ProcessLine(char *aBuf, uint16_t aBufLength) | |
| { | |
| char * args[kMaxArgs] = {nullptr}; | |
| uint8_t argsLength; | |
| const Command *command; | |
| VerifyOrExit(aBuf != nullptr && StringLength(aBuf, aBufLength + 1) <= aBufLength); | |
| VerifyOrExit(Utils::CmdLineParser::ParseCmd(aBuf, argsLength, args, kMaxArgs) == OT_ERROR_NONE, | |
| OutputLine("Error: too many args (max %d)", kMaxArgs)); | |
| VerifyOrExit(argsLength >= 1, OutputLine("Error: no given command.")); | |
| #if OPENTHREAD_CONFIG_DIAG_ENABLE | |
| VerifyOrExit((!otDiagIsEnabled(mInstance) || (strcmp(args[0], "diag") == 0)), | |
| OutputLine("under diagnostics mode, execute 'diag stop' before running any other commands.")); | |
| #endif | |
| command = Utils::LookupTable::Find(args[0], sCommands); | |
| if (command != nullptr) | |
| { | |
| OutputResult((this->*command->mHandler)(argsLength - 1, &args[1])); | |
| ExitNow(); | |
| } | |
| // Check user defined commands if built-in command has not been found | |
| for (uint8_t i = 0; i < mUserCommandsLength; i++) | |
| { | |
| if (strcmp(args[0], mUserCommands[i].mName) == 0) | |
| { | |
| mUserCommands[i].mCommand(mUserCommandsContext, argsLength - 1, &args[1]); | |
| ExitNow(); | |
| } | |
| } | |
| OutputResult(OT_ERROR_INVALID_COMMAND); | |
| exit: | |
| return; | |
| } | |
| #if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE | |
| otError Interpreter::ProcessNetworkDiagnostic(uint8_t aArgsLength, char *aArgs[]) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| otIp6Address address; | |
| uint8_t tlvTypes[OT_NETWORK_DIAGNOSTIC_TYPELIST_MAX_ENTRIES]; | |
| uint8_t count = 0; | |
| uint8_t argsIndex = 0; | |
| // Include operation, address and type tlv list. | |
| VerifyOrExit(aArgsLength > 2, error = OT_ERROR_INVALID_ARGS); | |
| SuccessOrExit(error = ParseAsIp6Address(aArgs[1], address)); | |
| argsIndex = 2; | |
| while (argsIndex < aArgsLength && count < sizeof(tlvTypes)) | |
| { | |
| SuccessOrExit(error = ParseAsUint8(aArgs[argsIndex++], tlvTypes[count++])); | |
| } | |
| if (strcmp(aArgs[0], "get") == 0) | |
| { | |
| IgnoreError(otThreadSendDiagnosticGet(mInstance, &address, tlvTypes, count)); | |
| ExitNow(error = OT_ERROR_PENDING); | |
| } | |
| else if (strcmp(aArgs[0], "reset") == 0) | |
| { | |
| IgnoreError(otThreadSendDiagnosticReset(mInstance, &address, tlvTypes, count)); | |
| } | |
| else | |
| { | |
| error = OT_ERROR_INVALID_COMMAND; | |
| } | |
| exit: | |
| return error; | |
| } | |
| void Interpreter::HandleDiagnosticGetResponse(otError aError, | |
| otMessage * aMessage, | |
| const otMessageInfo *aMessageInfo, | |
| void * aContext) | |
| { | |
| static_cast<Interpreter *>(aContext)->HandleDiagnosticGetResponse( | |
| aError, aMessage, static_cast<const Ip6::MessageInfo *>(aMessageInfo)); | |
| } | |
| void Interpreter::HandleDiagnosticGetResponse(otError aError, | |
| const otMessage * aMessage, | |
| const Ip6::MessageInfo *aMessageInfo) | |
| { | |
| uint8_t buf[16]; | |
| uint16_t bytesToPrint; | |
| uint16_t bytesPrinted = 0; | |
| uint16_t length; | |
| otNetworkDiagTlv diagTlv; | |
| otNetworkDiagIterator iterator = OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT; | |
| SuccessOrExit(aError); | |
| OutputFormat("DIAG_GET.rsp/ans from "); | |
| OutputIp6Address(aMessageInfo->mPeerAddr); | |
| OutputFormat(": "); | |
| length = otMessageGetLength(aMessage) - otMessageGetOffset(aMessage); | |
| while (length > 0) | |
| { | |
| bytesToPrint = (length < sizeof(buf)) ? length : sizeof(buf); | |
| otMessageRead(aMessage, otMessageGetOffset(aMessage) + bytesPrinted, buf, bytesToPrint); | |
| OutputBytes(buf, static_cast<uint8_t>(bytesToPrint)); | |
| length -= bytesToPrint; | |
| bytesPrinted += bytesToPrint; | |
| } | |
| OutputLine(""); | |
| // Output Network Diagnostic TLV values in standard YAML format. | |
| while ((aError = otThreadGetNextDiagnosticTlv(aMessage, &iterator, &diagTlv)) == OT_ERROR_NONE) | |
| { | |
| switch (diagTlv.mType) | |
| { | |
| case OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS: | |
| OutputFormat("Ext Address: '"); | |
| OutputExtAddress(diagTlv.mData.mExtAddress); | |
| OutputLine("'"); | |
| break; | |
| case OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS: | |
| OutputLine("Rloc16: 0x%04x", diagTlv.mData.mAddr16); | |
| break; | |
| case OT_NETWORK_DIAGNOSTIC_TLV_MODE: | |
| OutputLine("Mode:"); | |
| OutputMode(kIndentSize, diagTlv.mData.mMode); | |
| break; | |
| case OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT: | |
| OutputLine("Timeout: %u", diagTlv.mData.mTimeout); | |
| break; | |
| case OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY: | |
| OutputLine("Connectivity:"); | |
| OutputConnectivity(kIndentSize, diagTlv.mData.mConnectivity); | |
| break; | |
| case OT_NETWORK_DIAGNOSTIC_TLV_ROUTE: | |
| OutputLine("Route:"); | |
| OutputRoute(kIndentSize, diagTlv.mData.mRoute); | |
| break; | |
| case OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA: | |
| OutputLine("Leader Data:"); | |
| OutputLeaderData(kIndentSize, diagTlv.mData.mLeaderData); | |
| break; | |
| case OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA: | |
| OutputFormat("Network Data: '"); | |
| OutputBytes(diagTlv.mData.mNetworkData.m8, diagTlv.mData.mNetworkData.mCount); | |
| OutputLine("'"); | |
| break; | |
| case OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST: | |
| OutputLine("IP6 Address List:"); | |
| for (uint16_t i = 0; i < diagTlv.mData.mIp6AddrList.mCount; ++i) | |
| { | |
| OutputFormat(kIndentSize, "- "); | |
| OutputIp6Address(diagTlv.mData.mIp6AddrList.mList[i]); | |
| OutputLine(""); | |
| } | |
| break; | |
| case OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS: | |
| OutputLine("MAC Counters:"); | |
| OutputNetworkDiagMacCounters(kIndentSize, diagTlv.mData.mMacCounters); | |
| break; | |
| case OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL: | |
| OutputLine("Battery Level: %u%%", diagTlv.mData.mBatteryLevel); | |
| break; | |
| case OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE: | |
| OutputLine("Supply Voltage: %umV", diagTlv.mData.mSupplyVoltage); | |
| break; | |
| case OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE: | |
| OutputLine("Child Table:"); | |
| for (uint16_t i = 0; i < diagTlv.mData.mChildTable.mCount; ++i) | |
| { | |
| OutputFormat(kIndentSize, "- "); | |
| OutputChildTableEntry(kIndentSize + 2, diagTlv.mData.mChildTable.mTable[i]); | |
| } | |
| break; | |
| case OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES: | |
| OutputFormat("Channel Pages: '"); | |
| OutputBytes(diagTlv.mData.mChannelPages.m8, diagTlv.mData.mChannelPages.mCount); | |
| OutputLine("'"); | |
| break; | |
| case OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT: | |
| OutputLine("Max Child Timeout: %u", diagTlv.mData.mMaxChildTimeout); | |
| break; | |
| } | |
| } | |
| if (aError == OT_ERROR_NOT_FOUND) | |
| { | |
| aError = OT_ERROR_NONE; | |
| } | |
| exit: | |
| OutputResult(aError); | |
| } | |
| void Interpreter::OutputMode(uint8_t aIndentSize, const otLinkModeConfig &aMode) | |
| { | |
| OutputLine(aIndentSize, "RxOnWhenIdle: %d", aMode.mRxOnWhenIdle); | |
| OutputLine(aIndentSize, "DeviceType: %d", aMode.mDeviceType); | |
| OutputLine(aIndentSize, "NetworkData: %d", aMode.mNetworkData); | |
| } | |
| void Interpreter::OutputConnectivity(uint8_t aIndentSize, const otNetworkDiagConnectivity &aConnectivity) | |
| { | |
| OutputLine(aIndentSize, "ParentPriority: %d", aConnectivity.mParentPriority); | |
| OutputLine(aIndentSize, "LinkQuality3: %u", aConnectivity.mLinkQuality3); | |
| OutputLine(aIndentSize, "LinkQuality2: %u", aConnectivity.mLinkQuality2); | |
| OutputLine(aIndentSize, "LinkQuality1: %u", aConnectivity.mLinkQuality1); | |
| OutputLine(aIndentSize, "LeaderCost: %u", aConnectivity.mLeaderCost); | |
| OutputLine(aIndentSize, "IdSequence: %u", aConnectivity.mIdSequence); | |
| OutputLine(aIndentSize, "ActiveRouters: %u", aConnectivity.mActiveRouters); | |
| OutputLine(aIndentSize, "SedBufferSize: %u", aConnectivity.mSedBufferSize); | |
| OutputLine(aIndentSize, "SedDatagramCount: %u", aConnectivity.mSedDatagramCount); | |
| } | |
| void Interpreter::OutputRoute(uint8_t aIndentSize, const otNetworkDiagRoute &aRoute) | |
| { | |
| OutputLine(aIndentSize, "IdSequence: %u", aRoute.mIdSequence); | |
| OutputLine(aIndentSize, "RouteData:"); | |
| aIndentSize += kIndentSize; | |
| for (uint16_t i = 0; i < aRoute.mRouteCount; ++i) | |
| { | |
| OutputFormat(aIndentSize, "- "); | |
| OutputRouteData(aIndentSize + 2, aRoute.mRouteData[i]); | |
| } | |
| } | |
| void Interpreter::OutputRouteData(uint8_t aIndentSize, const otNetworkDiagRouteData &aRouteData) | |
| { | |
| OutputLine("RouteId: 0x%02x", aRouteData.mRouterId); | |
| OutputLine(aIndentSize, "LinkQualityOut: %u", aRouteData.mLinkQualityOut); | |
| OutputLine(aIndentSize, "LinkQualityIn: %u", aRouteData.mLinkQualityIn); | |
| OutputLine(aIndentSize, "RouteCost: %u", aRouteData.mRouteCost); | |
| } | |
| void Interpreter::OutputLeaderData(uint8_t aIndentSize, const otLeaderData &aLeaderData) | |
| { | |
| OutputLine(aIndentSize, "PartitionId: 0x%08x", aLeaderData.mPartitionId); | |
| OutputLine(aIndentSize, "Weighting: %u", aLeaderData.mWeighting); | |
| OutputLine(aIndentSize, "DataVersion: %u", aLeaderData.mDataVersion); | |
| OutputLine(aIndentSize, "StableDataVersion: %u", aLeaderData.mStableDataVersion); | |
| OutputLine(aIndentSize, "LeaderRouterId: 0x%02x", aLeaderData.mLeaderRouterId); | |
| } | |
| void Interpreter::OutputNetworkDiagMacCounters(uint8_t aIndentSize, const otNetworkDiagMacCounters &aMacCounters) | |
| { | |
| OutputLine(aIndentSize, "IfInUnknownProtos: %u", aMacCounters.mIfInUnknownProtos); | |
| OutputLine(aIndentSize, "IfInErrors: %u", aMacCounters.mIfInErrors); | |
| OutputLine(aIndentSize, "IfOutErrors: %u", aMacCounters.mIfOutErrors); | |
| OutputLine(aIndentSize, "IfInUcastPkts: %u", aMacCounters.mIfInUcastPkts); | |
| OutputLine(aIndentSize, "IfInBroadcastPkts: %u", aMacCounters.mIfInBroadcastPkts); | |
| OutputLine(aIndentSize, "IfInDiscards: %u", aMacCounters.mIfInDiscards); | |
| OutputLine(aIndentSize, "IfOutUcastPkts: %u", aMacCounters.mIfOutUcastPkts); | |
| OutputLine(aIndentSize, "IfOutBroadcastPkts: %u", aMacCounters.mIfOutBroadcastPkts); | |
| OutputLine(aIndentSize, "IfOutDiscards: %u", aMacCounters.mIfOutDiscards); | |
| } | |
| void Interpreter::OutputChildTableEntry(uint8_t aIndentSize, const otNetworkDiagChildEntry &aChildEntry) | |
| { | |
| OutputLine("ChildId: 0x%04x", aChildEntry.mChildId); | |
| OutputLine(aIndentSize, "Timeout: %u", aChildEntry.mTimeout); | |
| OutputLine(aIndentSize, "Mode:"); | |
| OutputMode(aIndentSize + kIndentSize, aChildEntry.mMode); | |
| } | |
| #endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE | |
| void Interpreter::SetUserCommands(const otCliCommand *aCommands, uint8_t aLength, void *aContext) | |
| { | |
| mUserCommands = aCommands; | |
| mUserCommandsLength = aLength; | |
| mUserCommandsContext = aContext; | |
| } | |
| Interpreter &Interpreter::GetOwner(InstanceLocator &aInstanceLocator) | |
| { | |
| OT_UNUSED_VARIABLE(aInstanceLocator); | |
| return Interpreter::GetInterpreter(); | |
| } | |
| void Interpreter::SignalPingRequest(const Ip6::Address &aPeerAddress, | |
| uint16_t aPingLength, | |
| uint32_t aTimestamp, | |
| uint8_t aHopLimit) | |
| { | |
| OT_UNUSED_VARIABLE(aPeerAddress); | |
| OT_UNUSED_VARIABLE(aPingLength); | |
| OT_UNUSED_VARIABLE(aTimestamp); | |
| OT_UNUSED_VARIABLE(aHopLimit); | |
| #if OPENTHREAD_CONFIG_OTNS_ENABLE | |
| mInstance->Get<Utils::Otns>().EmitPingRequest(aPeerAddress, aPingLength, aTimestamp, aHopLimit); | |
| #endif | |
| } | |
| void Interpreter::SignalPingReply(const Ip6::Address &aPeerAddress, | |
| uint16_t aPingLength, | |
| uint32_t aTimestamp, | |
| uint8_t aHopLimit) | |
| { | |
| OT_UNUSED_VARIABLE(aPeerAddress); | |
| OT_UNUSED_VARIABLE(aPingLength); | |
| OT_UNUSED_VARIABLE(aTimestamp); | |
| OT_UNUSED_VARIABLE(aHopLimit); | |
| #if OPENTHREAD_CONFIG_OTNS_ENABLE | |
| mInstance->Get<Utils::Otns>().EmitPingReply(aPeerAddress, aPingLength, aTimestamp, aHopLimit); | |
| #endif | |
| } | |
| void Interpreter::HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo &aInfo) | |
| { | |
| OutputFormat("~ Discovery Request from "); | |
| OutputExtAddress(aInfo.mExtAddress); | |
| OutputLine(": version=%u,joiner=%d", aInfo.mVersion, aInfo.mIsJoiner); | |
| } | |
| int Interpreter::OutputFormat(const char *aFormat, ...) | |
| { | |
| int rval; | |
| va_list ap; | |
| va_start(ap, aFormat); | |
| rval = OutputFormatV(aFormat, ap); | |
| va_end(ap); | |
| return rval; | |
| } | |
| void Interpreter::OutputFormat(uint8_t aIndentSize, const char *aFormat, ...) | |
| { | |
| va_list ap; | |
| OutputSpaces(aIndentSize); | |
| va_start(ap, aFormat); | |
| OutputFormatV(aFormat, ap); | |
| va_end(ap); | |
| } | |
| void Interpreter::OutputLine(const char *aFormat, ...) | |
| { | |
| va_list args; | |
| va_start(args, aFormat); | |
| OutputFormatV(aFormat, args); | |
| va_end(args); | |
| OutputFormat("\r\n"); | |
| } | |
| void Interpreter::OutputLine(uint8_t aIndentSize, const char *aFormat, ...) | |
| { | |
| va_list args; | |
| OutputSpaces(aIndentSize); | |
| va_start(args, aFormat); | |
| OutputFormatV(aFormat, args); | |
| va_end(args); | |
| OutputFormat("\r\n"); | |
| } | |
| void Interpreter::OutputSpaces(uint8_t aCount) | |
| { | |
| static const char kSpaces[] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}; | |
| while (aCount > 0) | |
| { | |
| uint8_t len = OT_MIN(aCount, sizeof(kSpaces)); | |
| Output(kSpaces, len); | |
| aCount -= len; | |
| } | |
| } | |
| int Interpreter::OutputFormatV(const char *aFormat, va_list aArguments) | |
| { | |
| char buf[kMaxLineLength]; | |
| vsnprintf(buf, sizeof(buf), aFormat, aArguments); | |
| return Output(buf, static_cast<uint16_t>(strlen(buf))); | |
| } | |
| extern "C" void otCliSetUserCommands(const otCliCommand *aUserCommands, uint8_t aLength, void *aContext) | |
| { | |
| Interpreter::GetInterpreter().SetUserCommands(aUserCommands, aLength, aContext); | |
| } | |
| extern "C" void otCliOutputBytes(const uint8_t *aBytes, uint8_t aLength) | |
| { | |
| Interpreter::GetInterpreter().OutputBytes(aBytes, aLength); | |
| } | |
| extern "C" void otCliOutputFormat(const char *aFmt, ...) | |
| { | |
| va_list aAp; | |
| va_start(aAp, aFmt); | |
| Interpreter::GetInterpreter().OutputFormatV(aFmt, aAp); | |
| va_end(aAp); | |
| } | |
| extern "C" void otCliOutput(const char *aString, uint16_t aLength) | |
| { | |
| Interpreter::GetInterpreter().Output(aString, aLength); | |
| } | |
| extern "C" void otCliAppendResult(otError aError) | |
| { | |
| Interpreter::GetInterpreter().OutputResult(aError); | |
| } | |
| extern "C" void otCliPlatLogv(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, va_list aArgs) | |
| { | |
| OT_UNUSED_VARIABLE(aLogLevel); | |
| OT_UNUSED_VARIABLE(aLogRegion); | |
| VerifyOrExit(Interpreter::IsInitialized()); | |
| Interpreter::GetInterpreter().OutputFormatV(aFormat, aArgs); | |
| Interpreter::GetInterpreter().OutputLine(""); | |
| exit: | |
| return; | |
| } | |
| extern "C" void otCliPlatLogLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogLine) | |
| { | |
| OT_UNUSED_VARIABLE(aLogLevel); | |
| OT_UNUSED_VARIABLE(aLogRegion); | |
| VerifyOrExit(Interpreter::IsInitialized()); | |
| Interpreter::GetInterpreter().OutputLine(aLogLine); | |
| exit: | |
| return; | |
| } | |
| } // namespace Cli | |
| } // namespace ot | |
| #if OPENTHREAD_CONFIG_LEGACY_ENABLE | |
| OT_TOOL_WEAK void otNcpRegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers) | |
| { | |
| OT_UNUSED_VARIABLE(aHandlers); | |
| } | |
| OT_TOOL_WEAK void otNcpHandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix) | |
| { | |
| OT_UNUSED_VARIABLE(aUlaPrefix); | |
| } | |
| OT_TOOL_WEAK void otNcpHandleLegacyNodeDidJoin(const otExtAddress *aExtAddr) | |
| { | |
| OT_UNUSED_VARIABLE(aExtAddr); | |
| } | |
| #endif |