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