1 /*
2 * Copyright (c) 2019, 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 a simple CLI for the Commissioner role.
32 */
33
34 #include "cli_commissioner.hpp"
35
36 #include "cli/cli.hpp"
37
38 #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
39
40 namespace ot {
41 namespace Cli {
42
43 /**
44 * @cli commissioner announce
45 * @code
46 * commissioner announce 0x00050000 2 32 fdde:ad00:beef:0:0:ff:fe00:c00
47 * Done
48 * @endcode
49 * @cparam commissioner announce @ca{mask} @ca{count} @ca{period} @ca{destination}
50 * * `mask`: Bitmask that identifies channels for sending MLE `Announce` messages.
51 * * `count`: Number of MLE `Announce` transmissions per channel.
52 * * `period`: Number of milliseconds between successive MLE `Announce` transmissions.
53 * * `destination`: Destination IPv6 address for the message. The message may be multicast.
54 * @par
55 * Sends an Announce Begin message.
56 * @note Use this command only after successfully starting the %Commissioner role
57 * with the `commissioner start` command.
58 * @csa{commissioner start}
59 * @sa otCommissionerAnnounceBegin
60 */
Process(Arg aArgs[])61 template <> otError Commissioner::Process<Cmd("announce")>(Arg aArgs[])
62 {
63 otError error;
64 uint32_t mask;
65 uint8_t count;
66 uint16_t period;
67 otIp6Address address;
68
69 SuccessOrExit(error = aArgs[0].ParseAsUint32(mask));
70 SuccessOrExit(error = aArgs[1].ParseAsUint8(count));
71 SuccessOrExit(error = aArgs[2].ParseAsUint16(period));
72 SuccessOrExit(error = aArgs[3].ParseAsIp6Address(address));
73
74 error = otCommissionerAnnounceBegin(GetInstancePtr(), mask, count, period, &address);
75
76 exit:
77 return error;
78 }
79
80 /**
81 * @cli commissioner energy
82 * @code
83 * commissioner energy 0x00050000 2 32 1000 fdde:ad00:beef:0:0:ff:fe00:c00
84 * Done
85 * Energy: 00050000 0 0 0 0
86 * @endcode
87 * @cparam commissioner energy @ca{mask} @ca{count} @ca{period} @ca{scanDuration} @ca{destination}
88 * * `mask`: Bitmask that identifies channels for performing IEEE 802.15.4 energy scans.
89 * * `count`: Number of IEEE 802.15.4 energy scans per channel.
90 * * `period`: Number of milliseconds between successive IEEE 802.15.4 energy scans.
91 * * `scanDuration`: Scan duration in milliseconds to use when
92 * performing an IEEE 802.15.4 energy scan.
93 * * `destination`: Destination IPv6 address for the message. The message may be multicast.
94 * @par
95 * Sends an Energy Scan Query message. Command output is printed as it is received.
96 * @note Use this command only after successfully starting the %Commissioner role
97 * with the `commissioner start` command.
98 * @csa{commissioner start}
99 * @sa otCommissionerEnergyScan
100 */
Process(Arg aArgs[])101 template <> otError Commissioner::Process<Cmd("energy")>(Arg aArgs[])
102 {
103 otError error;
104 uint32_t mask;
105 uint8_t count;
106 uint16_t period;
107 uint16_t scanDuration;
108 otIp6Address address;
109
110 SuccessOrExit(error = aArgs[0].ParseAsUint32(mask));
111 SuccessOrExit(error = aArgs[1].ParseAsUint8(count));
112 SuccessOrExit(error = aArgs[2].ParseAsUint16(period));
113 SuccessOrExit(error = aArgs[3].ParseAsUint16(scanDuration));
114 SuccessOrExit(error = aArgs[4].ParseAsIp6Address(address));
115
116 error = otCommissionerEnergyScan(GetInstancePtr(), mask, count, period, scanDuration, &address,
117 &Commissioner::HandleEnergyReport, this);
118
119 exit:
120 return error;
121 }
122
Process(Arg aArgs[])123 template <> otError Commissioner::Process<Cmd("joiner")>(Arg aArgs[])
124 {
125 otError error = OT_ERROR_NONE;
126 otExtAddress addr;
127 const otExtAddress *addrPtr = nullptr;
128 otJoinerDiscerner discerner;
129
130 /**
131 * @cli commissioner joiner table
132 * @code
133 * commissioner joiner table
134 * | ID | PSKd | Expiration |
135 * +-----------------------+----------------------------------+------------+
136 * | * | J01NME | 81015 |
137 * | d45e64fa83f81cf7 | J01NME | 101204 |
138 * | 0x0000000000000abc/12 | J01NME | 114360 |
139 * Done
140 * @endcode
141 * @par
142 * Lists all %Joiner entries in table format.
143 */
144 if (aArgs[0] == "table")
145 {
146 uint16_t iter = 0;
147 otJoinerInfo joinerInfo;
148
149 static const char *const kJoinerTableTitles[] = {"ID", "PSKd", "Expiration"};
150
151 static const uint8_t kJoinerTableColumnWidths[] = {
152 23,
153 34,
154 12,
155 };
156
157 OutputTableHeader(kJoinerTableTitles, kJoinerTableColumnWidths);
158
159 while (otCommissionerGetNextJoinerInfo(GetInstancePtr(), &iter, &joinerInfo) == OT_ERROR_NONE)
160 {
161 switch (joinerInfo.mType)
162 {
163 case OT_JOINER_INFO_TYPE_ANY:
164 OutputFormat("| %21s", "*");
165 break;
166
167 case OT_JOINER_INFO_TYPE_EUI64:
168 OutputFormat("| ");
169 OutputExtAddress(joinerInfo.mSharedId.mEui64);
170 break;
171
172 case OT_JOINER_INFO_TYPE_DISCERNER:
173 OutputFormat("| 0x%08lx%08lx/%2u",
174 static_cast<unsigned long>(joinerInfo.mSharedId.mDiscerner.mValue >> 32),
175 static_cast<unsigned long>(joinerInfo.mSharedId.mDiscerner.mValue & 0xffffffff),
176 joinerInfo.mSharedId.mDiscerner.mLength);
177 break;
178 }
179
180 OutputFormat(" | %32s | %10lu |", joinerInfo.mPskd.m8, ToUlong(joinerInfo.mExpirationTime));
181 OutputNewLine();
182 }
183
184 ExitNow(error = OT_ERROR_NONE);
185 }
186
187 VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
188
189 ClearAllBytes(discerner);
190
191 if (aArgs[1] == "*")
192 {
193 // Intentionally empty
194 }
195 else
196 {
197 error = ParseJoinerDiscerner(aArgs[1], discerner);
198
199 if (error == OT_ERROR_NOT_FOUND)
200 {
201 error = aArgs[1].ParseAsHexString(addr.m8);
202 addrPtr = &addr;
203 }
204
205 SuccessOrExit(error);
206 }
207
208 /**
209 * @cli commissioner joiner add
210 * @code
211 * commissioner joiner add d45e64fa83f81cf7 J01NME
212 * Done
213 * @endcode
214 * @code
215 * commissioner joiner add 0xabc/12 J01NME
216 * Done
217 * @endcode
218 * @cparam commissioner joiner add @ca{eui64}|@ca{discerner pksd} [@ca{timeout}]
219 * * `eui64`: IEEE EUI-64 of the %Joiner. To match any joiner, use `*`.
220 * * `discerner`: The %Joiner discerner in the format `number/length`.
221 * * `pksd`: Pre-Shared Key for the joiner.
222 * * `timeout`: The %Joiner timeout in seconds.
223 * @par
224 * Adds a joiner entry.
225 * @note Use this command only after successfully starting the %Commissioner role
226 * with the `commissioner start` command.
227 * @csa{commissioner start}
228 * @sa otCommissionerAddJoiner
229 * @sa otCommissionerAddJoinerWithDiscerner
230 */
231 if (aArgs[0] == "add")
232 {
233 uint32_t timeout = kDefaultJoinerTimeout;
234
235 VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
236
237 if (!aArgs[3].IsEmpty())
238 {
239 SuccessOrExit(error = aArgs[3].ParseAsUint32(timeout));
240 }
241
242 if (discerner.mLength)
243 {
244 error = otCommissionerAddJoinerWithDiscerner(GetInstancePtr(), &discerner, aArgs[2].GetCString(), timeout);
245 }
246 else
247 {
248 error = otCommissionerAddJoiner(GetInstancePtr(), addrPtr, aArgs[2].GetCString(), timeout);
249 }
250 /**
251 * @cli commissioner joiner remove
252 * @code
253 * commissioner joiner remove d45e64fa83f81cf7
254 * Done
255 * @endcode
256 * @code
257 * commissioner joiner remove 0xabc/12
258 * Done
259 * @endcode
260 * @cparam commissioner joiner remove @ca{eui64}|@ca{discerner}
261 * * `eui64`: IEEE EUI-64 of the joiner. To match any joiner, use `*`.
262 * * `discerner`: The joiner discerner in the format `number/length`.
263 * @par
264 * Removes a %Joiner entry.
265 * @note Use this command only after successfully starting the %Commissioner role
266 * with the `commissioner start` command.
267 * @csa{commissioner start}
268 * @sa otCommissionerRemoveJoiner
269 * @sa otCommissionerRemoveJoinerWithDiscerner
270 */
271 }
272 else if (aArgs[0] == "remove")
273 {
274 if (discerner.mLength)
275 {
276 error = otCommissionerRemoveJoinerWithDiscerner(GetInstancePtr(), &discerner);
277 }
278 else
279 {
280 error = otCommissionerRemoveJoiner(GetInstancePtr(), addrPtr);
281 }
282 }
283 else
284 {
285 error = OT_ERROR_INVALID_ARGS;
286 }
287
288 exit:
289 return error;
290 }
291
292 /**
293 * @cli commissioner mgmtget
294 * @code
295 * commissioner mgmtget locator sessionid
296 * Done
297 * @endcode
298 * @cparam commissioner mgmtget [locator] [sessionid] <!--
299 * --> [steeringdata] [joinerudpport] <!--
300 * --> [-x @ca{TLVs}]
301 * * `locator`: Border Router RLOC16.
302 * * `sessionid`: Session ID of the %Commissioner.
303 * * `steeringdata`: Steering data.
304 * * `joinerudpport`: %Joiner UDP port.
305 * * `TLVs`: The set of TLVs to be retrieved.
306 * @par
307 * Sends a `MGMT_GET` (Management Get) message to the Leader.
308 * Variable values that have been set using the `commissioner mgmtset` command are returned.
309 * @sa otCommissionerSendMgmtGet
310 */
Process(Arg aArgs[])311 template <> otError Commissioner::Process<Cmd("mgmtget")>(Arg aArgs[])
312 {
313 otError error = OT_ERROR_NONE;
314 uint8_t tlvs[32];
315 uint8_t length = 0;
316
317 for (; !aArgs->IsEmpty(); aArgs++)
318 {
319 VerifyOrExit(static_cast<size_t>(length) < sizeof(tlvs), error = OT_ERROR_NO_BUFS);
320
321 if (*aArgs == "locator")
322 {
323 tlvs[length++] = OT_MESHCOP_TLV_BORDER_AGENT_RLOC;
324 }
325 else if (*aArgs == "sessionid")
326 {
327 tlvs[length++] = OT_MESHCOP_TLV_COMM_SESSION_ID;
328 }
329 else if (*aArgs == "steeringdata")
330 {
331 tlvs[length++] = OT_MESHCOP_TLV_STEERING_DATA;
332 }
333 else if (*aArgs == "joinerudpport")
334 {
335 tlvs[length++] = OT_MESHCOP_TLV_JOINER_UDP_PORT;
336 }
337 else if (*aArgs == "-x")
338 {
339 uint16_t readLength;
340
341 aArgs++;
342 readLength = static_cast<uint16_t>(sizeof(tlvs) - length);
343 SuccessOrExit(error = aArgs->ParseAsHexString(readLength, tlvs + length));
344 length += static_cast<uint8_t>(readLength);
345 }
346 else
347 {
348 ExitNow(error = OT_ERROR_INVALID_ARGS);
349 }
350 }
351
352 error = otCommissionerSendMgmtGet(GetInstancePtr(), tlvs, static_cast<uint8_t>(length));
353
354 exit:
355 return error;
356 }
357
358 /**
359 * @cli commissioner mgmtset
360 * @code
361 * commissioner mgmtset joinerudpport 9988
362 * Done
363 * @endcode
364 * @cparam commissioner mgmtset [locator @ca{locator}] [sessionid @ca{sessionid}] <!--
365 * --> [steeringdata @ca{steeringdata}] [joinerudpport @ca{joinerudpport}] <!--
366 * --> [-x @ca{TLVs}]
367 * * `locator`: Border Router RLOC16.
368 * * `sessionid`: Session ID of the %Commissioner.
369 * * `steeringdata`: Steering data.
370 * * `joinerudpport`: %Joiner UDP port.
371 * * `TLVs`: The set of TLVs to be retrieved.
372 * @par
373 * Sends a `MGMT_SET` (Management Set) message to the Leader, and sets the
374 * variables to the values specified.
375 * @sa otCommissionerSendMgmtSet
376 */
Process(Arg aArgs[])377 template <> otError Commissioner::Process<Cmd("mgmtset")>(Arg aArgs[])
378 {
379 otError error;
380 otCommissioningDataset dataset;
381 uint8_t tlvs[32];
382 uint8_t tlvsLength = 0;
383
384 VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
385
386 ClearAllBytes(dataset);
387
388 for (; !aArgs->IsEmpty(); aArgs++)
389 {
390 if (*aArgs == "locator")
391 {
392 aArgs++;
393 dataset.mIsLocatorSet = true;
394 SuccessOrExit(error = aArgs->ParseAsUint16(dataset.mLocator));
395 }
396 else if (*aArgs == "sessionid")
397 {
398 aArgs++;
399 dataset.mIsSessionIdSet = true;
400 SuccessOrExit(error = aArgs->ParseAsUint16(dataset.mSessionId));
401 }
402 else if (*aArgs == "steeringdata")
403 {
404 uint16_t length;
405
406 aArgs++;
407 dataset.mIsSteeringDataSet = true;
408 length = sizeof(dataset.mSteeringData.m8);
409 SuccessOrExit(error = aArgs->ParseAsHexString(length, dataset.mSteeringData.m8));
410 dataset.mSteeringData.mLength = static_cast<uint8_t>(length);
411 }
412 else if (*aArgs == "joinerudpport")
413 {
414 aArgs++;
415 dataset.mIsJoinerUdpPortSet = true;
416 SuccessOrExit(error = aArgs->ParseAsUint16(dataset.mJoinerUdpPort));
417 }
418 else if (*aArgs == "-x")
419 {
420 uint16_t length;
421
422 aArgs++;
423 length = sizeof(tlvs);
424 SuccessOrExit(error = aArgs->ParseAsHexString(length, tlvs));
425 tlvsLength = static_cast<uint8_t>(length);
426 }
427 else
428 {
429 ExitNow(error = OT_ERROR_INVALID_ARGS);
430 }
431 }
432
433 error = otCommissionerSendMgmtSet(GetInstancePtr(), &dataset, tlvs, tlvsLength);
434
435 exit:
436 return error;
437 }
438
439 /**
440 * @cli commissioner panid
441 * @code
442 * commissioner panid 0xdead 0x7fff800 fdde:ad00:beef:0:0:ff:fe00:c00
443 * Done
444 * Conflict: dead, 00000800
445 * @endcode
446 * @cparam commissioner panid @ca{panid} @ca{mask} @ca{destination}
447 * * `paind`: PAN ID to use to check for conflicts.
448 * * `mask`; Bitmask that identifies channels to perform IEEE 802.15.4
449 * Active Scans.
450 * * `destination`: IPv6 destination address for the message. The message may be multicast.
451 * @par
452 * Sends a PAN ID query. Command output is returned as it is received.
453 * @note Use this command only after successfully starting the %Commissioner role
454 * with the `commissioner start` command.
455 * @csa{commissioner start}
456 * @sa otCommissionerPanIdQuery
457 */
Process(Arg aArgs[])458 template <> otError Commissioner::Process<Cmd("panid")>(Arg aArgs[])
459 {
460 otError error;
461 uint16_t panId;
462 uint32_t mask;
463 otIp6Address address;
464
465 SuccessOrExit(error = aArgs[0].ParseAsUint16(panId));
466 SuccessOrExit(error = aArgs[1].ParseAsUint32(mask));
467 SuccessOrExit(error = aArgs[2].ParseAsIp6Address(address));
468
469 error = otCommissionerPanIdQuery(GetInstancePtr(), panId, mask, &address, &Commissioner::HandlePanIdConflict, this);
470
471 exit:
472 return error;
473 }
474
475 /**
476 * @cli commissioner provisioningurl
477 * @code
478 * commissioner provisioningurl http://github.com/openthread/openthread
479 * Done
480 * @endcode
481 * @cparam commissioner provisioningurl @ca{provisioningurl}
482 * @par
483 * Sets the %Commissioner provisioning URL.
484 * @sa otCommissionerSetProvisioningUrl
485 */
Process(Arg aArgs[])486 template <> otError Commissioner::Process<Cmd("provisioningurl")>(Arg aArgs[])
487 {
488 // If aArgs[0] is empty, `GetCString() will return `nullptr`
489 /// which will correctly clear the provisioning URL.
490 return otCommissionerSetProvisioningUrl(GetInstancePtr(), aArgs[0].GetCString());
491 }
492
493 /**
494 * @cli commissioner sessionid
495 * @code
496 * commissioner sessionid
497 * 0
498 * Done
499 * @endcode
500 * @par
501 * Gets the current %Commissioner session ID.
502 * @sa otCommissionerGetSessionId
503 */
Process(Arg aArgs[])504 template <> otError Commissioner::Process<Cmd("sessionid")>(Arg aArgs[])
505 {
506 OT_UNUSED_VARIABLE(aArgs);
507
508 OutputLine("%d", otCommissionerGetSessionId(GetInstancePtr()));
509
510 return OT_ERROR_NONE;
511 }
512
513 /**
514 * @cli commissioner id (get,set)
515 * @code
516 * commissioner id OpenThread Commissioner
517 * Done
518 * @endcode
519 * @code
520 * commissioner id
521 * OpenThread Commissioner
522 * Done
523 * @endcode
524 * @cparam commissioner id @ca{name}
525 * @par
526 * Gets or sets the OpenThread %Commissioner ID name.
527 * @sa otCommissionerSetId
528 */
Process(Arg aArgs[])529 template <> otError Commissioner::Process<Cmd("id")>(Arg aArgs[])
530 {
531 otError error;
532
533 if (aArgs[0].IsEmpty())
534 {
535 OutputLine("%s", otCommissionerGetId(GetInstancePtr()));
536 error = OT_ERROR_NONE;
537 }
538 else
539 {
540 error = otCommissionerSetId(GetInstancePtr(), aArgs[0].GetCString());
541 }
542
543 return error;
544 }
545
546 /**
547 * @cli commissioner start
548 * @code
549 * commissioner start
550 * Commissioner: petitioning
551 * Done
552 * Commissioner: active
553 * @endcode
554 * @par
555 * Starts the Thread %Commissioner role.
556 * @note The `commissioner` commands are available only when
557 * `OPENTHREAD_CONFIG_COMMISSIONER_ENABLE` and `OPENTHREAD_FTD` are set.
558 * @sa otCommissionerStart
559 */
Process(Arg aArgs[])560 template <> otError Commissioner::Process<Cmd("start")>(Arg aArgs[])
561 {
562 OT_UNUSED_VARIABLE(aArgs);
563
564 return otCommissionerStart(GetInstancePtr(), &Commissioner::HandleStateChanged, &Commissioner::HandleJoinerEvent,
565 this);
566 }
567
HandleStateChanged(otCommissionerState aState,void * aContext)568 void Commissioner::HandleStateChanged(otCommissionerState aState, void *aContext)
569 {
570 static_cast<Commissioner *>(aContext)->HandleStateChanged(aState);
571 }
572
HandleStateChanged(otCommissionerState aState)573 void Commissioner::HandleStateChanged(otCommissionerState aState)
574 {
575 OutputLine("Commissioner: %s", StateToString(aState));
576 }
577
StateToString(otCommissionerState aState)578 const char *Commissioner::StateToString(otCommissionerState aState)
579 {
580 static const char *const kStateString[] = {
581 "disabled", // (0) OT_COMMISSIONER_STATE_DISABLED
582 "petitioning", // (1) OT_COMMISSIONER_STATE_PETITION
583 "active", // (2) OT_COMMISSIONER_STATE_ACTIVE
584 };
585
586 static_assert(0 == OT_COMMISSIONER_STATE_DISABLED, "OT_COMMISSIONER_STATE_DISABLED value is incorrect");
587 static_assert(1 == OT_COMMISSIONER_STATE_PETITION, "OT_COMMISSIONER_STATE_PETITION value is incorrect");
588 static_assert(2 == OT_COMMISSIONER_STATE_ACTIVE, "OT_COMMISSIONER_STATE_ACTIVE value is incorrect");
589
590 return Stringify(aState, kStateString);
591 }
592
HandleJoinerEvent(otCommissionerJoinerEvent aEvent,const otJoinerInfo * aJoinerInfo,const otExtAddress * aJoinerId,void * aContext)593 void Commissioner::HandleJoinerEvent(otCommissionerJoinerEvent aEvent,
594 const otJoinerInfo *aJoinerInfo,
595 const otExtAddress *aJoinerId,
596 void *aContext)
597 {
598 static_cast<Commissioner *>(aContext)->HandleJoinerEvent(aEvent, aJoinerInfo, aJoinerId);
599 }
600
HandleJoinerEvent(otCommissionerJoinerEvent aEvent,const otJoinerInfo * aJoinerInfo,const otExtAddress * aJoinerId)601 void Commissioner::HandleJoinerEvent(otCommissionerJoinerEvent aEvent,
602 const otJoinerInfo *aJoinerInfo,
603 const otExtAddress *aJoinerId)
604 {
605 static const char *const kEventStrings[] = {
606 "start", // (0) OT_COMMISSIONER_JOINER_START
607 "connect", // (1) OT_COMMISSIONER_JOINER_CONNECTED
608 "finalize", // (2) OT_COMMISSIONER_JOINER_FINALIZE
609 "end", // (3) OT_COMMISSIONER_JOINER_END
610 "remove", // (4) OT_COMMISSIONER_JOINER_REMOVED
611 };
612
613 static_assert(0 == OT_COMMISSIONER_JOINER_START, "OT_COMMISSIONER_JOINER_START value is incorrect");
614 static_assert(1 == OT_COMMISSIONER_JOINER_CONNECTED, "OT_COMMISSIONER_JOINER_CONNECTED value is incorrect");
615 static_assert(2 == OT_COMMISSIONER_JOINER_FINALIZE, "OT_COMMISSIONER_JOINER_FINALIZE value is incorrect");
616 static_assert(3 == OT_COMMISSIONER_JOINER_END, "OT_COMMISSIONER_JOINER_END value is incorrect");
617 static_assert(4 == OT_COMMISSIONER_JOINER_REMOVED, "OT_COMMISSIONER_JOINER_REMOVED value is incorrect");
618
619 OT_UNUSED_VARIABLE(aJoinerInfo);
620
621 OutputFormat("Commissioner: Joiner %s ", Stringify(aEvent, kEventStrings));
622
623 if (aJoinerId != nullptr)
624 {
625 OutputExtAddress(*aJoinerId);
626 }
627
628 OutputNewLine();
629 }
630
631 /**
632 * @cli commissioner stop
633 * @code
634 * commissioner stop
635 * Done
636 * @endcode
637 * @par
638 * Stops the Thread %Commissioner role.
639 * @sa otCommissionerStop
640 */
Process(Arg aArgs[])641 template <> otError Commissioner::Process<Cmd("stop")>(Arg aArgs[])
642 {
643 OT_UNUSED_VARIABLE(aArgs);
644
645 return otCommissionerStop(GetInstancePtr());
646 }
647
648 /**
649 * @cli commissioner state
650 * @code
651 * commissioner state
652 * active
653 * Done
654 * @endcode
655 * @par
656 * Returns the current state of the %Commissioner. Possible values are
657 * `active`, `disabled`, or `petition` (petitioning to become %Commissioner).
658 * @sa otCommissionerState
659 */
Process(Arg aArgs[])660 template <> otError Commissioner::Process<Cmd("state")>(Arg aArgs[])
661 {
662 OT_UNUSED_VARIABLE(aArgs);
663
664 OutputLine("%s", StateToString(otCommissionerGetState(GetInstancePtr())));
665
666 return OT_ERROR_NONE;
667 }
668
Process(Arg aArgs[])669 otError Commissioner::Process(Arg aArgs[])
670 {
671 #define CmdEntry(aCommandString) \
672 { \
673 aCommandString, &Commissioner::Process<Cmd(aCommandString)> \
674 }
675
676 static constexpr Command kCommands[] = {
677 CmdEntry("announce"), CmdEntry("energy"), CmdEntry("id"), CmdEntry("joiner"),
678 CmdEntry("mgmtget"), CmdEntry("mgmtset"), CmdEntry("panid"), CmdEntry("provisioningurl"),
679 CmdEntry("sessionid"), CmdEntry("start"), CmdEntry("state"), CmdEntry("stop"),
680 };
681
682 #undef CmdEntry
683
684 static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
685
686 otError error = OT_ERROR_INVALID_COMMAND;
687 const Command *command;
688
689 if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
690 {
691 OutputCommandTable(kCommands);
692 ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
693 }
694
695 command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
696 VerifyOrExit(command != nullptr);
697
698 error = (this->*command->mHandler)(aArgs + 1);
699
700 exit:
701 return error;
702 }
703
HandleEnergyReport(uint32_t aChannelMask,const uint8_t * aEnergyList,uint8_t aEnergyListLength,void * aContext)704 void Commissioner::HandleEnergyReport(uint32_t aChannelMask,
705 const uint8_t *aEnergyList,
706 uint8_t aEnergyListLength,
707 void *aContext)
708 {
709 static_cast<Commissioner *>(aContext)->HandleEnergyReport(aChannelMask, aEnergyList, aEnergyListLength);
710 }
711
HandleEnergyReport(uint32_t aChannelMask,const uint8_t * aEnergyList,uint8_t aEnergyListLength)712 void Commissioner::HandleEnergyReport(uint32_t aChannelMask, const uint8_t *aEnergyList, uint8_t aEnergyListLength)
713 {
714 OutputFormat("Energy: %08lx ", ToUlong(aChannelMask));
715
716 for (uint8_t i = 0; i < aEnergyListLength; i++)
717 {
718 OutputFormat("%d ", static_cast<int8_t>(aEnergyList[i]));
719 }
720
721 OutputNewLine();
722 }
723
HandlePanIdConflict(uint16_t aPanId,uint32_t aChannelMask,void * aContext)724 void Commissioner::HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask, void *aContext)
725 {
726 static_cast<Commissioner *>(aContext)->HandlePanIdConflict(aPanId, aChannelMask);
727 }
728
HandlePanIdConflict(uint16_t aPanId,uint32_t aChannelMask)729 void Commissioner::HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask)
730 {
731 OutputLine("Conflict: %04x, %08lx", aPanId, ToUlong(aChannelMask));
732 }
733
734 } // namespace Cli
735 } // namespace ot
736
737 #endif // OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
738