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