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