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