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