| /* |
| * 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/dns.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_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 |
| otThreadSetDiscoveryRequestCallback(mInstance, &Interpreter::HandleDiscoveryRequest, this); |
| #endif |
| |
| mIcmpHandler.mReceiveCallback = Interpreter::HandleIcmpReceive; |
| mIcmpHandler.mContext = this; |
| IgnoreError(otIcmp6RegisterHandler(mInstance, &mIcmpHandler)); |
| } |
| |
| 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]); |
| } |
| } |
| |
| void Interpreter::OutputEnabledDisabledStatus(bool aEnabled) |
| { |
| OutputLine(aEnabled ? "Enabled" : "Disabled"); |
| } |
| |
| 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) |
| { |
| OutputEnabledDisabledStatus(otPlatRadioIsCoexEnabled(mInstance)); |
| } |
| 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; |
| } |
| |
| void Interpreter::OutputDnsTxtData(const uint8_t *aTxtData, uint16_t aTxtDataLength) |
| { |
| otDnsTxtEntry entry; |
| otDnsTxtEntryIterator iterator; |
| bool isFirst = true; |
| |
| otDnsInitTxtEntryIterator(&iterator, aTxtData, aTxtDataLength); |
| |
| OutputFormat("["); |
| |
| while (otDnsGetNextTxtEntry(&iterator, &entry) == OT_ERROR_NONE) |
| { |
| if (!isFirst) |
| { |
| OutputFormat(", "); |
| } |
| |
| if (entry.mKey == nullptr) |
| { |
| // A null `mKey` indicates that the key in the entry is |
| // longer than the recommended max key length, so the entry |
| // could not be parsed. In this case, the whole entry is |
| // returned encoded in `mValue`. |
| |
| OutputFormat("["); |
| OutputBytes(entry.mValue, entry.mValueLength); |
| OutputFormat("]"); |
| } |
| else |
| { |
| OutputFormat("%s", entry.mKey); |
| |
| if (entry.mValue != nullptr) |
| { |
| OutputFormat("="); |
| OutputBytes(entry.mValue, entry.mValueLength); |
| } |
| } |
| |
| isFirst = false; |
| } |
| |
| OutputFormat("]"); |
| } |
| |
| #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE |
| |
| otError Interpreter::GetDnsConfig(uint8_t aArgsLength, |
| char * aArgs[], |
| otDnsQueryConfig *&aConfig, |
| uint8_t aStartArgsIndex) |
| { |
| // This method gets the optional config from given `aArgs` after the |
| // `aStartArgsIndex`. The format: `[server IPv6 address] [server |
| // port] [timeout] [max tx attempt] [recursion desired]`. |
| |
| otError error = OT_ERROR_NONE; |
| bool recursionDesired; |
| |
| memset(aConfig, 0, sizeof(otDnsQueryConfig)); |
| |
| VerifyOrExit(aArgsLength > aStartArgsIndex, aConfig = nullptr); |
| |
| SuccessOrExit(error = ParseAsIp6Address(aArgs[aStartArgsIndex], aConfig->mServerSockAddr.mAddress)); |
| |
| VerifyOrExit(aArgsLength > aStartArgsIndex + 1); |
| SuccessOrExit(error = ParseAsUint16(aArgs[aStartArgsIndex + 1], aConfig->mServerSockAddr.mPort)); |
| |
| VerifyOrExit(aArgsLength > aStartArgsIndex + 2); |
| SuccessOrExit(error = ParseAsUint32(aArgs[aStartArgsIndex + 2], aConfig->mResponseTimeout)); |
| |
| VerifyOrExit(aArgsLength > aStartArgsIndex + 3); |
| SuccessOrExit(error = ParseAsUint8(aArgs[aStartArgsIndex + 3], aConfig->mMaxTxAttempts)); |
| |
| VerifyOrExit(aArgsLength > aStartArgsIndex + 4); |
| SuccessOrExit(error = ParseAsBool(aArgs[aStartArgsIndex + 4], recursionDesired)); |
| aConfig->mRecursionFlag = recursionDesired ? OT_DNS_FLAG_RECURSION_DESIRED : OT_DNS_FLAG_NO_RECURSION; |
| |
| exit: |
| return error; |
| } |
| |
| otError Interpreter::ProcessDns(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| otDnsQueryConfig queryConfig; |
| otDnsQueryConfig *config = &queryConfig; |
| |
| VerifyOrExit(aArgsLength > 0, error = OT_ERROR_INVALID_ARGS); |
| |
| if (strcmp(aArgs[0], "config") == 0) |
| { |
| if (aArgsLength == 1) |
| { |
| const otDnsQueryConfig *defaultConfig = otDnsClientGetDefaultConfig(mInstance); |
| |
| OutputFormat("Server: ["); |
| OutputIp6Address(defaultConfig->mServerSockAddr.mAddress); |
| OutputLine("]:%d", defaultConfig->mServerSockAddr.mPort); |
| OutputLine("ResponseTimeout: %u ms", defaultConfig->mResponseTimeout); |
| OutputLine("MaxTxAttempts: %u", defaultConfig->mMaxTxAttempts); |
| OutputLine("RecursionDesired: %s", |
| (defaultConfig->mRecursionFlag == OT_DNS_FLAG_RECURSION_DESIRED) ? "yes" : "no"); |
| } |
| else |
| { |
| SuccessOrExit(error = GetDnsConfig(aArgsLength, aArgs, config, 1)); |
| otDnsClientSetDefaultConfig(mInstance, config); |
| } |
| } |
| else if (strcmp(aArgs[0], "resolve") == 0) |
| { |
| SuccessOrExit(error = GetDnsConfig(aArgsLength, aArgs, config, 2)); |
| SuccessOrExit(error = otDnsClientResolveAddress(mInstance, aArgs[1], &Interpreter::HandleDnsAddressResponse, |
| this, config)); |
| error = OT_ERROR_PENDING; |
| } |
| #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE |
| else if (strcmp(aArgs[0], "browse") == 0) |
| { |
| SuccessOrExit(error = GetDnsConfig(aArgsLength, aArgs, config, 2)); |
| SuccessOrExit(error = |
| otDnsClientBrowse(mInstance, aArgs[1], &Interpreter::HandleDnsBrowseResponse, this, config)); |
| error = OT_ERROR_PENDING; |
| } |
| else if (strcmp(aArgs[0], "service") == 0) |
| { |
| SuccessOrExit(error = GetDnsConfig(aArgsLength, aArgs, config, 3)); |
| SuccessOrExit(error = otDnsClientResolveService(mInstance, aArgs[1], aArgs[2], |
| &Interpreter::HandleDnsServiceResponse, this, config)); |
| error = OT_ERROR_PENDING; |
| } |
| #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_COMMAND); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| void Interpreter::HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse, void *aContext) |
| { |
| static_cast<Interpreter *>(aContext)->HandleDnsAddressResponse(aError, aResponse); |
| } |
| |
| void Interpreter::HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse) |
| { |
| char hostName[OT_DNS_MAX_NAME_SIZE]; |
| otIp6Address address; |
| uint32_t ttl; |
| |
| IgnoreError(otDnsAddressResponseGetHostName(aResponse, hostName, sizeof(hostName))); |
| |
| OutputFormat("DNS response for %s - ", hostName); |
| |
| if (aError == OT_ERROR_NONE) |
| { |
| uint16_t index = 0; |
| |
| while (otDnsAddressResponseGetAddress(aResponse, index, &address, &ttl) == OT_ERROR_NONE) |
| { |
| OutputIp6Address(address); |
| OutputFormat(" TTL:%u ", ttl); |
| index++; |
| } |
| |
| OutputLine(""); |
| } |
| |
| OutputResult(aError); |
| } |
| |
| #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE |
| |
| void Interpreter::OutputDnsServiceInfo(uint8_t aIndentSize, const otDnsServiceInfo &aServiceInfo) |
| { |
| OutputLine(aIndentSize, "Port:%d, Priority:%d, Weight:%d, TTL:%u", aServiceInfo.mPort, aServiceInfo.mPriority, |
| aServiceInfo.mWeight, aServiceInfo.mTtl); |
| OutputLine(aIndentSize, "Host:%s", aServiceInfo.mHostNameBuffer); |
| OutputFormat(aIndentSize, "HostAddress:"); |
| OutputIp6Address(aServiceInfo.mHostAddress); |
| OutputLine(" TTL:%u", aServiceInfo.mHostAddressTtl); |
| OutputFormat(aIndentSize, "TXT:"); |
| OutputDnsTxtData(aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize); |
| OutputLine(" TTL:%u", aServiceInfo.mTxtDataTtl); |
| } |
| |
| void Interpreter::HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse, void *aContext) |
| { |
| static_cast<Interpreter *>(aContext)->HandleDnsBrowseResponse(aError, aResponse); |
| } |
| |
| void Interpreter::HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse) |
| { |
| char name[OT_DNS_MAX_NAME_SIZE]; |
| char label[OT_DNS_MAX_LABEL_SIZE]; |
| uint8_t txtBuffer[255]; |
| otDnsServiceInfo serviceInfo; |
| |
| IgnoreError(otDnsBrowseResponseGetServiceName(aResponse, name, sizeof(name))); |
| |
| OutputLine("DNS browse response for %s", name); |
| |
| if (aError == OT_ERROR_NONE) |
| { |
| uint16_t index = 0; |
| |
| while (otDnsBrowseResponseGetServiceInstance(aResponse, index, label, sizeof(label)) == OT_ERROR_NONE) |
| { |
| OutputLine("%s", label); |
| index++; |
| |
| serviceInfo.mHostNameBuffer = name; |
| serviceInfo.mHostNameBufferSize = sizeof(name); |
| serviceInfo.mTxtData = txtBuffer; |
| serviceInfo.mTxtDataSize = sizeof(txtBuffer); |
| |
| if (otDnsBrowseResponseGetServiceInfo(aResponse, label, &serviceInfo) == OT_ERROR_NONE) |
| { |
| OutputDnsServiceInfo(kIndentSize, serviceInfo); |
| } |
| |
| OutputLine(""); |
| } |
| } |
| |
| OutputResult(aError); |
| } |
| |
| void Interpreter::HandleDnsServiceResponse(otError aError, const otDnsServiceResponse *aResponse, void *aContext) |
| { |
| static_cast<Interpreter *>(aContext)->HandleDnsServiceResponse(aError, aResponse); |
| } |
| |
| void Interpreter::HandleDnsServiceResponse(otError aError, const otDnsServiceResponse *aResponse) |
| { |
| char name[OT_DNS_MAX_NAME_SIZE]; |
| char label[OT_DNS_MAX_LABEL_SIZE]; |
| uint8_t txtBuffer[255]; |
| otDnsServiceInfo serviceInfo; |
| |
| IgnoreError(otDnsServiceResponseGetServiceName(aResponse, label, sizeof(label), name, sizeof(name))); |
| |
| OutputLine("DNS service resolution response for %s for service %s", label, name); |
| |
| if (aError == OT_ERROR_NONE) |
| { |
| serviceInfo.mHostNameBuffer = name; |
| serviceInfo.mHostNameBufferSize = sizeof(name); |
| serviceInfo.mTxtData = txtBuffer; |
| serviceInfo.mTxtDataSize = sizeof(txtBuffer); |
| |
| if (otDnsServiceResponseGetServiceInfo(aResponse, &serviceInfo) == OT_ERROR_NONE) |
| { |
| OutputDnsServiceInfo(/* aIndetSize */ 0, serviceInfo); |
| OutputLine(""); |
| } |
| } |
| |
| OutputResult(aError); |
| } |
| |
| #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE |
| #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) |
| { |
| OutputEnabledDisabledStatus(otIp6IsMulticastPromiscuousEnabled(mInstance)); |
| } |
| 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; |
| } |
| |
| otError Interpreter::ProcessMultiRadio(uint8_t aArgsLength, char *aArgs[]) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| OT_UNUSED_VARIABLE(aArgs); |
| |
| if (aArgsLength == 0) |
| { |
| bool isFirst = true; |
| |
| OutputFormat("["); |
| #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE |
| OutputFormat("15.4"); |
| isFirst = false; |
| #endif |
| #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE |
| OutputFormat("%sTREL", isFirst ? "" : ", "); |
| #endif |
| OutputLine("]"); |
| |
| OT_UNUSED_VARIABLE(isFirst); |
| } |
| #if OPENTHREAD_CONFIG_MULTI_RADIO |
| else if (strcmp(aArgs[0], "neighbor") == 0) |
| { |
| otMultiRadioNeighborInfo multiRadioInfo; |
| |
| VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS); |
| |
| if (strcmp(aArgs[1], "list") == 0) |
| { |
| otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT; |
| otNeighborInfo neighInfo; |
| |
| while (otThreadGetNextNeighborInfo(mInstance, &iterator, &neighInfo) == OT_ERROR_NONE) |
| { |
| if (otMultiRadioGetNeighborInfo(mInstance, &neighInfo.mExtAddress, &multiRadioInfo) != OT_ERROR_NONE) |
| { |
| continue; |
| } |
| |
| OutputFormat("ExtAddr:"); |
| OutputExtAddress(neighInfo.mExtAddress); |
| OutputFormat(", RLOC16:0x%04x, Radios:", neighInfo.mRloc16); |
| OutputMultiRadioInfo(multiRadioInfo); |
| } |
| } |
| else |
| { |
| otExtAddress extAddress; |
| |
| SuccessOrExit(error = ParseAsHexString(aArgs[1], extAddress.m8)); |
| SuccessOrExit(error = otMultiRadioGetNeighborInfo(mInstance, &extAddress, &multiRadioInfo)); |
| OutputMultiRadioInfo(multiRadioInfo); |
| } |
| } |
| #endif // OPENTHREAD_CONFIG_MULTI_RADIO |
| else |
| { |
| ExitNow(error = OT_ERROR_INVALID_COMMAND); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| #if OPENTHREAD_CONFIG_MULTI_RADIO |
| void Interpreter::OutputMultiRadioInfo(const otMultiRadioNeighborInfo &aMultiRadioInfo) |
| { |
| bool isFirst = true; |
| |
| OutputFormat("["); |
| |
| if (aMultiRadioInfo.mSupportsIeee802154) |
| { |
| OutputFormat("15.4(%d)", aMultiRadioInfo.mIeee802154Info.mPreference); |
| isFirst = false; |
| } |
| |
| if (aMultiRadioInfo.mSupportsTrelUdp6) |
| { |
| OutputFormat("%sTREL(%d)", isFirst ? "" : ", ", aMultiRadioInfo.mTrelUdp6Info.mPreference); |
| } |
| |
| OutputLine("]"); |
| } |
| #endif // OPENTHREAD_CONFIG_MULTI_RADIO |
| |
| #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) |
| { |
| OutputEnabledDisabledStatus(otLinkIsPromiscuous(mInstance) && otPlatRadioGetPromiscuous(mInstance)); |
| } |
| 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) |
| { |
| OutputEnabledDisabledStatus(otThreadIsRouterEligible(mInstance)); |
| } |
| 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) |
| { |
| SuccessOrExit(error = otThreadSendDiagnosticGet(mInstance, &address, tlvTypes, count, |
| &Interpreter::HandleDiagnosticGetResponse, this)); |
| 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 |