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