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