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