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