1 /*
2  *  Copyright (c) 2022, 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 strain 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 #include "cli.hpp"
30 
31 #include <assert.h>
32 #include <errno.h>
33 #include <getopt.h>
34 #include <stdbool.h>
35 #include <stdint.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 
41 #include "power.hpp"
42 #include "common/code_utils.hpp"
43 
44 namespace ot {
45 namespace Fct {
46 
47 const struct Cli::Command Cli::sCommands[] = {
48     {"powercalibrationtable", &Cli::ProcessCalibrationTable},
49     {"targetpowertable", &Cli::ProcessTargetPowerTable},
50     {"regiondomaintable", &Cli::ProcessRegionDomainTable},
51 };
52 
GetNextTargetPower(const Power::Domain & aDomain,int & aIterator,Power::TargetPower & aTargetPower)53 otError Cli::GetNextTargetPower(const Power::Domain &aDomain, int &aIterator, Power::TargetPower &aTargetPower)
54 {
55     otError error = OT_ERROR_NOT_FOUND;
56     char    value[kMaxValueSize];
57     char   *domain;
58     char   *psave;
59 
60     while (mProductConfigFile.Get(kKeyTargetPower, aIterator, value, sizeof(value)) == OT_ERROR_NONE)
61     {
62         if (((domain = strtok_r(value, kCommaDelimiter, &psave)) == nullptr) || (aDomain != domain))
63         {
64             continue;
65         }
66 
67         error = aTargetPower.FromString(psave);
68         break;
69     }
70 
71     return error;
72 }
73 
GetNextDomain(int & aIterator,Power::Domain & aDomain)74 otError Cli::GetNextDomain(int &aIterator, Power::Domain &aDomain)
75 {
76     otError error = OT_ERROR_NOT_FOUND;
77     char    value[kMaxValueSize];
78     char   *str;
79 
80     while (mProductConfigFile.Get(kKeyRegionDomainMapping, aIterator, value, sizeof(value)) == OT_ERROR_NONE)
81     {
82         if ((str = strtok(value, kCommaDelimiter)) == nullptr)
83         {
84             continue;
85         }
86 
87         error = aDomain.Set(str);
88         break;
89     }
90 
91 exit:
92     return error;
93 }
94 
ProcessTargetPowerTable(Utils::CmdLineParser::Arg aArgs[])95 otError Cli::ProcessTargetPowerTable(Utils::CmdLineParser::Arg aArgs[])
96 {
97     otError       error    = OT_ERROR_NONE;
98     int           iterator = 0;
99     Power::Domain domain;
100 
101     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
102 
103     printf("|  Domain  | ChStart |  ChEnd  | TargetPower(0.01dBm) |\r\n");
104     printf("+----------+---------+---------+----------------------+\r\n");
105     while (GetNextDomain(iterator, domain) == OT_ERROR_NONE)
106     {
107         int                iter = 0;
108         Power::TargetPower targetPower;
109 
110         while (GetNextTargetPower(domain, iter, targetPower) == OT_ERROR_NONE)
111         {
112             printf("| %-8s | %-7d | %-7d | %-20d |\r\n", domain.AsCString(), targetPower.GetChannelStart(),
113                    targetPower.GetChannelEnd(), targetPower.GetTargetPower());
114         }
115     }
116 
117 exit:
118     return error;
119 }
120 
ProcessRegionDomainTable(Utils::CmdLineParser::Arg aArgs[])121 otError Cli::ProcessRegionDomainTable(Utils::CmdLineParser::Arg aArgs[])
122 {
123     otError error    = OT_ERROR_NONE;
124     int     iterator = 0;
125     char    value[kMaxValueSize];
126     char   *domain;
127     char   *psave;
128 
129     VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
130 
131     while (mProductConfigFile.Get(kKeyRegionDomainMapping, iterator, value, sizeof(value)) == OT_ERROR_NONE)
132     {
133         printf("%s\r\n", value);
134     }
135 
136 exit:
137     return error;
138 }
139 
ParseNextCalibratedPower(char * aCalibratedPowerString,uint16_t aLength,uint16_t & aIterator,Power::CalibratedPower & aCalibratedPower)140 otError Cli::ParseNextCalibratedPower(char                   *aCalibratedPowerString,
141                                       uint16_t                aLength,
142                                       uint16_t               &aIterator,
143                                       Power::CalibratedPower &aCalibratedPower)
144 {
145     otError                    error = OT_ERROR_NONE;
146     char                      *start = aCalibratedPowerString + aIterator;
147     char                      *end;
148     char                      *subString;
149     int16_t                    actualPower;
150     ot::Power::RawPowerSetting rawPowerSetting;
151 
152     VerifyOrExit(aIterator < aLength, error = OT_ERROR_PARSE);
153 
154     end = strstr(start, "/");
155     if (end != nullptr)
156     {
157         aIterator = end - aCalibratedPowerString + 1; // +1 to skip '/'
158         *end      = '\0';
159     }
160     else
161     {
162         aIterator = aLength;
163         end       = aCalibratedPowerString + aLength;
164     }
165 
166     subString = strstr(start, kCommaDelimiter);
167     VerifyOrExit(subString != nullptr, error = OT_ERROR_PARSE);
168     *subString = '\0';
169     subString++;
170 
171     SuccessOrExit(error = Utils::CmdLineParser::ParseAsInt16(start, actualPower));
172     aCalibratedPower.SetActualPower(actualPower);
173 
174     VerifyOrExit(subString < end, error = OT_ERROR_PARSE);
175     SuccessOrExit(error = rawPowerSetting.Set(subString));
176     aCalibratedPower.SetRawPowerSetting(rawPowerSetting);
177 
178 exit:
179     return error;
180 }
181 
ProcessCalibrationTable(Utils::CmdLineParser::Arg aArgs[])182 otError Cli::ProcessCalibrationTable(Utils::CmdLineParser::Arg aArgs[])
183 {
184     otError error = OT_ERROR_NONE;
185 
186     if (aArgs[0].IsEmpty())
187     {
188         int  iterator = 0;
189         char value[kMaxValueSize];
190 
191         ot::Power::CalibratedPower calibratedPower;
192 
193         printf("| ChStart |  ChEnd  | ActualPower(0.01dBm) | RawPowerSetting |\r\n");
194         printf("+---------+---------+----------------------+-----------------+\r\n");
195 
196         while (mFactoryConfigFile.Get(kKeyCalibratedPower, iterator, value, sizeof(value)) == OT_ERROR_NONE)
197         {
198             SuccessOrExit(error = calibratedPower.FromString(value));
199             printf("| %-7d | %-7d | %-20d | %-15s |\r\n", calibratedPower.GetChannelStart(),
200                    calibratedPower.GetChannelEnd(), calibratedPower.GetActualPower(),
201                    calibratedPower.GetRawPowerSetting().ToString().AsCString());
202         }
203     }
204     else if (aArgs[0] == "add")
205     {
206         constexpr uint16_t kStateSearchDomain = 0;
207         constexpr uint16_t kStateSearchPower  = 1;
208 
209         uint8_t                state = kStateSearchDomain;
210         char                  *subString;
211         uint8_t                channel;
212         Power::CalibratedPower calibratedPower;
213 
214         for (Utils::CmdLineParser::Arg *arg = &aArgs[1]; !arg->IsEmpty(); arg++)
215         {
216             if ((state == kStateSearchDomain) && (*arg == "-b"))
217             {
218                 arg++;
219                 VerifyOrExit(!arg->IsEmpty(), error = OT_ERROR_INVALID_ARGS);
220 
221                 subString = strtok(arg->GetCString(), kCommaDelimiter);
222                 VerifyOrExit(subString != nullptr, error = OT_ERROR_PARSE);
223                 SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(subString, channel));
224                 calibratedPower.SetChannelStart(channel);
225 
226                 subString = strtok(NULL, kCommaDelimiter);
227                 VerifyOrExit(subString != nullptr, error = OT_ERROR_PARSE);
228                 SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(subString, channel));
229                 calibratedPower.SetChannelEnd(channel);
230                 VerifyOrExit(calibratedPower.GetChannelStart() <= calibratedPower.GetChannelEnd(),
231                              error = OT_ERROR_INVALID_ARGS);
232 
233                 state = kStateSearchPower;
234             }
235             else if ((state == kStateSearchPower) && (*arg == "-c"))
236             {
237                 uint16_t length;
238                 uint16_t iterator = 0;
239 
240                 arg++;
241                 VerifyOrExit(!arg->IsEmpty(), error = OT_ERROR_INVALID_ARGS);
242 
243                 length = strlen(arg->GetCString());
244                 while (ParseNextCalibratedPower(arg->GetCString(), length, iterator, calibratedPower) == OT_ERROR_NONE)
245                 {
246                     SuccessOrExit(
247                         error = mFactoryConfigFile.Add(kKeyCalibratedPower, calibratedPower.ToString().AsCString()));
248                 }
249 
250                 state = kStateSearchDomain;
251             }
252             else
253             {
254                 error = OT_ERROR_INVALID_ARGS;
255                 break;
256             }
257         }
258 
259         if (state == kStateSearchPower)
260         {
261             error = OT_ERROR_INVALID_ARGS;
262         }
263     }
264     else if (aArgs[0] == "clear")
265     {
266         error = mFactoryConfigFile.Clear(kKeyCalibratedPower);
267     }
268     else
269     {
270         error = OT_ERROR_INVALID_ARGS;
271     }
272 
273 exit:
274     return error;
275 }
276 
ProcessCommand(Utils::CmdLineParser::Arg aArgs[])277 void Cli::ProcessCommand(Utils::CmdLineParser::Arg aArgs[])
278 {
279     otError error = OT_ERROR_NOT_FOUND;
280     int     i;
281 
282     for (i = 0; i < (sizeof(sCommands) / sizeof(sCommands[0])); i++)
283     {
284         if (strcmp(aArgs[0].GetCString(), sCommands[i].mName) == 0)
285         {
286             error = (this->*sCommands[i].mCommand)(aArgs + 1);
287             break;
288         }
289     }
290 
291 exit:
292     AppendErrorResult(error);
293 }
294 
ProcessLine(char * aLine)295 void Cli::ProcessLine(char *aLine)
296 {
297     const int                 kMaxArgs = 20;
298     Utils::CmdLineParser::Arg args[kMaxArgs + 1];
299 
300     SuccessOrExit(ot::Utils::CmdLineParser::ParseCmd(aLine, args, kMaxArgs));
301     VerifyOrExit(!args[0].IsEmpty());
302 
303     ProcessCommand(args);
304 
305 exit:
306     OutputPrompt();
307 }
308 
OutputPrompt(void)309 void Cli::OutputPrompt(void)
310 {
311     printf("> ");
312     fflush(stdout);
313 }
314 
AppendErrorResult(otError aError)315 void Cli::AppendErrorResult(otError aError)
316 {
317     if (aError != OT_ERROR_NONE)
318     {
319         printf("failed\r\nstatus %#x\r\n", aError);
320     }
321     else
322     {
323         printf("Done\r\n");
324     }
325 
326     fflush(stdout);
327 }
328 } // namespace Fct
329 } // namespace ot
330