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