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