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