• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2016, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file implements the CLI interpreter.
32  */
33 
34 #include "cli.hpp"
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 
40 #include <openthread/child_supervision.h>
41 #include <openthread/diag.h>
42 #include <openthread/dns.h>
43 #include <openthread/icmp6.h>
44 #include <openthread/link.h>
45 #include <openthread/logging.h>
46 #include <openthread/ncp.h>
47 #include <openthread/thread.h>
48 #include "common/num_utils.hpp"
49 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
50 #include <openthread/network_time.h>
51 #endif
52 #if OPENTHREAD_FTD
53 #include <openthread/dataset_ftd.h>
54 #include <openthread/thread_ftd.h>
55 #endif
56 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
57 #include <openthread/border_router.h>
58 #endif
59 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
60 #include <openthread/server.h>
61 #endif
62 #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
63 #include <openthread/platform/misc.h>
64 #endif
65 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
66 #include <openthread/backbone_router.h>
67 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
68 #include <openthread/backbone_router_ftd.h>
69 #endif
70 #endif
71 #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
72     (OPENTHREAD_FTD ||                          \
73      (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
74 #include <openthread/channel_manager.h>
75 #endif
76 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
77 #include <openthread/channel_monitor.h>
78 #endif
79 #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX
80 #include <openthread/platform/debug_uart.h>
81 #endif
82 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
83 #include <openthread/trel.h>
84 #endif
85 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE || OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
86 #include <openthread/nat64.h>
87 #endif
88 #if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
89 #include <openthread/radio_stats.h>
90 #endif
91 #include "common/new.hpp"
92 #include "common/numeric_limits.hpp"
93 #include "common/string.hpp"
94 #include "mac/channel_mask.hpp"
95 
96 namespace ot {
97 namespace Cli {
98 
99 Interpreter *Interpreter::sInterpreter = nullptr;
100 static OT_DEFINE_ALIGNED_VAR(sInterpreterRaw, sizeof(Interpreter), uint64_t);
101 
Interpreter(Instance * aInstance,otCliOutputCallback aCallback,void * aContext)102 Interpreter::Interpreter(Instance *aInstance, otCliOutputCallback aCallback, void *aContext)
103     : OutputImplementer(aCallback, aContext)
104     , Utils(aInstance, *this)
105     , mCommandIsPending(false)
106     , mInternalDebugCommand(false)
107     , mTimer(*aInstance, HandleTimer, this)
108 #if OPENTHREAD_FTD || OPENTHREAD_MTD
109 #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
110     , mSntpQueryingInProgress(false)
111 #endif
112     , mDataset(aInstance, *this)
113     , mNetworkData(aInstance, *this)
114     , mUdp(aInstance, *this)
115 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
116     , mMacFilter(aInstance, *this)
117 #endif
118 #if OPENTHREAD_CLI_DNS_ENABLE
119     , mDns(aInstance, *this)
120 #endif
121 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
122     , mBbr(aInstance, *this)
123 #endif
124 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
125     , mBr(aInstance, *this)
126 #endif
127 #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
128     , mTcp(aInstance, *this)
129 #endif
130 #if OPENTHREAD_CONFIG_COAP_API_ENABLE
131     , mCoap(aInstance, *this)
132 #endif
133 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
134     , mCoapSecure(aInstance, *this)
135 #endif
136 #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
137     , mCommissioner(aInstance, *this)
138 #endif
139 #if OPENTHREAD_CONFIG_JOINER_ENABLE
140     , mJoiner(aInstance, *this)
141 #endif
142 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
143     , mSrpClient(aInstance, *this)
144 #endif
145 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
146     , mSrpServer(aInstance, *this)
147 #endif
148 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
149     , mHistory(aInstance, *this)
150 #endif
151 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
152     , mLinkMetrics(aInstance, *this)
153 #endif
154 #if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE
155     , mTcat(aInstance, *this)
156 #endif
157 #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
158     , mPing(aInstance, *this)
159 #endif
160 #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
161     , mLocateInProgress(false)
162 #endif
163 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
164 {
165 #if (OPENTHREAD_FTD || OPENTHREAD_MTD) && OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK
166     otIp6SetReceiveCallback(GetInstancePtr(), &Interpreter::HandleIp6Receive, this);
167 #endif
168     ClearAllBytes(mUserCommands);
169 
170     OutputPrompt();
171 }
172 
OutputResult(otError aError)173 void Interpreter::OutputResult(otError aError)
174 {
175     if (mInternalDebugCommand)
176     {
177         if (aError != OT_ERROR_NONE)
178         {
179             OutputLine("Error %u: %s", aError, otThreadErrorToString(aError));
180         }
181 
182         ExitNow();
183     }
184 
185     OT_ASSERT(mCommandIsPending);
186 
187     VerifyOrExit(aError != OT_ERROR_PENDING);
188 
189     if (aError == OT_ERROR_NONE)
190     {
191         OutputLine("Done");
192     }
193     else
194     {
195         OutputLine("Error %u: %s", aError, otThreadErrorToString(aError));
196     }
197 
198     mCommandIsPending = false;
199     mTimer.Stop();
200     OutputPrompt();
201 
202 exit:
203     return;
204 }
205 
LinkModeToString(const otLinkModeConfig & aLinkMode,char (& aStringBuffer)[kLinkModeStringSize])206 const char *Interpreter::LinkModeToString(const otLinkModeConfig &aLinkMode, char (&aStringBuffer)[kLinkModeStringSize])
207 {
208     char *flagsPtr = &aStringBuffer[0];
209 
210     if (aLinkMode.mRxOnWhenIdle)
211     {
212         *flagsPtr++ = 'r';
213     }
214 
215     if (aLinkMode.mDeviceType)
216     {
217         *flagsPtr++ = 'd';
218     }
219 
220     if (aLinkMode.mNetworkData)
221     {
222         *flagsPtr++ = 'n';
223     }
224 
225     if (flagsPtr == &aStringBuffer[0])
226     {
227         *flagsPtr++ = '-';
228     }
229 
230     *flagsPtr = '\0';
231 
232     return aStringBuffer;
233 }
234 
235 #if OPENTHREAD_CONFIG_DIAG_ENABLE
Process(Arg aArgs[])236 template <> otError Interpreter::Process<Cmd("diag")>(Arg aArgs[])
237 {
238     otError error;
239     char   *args[kMaxArgs];
240     char    output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE];
241 
242     // all diagnostics related features are processed within diagnostics module
243     Arg::CopyArgsToStringArray(aArgs, args);
244 
245     error = otDiagProcessCmd(GetInstancePtr(), Arg::GetArgsLength(aArgs), args, output, sizeof(output));
246 
247     OutputFormat("%s", output);
248 
249     return error;
250 }
251 #endif
252 
Process(Arg aArgs[])253 template <> otError Interpreter::Process<Cmd("version")>(Arg aArgs[])
254 {
255     otError error = OT_ERROR_NONE;
256 
257     /**
258      * @cli version
259      * @code
260      * version
261      * OPENTHREAD/gf4f2f04; Jul 1 2016 17:00:09
262      * Done
263      * @endcode
264      * @par api_copy
265      * #otGetVersionString
266      */
267     if (aArgs[0].IsEmpty())
268     {
269         OutputLine("%s", otGetVersionString());
270     }
271 
272     /**
273      * @cli version api
274      * @code
275      * version api
276      * 28
277      * Done
278      * @endcode
279      * @par
280      * Prints the API version number.
281      */
282     else if (aArgs[0] == "api")
283     {
284         OutputLine("%u", OPENTHREAD_API_VERSION);
285     }
286     else
287     {
288         error = OT_ERROR_INVALID_COMMAND;
289     }
290 
291     return error;
292 }
293 
Process(Arg aArgs[])294 template <> otError Interpreter::Process<Cmd("reset")>(Arg aArgs[])
295 {
296     otError error = OT_ERROR_NONE;
297 
298     if (aArgs[0].IsEmpty())
299     {
300         otInstanceReset(GetInstancePtr());
301     }
302 
303 #if OPENTHREAD_CONFIG_PLATFORM_BOOTLOADER_MODE_ENABLE
304     /**
305      * @cli reset bootloader
306      * @code
307      * reset bootloader
308      * @endcode
309      * @cparam reset bootloader
310      * @par api_copy
311      * #otInstanceResetToBootloader
312      */
313     else if (aArgs[0] == "bootloader")
314     {
315         error = otInstanceResetToBootloader(GetInstancePtr());
316     }
317 #endif
318     else
319     {
320         error = OT_ERROR_INVALID_COMMAND;
321     }
322 
323     return error;
324 }
325 
ProcessLine(char * aBuf)326 void Interpreter::ProcessLine(char *aBuf)
327 {
328     Arg     args[kMaxArgs + 1];
329     otError error = OT_ERROR_NONE;
330 
331     OT_ASSERT(aBuf != nullptr);
332 
333     if (!mInternalDebugCommand)
334     {
335         // Ignore the command if another command is pending.
336         VerifyOrExit(!mCommandIsPending, args[0].Clear());
337         mCommandIsPending = true;
338 
339         VerifyOrExit(StringLength(aBuf, kMaxLineLength) <= kMaxLineLength - 1, error = OT_ERROR_PARSE);
340     }
341 
342     SuccessOrExit(error = ot::Utils::CmdLineParser::ParseCmd(aBuf, args, kMaxArgs));
343     VerifyOrExit(!args[0].IsEmpty(), mCommandIsPending = false);
344 
345     if (!mInternalDebugCommand)
346     {
347         LogInput(args);
348 
349 #if OPENTHREAD_CONFIG_DIAG_ENABLE
350         if (otDiagIsEnabled(GetInstancePtr()) && (args[0] != "diag") && (args[0] != "factoryreset"))
351         {
352             OutputLine("under diagnostics mode, execute 'diag stop' before running any other commands.");
353             ExitNow(error = OT_ERROR_INVALID_STATE);
354         }
355 #endif
356     }
357 
358     error = ProcessCommand(args);
359 
360 exit:
361     if ((error != OT_ERROR_NONE) || !args[0].IsEmpty())
362     {
363         OutputResult(error);
364     }
365     else if (!mCommandIsPending)
366     {
367         OutputPrompt();
368     }
369 }
370 
ProcessUserCommands(Arg aArgs[])371 otError Interpreter::ProcessUserCommands(Arg aArgs[])
372 {
373     otError error = OT_ERROR_INVALID_COMMAND;
374 
375     for (const UserCommandsEntry &entry : mUserCommands)
376     {
377         for (uint8_t i = 0; i < entry.mLength; i++)
378         {
379             if (aArgs[0] == entry.mCommands[i].mName)
380             {
381                 char *args[kMaxArgs];
382 
383                 Arg::CopyArgsToStringArray(aArgs, args);
384                 error = entry.mCommands[i].mCommand(entry.mContext, Arg::GetArgsLength(aArgs) - 1, args + 1);
385                 break;
386             }
387         }
388     }
389 
390     return error;
391 }
392 
SetUserCommands(const otCliCommand * aCommands,uint8_t aLength,void * aContext)393 otError Interpreter::SetUserCommands(const otCliCommand *aCommands, uint8_t aLength, void *aContext)
394 {
395     otError error = OT_ERROR_FAILED;
396 
397     for (UserCommandsEntry &entry : mUserCommands)
398     {
399         if (entry.mCommands == nullptr)
400         {
401             entry.mCommands = aCommands;
402             entry.mLength   = aLength;
403             entry.mContext  = aContext;
404 
405             error = OT_ERROR_NONE;
406             break;
407         }
408     }
409 
410     return error;
411 }
412 
413 #if OPENTHREAD_FTD || OPENTHREAD_MTD
414 
ParseJoinerDiscerner(Arg & aArg,otJoinerDiscerner & aDiscerner)415 otError Interpreter::ParseJoinerDiscerner(Arg &aArg, otJoinerDiscerner &aDiscerner)
416 {
417     otError error;
418     char   *separator;
419 
420     VerifyOrExit(!aArg.IsEmpty(), error = OT_ERROR_INVALID_ARGS);
421 
422     separator = strstr(aArg.GetCString(), "/");
423 
424     VerifyOrExit(separator != nullptr, error = OT_ERROR_NOT_FOUND);
425 
426     SuccessOrExit(error = ot::Utils::CmdLineParser::ParseAsUint8(separator + 1, aDiscerner.mLength));
427     VerifyOrExit(aDiscerner.mLength > 0 && aDiscerner.mLength <= 64, error = OT_ERROR_INVALID_ARGS);
428     *separator = '\0';
429     error      = aArg.ParseAsUint64(aDiscerner.mValue);
430 
431 exit:
432     return error;
433 }
434 
ParsePreference(const Arg & aArg,otRoutePreference & aPreference)435 otError Interpreter::ParsePreference(const Arg &aArg, otRoutePreference &aPreference)
436 {
437     otError error = OT_ERROR_NONE;
438 
439     if (aArg == "high")
440     {
441         aPreference = OT_ROUTE_PREFERENCE_HIGH;
442     }
443     else if (aArg == "med")
444     {
445         aPreference = OT_ROUTE_PREFERENCE_MED;
446     }
447     else if (aArg == "low")
448     {
449         aPreference = OT_ROUTE_PREFERENCE_LOW;
450     }
451     else
452     {
453         error = OT_ERROR_INVALID_ARGS;
454     }
455 
456     return error;
457 }
458 
PreferenceToString(signed int aPreference)459 const char *Interpreter::PreferenceToString(signed int aPreference)
460 {
461     const char *str = "";
462 
463     switch (aPreference)
464     {
465     case OT_ROUTE_PREFERENCE_LOW:
466         str = "low";
467         break;
468 
469     case OT_ROUTE_PREFERENCE_MED:
470         str = "med";
471         break;
472 
473     case OT_ROUTE_PREFERENCE_HIGH:
474         str = "high";
475         break;
476 
477     default:
478         break;
479     }
480 
481     return str;
482 }
483 
ParseToIp6Address(otInstance * aInstance,const Arg & aArg,otIp6Address & aAddress,bool & aSynthesized)484 otError Interpreter::ParseToIp6Address(otInstance   *aInstance,
485                                        const Arg    &aArg,
486                                        otIp6Address &aAddress,
487                                        bool         &aSynthesized)
488 {
489     Error error = OT_ERROR_NONE;
490 
491     VerifyOrExit(!aArg.IsEmpty(), error = OT_ERROR_INVALID_ARGS);
492     error        = aArg.ParseAsIp6Address(aAddress);
493     aSynthesized = false;
494 
495     if (error != OT_ERROR_NONE)
496     {
497         // It might be an IPv4 address, let's have a try.
498         otIp4Address ip4Address;
499 
500         // Do not touch the error value if we failed to parse it as an IPv4 address.
501         SuccessOrExit(aArg.ParseAsIp4Address(ip4Address));
502         SuccessOrExit(error = otNat64SynthesizeIp6Address(aInstance, &ip4Address, &aAddress));
503         aSynthesized = true;
504     }
505 
506 exit:
507     return error;
508 }
509 
510 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
Process(Arg aArgs[])511 template <> otError Interpreter::Process<Cmd("history")>(Arg aArgs[]) { return mHistory.Process(aArgs); }
512 #endif
513 
514 #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
Process(Arg aArgs[])515 template <> otError Interpreter::Process<Cmd("ba")>(Arg aArgs[])
516 {
517     otError error = OT_ERROR_NONE;
518 
519     /**
520      * @cli ba port
521      * @code
522      * ba port
523      * 49153
524      * Done
525      * @endcode
526      * @par api_copy
527      * #otBorderAgentGetUdpPort
528      */
529     if (aArgs[0] == "port")
530     {
531         OutputLine("%hu", otBorderAgentGetUdpPort(GetInstancePtr()));
532     }
533     /**
534      * @cli ba state
535      * @code
536      * ba state
537      * Started
538      * Done
539      * @endcode
540      * @par api_copy
541      * #otBorderAgentGetState
542      */
543     else if (aArgs[0] == "state")
544     {
545         static const char *const kStateStrings[] = {
546             "Stopped", // (0) OT_BORDER_AGENT_STATE_STOPPED
547             "Started", // (1) OT_BORDER_AGENT_STATE_STARTED
548             "Active",  // (2) OT_BORDER_AGENT_STATE_ACTIVE
549         };
550 
551         static_assert(0 == OT_BORDER_AGENT_STATE_STOPPED, "OT_BORDER_AGENT_STATE_STOPPED value is incorrect");
552         static_assert(1 == OT_BORDER_AGENT_STATE_STARTED, "OT_BORDER_AGENT_STATE_STARTED value is incorrect");
553         static_assert(2 == OT_BORDER_AGENT_STATE_ACTIVE, "OT_BORDER_AGENT_STATE_ACTIVE value is incorrect");
554 
555         OutputLine("%s", Stringify(otBorderAgentGetState(GetInstancePtr()), kStateStrings));
556     }
557 #if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
558     /**
559      * @cli ba id (get,set)
560      * @code
561      * ba id
562      * cb6da1e0c0448aaec39fa90f3d58f45c
563      * Done
564      * @endcode
565      * @code
566      * ba id 00112233445566778899aabbccddeeff
567      * Done
568      * @endcode
569      * @cparam ba id [@ca{border-agent-id}]
570      * Use the optional `border-agent-id` argument to set the Border Agent ID.
571      * @par
572      * Gets or sets the 16 bytes Border Router ID which can uniquely identifies the device among multiple BRs.
573      * @sa otBorderAgentGetId
574      * @sa otBorderAgentSetId
575      */
576     else if (aArgs[0] == "id")
577     {
578         otBorderAgentId id;
579 
580         if (aArgs[1].IsEmpty())
581         {
582             SuccessOrExit(error = otBorderAgentGetId(GetInstancePtr(), &id));
583             OutputBytesLine(id.mId);
584         }
585         else
586         {
587             uint16_t idLength = sizeof(id);
588 
589             SuccessOrExit(error = aArgs[1].ParseAsHexString(idLength, id.mId));
590             VerifyOrExit(idLength == sizeof(id), error = OT_ERROR_INVALID_ARGS);
591             error = otBorderAgentSetId(GetInstancePtr(), &id);
592         }
593     }
594 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
595 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
596     else if (aArgs[0] == "ephemeralkey")
597     {
598         /**
599          * @cli ba ephemeralkey
600          * @code
601          * ba ephemeralkey
602          * active
603          * Done
604          * @endcode
605          * @par api_copy
606          * #otBorderAgentIsEphemeralKeyActive
607          */
608         if (aArgs[1].IsEmpty())
609         {
610             OutputLine("%sactive", otBorderAgentIsEphemeralKeyActive(GetInstancePtr()) ? "" : "in");
611         }
612         /**
613          * @cli ba ephemeralkey set <keystring> [timeout-in-msec] [port]
614          * @code
615          * ba ephemeralkey set Z10X20g3J15w1000P60m16 5000 1234
616          * Done
617          * @endcode
618          * @par api_copy
619          * #otBorderAgentSetEphemeralKey
620          */
621         else if (aArgs[1] == "set")
622         {
623             uint32_t timeout = 0;
624             uint16_t port    = 0;
625 
626             VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
627 
628             if (!aArgs[3].IsEmpty())
629             {
630                 SuccessOrExit(error = aArgs[3].ParseAsUint32(timeout));
631             }
632 
633             if (!aArgs[4].IsEmpty())
634             {
635                 SuccessOrExit(error = aArgs[4].ParseAsUint16(port));
636             }
637 
638             error = otBorderAgentSetEphemeralKey(GetInstancePtr(), aArgs[2].GetCString(), timeout, port);
639         }
640         /**
641          * @cli ba ephemeralkey clear
642          * @code
643          * ba ephemeralkey clear
644          * Done
645          * @endcode
646          * @par api_copy
647          * #otBorderAgentClearEphemeralKey
648          */
649         else if (aArgs[1] == "clear")
650         {
651             otBorderAgentClearEphemeralKey(GetInstancePtr());
652         }
653         /**
654          * @cli ba ephemeralkey callback (enable, disable)
655          * @code
656          * ba ephemeralkey callback enable
657          * Done
658          * ba ephemeralkey set W10X1 5000 49155
659          * Done
660          * BorderAgent callback: Ephemeral key active, port:49155
661          * BorderAgent callback: Ephemeral key inactive
662          * @endcode
663          * @par api_copy
664          * #otBorderAgentSetEphemeralKeyCallback
665          */
666         else if (aArgs[1] == "callback")
667         {
668             bool enable;
669 
670             SuccessOrExit(error = ParseEnableOrDisable(aArgs[2], enable));
671 
672             if (enable)
673             {
674                 otBorderAgentSetEphemeralKeyCallback(GetInstancePtr(), HandleBorderAgentEphemeralKeyStateChange, this);
675             }
676             else
677             {
678                 otBorderAgentSetEphemeralKeyCallback(GetInstancePtr(), nullptr, nullptr);
679             }
680         }
681         else
682         {
683             error = OT_ERROR_INVALID_ARGS;
684         }
685     }
686 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
687     else
688     {
689         ExitNow(error = OT_ERROR_INVALID_COMMAND);
690     }
691 
692 exit:
693     return error;
694 }
695 
696 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
HandleBorderAgentEphemeralKeyStateChange(void * aContext)697 void Interpreter::HandleBorderAgentEphemeralKeyStateChange(void *aContext)
698 {
699     reinterpret_cast<Interpreter *>(aContext)->HandleBorderAgentEphemeralKeyStateChange();
700 }
701 
HandleBorderAgentEphemeralKeyStateChange(void)702 void Interpreter::HandleBorderAgentEphemeralKeyStateChange(void)
703 {
704     bool active = otBorderAgentIsEphemeralKeyActive(GetInstancePtr());
705 
706     OutputFormat("BorderAgent callback: Ephemeral key %sactive", active ? "" : "in");
707 
708     if (active)
709     {
710         OutputFormat(", port:%u", otBorderAgentGetUdpPort(GetInstancePtr()));
711     }
712 
713     OutputNewLine();
714 }
715 #endif
716 
717 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
718 
719 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
Process(Arg aArgs[])720 template <> otError Interpreter::Process<Cmd("br")>(Arg aArgs[]) { return mBr.Process(aArgs); }
721 #endif
722 
723 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE || OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
Process(Arg aArgs[])724 template <> otError Interpreter::Process<Cmd("nat64")>(Arg aArgs[])
725 {
726     otError error = OT_ERROR_NONE;
727 
728     if (aArgs[0].IsEmpty())
729     {
730         ExitNow(error = OT_ERROR_INVALID_COMMAND);
731     }
732 
733     /**
734      * @cli nat64 (enable,disable)
735      * @code
736      * nat64 enable
737      * Done
738      * @endcode
739      * @code
740      * nat64 disable
741      * Done
742      * @endcode
743      * @cparam nat64 @ca{enable|disable}
744      * @par api_copy
745      * #otNat64SetEnabled
746      *
747      */
748     if (ProcessEnableDisable(aArgs, otNat64SetEnabled) == OT_ERROR_NONE)
749     {
750     }
751     /**
752      * @cli nat64 state
753      * @code
754      * nat64 state
755      * PrefixManager: Active
756      * Translator: Active
757      * Done
758      * @endcode
759      * @par
760      * Gets the state of NAT64 functions.
761      * @par
762      * `PrefixManager` state is available when `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is enabled.
763      * `Translator` state is available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.
764      * @par
765      * When `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is enabled, `PrefixManager` returns one of the following
766      * states:
767      * - `Disabled`: NAT64 prefix manager is disabled.
768      * - `NotRunning`: NAT64 prefix manager is enabled, but is not running. This could mean that the routing manager is
769      *   disabled.
770      * - `Idle`: NAT64 prefix manager is enabled and is running, but is not publishing a NAT64 prefix. This can happen
771      *   when there is another border router publishing a NAT64 prefix with a higher priority.
772      * - `Active`: NAT64 prefix manager is enabled, running, and publishing a NAT64 prefix.
773      * @par
774      * When `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled, `Translator` returns one of the following states:
775      * - `Disabled`: NAT64 translator is disabled.
776      * - `NotRunning`: NAT64 translator is enabled, but is not translating packets. This could mean that the Translator
777      *   is not configured with a NAT64 prefix or a CIDR for NAT64.
778      * - `Active`: NAT64 translator is enabled and is translating packets.
779      * @sa otNat64GetPrefixManagerState
780      * @sa otNat64GetTranslatorState
781      *
782      */
783     else if (aArgs[0] == "state")
784     {
785         static const char *const kNat64State[] = {"Disabled", "NotRunning", "Idle", "Active"};
786 
787         static_assert(0 == OT_NAT64_STATE_DISABLED, "OT_NAT64_STATE_DISABLED value is incorrect");
788         static_assert(1 == OT_NAT64_STATE_NOT_RUNNING, "OT_NAT64_STATE_NOT_RUNNING value is incorrect");
789         static_assert(2 == OT_NAT64_STATE_IDLE, "OT_NAT64_STATE_IDLE value is incorrect");
790         static_assert(3 == OT_NAT64_STATE_ACTIVE, "OT_NAT64_STATE_ACTIVE value is incorrect");
791 
792 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
793         OutputLine("PrefixManager: %s", kNat64State[otNat64GetPrefixManagerState(GetInstancePtr())]);
794 #endif
795 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
796         OutputLine("Translator: %s", kNat64State[otNat64GetTranslatorState(GetInstancePtr())]);
797 #endif
798     }
799 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
800     else if (aArgs[0] == "cidr")
801     {
802         otIp4Cidr cidr;
803 
804         /**
805          * @cli nat64 cidr
806          * @code
807          * nat64 cidr
808          * 192.168.255.0/24
809          * Done
810          * @endcode
811          * @par api_copy
812          * #otNat64GetCidr
813          *
814          */
815         if (aArgs[1].IsEmpty())
816         {
817             char cidrString[OT_IP4_CIDR_STRING_SIZE];
818 
819             SuccessOrExit(error = otNat64GetCidr(GetInstancePtr(), &cidr));
820             otIp4CidrToString(&cidr, cidrString, sizeof(cidrString));
821             OutputLine("%s", cidrString);
822         }
823         /**
824          * @cli nat64 cidr <cidr>
825          * @code
826          * nat64 cidr 192.168.255.0/24
827          * Done
828          * @endcode
829          * @par api_copy
830          * #otPlatNat64SetIp4Cidr
831          *
832          */
833         else
834         {
835             SuccessOrExit(error = otIp4CidrFromString(aArgs[1].GetCString(), &cidr));
836             error = otNat64SetIp4Cidr(GetInstancePtr(), &cidr);
837         }
838     }
839     /**
840      * @cli nat64 mappings
841      * @code
842      * nat64 mappings
843      * |          | Address                   |        | 4 to 6       | 6 to 4       |
844      * +----------+---------------------------+--------+--------------+--------------+
845      * | ID       | IPv6       | IPv4         | Expiry | Pkts | Bytes | Pkts | Bytes |
846      * +----------+------------+--------------+--------+------+-------+------+-------+
847      * | 00021cb9 | fdc7::df79 | 192.168.64.2 |  7196s |    6 |   456 |   11 |  1928 |
848      * |          |                                TCP |    0 |     0 |    0 |     0 |
849      * |          |                                UDP |    1 |   136 |   16 |  1608 |
850      * |          |                               ICMP |    5 |   320 |    5 |   320 |
851      * @endcode
852      * @par api_copy
853      * #otNat64GetNextAddressMapping
854      *
855      */
856     else if (aArgs[0] == "mappings")
857     {
858         static const char *const kNat64StatusLevel1Title[] = {"", "Address", "", "4 to 6", "6 to 4"};
859 
860         static const uint8_t kNat64StatusLevel1ColumnWidths[] = {
861             18, 61, 8, 25, 25,
862         };
863 
864         static const char *const kNat64StatusTableHeader[] = {
865             "ID", "IPv6", "IPv4", "Expiry", "Pkts", "Bytes", "Pkts", "Bytes",
866         };
867 
868         static const uint8_t kNat64StatusTableColumnWidths[] = {
869             18, 42, 18, 8, 10, 14, 10, 14,
870         };
871 
872         otNat64AddressMappingIterator iterator;
873         otNat64AddressMapping         mapping;
874 
875         OutputTableHeader(kNat64StatusLevel1Title, kNat64StatusLevel1ColumnWidths);
876         OutputTableHeader(kNat64StatusTableHeader, kNat64StatusTableColumnWidths);
877 
878         otNat64InitAddressMappingIterator(GetInstancePtr(), &iterator);
879         while (otNat64GetNextAddressMapping(GetInstancePtr(), &iterator, &mapping) == OT_ERROR_NONE)
880         {
881             char ip4AddressString[OT_IP4_ADDRESS_STRING_SIZE];
882             char ip6AddressString[OT_IP6_PREFIX_STRING_SIZE];
883 
884             otIp6AddressToString(&mapping.mIp6, ip6AddressString, sizeof(ip6AddressString));
885             otIp4AddressToString(&mapping.mIp4, ip4AddressString, sizeof(ip4AddressString));
886 
887             OutputFormat("| %08lx%08lx ", ToUlong(static_cast<uint32_t>(mapping.mId >> 32)),
888                          ToUlong(static_cast<uint32_t>(mapping.mId & 0xffffffff)));
889             OutputFormat("| %40s ", ip6AddressString);
890             OutputFormat("| %16s ", ip4AddressString);
891             OutputFormat("| %5lus ", ToUlong(mapping.mRemainingTimeMs / 1000));
892             OutputNat64Counters(mapping.mCounters.mTotal);
893 
894             OutputFormat("| %16s ", "");
895             OutputFormat("| %68s ", "TCP");
896             OutputNat64Counters(mapping.mCounters.mTcp);
897 
898             OutputFormat("| %16s ", "");
899             OutputFormat("| %68s ", "UDP");
900             OutputNat64Counters(mapping.mCounters.mUdp);
901 
902             OutputFormat("| %16s ", "");
903             OutputFormat("| %68s ", "ICMP");
904             OutputNat64Counters(mapping.mCounters.mIcmp);
905         }
906     }
907     /**
908      * @cli nat64 counters
909      * @code
910      * nat64 counters
911      * |               | 4 to 6                  | 6 to 4                  |
912      * +---------------+-------------------------+-------------------------+
913      * | Protocol      | Pkts     | Bytes        | Pkts     | Bytes        |
914      * +---------------+----------+--------------+----------+--------------+
915      * |         Total |       11 |          704 |       11 |          704 |
916      * |           TCP |        0 |            0 |        0 |            0 |
917      * |           UDP |        0 |            0 |        0 |            0 |
918      * |          ICMP |       11 |          704 |       11 |          704 |
919      * | Errors        | Pkts                    | Pkts                    |
920      * +---------------+-------------------------+-------------------------+
921      * |         Total |                       8 |                       4 |
922      * |   Illegal Pkt |                       0 |                       0 |
923      * |   Unsup Proto |                       0 |                       0 |
924      * |    No Mapping |                       2 |                       0 |
925      * Done
926      * @endcode
927      * @par
928      * Gets the NAT64 translator packet and error counters.
929      * @par
930      * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled.
931      * @sa otNat64GetCounters
932      * @sa otNat64GetErrorCounters
933      *
934      */
935     else if (aArgs[0] == "counters")
936     {
937         static const char *const kNat64CounterTableHeader[] = {
938             "",
939             "4 to 6",
940             "6 to 4",
941         };
942         static const uint8_t     kNat64CounterTableHeaderColumns[] = {15, 25, 25};
943         static const char *const kNat64CounterTableSubHeader[]     = {
944                 "Protocol", "Pkts", "Bytes", "Pkts", "Bytes",
945         };
946         static const uint8_t kNat64CounterTableSubHeaderColumns[] = {
947             15, 10, 14, 10, 14,
948         };
949         static const char *const kNat64CounterTableErrorSubHeader[] = {
950             "Errors",
951             "Pkts",
952             "Pkts",
953         };
954         static const uint8_t kNat64CounterTableErrorSubHeaderColumns[] = {
955             15,
956             25,
957             25,
958         };
959         static const char *const kNat64CounterErrorType[] = {
960             "Unknown",
961             "Illegal Pkt",
962             "Unsup Proto",
963             "No Mapping",
964         };
965 
966         otNat64ProtocolCounters counters;
967         otNat64ErrorCounters    errorCounters;
968         Uint64StringBuffer      u64StringBuffer;
969 
970         OutputTableHeader(kNat64CounterTableHeader, kNat64CounterTableHeaderColumns);
971         OutputTableHeader(kNat64CounterTableSubHeader, kNat64CounterTableSubHeaderColumns);
972 
973         otNat64GetCounters(GetInstancePtr(), &counters);
974         otNat64GetErrorCounters(GetInstancePtr(), &errorCounters);
975 
976         OutputFormat("| %13s ", "Total");
977         OutputNat64Counters(counters.mTotal);
978 
979         OutputFormat("| %13s ", "TCP");
980         OutputNat64Counters(counters.mTcp);
981 
982         OutputFormat("| %13s ", "UDP");
983         OutputNat64Counters(counters.mUdp);
984 
985         OutputFormat("| %13s ", "ICMP");
986         OutputNat64Counters(counters.mIcmp);
987 
988         OutputTableHeader(kNat64CounterTableErrorSubHeader, kNat64CounterTableErrorSubHeaderColumns);
989         for (uint8_t i = 0; i < OT_NAT64_DROP_REASON_COUNT; i++)
990         {
991             OutputFormat("| %13s | %23s ", kNat64CounterErrorType[i],
992                          Uint64ToString(errorCounters.mCount4To6[i], u64StringBuffer));
993             OutputLine("| %23s |", Uint64ToString(errorCounters.mCount6To4[i], u64StringBuffer));
994         }
995     }
996 #endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
997     else
998     {
999         ExitNow(error = OT_ERROR_INVALID_COMMAND);
1000     }
1001 
1002 exit:
1003     return error;
1004 }
1005 
1006 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
OutputNat64Counters(const otNat64Counters & aCounters)1007 void Interpreter::OutputNat64Counters(const otNat64Counters &aCounters)
1008 {
1009     Uint64StringBuffer u64StringBuffer;
1010 
1011     OutputFormat("| %8s ", Uint64ToString(aCounters.m4To6Packets, u64StringBuffer));
1012     OutputFormat("| %12s ", Uint64ToString(aCounters.m4To6Bytes, u64StringBuffer));
1013     OutputFormat("| %8s ", Uint64ToString(aCounters.m6To4Packets, u64StringBuffer));
1014     OutputLine("| %12s |", Uint64ToString(aCounters.m6To4Bytes, u64StringBuffer));
1015 }
1016 #endif
1017 
1018 #endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE || OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
1019 
1020 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
1021 
Process(Arg aArgs[])1022 template <> otError Interpreter::Process<Cmd("bbr")>(Arg aArgs[]) { return mBbr.Process(aArgs); }
1023 
1024 /**
1025  * @cli domainname
1026  * @code
1027  * domainname
1028  * Thread
1029  * Done
1030  * @endcode
1031  * @par api_copy
1032  * #otThreadGetDomainName
1033  */
Process(Arg aArgs[])1034 template <> otError Interpreter::Process<Cmd("domainname")>(Arg aArgs[])
1035 {
1036     /**
1037      * @cli domainname (set)
1038      * @code
1039      * domainname Test\ Thread
1040      * Done
1041      * @endcode
1042      * @cparam domainname @ca{name}
1043      * Use a `backslash` to escape spaces.
1044      * @par api_copy
1045      * #otThreadSetDomainName
1046      */
1047     return ProcessGetSet(aArgs, otThreadGetDomainName, otThreadSetDomainName);
1048 }
1049 
1050 #if OPENTHREAD_CONFIG_DUA_ENABLE
Process(Arg aArgs[])1051 template <> otError Interpreter::Process<Cmd("dua")>(Arg aArgs[])
1052 {
1053     otError error = OT_ERROR_NONE;
1054 
1055     /**
1056      * @cli dua iid
1057      * @code
1058      * dua iid
1059      * 0004000300020001
1060      * Done
1061      * @endcode
1062      * @par api_copy
1063      * #otThreadGetFixedDuaInterfaceIdentifier
1064      */
1065     if (aArgs[0] == "iid")
1066     {
1067         if (aArgs[1].IsEmpty())
1068         {
1069             const otIp6InterfaceIdentifier *iid = otThreadGetFixedDuaInterfaceIdentifier(GetInstancePtr());
1070 
1071             if (iid != nullptr)
1072             {
1073                 OutputBytesLine(iid->mFields.m8);
1074             }
1075         }
1076         /**
1077          * @cli dua iid (set,clear)
1078          * @code
1079          * dua iid 0004000300020001
1080          * Done
1081          * @endcode
1082          * @code
1083          * dua iid clear
1084          * Done
1085          * @endcode
1086          * @cparam dua iid @ca{iid|clear}
1087          * `dua iid clear` passes a `nullptr` to #otThreadSetFixedDuaInterfaceIdentifier.
1088          * Otherwise, you can pass the `iid`.
1089          * @par api_copy
1090          * #otThreadSetFixedDuaInterfaceIdentifier
1091          */
1092         else if (aArgs[1] == "clear")
1093         {
1094             error = otThreadSetFixedDuaInterfaceIdentifier(GetInstancePtr(), nullptr);
1095         }
1096         else
1097         {
1098             otIp6InterfaceIdentifier iid;
1099 
1100             SuccessOrExit(error = aArgs[1].ParseAsHexString(iid.mFields.m8));
1101             error = otThreadSetFixedDuaInterfaceIdentifier(GetInstancePtr(), &iid);
1102         }
1103     }
1104     else
1105     {
1106         error = OT_ERROR_INVALID_COMMAND;
1107     }
1108 
1109 exit:
1110     return error;
1111 }
1112 #endif // OPENTHREAD_CONFIG_DUA_ENABLE
1113 
1114 #endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
1115 
1116 /**
1117  * @cli bufferinfo
1118  * @code
1119  * bufferinfo
1120  * total: 40
1121  * free: 40
1122  * max-used: 5
1123  * 6lo send: 0 0 0
1124  * 6lo reas: 0 0 0
1125  * ip6: 0 0 0
1126  * mpl: 0 0 0
1127  * mle: 0 0 0
1128  * coap: 0 0 0
1129  * coap secure: 0 0 0
1130  * application coap: 0 0 0
1131  * Done
1132  * @endcode
1133  * @par
1134  * Gets the current message buffer information.
1135  * *   `total` displays the total number of message buffers in pool.
1136  * *   `free` displays the number of free message buffers.
1137  * *   `max-used` displays max number of used buffers at the same time since OT stack
1138  *     initialization or last `bufferinfo reset`.
1139  * @par
1140  * Next, the CLI displays info about different queues used by the OpenThread stack,
1141  * for example `6lo send`. Each line after the queue represents info about a queue:
1142  * *   The first number shows number messages in the queue.
1143  * *   The second number shows number of buffers used by all messages in the queue.
1144  * *   The third number shows total number of bytes of all messages in the queue.
1145  * @sa otMessageGetBufferInfo
1146  */
Process(Arg aArgs[])1147 template <> otError Interpreter::Process<Cmd("bufferinfo")>(Arg aArgs[])
1148 {
1149     struct BufferInfoName
1150     {
1151         const otMessageQueueInfo otBufferInfo::*mQueuePtr;
1152         const char                             *mName;
1153     };
1154 
1155     static const BufferInfoName kBufferInfoNames[] = {
1156         {&otBufferInfo::m6loSendQueue, "6lo send"},
1157         {&otBufferInfo::m6loReassemblyQueue, "6lo reas"},
1158         {&otBufferInfo::mIp6Queue, "ip6"},
1159         {&otBufferInfo::mMplQueue, "mpl"},
1160         {&otBufferInfo::mMleQueue, "mle"},
1161         {&otBufferInfo::mCoapQueue, "coap"},
1162         {&otBufferInfo::mCoapSecureQueue, "coap secure"},
1163         {&otBufferInfo::mApplicationCoapQueue, "application coap"},
1164     };
1165 
1166     otError error = OT_ERROR_NONE;
1167 
1168     if (aArgs[0].IsEmpty())
1169     {
1170         otBufferInfo bufferInfo;
1171 
1172         otMessageGetBufferInfo(GetInstancePtr(), &bufferInfo);
1173 
1174         OutputLine("total: %u", bufferInfo.mTotalBuffers);
1175         OutputLine("free: %u", bufferInfo.mFreeBuffers);
1176         OutputLine("max-used: %u", bufferInfo.mMaxUsedBuffers);
1177 
1178         for (const BufferInfoName &info : kBufferInfoNames)
1179         {
1180             OutputLine("%s: %u %u %lu", info.mName, (bufferInfo.*info.mQueuePtr).mNumMessages,
1181                        (bufferInfo.*info.mQueuePtr).mNumBuffers, ToUlong((bufferInfo.*info.mQueuePtr).mTotalBytes));
1182         }
1183     }
1184     /**
1185      * @cli bufferinfo reset
1186      * @code
1187      * bufferinfo reset
1188      * Done
1189      * @endcode
1190      * @par api_copy
1191      * #otMessageResetBufferInfo
1192      */
1193     else if (aArgs[0] == "reset")
1194     {
1195         otMessageResetBufferInfo(GetInstancePtr());
1196     }
1197     else
1198     {
1199         error = OT_ERROR_INVALID_ARGS;
1200     }
1201 
1202     return error;
1203 }
1204 
1205 /**
1206  * @cli ccathreshold (get,set)
1207  * @code
1208  * ccathreshold
1209  * -75 dBm
1210  * Done
1211  * @endcode
1212  * @code
1213  * ccathreshold -62
1214  * Done
1215  * @endcode
1216  * @cparam ccathreshold [@ca{CCA-threshold-dBm}]
1217  * Use the optional `CCA-threshold-dBm` argument to set the CCA threshold.
1218  * @par
1219  * Gets or sets the CCA threshold in dBm measured at the antenna connector per
1220  * IEEE 802.15.4 - 2015 section 10.1.4.
1221  * @sa otPlatRadioGetCcaEnergyDetectThreshold
1222  * @sa otPlatRadioSetCcaEnergyDetectThreshold
1223  */
Process(Arg aArgs[])1224 template <> otError Interpreter::Process<Cmd("ccathreshold")>(Arg aArgs[])
1225 {
1226     otError error = OT_ERROR_NONE;
1227     int8_t  cca;
1228 
1229     if (aArgs[0].IsEmpty())
1230     {
1231         SuccessOrExit(error = otPlatRadioGetCcaEnergyDetectThreshold(GetInstancePtr(), &cca));
1232         OutputLine("%d dBm", cca);
1233     }
1234     else
1235     {
1236         SuccessOrExit(error = aArgs[0].ParseAsInt8(cca));
1237         error = otPlatRadioSetCcaEnergyDetectThreshold(GetInstancePtr(), cca);
1238     }
1239 
1240 exit:
1241     return error;
1242 }
1243 
1244 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
Process(Arg aArgs[])1245 template <> otError Interpreter::Process<Cmd("ccm")>(Arg aArgs[])
1246 {
1247     return ProcessEnableDisable(aArgs, otThreadSetCcmEnabled);
1248 }
1249 
1250 /**
1251  * @cli tvcheck (enable,disable)
1252  * @code
1253  * tvcheck enable
1254  * Done
1255  * @endcode
1256  * @code
1257  * tvcheck disable
1258  * Done
1259  * @endcode
1260  * @par
1261  * Enables or disables the Thread version check when upgrading to router or leader.
1262  * This check is enabled by default.
1263  * @note `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is required.
1264  * @sa otThreadSetThreadVersionCheckEnabled
1265  */
Process(Arg aArgs[])1266 template <> otError Interpreter::Process<Cmd("tvcheck")>(Arg aArgs[])
1267 {
1268     return ProcessEnableDisable(aArgs, otThreadSetThreadVersionCheckEnabled);
1269 }
1270 #endif
1271 
1272 /**
1273  * @cli channel (get,set)
1274  * @code
1275  * channel
1276  * 11
1277  * Done
1278  * @endcode
1279  * @code
1280  * channel 11
1281  * Done
1282  * @endcode
1283  * @cparam channel [@ca{channel-num}]
1284  * Use `channel-num` to set the channel.
1285  * @par
1286  * Gets or sets the IEEE 802.15.4 Channel value.
1287  */
Process(Arg aArgs[])1288 template <> otError Interpreter::Process<Cmd("channel")>(Arg aArgs[])
1289 {
1290     otError error = OT_ERROR_NONE;
1291 
1292     /**
1293      * @cli channel supported
1294      * @code
1295      * channel supported
1296      * 0x7fff800
1297      * Done
1298      * @endcode
1299      * @par api_copy
1300      * #otPlatRadioGetSupportedChannelMask
1301      */
1302     if (aArgs[0] == "supported")
1303     {
1304         OutputLine("0x%lx", ToUlong(otPlatRadioGetSupportedChannelMask(GetInstancePtr())));
1305     }
1306     /**
1307      * @cli channel preferred
1308      * @code
1309      * channel preferred
1310      * 0x7fff800
1311      * Done
1312      * @endcode
1313      * @par api_copy
1314      * #otPlatRadioGetPreferredChannelMask
1315      */
1316     else if (aArgs[0] == "preferred")
1317     {
1318         OutputLine("0x%lx", ToUlong(otPlatRadioGetPreferredChannelMask(GetInstancePtr())));
1319     }
1320 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
1321     /**
1322      * @cli channel monitor
1323      * @code
1324      * channel monitor
1325      * enabled: 1
1326      * interval: 41000
1327      * threshold: -75
1328      * window: 960
1329      * count: 10552
1330      * occupancies:
1331      * ch 11 (0x0cb7)  4.96% busy
1332      * ch 12 (0x2e2b) 18.03% busy
1333      * ch 13 (0x2f54) 18.48% busy
1334      * ch 14 (0x0fef)  6.22% busy
1335      * ch 15 (0x1536)  8.28% busy
1336      * ch 16 (0x1746)  9.09% busy
1337      * ch 17 (0x0b8b)  4.50% busy
1338      * ch 18 (0x60a7) 37.75% busy
1339      * ch 19 (0x0810)  3.14% busy
1340      * ch 20 (0x0c2a)  4.75% busy
1341      * ch 21 (0x08dc)  3.46% busy
1342      * ch 22 (0x101d)  6.29% busy
1343      * ch 23 (0x0092)  0.22% busy
1344      * ch 24 (0x0028)  0.06% busy
1345      * ch 25 (0x0063)  0.15% busy
1346      * ch 26 (0x058c)  2.16% busy
1347      * Done
1348      * @endcode
1349      * @par
1350      * Get the current channel monitor state and channel occupancy.
1351      * `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` is required.
1352      */
1353     else if (aArgs[0] == "monitor")
1354     {
1355         if (aArgs[1].IsEmpty())
1356         {
1357             OutputLine("enabled: %d", otChannelMonitorIsEnabled(GetInstancePtr()));
1358             if (otChannelMonitorIsEnabled(GetInstancePtr()))
1359             {
1360                 uint32_t channelMask = otLinkGetSupportedChannelMask(GetInstancePtr());
1361                 uint8_t  channelNum  = BitSizeOf(channelMask);
1362 
1363                 OutputLine("interval: %lu", ToUlong(otChannelMonitorGetSampleInterval(GetInstancePtr())));
1364                 OutputLine("threshold: %d", otChannelMonitorGetRssiThreshold(GetInstancePtr()));
1365                 OutputLine("window: %lu", ToUlong(otChannelMonitorGetSampleWindow(GetInstancePtr())));
1366                 OutputLine("count: %lu", ToUlong(otChannelMonitorGetSampleCount(GetInstancePtr())));
1367 
1368                 OutputLine("occupancies:");
1369 
1370                 for (uint8_t channel = 0; channel < channelNum; channel++)
1371                 {
1372                     uint16_t               occupancy;
1373                     PercentageStringBuffer stringBuffer;
1374 
1375                     if (!((1UL << channel) & channelMask))
1376                     {
1377                         continue;
1378                     }
1379 
1380                     occupancy = otChannelMonitorGetChannelOccupancy(GetInstancePtr(), channel);
1381 
1382                     OutputLine("ch %u (0x%04x) %6s%% busy", channel, occupancy,
1383                                PercentageToString(occupancy, stringBuffer));
1384                 }
1385 
1386                 OutputNewLine();
1387             }
1388         }
1389         /**
1390          * @cli channel monitor start
1391          * @code
1392          * channel monitor start
1393          * channel monitor start
1394          * Done
1395          * @endcode
1396          * @par
1397          * Start the channel monitor.
1398          * OT CLI sends a boolean value of `true` to #otChannelMonitorSetEnabled.
1399          * `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` is required.
1400          * @sa otChannelMonitorSetEnabled
1401          */
1402         else if (aArgs[1] == "start")
1403         {
1404             error = otChannelMonitorSetEnabled(GetInstancePtr(), true);
1405         }
1406         /**
1407          * @cli channel monitor stop
1408          * @code
1409          * channel monitor stop
1410          * channel monitor stop
1411          * Done
1412          * @endcode
1413          * @par
1414          * Stop the channel monitor.
1415          * OT CLI sends a boolean value of `false` to #otChannelMonitorSetEnabled.
1416          * `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` is required.
1417          * @sa otChannelMonitorSetEnabled
1418          */
1419         else if (aArgs[1] == "stop")
1420         {
1421             error = otChannelMonitorSetEnabled(GetInstancePtr(), false);
1422         }
1423         else
1424         {
1425             ExitNow(error = OT_ERROR_INVALID_ARGS);
1426         }
1427     }
1428 #endif // OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
1429 #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
1430     (OPENTHREAD_FTD ||                          \
1431      (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
1432     else if (aArgs[0] == "manager")
1433     {
1434         /**
1435          * @cli channel manager
1436          * @code
1437          * channel manager
1438          * channel: 11
1439          * auto: 1
1440          * delay: 120
1441          * interval: 10800
1442          * supported: { 11-26}
1443          * favored: { 11-26}
1444          * Done
1445          * @endcode
1446          * @par
1447          * Get the channel manager state.
1448          * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
1449          * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE` is required.
1450          * @sa otChannelManagerGetRequestedChannel
1451          */
1452         if (aArgs[1].IsEmpty())
1453         {
1454             OutputLine("channel: %u", otChannelManagerGetRequestedChannel(GetInstancePtr()));
1455 #if OPENTHREAD_FTD
1456             OutputLine("auto: %d", otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr()));
1457 #endif
1458 #if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
1459             OutputLine("autocsl: %u", otChannelManagerGetAutoCslChannelSelectionEnabled(GetInstancePtr()));
1460 #endif
1461 
1462 #if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && \
1463      OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
1464             if (otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr()) ||
1465                 otChannelManagerGetAutoCslChannelSelectionEnabled(GetInstancePtr()))
1466 #elif OPENTHREAD_FTD
1467             if (otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr()))
1468 #elif (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
1469             if (otChannelManagerGetAutoCslChannelSelectionEnabled(GetInstancePtr()))
1470 #endif
1471             {
1472                 Mac::ChannelMask supportedMask(otChannelManagerGetSupportedChannels(GetInstancePtr()));
1473                 Mac::ChannelMask favoredMask(otChannelManagerGetFavoredChannels(GetInstancePtr()));
1474 #if OPENTHREAD_FTD
1475                 OutputLine("delay: %u", otChannelManagerGetDelay(GetInstancePtr()));
1476 #endif
1477                 OutputLine("interval: %lu", ToUlong(otChannelManagerGetAutoChannelSelectionInterval(GetInstancePtr())));
1478                 OutputLine("cca threshold: 0x%04x", otChannelManagerGetCcaFailureRateThreshold(GetInstancePtr()));
1479                 OutputLine("supported: %s", supportedMask.ToString().AsCString());
1480                 OutputLine("favored: %s", favoredMask.ToString().AsCString());
1481             }
1482         }
1483 #if OPENTHREAD_FTD
1484         /**
1485          * @cli channel manager change
1486          * @code
1487          * channel manager change 11
1488          * channel manager change 11
1489          * Done
1490          * @endcode
1491          * @cparam channel manager change @ca{channel-num}
1492          * @par
1493          * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` is required.
1494          * @par api_copy
1495          * #otChannelManagerRequestChannelChange
1496          */
1497         else if (aArgs[1] == "change")
1498         {
1499             error = ProcessSet(aArgs + 2, otChannelManagerRequestChannelChange);
1500         }
1501 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
1502         /**
1503          * @cli channel manager select
1504          * @code
1505          * channel manager select 1
1506          * channel manager select 1
1507          * Done
1508          * @endcode
1509          * @cparam channel manager select @ca{skip-quality-check}
1510          * Use a `1` or `0` for the boolean `skip-quality-check`.
1511          * @par
1512          * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
1513          * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
1514          * are required.
1515          * @par api_copy
1516          * #otChannelManagerRequestChannelSelect
1517          */
1518         else if (aArgs[1] == "select")
1519         {
1520             bool enable;
1521 
1522             SuccessOrExit(error = aArgs[2].ParseAsBool(enable));
1523             error = otChannelManagerRequestChannelSelect(GetInstancePtr(), enable);
1524         }
1525 #endif // OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
1526         /**
1527          * @cli channel manager auto
1528          * @code
1529          * channel manager auto 1
1530          * channel manager auto 1
1531          * Done
1532          * @endcode
1533          * @cparam channel manager auto @ca{enable}
1534          * `1` is a boolean to `enable`.
1535          * @par
1536          * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
1537          * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
1538          * are required.
1539          * @par api_copy
1540          * #otChannelManagerSetAutoChannelSelectionEnabled
1541          */
1542         else if (aArgs[1] == "auto")
1543         {
1544             bool enable;
1545 
1546             SuccessOrExit(error = aArgs[2].ParseAsBool(enable));
1547             otChannelManagerSetAutoChannelSelectionEnabled(GetInstancePtr(), enable);
1548         }
1549 #endif // OPENTHREAD_FTD
1550 #if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
1551         /**
1552          * @cli channel manager autocsl
1553          * @code
1554          * channel manager autocsl 1
1555          * Done
1556          * @endcode
1557          * @cparam channel manager autocsl @ca{enable}
1558          * `1` is a boolean to `enable`.
1559          * @par
1560          * Enables or disables the auto channel selection functionality for a CSL channel.
1561          * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
1562          * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
1563          * are required.
1564          * @sa otChannelManagerSetAutoCslChannelSelectionEnabled
1565          */
1566         else if (aArgs[1] == "autocsl")
1567         {
1568             bool enable;
1569 
1570             SuccessOrExit(error = aArgs[2].ParseAsBool(enable));
1571             otChannelManagerSetAutoCslChannelSelectionEnabled(GetInstancePtr(), enable);
1572         }
1573 #endif // (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
1574 #if OPENTHREAD_FTD
1575         /**
1576          * @cli channel manager delay
1577          * @code
1578          * channel manager delay 120
1579          * channel manager delay 120
1580          * Done
1581          * @endcode
1582          * @cparam channel manager delay @ca{delay-seconds}
1583          * @par
1584          * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
1585          * @par api_copy
1586          * #otChannelManagerSetDelay
1587          */
1588         else if (aArgs[1] == "delay")
1589         {
1590             error = ProcessGetSet(aArgs + 2, otChannelManagerGetDelay, otChannelManagerSetDelay);
1591         }
1592 #endif
1593         /**
1594          * @cli channel manager interval
1595          * @code
1596          * channel manager interval 10800
1597          * channel manager interval 10800
1598          * Done
1599          * @endcode
1600          * @cparam channel manager interval @ca{interval-seconds}
1601          * @par
1602          * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
1603          * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
1604          * are required.
1605          * @par api_copy
1606          * #otChannelManagerSetAutoChannelSelectionInterval
1607          */
1608         else if (aArgs[1] == "interval")
1609         {
1610             error = ProcessSet(aArgs + 2, otChannelManagerSetAutoChannelSelectionInterval);
1611         }
1612         /**
1613          * @cli channel manager supported
1614          * @code
1615          * channel manager supported 0x7fffc00
1616          * channel manager supported 0x7fffc00
1617          * Done
1618          * @endcode
1619          * @cparam channel manager supported @ca{mask}
1620          * @par
1621          * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
1622          * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
1623          * are required.
1624          * @par api_copy
1625          * #otChannelManagerSetSupportedChannels
1626          */
1627         else if (aArgs[1] == "supported")
1628         {
1629             error = ProcessSet(aArgs + 2, otChannelManagerSetSupportedChannels);
1630         }
1631         /**
1632          * @cli channel manager favored
1633          * @code
1634          * channel manager favored 0x7fffc00
1635          * channel manager favored 0x7fffc00
1636          * Done
1637          * @endcode
1638          * @cparam channel manager favored @ca{mask}
1639          * @par
1640          * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
1641          * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
1642          * are required.
1643          * @par api_copy
1644          * #otChannelManagerSetFavoredChannels
1645          */
1646         else if (aArgs[1] == "favored")
1647         {
1648             error = ProcessSet(aArgs + 2, otChannelManagerSetFavoredChannels);
1649         }
1650         /**
1651          * @cli channel manager threshold
1652          * @code
1653          * channel manager threshold 0xffff
1654          * channel manager threshold 0xffff
1655          * Done
1656          * @endcode
1657          * @cparam channel manager threshold @ca{threshold-percent}
1658          * Use a hex value for `threshold-percent`. `0` maps to 0% and `0xffff` maps to 100%.
1659          * @par
1660          * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
1661          * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
1662          * are required.
1663          * @par api_copy
1664          * #otChannelManagerSetCcaFailureRateThreshold
1665          */
1666         else if (aArgs[1] == "threshold")
1667         {
1668             error = ProcessSet(aArgs + 2, otChannelManagerSetCcaFailureRateThreshold);
1669         }
1670         else
1671         {
1672             ExitNow(error = OT_ERROR_INVALID_ARGS);
1673         }
1674     }
1675 #endif // OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
1676     else
1677     {
1678         ExitNow(error = ProcessGetSet(aArgs, otLinkGetChannel, otLinkSetChannel));
1679     }
1680 
1681 exit:
1682     return error;
1683 }
1684 
1685 #if OPENTHREAD_FTD
Process(Arg aArgs[])1686 template <> otError Interpreter::Process<Cmd("child")>(Arg aArgs[])
1687 {
1688     otError          error = OT_ERROR_NONE;
1689     otChildInfo      childInfo;
1690     uint16_t         childId;
1691     bool             isTable;
1692     otLinkModeConfig linkMode;
1693     char             linkModeString[kLinkModeStringSize];
1694 
1695     isTable = (aArgs[0] == "table");
1696 
1697     if (isTable || (aArgs[0] == "list"))
1698     {
1699         uint16_t maxChildren;
1700 
1701         /**
1702          * @cli child table
1703          * @code
1704          * child table
1705          * | ID  | RLOC16 | Timeout    | Age        | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt| Extended MAC     |
1706          * +-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+------------------+
1707          * |   1 | 0xc801 |        240 |         24 |     3 |  131 |1|0|0|  3| 0 |     0 | 4ecede68435358ac |
1708          * |   2 | 0xc802 |        240 |          2 |     3 |  131 |0|0|0|  3| 1 |     0 | a672a601d2ce37d8 |
1709          * Done
1710          * @endcode
1711          * @par
1712          * Prints a table of the attached children.
1713          * @sa otThreadGetChildInfoByIndex
1714          */
1715         if (isTable)
1716         {
1717             static const char *const kChildTableTitles[] = {
1718                 "ID", "RLOC16", "Timeout", "Age", "LQ In",   "C_VN",    "R",
1719                 "D",  "N",      "Ver",     "CSL", "QMsgCnt", "Suprvsn", "Extended MAC",
1720             };
1721 
1722             static const uint8_t kChildTableColumnWidths[] = {
1723                 5, 8, 12, 12, 7, 6, 1, 1, 1, 3, 3, 7, 7, 18,
1724             };
1725 
1726             OutputTableHeader(kChildTableTitles, kChildTableColumnWidths);
1727         }
1728 
1729         maxChildren = otThreadGetMaxAllowedChildren(GetInstancePtr());
1730 
1731         for (uint16_t i = 0; i < maxChildren; i++)
1732         {
1733             if ((otThreadGetChildInfoByIndex(GetInstancePtr(), i, &childInfo) != OT_ERROR_NONE) ||
1734                 childInfo.mIsStateRestoring)
1735             {
1736                 continue;
1737             }
1738 
1739             if (isTable)
1740             {
1741                 OutputFormat("| %3u ", childInfo.mChildId);
1742                 OutputFormat("| 0x%04x ", childInfo.mRloc16);
1743                 OutputFormat("| %10lu ", ToUlong(childInfo.mTimeout));
1744                 OutputFormat("| %10lu ", ToUlong(childInfo.mAge));
1745                 OutputFormat("| %5u ", childInfo.mLinkQualityIn);
1746                 OutputFormat("| %4u ", childInfo.mNetworkDataVersion);
1747                 OutputFormat("|%1d", childInfo.mRxOnWhenIdle);
1748                 OutputFormat("|%1d", childInfo.mFullThreadDevice);
1749                 OutputFormat("|%1d", childInfo.mFullNetworkData);
1750                 OutputFormat("|%3u", childInfo.mVersion);
1751                 OutputFormat("| %1d ", childInfo.mIsCslSynced);
1752                 OutputFormat("| %5u ", childInfo.mQueuedMessageCnt);
1753                 OutputFormat("| %5u ", childInfo.mSupervisionInterval);
1754                 OutputFormat("| ");
1755                 OutputExtAddress(childInfo.mExtAddress);
1756                 OutputLine(" |");
1757             }
1758             /**
1759              * @cli child list
1760              * @code
1761              * child list
1762              * 1 2 3 6 7 8
1763              * Done
1764              * @endcode
1765              * @par
1766              * Returns a list of attached Child IDs.
1767              * @sa otThreadGetChildInfoByIndex
1768              */
1769             else
1770             {
1771                 OutputFormat("%u ", childInfo.mChildId);
1772             }
1773         }
1774 
1775         OutputNewLine();
1776         ExitNow();
1777     }
1778 
1779     SuccessOrExit(error = aArgs[0].ParseAsUint16(childId));
1780     SuccessOrExit(error = otThreadGetChildInfoById(GetInstancePtr(), childId, &childInfo));
1781 
1782     /**
1783      * @cli child (id)
1784      * @code
1785      * child 1
1786      * Child ID: 1
1787      * Rloc: 9c01
1788      * Ext Addr: e2b3540590b0fd87
1789      * Mode: rn
1790      * CSL Synchronized: 1
1791      * Net Data: 184
1792      * Timeout: 100
1793      * Age: 0
1794      * Link Quality In: 3
1795      * RSSI: -20
1796      * Done
1797      * @endcode
1798      * @cparam child @ca{child-id}
1799      * @par api_copy
1800      * #otThreadGetChildInfoById
1801      */
1802     OutputLine("Child ID: %u", childInfo.mChildId);
1803     OutputLine("Rloc: %04x", childInfo.mRloc16);
1804     OutputFormat("Ext Addr: ");
1805     OutputExtAddressLine(childInfo.mExtAddress);
1806     linkMode.mRxOnWhenIdle = childInfo.mRxOnWhenIdle;
1807     linkMode.mDeviceType   = childInfo.mFullThreadDevice;
1808     linkMode.mNetworkData  = childInfo.mFullThreadDevice;
1809     OutputLine("Mode: %s", LinkModeToString(linkMode, linkModeString));
1810     OutputLine("CSL Synchronized: %d ", childInfo.mIsCslSynced);
1811     OutputLine("Net Data: %u", childInfo.mNetworkDataVersion);
1812     OutputLine("Timeout: %lu", ToUlong(childInfo.mTimeout));
1813     OutputLine("Age: %lu", ToUlong(childInfo.mAge));
1814     OutputLine("Link Quality In: %u", childInfo.mLinkQualityIn);
1815     OutputLine("RSSI: %d", childInfo.mAverageRssi);
1816     OutputLine("Supervision Interval: %d", childInfo.mSupervisionInterval);
1817 
1818 exit:
1819     return error;
1820 }
1821 
Process(Arg aArgs[])1822 template <> otError Interpreter::Process<Cmd("childip")>(Arg aArgs[])
1823 {
1824     otError error = OT_ERROR_NONE;
1825 
1826     /**
1827      * @cli childip
1828      * @code
1829      * childip
1830      * 3401: fdde:ad00:beef:0:3037:3e03:8c5f:bc0c
1831      * Done
1832      * @endcode
1833      * @par
1834      * Gets a list of IP addresses stored for MTD children.
1835      * @sa otThreadGetChildNextIp6Address
1836      */
1837     if (aArgs[0].IsEmpty())
1838     {
1839         uint16_t maxChildren = otThreadGetMaxAllowedChildren(GetInstancePtr());
1840 
1841         for (uint16_t childIndex = 0; childIndex < maxChildren; childIndex++)
1842         {
1843             otChildIp6AddressIterator iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT;
1844             otIp6Address              ip6Address;
1845             otChildInfo               childInfo;
1846 
1847             if ((otThreadGetChildInfoByIndex(GetInstancePtr(), childIndex, &childInfo) != OT_ERROR_NONE) ||
1848                 childInfo.mIsStateRestoring)
1849             {
1850                 continue;
1851             }
1852 
1853             iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT;
1854 
1855             while (otThreadGetChildNextIp6Address(GetInstancePtr(), childIndex, &iterator, &ip6Address) ==
1856                    OT_ERROR_NONE)
1857             {
1858                 OutputFormat("%04x: ", childInfo.mRloc16);
1859                 OutputIp6AddressLine(ip6Address);
1860             }
1861         }
1862     }
1863     /**
1864      * @cli childip max
1865      * @code
1866      * childip max
1867      * 4
1868      * Done
1869      * @endcode
1870      * @par api_copy
1871      * #otThreadGetMaxChildIpAddresses
1872      */
1873     else if (aArgs[0] == "max")
1874     {
1875 #if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
1876         error = ProcessGet(aArgs + 1, otThreadGetMaxChildIpAddresses);
1877 #else
1878         /**
1879          * @cli childip max (set)
1880          * @code
1881          * childip max 2
1882          * Done
1883          * @endcode
1884          * @cparam childip max @ca{count}
1885          * @par api_copy
1886          * #otThreadSetMaxChildIpAddresses
1887          */
1888         error = ProcessGetSet(aArgs + 1, otThreadGetMaxChildIpAddresses, otThreadSetMaxChildIpAddresses);
1889 #endif
1890     }
1891     else
1892     {
1893         error = OT_ERROR_INVALID_COMMAND;
1894     }
1895 
1896     return error;
1897 }
1898 
1899 /**
1900  * @cli childmax
1901  * @code
1902  * childmax
1903  * 5
1904  * Done
1905  * @endcode
1906  * @par api_copy
1907  * #otThreadGetMaxAllowedChildren
1908  */
Process(Arg aArgs[])1909 template <> otError Interpreter::Process<Cmd("childmax")>(Arg aArgs[])
1910 {
1911     /**
1912      * @cli childmax (set)
1913      * @code
1914      * childmax 2
1915      * Done
1916      * @endcode
1917      * @cparam childmax @ca{count}
1918      * @par api_copy
1919      * #otThreadSetMaxAllowedChildren
1920      */
1921     return ProcessGetSet(aArgs, otThreadGetMaxAllowedChildren, otThreadSetMaxAllowedChildren);
1922 }
1923 #endif // OPENTHREAD_FTD
1924 
Process(Arg aArgs[])1925 template <> otError Interpreter::Process<Cmd("childsupervision")>(Arg aArgs[])
1926 {
1927     otError error = OT_ERROR_INVALID_ARGS;
1928 
1929     /**
1930      * @cli childsupervision checktimeout
1931      * @code
1932      * childsupervision checktimeout
1933      * 30
1934      * Done
1935      * @endcode
1936      * @par api_copy
1937      * #otChildSupervisionGetCheckTimeout
1938      */
1939     if (aArgs[0] == "checktimeout")
1940     {
1941         /** @cli childsupervision checktimeout (set)
1942          * @code
1943          * childsupervision checktimeout 30
1944          * Done
1945          * @endcode
1946          * @cparam childsupervision checktimeout @ca{timeout-seconds}
1947          * @par api_copy
1948          * #otChildSupervisionSetCheckTimeout
1949          */
1950         error = ProcessGetSet(aArgs + 1, otChildSupervisionGetCheckTimeout, otChildSupervisionSetCheckTimeout);
1951     }
1952     /**
1953      * @cli childsupervision interval
1954      * @code
1955      * childsupervision interval
1956      * 30
1957      * Done
1958      * @endcode
1959      * @par api_copy
1960      * #otChildSupervisionGetInterval
1961      */
1962     else if (aArgs[0] == "interval")
1963     {
1964         /**
1965          * @cli childsupervision interval (set)
1966          * @code
1967          * childsupervision interval 30
1968          * Done
1969          * @endcode
1970          * @cparam childsupervision interval @ca{interval-seconds}
1971          * @par api_copy
1972          * #otChildSupervisionSetInterval
1973          */
1974         error = ProcessGetSet(aArgs + 1, otChildSupervisionGetInterval, otChildSupervisionSetInterval);
1975     }
1976     else if (aArgs[0] == "failcounter")
1977     {
1978         if (aArgs[1].IsEmpty())
1979         {
1980             OutputLine("%u", otChildSupervisionGetCheckFailureCounter(GetInstancePtr()));
1981             error = OT_ERROR_NONE;
1982         }
1983         else if (aArgs[1] == "reset")
1984         {
1985             otChildSupervisionResetCheckFailureCounter(GetInstancePtr());
1986             error = OT_ERROR_NONE;
1987         }
1988     }
1989 
1990     return error;
1991 }
1992 
1993 /** @cli childtimeout
1994  * @code
1995  * childtimeout
1996  * 300
1997  * Done
1998  * @endcode
1999  * @par api_copy
2000  * #otThreadGetChildTimeout
2001  */
Process(Arg aArgs[])2002 template <> otError Interpreter::Process<Cmd("childtimeout")>(Arg aArgs[])
2003 {
2004     /** @cli childtimeout (set)
2005      * @code
2006      * childtimeout 300
2007      * Done
2008      * @endcode
2009      * @cparam childtimeout @ca{timeout-seconds}
2010      * @par api_copy
2011      * #otThreadSetChildTimeout
2012      */
2013     return ProcessGetSet(aArgs, otThreadGetChildTimeout, otThreadSetChildTimeout);
2014 }
2015 
2016 #if OPENTHREAD_CONFIG_COAP_API_ENABLE
Process(Arg aArgs[])2017 template <> otError Interpreter::Process<Cmd("coap")>(Arg aArgs[]) { return mCoap.Process(aArgs); }
2018 #endif
2019 
2020 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
Process(Arg aArgs[])2021 template <> otError Interpreter::Process<Cmd("coaps")>(Arg aArgs[]) { return mCoapSecure.Process(aArgs); }
2022 #endif
2023 
2024 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
Process(Arg aArgs[])2025 template <> otError Interpreter::Process<Cmd("coex")>(Arg aArgs[])
2026 {
2027     otError error = OT_ERROR_NONE;
2028 
2029     if (ProcessEnableDisable(aArgs, otPlatRadioIsCoexEnabled, otPlatRadioSetCoexEnabled) == OT_ERROR_NONE)
2030     {
2031     }
2032     else if (aArgs[0] == "metrics")
2033     {
2034         struct RadioCoexMetricName
2035         {
2036             const uint32_t otRadioCoexMetrics::*mValuePtr;
2037             const char                         *mName;
2038         };
2039 
2040         static const RadioCoexMetricName kTxMetricNames[] = {
2041             {&otRadioCoexMetrics::mNumTxRequest, "Request"},
2042             {&otRadioCoexMetrics::mNumTxGrantImmediate, "Grant Immediate"},
2043             {&otRadioCoexMetrics::mNumTxGrantWait, "Grant Wait"},
2044             {&otRadioCoexMetrics::mNumTxGrantWaitActivated, "Grant Wait Activated"},
2045             {&otRadioCoexMetrics::mNumTxGrantWaitTimeout, "Grant Wait Timeout"},
2046             {&otRadioCoexMetrics::mNumTxGrantDeactivatedDuringRequest, "Grant Deactivated During Request"},
2047             {&otRadioCoexMetrics::mNumTxDelayedGrant, "Delayed Grant"},
2048             {&otRadioCoexMetrics::mAvgTxRequestToGrantTime, "Average Request To Grant Time"},
2049         };
2050 
2051         static const RadioCoexMetricName kRxMetricNames[] = {
2052             {&otRadioCoexMetrics::mNumRxRequest, "Request"},
2053             {&otRadioCoexMetrics::mNumRxGrantImmediate, "Grant Immediate"},
2054             {&otRadioCoexMetrics::mNumRxGrantWait, "Grant Wait"},
2055             {&otRadioCoexMetrics::mNumRxGrantWaitActivated, "Grant Wait Activated"},
2056             {&otRadioCoexMetrics::mNumRxGrantWaitTimeout, "Grant Wait Timeout"},
2057             {&otRadioCoexMetrics::mNumRxGrantDeactivatedDuringRequest, "Grant Deactivated During Request"},
2058             {&otRadioCoexMetrics::mNumRxDelayedGrant, "Delayed Grant"},
2059             {&otRadioCoexMetrics::mAvgRxRequestToGrantTime, "Average Request To Grant Time"},
2060             {&otRadioCoexMetrics::mNumRxGrantNone, "Grant None"},
2061         };
2062 
2063         otRadioCoexMetrics metrics;
2064 
2065         SuccessOrExit(error = otPlatRadioGetCoexMetrics(GetInstancePtr(), &metrics));
2066 
2067         OutputLine("Stopped: %s", metrics.mStopped ? "true" : "false");
2068         OutputLine("Grant Glitch: %lu", ToUlong(metrics.mNumGrantGlitch));
2069         OutputLine("Transmit metrics");
2070 
2071         for (const RadioCoexMetricName &metric : kTxMetricNames)
2072         {
2073             OutputLine(kIndentSize, "%s: %lu", metric.mName, ToUlong(metrics.*metric.mValuePtr));
2074         }
2075 
2076         OutputLine("Receive metrics");
2077 
2078         for (const RadioCoexMetricName &metric : kRxMetricNames)
2079         {
2080             OutputLine(kIndentSize, "%s: %lu", metric.mName, ToUlong(metrics.*metric.mValuePtr));
2081         }
2082     }
2083     else
2084     {
2085         error = OT_ERROR_INVALID_ARGS;
2086     }
2087 
2088 exit:
2089     return error;
2090 }
2091 #endif // OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
2092 
2093 #if OPENTHREAD_FTD
2094 /**
2095  * @cli contextreusedelay (get,set)
2096  * @code
2097  * contextreusedelay
2098  * 11
2099  * Done
2100  * @endcode
2101  * @code
2102  * contextreusedelay 11
2103  * Done
2104  * @endcode
2105  * @cparam contextreusedelay @ca{delay}
2106  * Use the optional `delay` argument to set the `CONTEXT_ID_REUSE_DELAY`.
2107  * @par
2108  * Gets or sets the `CONTEXT_ID_REUSE_DELAY` value.
2109  * @sa otThreadGetContextIdReuseDelay
2110  * @sa otThreadSetContextIdReuseDelay
2111  */
Process(Arg aArgs[])2112 template <> otError Interpreter::Process<Cmd("contextreusedelay")>(Arg aArgs[])
2113 {
2114     return ProcessGetSet(aArgs, otThreadGetContextIdReuseDelay, otThreadSetContextIdReuseDelay);
2115 }
2116 #endif
2117 
2118 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
OutputBorderRouterCounters(void)2119 void Interpreter::OutputBorderRouterCounters(void)
2120 {
2121     struct BrCounterName
2122     {
2123         const otPacketsAndBytes otBorderRoutingCounters::*mPacketsAndBytes;
2124         const char                                       *mName;
2125     };
2126 
2127     static const BrCounterName kCounterNames[] = {
2128         {&otBorderRoutingCounters::mInboundUnicast, "Inbound Unicast"},
2129         {&otBorderRoutingCounters::mInboundMulticast, "Inbound Multicast"},
2130         {&otBorderRoutingCounters::mOutboundUnicast, "Outbound Unicast"},
2131         {&otBorderRoutingCounters::mOutboundMulticast, "Outbound Multicast"},
2132     };
2133 
2134     const otBorderRoutingCounters *brCounters = otIp6GetBorderRoutingCounters(GetInstancePtr());
2135     Uint64StringBuffer             uint64StringBuffer;
2136 
2137     for (const BrCounterName &counter : kCounterNames)
2138     {
2139         OutputFormat("%s:", counter.mName);
2140         OutputFormat(" Packets %s",
2141                      Uint64ToString((brCounters->*counter.mPacketsAndBytes).mPackets, uint64StringBuffer));
2142         OutputLine(" Bytes %s", Uint64ToString((brCounters->*counter.mPacketsAndBytes).mBytes, uint64StringBuffer));
2143     }
2144 
2145     OutputLine("RA Rx: %lu", ToUlong(brCounters->mRaRx));
2146     OutputLine("RA TxSuccess: %lu", ToUlong(brCounters->mRaTxSuccess));
2147     OutputLine("RA TxFailed: %lu", ToUlong(brCounters->mRaTxFailure));
2148     OutputLine("RS Rx: %lu", ToUlong(brCounters->mRsRx));
2149     OutputLine("RS TxSuccess: %lu", ToUlong(brCounters->mRsTxSuccess));
2150     OutputLine("RS TxFailed: %lu", ToUlong(brCounters->mRsTxFailure));
2151 }
2152 #endif // OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
2153 
Process(Arg aArgs[])2154 template <> otError Interpreter::Process<Cmd("counters")>(Arg aArgs[])
2155 {
2156     otError error = OT_ERROR_NONE;
2157 
2158     /**
2159      * @cli counters
2160      * @code
2161      * counters
2162      * ip
2163      * mac
2164      * mle
2165      * Done
2166      * @endcode
2167      * @par
2168      * Gets the supported counter names.
2169      */
2170     if (aArgs[0].IsEmpty())
2171     {
2172 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
2173         OutputLine("br");
2174 #endif
2175         OutputLine("ip");
2176         OutputLine("mac");
2177         OutputLine("mle");
2178     }
2179 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
2180     /**
2181      * @cli counters br
2182      * @code
2183      * counters br
2184      * Inbound Unicast: Packets 4 Bytes 320
2185      * Inbound Multicast: Packets 0 Bytes 0
2186      * Outbound Unicast: Packets 2 Bytes 160
2187      * Outbound Multicast: Packets 0 Bytes 0
2188      * RA Rx: 4
2189      * RA TxSuccess: 2
2190      * RA TxFailed: 0
2191      * RS Rx: 0
2192      * RS TxSuccess: 2
2193      * RS TxFailed: 0
2194      * Done
2195      * @endcode
2196      * @par api_copy
2197      * #otIp6GetBorderRoutingCounters
2198      */
2199     else if (aArgs[0] == "br")
2200     {
2201         if (aArgs[1].IsEmpty())
2202         {
2203             OutputBorderRouterCounters();
2204         }
2205         /**
2206          * @cli counters br reset
2207          * @code
2208          * counters br reset
2209          * Done
2210          * @endcode
2211          * @par api_copy
2212          * #otIp6ResetBorderRoutingCounters
2213          */
2214         else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty())
2215         {
2216             otIp6ResetBorderRoutingCounters(GetInstancePtr());
2217         }
2218         else
2219         {
2220             error = OT_ERROR_INVALID_ARGS;
2221         }
2222     }
2223 #endif
2224     /**
2225      * @cli counters (mac)
2226      * @code
2227      * counters mac
2228      * TxTotal: 10
2229      *    TxUnicast: 3
2230      *    TxBroadcast: 7
2231      *    TxAckRequested: 3
2232      *    TxAcked: 3
2233      *    TxNoAckRequested: 7
2234      *    TxData: 10
2235      *    TxDataPoll: 0
2236      *    TxBeacon: 0
2237      *    TxBeaconRequest: 0
2238      *    TxOther: 0
2239      *    TxRetry: 0
2240      *    TxErrCca: 0
2241      *    TxErrBusyChannel: 0
2242      * RxTotal: 2
2243      *    RxUnicast: 1
2244      *    RxBroadcast: 1
2245      *    RxData: 2
2246      *    RxDataPoll: 0
2247      *    RxBeacon: 0
2248      *    RxBeaconRequest: 0
2249      *    RxOther: 0
2250      *    RxAddressFiltered: 0
2251      *    RxDestAddrFiltered: 0
2252      *    RxDuplicated: 0
2253      *    RxErrNoFrame: 0
2254      *    RxErrNoUnknownNeighbor: 0
2255      *    RxErrInvalidSrcAddr: 0
2256      *    RxErrSec: 0
2257      *    RxErrFcs: 0
2258      *    RxErrOther: 0
2259      * Done
2260      * @endcode
2261      * @cparam counters @ca{mac}
2262      * @par api_copy
2263      * #otLinkGetCounters
2264      */
2265     else if (aArgs[0] == "mac")
2266     {
2267         if (aArgs[1].IsEmpty())
2268         {
2269             struct MacCounterName
2270             {
2271                 const uint32_t otMacCounters::*mValuePtr;
2272                 const char                    *mName;
2273             };
2274 
2275             static const MacCounterName kTxCounterNames[] = {
2276                 {&otMacCounters::mTxUnicast, "TxUnicast"},
2277                 {&otMacCounters::mTxBroadcast, "TxBroadcast"},
2278                 {&otMacCounters::mTxAckRequested, "TxAckRequested"},
2279                 {&otMacCounters::mTxAcked, "TxAcked"},
2280                 {&otMacCounters::mTxNoAckRequested, "TxNoAckRequested"},
2281                 {&otMacCounters::mTxData, "TxData"},
2282                 {&otMacCounters::mTxDataPoll, "TxDataPoll"},
2283                 {&otMacCounters::mTxBeacon, "TxBeacon"},
2284                 {&otMacCounters::mTxBeaconRequest, "TxBeaconRequest"},
2285                 {&otMacCounters::mTxOther, "TxOther"},
2286                 {&otMacCounters::mTxRetry, "TxRetry"},
2287                 {&otMacCounters::mTxErrCca, "TxErrCca"},
2288                 {&otMacCounters::mTxErrBusyChannel, "TxErrBusyChannel"},
2289                 {&otMacCounters::mTxErrAbort, "TxErrAbort"},
2290                 {&otMacCounters::mTxDirectMaxRetryExpiry, "TxDirectMaxRetryExpiry"},
2291                 {&otMacCounters::mTxIndirectMaxRetryExpiry, "TxIndirectMaxRetryExpiry"},
2292             };
2293 
2294             static const MacCounterName kRxCounterNames[] = {
2295                 {&otMacCounters::mRxUnicast, "RxUnicast"},
2296                 {&otMacCounters::mRxBroadcast, "RxBroadcast"},
2297                 {&otMacCounters::mRxData, "RxData"},
2298                 {&otMacCounters::mRxDataPoll, "RxDataPoll"},
2299                 {&otMacCounters::mRxBeacon, "RxBeacon"},
2300                 {&otMacCounters::mRxBeaconRequest, "RxBeaconRequest"},
2301                 {&otMacCounters::mRxOther, "RxOther"},
2302                 {&otMacCounters::mRxAddressFiltered, "RxAddressFiltered"},
2303                 {&otMacCounters::mRxDestAddrFiltered, "RxDestAddrFiltered"},
2304                 {&otMacCounters::mRxDuplicated, "RxDuplicated"},
2305                 {&otMacCounters::mRxErrNoFrame, "RxErrNoFrame"},
2306                 {&otMacCounters::mRxErrUnknownNeighbor, "RxErrNoUnknownNeighbor"},
2307                 {&otMacCounters::mRxErrInvalidSrcAddr, "RxErrInvalidSrcAddr"},
2308                 {&otMacCounters::mRxErrSec, "RxErrSec"},
2309                 {&otMacCounters::mRxErrFcs, "RxErrFcs"},
2310                 {&otMacCounters::mRxErrOther, "RxErrOther"},
2311             };
2312 
2313             const otMacCounters *macCounters = otLinkGetCounters(GetInstancePtr());
2314 
2315             OutputLine("TxTotal: %lu", ToUlong(macCounters->mTxTotal));
2316 
2317             for (const MacCounterName &counter : kTxCounterNames)
2318             {
2319                 OutputLine(kIndentSize, "%s: %lu", counter.mName, ToUlong(macCounters->*counter.mValuePtr));
2320             }
2321 
2322             OutputLine("RxTotal: %lu", ToUlong(macCounters->mRxTotal));
2323 
2324             for (const MacCounterName &counter : kRxCounterNames)
2325             {
2326                 OutputLine(kIndentSize, "%s: %lu", counter.mName, ToUlong(macCounters->*counter.mValuePtr));
2327             }
2328         }
2329         /**
2330          * @cli counters mac reset
2331          * @code
2332          * counters mac reset
2333          * Done
2334          * @endcode
2335          * @cparam counters @ca{mac} reset
2336          * @par api_copy
2337          * #otLinkResetCounters
2338          */
2339         else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty())
2340         {
2341             otLinkResetCounters(GetInstancePtr());
2342         }
2343         else
2344         {
2345             error = OT_ERROR_INVALID_ARGS;
2346         }
2347     }
2348     /**
2349      * @cli counters (mle)
2350      * @code
2351      * counters mle
2352      * Role Disabled: 0
2353      * Role Detached: 1
2354      * Role Child: 0
2355      * Role Router: 0
2356      * Role Leader: 1
2357      * Attach Attempts: 1
2358      * Partition Id Changes: 1
2359      * Better Partition Attach Attempts: 0
2360      * Parent Changes: 0
2361      * Done
2362      * @endcode
2363      * @cparam counters @ca{mle}
2364      * @par api_copy
2365      * #otThreadGetMleCounters
2366      */
2367     else if (aArgs[0] == "mle")
2368     {
2369         if (aArgs[1].IsEmpty())
2370         {
2371             struct MleCounterName
2372             {
2373                 const uint16_t otMleCounters::*mValuePtr;
2374                 const char                    *mName;
2375             };
2376 
2377             static const MleCounterName kCounterNames[] = {
2378                 {&otMleCounters::mDisabledRole, "Role Disabled"},
2379                 {&otMleCounters::mDetachedRole, "Role Detached"},
2380                 {&otMleCounters::mChildRole, "Role Child"},
2381                 {&otMleCounters::mRouterRole, "Role Router"},
2382                 {&otMleCounters::mLeaderRole, "Role Leader"},
2383                 {&otMleCounters::mAttachAttempts, "Attach Attempts"},
2384                 {&otMleCounters::mPartitionIdChanges, "Partition Id Changes"},
2385                 {&otMleCounters::mBetterPartitionAttachAttempts, "Better Partition Attach Attempts"},
2386                 {&otMleCounters::mParentChanges, "Parent Changes"},
2387             };
2388 
2389             const otMleCounters *mleCounters = otThreadGetMleCounters(GetInstancePtr());
2390 
2391             for (const MleCounterName &counter : kCounterNames)
2392             {
2393                 OutputLine("%s: %u", counter.mName, mleCounters->*counter.mValuePtr);
2394             }
2395 #if OPENTHREAD_CONFIG_UPTIME_ENABLE
2396             {
2397                 struct MleTimeCounterName
2398                 {
2399                     const uint64_t otMleCounters::*mValuePtr;
2400                     const char                    *mName;
2401                 };
2402 
2403                 static const MleTimeCounterName kTimeCounterNames[] = {
2404                     {&otMleCounters::mDisabledTime, "Disabled"}, {&otMleCounters::mDetachedTime, "Detached"},
2405                     {&otMleCounters::mChildTime, "Child"},       {&otMleCounters::mRouterTime, "Router"},
2406                     {&otMleCounters::mLeaderTime, "Leader"},
2407                 };
2408 
2409                 for (const MleTimeCounterName &counter : kTimeCounterNames)
2410                 {
2411                     OutputFormat("Time %s Milli: ", counter.mName);
2412                     OutputUint64Line(mleCounters->*counter.mValuePtr);
2413                 }
2414 
2415                 OutputFormat("Time Tracked Milli: ");
2416                 OutputUint64Line(mleCounters->mTrackedTime);
2417             }
2418 #endif
2419         }
2420         /**
2421          * @cli counters mle reset
2422          * @code
2423          * counters mle reset
2424          * Done
2425          * @endcode
2426          * @cparam counters @ca{mle} reset
2427          * @par api_copy
2428          * #otThreadResetMleCounters
2429          */
2430         else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty())
2431         {
2432             otThreadResetMleCounters(GetInstancePtr());
2433         }
2434         else
2435         {
2436             error = OT_ERROR_INVALID_ARGS;
2437         }
2438     }
2439     /**
2440      * @cli counters ip
2441      * @code
2442      * counters ip
2443      * TxSuccess: 10
2444      * TxFailed: 0
2445      * RxSuccess: 5
2446      * RxFailed: 0
2447      * Done
2448      * @endcode
2449      * @cparam counters @ca{ip}
2450      * @par api_copy
2451      * #otThreadGetIp6Counters
2452      */
2453     else if (aArgs[0] == "ip")
2454     {
2455         if (aArgs[1].IsEmpty())
2456         {
2457             struct IpCounterName
2458             {
2459                 const uint32_t otIpCounters::*mValuePtr;
2460                 const char                   *mName;
2461             };
2462 
2463             static const IpCounterName kCounterNames[] = {
2464                 {&otIpCounters::mTxSuccess, "TxSuccess"},
2465                 {&otIpCounters::mTxFailure, "TxFailed"},
2466                 {&otIpCounters::mRxSuccess, "RxSuccess"},
2467                 {&otIpCounters::mRxFailure, "RxFailed"},
2468             };
2469 
2470             const otIpCounters *ipCounters = otThreadGetIp6Counters(GetInstancePtr());
2471 
2472             for (const IpCounterName &counter : kCounterNames)
2473             {
2474                 OutputLine("%s: %lu", counter.mName, ToUlong(ipCounters->*counter.mValuePtr));
2475             }
2476         }
2477         /**
2478          * @cli counters ip reset
2479          * @code
2480          * counters ip reset
2481          * Done
2482          * @endcode
2483          * @cparam counters @ca{ip} reset
2484          * @par api_copy
2485          * #otThreadResetIp6Counters
2486          */
2487         else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty())
2488         {
2489             otThreadResetIp6Counters(GetInstancePtr());
2490         }
2491         else
2492         {
2493             error = OT_ERROR_INVALID_ARGS;
2494         }
2495     }
2496     else
2497     {
2498         error = OT_ERROR_INVALID_ARGS;
2499     }
2500 
2501     return error;
2502 }
2503 
2504 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
Process(Arg aArgs[])2505 template <> otError Interpreter::Process<Cmd("csl")>(Arg aArgs[])
2506 {
2507     otError error = OT_ERROR_NONE;
2508 
2509     /**
2510      * @cli csl
2511      * @code
2512      * csl
2513      * Channel: 11
2514      * Period: 160000us
2515      * Timeout: 1000s
2516      * Done
2517      * @endcode
2518      * @par
2519      * Gets the CSL configuration.
2520      * @sa otLinkGetCslChannel
2521      * @sa otLinkGetCslPeriod
2522      * @sa otLinkGetCslPeriod
2523      * @sa otLinkGetCslTimeout
2524      */
2525     if (aArgs[0].IsEmpty())
2526     {
2527         OutputLine("channel: %u", otLinkGetCslChannel(GetInstancePtr()));
2528         OutputLine("period: %luus", ToUlong(otLinkGetCslPeriod(GetInstancePtr())));
2529         OutputLine("timeout: %lus", ToUlong(otLinkGetCslTimeout(GetInstancePtr())));
2530     }
2531     /**
2532      * @cli csl channel
2533      * @code
2534      * csl channel 20
2535      * Done
2536      * @endcode
2537      * @cparam csl channel @ca{channel}
2538      * @par api_copy
2539      * #otLinkSetCslChannel
2540      */
2541     else if (aArgs[0] == "channel")
2542     {
2543         error = ProcessSet(aArgs + 1, otLinkSetCslChannel);
2544     }
2545     /**
2546      * @cli csl period
2547      * @code
2548      * csl period 3000000
2549      * Done
2550      * @endcode
2551      * @cparam csl period @ca{period}
2552      * @par api_copy
2553      * #otLinkSetCslPeriod
2554      */
2555     else if (aArgs[0] == "period")
2556     {
2557         error = ProcessSet(aArgs + 1, otLinkSetCslPeriod);
2558     }
2559     /**
2560      * @cli csl timeout
2561      * @code
2562      * cls timeout 10
2563      * Done
2564      * @endcode
2565      * @cparam csl timeout @ca{timeout}
2566      * @par api_copy
2567      * #otLinkSetCslTimeout
2568      */
2569     else if (aArgs[0] == "timeout")
2570     {
2571         error = ProcessSet(aArgs + 1, otLinkSetCslTimeout);
2572     }
2573     else
2574     {
2575         error = OT_ERROR_INVALID_ARGS;
2576     }
2577 
2578     return error;
2579 }
2580 #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
2581 
2582 #if OPENTHREAD_FTD
Process(Arg aArgs[])2583 template <> otError Interpreter::Process<Cmd("delaytimermin")>(Arg aArgs[])
2584 {
2585     otError error = OT_ERROR_NONE;
2586 
2587     /**
2588      * @cli delaytimermin
2589      * @code
2590      * delaytimermin
2591      * 30
2592      * Done
2593      * @endcode
2594      * @par
2595      * Get the minimal delay timer (in seconds).
2596      * @sa otDatasetGetDelayTimerMinimal
2597      */
2598     if (aArgs[0].IsEmpty())
2599     {
2600         OutputLine("%lu", ToUlong((otDatasetGetDelayTimerMinimal(GetInstancePtr()) / 1000)));
2601     }
2602     /**
2603      * @cli delaytimermin (set)
2604      * @code
2605      * delaytimermin 60
2606      * Done
2607      * @endcode
2608      * @cparam delaytimermin @ca{delaytimermin}
2609      * @par
2610      * Sets the minimal delay timer (in seconds).
2611      * @sa otDatasetSetDelayTimerMinimal
2612      */
2613     else if (aArgs[1].IsEmpty())
2614     {
2615         uint32_t delay;
2616         SuccessOrExit(error = aArgs[0].ParseAsUint32(delay));
2617         SuccessOrExit(error = otDatasetSetDelayTimerMinimal(GetInstancePtr(), static_cast<uint32_t>(delay * 1000)));
2618     }
2619     else
2620     {
2621         error = OT_ERROR_INVALID_ARGS;
2622     }
2623 
2624 exit:
2625     return error;
2626 }
2627 #endif
2628 
2629 /**
2630  * @cli detach
2631  * @code
2632  * detach
2633  * Finished detaching
2634  * Done
2635  * @endcode
2636  * @par
2637  * Start the graceful detach process by first notifying other nodes (sending Address Release if acting as a router, or
2638  * setting Child Timeout value to zero on parent if acting as a child) and then stopping Thread protocol operation.
2639  * @sa otThreadDetachGracefully
2640  */
Process(Arg aArgs[])2641 template <> otError Interpreter::Process<Cmd("detach")>(Arg aArgs[])
2642 {
2643     otError error = OT_ERROR_NONE;
2644 
2645     /**
2646      * @cli detach async
2647      * @code
2648      * detach async
2649      * Done
2650      * @endcode
2651      * @par
2652      * Start the graceful detach process similar to the `detach` command without blocking and waiting for the callback
2653      * indicating that detach is finished.
2654      * @csa{detach}
2655      * @sa otThreadDetachGracefully
2656      */
2657     if (aArgs[0] == "async")
2658     {
2659         SuccessOrExit(error = otThreadDetachGracefully(GetInstancePtr(), nullptr, nullptr));
2660     }
2661     else
2662     {
2663         SuccessOrExit(error = otThreadDetachGracefully(GetInstancePtr(), HandleDetachGracefullyResult, this));
2664         error = OT_ERROR_PENDING;
2665     }
2666 
2667 exit:
2668     return error;
2669 }
2670 
HandleDetachGracefullyResult(void * aContext)2671 void Interpreter::HandleDetachGracefullyResult(void *aContext)
2672 {
2673     static_cast<Interpreter *>(aContext)->HandleDetachGracefullyResult();
2674 }
2675 
HandleDetachGracefullyResult(void)2676 void Interpreter::HandleDetachGracefullyResult(void)
2677 {
2678     OutputLine("Finished detaching");
2679     OutputResult(OT_ERROR_NONE);
2680 }
2681 
2682 /**
2683  * @cli discover
2684  * @code
2685  * discover
2686  * | J | Network Name     | Extended PAN     | PAN  | MAC Address      | Ch | dBm | LQI |
2687  * +---+------------------+------------------+------+------------------+----+-----+-----+
2688  * | 0 | OpenThread       | dead00beef00cafe | ffff | f1d92a82c8d8fe43 | 11 | -20 |   0 |
2689  * Done
2690  * @endcode
2691  * @cparam discover [@ca{channel}]
2692  * `channel`: The channel to discover on. If no channel is provided, the discovery will cover all
2693  * valid channels.
2694  * @par
2695  * Perform an MLE Discovery operation.
2696  * @sa otThreadDiscover
2697  */
Process(Arg aArgs[])2698 template <> otError Interpreter::Process<Cmd("discover")>(Arg aArgs[])
2699 {
2700     otError  error        = OT_ERROR_NONE;
2701     uint32_t scanChannels = 0;
2702 
2703 #if OPENTHREAD_FTD
2704     /**
2705      * @cli discover reqcallback (enable,disable)
2706      * @code
2707      * discover reqcallback enable
2708      * Done
2709      * @endcode
2710      * @cparam discover reqcallback @ca{enable|disable}
2711      * @par api_copy
2712      * #otThreadSetDiscoveryRequestCallback
2713      */
2714     if (aArgs[0] == "reqcallback")
2715     {
2716         bool                             enable;
2717         otThreadDiscoveryRequestCallback callback = nullptr;
2718         void                            *context  = nullptr;
2719 
2720         SuccessOrExit(error = ParseEnableOrDisable(aArgs[1], enable));
2721 
2722         if (enable)
2723         {
2724             callback = &Interpreter::HandleDiscoveryRequest;
2725             context  = this;
2726         }
2727 
2728         otThreadSetDiscoveryRequestCallback(GetInstancePtr(), callback, context);
2729         ExitNow();
2730     }
2731 #endif // OPENTHREAD_FTD
2732 
2733     if (!aArgs[0].IsEmpty())
2734     {
2735         uint8_t channel;
2736 
2737         SuccessOrExit(error = aArgs[0].ParseAsUint8(channel));
2738         VerifyOrExit(channel < BitSizeOf(scanChannels), error = OT_ERROR_INVALID_ARGS);
2739         scanChannels = 1 << channel;
2740     }
2741 
2742     SuccessOrExit(error = otThreadDiscover(GetInstancePtr(), scanChannels, OT_PANID_BROADCAST, false, false,
2743                                            &Interpreter::HandleActiveScanResult, this));
2744 
2745     static const char *const kScanTableTitles[] = {
2746         "Network Name", "Extended PAN", "PAN", "MAC Address", "Ch", "dBm", "LQI",
2747     };
2748 
2749     static const uint8_t kScanTableColumnWidths[] = {
2750         18, 18, 6, 18, 4, 5, 5,
2751     };
2752 
2753     OutputTableHeader(kScanTableTitles, kScanTableColumnWidths);
2754 
2755     error = OT_ERROR_PENDING;
2756 
2757 exit:
2758     return error;
2759 }
2760 
2761 #if OPENTHREAD_CLI_DNS_ENABLE
Process(Arg aArgs[])2762 template <> otError Interpreter::Process<Cmd("dns")>(Arg aArgs[]) { return mDns.Process(aArgs); }
2763 #endif
2764 
2765 #if OPENTHREAD_FTD
OutputEidCacheEntry(const otCacheEntryInfo & aEntry)2766 void Interpreter::OutputEidCacheEntry(const otCacheEntryInfo &aEntry)
2767 {
2768     static const char *const kStateStrings[] = {
2769         "cache", // (0) OT_CACHE_ENTRY_STATE_CACHED
2770         "snoop", // (1) OT_CACHE_ENTRY_STATE_SNOOPED
2771         "query", // (2) OT_CACHE_ENTRY_STATE_QUERY
2772         "retry", // (3) OT_CACHE_ENTRY_STATE_RETRY_QUERY
2773     };
2774 
2775     static_assert(0 == OT_CACHE_ENTRY_STATE_CACHED, "OT_CACHE_ENTRY_STATE_CACHED value is incorrect");
2776     static_assert(1 == OT_CACHE_ENTRY_STATE_SNOOPED, "OT_CACHE_ENTRY_STATE_SNOOPED value is incorrect");
2777     static_assert(2 == OT_CACHE_ENTRY_STATE_QUERY, "OT_CACHE_ENTRY_STATE_QUERY value is incorrect");
2778     static_assert(3 == OT_CACHE_ENTRY_STATE_RETRY_QUERY, "OT_CACHE_ENTRY_STATE_RETRY_QUERY value is incorrect");
2779 
2780     OutputIp6Address(aEntry.mTarget);
2781     OutputFormat(" %04x", aEntry.mRloc16);
2782     OutputFormat(" %s", Stringify(aEntry.mState, kStateStrings));
2783     OutputFormat(" canEvict=%d", aEntry.mCanEvict);
2784 
2785     if (aEntry.mState == OT_CACHE_ENTRY_STATE_CACHED)
2786     {
2787         if (aEntry.mValidLastTrans)
2788         {
2789             OutputFormat(" transTime=%lu eid=", ToUlong(aEntry.mLastTransTime));
2790             OutputIp6Address(aEntry.mMeshLocalEid);
2791         }
2792     }
2793     else
2794     {
2795         OutputFormat(" timeout=%u", aEntry.mTimeout);
2796     }
2797 
2798     if (aEntry.mState == OT_CACHE_ENTRY_STATE_RETRY_QUERY)
2799     {
2800         OutputFormat(" retryDelay=%u rampDown=%d", aEntry.mRetryDelay, aEntry.mRampDown);
2801     }
2802 
2803     OutputNewLine();
2804 }
2805 
2806 /**
2807  * @cli eidcache
2808  * @code
2809  * eidcache
2810  * fd49:caf4:a29f:dc0e:97fc:69dd:3c16:df7d 2000 cache canEvict=1 transTime=0 eid=fd49:caf4:a29f:dc0e:97fc:69dd:3c16:df7d
2811  * fd49:caf4:a29f:dc0e:97fc:69dd:3c16:df7f fffe retry canEvict=1 timeout=10 retryDelay=30
2812  * Done
2813  * @endcode
2814  * @par
2815  * Returns the EID-to-RLOC cache entries.
2816  * @sa otThreadGetNextCacheEntry
2817  */
Process(Arg aArgs[])2818 template <> otError Interpreter::Process<Cmd("eidcache")>(Arg aArgs[])
2819 {
2820     OT_UNUSED_VARIABLE(aArgs);
2821 
2822     otCacheEntryIterator iterator;
2823     otCacheEntryInfo     entry;
2824 
2825     ClearAllBytes(iterator);
2826 
2827     while (true)
2828     {
2829         SuccessOrExit(otThreadGetNextCacheEntry(GetInstancePtr(), &entry, &iterator));
2830         OutputEidCacheEntry(entry);
2831     }
2832 
2833 exit:
2834     return OT_ERROR_NONE;
2835 }
2836 #endif
2837 
2838 /**
2839  * @cli eui64
2840  * @code
2841  * eui64
2842  * 0615aae900124b00
2843  * Done
2844  * @endcode
2845  * @par api_copy
2846  * #otPlatRadioGetIeeeEui64
2847  */
Process(Arg aArgs[])2848 template <> otError Interpreter::Process<Cmd("eui64")>(Arg aArgs[])
2849 {
2850     OT_UNUSED_VARIABLE(aArgs);
2851 
2852     otError      error = OT_ERROR_NONE;
2853     otExtAddress extAddress;
2854 
2855     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
2856 
2857     otLinkGetFactoryAssignedIeeeEui64(GetInstancePtr(), &extAddress);
2858     OutputExtAddressLine(extAddress);
2859 
2860 exit:
2861     return error;
2862 }
2863 
Process(Arg aArgs[])2864 template <> otError Interpreter::Process<Cmd("extaddr")>(Arg aArgs[])
2865 {
2866     otError error = OT_ERROR_NONE;
2867 
2868     /**
2869      * @cli extaddr
2870      * @code
2871      * extaddr
2872      * dead00beef00cafe
2873      * Done
2874      * @endcode
2875      * @par api_copy
2876      * #otLinkGetExtendedAddress
2877      */
2878     if (aArgs[0].IsEmpty())
2879     {
2880         OutputExtAddressLine(*otLinkGetExtendedAddress(GetInstancePtr()));
2881     }
2882     /**
2883      * @cli extaddr (set)
2884      * @code
2885      * extaddr dead00beef00cafe
2886      * dead00beef00cafe
2887      * Done
2888      * @endcode
2889      * @cparam extaddr @ca{extaddr}
2890      * @par api_copy
2891      * #otLinkSetExtendedAddress
2892      */
2893     else
2894     {
2895         otExtAddress extAddress;
2896 
2897         SuccessOrExit(error = aArgs[0].ParseAsHexString(extAddress.m8));
2898         error = otLinkSetExtendedAddress(GetInstancePtr(), &extAddress);
2899     }
2900 
2901 exit:
2902     return error;
2903 }
2904 
Process(Arg aArgs[])2905 template <> otError Interpreter::Process<Cmd("log")>(Arg aArgs[])
2906 {
2907     otError error = OT_ERROR_NONE;
2908 
2909     /**
2910      * @cli log level
2911      * @code
2912      * log level
2913      * 1
2914      * Done
2915      * @endcode
2916      * @par
2917      * Get the log level.
2918      * @sa otLoggingGetLevel
2919      */
2920     if (aArgs[0] == "level")
2921     {
2922         if (aArgs[1].IsEmpty())
2923         {
2924             OutputLine("%d", otLoggingGetLevel());
2925         }
2926         else
2927         {
2928 #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
2929             uint8_t level;
2930 
2931             /**
2932              * @cli log level (set)
2933              * @code
2934              * log level 4
2935              * Done
2936              * @endcode
2937              * @par api_copy
2938              * #otLoggingSetLevel
2939              * @cparam log level @ca{level}
2940              */
2941             VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
2942             SuccessOrExit(error = aArgs[1].ParseAsUint8(level));
2943             error = otLoggingSetLevel(static_cast<otLogLevel>(level));
2944 #else
2945             error = OT_ERROR_INVALID_ARGS;
2946 #endif
2947         }
2948     }
2949 #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX
2950     /**
2951      * @cli log filename
2952      * @par
2953      * Specifies filename to capture `otPlatLog()` messages, useful when debugging
2954      * automated test scripts on Linux when logging disrupts the automated test scripts.
2955      * @par
2956      * Requires `OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART`
2957      * and `OPENTHREAD_POSIX`.
2958      * @par api_copy
2959      * #otPlatDebugUart_logfile
2960      * @cparam log filename @ca{filename}
2961      */
2962     else if (aArgs[0] == "filename")
2963     {
2964         VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
2965         SuccessOrExit(error = otPlatDebugUart_logfile(aArgs[1].GetCString()));
2966     }
2967 #endif
2968     else
2969     {
2970         ExitNow(error = OT_ERROR_INVALID_ARGS);
2971     }
2972 
2973 exit:
2974     return error;
2975 }
2976 
Process(Arg aArgs[])2977 template <> otError Interpreter::Process<Cmd("extpanid")>(Arg aArgs[])
2978 {
2979     otError error = OT_ERROR_NONE;
2980 
2981     /**
2982      * @cli extpanid
2983      * @code
2984      * extpanid
2985      * dead00beef00cafe
2986      * Done
2987      * @endcode
2988      * @par api_copy
2989      * #otThreadGetExtendedPanId
2990      */
2991     if (aArgs[0].IsEmpty())
2992     {
2993         OutputBytesLine(otThreadGetExtendedPanId(GetInstancePtr())->m8);
2994     }
2995     /**
2996      * @cli extpanid (set)
2997      * @code
2998      * extpanid dead00beef00cafe
2999      * Done
3000      * @endcode
3001      * @cparam extpanid @ca{extpanid}
3002      * @par
3003      * @note The current commissioning credential becomes stale after changing this value.
3004      * Use `pskc` to reset.
3005      * @par api_copy
3006      * #otThreadSetExtendedPanId
3007      */
3008     else
3009     {
3010         otExtendedPanId extPanId;
3011 
3012         SuccessOrExit(error = aArgs[0].ParseAsHexString(extPanId.m8));
3013         error = otThreadSetExtendedPanId(GetInstancePtr(), &extPanId);
3014     }
3015 
3016 exit:
3017     return error;
3018 }
3019 
3020 /**
3021  * @cli factoryreset
3022  * @code
3023  * factoryreset
3024  * @endcode
3025  * @par api_copy
3026  * #otInstanceFactoryReset
3027  */
Process(Arg aArgs[])3028 template <> otError Interpreter::Process<Cmd("factoryreset")>(Arg aArgs[])
3029 {
3030     OT_UNUSED_VARIABLE(aArgs);
3031 
3032     otInstanceFactoryReset(GetInstancePtr());
3033 
3034     return OT_ERROR_NONE;
3035 }
3036 
3037 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
Process(Arg aArgs[])3038 template <> otError Interpreter::Process<Cmd("fake")>(Arg aArgs[])
3039 {
3040     otError error = OT_ERROR_INVALID_COMMAND;
3041 
3042     /**
3043      * @cli fake (a,an)
3044      * @code
3045      * fake /a/an fdde:ad00:beef:0:0:ff:fe00:a800 fd00:7d03:7d03:7d03:55f2:bb6a:7a43:a03b 1111222233334444
3046      * Done
3047      * @endcode
3048      * @cparam fake /a/an @ca{dst-ipaddr} @ca{target} @ca{meshLocalIid}
3049      * @par
3050      * Sends fake Thread messages.
3051      * @par
3052      * Available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.
3053      * @sa otThreadSendAddressNotification
3054      */
3055     if (aArgs[0] == "/a/an")
3056     {
3057         otIp6Address             destination, target;
3058         otIp6InterfaceIdentifier mlIid;
3059 
3060         SuccessOrExit(error = aArgs[1].ParseAsIp6Address(destination));
3061         SuccessOrExit(error = aArgs[2].ParseAsIp6Address(target));
3062         SuccessOrExit(error = aArgs[3].ParseAsHexString(mlIid.mFields.m8));
3063         otThreadSendAddressNotification(GetInstancePtr(), &destination, &target, &mlIid);
3064     }
3065 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
3066     else if (aArgs[0] == "/b/ba")
3067     {
3068         otIp6Address             target;
3069         otIp6InterfaceIdentifier mlIid;
3070         uint32_t                 timeSinceLastTransaction;
3071 
3072         SuccessOrExit(error = aArgs[1].ParseAsIp6Address(target));
3073         SuccessOrExit(error = aArgs[2].ParseAsHexString(mlIid.mFields.m8));
3074         SuccessOrExit(error = aArgs[3].ParseAsUint32(timeSinceLastTransaction));
3075 
3076         error = otThreadSendProactiveBackboneNotification(GetInstancePtr(), &target, &mlIid, timeSinceLastTransaction);
3077     }
3078 #endif
3079 
3080 exit:
3081     return error;
3082 }
3083 #endif
3084 
Process(Arg aArgs[])3085 template <> otError Interpreter::Process<Cmd("fem")>(Arg aArgs[])
3086 {
3087     otError error = OT_ERROR_NONE;
3088 
3089     /**
3090      * @cli fem
3091      * @code
3092      * fem
3093      * LNA gain 11 dBm
3094      * Done
3095      * @endcode
3096      * @par
3097      * Gets external FEM parameters.
3098      * @sa otPlatRadioGetFemLnaGain
3099      */
3100     if (aArgs[0].IsEmpty())
3101     {
3102         int8_t lnaGain;
3103 
3104         SuccessOrExit(error = otPlatRadioGetFemLnaGain(GetInstancePtr(), &lnaGain));
3105         OutputLine("LNA gain %d dBm", lnaGain);
3106     }
3107     /**
3108      * @cli fem lnagain (get)
3109      * @code
3110      * fem lnagain
3111      * 11
3112      * Done
3113      * @endcode
3114      * @par api_copy
3115      * #otPlatRadioGetFemLnaGain
3116      */
3117     else if (aArgs[0] == "lnagain")
3118     {
3119         if (aArgs[1].IsEmpty())
3120         {
3121             int8_t lnaGain;
3122 
3123             SuccessOrExit(error = otPlatRadioGetFemLnaGain(GetInstancePtr(), &lnaGain));
3124             OutputLine("%d", lnaGain);
3125         }
3126         /**
3127          * @cli fem lnagain (set)
3128          * @code
3129          * fem lnagain 8
3130          * Done
3131          * @endcode
3132          * @par api_copy
3133          * #otPlatRadioSetFemLnaGain
3134          */
3135         else
3136         {
3137             int8_t lnaGain;
3138 
3139             SuccessOrExit(error = aArgs[1].ParseAsInt8(lnaGain));
3140             SuccessOrExit(error = otPlatRadioSetFemLnaGain(GetInstancePtr(), lnaGain));
3141         }
3142     }
3143     else
3144     {
3145         error = OT_ERROR_INVALID_ARGS;
3146     }
3147 
3148 exit:
3149     return error;
3150 }
3151 
Process(Arg aArgs[])3152 template <> otError Interpreter::Process<Cmd("ifconfig")>(Arg aArgs[])
3153 {
3154     otError error = OT_ERROR_NONE;
3155 
3156     /**
3157      * @cli ifconfig
3158      * @code
3159      * ifconfig
3160      * down
3161      * Done
3162      * @endcode
3163      * @code
3164      * ifconfig
3165      * up
3166      * Done
3167      * @endcode
3168      * @par api_copy
3169      * #otIp6IsEnabled
3170      */
3171     if (aArgs[0].IsEmpty())
3172     {
3173         if (otIp6IsEnabled(GetInstancePtr()))
3174         {
3175             OutputLine("up");
3176         }
3177         else
3178         {
3179             OutputLine("down");
3180         }
3181     }
3182     /**
3183      * @cli ifconfig (up,down)
3184      * @code
3185      * ifconfig up
3186      * Done
3187      * @endcode
3188      * @code
3189      * ifconfig down
3190      * Done
3191      * @endcode
3192      * @cparam ifconfig @ca{up|down}
3193      * @par api_copy
3194      * #otIp6SetEnabled
3195      */
3196     else if (aArgs[0] == "up")
3197     {
3198         SuccessOrExit(error = otIp6SetEnabled(GetInstancePtr(), true));
3199     }
3200     else if (aArgs[0] == "down")
3201     {
3202         SuccessOrExit(error = otIp6SetEnabled(GetInstancePtr(), false));
3203     }
3204     else
3205     {
3206         ExitNow(error = OT_ERROR_INVALID_ARGS);
3207     }
3208 
3209 exit:
3210     return error;
3211 }
3212 
Process(Arg aArgs[])3213 template <> otError Interpreter::Process<Cmd("instanceid")>(Arg aArgs[])
3214 {
3215     otError error = OT_ERROR_INVALID_ARGS;
3216 
3217     /**
3218      * @cli instanceid
3219      * @code
3220      * instanceid
3221      * 468697314
3222      * Done
3223      * @endcode
3224      * @par api_copy
3225      * #otInstanceGetId
3226      */
3227     if (aArgs[0].IsEmpty())
3228     {
3229         OutputLine("%lu", ToUlong(otInstanceGetId(GetInstancePtr())));
3230         error = OT_ERROR_NONE;
3231     }
3232 
3233     return error;
3234 }
3235 
AddressOriginToString(uint8_t aOrigin)3236 const char *Interpreter::AddressOriginToString(uint8_t aOrigin)
3237 {
3238     static const char *const kOriginStrings[4] = {
3239         "thread", // 0, OT_ADDRESS_ORIGIN_THREAD
3240         "slaac",  // 1, OT_ADDRESS_ORIGIN_SLAAC
3241         "dhcp6",  // 2, OT_ADDRESS_ORIGIN_DHCPV6
3242         "manual", // 3, OT_ADDRESS_ORIGIN_MANUAL
3243     };
3244 
3245     static_assert(0 == OT_ADDRESS_ORIGIN_THREAD, "OT_ADDRESS_ORIGIN_THREAD value is incorrect");
3246     static_assert(1 == OT_ADDRESS_ORIGIN_SLAAC, "OT_ADDRESS_ORIGIN_SLAAC value is incorrect");
3247     static_assert(2 == OT_ADDRESS_ORIGIN_DHCPV6, "OT_ADDRESS_ORIGIN_DHCPV6 value is incorrect");
3248     static_assert(3 == OT_ADDRESS_ORIGIN_MANUAL, "OT_ADDRESS_ORIGIN_MANUAL value is incorrect");
3249 
3250     return Stringify(aOrigin, kOriginStrings);
3251 }
3252 
Process(Arg aArgs[])3253 template <> otError Interpreter::Process<Cmd("ipaddr")>(Arg aArgs[])
3254 {
3255     otError error   = OT_ERROR_NONE;
3256     bool    verbose = false;
3257 
3258     if (aArgs[0] == "-v")
3259     {
3260         aArgs++;
3261         verbose = true;
3262     }
3263 
3264     /**
3265      * @cli ipaddr
3266      * @code
3267      * ipaddr
3268      * fdde:ad00:beef:0:0:ff:fe00:0
3269      * fdde:ad00:beef:0:558:f56b:d688:799
3270      * fe80:0:0:0:f3d9:2a82:c8d8:fe43
3271      * Done
3272      * @endcode
3273      * @code
3274      * ipaddr -v
3275      * fd5e:18fa:f4a5:b8:0:ff:fe00:fc00 origin:thread plen:64 preferred:0 valid:1
3276      * fd5e:18fa:f4a5:b8:0:ff:fe00:dc00 origin:thread plen:64 preferred:0 valid:1
3277      * fd5e:18fa:f4a5:b8:f8e:5d95:87a0:e82c origin:thread plen:64 preferred:0 valid:1
3278      * fe80:0:0:0:4891:b191:e277:8826 origin:thread plen:64 preferred:1 valid:1
3279      * Done
3280      * @endcode
3281      * @cparam ipaddr [@ca{-v}]
3282      * Use `-v` to get more verbose information about the address:
3283      * - `origin`: can be `thread`, `slaac`, `dhcp6`, `manual` and indicates the origin of the address
3284      * - `plen`: prefix length
3285      * - `preferred`: preferred flag (boolean)
3286      * - `valid`: valid flag (boolean)
3287      * @par api_copy
3288      * #otIp6GetUnicastAddresses
3289      */
3290     if (aArgs[0].IsEmpty())
3291     {
3292         const otNetifAddress *unicastAddrs = otIp6GetUnicastAddresses(GetInstancePtr());
3293 
3294         for (const otNetifAddress *addr = unicastAddrs; addr; addr = addr->mNext)
3295         {
3296             OutputIp6Address(addr->mAddress);
3297 
3298             if (verbose)
3299             {
3300                 OutputFormat(" origin:%s plen:%u preferred:%u valid:%u", AddressOriginToString(addr->mAddressOrigin),
3301                              addr->mPrefixLength, addr->mPreferred, addr->mValid);
3302             }
3303 
3304             OutputNewLine();
3305         }
3306     }
3307     /**
3308      * @cli ipaddr add
3309      * @code
3310      * ipaddr add 2001::dead:beef:cafe
3311      * Done
3312      * @endcode
3313      * @cparam ipaddr add @ca{aAddress}
3314      * @par api_copy
3315      * #otIp6AddUnicastAddress
3316      */
3317     else if (aArgs[0] == "add")
3318     {
3319         otNetifAddress address;
3320 
3321         SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address.mAddress));
3322         address.mPrefixLength  = 64;
3323         address.mPreferred     = true;
3324         address.mValid         = true;
3325         address.mAddressOrigin = OT_ADDRESS_ORIGIN_MANUAL;
3326 
3327         error = otIp6AddUnicastAddress(GetInstancePtr(), &address);
3328     }
3329     /**
3330      * @cli ipaddr del
3331      * @code
3332      * ipaddr del 2001::dead:beef:cafe
3333      * Done
3334      * @endcode
3335      * @cparam ipaddr del @ca{aAddress}
3336      * @par api_copy
3337      * #otIp6RemoveUnicastAddress
3338      */
3339     else if (aArgs[0] == "del")
3340     {
3341         otIp6Address address;
3342 
3343         SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
3344         error = otIp6RemoveUnicastAddress(GetInstancePtr(), &address);
3345     }
3346     /**
3347      * @cli ipaddr linklocal
3348      * @code
3349      * ipaddr linklocal
3350      * fe80:0:0:0:f3d9:2a82:c8d8:fe43
3351      * Done
3352      * @endcode
3353      * @par api_copy
3354      * #otThreadGetLinkLocalIp6Address
3355      */
3356     else if (aArgs[0] == "linklocal")
3357     {
3358         OutputIp6AddressLine(*otThreadGetLinkLocalIp6Address(GetInstancePtr()));
3359     }
3360     /**
3361      * @cli ipaddr rloc
3362      * @code
3363      * ipaddr rloc
3364      * fdde:ad00:beef:0:0:ff:fe00:0
3365      * Done
3366      * @endcode
3367      * @par api_copy
3368      * #otThreadGetRloc
3369      */
3370     else if (aArgs[0] == "rloc")
3371     {
3372         OutputIp6AddressLine(*otThreadGetRloc(GetInstancePtr()));
3373     }
3374     /**
3375      * @cli ipaddr mleid
3376      * @code
3377      * ipaddr mleid
3378      * fdde:ad00:beef:0:558:f56b:d688:799
3379      * Done
3380      * @endcode
3381      * @par api_copy
3382      * #otThreadGetMeshLocalEid
3383      */
3384     else if (aArgs[0] == "mleid")
3385     {
3386         OutputIp6AddressLine(*otThreadGetMeshLocalEid(GetInstancePtr()));
3387     }
3388     else
3389     {
3390         error = OT_ERROR_INVALID_COMMAND;
3391     }
3392 
3393 exit:
3394     return error;
3395 }
3396 
Process(Arg aArgs[])3397 template <> otError Interpreter::Process<Cmd("ipmaddr")>(Arg aArgs[])
3398 {
3399     otError error = OT_ERROR_NONE;
3400 
3401     /**
3402      * @cli ipmaddr
3403      * @code
3404      * ipmaddr
3405      * ff05:0:0:0:0:0:0:1
3406      * ff33:40:fdde:ad00:beef:0:0:1
3407      * ff32:40:fdde:ad00:beef:0:0:1
3408      * Done
3409      * @endcode
3410      * @par api_copy
3411      * #otIp6GetMulticastAddresses
3412      */
3413     if (aArgs[0].IsEmpty())
3414     {
3415         for (const otNetifMulticastAddress *addr = otIp6GetMulticastAddresses(GetInstancePtr()); addr;
3416              addr                                = addr->mNext)
3417         {
3418             OutputIp6AddressLine(addr->mAddress);
3419         }
3420     }
3421     /**
3422      * @cli ipmaddr add
3423      * @code
3424      * ipmaddr add ff05::1
3425      * Done
3426      * @endcode
3427      * @cparam ipmaddr add @ca{aAddress}
3428      * @par api_copy
3429      * #otIp6SubscribeMulticastAddress
3430      */
3431     else if (aArgs[0] == "add")
3432     {
3433         otIp6Address address;
3434 
3435         aArgs++;
3436 
3437         do
3438         {
3439             SuccessOrExit(error = aArgs->ParseAsIp6Address(address));
3440             SuccessOrExit(error = otIp6SubscribeMulticastAddress(GetInstancePtr(), &address));
3441         }
3442 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
3443         while (!(++aArgs)->IsEmpty());
3444 #else
3445         while (false);
3446 #endif
3447     }
3448     /**
3449      * @cli ipmaddr del
3450      * @code
3451      * ipmaddr del ff05::1
3452      * Done
3453      * @endcode
3454      * @cparam ipmaddr del @ca{aAddress}
3455      * @par api_copy
3456      * #otIp6UnsubscribeMulticastAddress
3457      */
3458     else if (aArgs[0] == "del")
3459     {
3460         otIp6Address address;
3461 
3462         SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
3463         error = otIp6UnsubscribeMulticastAddress(GetInstancePtr(), &address);
3464     }
3465     /**
3466      * @cli ipmaddr promiscuous
3467      * @code
3468      * ipmaddr promiscuous
3469      * Disabled
3470      * Done
3471      * @endcode
3472      * @par api_copy
3473      * #otIp6IsMulticastPromiscuousEnabled
3474      */
3475     else if (aArgs[0] == "promiscuous")
3476     {
3477         /**
3478          * @cli ipmaddr promiscuous (enable,disable)
3479          * @code
3480          * ipmaddr promiscuous enable
3481          * Done
3482          * @endcode
3483          * @code
3484          * ipmaddr promiscuous disable
3485          * Done
3486          * @endcode
3487          * @cparam ipmaddr promiscuous @ca{enable|disable}
3488          * @par api_copy
3489          * #otIp6SetMulticastPromiscuousEnabled
3490          */
3491         error =
3492             ProcessEnableDisable(aArgs + 1, otIp6IsMulticastPromiscuousEnabled, otIp6SetMulticastPromiscuousEnabled);
3493     }
3494     /**
3495      * @cli ipmaddr llatn
3496      * @code
3497      * ipmaddr llatn
3498      * ff32:40:fdde:ad00:beef:0:0:1
3499      * Done
3500      * @endcode
3501      * @par api_copy
3502      * #otThreadGetLinkLocalAllThreadNodesMulticastAddress
3503      */
3504     else if (aArgs[0] == "llatn")
3505     {
3506         OutputIp6AddressLine(*otThreadGetLinkLocalAllThreadNodesMulticastAddress(GetInstancePtr()));
3507     }
3508     /**
3509      * @cli ipmaddr rlatn
3510      * @code
3511      * ipmaddr rlatn
3512      * ff33:40:fdde:ad00:beef:0:0:1
3513      * Done
3514      * @endcode
3515      * @par api_copy
3516      * #otThreadGetRealmLocalAllThreadNodesMulticastAddress
3517      */
3518     else if (aArgs[0] == "rlatn")
3519     {
3520         OutputIp6AddressLine(*otThreadGetRealmLocalAllThreadNodesMulticastAddress(GetInstancePtr()));
3521     }
3522     else
3523     {
3524         error = OT_ERROR_INVALID_COMMAND;
3525     }
3526 
3527 exit:
3528     return error;
3529 }
3530 
Process(Arg aArgs[])3531 template <> otError Interpreter::Process<Cmd("keysequence")>(Arg aArgs[])
3532 {
3533     otError error = OT_ERROR_INVALID_ARGS;
3534 
3535     /**
3536      * @cli keysequence counter
3537      * @code
3538      * keysequence counter
3539      * 10
3540      * Done
3541      * @endcode
3542      * @par api_copy
3543      * #otThreadGetKeySequenceCounter
3544      */
3545     if (aArgs[0] == "counter")
3546     {
3547         /**
3548          * @cli keysequence counter (set)
3549          * @code
3550          * keysequence counter 10
3551          * Done
3552          * @endcode
3553          * @cparam keysequence counter @ca{counter}
3554          * @par api_copy
3555          * #otThreadSetKeySequenceCounter
3556          */
3557         error = ProcessGetSet(aArgs + 1, otThreadGetKeySequenceCounter, otThreadSetKeySequenceCounter);
3558     }
3559     /**
3560      * @cli keysequence guardtime
3561      * @code
3562      * keysequence guardtime
3563      * 0
3564      * Done
3565      * @endcode
3566      * @par api_copy
3567      * #otThreadGetKeySwitchGuardTime
3568      */
3569     else if (aArgs[0] == "guardtime")
3570     {
3571         /**
3572          * @cli keysequence guardtime (set)
3573          * @code
3574          * keysequence guardtime 0
3575          * Done
3576          * @endcode
3577          * @cparam keysequence guardtime @ca{guardtime-hours}
3578          * Use `0` to `Thread Key Switch` immediately if there's a key index match.
3579          * @par api_copy
3580          * #otThreadSetKeySwitchGuardTime
3581          */
3582         error = ProcessGetSet(aArgs + 1, otThreadGetKeySwitchGuardTime, otThreadSetKeySwitchGuardTime);
3583     }
3584 
3585     return error;
3586 }
3587 
3588 /**
3589  * @cli leaderdata
3590  * @code
3591  * leaderdata
3592  * Partition ID: 1077744240
3593  * Weighting: 64
3594  * Data Version: 109
3595  * Stable Data Version: 211
3596  * Leader Router ID: 60
3597  * Done
3598  * @endcode
3599  * @par
3600  * Gets the Thread Leader Data.
3601  * @sa otThreadGetLeaderData
3602  */
Process(Arg aArgs[])3603 template <> otError Interpreter::Process<Cmd("leaderdata")>(Arg aArgs[])
3604 {
3605     OT_UNUSED_VARIABLE(aArgs);
3606 
3607     otError      error;
3608     otLeaderData leaderData;
3609 
3610     SuccessOrExit(error = otThreadGetLeaderData(GetInstancePtr(), &leaderData));
3611 
3612     OutputLine("Partition ID: %lu", ToUlong(leaderData.mPartitionId));
3613     OutputLine("Weighting: %u", leaderData.mWeighting);
3614     OutputLine("Data Version: %u", leaderData.mDataVersion);
3615     OutputLine("Stable Data Version: %u", leaderData.mStableDataVersion);
3616     OutputLine("Leader Router ID: %u", leaderData.mLeaderRouterId);
3617 
3618 exit:
3619     return error;
3620 }
3621 
3622 #if OPENTHREAD_FTD
Process(Arg aArgs[])3623 template <> otError Interpreter::Process<Cmd("partitionid")>(Arg aArgs[])
3624 {
3625     otError error = OT_ERROR_INVALID_COMMAND;
3626 
3627     /**
3628      * @cli partitionid
3629      * @code
3630      * partitionid
3631      * 4294967295
3632      * Done
3633      * @endcode
3634      * @par
3635      * Get the Thread Network Partition ID.
3636      * @sa otThreadGetPartitionId
3637      */
3638     if (aArgs[0].IsEmpty())
3639     {
3640         OutputLine("%lu", ToUlong(otThreadGetPartitionId(GetInstancePtr())));
3641         error = OT_ERROR_NONE;
3642     }
3643 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
3644     /**
3645      * @cli partitionid preferred (get,set)
3646      * @code
3647      * partitionid preferred
3648      * 4294967295
3649      * Done
3650      * @endcode
3651      * @code
3652      * partitionid preferred 0xffffffff
3653      * Done
3654      * @endcode
3655      * @cparam partitionid preferred @ca{partitionid}
3656      * @sa otThreadGetPreferredLeaderPartitionId
3657      * @sa otThreadSetPreferredLeaderPartitionId
3658      * @par
3659      * `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is required.
3660      */
3661     else if (aArgs[0] == "preferred")
3662     {
3663         error = ProcessGetSet(aArgs + 1, otThreadGetPreferredLeaderPartitionId, otThreadSetPreferredLeaderPartitionId);
3664     }
3665 #endif
3666 
3667     return error;
3668 }
3669 
3670 /**
3671  * @cli leaderweight
3672  * @code
3673  * leaderweight
3674  * 128
3675  * Done
3676  * @endcode
3677  * @par api_copy
3678  * #otThreadGetLocalLeaderWeight
3679  */
Process(Arg aArgs[])3680 template <> otError Interpreter::Process<Cmd("leaderweight")>(Arg aArgs[])
3681 {
3682     /**
3683      * @cli leaderweight (set)
3684      * @code
3685      * leaderweight 128
3686      * Done
3687      * @endcode
3688      * @cparam leaderweight @ca{weight}
3689      * @par api_copy
3690      * #otThreadSetLocalLeaderWeight
3691      */
3692     return ProcessGetSet(aArgs, otThreadGetLocalLeaderWeight, otThreadSetLocalLeaderWeight);
3693 }
3694 
3695 #if OPENTHREAD_CONFIG_MLE_DEVICE_PROPERTY_LEADER_WEIGHT_ENABLE
Process(Arg aArgs[])3696 template <> otError Interpreter::Process<Cmd("deviceprops")>(Arg aArgs[])
3697 {
3698     static const char *const kPowerSupplyStrings[4] = {
3699         "battery",           // (0) OT_POWER_SUPPLY_BATTERY
3700         "external",          // (1) OT_POWER_SUPPLY_EXTERNAL
3701         "external-stable",   // (2) OT_POWER_SUPPLY_EXTERNAL_STABLE
3702         "external-unstable", // (3) OT_POWER_SUPPLY_EXTERNAL_UNSTABLE
3703     };
3704 
3705     static_assert(0 == OT_POWER_SUPPLY_BATTERY, "OT_POWER_SUPPLY_BATTERY value is incorrect");
3706     static_assert(1 == OT_POWER_SUPPLY_EXTERNAL, "OT_POWER_SUPPLY_EXTERNAL value is incorrect");
3707     static_assert(2 == OT_POWER_SUPPLY_EXTERNAL_STABLE, "OT_POWER_SUPPLY_EXTERNAL_STABLE value is incorrect");
3708     static_assert(3 == OT_POWER_SUPPLY_EXTERNAL_UNSTABLE, "OT_POWER_SUPPLY_EXTERNAL_UNSTABLE value is incorrect");
3709 
3710     otError error = OT_ERROR_NONE;
3711 
3712     /**
3713      * @cli deviceprops
3714      * @code
3715      * deviceprops
3716      * PowerSupply      : external
3717      * IsBorderRouter   : yes
3718      * SupportsCcm      : no
3719      * IsUnstable       : no
3720      * WeightAdjustment : 0
3721      * Done
3722      * @endcode
3723      * @par api_copy
3724      * #otThreadGetDeviceProperties
3725      */
3726     if (aArgs[0].IsEmpty())
3727     {
3728         const otDeviceProperties *props = otThreadGetDeviceProperties(GetInstancePtr());
3729 
3730         OutputLine("PowerSupply      : %s", Stringify(props->mPowerSupply, kPowerSupplyStrings));
3731         OutputLine("IsBorderRouter   : %s", props->mIsBorderRouter ? "yes" : "no");
3732         OutputLine("SupportsCcm      : %s", props->mSupportsCcm ? "yes" : "no");
3733         OutputLine("IsUnstable       : %s", props->mIsUnstable ? "yes" : "no");
3734         OutputLine("WeightAdjustment : %d", props->mLeaderWeightAdjustment);
3735     }
3736     /**
3737      * @cli deviceprops (set)
3738      * @code
3739      * deviceprops battery 0 0 0 -5
3740      * Done
3741      * @endcode
3742      * @code
3743      * deviceprops
3744      * PowerSupply      : battery
3745      * IsBorderRouter   : no
3746      * SupportsCcm      : no
3747      * IsUnstable       : no
3748      * WeightAdjustment : -5
3749      * Done
3750      * @endcode
3751      * @cparam deviceprops @ca{powerSupply} @ca{isBr} @ca{supportsCcm} @ca{isUnstable} @ca{weightAdjustment}
3752      * `powerSupply`: should be 'battery', 'external', 'external-stable', 'external-unstable'.
3753      * @par
3754      * Sets the device properties.
3755      * @csa{leaderweight}
3756      * @csa{leaderweight (set)}
3757      * @sa #otThreadSetDeviceProperties
3758      */
3759     else
3760     {
3761         otDeviceProperties props;
3762         bool               value;
3763         uint8_t            index;
3764 
3765         for (index = 0; index < OT_ARRAY_LENGTH(kPowerSupplyStrings); index++)
3766         {
3767             if (aArgs[0] == kPowerSupplyStrings[index])
3768             {
3769                 props.mPowerSupply = static_cast<otPowerSupply>(index);
3770                 break;
3771             }
3772         }
3773 
3774         VerifyOrExit(index < OT_ARRAY_LENGTH(kPowerSupplyStrings), error = OT_ERROR_INVALID_ARGS);
3775 
3776         SuccessOrExit(error = aArgs[1].ParseAsBool(value));
3777         props.mIsBorderRouter = value;
3778 
3779         SuccessOrExit(error = aArgs[2].ParseAsBool(value));
3780         props.mSupportsCcm = value;
3781 
3782         SuccessOrExit(error = aArgs[3].ParseAsBool(value));
3783         props.mIsUnstable = value;
3784 
3785         SuccessOrExit(error = aArgs[4].ParseAsInt8(props.mLeaderWeightAdjustment));
3786 
3787         VerifyOrExit(aArgs[5].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
3788         otThreadSetDeviceProperties(GetInstancePtr(), &props);
3789     }
3790 
3791 exit:
3792     return error;
3793 }
3794 #endif // OPENTHREAD_CONFIG_MLE_DEVICE_PROPERTY_LEADER_WEIGHT_ENABLE
3795 
3796 #endif // OPENTHREAD_FTD
3797 
3798 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
3799 
Process(Arg aArgs[])3800 template <> otError Interpreter::Process<Cmd("linkmetrics")>(Arg aArgs[]) { return mLinkMetrics.Process(aArgs); }
3801 
3802 #if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
Process(Arg aArgs[])3803 template <> otError Interpreter::Process<Cmd("linkmetricsmgr")>(Arg aArgs[])
3804 {
3805     otError error = OT_ERROR_NONE;
3806 
3807     VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
3808 
3809     /**
3810      * @cli linkmetricsmgr (enable,disable)
3811      * @code
3812      * linkmetricmgr enable
3813      * Done
3814      * @endcode
3815      * @code
3816      * linkmetricmgr disable
3817      * Done
3818      * @endcode
3819      * @cparam linkmetricsmgr @ca{enable|disable}
3820      * @par api_copy
3821      * #otLinkMetricsManagerSetEnabled
3822      *
3823      */
3824     if (ProcessEnableDisable(aArgs, otLinkMetricsManagerSetEnabled) == OT_ERROR_NONE)
3825     {
3826     }
3827     /**
3828      * @cli linkmetricsmgr show
3829      * @code
3830      * linkmetricsmgr show
3831      * ExtAddr:827aa7f7f63e1234, LinkMargin:80, Rssi:-20
3832      * Done
3833      * @endcode
3834      * @par api_copy
3835      * #otLinkMetricsManagerGetMetricsValueByExtAddr
3836      *
3837      */
3838     else if (aArgs[0] == "show")
3839     {
3840         otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT;
3841         otNeighborInfo         neighborInfo;
3842 
3843         while (otThreadGetNextNeighborInfo(GetInstancePtr(), &iterator, &neighborInfo) == OT_ERROR_NONE)
3844         {
3845             otLinkMetricsValues linkMetricsValues;
3846 
3847             if (otLinkMetricsManagerGetMetricsValueByExtAddr(GetInstancePtr(), &neighborInfo.mExtAddress,
3848                                                              &linkMetricsValues) != OT_ERROR_NONE)
3849             {
3850                 continue;
3851             }
3852 
3853             OutputFormat("ExtAddr:");
3854             OutputExtAddress(neighborInfo.mExtAddress);
3855             OutputLine(", LinkMargin:%u, Rssi:%d", linkMetricsValues.mLinkMarginValue, linkMetricsValues.mRssiValue);
3856         }
3857     }
3858     else
3859     {
3860         error = OT_ERROR_INVALID_COMMAND;
3861     }
3862 
3863 exit:
3864     return error;
3865 }
3866 #endif // OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
3867 
3868 #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
3869 
3870 #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
3871 
Process(Arg aArgs[])3872 template <> otError Interpreter::Process<Cmd("locate")>(Arg aArgs[])
3873 {
3874     otError      error = OT_ERROR_INVALID_ARGS;
3875     otIp6Address anycastAddress;
3876 
3877     /**
3878      * @cli locate
3879      * @code
3880      * locate
3881      * Idle
3882      * Done
3883      * @endcode
3884      * @code
3885      * locate fdde:ad00:beef:0:0:ff:fe00:fc10
3886      * @endcode
3887      * @code
3888      * locate
3889      * In Progress
3890      * Done
3891      * @endcode
3892      * @par
3893      * Gets the current state (`In Progress` or `Idle`) of anycast locator.
3894      * @par
3895      * Available when `OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE` is enabled.
3896      * @sa otThreadIsAnycastLocateInProgress
3897      */
3898     if (aArgs[0].IsEmpty())
3899     {
3900         OutputLine(otThreadIsAnycastLocateInProgress(GetInstancePtr()) ? "In Progress" : "Idle");
3901         ExitNow(error = OT_ERROR_NONE);
3902     }
3903 
3904     /**
3905      * @cli locate (set)
3906      * @code
3907      * locate fdde:ad00:beef:0:0:ff:fe00:fc00
3908      * fdde:ad00:beef:0:d9d3:9000:16b:d03b 0xc800
3909      * Done
3910      * @endcode
3911      * @par
3912      * Locate the closest destination of an anycast address (i.e., find the
3913      * destination's mesh local EID and RLOC16).
3914      * @par
3915      * The closest destination is determined based on the current routing
3916      * table and path costs within the Thread mesh.
3917      * @par
3918      * Available when `OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE` is enabled.
3919      * @sa otThreadLocateAnycastDestination
3920      * @cparam locate @ca{anycastaddr}
3921      */
3922     SuccessOrExit(error = aArgs[0].ParseAsIp6Address(anycastAddress));
3923     SuccessOrExit(error =
3924                       otThreadLocateAnycastDestination(GetInstancePtr(), &anycastAddress, HandleLocateResult, this));
3925     SetCommandTimeout(kLocateTimeoutMsecs);
3926     mLocateInProgress = true;
3927     error             = OT_ERROR_PENDING;
3928 
3929 exit:
3930     return error;
3931 }
3932 
HandleLocateResult(void * aContext,otError aError,const otIp6Address * aMeshLocalAddress,uint16_t aRloc16)3933 void Interpreter::HandleLocateResult(void               *aContext,
3934                                      otError             aError,
3935                                      const otIp6Address *aMeshLocalAddress,
3936                                      uint16_t            aRloc16)
3937 {
3938     static_cast<Interpreter *>(aContext)->HandleLocateResult(aError, aMeshLocalAddress, aRloc16);
3939 }
3940 
HandleLocateResult(otError aError,const otIp6Address * aMeshLocalAddress,uint16_t aRloc16)3941 void Interpreter::HandleLocateResult(otError aError, const otIp6Address *aMeshLocalAddress, uint16_t aRloc16)
3942 {
3943     VerifyOrExit(mLocateInProgress);
3944 
3945     mLocateInProgress = false;
3946 
3947     if (aError == OT_ERROR_NONE)
3948     {
3949         OutputIp6Address(*aMeshLocalAddress);
3950         OutputLine(" 0x%04x", aRloc16);
3951     }
3952 
3953     OutputResult(aError);
3954 
3955 exit:
3956     return;
3957 }
3958 
3959 #endif //  OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
3960 
3961 #if OPENTHREAD_FTD
Process(Arg aArgs[])3962 template <> otError Interpreter::Process<Cmd("pskc")>(Arg aArgs[])
3963 {
3964     otError error = OT_ERROR_NONE;
3965     otPskc  pskc;
3966 
3967     /**
3968      * @cli pskc
3969      * @code
3970      * pskc
3971      * 67c0c203aa0b042bfb5381c47aef4d9e
3972      * Done
3973      * @endcode
3974      * @par api_copy
3975      * #otThreadGetPskc
3976      */
3977     if (aArgs[0].IsEmpty())
3978     {
3979         otThreadGetPskc(GetInstancePtr(), &pskc);
3980         OutputBytesLine(pskc.m8);
3981     }
3982     else
3983     /**
3984      * @cli pskc (set)
3985      * @code
3986      * pskc 67c0c203aa0b042bfb5381c47aef4d9e
3987      * Done
3988      * @endcode
3989      * @cparam pskc @ca{key}
3990      * @par
3991      * Sets the pskc in hexadecimal format.
3992      */
3993     {
3994         if (aArgs[1].IsEmpty())
3995         {
3996             SuccessOrExit(error = aArgs[0].ParseAsHexString(pskc.m8));
3997         }
3998         /**
3999          * @cli pskc -p
4000          * @code
4001          * pskc -p 123456
4002          * Done
4003          * @endcode
4004          * @cparam pskc -p @ca{passphrase}
4005          * @par
4006          * Generates the pskc from the passphrase (UTF-8 encoded), together with the current network name and extended
4007          * PAN ID.
4008          */
4009         else if (aArgs[0] == "-p")
4010         {
4011             SuccessOrExit(error = otDatasetGeneratePskc(
4012                               aArgs[1].GetCString(),
4013                               reinterpret_cast<const otNetworkName *>(otThreadGetNetworkName(GetInstancePtr())),
4014                               otThreadGetExtendedPanId(GetInstancePtr()), &pskc));
4015         }
4016         else
4017         {
4018             ExitNow(error = OT_ERROR_INVALID_ARGS);
4019         }
4020 
4021         error = otThreadSetPskc(GetInstancePtr(), &pskc);
4022     }
4023 
4024 exit:
4025     return error;
4026 }
4027 
4028 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
Process(Arg aArgs[])4029 template <> otError Interpreter::Process<Cmd("pskcref")>(Arg aArgs[])
4030 {
4031     otError error = OT_ERROR_NONE;
4032 
4033     /**
4034      * @cli pskcref
4035      * @code
4036      * pskcref
4037      * 0x80000000
4038      * Done
4039      * @endcode
4040      * @par api_copy
4041      * #otThreadGetPskcRef
4042      */
4043     if (aArgs[0].IsEmpty())
4044     {
4045         OutputLine("0x%08lx", ToUlong(otThreadGetPskcRef(GetInstancePtr())));
4046     }
4047     else
4048     {
4049         otPskcRef pskcRef;
4050 
4051         /**
4052          * @cli pskcref (set)
4053          * @code
4054          * pskc 0x20017
4055          * Done
4056          * @endcode
4057          * @cparam pskc @ca{keyref}
4058          * @par api_copy
4059          * #otThreadSetPskcRef
4060          */
4061         if (aArgs[1].IsEmpty())
4062         {
4063             SuccessOrExit(error = aArgs[0].ParseAsUint32(pskcRef));
4064         }
4065         else
4066         {
4067             ExitNow(error = OT_ERROR_INVALID_ARGS);
4068         }
4069 
4070         error = otThreadSetPskcRef(GetInstancePtr(), pskcRef);
4071     }
4072 
4073 exit:
4074     return error;
4075 }
4076 #endif
4077 #endif // OPENTHREAD_FTD
4078 
4079 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
4080 /**
4081  * @cli mleadvimax
4082  * @code
4083  * mleadvimax
4084  * 12000
4085  * Done
4086  * @endcode
4087  * @par api_copy
4088  * #otThreadGetAdvertisementTrickleIntervalMax
4089  */
Process(Arg aArgs[])4090 template <> otError Interpreter::Process<Cmd("mleadvimax")>(Arg aArgs[])
4091 {
4092     return ProcessGet(aArgs, otThreadGetAdvertisementTrickleIntervalMax);
4093 }
4094 #endif
4095 
4096 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
4097 /**
4098  * @cli mliid
4099  * @code
4100  * mliid 1122334455667788
4101  * Done
4102  * @endcode
4103  * @par
4104  * It must be used before Thread stack is enabled.
4105  * @par
4106  * Only for testing/reference device.
4107  * @par api_copy
4108  * #otIp6SetMeshLocalIid
4109  * @cparam mliid @ca{iid}
4110  */
Process(Arg aArgs[])4111 template <> otError Interpreter::Process<Cmd("mliid")>(Arg aArgs[])
4112 {
4113     otError                  error = OT_ERROR_NONE;
4114     otIp6InterfaceIdentifier iid;
4115 
4116     VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
4117 
4118     SuccessOrExit(error = aArgs[0].ParseAsHexString(iid.mFields.m8));
4119     SuccessOrExit(error = otIp6SetMeshLocalIid(GetInstancePtr(), &iid));
4120 
4121 exit:
4122     return error;
4123 }
4124 #endif
4125 
4126 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
4127 /**
4128  * @cli mlr reg
4129  * @code
4130  * mlr reg ff04::1
4131  * status 0, 0 failed
4132  * Done
4133  * @endcode
4134  * @code
4135  * mlr reg ff04::1 ff04::2 ff02::1
4136  * status 2, 1 failed
4137  * ff02:0:0:0:0:0:0:1
4138  * Done
4139  * @endcode
4140  * @code
4141  * mlr reg ff04::1 ff04::2 1000
4142  * status 0, 0 failed
4143  * Done
4144  * @endcode
4145  * @code
4146  * mlr reg ff04::1 ff04::2 0
4147  * status 0, 0 failed
4148  * Done
4149  * @endcode
4150  * @par
4151  * Omit timeout to use the default MLR timeout on the Primary Backbone Router.
4152  * @par
4153  * Use timeout = 0 to deregister Multicast Listeners.
4154  * @par api_copy
4155  * #otIp6RegisterMulticastListeners
4156  * @cparam mlr reg @ca{ipaddr} [@ca{timeout}]
4157  */
Process(Arg aArgs[])4158 template <> otError Interpreter::Process<Cmd("mlr")>(Arg aArgs[])
4159 {
4160     otError error = OT_ERROR_INVALID_COMMAND;
4161 
4162     if (aArgs[0] == "reg")
4163     {
4164         otIp6Address addresses[OT_IP6_MAX_MLR_ADDRESSES];
4165         uint32_t     timeout;
4166         bool         hasTimeout   = false;
4167         uint8_t      numAddresses = 0;
4168 
4169         aArgs++;
4170 
4171         while (aArgs->ParseAsIp6Address(addresses[numAddresses]) == OT_ERROR_NONE)
4172         {
4173             aArgs++;
4174             numAddresses++;
4175 
4176             if (numAddresses == OT_ARRAY_LENGTH(addresses))
4177             {
4178                 break;
4179             }
4180         }
4181 
4182         if (aArgs->ParseAsUint32(timeout) == OT_ERROR_NONE)
4183         {
4184             aArgs++;
4185             hasTimeout = true;
4186         }
4187 
4188         VerifyOrExit(aArgs->IsEmpty() && (numAddresses > 0), error = OT_ERROR_INVALID_ARGS);
4189 
4190         SuccessOrExit(error = otIp6RegisterMulticastListeners(GetInstancePtr(), addresses, numAddresses,
4191                                                               hasTimeout ? &timeout : nullptr,
4192                                                               Interpreter::HandleMlrRegResult, this));
4193 
4194         error = OT_ERROR_PENDING;
4195     }
4196 
4197 exit:
4198     return error;
4199 }
4200 
HandleMlrRegResult(void * aContext,otError aError,uint8_t aMlrStatus,const otIp6Address * aFailedAddresses,uint8_t aFailedAddressNum)4201 void Interpreter::HandleMlrRegResult(void               *aContext,
4202                                      otError             aError,
4203                                      uint8_t             aMlrStatus,
4204                                      const otIp6Address *aFailedAddresses,
4205                                      uint8_t             aFailedAddressNum)
4206 {
4207     static_cast<Interpreter *>(aContext)->HandleMlrRegResult(aError, aMlrStatus, aFailedAddresses, aFailedAddressNum);
4208 }
4209 
HandleMlrRegResult(otError aError,uint8_t aMlrStatus,const otIp6Address * aFailedAddresses,uint8_t aFailedAddressNum)4210 void Interpreter::HandleMlrRegResult(otError             aError,
4211                                      uint8_t             aMlrStatus,
4212                                      const otIp6Address *aFailedAddresses,
4213                                      uint8_t             aFailedAddressNum)
4214 {
4215     if (aError == OT_ERROR_NONE)
4216     {
4217         OutputLine("status %d, %d failed", aMlrStatus, aFailedAddressNum);
4218 
4219         for (uint8_t i = 0; i < aFailedAddressNum; i++)
4220         {
4221             OutputIp6AddressLine(aFailedAddresses[i]);
4222         }
4223     }
4224 
4225     OutputResult(aError);
4226 }
4227 
4228 #endif // (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
4229 
Process(Arg aArgs[])4230 template <> otError Interpreter::Process<Cmd("mode")>(Arg aArgs[])
4231 {
4232     otError          error = OT_ERROR_NONE;
4233     otLinkModeConfig linkMode;
4234 
4235     ClearAllBytes(linkMode);
4236 
4237     if (aArgs[0].IsEmpty())
4238     {
4239         char linkModeString[kLinkModeStringSize];
4240 
4241         OutputLine("%s", LinkModeToString(otThreadGetLinkMode(GetInstancePtr()), linkModeString));
4242         ExitNow();
4243     }
4244 
4245     /**
4246      * @cli mode (get,set)
4247      * @code
4248      * mode rdn
4249      * Done
4250      * @endcode
4251      * @code
4252      * mode -
4253      * Done
4254      * @endcode
4255      * @par api_copy
4256      * #otThreadSetLinkMode
4257      * @cparam mode [@ca{rdn}]
4258      * - `-`: no flags set (rx-off-when-idle, minimal Thread device, stable network data)
4259      * - `r`: rx-on-when-idle
4260      * - `d`: Full Thread Device
4261      * - `n`: Full Network Data
4262      */
4263     if (aArgs[0] != "-")
4264     {
4265         for (const char *arg = aArgs[0].GetCString(); *arg != '\0'; arg++)
4266         {
4267             switch (*arg)
4268             {
4269             case 'r':
4270                 linkMode.mRxOnWhenIdle = true;
4271                 break;
4272 
4273             case 'd':
4274                 linkMode.mDeviceType = true;
4275                 break;
4276 
4277             case 'n':
4278                 linkMode.mNetworkData = true;
4279                 break;
4280 
4281             default:
4282                 ExitNow(error = OT_ERROR_INVALID_ARGS);
4283             }
4284         }
4285     }
4286 
4287     error = otThreadSetLinkMode(GetInstancePtr(), linkMode);
4288 
4289 exit:
4290     return error;
4291 }
4292 
4293 /**
4294  * @cli multiradio
4295  * @code
4296  * multiradio
4297  * [15.4, TREL]
4298  * Done
4299  * @endcode
4300  * @par
4301  * Get the list of supported radio links by the device.
4302  * @par
4303  * This command is always available, even when only a single radio is supported by the device.
4304  */
Process(Arg aArgs[])4305 template <> otError Interpreter::Process<Cmd("multiradio")>(Arg aArgs[])
4306 {
4307     otError error = OT_ERROR_NONE;
4308 
4309     OT_UNUSED_VARIABLE(aArgs);
4310 
4311     if (aArgs[0].IsEmpty())
4312     {
4313         bool isFirst = true;
4314 
4315         OutputFormat("[");
4316 #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
4317         OutputFormat("15.4");
4318         isFirst = false;
4319 #endif
4320 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
4321         OutputFormat("%sTREL", isFirst ? "" : ", ");
4322 #endif
4323         OutputLine("]");
4324 
4325         OT_UNUSED_VARIABLE(isFirst);
4326     }
4327 #if OPENTHREAD_CONFIG_MULTI_RADIO
4328     else if (aArgs[0] == "neighbor")
4329     {
4330         otMultiRadioNeighborInfo multiRadioInfo;
4331 
4332         /**
4333          * @cli multiradio neighbor list
4334          * @code
4335          * multiradio neighbor list
4336          * ExtAddr:3a65bc38dbe4a5be, RLOC16:0xcc00, Radios:[15.4(255), TREL(255)]
4337          * ExtAddr:17df23452ee4a4be, RLOC16:0x1300, Radios:[15.4(255)]
4338          * Done
4339          * @endcode
4340          * @par api_copy
4341          * #otMultiRadioGetNeighborInfo
4342          */
4343         if (aArgs[1] == "list")
4344         {
4345             otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT;
4346             otNeighborInfo         neighInfo;
4347 
4348             while (otThreadGetNextNeighborInfo(GetInstancePtr(), &iterator, &neighInfo) == OT_ERROR_NONE)
4349             {
4350                 if (otMultiRadioGetNeighborInfo(GetInstancePtr(), &neighInfo.mExtAddress, &multiRadioInfo) !=
4351                     OT_ERROR_NONE)
4352                 {
4353                     continue;
4354                 }
4355 
4356                 OutputFormat("ExtAddr:");
4357                 OutputExtAddress(neighInfo.mExtAddress);
4358                 OutputFormat(", RLOC16:0x%04x, Radios:", neighInfo.mRloc16);
4359                 OutputMultiRadioInfo(multiRadioInfo);
4360             }
4361         }
4362         else
4363         {
4364             /**
4365              * @cli multiradio neighbor
4366              * @code
4367              * multiradio neighbor 3a65bc38dbe4a5be
4368              * [15.4(255), TREL(255)]
4369              * Done
4370              * @endcode
4371              * @par api_copy
4372              * #otMultiRadioGetNeighborInfo
4373              * @cparam multiradio neighbor @ca{ext-address}
4374              */
4375             otExtAddress extAddress;
4376 
4377             SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddress.m8));
4378             SuccessOrExit(error = otMultiRadioGetNeighborInfo(GetInstancePtr(), &extAddress, &multiRadioInfo));
4379             OutputMultiRadioInfo(multiRadioInfo);
4380         }
4381     }
4382 #endif // OPENTHREAD_CONFIG_MULTI_RADIO
4383     else
4384     {
4385         ExitNow(error = OT_ERROR_INVALID_COMMAND);
4386     }
4387 
4388 exit:
4389     return error;
4390 }
4391 
4392 #if OPENTHREAD_CONFIG_MULTI_RADIO
OutputMultiRadioInfo(const otMultiRadioNeighborInfo & aMultiRadioInfo)4393 void Interpreter::OutputMultiRadioInfo(const otMultiRadioNeighborInfo &aMultiRadioInfo)
4394 {
4395     bool isFirst = true;
4396 
4397     OutputFormat("[");
4398 
4399     if (aMultiRadioInfo.mSupportsIeee802154)
4400     {
4401         OutputFormat("15.4(%u)", aMultiRadioInfo.mIeee802154Info.mPreference);
4402         isFirst = false;
4403     }
4404 
4405     if (aMultiRadioInfo.mSupportsTrelUdp6)
4406     {
4407         OutputFormat("%sTREL(%u)", isFirst ? "" : ", ", aMultiRadioInfo.mTrelUdp6Info.mPreference);
4408     }
4409 
4410     OutputLine("]");
4411 }
4412 #endif // OPENTHREAD_CONFIG_MULTI_RADIO
4413 
4414 #if OPENTHREAD_FTD
Process(Arg aArgs[])4415 template <> otError Interpreter::Process<Cmd("neighbor")>(Arg aArgs[])
4416 {
4417     otError                error = OT_ERROR_NONE;
4418     otNeighborInfo         neighborInfo;
4419     bool                   isTable;
4420     otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT;
4421 
4422     isTable = (aArgs[0] == "table");
4423 
4424     if (isTable || (aArgs[0] == "list"))
4425     {
4426         if (isTable)
4427         {
4428             static const char *const kNeighborTableTitles[] = {
4429                 "Role", "RLOC16", "Age", "Avg RSSI", "Last RSSI", "R", "D", "N", "Extended MAC", "Version",
4430             };
4431 
4432             static const uint8_t kNeighborTableColumnWidths[] = {
4433                 6, 8, 5, 10, 11, 1, 1, 1, 18, 9,
4434             };
4435 
4436             OutputTableHeader(kNeighborTableTitles, kNeighborTableColumnWidths);
4437         }
4438 
4439         while (otThreadGetNextNeighborInfo(GetInstancePtr(), &iterator, &neighborInfo) == OT_ERROR_NONE)
4440         {
4441             /**
4442              * @cli neighbor table
4443              * @code
4444              * neighbor table
4445              * | Role | RLOC16 | Age | Avg RSSI | Last RSSI |R|D|N| Extended MAC     |
4446              * +------+--------+-----+----------+-----------+-+-+-+------------------+
4447              * |   C  | 0xcc01 |  96 |      -46 |       -46 |1|1|1| 1eb9ba8a6522636b |
4448              * |   R  | 0xc800 |   2 |      -29 |       -29 |1|1|1| 9a91556102c39ddb |
4449              * |   R  | 0xf000 |   3 |      -28 |       -28 |1|1|1| 0ad7ed6beaa6016d |
4450              * Done
4451              * @endcode
4452              * @par
4453              * Prints information in table format about all neighbors.
4454              * @par
4455              * For `Role`, the only possible values for this table are `C` (Child) or `R` (Router).
4456              * @par
4457              * The following columns provide information about the device mode of neighbors.
4458              * Each column has a value of `0` (off) or `1` (on).
4459              * - `R`: RX on when idle
4460              * - `D`: Full Thread device
4461              * - `N`: Full network data
4462              * @sa otThreadGetNextNeighborInfo
4463              */
4464             if (isTable)
4465             {
4466                 OutputFormat("| %3c  ", neighborInfo.mIsChild ? 'C' : 'R');
4467                 OutputFormat("| 0x%04x ", neighborInfo.mRloc16);
4468                 OutputFormat("| %3lu ", ToUlong(neighborInfo.mAge));
4469                 OutputFormat("| %8d ", neighborInfo.mAverageRssi);
4470                 OutputFormat("| %9d ", neighborInfo.mLastRssi);
4471                 OutputFormat("|%1d", neighborInfo.mRxOnWhenIdle);
4472                 OutputFormat("|%1d", neighborInfo.mFullThreadDevice);
4473                 OutputFormat("|%1d", neighborInfo.mFullNetworkData);
4474                 OutputFormat("| ");
4475                 OutputExtAddress(neighborInfo.mExtAddress);
4476                 OutputLine(" | %7d |", neighborInfo.mVersion);
4477             }
4478             /**
4479              * @cli neighbor list
4480              * @code
4481              * neighbor list
4482              * 0xcc01 0xc800 0xf000
4483              * Done
4484              * @endcode
4485              * @par
4486              * Lists the RLOC16 of each neighbor.
4487              */
4488             else
4489             {
4490                 OutputFormat("0x%04x ", neighborInfo.mRloc16);
4491             }
4492         }
4493 
4494         OutputNewLine();
4495     }
4496     /**
4497      * @cli neighbor linkquality
4498      * @code
4499      * neighbor linkquality
4500      * | RLOC16 | Extended MAC     | Frame Error | Msg Error | Avg RSS | Last RSS | Age   |
4501      * +--------+------------------+-------------+-----------+---------+----------+-------+
4502      * | 0xe800 | 9e2fa4e1b84f92db |      0.00 % |    0.00 % |     -46 |      -48 |     1 |
4503      * | 0xc001 | 0ad7ed6beaa6016d |      4.67 % |    0.08 % |     -68 |      -72 |    10 |
4504      * Done
4505      * @endcode
4506      * @par
4507      * Prints link quality information about all neighbors.
4508      */
4509     else if (aArgs[0] == "linkquality")
4510     {
4511         static const char *const kLinkQualityTableTitles[] = {
4512             "RLOC16", "Extended MAC", "Frame Error", "Msg Error", "Avg RSS", "Last RSS", "Age",
4513         };
4514 
4515         static const uint8_t kLinkQualityTableColumnWidths[] = {
4516             8, 18, 13, 11, 9, 10, 7,
4517         };
4518 
4519         OutputTableHeader(kLinkQualityTableTitles, kLinkQualityTableColumnWidths);
4520 
4521         while (otThreadGetNextNeighborInfo(GetInstancePtr(), &iterator, &neighborInfo) == OT_ERROR_NONE)
4522         {
4523             PercentageStringBuffer stringBuffer;
4524 
4525             OutputFormat("| 0x%04x | ", neighborInfo.mRloc16);
4526             OutputExtAddress(neighborInfo.mExtAddress);
4527             OutputFormat(" | %9s %% ", PercentageToString(neighborInfo.mFrameErrorRate, stringBuffer));
4528             OutputFormat("| %7s %% ", PercentageToString(neighborInfo.mMessageErrorRate, stringBuffer));
4529             OutputFormat("| %7d ", neighborInfo.mAverageRssi);
4530             OutputFormat("| %8d ", neighborInfo.mLastRssi);
4531             OutputLine("| %5lu |", ToUlong(neighborInfo.mAge));
4532         }
4533     }
4534 #if OPENTHREAD_CONFIG_UPTIME_ENABLE
4535     /**
4536      * @cli neighbor conntime
4537      * @code
4538      * neighbor conntime
4539      * | RLOC16 | Extended MAC     | Last Heard (Age) | Connection Time  |
4540      * +--------+------------------+------------------+------------------+
4541      * | 0x8401 | 1a28be396a14a318 |         00:00:13 |         00:07:59 |
4542      * | 0x5c00 | 723ebf0d9eba3264 |         00:00:03 |         00:11:27 |
4543      * | 0xe800 | ce53628a1e3f5b3c |         00:00:02 |         00:00:15 |
4544      * Done
4545      * @endcode
4546      * @par
4547      * Prints the connection time and age of neighbors. Information per neighbor:
4548      * - RLOC16
4549      * - Extended MAC
4550      * - Last Heard (Age): Number of seconds since last heard from neighbor.
4551      * - Connection Time: Number of seconds since link establishment with neighbor.
4552      * Duration intervals are formatted as `{hh}:{mm}:{ss}` for hours, minutes, and seconds if the duration is less
4553      * than one day. If the duration is longer than one day, the format is `{dd}d.{hh}:{mm}:{ss}`.
4554      * @csa{neighbor conntime list}
4555      */
4556     else if (aArgs[0] == "conntime")
4557     {
4558         /**
4559          * @cli neighbor conntime list
4560          * @code
4561          * neighbor conntime list
4562          * 0x8401 1a28be396a14a318 age:63 conn-time:644
4563          * 0x5c00 723ebf0d9eba3264 age:23 conn-time:852
4564          * 0xe800 ce53628a1e3f5b3c age:23 conn-time:180
4565          * Done
4566          * @endcode
4567          * @par
4568          * Prints the connection time and age of neighbors.
4569          * This command is similar to `neighbor conntime`, but it displays the information in a list format. The age
4570          * and connection time are both displayed in seconds.
4571          * @csa{neighbor conntime}
4572          */
4573         if (aArgs[1] == "list")
4574         {
4575             isTable = false;
4576         }
4577         else
4578         {
4579             static const char *const kConnTimeTableTitles[] = {
4580                 "RLOC16",
4581                 "Extended MAC",
4582                 "Last Heard (Age)",
4583                 "Connection Time",
4584             };
4585 
4586             static const uint8_t kConnTimeTableColumnWidths[] = {8, 18, 18, 18};
4587 
4588             isTable = true;
4589             OutputTableHeader(kConnTimeTableTitles, kConnTimeTableColumnWidths);
4590         }
4591 
4592         while (otThreadGetNextNeighborInfo(GetInstancePtr(), &iterator, &neighborInfo) == OT_ERROR_NONE)
4593         {
4594             if (isTable)
4595             {
4596                 char string[OT_DURATION_STRING_SIZE];
4597 
4598                 OutputFormat("| 0x%04x | ", neighborInfo.mRloc16);
4599                 OutputExtAddress(neighborInfo.mExtAddress);
4600                 otConvertDurationInSecondsToString(neighborInfo.mAge, string, sizeof(string));
4601                 OutputFormat(" | %16s", string);
4602                 otConvertDurationInSecondsToString(neighborInfo.mConnectionTime, string, sizeof(string));
4603                 OutputLine(" | %16s |", string);
4604             }
4605             else
4606             {
4607                 OutputFormat("0x%04x ", neighborInfo.mRloc16);
4608                 OutputExtAddress(neighborInfo.mExtAddress);
4609                 OutputLine(" age:%lu conn-time:%lu", ToUlong(neighborInfo.mAge), ToUlong(neighborInfo.mConnectionTime));
4610             }
4611         }
4612     }
4613 #endif
4614     else
4615     {
4616         error = OT_ERROR_INVALID_ARGS;
4617     }
4618 
4619     return error;
4620 }
4621 #endif // OPENTHREAD_FTD
4622 
4623 /**
4624  * @cli netstat
4625  * @code
4626  * netstat
4627  * | Local Address                                   | Peer Address                                    |
4628  * +-------------------------------------------------+-------------------------------------------------+
4629  * | [0:0:0:0:0:0:0:0]:49153                         | [0:0:0:0:0:0:0:0]:0                             |
4630  * | [0:0:0:0:0:0:0:0]:49152                         | [0:0:0:0:0:0:0:0]:0                             |
4631  * | [0:0:0:0:0:0:0:0]:61631                         | [0:0:0:0:0:0:0:0]:0                             |
4632  * | [0:0:0:0:0:0:0:0]:19788                         | [0:0:0:0:0:0:0:0]:0                             |
4633  * Done
4634  * @endcode
4635  * @par api_copy
4636  * #otUdpGetSockets
4637  */
Process(Arg aArgs[])4638 template <> otError Interpreter::Process<Cmd("netstat")>(Arg aArgs[])
4639 {
4640     OT_UNUSED_VARIABLE(aArgs);
4641 
4642     static const char *const kNetstatTableTitles[]       = {"Local Address", "Peer Address"};
4643     static const uint8_t     kNetstatTableColumnWidths[] = {49, 49};
4644 
4645     char string[OT_IP6_SOCK_ADDR_STRING_SIZE];
4646 
4647     OutputTableHeader(kNetstatTableTitles, kNetstatTableColumnWidths);
4648 
4649     for (const otUdpSocket *socket = otUdpGetSockets(GetInstancePtr()); socket != nullptr; socket = socket->mNext)
4650     {
4651         otIp6SockAddrToString(&socket->mSockName, string, sizeof(string));
4652         OutputFormat("| %-47s ", string);
4653         otIp6SockAddrToString(&socket->mPeerName, string, sizeof(string));
4654         OutputLine("| %-47s |", string);
4655     }
4656 
4657     return OT_ERROR_NONE;
4658 }
4659 
4660 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
Process(Arg aArgs[])4661 template <> otError Interpreter::Process<Cmd("service")>(Arg aArgs[])
4662 {
4663     otError         error = OT_ERROR_INVALID_COMMAND;
4664     otServiceConfig cfg;
4665 
4666     if (aArgs[0].IsEmpty())
4667     {
4668         otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
4669         otServiceConfig       config;
4670 
4671         while (otServerGetNextService(GetInstancePtr(), &iterator, &config) == OT_ERROR_NONE)
4672         {
4673             mNetworkData.OutputService(config);
4674         }
4675 
4676         error = OT_ERROR_NONE;
4677     }
4678     else
4679     {
4680         uint16_t length;
4681 
4682         SuccessOrExit(error = aArgs[1].ParseAsUint32(cfg.mEnterpriseNumber));
4683 
4684         length = sizeof(cfg.mServiceData);
4685         SuccessOrExit(error = aArgs[2].ParseAsHexString(length, cfg.mServiceData));
4686         VerifyOrExit(length > 0, error = OT_ERROR_INVALID_ARGS);
4687         cfg.mServiceDataLength = static_cast<uint8_t>(length);
4688 
4689         /**
4690          * @cli service add
4691          * @code
4692          * service add 44970 112233 aabbcc
4693          * Done
4694          * @endcode
4695          * @code
4696          * netdata register
4697          * Done
4698          * @endcode
4699          * @cparam service add @ca{enterpriseNumber} @ca{serviceData} @ca{serverData}
4700          * @par
4701          * Adds service to the network data.
4702          * @par
4703          * - enterpriseNumber: IANA enterprise number
4704          * - serviceData: Hex-encoded binary service data
4705          * - serverData: Hex-encoded binary server data
4706          * @par
4707          * Note: For each change in service registration to take effect, run
4708          * the `netdata register` command after running the `service add` command to notify the leader.
4709          * @sa otServerAddService
4710          * @csa{netdata register}
4711          */
4712         if (aArgs[0] == "add")
4713         {
4714             length = sizeof(cfg.mServerConfig.mServerData);
4715             SuccessOrExit(error = aArgs[3].ParseAsHexString(length, cfg.mServerConfig.mServerData));
4716             VerifyOrExit(length > 0, error = OT_ERROR_INVALID_ARGS);
4717             cfg.mServerConfig.mServerDataLength = static_cast<uint8_t>(length);
4718 
4719             cfg.mServerConfig.mStable = true;
4720 
4721             error = otServerAddService(GetInstancePtr(), &cfg);
4722         }
4723         /**
4724          * @cli service remove
4725          * @code
4726          * service remove 44970 112233
4727          * Done
4728          * @endcode
4729          * @code
4730          * netdata register
4731          * Done
4732          * @endcode
4733          * @cparam service remove @ca{enterpriseNumber} @ca{serviceData}
4734          * @par
4735          * Removes service from the network data.
4736          * @par
4737          * - enterpriseNumber: IANA enterprise number
4738          * - serviceData: Hex-encoded binary service data
4739          * @par
4740          * Note: For each change in service registration to take effect, run
4741          * the `netdata register` command after running the `service remove` command to notify the leader.
4742          * @sa otServerRemoveService
4743          * @csa{netdata register}
4744          */
4745         else if (aArgs[0] == "remove")
4746         {
4747             error = otServerRemoveService(GetInstancePtr(), cfg.mEnterpriseNumber, cfg.mServiceData,
4748                                           cfg.mServiceDataLength);
4749         }
4750     }
4751 
4752 exit:
4753     return error;
4754 }
4755 #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
4756 
Process(Arg aArgs[])4757 template <> otError Interpreter::Process<Cmd("netdata")>(Arg aArgs[]) { return mNetworkData.Process(aArgs); }
4758 
4759 #if OPENTHREAD_FTD
4760 /**
4761  * @cli networkidtimeout (get,set)
4762  * @code
4763  * networkidtimeout 120
4764  * Done
4765  * @endcode
4766  * @code
4767  * networkidtimeout
4768  * 120
4769  * Done
4770  * @endcode
4771  * @cparam networkidtimeout [@ca{timeout}]
4772  * Use the optional `timeout` argument to set the value of the `NETWORK_ID_TIMEOUT` parameter.
4773  * @par
4774  * Gets or sets the `NETWORK_ID_TIMEOUT` parameter.
4775  * @note This command is reserved for testing and demo purposes only.
4776  * Changing settings with this API will render a production application
4777  * non-compliant with the Thread Specification.
4778  * @sa otThreadGetNetworkIdTimeout
4779  * @sa otThreadSetNetworkIdTimeout
4780  */
Process(Arg aArgs[])4781 template <> otError Interpreter::Process<Cmd("networkidtimeout")>(Arg aArgs[])
4782 {
4783     return ProcessGetSet(aArgs, otThreadGetNetworkIdTimeout, otThreadSetNetworkIdTimeout);
4784 }
4785 #endif
4786 
Process(Arg aArgs[])4787 template <> otError Interpreter::Process<Cmd("networkkey")>(Arg aArgs[])
4788 {
4789     otError error = OT_ERROR_NONE;
4790 
4791     /**
4792      * @cli networkkey
4793      * @code
4794      * networkkey
4795      * 00112233445566778899aabbccddeeff
4796      * Done
4797      * @endcode
4798      * @par api_copy
4799      * #otThreadGetNetworkKey
4800      */
4801     if (aArgs[0].IsEmpty())
4802     {
4803         otNetworkKey networkKey;
4804 
4805         otThreadGetNetworkKey(GetInstancePtr(), &networkKey);
4806         OutputBytesLine(networkKey.m8);
4807     }
4808     /**
4809      * @cli networkkey (key)
4810      * @code
4811      * networkkey 00112233445566778899aabbccddeeff
4812      * Done
4813      * @endcode
4814      * @par api_copy
4815      * #otThreadSetNetworkKey
4816      * @cparam networkkey @ca{key}
4817      */
4818     else
4819     {
4820         otNetworkKey key;
4821 
4822         SuccessOrExit(error = aArgs[0].ParseAsHexString(key.m8));
4823         SuccessOrExit(error = otThreadSetNetworkKey(GetInstancePtr(), &key));
4824     }
4825 
4826 exit:
4827     return error;
4828 }
4829 
4830 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
Process(Arg aArgs[])4831 template <> otError Interpreter::Process<Cmd("networkkeyref")>(Arg aArgs[])
4832 {
4833     otError error = OT_ERROR_NONE;
4834 
4835     if (aArgs[0].IsEmpty())
4836     {
4837         OutputLine("0x%08lx", ToUlong(otThreadGetNetworkKeyRef(GetInstancePtr())));
4838     }
4839     else
4840     {
4841         otNetworkKeyRef keyRef;
4842 
4843         SuccessOrExit(error = aArgs[0].ParseAsUint32(keyRef));
4844         SuccessOrExit(error = otThreadSetNetworkKeyRef(GetInstancePtr(), keyRef));
4845     }
4846 
4847 exit:
4848     return error;
4849 }
4850 #endif
4851 
4852 /**
4853  * @cli networkname
4854  * @code
4855  * networkname
4856  * OpenThread
4857  * Done
4858  * @endcode
4859  * @par api_copy
4860  * #otThreadGetNetworkName
4861  */
Process(Arg aArgs[])4862 template <> otError Interpreter::Process<Cmd("networkname")>(Arg aArgs[])
4863 {
4864     /**
4865      * @cli networkname (name)
4866      * @code
4867      * networkname OpenThread
4868      * Done
4869      * @endcode
4870      * @par api_copy
4871      * #otThreadSetNetworkName
4872      * @cparam networkname @ca{name}
4873      * @par
4874      * Note: The current commissioning credential becomes stale after changing this value. Use `pskc` to reset.
4875      */
4876     return ProcessGetSet(aArgs, otThreadGetNetworkName, otThreadSetNetworkName);
4877 }
4878 
4879 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
Process(Arg aArgs[])4880 template <> otError Interpreter::Process<Cmd("networktime")>(Arg aArgs[])
4881 {
4882     otError error = OT_ERROR_NONE;
4883 
4884     /**
4885      * @cli networktime
4886      * @code
4887      * networktime
4888      * Network Time:     21084154us (synchronized)
4889      * Time Sync Period: 100s
4890      * XTAL Threshold:   300ppm
4891      * Done
4892      * @endcode
4893      * @par
4894      * Gets the Thread network time and the time sync parameters.
4895      * @sa otNetworkTimeGet
4896      * @sa otNetworkTimeGetSyncPeriod
4897      * @sa otNetworkTimeGetXtalThreshold
4898      */
4899     if (aArgs[0].IsEmpty())
4900     {
4901         uint64_t            time;
4902         otNetworkTimeStatus networkTimeStatus;
4903 
4904         networkTimeStatus = otNetworkTimeGet(GetInstancePtr(), &time);
4905 
4906         OutputFormat("Network Time:     ");
4907         OutputUint64(time);
4908         OutputFormat("us");
4909 
4910         switch (networkTimeStatus)
4911         {
4912         case OT_NETWORK_TIME_UNSYNCHRONIZED:
4913             OutputLine(" (unsynchronized)");
4914             break;
4915 
4916         case OT_NETWORK_TIME_RESYNC_NEEDED:
4917             OutputLine(" (resync needed)");
4918             break;
4919 
4920         case OT_NETWORK_TIME_SYNCHRONIZED:
4921             OutputLine(" (synchronized)");
4922             break;
4923 
4924         default:
4925             break;
4926         }
4927 
4928         OutputLine("Time Sync Period: %us", otNetworkTimeGetSyncPeriod(GetInstancePtr()));
4929         OutputLine("XTAL Threshold:   %uppm", otNetworkTimeGetXtalThreshold(GetInstancePtr()));
4930     }
4931     /**
4932      * @cli networktime (set)
4933      * @code
4934      * networktime 100 300
4935      * Done
4936      * @endcode
4937      * @cparam networktime @ca{timesyncperiod} @ca{xtalthreshold}
4938      * @par
4939      * Sets the time sync parameters.
4940      * *   `timesyncperiod`: The time synchronization period, in seconds.
4941      * *   `xtalthreshold`: The XTAL accuracy threshold for a device to become Router-Capable device, in PPM.
4942      * @sa otNetworkTimeSetSyncPeriod
4943      * @sa otNetworkTimeSetXtalThreshold
4944      */
4945     else
4946     {
4947         uint16_t period;
4948         uint16_t xtalThreshold;
4949 
4950         SuccessOrExit(error = aArgs[0].ParseAsUint16(period));
4951         SuccessOrExit(error = aArgs[1].ParseAsUint16(xtalThreshold));
4952         SuccessOrExit(error = otNetworkTimeSetSyncPeriod(GetInstancePtr(), period));
4953         SuccessOrExit(error = otNetworkTimeSetXtalThreshold(GetInstancePtr(), xtalThreshold));
4954     }
4955 
4956 exit:
4957     return error;
4958 }
4959 #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
4960 
4961 #if OPENTHREAD_FTD
Process(Arg aArgs[])4962 template <> otError Interpreter::Process<Cmd("nexthop")>(Arg aArgs[])
4963 {
4964     constexpr uint8_t  kRouterIdOffset = 10; // Bit offset of Router ID in RLOC16
4965     constexpr uint16_t kInvalidRloc16  = 0xfffe;
4966 
4967     otError  error = OT_ERROR_NONE;
4968     uint16_t destRloc16;
4969     uint16_t nextHopRloc16;
4970     uint8_t  pathCost;
4971 
4972     /**
4973      * @cli nexthop
4974      * @code
4975      * nexthop
4976      * | ID   |NxtHop| Cost |
4977      * +------+------+------+
4978      * |    9 |    9 |    1 |
4979      * |   25 |   25 |    0 |
4980      * |   30 |   30 |    1 |
4981      * |   46 |    - |    - |
4982      * |   50 |   30 |    3 |
4983      * |   60 |   30 |    2 |
4984      * Done
4985      * @endcode
4986      * @par
4987      * Output table of allocated Router IDs and current next hop and path
4988      * cost for each router.
4989      * @sa otThreadGetNextHopAndPathCost
4990      * @sa otThreadIsRouterIdAllocated
4991      */
4992     if (aArgs[0].IsEmpty())
4993     {
4994         static const char *const kNextHopTableTitles[] = {
4995             "ID",
4996             "NxtHop",
4997             "Cost",
4998         };
4999 
5000         static const uint8_t kNextHopTableColumnWidths[] = {
5001             6,
5002             6,
5003             6,
5004         };
5005 
5006         OutputTableHeader(kNextHopTableTitles, kNextHopTableColumnWidths);
5007 
5008         for (uint8_t routerId = 0; routerId <= OT_NETWORK_MAX_ROUTER_ID; routerId++)
5009         {
5010             if (!otThreadIsRouterIdAllocated(GetInstancePtr(), routerId))
5011             {
5012                 continue;
5013             }
5014 
5015             destRloc16 = routerId;
5016             destRloc16 <<= kRouterIdOffset;
5017 
5018             otThreadGetNextHopAndPathCost(GetInstancePtr(), destRloc16, &nextHopRloc16, &pathCost);
5019 
5020             OutputFormat("| %4u | ", routerId);
5021 
5022             if (nextHopRloc16 != kInvalidRloc16)
5023             {
5024                 OutputLine("%4u | %4u |", nextHopRloc16 >> kRouterIdOffset, pathCost);
5025             }
5026             else
5027             {
5028                 OutputLine("%4s | %4s |", "-", "-");
5029             }
5030         }
5031     }
5032     /**
5033      * @cli nexthop (get)
5034      * @code
5035      * nexthop 0xc000
5036      * 0xc000 cost:0
5037      * Done
5038      * @endcode
5039      * @code
5040      * nexthop 0x8001
5041      * 0x2000 cost:3
5042      * Done
5043      * @endcode
5044      * @cparam nexthop @ca{rloc16}
5045      * @par api_copy
5046      * #otThreadGetNextHopAndPathCost
5047      */
5048     else
5049     {
5050         SuccessOrExit(error = aArgs[0].ParseAsUint16(destRloc16));
5051         otThreadGetNextHopAndPathCost(GetInstancePtr(), destRloc16, &nextHopRloc16, &pathCost);
5052         OutputLine("0x%04x cost:%u", nextHopRloc16, pathCost);
5053     }
5054 
5055 exit:
5056     return error;
5057 }
5058 
5059 #if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE
5060 
Process(Arg aArgs[])5061 template <> otError Interpreter::Process<Cmd("meshdiag")>(Arg aArgs[])
5062 {
5063     otError error = OT_ERROR_NONE;
5064 
5065     /**
5066      * @cli meshdiag topology
5067      * @code
5068      * meshdiag topology
5069      * id:02 rloc16:0x0800 ext-addr:8aa57d2c603fe16c ver:4 - me - leader
5070      *    3-links:{ 46 }
5071      * id:46 rloc16:0xb800 ext-addr:fe109d277e0175cc ver:4
5072      *    3-links:{ 02 51 57 }
5073      * id:33 rloc16:0x8400 ext-addr:d2e511a146b9e54d ver:4
5074      *    3-links:{ 51 57 }
5075      * id:51 rloc16:0xcc00 ext-addr:9aab43ababf05352 ver:4
5076      *    3-links:{ 33 57 }
5077      *    2-links:{ 46 }
5078      * id:57 rloc16:0xe400 ext-addr:dae9c4c0e9da55ff ver:4
5079      *    3-links:{ 46 51 }
5080      *    1-links:{ 33 }
5081      * Done
5082      * @endcode
5083      * @par
5084      * Discover network topology (list of routers and their connections).
5085      * Parameters are optional and indicate additional items to discover. Can be added in any order.
5086      * * `ip6-addrs` to discover the list of IPv6 addresses of every router.
5087      * * `children` to discover the child table of every router.
5088      * @par
5089      * Information per router:
5090      * * Router ID
5091      * * RLOC16
5092      * * Extended MAC address
5093      * * Thread Version (if known)
5094      * * Whether the router is this device is itself (`me`)
5095      * * Whether the router is the parent of this device when device is a child (`parent`)
5096      * * Whether the router is `leader`
5097      * * Whether the router acts as a border router providing external connectivity (`br`)
5098      * * List of routers to which this router has a link:
5099      *   * `3-links`: Router IDs to which this router has a incoming link with link quality 3
5100      *   * `2-links`: Router IDs to which this router has a incoming link with link quality 2
5101      *   * `1-links`: Router IDs to which this router has a incoming link with link quality 1
5102      *   * If a list if empty, it is omitted in the out.
5103      * * If `ip6-addrs`, list of IPv6 addresses of the router
5104      * * If `children`, list of all children of the router. Information per child:
5105      *   * RLOC16
5106      *   * Incoming Link Quality from perspective of parent to child (zero indicates unknown)
5107      *   * Child Device mode (`r` rx-on-when-idle, `d` Full Thread Device, `n` Full Network Data, `-` no flags set)
5108      *   * Whether the child is this device itself (`me`)
5109      *   * Whether the child acts as a border router providing external connectivity (`br`)
5110      * @cparam meshdiag topology [@ca{ip6-addrs}] [@ca{children}]
5111      * @sa otMeshDiagDiscoverTopology
5112      */
5113     if (aArgs[0] == "topology")
5114     {
5115         otMeshDiagDiscoverConfig config;
5116 
5117         config.mDiscoverIp6Addresses = false;
5118         config.mDiscoverChildTable   = false;
5119 
5120         aArgs++;
5121 
5122         for (; !aArgs->IsEmpty(); aArgs++)
5123         {
5124             if (*aArgs == "ip6-addrs")
5125             {
5126                 config.mDiscoverIp6Addresses = true;
5127             }
5128             else if (*aArgs == "children")
5129             {
5130                 config.mDiscoverChildTable = true;
5131             }
5132             else
5133             {
5134                 ExitNow(error = OT_ERROR_INVALID_ARGS);
5135             }
5136         }
5137 
5138         SuccessOrExit(error = otMeshDiagDiscoverTopology(GetInstancePtr(), &config, HandleMeshDiagDiscoverDone, this));
5139         error = OT_ERROR_PENDING;
5140     }
5141     /**
5142      * @cli meshdiag childtable
5143      * @code
5144      * meshdiag childtable 0x6400
5145      * rloc16:0x6402 ext-addr:8e6f4d323bbed1fe ver:4
5146      *     timeout:120 age:36 supvn:129 q-msg:0
5147      *     rx-on:yes type:ftd full-net:yes
5148      *     rss - ave:-20 last:-20 margin:80
5149      *     err-rate - frame:11.51% msg:0.76%
5150      *     conn-time:00:11:07
5151      *     csl - sync:no period:0 timeout:0 channel:0
5152      * rloc16:0x6403 ext-addr:ee24e64ecf8c079a ver:4
5153      *     timeout:120 age:19 supvn:129 q-msg:0
5154      *     rx-on:no type:mtd full-net:no
5155      *     rss - ave:-20 last:-20  margin:80
5156      *     err-rate - frame:0.73% msg:0.00%
5157      *     conn-time:01:08:53
5158      *     csl - sync:no period:0 timeout:0 channel:0
5159      * Done
5160      * @endcode
5161      * @par
5162      * Start a query for child table of a router with a given RLOC16.
5163      * Output lists all child entries. Information per child:
5164      * - RLOC16
5165      * - Extended MAC address
5166      * - Thread Version
5167      * - Timeout (in seconds)
5168      * - Age (seconds since last heard)
5169      * - Supervision interval (in seconds)
5170      * - Number of queued messages (in case child is sleepy)
5171      * - Device Mode
5172      * - RSS (average and last)
5173      * - Error rates: frame tx (at MAC layer), IPv6 message tx (above MAC)
5174      * - Connection time (seconds since link establishment `{dd}d.{hh}:{mm}:{ss}` format)
5175      * - CSL info:
5176      *   - If synchronized
5177      *   - Period (in unit of 10-symbols-time)
5178      *   - Timeout (in seconds)
5179      *
5180      * @cparam meshdiag childtable @ca{router-rloc16}
5181      * @sa otMeshDiagQueryChildTable
5182      */
5183     else if (aArgs[0] == "childtable")
5184     {
5185         uint16_t routerRloc16;
5186 
5187         SuccessOrExit(error = aArgs[1].ParseAsUint16(routerRloc16));
5188         VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
5189 
5190         SuccessOrExit(error = otMeshDiagQueryChildTable(GetInstancePtr(), routerRloc16,
5191                                                         HandleMeshDiagQueryChildTableResult, this));
5192 
5193         error = OT_ERROR_PENDING;
5194     }
5195     /**
5196      * @cli meshdiag childip6
5197      * @code
5198      * meshdiag childip6 0xdc00
5199      * child-rloc16: 0xdc02
5200      *     fdde:ad00:beef:0:ded8:cd58:b73:2c21
5201      *     fd00:2:0:0:c24a:456:3b6b:c597
5202      *     fd00:1:0:0:120b:95fe:3ecc:d238
5203      * child-rloc16: 0xdc03
5204      *     fdde:ad00:beef:0:3aa6:b8bf:e7d6:eefe
5205      *     fd00:2:0:0:8ff8:a188:7436:6720
5206      *     fd00:1:0:0:1fcf:5495:790a:370f
5207      * Done
5208      * @endcode
5209      * @par
5210      * Send a query to a parent to retrieve the IPv6 addresses of all its MTD children.
5211      * @cparam meshdiag childip6 @ca{parent-rloc16}
5212      * @sa otMeshDiagQueryChildrenIp6Addrs
5213      */
5214     else if (aArgs[0] == "childip6")
5215     {
5216         uint16_t parentRloc16;
5217 
5218         SuccessOrExit(error = aArgs[1].ParseAsUint16(parentRloc16));
5219         VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
5220 
5221         SuccessOrExit(error = otMeshDiagQueryChildrenIp6Addrs(GetInstancePtr(), parentRloc16,
5222                                                               HandleMeshDiagQueryChildIp6Addrs, this));
5223 
5224         error = OT_ERROR_PENDING;
5225     }
5226     /**
5227      * @cli meshdiag routerneighbortable
5228      * @code
5229      * meshdiag routerneighbortable 0x7400
5230      * rloc16:0x9c00 ext-addr:764788cf6e57a4d2 ver:4
5231      *    rss - ave:-20 last:-20 margin:80
5232      *    err-rate - frame:1.38% msg:0.00%
5233      *    conn-time:01:54:02
5234      * rloc16:0x7c00 ext-addr:4ed24fceec9bf6d3 ver:4
5235      *    rss - ave:-20 last:-20 margin:80
5236      *    err-rate - frame:0.72% msg:0.00%
5237      *    conn-time:00:11:27
5238      * Done
5239      * @endcode
5240      * @par
5241      * Start a query for router neighbor table of a router with a given RLOC16.
5242      * Output lists all router neighbor entries. Information per entry:
5243      *  - RLOC16
5244      *  - Extended MAC address
5245      *  - Thread Version
5246      *  - RSS (average and last) and link margin
5247      *  - Error rates, frame tx (at MAC layer), IPv6 message tx (above MAC)
5248      *  - Connection time (seconds since link establishment `{dd}d.{hh}:{mm}:{ss}` format)
5249      * @cparam meshdiag routerneighbortable @ca{router-rloc16}
5250      * @sa otMeshDiagQueryRouterNeighborTable
5251      */
5252     else if (aArgs[0] == "routerneighbortable")
5253     {
5254         uint16_t routerRloc16;
5255 
5256         SuccessOrExit(error = aArgs[1].ParseAsUint16(routerRloc16));
5257         VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
5258 
5259         SuccessOrExit(error = otMeshDiagQueryRouterNeighborTable(GetInstancePtr(), routerRloc16,
5260                                                                  HandleMeshDiagQueryRouterNeighborTableResult, this));
5261 
5262         error = OT_ERROR_PENDING;
5263     }
5264     else
5265     {
5266         error = OT_ERROR_INVALID_COMMAND;
5267     }
5268 
5269 exit:
5270     return error;
5271 }
5272 
HandleMeshDiagDiscoverDone(otError aError,otMeshDiagRouterInfo * aRouterInfo,void * aContext)5273 void Interpreter::HandleMeshDiagDiscoverDone(otError aError, otMeshDiagRouterInfo *aRouterInfo, void *aContext)
5274 {
5275     reinterpret_cast<Interpreter *>(aContext)->HandleMeshDiagDiscoverDone(aError, aRouterInfo);
5276 }
5277 
HandleMeshDiagDiscoverDone(otError aError,otMeshDiagRouterInfo * aRouterInfo)5278 void Interpreter::HandleMeshDiagDiscoverDone(otError aError, otMeshDiagRouterInfo *aRouterInfo)
5279 {
5280     VerifyOrExit(aRouterInfo != nullptr);
5281 
5282     OutputFormat("id:%02u rloc16:0x%04x ext-addr:", aRouterInfo->mRouterId, aRouterInfo->mRloc16);
5283     OutputExtAddress(aRouterInfo->mExtAddress);
5284 
5285     if (aRouterInfo->mVersion != OT_MESH_DIAG_VERSION_UNKNOWN)
5286     {
5287         OutputFormat(" ver:%u", aRouterInfo->mVersion);
5288     }
5289 
5290     if (aRouterInfo->mIsThisDevice)
5291     {
5292         OutputFormat(" - me");
5293     }
5294 
5295     if (aRouterInfo->mIsThisDeviceParent)
5296     {
5297         OutputFormat(" - parent");
5298     }
5299 
5300     if (aRouterInfo->mIsLeader)
5301     {
5302         OutputFormat(" - leader");
5303     }
5304 
5305     if (aRouterInfo->mIsBorderRouter)
5306     {
5307         OutputFormat(" - br");
5308     }
5309 
5310     OutputNewLine();
5311 
5312     for (uint8_t linkQuality = 3; linkQuality > 0; linkQuality--)
5313     {
5314         bool hasLinkQuality = false;
5315 
5316         for (uint8_t entryQuality : aRouterInfo->mLinkQualities)
5317         {
5318             if (entryQuality == linkQuality)
5319             {
5320                 hasLinkQuality = true;
5321                 break;
5322             }
5323         }
5324 
5325         if (hasLinkQuality)
5326         {
5327             OutputFormat(kIndentSize, "%u-links:{ ", linkQuality);
5328 
5329             for (uint8_t id = 0; id < static_cast<uint8_t>(OT_ARRAY_LENGTH(aRouterInfo->mLinkQualities)); id++)
5330             {
5331                 if (aRouterInfo->mLinkQualities[id] == linkQuality)
5332                 {
5333                     OutputFormat("%02u ", id);
5334                 }
5335             }
5336 
5337             OutputLine("}");
5338         }
5339     }
5340 
5341     if (aRouterInfo->mIp6AddrIterator != nullptr)
5342     {
5343         otIp6Address ip6Address;
5344 
5345         OutputLine(kIndentSize, "ip6-addrs:");
5346 
5347         while (otMeshDiagGetNextIp6Address(aRouterInfo->mIp6AddrIterator, &ip6Address) == OT_ERROR_NONE)
5348         {
5349             OutputSpaces(kIndentSize * 2);
5350             OutputIp6AddressLine(ip6Address);
5351         }
5352     }
5353 
5354     if (aRouterInfo->mChildIterator != nullptr)
5355     {
5356         otMeshDiagChildInfo childInfo;
5357         char                linkModeString[kLinkModeStringSize];
5358         bool                isFirst = true;
5359 
5360         while (otMeshDiagGetNextChildInfo(aRouterInfo->mChildIterator, &childInfo) == OT_ERROR_NONE)
5361         {
5362             if (isFirst)
5363             {
5364                 OutputLine(kIndentSize, "children:");
5365                 isFirst = false;
5366             }
5367 
5368             OutputFormat(kIndentSize * 2, "rloc16:0x%04x lq:%u, mode:%s", childInfo.mRloc16, childInfo.mLinkQuality,
5369                          LinkModeToString(childInfo.mMode, linkModeString));
5370 
5371             if (childInfo.mIsThisDevice)
5372             {
5373                 OutputFormat(" - me");
5374             }
5375 
5376             if (childInfo.mIsBorderRouter)
5377             {
5378                 OutputFormat(" - br");
5379             }
5380 
5381             OutputNewLine();
5382         }
5383 
5384         if (isFirst)
5385         {
5386             OutputLine(kIndentSize, "children: none");
5387         }
5388     }
5389 
5390 exit:
5391     OutputResult(aError);
5392 }
5393 
HandleMeshDiagQueryChildTableResult(otError aError,const otMeshDiagChildEntry * aChildEntry,void * aContext)5394 void Interpreter::HandleMeshDiagQueryChildTableResult(otError                     aError,
5395                                                       const otMeshDiagChildEntry *aChildEntry,
5396                                                       void                       *aContext)
5397 {
5398     reinterpret_cast<Interpreter *>(aContext)->HandleMeshDiagQueryChildTableResult(aError, aChildEntry);
5399 }
5400 
HandleMeshDiagQueryChildTableResult(otError aError,const otMeshDiagChildEntry * aChildEntry)5401 void Interpreter::HandleMeshDiagQueryChildTableResult(otError aError, const otMeshDiagChildEntry *aChildEntry)
5402 {
5403     PercentageStringBuffer stringBuffer;
5404     char                   string[OT_DURATION_STRING_SIZE];
5405 
5406     VerifyOrExit(aChildEntry != nullptr);
5407 
5408     OutputFormat("rloc16:0x%04x ext-addr:", aChildEntry->mRloc16);
5409     OutputExtAddress(aChildEntry->mExtAddress);
5410     OutputLine(" ver:%u", aChildEntry->mVersion);
5411 
5412     OutputLine(kIndentSize, "timeout:%lu age:%lu supvn:%u q-msg:%u", ToUlong(aChildEntry->mTimeout),
5413                ToUlong(aChildEntry->mAge), aChildEntry->mSupervisionInterval, aChildEntry->mQueuedMessageCount);
5414 
5415     OutputLine(kIndentSize, "rx-on:%s type:%s full-net:%s", aChildEntry->mRxOnWhenIdle ? "yes" : "no",
5416                aChildEntry->mDeviceTypeFtd ? "ftd" : "mtd", aChildEntry->mFullNetData ? "yes" : "no");
5417 
5418     OutputLine(kIndentSize, "rss - ave:%d last:%d margin:%d", aChildEntry->mAverageRssi, aChildEntry->mLastRssi,
5419                aChildEntry->mLinkMargin);
5420 
5421     if (aChildEntry->mSupportsErrRate)
5422     {
5423         OutputFormat(kIndentSize, "err-rate - frame:%s%% ",
5424                      PercentageToString(aChildEntry->mFrameErrorRate, stringBuffer));
5425         OutputLine("msg:%s%% ", PercentageToString(aChildEntry->mMessageErrorRate, stringBuffer));
5426     }
5427 
5428     otConvertDurationInSecondsToString(aChildEntry->mConnectionTime, string, sizeof(string));
5429     OutputLine(kIndentSize, "conn-time:%s", string);
5430 
5431     OutputLine(kIndentSize, "csl - sync:%s period:%u timeout:%lu channel:%u",
5432                aChildEntry->mCslSynchronized ? "yes" : "no", aChildEntry->mCslPeriod, ToUlong(aChildEntry->mCslTimeout),
5433                aChildEntry->mCslChannel);
5434 
5435 exit:
5436     OutputResult(aError);
5437 }
5438 
HandleMeshDiagQueryRouterNeighborTableResult(otError aError,const otMeshDiagRouterNeighborEntry * aNeighborEntry,void * aContext)5439 void Interpreter::HandleMeshDiagQueryRouterNeighborTableResult(otError                              aError,
5440                                                                const otMeshDiagRouterNeighborEntry *aNeighborEntry,
5441                                                                void                                *aContext)
5442 {
5443     reinterpret_cast<Interpreter *>(aContext)->HandleMeshDiagQueryRouterNeighborTableResult(aError, aNeighborEntry);
5444 }
5445 
HandleMeshDiagQueryRouterNeighborTableResult(otError aError,const otMeshDiagRouterNeighborEntry * aNeighborEntry)5446 void Interpreter::HandleMeshDiagQueryRouterNeighborTableResult(otError                              aError,
5447                                                                const otMeshDiagRouterNeighborEntry *aNeighborEntry)
5448 {
5449     PercentageStringBuffer stringBuffer;
5450     char                   string[OT_DURATION_STRING_SIZE];
5451 
5452     VerifyOrExit(aNeighborEntry != nullptr);
5453 
5454     OutputFormat("rloc16:0x%04x ext-addr:", aNeighborEntry->mRloc16);
5455     OutputExtAddress(aNeighborEntry->mExtAddress);
5456     OutputLine(" ver:%u", aNeighborEntry->mVersion);
5457 
5458     OutputLine(kIndentSize, "rss - ave:%d last:%d margin:%d", aNeighborEntry->mAverageRssi, aNeighborEntry->mLastRssi,
5459                aNeighborEntry->mLinkMargin);
5460 
5461     if (aNeighborEntry->mSupportsErrRate)
5462     {
5463         OutputFormat(kIndentSize, "err-rate - frame:%s%% ",
5464                      PercentageToString(aNeighborEntry->mFrameErrorRate, stringBuffer));
5465         OutputLine("msg:%s%% ", PercentageToString(aNeighborEntry->mMessageErrorRate, stringBuffer));
5466     }
5467 
5468     otConvertDurationInSecondsToString(aNeighborEntry->mConnectionTime, string, sizeof(string));
5469     OutputLine(kIndentSize, "conn-time:%s", string);
5470 
5471 exit:
5472     OutputResult(aError);
5473 }
5474 
HandleMeshDiagQueryChildIp6Addrs(otError aError,uint16_t aChildRloc16,otMeshDiagIp6AddrIterator * aIp6AddrIterator,void * aContext)5475 void Interpreter::HandleMeshDiagQueryChildIp6Addrs(otError                    aError,
5476                                                    uint16_t                   aChildRloc16,
5477                                                    otMeshDiagIp6AddrIterator *aIp6AddrIterator,
5478                                                    void                      *aContext)
5479 {
5480     reinterpret_cast<Interpreter *>(aContext)->HandleMeshDiagQueryChildIp6Addrs(aError, aChildRloc16, aIp6AddrIterator);
5481 }
5482 
HandleMeshDiagQueryChildIp6Addrs(otError aError,uint16_t aChildRloc16,otMeshDiagIp6AddrIterator * aIp6AddrIterator)5483 void Interpreter::HandleMeshDiagQueryChildIp6Addrs(otError                    aError,
5484                                                    uint16_t                   aChildRloc16,
5485                                                    otMeshDiagIp6AddrIterator *aIp6AddrIterator)
5486 {
5487     otIp6Address ip6Address;
5488 
5489     VerifyOrExit(aError == OT_ERROR_NONE || aError == OT_ERROR_PENDING);
5490     VerifyOrExit(aIp6AddrIterator != nullptr);
5491 
5492     OutputLine("child-rloc16: 0x%04x", aChildRloc16);
5493 
5494     while (otMeshDiagGetNextIp6Address(aIp6AddrIterator, &ip6Address) == OT_ERROR_NONE)
5495     {
5496         OutputSpaces(kIndentSize);
5497         OutputIp6AddressLine(ip6Address);
5498     }
5499 
5500 exit:
5501     OutputResult(aError);
5502 }
5503 
5504 #endif // OPENTHREAD_CONFIG_MESH_DIAG_ENABLE
5505 
5506 #endif // OPENTHREAD_FTD
5507 
Process(Arg aArgs[])5508 template <> otError Interpreter::Process<Cmd("panid")>(Arg aArgs[])
5509 {
5510     otError error = OT_ERROR_NONE;
5511     /**
5512      * @cli panid
5513      * @code
5514      * panid
5515      * 0xdead
5516      * Done
5517      * @endcode
5518      * @par api_copy
5519      * #otLinkGetPanId
5520      */
5521     if (aArgs[0].IsEmpty())
5522     {
5523         OutputLine("0x%04x", otLinkGetPanId(GetInstancePtr()));
5524     }
5525     /**
5526      * @cli panid (panid)
5527      * @code
5528      * panid 0xdead
5529      * Done
5530      * @endcode
5531      * @par api_copy
5532      * #otLinkSetPanId
5533      * @cparam panid @ca{panid}
5534      */
5535     else
5536     {
5537         error = ProcessSet(aArgs, otLinkSetPanId);
5538     }
5539 
5540     return error;
5541 }
5542 
Process(Arg aArgs[])5543 template <> otError Interpreter::Process<Cmd("parent")>(Arg aArgs[])
5544 {
5545     otError error = OT_ERROR_NONE;
5546     /**
5547      * @cli parent
5548      * @code
5549      * parent
5550      * Ext Addr: be1857c6c21dce55
5551      * Rloc: 5c00
5552      * Link Quality In: 3
5553      * Link Quality Out: 3
5554      * Age: 20
5555      * Version: 4
5556      * Done
5557      * @endcode
5558      * @sa otThreadGetParentInfo
5559      * @par
5560      * Get the diagnostic information for a Thread Router as parent.
5561      * @par
5562      * When operating as a Thread Router when OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE is enabled, this command
5563      * will return the cached information from when the device was previously attached as a Thread Child. Returning
5564      * cached information is necessary to support the Thread Test Harness - Test Scenario 8.2.x requests the former
5565      * parent (i.e. %Joiner Router's) MAC address even if the device has already promoted to a router.
5566      * @par
5567      * Note: When OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE is enabled, this command will return two extra lines with
5568      * information relevant for CSL Receiver operation.
5569      */
5570     if (aArgs[0].IsEmpty())
5571     {
5572         otRouterInfo parentInfo;
5573 
5574         SuccessOrExit(error = otThreadGetParentInfo(GetInstancePtr(), &parentInfo));
5575         OutputFormat("Ext Addr: ");
5576         OutputExtAddressLine(parentInfo.mExtAddress);
5577         OutputLine("Rloc: %x", parentInfo.mRloc16);
5578         OutputLine("Link Quality In: %u", parentInfo.mLinkQualityIn);
5579         OutputLine("Link Quality Out: %u", parentInfo.mLinkQualityOut);
5580         OutputLine("Age: %lu", ToUlong(parentInfo.mAge));
5581         OutputLine("Version: %u", parentInfo.mVersion);
5582 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
5583         OutputLine("CSL clock accuracy: %u", parentInfo.mCslClockAccuracy);
5584         OutputLine("CSL uncertainty: %u", parentInfo.mCslUncertainty);
5585 #endif
5586     }
5587     /**
5588      * @cli parent search
5589      * @code
5590      * parent search
5591      * Done
5592      * @endcode
5593      * @par api_copy
5594      * #otThreadSearchForBetterParent
5595      */
5596     else if (aArgs[0] == "search")
5597     {
5598         error = otThreadSearchForBetterParent(GetInstancePtr());
5599     }
5600     else
5601     {
5602         error = OT_ERROR_INVALID_ARGS;
5603     }
5604 
5605 exit:
5606     return error;
5607 }
5608 
5609 #if OPENTHREAD_FTD
5610 /**
5611  * @cli parentpriority (get,set)
5612  * @code
5613  * parentpriority
5614  * 1
5615  * Done
5616  * @endcode
5617  * @code
5618  * parentpriority 1
5619  * Done
5620  * @endcode
5621  * @cparam parentpriority [@ca{parentpriority}]
5622  * @par
5623  * Gets or sets the assigned parent priority value: 1, 0, -1 or -2. -2 means not assigned.
5624  * @sa otThreadGetParentPriority
5625  * @sa otThreadSetParentPriority
5626  */
Process(Arg aArgs[])5627 template <> otError Interpreter::Process<Cmd("parentpriority")>(Arg aArgs[])
5628 {
5629     return ProcessGetSet(aArgs, otThreadGetParentPriority, otThreadSetParentPriority);
5630 }
5631 #endif
5632 
5633 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
Process(Arg * aArgs)5634 template <> otError Interpreter::Process<Cmd("routeridrange")>(Arg *aArgs)
5635 {
5636     uint8_t minRouterId;
5637     uint8_t maxRouterId;
5638     otError error = OT_ERROR_NONE;
5639 
5640     if (aArgs[0].IsEmpty())
5641     {
5642         otThreadGetRouterIdRange(GetInstancePtr(), &minRouterId, &maxRouterId);
5643         OutputLine("%u %u", minRouterId, maxRouterId);
5644     }
5645     else
5646     {
5647         SuccessOrExit(error = aArgs[0].ParseAsUint8(minRouterId));
5648         SuccessOrExit(error = aArgs[1].ParseAsUint8(maxRouterId));
5649         VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
5650         error = otThreadSetRouterIdRange(GetInstancePtr(), minRouterId, maxRouterId);
5651     }
5652 
5653 exit:
5654     return error;
5655 }
5656 #endif
5657 
5658 #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
5659 /**
5660  * @cli ping
5661  * @code
5662  * ping fd00:db8:0:0:76b:6a05:3ae9:a61a
5663  * 16 bytes from fd00:db8:0:0:76b:6a05:3ae9:a61a: icmp_seq=5 hlim=64 time=0ms
5664  * 1 packets transmitted, 1 packets received. Packet loss = 0.0%. Round-trip min/avg/max = 0/0.0/0 ms.
5665  * Done
5666  * @endcode
5667  * @code
5668  * ping -I fd00:db8:0:0:76b:6a05:3ae9:a61a ff02::1 100 1 1 1
5669  * 108 bytes from fd00:db8:0:0:f605:fb4b:d429:d59a: icmp_seq=4 hlim=64 time=7ms
5670  * 1 packets transmitted, 1 packets received. Round-trip min/avg/max = 7/7.0/7 ms.
5671  * Done
5672  * @endcode
5673  * @code
5674  * ping 172.17.0.1
5675  * Pinging synthesized IPv6 address: fdde:ad00:beef:2:0:0:ac11:1
5676  * 16 bytes from fdde:ad00:beef:2:0:0:ac11:1: icmp_seq=5 hlim=64 time=0ms
5677  * 1 packets transmitted, 1 packets received. Packet loss = 0.0%. Round-trip min/avg/max = 0/0.0/0 ms.
5678  * Done
5679  * @endcode
5680  * @cparam ping [@ca{async}] [@ca{-I source}] [@ca{-m}] @ca{ipaddrc} [@ca{size}] [@ca{count}] <!--
5681  * -->          [@ca{interval}] [@ca{hoplimit}] [@ca{timeout}]
5682  * @par
5683  * Send an ICMPv6 Echo Request.
5684  * @par
5685  * The address can be an IPv4 address, which will be synthesized to an IPv6 address using the preferred NAT64 prefix
5686  * from the network data.
5687  * @par
5688  * The optional `-m` flag sets the multicast loop flag, which allows looping back pings to multicast addresses that the
5689  * device itself is subscribed to.
5690  * @par
5691  * Note: The command will return InvalidState when the preferred NAT64 prefix is unavailable.
5692  * @sa otPingSenderPing
5693  */
Process(Arg aArgs[])5694 template <> otError Interpreter::Process<Cmd("ping")>(Arg aArgs[]) { return mPing.Process(aArgs); }
5695 #endif // OPENTHREAD_CONFIG_PING_SENDER_ENABLE
5696 
5697 /**
5698  * @cli platform
5699  * @code
5700  * platform
5701  * NRF52840
5702  * Done
5703  * @endcode
5704  * @par
5705  * Print the current platform
5706  */
Process(Arg aArgs[])5707 template <> otError Interpreter::Process<Cmd("platform")>(Arg aArgs[])
5708 {
5709     OT_UNUSED_VARIABLE(aArgs);
5710     OutputLine("%s", OPENTHREAD_CONFIG_PLATFORM_INFO);
5711     return OT_ERROR_NONE;
5712 }
5713 
5714 /**
5715  * @cli pollperiod (get,set)
5716  * @code
5717  * pollperiod
5718  * 0
5719  * Done
5720  * @endcode
5721  * @code
5722  * pollperiod 10
5723  * Done
5724  * @endcode
5725  * @sa otLinkGetPollPeriod
5726  * @sa otLinkSetPollPeriod
5727  * @par
5728  * Get or set the customized data poll period of sleepy end device (milliseconds). Only for certification test.
5729  */
Process(Arg aArgs[])5730 template <> otError Interpreter::Process<Cmd("pollperiod")>(Arg aArgs[])
5731 {
5732     return ProcessGetSet(aArgs, otLinkGetPollPeriod, otLinkSetPollPeriod);
5733 }
5734 
Process(Arg aArgs[])5735 template <> otError Interpreter::Process<Cmd("promiscuous")>(Arg aArgs[])
5736 {
5737     otError error = OT_ERROR_NONE;
5738 
5739     /**
5740      * @cli promiscuous
5741      * @code
5742      * promiscuous
5743      * Disabled
5744      * Done
5745      * @endcode
5746      * @par api_copy
5747      * #otLinkIsPromiscuous
5748      * @sa otPlatRadioGetPromiscuous
5749      */
5750     if (aArgs[0].IsEmpty())
5751     {
5752         OutputEnabledDisabledStatus(otLinkIsPromiscuous(GetInstancePtr()) &&
5753                                     otPlatRadioGetPromiscuous(GetInstancePtr()));
5754     }
5755     else
5756     {
5757         bool enable;
5758 
5759         SuccessOrExit(error = ParseEnableOrDisable(aArgs[0], enable));
5760 
5761         /**
5762          * @cli promiscuous (enable,disable)
5763          * @code
5764          * promiscuous enable
5765          * Done
5766          * @endcode
5767          * @code
5768          * promiscuous disable
5769          * Done
5770          * @endcode
5771          * @cparam promiscuous @ca{enable|disable}
5772          * @par api_copy
5773          * #otLinkSetPromiscuous
5774          */
5775         if (!enable)
5776         {
5777             otLinkSetPcapCallback(GetInstancePtr(), nullptr, nullptr);
5778         }
5779 
5780         SuccessOrExit(error = otLinkSetPromiscuous(GetInstancePtr(), enable));
5781 
5782         if (enable)
5783         {
5784             otLinkSetPcapCallback(GetInstancePtr(), &HandleLinkPcapReceive, this);
5785         }
5786     }
5787 
5788 exit:
5789     return error;
5790 }
5791 
HandleLinkPcapReceive(const otRadioFrame * aFrame,bool aIsTx,void * aContext)5792 void Interpreter::HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx, void *aContext)
5793 {
5794     static_cast<Interpreter *>(aContext)->HandleLinkPcapReceive(aFrame, aIsTx);
5795 }
5796 
HandleLinkPcapReceive(const otRadioFrame * aFrame,bool aIsTx)5797 void Interpreter::HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx)
5798 {
5799     otLogHexDumpInfo info;
5800 
5801     info.mDataBytes  = aFrame->mPsdu;
5802     info.mDataLength = aFrame->mLength;
5803     info.mTitle      = (aIsTx) ? "TX" : "RX";
5804     info.mIterator   = 0;
5805 
5806     OutputNewLine();
5807 
5808     while (otLogGenerateNextHexDumpLine(&info) == OT_ERROR_NONE)
5809     {
5810         OutputLine("%s", info.mLine);
5811     }
5812 }
5813 
5814 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
ParsePrefix(Arg aArgs[],otBorderRouterConfig & aConfig)5815 otError Interpreter::ParsePrefix(Arg aArgs[], otBorderRouterConfig &aConfig)
5816 {
5817     otError error = OT_ERROR_NONE;
5818 
5819     ClearAllBytes(aConfig);
5820 
5821     SuccessOrExit(error = aArgs[0].ParseAsIp6Prefix(aConfig.mPrefix));
5822     aArgs++;
5823 
5824     for (; !aArgs->IsEmpty(); aArgs++)
5825     {
5826         otRoutePreference preference;
5827 
5828         if (ParsePreference(*aArgs, preference) == OT_ERROR_NONE)
5829         {
5830             aConfig.mPreference = preference;
5831         }
5832         else
5833         {
5834             for (char *arg = aArgs->GetCString(); *arg != '\0'; arg++)
5835             {
5836                 switch (*arg)
5837                 {
5838                 case 'p':
5839                     aConfig.mPreferred = true;
5840                     break;
5841 
5842                 case 'a':
5843                     aConfig.mSlaac = true;
5844                     break;
5845 
5846                 case 'd':
5847                     aConfig.mDhcp = true;
5848                     break;
5849 
5850                 case 'c':
5851                     aConfig.mConfigure = true;
5852                     break;
5853 
5854                 case 'r':
5855                     aConfig.mDefaultRoute = true;
5856                     break;
5857 
5858                 case 'o':
5859                     aConfig.mOnMesh = true;
5860                     break;
5861 
5862                 case 's':
5863                     aConfig.mStable = true;
5864                     break;
5865 
5866                 case 'n':
5867                     aConfig.mNdDns = true;
5868                     break;
5869 
5870 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
5871                 case 'D':
5872                     aConfig.mDp = true;
5873                     break;
5874 #endif
5875                 case '-':
5876                     break;
5877 
5878                 default:
5879                     ExitNow(error = OT_ERROR_INVALID_ARGS);
5880                 }
5881             }
5882         }
5883     }
5884 
5885 exit:
5886     return error;
5887 }
5888 
Process(Arg aArgs[])5889 template <> otError Interpreter::Process<Cmd("prefix")>(Arg aArgs[])
5890 {
5891     otError error = OT_ERROR_NONE;
5892 
5893     /**
5894      * @cli prefix
5895      * @code
5896      * prefix
5897      * 2001:dead:beef:cafe::/64 paros med
5898      * - fd00:7d03:7d03:7d03::/64 prosD med
5899      * Done
5900      * @endcode
5901      * @par
5902      * Get the prefix list in the local Network Data.
5903      * @note For the Thread 1.2 border router with backbone capability, the local Domain Prefix
5904      * is listed as well and includes the `D` flag. If backbone functionality is disabled, a dash
5905      * `-` is printed before the local Domain Prefix.
5906      * @par
5907      * For more information about #otBorderRouterConfig flags, refer to @overview.
5908      * @sa otBorderRouterGetNextOnMeshPrefix
5909      */
5910     if (aArgs[0].IsEmpty())
5911     {
5912         otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
5913         otBorderRouterConfig  config;
5914 
5915         while (otBorderRouterGetNextOnMeshPrefix(GetInstancePtr(), &iterator, &config) == OT_ERROR_NONE)
5916         {
5917             mNetworkData.OutputPrefix(config);
5918         }
5919 
5920 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
5921         if (otBackboneRouterGetState(GetInstancePtr()) == OT_BACKBONE_ROUTER_STATE_DISABLED)
5922         {
5923             SuccessOrExit(otBackboneRouterGetDomainPrefix(GetInstancePtr(), &config));
5924             OutputFormat("- ");
5925             mNetworkData.OutputPrefix(config);
5926         }
5927 #endif
5928     }
5929     /**
5930      * @cli prefix add
5931      * @code
5932      * prefix add 2001:dead:beef:cafe::/64 paros med
5933      * Done
5934      * @endcode
5935      * @code
5936      * prefix add fd00:7d03:7d03:7d03::/64 prosD low
5937      * Done
5938      * @endcode
5939      * @cparam prefix add @ca{prefix} [@ca{padcrosnD}] [@ca{high}|@ca{med}|@ca{low}]
5940      * OT CLI uses mapped arguments to configure #otBorderRouterConfig values. @moreinfo{the @overview}.
5941      * @par
5942      * Adds a valid prefix to the Network Data.
5943      * @sa otBorderRouterAddOnMeshPrefix
5944      */
5945     else if (aArgs[0] == "add")
5946     {
5947         otBorderRouterConfig config;
5948 
5949         SuccessOrExit(error = ParsePrefix(aArgs + 1, config));
5950         error = otBorderRouterAddOnMeshPrefix(GetInstancePtr(), &config);
5951     }
5952     /**
5953      * @cli prefix remove
5954      * @code
5955      * prefix remove 2001:dead:beef:cafe::/64
5956      * Done
5957      * @endcode
5958      * @par api_copy
5959      * #otBorderRouterRemoveOnMeshPrefix
5960      */
5961     else if (aArgs[0] == "remove")
5962     {
5963         otIp6Prefix prefix;
5964 
5965         SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix));
5966         error = otBorderRouterRemoveOnMeshPrefix(GetInstancePtr(), &prefix);
5967     }
5968     /**
5969      * @cli prefix meshlocal
5970      * @code
5971      * prefix meshlocal
5972      * fdde:ad00:beef:0::/64
5973      * Done
5974      * @endcode
5975      * @par
5976      * Get the mesh local prefix.
5977      */
5978     else if (aArgs[0] == "meshlocal")
5979     {
5980         if (aArgs[1].IsEmpty())
5981         {
5982             OutputIp6PrefixLine(*otThreadGetMeshLocalPrefix(GetInstancePtr()));
5983         }
5984         else
5985         {
5986             otIp6Prefix prefix;
5987 
5988             SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix));
5989             VerifyOrExit(prefix.mLength == OT_IP6_PREFIX_BITSIZE, error = OT_ERROR_INVALID_ARGS);
5990             error =
5991                 otThreadSetMeshLocalPrefix(GetInstancePtr(), reinterpret_cast<otMeshLocalPrefix *>(&prefix.mPrefix));
5992         }
5993     }
5994     else
5995     {
5996         error = OT_ERROR_INVALID_COMMAND;
5997     }
5998 
5999 exit:
6000     return error;
6001 }
6002 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
6003 
6004 /**
6005  * @cli preferrouterid
6006  * @code
6007  * preferrouterid 16
6008  * Done
6009  * @endcode
6010  * @cparam preferrouterid @ca{routerid}
6011  * @par
6012  * Specifies the preferred router ID that the leader should provide when solicited.
6013  * @sa otThreadSetPreferredRouterId
6014  */
6015 #if OPENTHREAD_FTD
Process(Arg aArgs[])6016 template <> otError Interpreter::Process<Cmd("preferrouterid")>(Arg aArgs[])
6017 {
6018     return ProcessSet(aArgs, otThreadSetPreferredRouterId);
6019 }
6020 #endif
6021 
6022 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
Process(Arg aArgs[])6023 template <> otError Interpreter::Process<Cmd("radiofilter")>(Arg aArgs[])
6024 {
6025     return ProcessEnableDisable(aArgs, otLinkIsRadioFilterEnabled, otLinkSetRadioFilterEnabled);
6026 }
6027 #endif
6028 
6029 #if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE
UsToSInt(uint64_t aUs)6030 inline unsigned long UsToSInt(uint64_t aUs) { return ToUlong(static_cast<uint32_t>(aUs / 1000000)); }
UsToSDec(uint64_t aUs)6031 inline unsigned long UsToSDec(uint64_t aUs) { return ToUlong(static_cast<uint32_t>(aUs % 1000000)); }
6032 
OutputRadioStatsTime(const char * aTimeName,uint64_t aTimeUs,uint64_t aTotalTimeUs)6033 void Interpreter::OutputRadioStatsTime(const char *aTimeName, uint64_t aTimeUs, uint64_t aTotalTimeUs)
6034 {
6035     uint32_t timePercentInt = static_cast<uint32_t>(aTimeUs * 100 / aTotalTimeUs);
6036     uint32_t timePercentDec = static_cast<uint32_t>((aTimeUs * 100 % aTotalTimeUs) * 100 / aTotalTimeUs);
6037 
6038     OutputLine("%s Time: %lu.%06lus (%lu.%02lu%%)", aTimeName, UsToSInt(aTimeUs), UsToSDec(aTimeUs),
6039                ToUlong(timePercentInt), ToUlong(timePercentDec));
6040 }
6041 #endif // OPENTHREAD_CONFIG_RADIO_STATS_ENABLE
6042 
Process(Arg aArgs[])6043 template <> otError Interpreter::Process<Cmd("radio")>(Arg aArgs[])
6044 {
6045     otError error = OT_ERROR_NONE;
6046 
6047     /**
6048      * @cli radio (enable,disable)
6049      * @code
6050      * radio enable
6051      * Done
6052      * @endcode
6053      * @code
6054      * radio disable
6055      * Done
6056      * @endcode
6057      * @cparam radio @ca{enable|disable}
6058      * @sa otLinkSetEnabled
6059      * @par
6060      * Enables or disables the radio.
6061      */
6062     if (ProcessEnableDisable(aArgs, otLinkSetEnabled) == OT_ERROR_NONE)
6063     {
6064     }
6065 #if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE
6066     /**
6067      * @cli radio stats
6068      * @code
6069      * radio stats
6070      * Radio Statistics:
6071      * Total Time: 67.756s
6072      * Tx Time: 0.022944s (0.03%)
6073      * Rx Time: 1.482353s (2.18%)
6074      * Sleep Time: 66.251128s (97.77%)
6075      * Disabled Time: 0.000080s (0.00%)
6076      * Done
6077      * @endcode
6078      * @par api_copy
6079      * #otRadioTimeStatsGet
6080      */
6081     else if (aArgs[0] == "stats")
6082     {
6083         if (aArgs[1].IsEmpty())
6084         {
6085             const otRadioTimeStats *radioStats = nullptr;
6086             uint64_t                totalTimeUs;
6087 
6088             radioStats = otRadioTimeStatsGet(GetInstancePtr());
6089 
6090             totalTimeUs =
6091                 radioStats->mSleepTime + radioStats->mTxTime + radioStats->mRxTime + radioStats->mDisabledTime;
6092             if (totalTimeUs == 0)
6093             {
6094                 OutputLine("Total Time is 0!");
6095             }
6096             else
6097             {
6098                 OutputLine("Radio Statistics:");
6099                 OutputLine("Total Time: %lu.%03lus", ToUlong(static_cast<uint32_t>(totalTimeUs / 1000000)),
6100                            ToUlong((totalTimeUs % 1000000) / 1000));
6101                 OutputRadioStatsTime("Tx", radioStats->mTxTime, totalTimeUs);
6102                 OutputRadioStatsTime("Rx", radioStats->mRxTime, totalTimeUs);
6103                 OutputRadioStatsTime("Sleep", radioStats->mSleepTime, totalTimeUs);
6104                 OutputRadioStatsTime("Disabled", radioStats->mDisabledTime, totalTimeUs);
6105             }
6106         }
6107         /**
6108          * @cli radio stats clear
6109          * @code
6110          * radio stats clear
6111          * Done
6112          * @endcode
6113          * @par api_copy
6114          * #otRadioTimeStatsReset
6115          */
6116         else if (aArgs[1] == "clear")
6117         {
6118             otRadioTimeStatsReset(GetInstancePtr());
6119         }
6120     }
6121 #endif // OPENTHREAD_CONFIG_RADIO_STATS_ENABLE
6122     else
6123     {
6124         error = OT_ERROR_INVALID_COMMAND;
6125     }
6126 
6127     return error;
6128 }
6129 
Process(Arg aArgs[])6130 template <> otError Interpreter::Process<Cmd("rcp")>(Arg aArgs[])
6131 {
6132     otError     error   = OT_ERROR_NONE;
6133     const char *version = otPlatRadioGetVersionString(GetInstancePtr());
6134 
6135     VerifyOrExit(version != otGetVersionString(), error = OT_ERROR_NOT_IMPLEMENTED);
6136     /**
6137      * @cli rcp version
6138      * @code
6139      * rcp version
6140      * OPENTHREAD/20191113-00825-g82053cc9d-dirty; SIMULATION; Jun  4 2020 17:53:16
6141      * Done
6142      * @endcode
6143      * @par api_copy
6144      * #otPlatRadioGetVersionString
6145      */
6146     if (aArgs[0] == "version")
6147     {
6148         OutputLine("%s", version);
6149     }
6150 
6151     else
6152     {
6153         error = OT_ERROR_INVALID_ARGS;
6154     }
6155 
6156 exit:
6157     return error;
6158 }
6159 /**
6160  * @cli region
6161  * @code
6162  * region
6163  * US
6164  * Done
6165  * @endcode
6166  * @par api_copy
6167  * #otLinkGetRegion
6168  */
Process(Arg aArgs[])6169 template <> otError Interpreter::Process<Cmd("region")>(Arg aArgs[])
6170 {
6171     otError  error = OT_ERROR_NONE;
6172     uint16_t regionCode;
6173 
6174     if (aArgs[0].IsEmpty())
6175     {
6176         SuccessOrExit(error = otLinkGetRegion(GetInstancePtr(), &regionCode));
6177         OutputLine("%c%c", regionCode >> 8, regionCode & 0xff);
6178     }
6179     /**
6180      * @cli region (set)
6181      * @code
6182      * region US
6183      * Done
6184      * @endcode
6185      * @par api_copy
6186      * #otLinkSetRegion
6187      * @par
6188      * Changing this can affect the transmit power limit.
6189      */
6190     else
6191 
6192     {
6193         VerifyOrExit(aArgs[0].GetLength() == 2, error = OT_ERROR_INVALID_ARGS);
6194 
6195         regionCode = static_cast<uint16_t>(static_cast<uint16_t>(aArgs[0].GetCString()[0]) << 8) +
6196                      static_cast<uint16_t>(aArgs[0].GetCString()[1]);
6197         error = otLinkSetRegion(GetInstancePtr(), regionCode);
6198     }
6199 
6200 exit:
6201     return error;
6202 }
6203 
6204 #if OPENTHREAD_FTD
6205 /**
6206  * @cli releaserouterid (routerid)
6207  * @code
6208  * releaserouterid 16
6209  * Done
6210  * @endcode
6211  * @cparam releaserouterid [@ca{routerid}]
6212  * @par api_copy
6213  * #otThreadReleaseRouterId
6214  */
Process(Arg aArgs[])6215 template <> otError Interpreter::Process<Cmd("releaserouterid")>(Arg aArgs[])
6216 
6217 {
6218     return ProcessSet(aArgs, otThreadReleaseRouterId);
6219 }
6220 #endif
6221 /**
6222  * @cli rloc16
6223  * @code
6224  * rloc16
6225  * 0xdead
6226  * Done
6227  * @endcode
6228  * @par api_copy
6229  * #otThreadGetRloc16
6230  */
Process(Arg aArgs[])6231 template <> otError Interpreter::Process<Cmd("rloc16")>(Arg aArgs[])
6232 
6233 {
6234     OT_UNUSED_VARIABLE(aArgs);
6235 
6236     OutputLine("%04x", otThreadGetRloc16(GetInstancePtr()));
6237 
6238     return OT_ERROR_NONE;
6239 }
6240 
6241 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
ParseRoute(Arg aArgs[],otExternalRouteConfig & aConfig)6242 otError Interpreter::ParseRoute(Arg aArgs[], otExternalRouteConfig &aConfig)
6243 {
6244     otError error = OT_ERROR_NONE;
6245 
6246     ClearAllBytes(aConfig);
6247 
6248     SuccessOrExit(error = aArgs[0].ParseAsIp6Prefix(aConfig.mPrefix));
6249     aArgs++;
6250 
6251     for (; !aArgs->IsEmpty(); aArgs++)
6252     {
6253         otRoutePreference preference;
6254 
6255         if (ParsePreference(*aArgs, preference) == OT_ERROR_NONE)
6256         {
6257             aConfig.mPreference = preference;
6258         }
6259         else
6260         {
6261             for (char *arg = aArgs->GetCString(); *arg != '\0'; arg++)
6262             {
6263                 switch (*arg)
6264                 {
6265                 case 's':
6266                     aConfig.mStable = true;
6267                     break;
6268 
6269                 case 'n':
6270                     aConfig.mNat64 = true;
6271                     break;
6272 
6273                 case 'a':
6274                     aConfig.mAdvPio = true;
6275                     break;
6276 
6277                 case '-':
6278                     break;
6279 
6280                 default:
6281                     ExitNow(error = OT_ERROR_INVALID_ARGS);
6282                 }
6283             }
6284         }
6285     }
6286 
6287 exit:
6288     return error;
6289 }
6290 
Process(Arg aArgs[])6291 template <> otError Interpreter::Process<Cmd("route")>(Arg aArgs[])
6292 {
6293     otError error = OT_ERROR_NONE;
6294     /**
6295      * @cli route
6296      * @code
6297      * route
6298      * 2001:dead:beef:cafe::/64 s med
6299      * Done
6300      * @endcode
6301      * @sa otBorderRouterGetNextRoute
6302      * @par
6303      * Get the external route list in the local Network Data.
6304      */
6305     if (aArgs[0].IsEmpty())
6306     {
6307         otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
6308         otExternalRouteConfig config;
6309 
6310         while (otBorderRouterGetNextRoute(GetInstancePtr(), &iterator, &config) == OT_ERROR_NONE)
6311         {
6312             mNetworkData.OutputRoute(config);
6313         }
6314     }
6315     /**
6316      * @cli route add
6317      * @code
6318      * route add 2001:dead:beef:cafe::/64 s med
6319      * Done
6320      * @endcode
6321      * @par
6322      * For parameters, use:
6323      * *    s: Stable flag
6324      * *    n: NAT64 flag
6325      * *    prf: Default Router Preference, [high, med, low].
6326      * @cparam route add @ca{prefix} [@ca{sn}] [@ca{high}|@ca{med}|@ca{low}]
6327      * @par api_copy
6328      * #otExternalRouteConfig
6329      * @par
6330      * Add a valid external route to the Network Data.
6331      */
6332     else if (aArgs[0] == "add")
6333     {
6334         otExternalRouteConfig config;
6335 
6336         SuccessOrExit(error = ParseRoute(aArgs + 1, config));
6337         error = otBorderRouterAddRoute(GetInstancePtr(), &config);
6338     }
6339     /**
6340      * @cli route remove
6341      * @code
6342      * route remove 2001:dead:beef:cafe::/64
6343      * Done
6344      * @endcode
6345      * @cparam route remove [@ca{prefix}]
6346      * @par api_copy
6347      * #otBorderRouterRemoveRoute
6348      */
6349     else if (aArgs[0] == "remove")
6350     {
6351         otIp6Prefix prefix;
6352 
6353         SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix));
6354         error = otBorderRouterRemoveRoute(GetInstancePtr(), &prefix);
6355     }
6356     else
6357     {
6358         error = OT_ERROR_INVALID_COMMAND;
6359     }
6360 
6361 exit:
6362     return error;
6363 }
6364 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
6365 
6366 #if OPENTHREAD_FTD
Process(Arg aArgs[])6367 template <> otError Interpreter::Process<Cmd("router")>(Arg aArgs[])
6368 {
6369     otError      error = OT_ERROR_NONE;
6370     otRouterInfo routerInfo;
6371     uint16_t     routerId;
6372     bool         isTable;
6373     /**
6374      * @cli router table
6375      * @code
6376      * router table
6377      * | ID | RLOC16 | Next Hop | Path Cost | LQ In | LQ Out | Age | Extended MAC     | Link |
6378      * +----+--------+----------+-----------+-------+--------+-----+------------------+------+
6379      * | 22 | 0x5800 |       63 |         0 |     0 |      0 |   0 | 0aeb8196c9f61658 |    0 |
6380      * | 49 | 0xc400 |       63 |         0 |     3 |      3 |   0 | faa1c03908e2dbf2 |    1 |
6381      * Done
6382      * @endcode
6383      * @sa otThreadGetRouterInfo
6384      * @par
6385      * Prints a list of routers in a table format.
6386      */
6387     isTable = (aArgs[0] == "table");
6388     /**
6389      * @cli router list
6390      * @code
6391      * router list
6392      * 8 24 50
6393      * Done
6394      * @endcode
6395      * @sa otThreadGetRouterInfo
6396      * @par
6397      * List allocated Router IDs.
6398      */
6399     if (isTable || (aArgs[0] == "list"))
6400     {
6401         uint8_t maxRouterId;
6402 
6403         if (isTable)
6404         {
6405             static const char *const kRouterTableTitles[] = {
6406                 "ID", "RLOC16", "Next Hop", "Path Cost", "LQ In", "LQ Out", "Age", "Extended MAC", "Link",
6407             };
6408 
6409             static const uint8_t kRouterTableColumnWidths[] = {
6410                 4, 8, 10, 11, 7, 8, 5, 18, 6,
6411             };
6412 
6413             OutputTableHeader(kRouterTableTitles, kRouterTableColumnWidths);
6414         }
6415 
6416         maxRouterId = otThreadGetMaxRouterId(GetInstancePtr());
6417 
6418         for (uint8_t i = 0; i <= maxRouterId; i++)
6419         {
6420             if (otThreadGetRouterInfo(GetInstancePtr(), i, &routerInfo) != OT_ERROR_NONE)
6421             {
6422                 continue;
6423             }
6424 
6425             if (isTable)
6426             {
6427                 OutputFormat("| %2u ", routerInfo.mRouterId);
6428                 OutputFormat("| 0x%04x ", routerInfo.mRloc16);
6429                 OutputFormat("| %8u ", routerInfo.mNextHop);
6430                 OutputFormat("| %9u ", routerInfo.mPathCost);
6431                 OutputFormat("| %5u ", routerInfo.mLinkQualityIn);
6432                 OutputFormat("| %6u ", routerInfo.mLinkQualityOut);
6433                 OutputFormat("| %3u ", routerInfo.mAge);
6434                 OutputFormat("| ");
6435                 OutputExtAddress(routerInfo.mExtAddress);
6436                 OutputLine(" | %4d |", routerInfo.mLinkEstablished);
6437             }
6438             else
6439             {
6440                 OutputFormat("%u ", i);
6441             }
6442         }
6443 
6444         OutputNewLine();
6445         ExitNow();
6446     }
6447     /**
6448      * @cli router (id)
6449      * @code
6450      * router 50
6451      * Alloc: 1
6452      * Router ID: 50
6453      * Rloc: c800
6454      * Next Hop: c800
6455      * Link: 1
6456      * Ext Addr: e2b3540590b0fd87
6457      * Cost: 0
6458      * Link Quality In: 3
6459      * Link Quality Out: 3
6460      * Age: 3
6461      * Done
6462      * @endcode
6463      * @code
6464      * router 0xc800
6465      * Alloc: 1
6466      * Router ID: 50
6467      * Rloc: c800
6468      * Next Hop: c800
6469      * Link: 1
6470      * Ext Addr: e2b3540590b0fd87
6471      * Cost: 0
6472      * Link Quality In: 3
6473      * Link Quality Out: 3
6474      * Age: 7
6475      * Done
6476      * @endcode
6477      * @cparam router [@ca{id}]
6478      * @par api_copy
6479      * #otThreadGetRouterInfo
6480      * @par
6481      * Print diagnostic information for a Thread Router. The id may be a Router ID or
6482      * an RLOC16.
6483      */
6484     SuccessOrExit(error = aArgs[0].ParseAsUint16(routerId));
6485     SuccessOrExit(error = otThreadGetRouterInfo(GetInstancePtr(), routerId, &routerInfo));
6486 
6487     OutputLine("Alloc: %d", routerInfo.mAllocated);
6488 
6489     if (routerInfo.mAllocated)
6490     {
6491         OutputLine("Router ID: %u", routerInfo.mRouterId);
6492         OutputLine("Rloc: %04x", routerInfo.mRloc16);
6493         OutputLine("Next Hop: %04x", static_cast<uint16_t>(routerInfo.mNextHop) << 10);
6494         OutputLine("Link: %d", routerInfo.mLinkEstablished);
6495 
6496         if (routerInfo.mLinkEstablished)
6497         {
6498             OutputFormat("Ext Addr: ");
6499             OutputExtAddressLine(routerInfo.mExtAddress);
6500             OutputLine("Cost: %u", routerInfo.mPathCost);
6501             OutputLine("Link Quality In: %u", routerInfo.mLinkQualityIn);
6502             OutputLine("Link Quality Out: %u", routerInfo.mLinkQualityOut);
6503             OutputLine("Age: %u", routerInfo.mAge);
6504         }
6505     }
6506 
6507 exit:
6508     return error;
6509 }
6510 /**
6511  * @cli routerdowngradethreshold (get,set)
6512  * @code routerdowngradethreshold
6513  * 23
6514  * Done
6515  * @endcode
6516  * @code routerdowngradethreshold 23
6517  * Done
6518  * @endcode
6519  * @cparam routerdowngradethreshold [@ca{threshold}]
6520  * @par
6521  * Gets or sets the ROUTER_DOWNGRADE_THRESHOLD value.
6522  * @sa otThreadGetRouterDowngradeThreshold
6523  * @sa otThreadSetRouterDowngradeThreshold
6524  */
Process(Arg aArgs[])6525 template <> otError Interpreter::Process<Cmd("routerdowngradethreshold")>(Arg aArgs[])
6526 {
6527     return ProcessGetSet(aArgs, otThreadGetRouterDowngradeThreshold, otThreadSetRouterDowngradeThreshold);
6528 }
6529 
6530 /**
6531  * @cli routereligible
6532  * @code
6533  * routereligible
6534  * Enabled
6535  * Done
6536  * @endcode
6537  * @sa otThreadIsRouterEligible
6538  * @par
6539  * Indicates whether the router role is enabled or disabled.
6540  */
Process(Arg aArgs[])6541 template <> otError Interpreter::Process<Cmd("routereligible")>(Arg aArgs[])
6542 {
6543     /**
6544      * @cli routereligible (enable,disable)
6545      * @code
6546      * routereligible enable
6547      * Done
6548      * @endcode
6549      * @code
6550      * routereligible disable
6551      * Done
6552      * @endcode
6553      * @cparam routereligible [@ca{enable|disable}]
6554      * @sa otThreadSetRouterEligible
6555      * @par
6556      * Enables or disables the router role.
6557      */
6558     return ProcessEnableDisable(aArgs, otThreadIsRouterEligible, otThreadSetRouterEligible);
6559 }
6560 
6561 /**
6562  * @cli routerselectionjitter
6563  * @code
6564  * routerselectionjitter
6565  * 120
6566  * Done
6567  * @endcode
6568  * @code
6569  * routerselectionjitter 120
6570  * Done
6571  * @endcode
6572  * @cparam routerselectionjitter [@ca{jitter}]
6573  * @par
6574  * Gets or sets the ROUTER_SELECTION_JITTER value.
6575  * @sa otThreadGetRouterSelectionJitter
6576  * @sa otThreadSetRouterSelectionJitter
6577  */
Process(Arg aArgs[])6578 template <> otError Interpreter::Process<Cmd("routerselectionjitter")>(Arg aArgs[])
6579 {
6580     return ProcessGetSet(aArgs, otThreadGetRouterSelectionJitter, otThreadSetRouterSelectionJitter);
6581 }
6582 /**
6583  * @cli routerupgradethreshold (get,set)
6584  * @code
6585  * routerupgradethreshold
6586  * 16
6587  * Done
6588  * @endcode
6589  * @code
6590  * routerupgradethreshold 16
6591  * Done
6592  * @endcode
6593  * @cparam routerupgradethreshold [@ca{threshold}]
6594  * @par
6595  * Gets or sets the ROUTER_UPGRADE_THRESHOLD value.
6596  * @sa otThreadGetRouterUpgradeThreshold
6597  * @sa otThreadSetRouterUpgradeThreshold
6598  */
Process(Arg aArgs[])6599 template <> otError Interpreter::Process<Cmd("routerupgradethreshold")>(Arg aArgs[])
6600 {
6601     return ProcessGetSet(aArgs, otThreadGetRouterUpgradeThreshold, otThreadSetRouterUpgradeThreshold);
6602 }
6603 /**
6604  * @cli childrouterlinks (get,set)
6605  * @code
6606  * childrouterlinks
6607  * 16
6608  * Done
6609  * @endcode
6610  * @code
6611  * childrouterlinks 16
6612  * Done
6613  * @endcode
6614  * @cparam childrouterlinks [@ca{links}]
6615  * @par
6616  * Gets or sets the MLE_CHILD_ROUTER_LINKS value.
6617  * @sa otThreadGetChildRouterLinks
6618  * @sa otThreadSetChildRouterLinks
6619  */
Process(Arg aArgs[])6620 template <> otError Interpreter::Process<Cmd("childrouterlinks")>(Arg aArgs[])
6621 {
6622     return ProcessGetSet(aArgs, otThreadGetChildRouterLinks, otThreadSetChildRouterLinks);
6623 }
6624 #endif // OPENTHREAD_FTD
6625 
Process(Arg aArgs[])6626 template <> otError Interpreter::Process<Cmd("scan")>(Arg aArgs[])
6627 {
6628     otError  error        = OT_ERROR_NONE;
6629     uint32_t scanChannels = 0;
6630     uint16_t scanDuration = 0;
6631     bool     energyScan   = false;
6632 
6633     if (aArgs[0] == "energy")
6634     {
6635         energyScan = true;
6636         aArgs++;
6637 
6638         if (!aArgs->IsEmpty())
6639         {
6640             SuccessOrExit(error = aArgs->ParseAsUint16(scanDuration));
6641             aArgs++;
6642         }
6643     }
6644 
6645     if (!aArgs->IsEmpty())
6646     {
6647         uint8_t channel;
6648 
6649         SuccessOrExit(error = aArgs->ParseAsUint8(channel));
6650         VerifyOrExit(channel < BitSizeOf(scanChannels), error = OT_ERROR_INVALID_ARGS);
6651         scanChannels = 1 << channel;
6652     }
6653 
6654     /**
6655      * @cli scan energy
6656      * @code
6657      * scan energy 10
6658      * | Ch | RSSI |
6659      * +----+------+
6660      * | 11 |  -59 |
6661      * | 12 |  -62 |
6662      * | 13 |  -67 |
6663      * | 14 |  -61 |
6664      * | 15 |  -87 |
6665      * | 16 |  -86 |
6666      * | 17 |  -86 |
6667      * | 18 |  -52 |
6668      * | 19 |  -58 |
6669      * | 20 |  -82 |
6670      * | 21 |  -76 |
6671      * | 22 |  -82 |
6672      * | 23 |  -74 |
6673      * | 24 |  -81 |
6674      * | 25 |  -88 |
6675      * | 26 |  -71 |
6676      * Done
6677      * @endcode
6678      * @code
6679      * scan energy 10 20
6680      * | Ch | RSSI |
6681      * +----+------+
6682      * | 20 |  -82 |
6683      * Done
6684      * @endcode
6685      * @cparam scan energy [@ca{duration}] [@ca{channel}]
6686      * @par
6687      * Performs an IEEE 802.15.4 energy scan, and displays the time in milliseconds
6688      * to use for scanning each channel. All channels are shown unless you specify a certain channel
6689      * by using the channel option.
6690      * @sa otLinkEnergyScan
6691      */
6692     if (energyScan)
6693     {
6694         static const char *const kEnergyScanTableTitles[]       = {"Ch", "RSSI"};
6695         static const uint8_t     kEnergyScanTableColumnWidths[] = {4, 6};
6696 
6697         OutputTableHeader(kEnergyScanTableTitles, kEnergyScanTableColumnWidths);
6698         SuccessOrExit(error = otLinkEnergyScan(GetInstancePtr(), scanChannels, scanDuration,
6699                                                &Interpreter::HandleEnergyScanResult, this));
6700     }
6701     /**
6702      * @cli scan
6703      * @code
6704      * scan
6705      * | PAN  | MAC Address      | Ch | dBm | LQI |
6706      * +------+------------------+----+-----+-----+
6707      * | ffff | f1d92a82c8d8fe43 | 11 | -20 |   0 |
6708      * Done
6709      * @endcode
6710      * @cparam scan [@ca{channel}]
6711      * @par
6712      * Performs an active IEEE 802.15.4 scan. The scan covers all channels if no channel is specified; otherwise the
6713      * span covers only the channel specified.
6714      * @sa otLinkActiveScan
6715      */
6716     else
6717     {
6718         static const char *const kScanTableTitles[]       = {"PAN", "MAC Address", "Ch", "dBm", "LQI"};
6719         static const uint8_t     kScanTableColumnWidths[] = {6, 18, 4, 5, 5};
6720 
6721         OutputTableHeader(kScanTableTitles, kScanTableColumnWidths);
6722 
6723         SuccessOrExit(error = otLinkActiveScan(GetInstancePtr(), scanChannels, scanDuration,
6724                                                &Interpreter::HandleActiveScanResult, this));
6725     }
6726 
6727     error = OT_ERROR_PENDING;
6728 
6729 exit:
6730     return error;
6731 }
6732 
HandleActiveScanResult(otActiveScanResult * aResult,void * aContext)6733 void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult, void *aContext)
6734 {
6735     static_cast<Interpreter *>(aContext)->HandleActiveScanResult(aResult);
6736 }
6737 
HandleActiveScanResult(otActiveScanResult * aResult)6738 void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult)
6739 {
6740     if (aResult == nullptr)
6741     {
6742         OutputResult(OT_ERROR_NONE);
6743         ExitNow();
6744     }
6745 
6746     if (aResult->mDiscover)
6747     {
6748         OutputFormat("| %-16s ", aResult->mNetworkName.m8);
6749 
6750         OutputFormat("| ");
6751         OutputBytes(aResult->mExtendedPanId.m8);
6752         OutputFormat(" ");
6753     }
6754 
6755     OutputFormat("| %04x | ", aResult->mPanId);
6756     OutputExtAddress(aResult->mExtAddress);
6757     OutputFormat(" | %2u ", aResult->mChannel);
6758     OutputFormat("| %3d ", aResult->mRssi);
6759     OutputLine("| %3u |", aResult->mLqi);
6760 
6761 exit:
6762     return;
6763 }
6764 
HandleEnergyScanResult(otEnergyScanResult * aResult,void * aContext)6765 void Interpreter::HandleEnergyScanResult(otEnergyScanResult *aResult, void *aContext)
6766 {
6767     static_cast<Interpreter *>(aContext)->HandleEnergyScanResult(aResult);
6768 }
6769 
HandleEnergyScanResult(otEnergyScanResult * aResult)6770 void Interpreter::HandleEnergyScanResult(otEnergyScanResult *aResult)
6771 {
6772     if (aResult == nullptr)
6773     {
6774         OutputResult(OT_ERROR_NONE);
6775         ExitNow();
6776     }
6777 
6778     OutputLine("| %2u | %4d |", aResult->mChannel, aResult->mMaxRssi);
6779 
6780 exit:
6781     return;
6782 }
6783 
6784 /**
6785  * @cli singleton
6786  * @code
6787  * singleton
6788  * true
6789  * Done
6790  * @endcode
6791  * @par
6792  * Indicates whether a node is the only router on the network.
6793  * Returns either `true` or `false`.
6794  * @sa otThreadIsSingleton
6795  */
Process(Arg aArgs[])6796 template <> otError Interpreter::Process<Cmd("singleton")>(Arg aArgs[])
6797 {
6798     OT_UNUSED_VARIABLE(aArgs);
6799 
6800     OutputLine(otThreadIsSingleton(GetInstancePtr()) ? "true" : "false");
6801 
6802     return OT_ERROR_NONE;
6803 }
6804 
6805 #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
Process(Arg aArgs[])6806 template <> otError Interpreter::Process<Cmd("sntp")>(Arg aArgs[])
6807 {
6808     otError          error = OT_ERROR_NONE;
6809     uint16_t         port  = OT_SNTP_DEFAULT_SERVER_PORT;
6810     Ip6::MessageInfo messageInfo;
6811     otSntpQuery      query;
6812 
6813     /**
6814      * @cli sntp query
6815      * @code
6816      * sntp query
6817      * SNTP response - Unix time: 1540894725 (era: 0)
6818      * Done
6819      * @endcode
6820      * @code
6821      * sntp query 64:ff9b::d8ef:2308
6822      * SNTP response - Unix time: 1540898611 (era: 0)
6823      * Done
6824      * @endcode
6825      * @cparam sntp query [@ca{SNTP server IP}] [@ca{SNTP server port}]
6826      * @par
6827      * Sends an SNTP query to obtain the current unix epoch time (from January 1, 1970).
6828      * - SNTP server default IP address: `2001:4860:4806:8::` (Google IPv6 NTP Server)
6829      * - SNTP server default port: `123`
6830      * @note This command is available only if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE is enabled.
6831      * @sa #otSntpClientQuery
6832      */
6833     if (aArgs[0] == "query")
6834     {
6835         VerifyOrExit(!mSntpQueryingInProgress, error = OT_ERROR_BUSY);
6836 
6837         if (!aArgs[1].IsEmpty())
6838         {
6839             SuccessOrExit(error = aArgs[1].ParseAsIp6Address(messageInfo.GetPeerAddr()));
6840         }
6841         else
6842         {
6843             // Use IPv6 address of default SNTP server.
6844             SuccessOrExit(error = messageInfo.GetPeerAddr().FromString(OT_SNTP_DEFAULT_SERVER_IP));
6845         }
6846 
6847         if (!aArgs[2].IsEmpty())
6848         {
6849             SuccessOrExit(error = aArgs[2].ParseAsUint16(port));
6850         }
6851 
6852         messageInfo.SetPeerPort(port);
6853 
6854         query.mMessageInfo = static_cast<const otMessageInfo *>(&messageInfo);
6855 
6856         SuccessOrExit(error = otSntpClientQuery(GetInstancePtr(), &query, &Interpreter::HandleSntpResponse, this));
6857 
6858         mSntpQueryingInProgress = true;
6859         error                   = OT_ERROR_PENDING;
6860     }
6861     else
6862     {
6863         error = OT_ERROR_INVALID_COMMAND;
6864     }
6865 
6866 exit:
6867     return error;
6868 }
6869 
HandleSntpResponse(void * aContext,uint64_t aTime,otError aResult)6870 void Interpreter::HandleSntpResponse(void *aContext, uint64_t aTime, otError aResult)
6871 {
6872     static_cast<Interpreter *>(aContext)->HandleSntpResponse(aTime, aResult);
6873 }
6874 
HandleSntpResponse(uint64_t aTime,otError aResult)6875 void Interpreter::HandleSntpResponse(uint64_t aTime, otError aResult)
6876 {
6877     if (aResult == OT_ERROR_NONE)
6878     {
6879         // Some Embedded C libraries do not support printing of 64-bit unsigned integers.
6880         // To simplify, unix epoch time and era number are printed separately.
6881         OutputLine("SNTP response - Unix time: %lu (era: %lu)", ToUlong(static_cast<uint32_t>(aTime)),
6882                    ToUlong(static_cast<uint32_t>(aTime >> 32)));
6883     }
6884     else
6885     {
6886         OutputLine("SNTP error - %s", otThreadErrorToString(aResult));
6887     }
6888 
6889     mSntpQueryingInProgress = false;
6890 
6891     OutputResult(OT_ERROR_NONE);
6892 }
6893 #endif // OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
6894 
6895 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
Process(Arg aArgs[])6896 template <> otError Interpreter::Process<Cmd("srp")>(Arg aArgs[])
6897 {
6898     otError error = OT_ERROR_NONE;
6899 
6900     if (aArgs[0].IsEmpty())
6901     {
6902 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
6903         OutputLine("client");
6904 #endif
6905 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
6906         OutputLine("server");
6907 #endif
6908         ExitNow();
6909     }
6910 
6911 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
6912     if (aArgs[0] == "client")
6913     {
6914         ExitNow(error = mSrpClient.Process(aArgs + 1));
6915     }
6916 #endif
6917 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
6918     if (aArgs[0] == "server")
6919     {
6920         ExitNow(error = mSrpServer.Process(aArgs + 1));
6921     }
6922 #endif
6923 
6924     error = OT_ERROR_INVALID_COMMAND;
6925 
6926 exit:
6927     return error;
6928 }
6929 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
6930 
Process(Arg aArgs[])6931 template <> otError Interpreter::Process<Cmd("state")>(Arg aArgs[])
6932 {
6933     otError error = OT_ERROR_NONE;
6934 
6935     /**
6936      * @cli state
6937      * @code
6938      * state
6939      * child
6940      * Done
6941      * @endcode
6942      * @code
6943      * state leader
6944      * Done
6945      * @endcode
6946      * @cparam state [@ca{child}|@ca{router}|@ca{leader}|@ca{detached}]
6947      * @par
6948      * Returns the current role of the Thread device, or changes the role as specified with one of the options.
6949      * Possible values returned when inquiring about the device role:
6950      * - `child`: The device is currently operating as a Thread child.
6951      * - `router`: The device is currently operating as a Thread router.
6952      * - `leader`: The device is currently operating as a Thread leader.
6953      * - `detached`: The device is not currently participating in a Thread network/partition.
6954      * - `disabled`: The Thread stack is currently disabled.
6955      * @par
6956      * Using one of the options allows you to change the current role of a device, with the exclusion of
6957      * changing to or from a `disabled` state.
6958      * @sa otThreadGetDeviceRole
6959      * @sa otThreadBecomeChild
6960      * @sa otThreadBecomeRouter
6961      * @sa otThreadBecomeLeader
6962      * @sa otThreadBecomeDetached
6963      */
6964     if (aArgs[0].IsEmpty())
6965     {
6966         OutputLine("%s", otThreadDeviceRoleToString(otThreadGetDeviceRole(GetInstancePtr())));
6967     }
6968     else if (aArgs[0] == "detached")
6969     {
6970         error = otThreadBecomeDetached(GetInstancePtr());
6971     }
6972     else if (aArgs[0] == "child")
6973     {
6974         error = otThreadBecomeChild(GetInstancePtr());
6975     }
6976 #if OPENTHREAD_FTD
6977     else if (aArgs[0] == "router")
6978     {
6979         error = otThreadBecomeRouter(GetInstancePtr());
6980     }
6981     else if (aArgs[0] == "leader")
6982     {
6983         error = otThreadBecomeLeader(GetInstancePtr());
6984     }
6985 #endif
6986     else
6987     {
6988         error = OT_ERROR_INVALID_ARGS;
6989     }
6990 
6991     return error;
6992 }
6993 
Process(Arg aArgs[])6994 template <> otError Interpreter::Process<Cmd("thread")>(Arg aArgs[])
6995 {
6996     otError error = OT_ERROR_NONE;
6997 
6998     /**
6999      * @cli thread start
7000      * @code
7001      * thread start
7002      * Done
7003      * @endcode
7004      * @par
7005      * Starts the Thread protocol operation.
7006      * @note The interface must be up when running this command.
7007      * @sa otThreadSetEnabled
7008      */
7009     if (aArgs[0] == "start")
7010     {
7011         error = otThreadSetEnabled(GetInstancePtr(), true);
7012     }
7013     /**
7014      * @cli thread stop
7015      * @code
7016      * thread stop
7017      * Done
7018      * @endcode
7019      * @par
7020      * Stops the Thread protocol operation.
7021      */
7022     else if (aArgs[0] == "stop")
7023     {
7024         error = otThreadSetEnabled(GetInstancePtr(), false);
7025     }
7026     /**
7027      * @cli thread version
7028      * @code thread version
7029      * 2
7030      * Done
7031      * @endcode
7032      * @par api_copy
7033      * #otThreadGetVersion
7034      */
7035     else if (aArgs[0] == "version")
7036     {
7037         OutputLine("%u", otThreadGetVersion());
7038     }
7039     else
7040     {
7041         error = OT_ERROR_INVALID_COMMAND;
7042     }
7043 
7044     return error;
7045 }
7046 
7047 #if OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE
Process(Arg aArgs[])7048 template <> otError Interpreter::Process<Cmd("timeinqueue")>(Arg aArgs[])
7049 {
7050     otError error = OT_ERROR_NONE;
7051 
7052     /**
7053      * @cli timeinqueue
7054      * @code
7055      * timeinqueue
7056      * | Min  | Max  |Msg Count|
7057      * +------+------+---------+
7058      * |    0 |    9 |    1537 |
7059      * |   10 |   19 |     156 |
7060      * |   20 |   29 |      57 |
7061      * |   30 |   39 |     108 |
7062      * |   40 |   49 |      60 |
7063      * |   50 |   59 |      76 |
7064      * |   60 |   69 |      88 |
7065      * |   70 |   79 |      51 |
7066      * |   80 |   89 |      86 |
7067      * |   90 |   99 |      45 |
7068      * |  100 |  109 |      43 |
7069      * |  110 |  119 |      44 |
7070      * |  120 |  129 |      38 |
7071      * |  130 |  139 |      44 |
7072      * |  140 |  149 |      35 |
7073      * |  150 |  159 |      41 |
7074      * |  160 |  169 |      34 |
7075      * |  170 |  179 |      13 |
7076      * |  180 |  189 |      24 |
7077      * |  190 |  199 |       3 |
7078      * |  200 |  209 |       0 |
7079      * |  210 |  219 |       0 |
7080      * |  220 |  229 |       2 |
7081      * |  230 |  239 |       0 |
7082      * |  240 |  249 |       0 |
7083      * |  250 |  259 |       0 |
7084      * |  260 |  269 |       0 |
7085      * |  270 |  279 |       0 |
7086      * |  280 |  289 |       0 |
7087      * |  290 |  299 |       1 |
7088      * |  300 |  309 |       0 |
7089      * |  310 |  319 |       0 |
7090      * |  320 |  329 |       0 |
7091      * |  330 |  339 |       0 |
7092      * |  340 |  349 |       0 |
7093      * |  350 |  359 |       0 |
7094      * |  360 |  369 |       0 |
7095      * |  370 |  379 |       0 |
7096      * |  380 |  389 |       0 |
7097      * |  390 |  399 |       0 |
7098      * |  400 |  409 |       0 |
7099      * |  410 |  419 |       0 |
7100      * |  420 |  429 |       0 |
7101      * |  430 |  439 |       0 |
7102      * |  440 |  449 |       0 |
7103      * |  450 |  459 |       0 |
7104      * |  460 |  469 |       0 |
7105      * |  470 |  479 |       0 |
7106      * |  480 |  489 |       0 |
7107      * |  490 |  inf |       0 |
7108      * Done
7109      * @endcode
7110      * @par api_copy
7111      * #otThreadGetTimeInQueueHistogram
7112      * @csa{timeinqueue reset}
7113      */
7114     if (aArgs[0].IsEmpty())
7115     {
7116         static const char *const kTimeInQueueTableTitles[]       = {"Min", "Max", "Msg Count"};
7117         static const uint8_t     kTimeInQueueTableColumnWidths[] = {6, 6, 9};
7118 
7119         uint16_t        numBins;
7120         uint32_t        binInterval;
7121         const uint32_t *histogram;
7122 
7123         OutputTableHeader(kTimeInQueueTableTitles, kTimeInQueueTableColumnWidths);
7124 
7125         histogram = otThreadGetTimeInQueueHistogram(GetInstancePtr(), &numBins, &binInterval);
7126 
7127         for (uint16_t index = 0; index < numBins; index++)
7128         {
7129             OutputFormat("| %4lu | ", ToUlong(index * binInterval));
7130 
7131             if (index < numBins - 1)
7132             {
7133                 OutputFormat("%4lu", ToUlong((index + 1) * binInterval - 1));
7134             }
7135             else
7136             {
7137                 OutputFormat("%4s", "inf");
7138             }
7139 
7140             OutputLine(" | %7lu |", ToUlong(histogram[index]));
7141         }
7142     }
7143     /**
7144      * @cli timeinqueue max
7145      * @code
7146      * timeinqueue max
7147      * 281
7148      * Done
7149      * @endcode
7150      * @par api_copy
7151      * #otThreadGetMaxTimeInQueue
7152      * @csa{timeinqueue reset}
7153      */
7154     else if (aArgs[0] == "max")
7155     {
7156         OutputLine("%lu", ToUlong(otThreadGetMaxTimeInQueue(GetInstancePtr())));
7157     }
7158     /**
7159      * @cli timeinqueue reset
7160      * @code
7161      * timeinqueue reset
7162      * Done
7163      * @endcode
7164      * @par api_copy
7165      * #otThreadResetTimeInQueueStat
7166      */
7167     else if (aArgs[0] == "reset")
7168     {
7169         otThreadResetTimeInQueueStat(GetInstancePtr());
7170     }
7171     else
7172     {
7173         error = OT_ERROR_INVALID_ARGS;
7174     }
7175 
7176     return error;
7177 }
7178 #endif // OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE
7179 
Process(Arg aArgs[])7180 template <> otError Interpreter::Process<Cmd("dataset")>(Arg aArgs[]) { return mDataset.Process(aArgs); }
7181 
7182 /**
7183  * @cli txpower (get,set)
7184  * @code
7185  * txpower -10
7186  * Done
7187  * @endcode
7188  * @code
7189  * txpower
7190  * -10 dBm
7191  * Done
7192  * @endcode
7193  * @cparam txpower [@ca{txpower}]
7194  * @par
7195  * Gets (or sets with the use of the optional `txpower` argument) the transmit power in dBm.
7196  * @sa otPlatRadioGetTransmitPower
7197  * @sa otPlatRadioSetTransmitPower
7198  */
Process(Arg aArgs[])7199 template <> otError Interpreter::Process<Cmd("txpower")>(Arg aArgs[])
7200 {
7201     otError error = OT_ERROR_NONE;
7202     int8_t  power;
7203 
7204     if (aArgs[0].IsEmpty())
7205     {
7206         SuccessOrExit(error = otPlatRadioGetTransmitPower(GetInstancePtr(), &power));
7207         OutputLine("%d dBm", power);
7208     }
7209     else
7210     {
7211         SuccessOrExit(error = aArgs[0].ParseAsInt8(power));
7212         error = otPlatRadioSetTransmitPower(GetInstancePtr(), power);
7213     }
7214 
7215 exit:
7216     return error;
7217 }
7218 
7219 /**
7220  * @cli debug
7221  * @par
7222  * Executes a series of CLI commands to gather information about the device and thread network. This is intended for
7223  * debugging.
7224  * The output will display each executed CLI command preceded by `$`, followed by the corresponding command's
7225  * generated output.
7226  * The generated output encompasses the following information:
7227  * - Version
7228  * - Current state
7229  * - RLOC16, extended MAC address
7230  * - Unicast and multicast IPv6 address list
7231  * - Channel
7232  * - PAN ID and extended PAN ID
7233  * - Network Data
7234  * - Partition ID
7235  * - Leader Data
7236  * @par
7237  * If the device is operating as FTD:
7238  * - Child and neighbor table
7239  * - Router table and next hop info
7240  * - Address cache table
7241  * - Registered MTD child IPv6 address
7242  * - Device properties
7243  * @par
7244  * If the device supports and acts as an SRP client:
7245  * - SRP client state
7246  * - SRP client services and host info
7247  * @par
7248  * If the device supports and acts as an SRP sever:
7249  * - SRP server state and address mode
7250  * - SRP server registered hosts and services
7251  * @par
7252  * If the device supports TREL:
7253  * - TREL status and peer table
7254  * @par
7255  * If the device supports and acts as a border router:
7256  * - BR state
7257  * - BR prefixes (OMR, on-link, NAT64)
7258  * - Discovered prefix table
7259  */
Process(Arg aArgs[])7260 template <> otError Interpreter::Process<Cmd("debug")>(Arg aArgs[])
7261 {
7262     static constexpr uint16_t kMaxDebugCommandSize = 30;
7263 
7264     static const char *const kDebugCommands[] = {
7265         "version",
7266         "state",
7267         "rloc16",
7268         "extaddr",
7269         "ipaddrs",
7270         "ipmaddrs",
7271         "channel",
7272         "panid",
7273         "extpanid",
7274         "netdata show",
7275         "netdata show -x",
7276         "partitionid",
7277         "leaderdata",
7278 #if OPENTHREAD_FTD
7279         "child table",
7280         "childip",
7281         "neighbor table",
7282         "router table",
7283         "nexthop",
7284         "eidcache",
7285 #if OPENTHREAD_CONFIG_MLE_DEVICE_PROPERTY_LEADER_WEIGHT_ENABLE
7286         "deviceprops",
7287 #endif
7288 #endif // OPENTHREAD_FTD
7289 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
7290         "srp client state",
7291         "srp client host",
7292         "srp client service",
7293         "srp client server",
7294 #endif
7295 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
7296         "srp server state",
7297         "srp server addrmode",
7298         "srp server host",
7299         "srp server service",
7300 #endif
7301 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
7302         "trel",
7303         "trel peers",
7304 #endif
7305 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
7306         "br state",
7307         "br omrprefix",
7308         "br onlinkprefix",
7309         "br prefixtable",
7310 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
7311         "br nat64prefix",
7312 #endif
7313 #endif
7314         "bufferinfo",
7315     };
7316 
7317     char commandString[kMaxDebugCommandSize];
7318 
7319     OT_UNUSED_VARIABLE(aArgs);
7320 
7321     mInternalDebugCommand = true;
7322 
7323     for (const char *debugCommand : kDebugCommands)
7324     {
7325         strncpy(commandString, debugCommand, sizeof(commandString) - 1);
7326         commandString[sizeof(commandString) - 1] = '\0';
7327 
7328         OutputLine("$ %s", commandString);
7329         ProcessLine(commandString);
7330     }
7331 
7332     mInternalDebugCommand = false;
7333 
7334     return OT_ERROR_NONE;
7335 }
7336 
7337 #if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE
Process(Arg aArgs[])7338 template <> otError Interpreter::Process<Cmd("tcat")>(Arg aArgs[]) { return mTcat.Process(aArgs); }
7339 #endif
7340 
7341 #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
Process(Arg aArgs[])7342 template <> otError Interpreter::Process<Cmd("tcp")>(Arg aArgs[]) { return mTcp.Process(aArgs); }
7343 #endif
7344 
Process(Arg aArgs[])7345 template <> otError Interpreter::Process<Cmd("udp")>(Arg aArgs[]) { return mUdp.Process(aArgs); }
7346 
Process(Arg aArgs[])7347 template <> otError Interpreter::Process<Cmd("unsecureport")>(Arg aArgs[])
7348 {
7349     otError error = OT_ERROR_NONE;
7350 
7351     /**
7352      * @cli unsecureport add
7353      * @code
7354      * unsecureport add 1234
7355      * Done
7356      * @endcode
7357      * @cparam unsecureport add @ca{port}
7358      * @par api_copy
7359      * #otIp6AddUnsecurePort
7360      */
7361     if (aArgs[0] == "add")
7362     {
7363         error = ProcessSet(aArgs + 1, otIp6AddUnsecurePort);
7364     }
7365     /**
7366      * @cli unsecureport remove
7367      * @code
7368      * unsecureport remove 1234
7369      * Done
7370      * @endcode
7371      * @code
7372      * unsecureport remove all
7373      * Done
7374      * @endcode
7375      * @cparam unsecureport remove @ca{port}|all
7376      * @par
7377      * Removes a specified port or all ports from the allowed unsecured port list.
7378      * @sa otIp6AddUnsecurePort
7379      * @sa otIp6RemoveAllUnsecurePorts
7380      */
7381     else if (aArgs[0] == "remove")
7382     {
7383         if (aArgs[1] == "all")
7384         {
7385             otIp6RemoveAllUnsecurePorts(GetInstancePtr());
7386         }
7387         else
7388         {
7389             error = ProcessSet(aArgs + 1, otIp6RemoveUnsecurePort);
7390         }
7391     }
7392     /**
7393      * @cli unsecure get
7394      * @code
7395      * unsecure get
7396      * 1234
7397      * Done
7398      * @endcode
7399      * @par
7400      * Lists all ports from the allowed unsecured port list.
7401      * @sa otIp6GetUnsecurePorts
7402      */
7403     else if (aArgs[0] == "get")
7404     {
7405         const uint16_t *ports;
7406         uint8_t         numPorts;
7407 
7408         ports = otIp6GetUnsecurePorts(GetInstancePtr(), &numPorts);
7409 
7410         if (ports != nullptr)
7411         {
7412             for (uint8_t i = 0; i < numPorts; i++)
7413             {
7414                 OutputFormat("%u ", ports[i]);
7415             }
7416         }
7417 
7418         OutputNewLine();
7419     }
7420     else
7421     {
7422         error = OT_ERROR_INVALID_COMMAND;
7423     }
7424 
7425     return error;
7426 }
7427 
7428 #if OPENTHREAD_CONFIG_UPTIME_ENABLE
Process(Arg aArgs[])7429 template <> otError Interpreter::Process<Cmd("uptime")>(Arg aArgs[])
7430 {
7431     otError error = OT_ERROR_NONE;
7432 
7433     /**
7434      * @cli uptime
7435      * @code
7436      * uptime
7437      * 12:46:35.469
7438      * Done
7439      * @endcode
7440      * @par api_copy
7441      * #otInstanceGetUptimeAsString
7442      */
7443     if (aArgs[0].IsEmpty())
7444     {
7445         char string[OT_UPTIME_STRING_SIZE];
7446 
7447         otInstanceGetUptimeAsString(GetInstancePtr(), string, sizeof(string));
7448         OutputLine("%s", string);
7449     }
7450 
7451     /**
7452      * @cli uptime ms
7453      * @code
7454      * uptime ms
7455      * 426238
7456      * Done
7457      * @endcode
7458      * @par api_copy
7459      * #otInstanceGetUptime
7460      */
7461     else if (aArgs[0] == "ms")
7462     {
7463         OutputUint64Line(otInstanceGetUptime(GetInstancePtr()));
7464     }
7465     else
7466     {
7467         error = OT_ERROR_INVALID_ARGS;
7468     }
7469 
7470     return error;
7471 }
7472 #endif
7473 
7474 #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
Process(Arg aArgs[])7475 template <> otError Interpreter::Process<Cmd("commissioner")>(Arg aArgs[]) { return mCommissioner.Process(aArgs); }
7476 #endif
7477 
7478 #if OPENTHREAD_CONFIG_JOINER_ENABLE
Process(Arg aArgs[])7479 template <> otError Interpreter::Process<Cmd("joiner")>(Arg aArgs[]) { return mJoiner.Process(aArgs); }
7480 #endif
7481 
7482 #if OPENTHREAD_FTD
7483 /**
7484  * @cli joinerport
7485  * @code
7486  * joinerport
7487  * 1000
7488  * Done
7489  * @endcode
7490  * @par api_copy
7491  * #otThreadGetJoinerUdpPort
7492  */
Process(Arg aArgs[])7493 template <> otError Interpreter::Process<Cmd("joinerport")>(Arg aArgs[])
7494 {
7495     /**
7496      * @cli joinerport (set)
7497      * @code
7498      * joinerport 1000
7499      * Done
7500      * @endcode
7501      * @cparam joinerport @ca{udp-port}
7502      * @par api_copy
7503      * #otThreadSetJoinerUdpPort
7504      */
7505     return ProcessGetSet(aArgs, otThreadGetJoinerUdpPort, otThreadSetJoinerUdpPort);
7506 }
7507 #endif
7508 
7509 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
Process(Arg aArgs[])7510 template <> otError Interpreter::Process<Cmd("macfilter")>(Arg aArgs[]) { return mMacFilter.Process(aArgs); }
7511 #endif
7512 
Process(Arg aArgs[])7513 template <> otError Interpreter::Process<Cmd("mac")>(Arg aArgs[])
7514 {
7515     otError error = OT_ERROR_NONE;
7516 
7517     if (aArgs[0] == "retries")
7518     {
7519         /**
7520          * @cli mac retries direct (get,set)
7521          * @code
7522          * mac retries direct
7523          * 3
7524          * Done
7525          * @endcode
7526          * @code
7527          * mac retries direct 5
7528          * Done
7529          * @endcode
7530          * @cparam mac retries direct [@ca{number}]
7531          * Use the optional `number` argument to set the number of direct TX retries.
7532          * @par
7533          * Gets or sets the number of direct TX retries on the MAC layer.
7534          * @sa otLinkGetMaxFrameRetriesDirect
7535          * @sa otLinkSetMaxFrameRetriesDirect
7536          */
7537         if (aArgs[1] == "direct")
7538         {
7539             error = ProcessGetSet(aArgs + 2, otLinkGetMaxFrameRetriesDirect, otLinkSetMaxFrameRetriesDirect);
7540         }
7541 #if OPENTHREAD_FTD
7542         /**
7543          * @cli mac retries indirect (get,set)
7544          * @code
7545          * mac retries indirect
7546          * 3
7547          * Done
7548          * @endcode
7549          * @code max retries indirect 5
7550          * Done
7551          * @endcode
7552          * @cparam mac retries indirect [@ca{number}]
7553          * Use the optional `number` argument to set the number of indirect Tx retries.
7554          * @par
7555          * Gets or sets the number of indirect TX retries on the MAC layer.
7556          * @sa otLinkGetMaxFrameRetriesIndirect
7557          * @sa otLinkSetMaxFrameRetriesIndirect
7558          */
7559         else if (aArgs[1] == "indirect")
7560         {
7561             error = ProcessGetSet(aArgs + 2, otLinkGetMaxFrameRetriesIndirect, otLinkSetMaxFrameRetriesIndirect);
7562         }
7563 #endif
7564         else
7565         {
7566             error = OT_ERROR_INVALID_ARGS;
7567         }
7568     }
7569 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
7570     /**
7571      * @cli mac send
7572      * @code
7573      * mac send datarequest
7574      * Done
7575      * @endcode
7576      * @code
7577      * mac send emptydata
7578      * Done
7579      * @endcode
7580      * @cparam mac send @ca{datarequest} | @ca{emptydata}
7581      * You must choose one of the following two arguments:
7582      * - `datarequest`: Enqueues an IEEE 802.15.4 Data Request message for transmission.
7583      * - `emptydata`: Instructs the device to send an empty IEEE 802.15.4 data frame.
7584      * @par
7585      * Instructs an `Rx-Off-When-Idle` device to send a MAC frame to its parent.
7586      * This command is for certification, and can only be used when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is
7587      * enabled.
7588      * @sa otLinkSendDataRequest
7589      * @sa otLinkSendEmptyData
7590      */
7591     else if (aArgs[0] == "send")
7592     {
7593         VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
7594 
7595         if (aArgs[1] == "datarequest")
7596         {
7597             error = otLinkSendDataRequest(GetInstancePtr());
7598         }
7599         else if (aArgs[1] == "emptydata")
7600         {
7601             error = otLinkSendEmptyData(GetInstancePtr());
7602         }
7603         else
7604         {
7605             error = OT_ERROR_INVALID_ARGS;
7606         }
7607     }
7608 #endif
7609     else
7610     {
7611         error = OT_ERROR_INVALID_COMMAND;
7612         ExitNow(); // To silence unused `exit` label warning when `REFERENCE_DEVICE_ENABLE` is not enabled.
7613     }
7614 
7615 exit:
7616     return error;
7617 }
7618 
7619 /**
7620  * @cli trel
7621  * @code
7622  * trel
7623  * Enabled
7624  * Done
7625  * @endcode
7626  * @par api_copy
7627  * #otTrelIsEnabled
7628  * @note `OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE` is required for all `trel` sub-commands.
7629  */
7630 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
Process(Arg aArgs[])7631 template <> otError Interpreter::Process<Cmd("trel")>(Arg aArgs[])
7632 {
7633     otError error = OT_ERROR_NONE;
7634 
7635     /**
7636      * @cli trel (enable,disable)
7637      * @code
7638      * trel enable
7639      * Done
7640      * @endcode
7641      * @code
7642      * trel disable
7643      * Done
7644      * @endcode
7645      * @cparam trel @ca{enable}|@ca{disable}
7646      * @par
7647      * Enables or disables the TREL radio operation.
7648      * @sa otTrelSetEnabled
7649      */
7650     if (ProcessEnableDisable(aArgs, otTrelIsEnabled, otTrelSetEnabled) == OT_ERROR_NONE)
7651     {
7652     }
7653     /**
7654      * @cli trel filter
7655      * @code
7656      * trel filter
7657      * Disabled
7658      * Done
7659      * @endcode
7660      * @par
7661      * Indicates whether TREL filter mode is enabled.
7662      * @par
7663      * When filter mode is enabled, all Rx and Tx traffic sent through the TREL interface gets silently dropped.
7664      * @note This mode is used mostly for testing.
7665      * @sa otTrelIsFilterEnabled
7666      */
7667     else if (aArgs[0] == "filter")
7668     /**
7669      * @cli trel filter (enable,disable)
7670      * @code
7671      * trel filter enable
7672      * Done
7673      * @endcode
7674      * @code
7675      * trel filter disable
7676      * Done
7677      * @endcode
7678      * @cparam trel filter @ca{enable}|@ca{disable}
7679      * @par
7680      * Enables or disables TREL filter mode.
7681      * @sa otTrelSetFilterEnabled
7682      */
7683     {
7684         error = ProcessEnableDisable(aArgs + 1, otTrelIsFilterEnabled, otTrelSetFilterEnabled);
7685     }
7686     /**
7687      * @cli trel peers
7688      * @code
7689      * trel peers
7690      * | No  | Ext MAC Address  | Ext PAN Id       | IPv6 Socket Address                              |
7691      * +-----+------------------+------------------+--------------------------------------------------+
7692      * |   1 | 5e5785ba3a63adb9 | f0d9c001f00d2e43 | [fe80:0:0:0:cc79:2a29:d311:1aea]:9202            |
7693      * |   2 | ce792a29d3111aea | dead00beef00cafe | [fe80:0:0:0:5c57:85ba:3a63:adb9]:9203            |
7694      * Done
7695      * @endcode
7696      * @code
7697      * trel peers list
7698      * 001 ExtAddr:5e5785ba3a63adb9 ExtPanId:f0d9c001f00d2e43 SockAddr:[fe80:0:0:0:cc79:2a29:d311:1aea]:9202
7699      * 002 ExtAddr:ce792a29d3111aea ExtPanId:dead00beef00cafe SockAddr:[fe80:0:0:0:5c57:85ba:3a63:adb9]:9203
7700      * Done
7701      * @endcode
7702      * @cparam trel peers [@ca{list}]
7703      * @par
7704      * Gets the TREL peer table in table or list format.
7705      * @sa otTrelGetNextPeer
7706      */
7707     else if (aArgs[0] == "peers")
7708     {
7709         uint16_t           index = 0;
7710         otTrelPeerIterator iterator;
7711         const otTrelPeer  *peer;
7712         bool               isTable = true;
7713 
7714         if (aArgs[1] == "list")
7715         {
7716             isTable = false;
7717         }
7718         else
7719         {
7720             VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
7721         }
7722 
7723         if (isTable)
7724         {
7725             static const char *const kTrelPeerTableTitles[] = {"No", "Ext MAC Address", "Ext PAN Id",
7726                                                                "IPv6 Socket Address"};
7727 
7728             static const uint8_t kTrelPeerTableColumnWidths[] = {5, 18, 18, 50};
7729 
7730             OutputTableHeader(kTrelPeerTableTitles, kTrelPeerTableColumnWidths);
7731         }
7732 
7733         otTrelInitPeerIterator(GetInstancePtr(), &iterator);
7734 
7735         while ((peer = otTrelGetNextPeer(GetInstancePtr(), &iterator)) != nullptr)
7736         {
7737             if (!isTable)
7738             {
7739                 OutputFormat("%03u ExtAddr:", ++index);
7740                 OutputExtAddress(peer->mExtAddress);
7741                 OutputFormat(" ExtPanId:");
7742                 OutputBytes(peer->mExtPanId.m8);
7743                 OutputFormat(" SockAddr:");
7744                 OutputSockAddrLine(peer->mSockAddr);
7745             }
7746             else
7747             {
7748                 char string[OT_IP6_SOCK_ADDR_STRING_SIZE];
7749 
7750                 OutputFormat("| %3u | ", ++index);
7751                 OutputExtAddress(peer->mExtAddress);
7752                 OutputFormat(" | ");
7753                 OutputBytes(peer->mExtPanId.m8);
7754                 otIp6SockAddrToString(&peer->mSockAddr, string, sizeof(string));
7755                 OutputLine(" | %-48s |", string);
7756             }
7757         }
7758     }
7759     /**
7760      * @cli trel counters
7761      * @code
7762      * trel counters
7763      * Inbound:  Packets 32 Bytes 4000
7764      * Outbound: Packets 4 Bytes 320 Failures 1
7765      * Done
7766      * @endcode
7767      * @par api_copy
7768      * #otTrelGetCounters
7769      */
7770     else if (aArgs[0] == "counters")
7771     {
7772         if (aArgs[1].IsEmpty())
7773         {
7774             OutputTrelCounters(*otTrelGetCounters(GetInstancePtr()));
7775         }
7776         /**
7777          * @cli trel counters reset
7778          * @code
7779          * trel counters reset
7780          * Done
7781          * @endcode
7782          * @par api_copy
7783          * #otTrelResetCounters
7784          */
7785         else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty())
7786         {
7787             otTrelResetCounters(GetInstancePtr());
7788         }
7789         else
7790         {
7791             error = OT_ERROR_INVALID_ARGS;
7792         }
7793     }
7794     else
7795     {
7796         error = OT_ERROR_INVALID_ARGS;
7797     }
7798 
7799 exit:
7800     return error;
7801 }
7802 
OutputTrelCounters(const otTrelCounters & aCounters)7803 void Interpreter::OutputTrelCounters(const otTrelCounters &aCounters)
7804 {
7805     Uint64StringBuffer u64StringBuffer;
7806 
7807     OutputFormat("Inbound: Packets %s ", Uint64ToString(aCounters.mRxPackets, u64StringBuffer));
7808     OutputLine("Bytes %s", Uint64ToString(aCounters.mRxBytes, u64StringBuffer));
7809 
7810     OutputFormat("Outbound: Packets %s ", Uint64ToString(aCounters.mTxPackets, u64StringBuffer));
7811     OutputFormat("Bytes %s ", Uint64ToString(aCounters.mTxBytes, u64StringBuffer));
7812     OutputLine("Failures %s", Uint64ToString(aCounters.mTxFailure, u64StringBuffer));
7813 }
7814 
7815 #endif
7816 
Process(Arg aArgs[])7817 template <> otError Interpreter::Process<Cmd("vendor")>(Arg aArgs[])
7818 {
7819     Error error = OT_ERROR_INVALID_ARGS;
7820 
7821     /**
7822      * @cli vendor name
7823      * @code
7824      * vendor name
7825      * nest
7826      * Done
7827      * @endcode
7828      * @par api_copy
7829      * #otThreadGetVendorName
7830      */
7831     if (aArgs[0] == "name")
7832     {
7833         aArgs++;
7834 
7835 #if !OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
7836         error = ProcessGet(aArgs, otThreadGetVendorName);
7837 #else
7838         /**
7839          * @cli vendor name (set)
7840          * @code
7841          * vendor name nest
7842          * Done
7843          * @endcode
7844          * @par api_copy
7845          * #otThreadSetVendorName
7846          * @cparam vendor name @ca{name}
7847          */
7848         error = ProcessGetSet(aArgs, otThreadGetVendorName, otThreadSetVendorName);
7849 #endif
7850     }
7851     /**
7852      * @cli vendor model
7853      * @code
7854      * vendor model
7855      * Hub Max
7856      * Done
7857      * @endcode
7858      * @par api_copy
7859      * #otThreadGetVendorModel
7860      */
7861     else if (aArgs[0] == "model")
7862     {
7863         aArgs++;
7864 
7865 #if !OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
7866         error = ProcessGet(aArgs, otThreadGetVendorModel);
7867 #else
7868         /**
7869          * @cli vendor model (set)
7870          * @code
7871          * vendor model Hub\ Max
7872          * Done
7873          * @endcode
7874          * @par api_copy
7875          * #otThreadSetVendorModel
7876          * @cparam vendor model @ca{name}
7877          */
7878         error = ProcessGetSet(aArgs, otThreadGetVendorModel, otThreadSetVendorModel);
7879 #endif
7880     }
7881     /**
7882      * @cli vendor swversion
7883      * @code
7884      * vendor swversion
7885      * Marble3.5.1
7886      * Done
7887      * @endcode
7888      * @par api_copy
7889      * #otThreadGetVendorSwVersion
7890      */
7891     else if (aArgs[0] == "swversion")
7892     {
7893         aArgs++;
7894 
7895 #if !OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
7896         error = ProcessGet(aArgs, otThreadGetVendorSwVersion);
7897 #else
7898         /**
7899          * @cli vendor swversion (set)
7900          * @code
7901          * vendor swversion Marble3.5.1
7902          * Done
7903          * @endcode
7904          * @par api_copy
7905          * #otThreadSetVendorSwVersion
7906          * @cparam vendor swversion @ca{version}
7907          */
7908         error = ProcessGetSet(aArgs, otThreadGetVendorSwVersion, otThreadSetVendorSwVersion);
7909 #endif
7910     }
7911     /**
7912      * @cli vendor appurl
7913      * @code
7914      * vendor appurl
7915      * http://www.example.com
7916      * Done
7917      * @endcode
7918      * @par api_copy
7919      * #otThreadGetVendorAppUrl
7920      */
7921     else if (aArgs[0] == "appurl")
7922     {
7923         aArgs++;
7924 
7925 #if !OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
7926         error = ProcessGet(aArgs, otThreadGetVendorAppUrl);
7927 #else
7928         /**
7929          * @cli vendor appurl (set)
7930          * @code
7931          * vendor appurl http://www.example.com
7932          * Done
7933          * @endcode
7934          * @par api_copy
7935          * #otThreadSetVendorAppUrl
7936          * @cparam vendor appurl @ca{url}
7937          */
7938         error = ProcessGetSet(aArgs, otThreadGetVendorAppUrl, otThreadSetVendorAppUrl);
7939 #endif
7940     }
7941 
7942     return error;
7943 }
7944 
7945 #if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE
7946 
Process(Arg aArgs[])7947 template <> otError Interpreter::Process<Cmd("networkdiagnostic")>(Arg aArgs[])
7948 {
7949     static constexpr uint16_t kMaxTlvs = 35;
7950 
7951     otError      error = OT_ERROR_NONE;
7952     otIp6Address address;
7953     uint8_t      tlvTypes[kMaxTlvs];
7954     uint8_t      count = 0;
7955 
7956     SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
7957 
7958     for (Arg *arg = &aArgs[2]; !arg->IsEmpty(); arg++)
7959     {
7960         VerifyOrExit(count < sizeof(tlvTypes), error = OT_ERROR_INVALID_ARGS);
7961         SuccessOrExit(error = arg->ParseAsUint8(tlvTypes[count++]));
7962     }
7963 
7964     /**
7965      * @cli networkdiagnostic get
7966      * @code
7967      * networkdiagnostic get fdde:ad00:beef:0:0:ff:fe00:fc00 0 1 6 23
7968      * DIAG_GET.rsp/ans: 00080e336e1c41494e1c01020c000608640b0f674074c503
7969      * Ext Address: 0e336e1c41494e1c
7970      * Rloc16: 0x0c00
7971      * Leader Data:
7972      *     PartitionId: 0x640b0f67
7973      *     Weighting: 64
7974      *     DataVersion: 116
7975      *     StableDataVersion: 197
7976      *     LeaderRouterId: 0x03
7977      * EUI64: 18b4300000000004
7978      * Done
7979      * @endcode
7980      * @code
7981      * networkdiagnostic get ff02::1 0 1
7982      * DIAG_GET.rsp/ans: 00080e336e1c41494e1c01020c00
7983      * Ext Address: '0e336e1c41494e1c'
7984      * Rloc16: 0x0c00
7985      * Done
7986      * DIAG_GET.rsp/ans: 00083efcdb7e3f9eb0f201021800
7987      * Ext Address: 3efcdb7e3f9eb0f2
7988      * Rloc16: 0x1800
7989      * Done
7990      * @endcode
7991      * @cparam networkdiagnostic get @ca{addr} @ca{type(s)}
7992      * For `addr`, a unicast address triggers a `Diagnostic Get`.
7993      * A multicast address triggers a `Diagnostic Query`.
7994      * TLV values you can specify (separated by a space if you specify more than one TLV):
7995      * - `0`: MAC Extended Address TLV
7996      * - `1`: Address16 TLV
7997      * - `2`: Mode TLV
7998      * - `3`: Timeout TLV (the maximum polling time period for SEDs)
7999      * - `4`: Connectivity TLV
8000      * - `5`: Route64 TLV
8001      * - `6`: Leader Data TLV
8002      * - `7`: Network Data TLV
8003      * - `8`: IPv6 Address List TLV
8004      * - `9`: MAC Counters TLV
8005      * - `14`: Battery Level TLV
8006      * - `15`: Supply Voltage TLV
8007      * - `16`: Child Table TLV
8008      * - `17`: Channel Pages TLV
8009      * - `19`: Max Child Timeout TLV
8010      * - `23`: EUI64 TLV
8011      * - `24`: Version TLV (version number for the protocols and features)
8012      * - `25`: Vendor Name TLV
8013      * - `26`: Vendor Model TLV
8014      * - `27`: Vendor SW Version TLV
8015      * - `28`: Thread Stack Version TLV (version identifier as UTF-8 string for Thread stack codebase/commit/version)
8016      * - `29`: Child TLV
8017      * - `34`: MLE Counters TLV
8018      * - `35`: Vendor App URL TLV
8019      * @par
8020      * Sends a network diagnostic request to retrieve specified Type Length Values (TLVs)
8021      * for the specified addresses(es).
8022      * @sa otThreadSendDiagnosticGet
8023      */
8024 
8025     if (aArgs[0] == "get")
8026     {
8027         SuccessOrExit(error = otThreadSendDiagnosticGet(GetInstancePtr(), &address, tlvTypes, count,
8028                                                         &Interpreter::HandleDiagnosticGetResponse, this));
8029         SetCommandTimeout(kNetworkDiagnosticTimeoutMsecs);
8030         error = OT_ERROR_PENDING;
8031     }
8032     /**
8033      * @cli networkdiagnostic reset
8034      * @code
8035      * networkdiagnostic reset fd00:db8::ff:fe00:0 9
8036      * Done
8037      * @endcode
8038      * @cparam networkdiagnostic reset @ca{addr} @ca{type(s)}
8039      * @par
8040      * Sends a network diagnostic request to reset the specified Type Length Values (TLVs)
8041      * on the specified address(es). This command only supports the
8042      * following TLV values: `9` (MAC Counters TLV) or `34` (MLE
8043      * Counters TLV)
8044      * @sa otThreadSendDiagnosticReset
8045      */
8046     else if (aArgs[0] == "reset")
8047     {
8048         IgnoreError(otThreadSendDiagnosticReset(GetInstancePtr(), &address, tlvTypes, count));
8049     }
8050     else
8051     {
8052         error = OT_ERROR_INVALID_COMMAND;
8053     }
8054 
8055 exit:
8056     return error;
8057 }
8058 
HandleDiagnosticGetResponse(otError aError,otMessage * aMessage,const otMessageInfo * aMessageInfo,void * aContext)8059 void Interpreter::HandleDiagnosticGetResponse(otError              aError,
8060                                               otMessage           *aMessage,
8061                                               const otMessageInfo *aMessageInfo,
8062                                               void                *aContext)
8063 {
8064     static_cast<Interpreter *>(aContext)->HandleDiagnosticGetResponse(
8065         aError, aMessage, static_cast<const Ip6::MessageInfo *>(aMessageInfo));
8066 }
8067 
HandleDiagnosticGetResponse(otError aError,const otMessage * aMessage,const Ip6::MessageInfo * aMessageInfo)8068 void Interpreter::HandleDiagnosticGetResponse(otError                 aError,
8069                                               const otMessage        *aMessage,
8070                                               const Ip6::MessageInfo *aMessageInfo)
8071 {
8072     uint8_t               buf[16];
8073     uint16_t              bytesToPrint;
8074     uint16_t              bytesPrinted = 0;
8075     uint16_t              length;
8076     otNetworkDiagTlv      diagTlv;
8077     otNetworkDiagIterator iterator = OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT;
8078 
8079     SuccessOrExit(aError);
8080 
8081     OutputFormat("DIAG_GET.rsp/ans from ");
8082     OutputIp6Address(aMessageInfo->mPeerAddr);
8083     OutputFormat(": ");
8084 
8085     length = otMessageGetLength(aMessage) - otMessageGetOffset(aMessage);
8086 
8087     while (length > 0)
8088     {
8089         bytesToPrint = Min(length, static_cast<uint16_t>(sizeof(buf)));
8090         otMessageRead(aMessage, otMessageGetOffset(aMessage) + bytesPrinted, buf, bytesToPrint);
8091 
8092         OutputBytes(buf, static_cast<uint8_t>(bytesToPrint));
8093 
8094         length -= bytesToPrint;
8095         bytesPrinted += bytesToPrint;
8096     }
8097 
8098     OutputNewLine();
8099 
8100     // Output Network Diagnostic TLV values in standard YAML format.
8101     while (otThreadGetNextDiagnosticTlv(aMessage, &iterator, &diagTlv) == OT_ERROR_NONE)
8102     {
8103         switch (diagTlv.mType)
8104         {
8105         case OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS:
8106             OutputFormat("Ext Address: ");
8107             OutputExtAddressLine(diagTlv.mData.mExtAddress);
8108             break;
8109         case OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS:
8110             OutputLine("Rloc16: 0x%04x", diagTlv.mData.mAddr16);
8111             break;
8112         case OT_NETWORK_DIAGNOSTIC_TLV_MODE:
8113             OutputLine("Mode:");
8114             OutputMode(kIndentSize, diagTlv.mData.mMode);
8115             break;
8116         case OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT:
8117             OutputLine("Timeout: %lu", ToUlong(diagTlv.mData.mTimeout));
8118             break;
8119         case OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY:
8120             OutputLine("Connectivity:");
8121             OutputConnectivity(kIndentSize, diagTlv.mData.mConnectivity);
8122             break;
8123         case OT_NETWORK_DIAGNOSTIC_TLV_ROUTE:
8124             OutputLine("Route:");
8125             OutputRoute(kIndentSize, diagTlv.mData.mRoute);
8126             break;
8127         case OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA:
8128             OutputLine("Leader Data:");
8129             OutputLeaderData(kIndentSize, diagTlv.mData.mLeaderData);
8130             break;
8131         case OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA:
8132             OutputFormat("Network Data: ");
8133             OutputBytesLine(diagTlv.mData.mNetworkData.m8, diagTlv.mData.mNetworkData.mCount);
8134             break;
8135         case OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST:
8136             OutputLine("IP6 Address List:");
8137             for (uint16_t i = 0; i < diagTlv.mData.mIp6AddrList.mCount; ++i)
8138             {
8139                 OutputFormat(kIndentSize, "- ");
8140                 OutputIp6AddressLine(diagTlv.mData.mIp6AddrList.mList[i]);
8141             }
8142             break;
8143         case OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS:
8144             OutputLine("MAC Counters:");
8145             OutputNetworkDiagMacCounters(kIndentSize, diagTlv.mData.mMacCounters);
8146             break;
8147         case OT_NETWORK_DIAGNOSTIC_TLV_MLE_COUNTERS:
8148             OutputLine("MLE Counters:");
8149             OutputNetworkDiagMleCounters(kIndentSize, diagTlv.mData.mMleCounters);
8150             break;
8151         case OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL:
8152             OutputLine("Battery Level: %u%%", diagTlv.mData.mBatteryLevel);
8153             break;
8154         case OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE:
8155             OutputLine("Supply Voltage: %umV", diagTlv.mData.mSupplyVoltage);
8156             break;
8157         case OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE:
8158             OutputLine("Child Table:");
8159             for (uint16_t i = 0; i < diagTlv.mData.mChildTable.mCount; ++i)
8160             {
8161                 OutputFormat(kIndentSize, "- ");
8162                 OutputChildTableEntry(kIndentSize + 2, diagTlv.mData.mChildTable.mTable[i]);
8163             }
8164             break;
8165         case OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES:
8166             OutputFormat("Channel Pages: '");
8167             OutputBytes(diagTlv.mData.mChannelPages.m8, diagTlv.mData.mChannelPages.mCount);
8168             OutputLine("'");
8169             break;
8170         case OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT:
8171             OutputLine("Max Child Timeout: %lu", ToUlong(diagTlv.mData.mMaxChildTimeout));
8172             break;
8173         case OT_NETWORK_DIAGNOSTIC_TLV_EUI64:
8174             OutputFormat("EUI64: ");
8175             OutputExtAddressLine(diagTlv.mData.mEui64);
8176             break;
8177         case OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_NAME:
8178             OutputLine("Vendor Name: %s", diagTlv.mData.mVendorName);
8179             break;
8180         case OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_MODEL:
8181             OutputLine("Vendor Model: %s", diagTlv.mData.mVendorModel);
8182             break;
8183         case OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_SW_VERSION:
8184             OutputLine("Vendor SW Version: %s", diagTlv.mData.mVendorSwVersion);
8185             break;
8186         case OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_APP_URL:
8187             OutputLine("Vendor App URL: %s", diagTlv.mData.mVendorAppUrl);
8188             break;
8189         case OT_NETWORK_DIAGNOSTIC_TLV_THREAD_STACK_VERSION:
8190             OutputLine("Thread Stack Version: %s", diagTlv.mData.mThreadStackVersion);
8191             break;
8192         default:
8193             break;
8194         }
8195     }
8196 
8197 exit:
8198     return;
8199 }
8200 
OutputMode(uint8_t aIndentSize,const otLinkModeConfig & aMode)8201 void Interpreter::OutputMode(uint8_t aIndentSize, const otLinkModeConfig &aMode)
8202 {
8203     OutputLine(aIndentSize, "RxOnWhenIdle: %d", aMode.mRxOnWhenIdle);
8204     OutputLine(aIndentSize, "DeviceType: %d", aMode.mDeviceType);
8205     OutputLine(aIndentSize, "NetworkData: %d", aMode.mNetworkData);
8206 }
8207 
OutputConnectivity(uint8_t aIndentSize,const otNetworkDiagConnectivity & aConnectivity)8208 void Interpreter::OutputConnectivity(uint8_t aIndentSize, const otNetworkDiagConnectivity &aConnectivity)
8209 {
8210     OutputLine(aIndentSize, "ParentPriority: %d", aConnectivity.mParentPriority);
8211     OutputLine(aIndentSize, "LinkQuality3: %u", aConnectivity.mLinkQuality3);
8212     OutputLine(aIndentSize, "LinkQuality2: %u", aConnectivity.mLinkQuality2);
8213     OutputLine(aIndentSize, "LinkQuality1: %u", aConnectivity.mLinkQuality1);
8214     OutputLine(aIndentSize, "LeaderCost: %u", aConnectivity.mLeaderCost);
8215     OutputLine(aIndentSize, "IdSequence: %u", aConnectivity.mIdSequence);
8216     OutputLine(aIndentSize, "ActiveRouters: %u", aConnectivity.mActiveRouters);
8217     OutputLine(aIndentSize, "SedBufferSize: %u", aConnectivity.mSedBufferSize);
8218     OutputLine(aIndentSize, "SedDatagramCount: %u", aConnectivity.mSedDatagramCount);
8219 }
OutputRoute(uint8_t aIndentSize,const otNetworkDiagRoute & aRoute)8220 void Interpreter::OutputRoute(uint8_t aIndentSize, const otNetworkDiagRoute &aRoute)
8221 {
8222     OutputLine(aIndentSize, "IdSequence: %u", aRoute.mIdSequence);
8223     OutputLine(aIndentSize, "RouteData:");
8224 
8225     aIndentSize += kIndentSize;
8226     for (uint16_t i = 0; i < aRoute.mRouteCount; ++i)
8227     {
8228         OutputFormat(aIndentSize, "- ");
8229         OutputRouteData(aIndentSize + 2, aRoute.mRouteData[i]);
8230     }
8231 }
8232 
OutputRouteData(uint8_t aIndentSize,const otNetworkDiagRouteData & aRouteData)8233 void Interpreter::OutputRouteData(uint8_t aIndentSize, const otNetworkDiagRouteData &aRouteData)
8234 {
8235     OutputLine("RouteId: 0x%02x", aRouteData.mRouterId);
8236 
8237     OutputLine(aIndentSize, "LinkQualityOut: %u", aRouteData.mLinkQualityOut);
8238     OutputLine(aIndentSize, "LinkQualityIn: %u", aRouteData.mLinkQualityIn);
8239     OutputLine(aIndentSize, "RouteCost: %u", aRouteData.mRouteCost);
8240 }
8241 
OutputLeaderData(uint8_t aIndentSize,const otLeaderData & aLeaderData)8242 void Interpreter::OutputLeaderData(uint8_t aIndentSize, const otLeaderData &aLeaderData)
8243 {
8244     OutputLine(aIndentSize, "PartitionId: 0x%08lx", ToUlong(aLeaderData.mPartitionId));
8245     OutputLine(aIndentSize, "Weighting: %u", aLeaderData.mWeighting);
8246     OutputLine(aIndentSize, "DataVersion: %u", aLeaderData.mDataVersion);
8247     OutputLine(aIndentSize, "StableDataVersion: %u", aLeaderData.mStableDataVersion);
8248     OutputLine(aIndentSize, "LeaderRouterId: 0x%02x", aLeaderData.mLeaderRouterId);
8249 }
8250 
OutputNetworkDiagMacCounters(uint8_t aIndentSize,const otNetworkDiagMacCounters & aMacCounters)8251 void Interpreter::OutputNetworkDiagMacCounters(uint8_t aIndentSize, const otNetworkDiagMacCounters &aMacCounters)
8252 {
8253     struct CounterName
8254     {
8255         const uint32_t otNetworkDiagMacCounters::*mValuePtr;
8256         const char                               *mName;
8257     };
8258 
8259     static const CounterName kCounterNames[] = {
8260         {&otNetworkDiagMacCounters::mIfInUnknownProtos, "IfInUnknownProtos"},
8261         {&otNetworkDiagMacCounters::mIfInErrors, "IfInErrors"},
8262         {&otNetworkDiagMacCounters::mIfOutErrors, "IfOutErrors"},
8263         {&otNetworkDiagMacCounters::mIfInUcastPkts, "IfInUcastPkts"},
8264         {&otNetworkDiagMacCounters::mIfInBroadcastPkts, "IfInBroadcastPkts"},
8265         {&otNetworkDiagMacCounters::mIfInDiscards, "IfInDiscards"},
8266         {&otNetworkDiagMacCounters::mIfOutUcastPkts, "IfOutUcastPkts"},
8267         {&otNetworkDiagMacCounters::mIfOutBroadcastPkts, "IfOutBroadcastPkts"},
8268         {&otNetworkDiagMacCounters::mIfOutDiscards, "IfOutDiscards"},
8269     };
8270 
8271     for (const CounterName &counter : kCounterNames)
8272     {
8273         OutputLine(aIndentSize, "%s: %lu", counter.mName, ToUlong(aMacCounters.*counter.mValuePtr));
8274     }
8275 }
8276 
OutputNetworkDiagMleCounters(uint8_t aIndentSize,const otNetworkDiagMleCounters & aMleCounters)8277 void Interpreter::OutputNetworkDiagMleCounters(uint8_t aIndentSize, const otNetworkDiagMleCounters &aMleCounters)
8278 {
8279     struct CounterName
8280     {
8281         const uint16_t otNetworkDiagMleCounters::*mValuePtr;
8282         const char                               *mName;
8283     };
8284 
8285     struct TimeCounterName
8286     {
8287         const uint64_t otNetworkDiagMleCounters::*mValuePtr;
8288         const char                               *mName;
8289     };
8290 
8291     static const CounterName kCounterNames[] = {
8292         {&otNetworkDiagMleCounters::mDisabledRole, "DisabledRole"},
8293         {&otNetworkDiagMleCounters::mDetachedRole, "DetachedRole"},
8294         {&otNetworkDiagMleCounters::mChildRole, "ChildRole"},
8295         {&otNetworkDiagMleCounters::mRouterRole, "RouterRole"},
8296         {&otNetworkDiagMleCounters::mLeaderRole, "LeaderRole"},
8297         {&otNetworkDiagMleCounters::mAttachAttempts, "AttachAttempts"},
8298         {&otNetworkDiagMleCounters::mPartitionIdChanges, "PartitionIdChanges"},
8299         {&otNetworkDiagMleCounters::mBetterPartitionAttachAttempts, "BetterPartitionAttachAttempts"},
8300         {&otNetworkDiagMleCounters::mParentChanges, "ParentChanges"},
8301     };
8302 
8303     static const TimeCounterName kTimeCounterNames[] = {
8304         {&otNetworkDiagMleCounters::mTrackedTime, "TrackedTime"},
8305         {&otNetworkDiagMleCounters::mDisabledTime, "DisabledTime"},
8306         {&otNetworkDiagMleCounters::mDetachedTime, "DetachedTime"},
8307         {&otNetworkDiagMleCounters::mChildTime, "ChildTime"},
8308         {&otNetworkDiagMleCounters::mRouterTime, "RouterTime"},
8309         {&otNetworkDiagMleCounters::mLeaderTime, "LeaderTime"},
8310     };
8311 
8312     for (const CounterName &counter : kCounterNames)
8313     {
8314         OutputLine(aIndentSize, "%s: %u", counter.mName, aMleCounters.*counter.mValuePtr);
8315     }
8316 
8317     for (const TimeCounterName &counter : kTimeCounterNames)
8318     {
8319         OutputFormat("%s: ", counter.mName);
8320         OutputUint64Line(aMleCounters.*counter.mValuePtr);
8321     }
8322 }
8323 
OutputChildTableEntry(uint8_t aIndentSize,const otNetworkDiagChildEntry & aChildEntry)8324 void Interpreter::OutputChildTableEntry(uint8_t aIndentSize, const otNetworkDiagChildEntry &aChildEntry)
8325 {
8326     OutputLine("ChildId: 0x%04x", aChildEntry.mChildId);
8327 
8328     OutputLine(aIndentSize, "Timeout: %u", aChildEntry.mTimeout);
8329     OutputLine(aIndentSize, "Link Quality: %u", aChildEntry.mLinkQuality);
8330     OutputLine(aIndentSize, "Mode:");
8331     OutputMode(aIndentSize + kIndentSize, aChildEntry.mMode);
8332 }
8333 #endif // OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE
8334 
8335 #if OPENTHREAD_FTD
HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo * aInfo,void * aContext)8336 void Interpreter::HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo *aInfo, void *aContext)
8337 {
8338     static_cast<Interpreter *>(aContext)->HandleDiscoveryRequest(*aInfo);
8339 }
8340 
HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo & aInfo)8341 void Interpreter::HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo &aInfo)
8342 {
8343     OutputFormat("~ Discovery Request from ");
8344     OutputExtAddress(aInfo.mExtAddress);
8345     OutputLine(": version=%u,joiner=%d", aInfo.mVersion, aInfo.mIsJoiner);
8346 }
8347 #endif
8348 
8349 #if OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK
HandleIp6Receive(otMessage * aMessage,void * aContext)8350 void Interpreter::HandleIp6Receive(otMessage *aMessage, void *aContext)
8351 {
8352     OT_UNUSED_VARIABLE(aContext);
8353 
8354     otMessageFree(aMessage);
8355 }
8356 #endif
8357 
8358 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
8359 
Initialize(otInstance * aInstance,otCliOutputCallback aCallback,void * aContext)8360 void Interpreter::Initialize(otInstance *aInstance, otCliOutputCallback aCallback, void *aContext)
8361 {
8362     Instance *instance = static_cast<Instance *>(aInstance);
8363 
8364     Interpreter::sInterpreter = new (&sInterpreterRaw) Interpreter(instance, aCallback, aContext);
8365 }
8366 
OutputPrompt(void)8367 void Interpreter::OutputPrompt(void)
8368 {
8369 #if OPENTHREAD_CONFIG_CLI_PROMPT_ENABLE
8370     static const char sPrompt[] = "> ";
8371 
8372     // The `OutputFormat()` below is adding the prompt which is not
8373     // part of any command output, so we set the `EmittingCommandOutput`
8374     // flag to false to avoid it being included in the command output
8375     // log (under `OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE`).
8376 
8377     SetEmittingCommandOutput(false);
8378     OutputFormat("%s", sPrompt);
8379     SetEmittingCommandOutput(true);
8380 #endif // OPENTHREAD_CONFIG_CLI_PROMPT_ENABLE
8381 }
8382 
HandleTimer(Timer & aTimer)8383 void Interpreter::HandleTimer(Timer &aTimer)
8384 {
8385     static_cast<Interpreter *>(static_cast<TimerMilliContext &>(aTimer).GetContext())->HandleTimer();
8386 }
8387 
HandleTimer(void)8388 void Interpreter::HandleTimer(void)
8389 {
8390 #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
8391     if (mLocateInProgress)
8392     {
8393         mLocateInProgress = false;
8394         OutputResult(OT_ERROR_RESPONSE_TIMEOUT);
8395     }
8396     else
8397 #endif
8398     {
8399         OutputResult(OT_ERROR_NONE);
8400     }
8401 }
8402 
SetCommandTimeout(uint32_t aTimeoutMilli)8403 void Interpreter::SetCommandTimeout(uint32_t aTimeoutMilli)
8404 {
8405     OT_ASSERT(mCommandIsPending);
8406     mTimer.Start(aTimeoutMilli);
8407 }
8408 
ProcessCommand(Arg aArgs[])8409 otError Interpreter::ProcessCommand(Arg aArgs[])
8410 {
8411 #define CmdEntry(aCommandString)                                   \
8412     {                                                              \
8413         aCommandString, &Interpreter::Process<Cmd(aCommandString)> \
8414     }
8415 
8416     static constexpr Command kCommands[] = {
8417 #if OPENTHREAD_FTD || OPENTHREAD_MTD
8418 #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
8419         CmdEntry("ba"),
8420 #endif
8421 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
8422         CmdEntry("bbr"),
8423 #endif
8424 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
8425         CmdEntry("br"),
8426 #endif
8427         CmdEntry("bufferinfo"),
8428         CmdEntry("ccathreshold"),
8429 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
8430         CmdEntry("ccm"),
8431 #endif
8432         CmdEntry("channel"),
8433 #if OPENTHREAD_FTD
8434         CmdEntry("child"),
8435         CmdEntry("childip"),
8436         CmdEntry("childmax"),
8437         CmdEntry("childrouterlinks"),
8438 #endif
8439         CmdEntry("childsupervision"),
8440         CmdEntry("childtimeout"),
8441 #if OPENTHREAD_CONFIG_COAP_API_ENABLE
8442         CmdEntry("coap"),
8443 #endif
8444 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
8445         CmdEntry("coaps"),
8446 #endif
8447 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
8448         CmdEntry("coex"),
8449 #endif
8450 #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
8451         CmdEntry("commissioner"),
8452 #endif
8453 #if OPENTHREAD_FTD
8454         CmdEntry("contextreusedelay"),
8455 #endif
8456         CmdEntry("counters"),
8457 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
8458         CmdEntry("csl"),
8459 #endif
8460         CmdEntry("dataset"),
8461         CmdEntry("debug"),
8462 #if OPENTHREAD_FTD
8463         CmdEntry("delaytimermin"),
8464 #endif
8465         CmdEntry("detach"),
8466 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
8467 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MLE_DEVICE_PROPERTY_LEADER_WEIGHT_ENABLE
8468         CmdEntry("deviceprops"),
8469 #endif
8470 #if OPENTHREAD_CONFIG_DIAG_ENABLE
8471         CmdEntry("diag"),
8472 #endif
8473 #if OPENTHREAD_FTD || OPENTHREAD_MTD
8474         CmdEntry("discover"),
8475 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE || OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE || \
8476     OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
8477         CmdEntry("dns"),
8478 #endif
8479 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
8480         CmdEntry("domainname"),
8481 #endif
8482 #if OPENTHREAD_CONFIG_DUA_ENABLE
8483         CmdEntry("dua"),
8484 #endif
8485 #if OPENTHREAD_FTD
8486         CmdEntry("eidcache"),
8487 #endif
8488         CmdEntry("eui64"),
8489         CmdEntry("extaddr"),
8490         CmdEntry("extpanid"),
8491         CmdEntry("factoryreset"),
8492 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
8493         CmdEntry("fake"),
8494 #endif
8495         CmdEntry("fem"),
8496 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
8497 #if OPENTHREAD_FTD || OPENTHREAD_MTD
8498 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
8499         CmdEntry("history"),
8500 #endif
8501         CmdEntry("ifconfig"),
8502         CmdEntry("instanceid"),
8503         CmdEntry("ipaddr"),
8504         CmdEntry("ipmaddr"),
8505 #if OPENTHREAD_CONFIG_JOINER_ENABLE
8506         CmdEntry("joiner"),
8507 #endif
8508 #if OPENTHREAD_FTD
8509         CmdEntry("joinerport"),
8510 #endif
8511         CmdEntry("keysequence"),
8512         CmdEntry("leaderdata"),
8513 #if OPENTHREAD_FTD
8514         CmdEntry("leaderweight"),
8515 #endif
8516 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
8517         CmdEntry("linkmetrics"),
8518 #if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
8519         CmdEntry("linkmetricsmgr"),
8520 #endif
8521 #endif
8522 #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
8523         CmdEntry("locate"),
8524 #endif
8525         CmdEntry("log"),
8526         CmdEntry("mac"),
8527 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
8528         CmdEntry("macfilter"),
8529 #endif
8530 #if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD
8531         CmdEntry("meshdiag"),
8532 #endif
8533 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
8534         CmdEntry("mleadvimax"),
8535 #endif
8536 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
8537         CmdEntry("mliid"),
8538 #endif
8539 #if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
8540         CmdEntry("mlr"),
8541 #endif
8542         CmdEntry("mode"),
8543         CmdEntry("multiradio"),
8544 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE || OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
8545         CmdEntry("nat64"),
8546 #endif
8547 #if OPENTHREAD_FTD
8548         CmdEntry("neighbor"),
8549 #endif
8550         CmdEntry("netdata"),
8551         CmdEntry("netstat"),
8552 #if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE
8553         CmdEntry("networkdiagnostic"),
8554 #endif
8555 #if OPENTHREAD_FTD
8556         CmdEntry("networkidtimeout"),
8557 #endif
8558         CmdEntry("networkkey"),
8559 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
8560         CmdEntry("networkkeyref"),
8561 #endif
8562         CmdEntry("networkname"),
8563 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
8564         CmdEntry("networktime"),
8565 #endif
8566 #if OPENTHREAD_FTD
8567         CmdEntry("nexthop"),
8568 #endif
8569         CmdEntry("panid"),
8570         CmdEntry("parent"),
8571 #if OPENTHREAD_FTD
8572         CmdEntry("parentpriority"),
8573         CmdEntry("partitionid"),
8574 #endif
8575 #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
8576         CmdEntry("ping"),
8577 #endif
8578         CmdEntry("platform"),
8579         CmdEntry("pollperiod"),
8580 #if OPENTHREAD_FTD
8581         CmdEntry("preferrouterid"),
8582 #endif
8583 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
8584         CmdEntry("prefix"),
8585 #endif
8586         CmdEntry("promiscuous"),
8587 #if OPENTHREAD_FTD
8588         CmdEntry("pskc"),
8589 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
8590         CmdEntry("pskcref"),
8591 #endif
8592 #endif
8593 #if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE
8594         CmdEntry("radio"),
8595 #endif
8596 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
8597         CmdEntry("radiofilter"),
8598 #endif
8599         CmdEntry("rcp"),
8600         CmdEntry("region"),
8601 #if OPENTHREAD_FTD
8602         CmdEntry("releaserouterid"),
8603 #endif
8604 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
8605         CmdEntry("reset"),
8606 #if OPENTHREAD_FTD || OPENTHREAD_MTD
8607         CmdEntry("rloc16"),
8608 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
8609         CmdEntry("route"),
8610 #endif
8611 #if OPENTHREAD_FTD
8612         CmdEntry("router"),
8613         CmdEntry("routerdowngradethreshold"),
8614         CmdEntry("routereligible"),
8615 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
8616         CmdEntry("routeridrange"),
8617 #endif
8618         CmdEntry("routerselectionjitter"),
8619         CmdEntry("routerupgradethreshold"),
8620 #endif
8621         CmdEntry("scan"),
8622 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
8623         CmdEntry("service"),
8624 #endif
8625         CmdEntry("singleton"),
8626 #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
8627         CmdEntry("sntp"),
8628 #endif
8629 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
8630         CmdEntry("srp"),
8631 #endif
8632         CmdEntry("state"),
8633 #if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE
8634         CmdEntry("tcat"),
8635 #endif
8636 #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
8637         CmdEntry("tcp"),
8638 #endif
8639         CmdEntry("thread"),
8640 #if OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE
8641         CmdEntry("timeinqueue"),
8642 #endif
8643 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
8644         CmdEntry("trel"),
8645 #endif
8646 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
8647         CmdEntry("tvcheck"),
8648 #endif
8649         CmdEntry("txpower"),
8650         CmdEntry("udp"),
8651         CmdEntry("unsecureport"),
8652 #if OPENTHREAD_CONFIG_UPTIME_ENABLE
8653         CmdEntry("uptime"),
8654 #endif
8655         CmdEntry("vendor"),
8656 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
8657         CmdEntry("version"),
8658     };
8659 
8660 #undef CmdEntry
8661 
8662     static_assert(BinarySearch::IsSorted(kCommands), "Command Table is not sorted");
8663 
8664     otError        error   = OT_ERROR_NONE;
8665     const Command *command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
8666 
8667     if (command != nullptr)
8668     {
8669         error = (this->*command->mHandler)(aArgs + 1);
8670     }
8671     else if (aArgs[0] == "help")
8672     {
8673         OutputCommandTable(kCommands);
8674 
8675         for (const UserCommandsEntry &entry : mUserCommands)
8676         {
8677             for (uint8_t i = 0; i < entry.mLength; i++)
8678             {
8679                 OutputLine("%s", entry.mCommands[i].mName);
8680             }
8681         }
8682     }
8683     else
8684     {
8685         error = ProcessUserCommands(aArgs);
8686     }
8687 
8688     return error;
8689 }
8690 
otCliInit(otInstance * aInstance,otCliOutputCallback aCallback,void * aContext)8691 extern "C" void otCliInit(otInstance *aInstance, otCliOutputCallback aCallback, void *aContext)
8692 {
8693     Interpreter::Initialize(aInstance, aCallback, aContext);
8694 
8695 #if OPENTHREAD_CONFIG_CLI_VENDOR_COMMANDS_ENABLE && OPENTHREAD_CONFIG_CLI_MAX_USER_CMD_ENTRIES > 1
8696     otCliVendorSetUserCommands();
8697 #endif
8698 }
8699 
otCliInputLine(char * aBuf)8700 extern "C" void otCliInputLine(char *aBuf) { Interpreter::GetInterpreter().ProcessLine(aBuf); }
8701 
otCliSetUserCommands(const otCliCommand * aUserCommands,uint8_t aLength,void * aContext)8702 extern "C" otError otCliSetUserCommands(const otCliCommand *aUserCommands, uint8_t aLength, void *aContext)
8703 {
8704     return Interpreter::GetInterpreter().SetUserCommands(aUserCommands, aLength, aContext);
8705 }
8706 
otCliOutputBytes(const uint8_t * aBytes,uint8_t aLength)8707 extern "C" void otCliOutputBytes(const uint8_t *aBytes, uint8_t aLength)
8708 {
8709     Interpreter::GetInterpreter().OutputBytes(aBytes, aLength);
8710 }
8711 
otCliOutputFormat(const char * aFmt,...)8712 extern "C" void otCliOutputFormat(const char *aFmt, ...)
8713 {
8714     va_list aAp;
8715     va_start(aAp, aFmt);
8716     Interpreter::GetInterpreter().OutputFormatV(aFmt, aAp);
8717     va_end(aAp);
8718 }
8719 
otCliAppendResult(otError aError)8720 extern "C" void otCliAppendResult(otError aError) { Interpreter::GetInterpreter().OutputResult(aError); }
8721 
otCliPlatLogv(otLogLevel aLogLevel,otLogRegion aLogRegion,const char * aFormat,va_list aArgs)8722 extern "C" void otCliPlatLogv(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, va_list aArgs)
8723 {
8724     OT_UNUSED_VARIABLE(aLogLevel);
8725     OT_UNUSED_VARIABLE(aLogRegion);
8726 
8727     VerifyOrExit(Interpreter::IsInitialized());
8728 
8729     // CLI output is being used for logging, so we set the flag
8730     // `EmittingCommandOutput` to false indicate this.
8731     Interpreter::GetInterpreter().SetEmittingCommandOutput(false);
8732     Interpreter::GetInterpreter().OutputFormatV(aFormat, aArgs);
8733     Interpreter::GetInterpreter().OutputNewLine();
8734     Interpreter::GetInterpreter().SetEmittingCommandOutput(true);
8735 
8736 exit:
8737     return;
8738 }
8739 
8740 } // namespace Cli
8741 } // namespace ot
8742