Coverage Report

Created: 2025-05-12 06:47

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