• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 Joiner role.
32  */
33 
34 #include "cli_joiner.hpp"
35 
36 #include <inttypes.h>
37 
38 #include "cli/cli.hpp"
39 
40 #if OPENTHREAD_CONFIG_JOINER_ENABLE
41 
42 namespace ot {
43 namespace Cli {
44 
Process(Arg aArgs[])45 template <> otError Joiner::Process<Cmd("discerner")>(Arg aArgs[])
46 {
47     otError error = OT_ERROR_INVALID_ARGS;
48 
49     /**
50      * @cli joiner discerner
51      * @code
52      * joiner discerner
53      * 0xabc/12
54      * Done
55      * @endcode
56      * @par api_copy
57      * #otJoinerGetDiscerner
58      */
59     if (aArgs[0].IsEmpty())
60     {
61         const otJoinerDiscerner *discerner = otJoinerGetDiscerner(GetInstancePtr());
62 
63         VerifyOrExit(discerner != nullptr, error = OT_ERROR_NOT_FOUND);
64 
65         OutputLine("0x%llx/%u", static_cast<unsigned long long>(discerner->mValue), discerner->mLength);
66         error = OT_ERROR_NONE;
67     }
68     else
69     {
70         otJoinerDiscerner discerner;
71 
72         memset(&discerner, 0, sizeof(discerner));
73 
74         /**
75          * @cli joiner discerner clear
76          * @code
77          * joiner discerner clear
78          * Done
79          * @endcode
80          * @par
81          * Clear the %Joiner discerner.
82          */
83         if (aArgs[0] == "clear")
84         {
85             error = otJoinerSetDiscerner(GetInstancePtr(), nullptr);
86         }
87         /**
88          * @cli joiner discerner (set)
89          * @code
90          * joiner discerner 0xabc/12
91          * Done
92          * @endcode
93          * @cparam joiner discerner @ca{discerner}
94          * *   Use `{number}/{length}` to set the `discerner`.
95          * *   `joiner discerner clear` sets `aDiscerner` to `nullptr`.
96          * @par api_copy
97          * #otJoinerSetDiscerner
98          */
99         else
100         {
101             VerifyOrExit(aArgs[1].IsEmpty());
102             SuccessOrExit(Interpreter::ParseJoinerDiscerner(aArgs[0], discerner));
103             error = otJoinerSetDiscerner(GetInstancePtr(), &discerner);
104         }
105     }
106 
107 exit:
108     return error;
109 }
110 
111 /**
112  * @cli joiner id
113  * @code
114  * joiner id
115  * d65e64fa83f81cf7
116  * Done
117  * @endcode
118  * @par api_copy
119  * #otJoinerGetId
120  */
Process(Arg aArgs[])121 template <> otError Joiner::Process<Cmd("id")>(Arg aArgs[])
122 {
123     OT_UNUSED_VARIABLE(aArgs);
124 
125     OutputExtAddressLine(*otJoinerGetId(GetInstancePtr()));
126 
127     return OT_ERROR_NONE;
128 }
129 
130 /**
131  * @cli joiner start
132  * @code
133  * joiner start J01NM3
134  * Done
135  * @endcode
136  * @cparam joiner start @ca{joining-device-credential} [@ca{provisioning-url}]
137  * *   `joining-device-credential`: %Joiner Passphrase. Must be a string of all uppercase alphanumeric
138  *     characters (0-9 and A-Y, excluding I, O, Q, and Z for readability), with a length between 6 and
139  *     32 characters.
140  * *   `provisioning-url`: Provisioning URL for the %Joiner (optional).
141  * @par api_copy
142  * #otJoinerStart
143  */
Process(Arg aArgs[])144 template <> otError Joiner::Process<Cmd("start")>(Arg aArgs[])
145 {
146     otError error;
147 
148     VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
149 
150     error = otJoinerStart(GetInstancePtr(),
151                           aArgs[0].GetCString(),           // aPskd
152                           aArgs[1].GetCString(),           // aProvisioningUrl (`nullptr` if aArgs[1] is empty)
153                           PACKAGE_NAME,                    // aVendorName
154                           OPENTHREAD_CONFIG_PLATFORM_INFO, // aVendorModel
155                           PACKAGE_VERSION,                 // aVendorSwVersion
156                           nullptr,                         // aVendorData
157                           &Joiner::HandleCallback, this);
158 
159 exit:
160     return error;
161 }
162 
163 /**
164  * @cli joiner stop
165  * @code
166  * joiner stop
167  * Done
168  * @endcode
169  * @par api_copy
170  * #otJoinerStop
171  */
Process(Arg aArgs[])172 template <> otError Joiner::Process<Cmd("stop")>(Arg aArgs[])
173 {
174     OT_UNUSED_VARIABLE(aArgs);
175 
176     otJoinerStop(GetInstancePtr());
177 
178     return OT_ERROR_NONE;
179 }
180 
181 /**
182  * @cli joiner state
183  * @code
184  * joiner state
185  * Idle
186  * Done
187  * @endcode
188  * @par api_copy
189  * #otJoinerGetState
190  * @par
191  * Returns one of the following states:
192  * *   `Idle`
193  * *   `Discover`
194  * *   `Connecting`
195  * *   `Connected`
196  * *   `Entrust`
197  * *   `Joined`
198  */
Process(Arg aArgs[])199 template <> otError Joiner::Process<Cmd("state")>(Arg aArgs[])
200 {
201     OT_UNUSED_VARIABLE(aArgs);
202 
203     OutputLine("%s", otJoinerStateToString(otJoinerGetState(GetInstancePtr())));
204 
205     return OT_ERROR_NONE;
206 }
207 
Process(Arg aArgs[])208 otError Joiner::Process(Arg aArgs[])
209 {
210 #define CmdEntry(aCommandString)                              \
211     {                                                         \
212         aCommandString, &Joiner::Process<Cmd(aCommandString)> \
213     }
214 
215     static constexpr Command kCommands[] = {
216         CmdEntry("discerner"), CmdEntry("id"), CmdEntry("start"), CmdEntry("state"), CmdEntry("stop"),
217     };
218 
219 #undef CmdEntry
220 
221     static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
222 
223     otError        error = OT_ERROR_INVALID_COMMAND;
224     const Command *command;
225 
226     /**
227      * @cli joiner help
228      * @code
229      * joiner help
230      * help
231      * id
232      * start
233      * state
234      * stop
235      * Done
236      * @endcode
237      * @par
238      * Print the `joiner` help menu.
239      */
240     if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
241     {
242         OutputCommandTable(kCommands);
243         ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
244     }
245 
246     command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
247     VerifyOrExit(command != nullptr);
248 
249     error = (this->*command->mHandler)(aArgs + 1);
250 
251 exit:
252     return error;
253 }
254 
HandleCallback(otError aError,void * aContext)255 void Joiner::HandleCallback(otError aError, void *aContext)
256 {
257     static_cast<Joiner *>(aContext)->HandleCallback(aError);
258 }
259 
HandleCallback(otError aError)260 void Joiner::HandleCallback(otError aError)
261 {
262     switch (aError)
263     {
264     case OT_ERROR_NONE:
265         OutputLine("Join success");
266         break;
267 
268     default:
269         OutputLine("Join failed [%s]", otThreadErrorToString(aError));
270         break;
271     }
272 }
273 
274 } // namespace Cli
275 } // namespace ot
276 
277 #endif // OPENTHREAD_CONFIG_JOINER_ENABLE
278