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