1 /*
2 * Copyright (c) 2016, 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 the diagnostics module.
32 */
33
34 #include "factory_diags.hpp"
35
36 #if OPENTHREAD_CONFIG_DIAG_ENABLE
37
38 #include <stdio.h>
39 #include <stdlib.h>
40
41 #include <openthread/platform/alarm-milli.h>
42 #include <openthread/platform/diag.h>
43
44 #include "common/as_core_type.hpp"
45 #include "common/code_utils.hpp"
46 #include "common/locator_getters.hpp"
47 #include "instance/instance.hpp"
48 #include "radio/radio.hpp"
49 #include "utils/parse_cmdline.hpp"
50
51 OT_TOOL_WEAK
otPlatDiagProcess(otInstance * aInstance,uint8_t aArgsLength,char * aArgs[],char * aOutput,size_t aOutputMaxLen)52 otError otPlatDiagProcess(otInstance *aInstance,
53 uint8_t aArgsLength,
54 char *aArgs[],
55 char *aOutput,
56 size_t aOutputMaxLen)
57 {
58 OT_UNUSED_VARIABLE(aArgsLength);
59 OT_UNUSED_VARIABLE(aArgs);
60 OT_UNUSED_VARIABLE(aInstance);
61 OT_UNUSED_VARIABLE(aOutput);
62 OT_UNUSED_VARIABLE(aOutputMaxLen);
63
64 return ot::kErrorInvalidCommand;
65 }
66
67 namespace ot {
68 namespace FactoryDiags {
69
70 #if OPENTHREAD_RADIO && !OPENTHREAD_RADIO_CLI
71
72 const struct Diags::Command Diags::sCommands[] = {
73 {"channel", &Diags::ProcessChannel},
74 {"cw", &Diags::ProcessContinuousWave},
75 {"echo", &Diags::ProcessEcho},
76 {"gpio", &Diags::ProcessGpio},
77 {"power", &Diags::ProcessPower},
78 {"powersettings", &Diags::ProcessPowerSettings},
79 {"rawpowersetting", &Diags::ProcessRawPowerSetting},
80 {"start", &Diags::ProcessStart},
81 {"stop", &Diags::ProcessStop},
82 {"stream", &Diags::ProcessStream},
83 };
84
Diags(Instance & aInstance)85 Diags::Diags(Instance &aInstance)
86 : InstanceLocator(aInstance)
87 {
88 }
89
ProcessChannel(uint8_t aArgsLength,char * aArgs[],char * aOutput,size_t aOutputMaxLen)90 Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
91 {
92 Error error = kErrorNone;
93 long value;
94
95 VerifyOrExit(aArgsLength == 1, error = kErrorInvalidArgs);
96
97 SuccessOrExit(error = ParseLong(aArgs[0], value));
98 VerifyOrExit(value >= Radio::kChannelMin && value <= Radio::kChannelMax, error = kErrorInvalidArgs);
99
100 otPlatDiagChannelSet(static_cast<uint8_t>(value));
101
102 exit:
103 AppendErrorResult(error, aOutput, aOutputMaxLen);
104 return error;
105 }
106
ProcessPower(uint8_t aArgsLength,char * aArgs[],char * aOutput,size_t aOutputMaxLen)107 Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
108 {
109 Error error = kErrorNone;
110 long value;
111
112 VerifyOrExit(aArgsLength == 1, error = kErrorInvalidArgs);
113
114 SuccessOrExit(error = ParseLong(aArgs[0], value));
115
116 otPlatDiagTxPowerSet(static_cast<int8_t>(value));
117
118 exit:
119 AppendErrorResult(error, aOutput, aOutputMaxLen);
120 return error;
121 }
122
ProcessEcho(uint8_t aArgsLength,char * aArgs[],char * aOutput,size_t aOutputMaxLen)123 Error Diags::ProcessEcho(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
124 {
125 Error error = kErrorNone;
126
127 if (aArgsLength == 1)
128 {
129 snprintf(aOutput, aOutputMaxLen, "%s\r\n", aArgs[0]);
130 }
131 else if ((aArgsLength == 2) && (strcmp(aArgs[0], "-n") == 0))
132 {
133 const uint8_t kReservedLen = 3; // 1 byte '\r', 1 byte '\n' and 1 byte '\0'
134 uint32_t outputMaxLen = static_cast<uint32_t>(aOutputMaxLen) - kReservedLen;
135 long value;
136 uint32_t i;
137 uint32_t number;
138
139 SuccessOrExit(error = ParseLong(aArgs[1], value));
140 number = Min(static_cast<uint32_t>(value), outputMaxLen);
141
142 for (i = 0; i < number; i++)
143 {
144 aOutput[i] = '0' + i % 10;
145 }
146
147 snprintf(&aOutput[i], aOutputMaxLen - i, "\r\n");
148 }
149 else
150 {
151 error = kErrorInvalidArgs;
152 }
153
154 exit:
155 AppendErrorResult(error, aOutput, aOutputMaxLen);
156 return error;
157 }
158
ProcessStart(uint8_t aArgsLength,char * aArgs[],char * aOutput,size_t aOutputMaxLen)159 Error Diags::ProcessStart(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
160 {
161 OT_UNUSED_VARIABLE(aArgsLength);
162 OT_UNUSED_VARIABLE(aArgs);
163 OT_UNUSED_VARIABLE(aOutput);
164 OT_UNUSED_VARIABLE(aOutputMaxLen);
165
166 otPlatDiagModeSet(true);
167
168 return kErrorNone;
169 }
170
ProcessStop(uint8_t aArgsLength,char * aArgs[],char * aOutput,size_t aOutputMaxLen)171 Error Diags::ProcessStop(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
172 {
173 OT_UNUSED_VARIABLE(aArgsLength);
174 OT_UNUSED_VARIABLE(aArgs);
175 OT_UNUSED_VARIABLE(aOutput);
176 OT_UNUSED_VARIABLE(aOutputMaxLen);
177
178 otPlatDiagModeSet(false);
179
180 return kErrorNone;
181 }
182
otPlatDiagAlarmFired(otInstance * aInstance)183 extern "C" void otPlatDiagAlarmFired(otInstance *aInstance) { otPlatDiagAlarmCallback(aInstance); }
184
185 #else // OPENTHREAD_RADIO && !OPENTHREAD_RADIO_CLI
186 // For OPENTHREAD_FTD, OPENTHREAD_MTD, OPENTHREAD_RADIO_CLI
187 const struct Diags::Command Diags::sCommands[] = {
188 {"channel", &Diags::ProcessChannel},
189 {"cw", &Diags::ProcessContinuousWave},
190 {"gpio", &Diags::ProcessGpio},
191 {"power", &Diags::ProcessPower},
192 {"powersettings", &Diags::ProcessPowerSettings},
193 {"rawpowersetting", &Diags::ProcessRawPowerSetting},
194 {"radio", &Diags::ProcessRadio},
195 {"repeat", &Diags::ProcessRepeat},
196 {"send", &Diags::ProcessSend},
197 {"start", &Diags::ProcessStart},
198 {"stats", &Diags::ProcessStats},
199 {"stop", &Diags::ProcessStop},
200 {"stream", &Diags::ProcessStream},
201 };
202
203 Diags::Diags(Instance &aInstance)
204 : InstanceLocator(aInstance)
205 , mTxPacket(&Get<Radio>().GetTransmitBuffer())
206 , mTxPeriod(0)
207 , mTxPackets(0)
208 , mChannel(20)
209 , mTxPower(0)
210 , mTxLen(0)
211 , mRepeatActive(false)
212 , mDiagSendOn(false)
213 {
214 mStats.Clear();
215 }
216
217 Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
218 {
219 Error error = kErrorNone;
220
221 VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState);
222
223 if (aArgsLength == 0)
224 {
225 snprintf(aOutput, aOutputMaxLen, "channel: %d\r\n", mChannel);
226 }
227 else
228 {
229 long value;
230
231 SuccessOrExit(error = ParseLong(aArgs[0], value));
232 VerifyOrExit(value >= Radio::kChannelMin && value <= Radio::kChannelMax, error = kErrorInvalidArgs);
233
234 mChannel = static_cast<uint8_t>(value);
235 IgnoreError(Get<Radio>().Receive(mChannel));
236 otPlatDiagChannelSet(mChannel);
237
238 snprintf(aOutput, aOutputMaxLen, "set channel to %d\r\nstatus 0x%02x\r\n", mChannel, error);
239 }
240
241 exit:
242 AppendErrorResult(error, aOutput, aOutputMaxLen);
243 return error;
244 }
245
246 Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
247 {
248 Error error = kErrorNone;
249
250 VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState);
251
252 if (aArgsLength == 0)
253 {
254 snprintf(aOutput, aOutputMaxLen, "tx power: %d dBm\r\n", mTxPower);
255 }
256 else
257 {
258 long value;
259
260 SuccessOrExit(error = ParseLong(aArgs[0], value));
261
262 mTxPower = static_cast<int8_t>(value);
263 SuccessOrExit(error = Get<Radio>().SetTransmitPower(mTxPower));
264 otPlatDiagTxPowerSet(mTxPower);
265
266 snprintf(aOutput, aOutputMaxLen, "set tx power to %d dBm\r\nstatus 0x%02x\r\n", mTxPower, error);
267 }
268
269 exit:
270 AppendErrorResult(error, aOutput, aOutputMaxLen);
271 return error;
272 }
273
274 Error Diags::ProcessRepeat(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
275 {
276 Error error = kErrorNone;
277
278 VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState);
279 VerifyOrExit(aArgsLength > 0, error = kErrorInvalidArgs);
280
281 if (strcmp(aArgs[0], "stop") == 0)
282 {
283 otPlatAlarmMilliStop(&GetInstance());
284 mRepeatActive = false;
285 snprintf(aOutput, aOutputMaxLen, "repeated packet transmission is stopped\r\nstatus 0x%02x\r\n", error);
286 }
287 else
288 {
289 long value;
290
291 VerifyOrExit(aArgsLength == 2, error = kErrorInvalidArgs);
292
293 SuccessOrExit(error = ParseLong(aArgs[0], value));
294 mTxPeriod = static_cast<uint32_t>(value);
295
296 SuccessOrExit(error = ParseLong(aArgs[1], value));
297 VerifyOrExit(value <= OT_RADIO_FRAME_MAX_SIZE, error = kErrorInvalidArgs);
298 VerifyOrExit(value >= OT_RADIO_FRAME_MIN_SIZE, error = kErrorInvalidArgs);
299 mTxLen = static_cast<uint8_t>(value);
300
301 mRepeatActive = true;
302 uint32_t now = otPlatAlarmMilliGetNow();
303 otPlatAlarmMilliStartAt(&GetInstance(), now, mTxPeriod);
304 snprintf(aOutput, aOutputMaxLen, "sending packets of length %#x at the delay of %#x ms\r\nstatus 0x%02x\r\n",
305 static_cast<int>(mTxLen), static_cast<int>(mTxPeriod), error);
306 }
307
308 exit:
309 AppendErrorResult(error, aOutput, aOutputMaxLen);
310 return error;
311 }
312
313 Error Diags::ProcessSend(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
314 {
315 Error error = kErrorNone;
316 long value;
317
318 VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState);
319 VerifyOrExit(aArgsLength == 2, error = kErrorInvalidArgs);
320
321 SuccessOrExit(error = ParseLong(aArgs[0], value));
322 mTxPackets = static_cast<uint32_t>(value);
323
324 SuccessOrExit(error = ParseLong(aArgs[1], value));
325 VerifyOrExit(value <= OT_RADIO_FRAME_MAX_SIZE, error = kErrorInvalidArgs);
326 VerifyOrExit(value >= OT_RADIO_FRAME_MIN_SIZE, error = kErrorInvalidArgs);
327 mTxLen = static_cast<uint8_t>(value);
328
329 snprintf(aOutput, aOutputMaxLen, "sending %#x packet(s), length %#x\r\nstatus 0x%02x\r\n",
330 static_cast<int>(mTxPackets), static_cast<int>(mTxLen), error);
331 TransmitPacket();
332
333 exit:
334 AppendErrorResult(error, aOutput, aOutputMaxLen);
335 return error;
336 }
337
338 Error Diags::ProcessStart(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
339 {
340 OT_UNUSED_VARIABLE(aArgsLength);
341 OT_UNUSED_VARIABLE(aArgs);
342
343 Error error = kErrorNone;
344
345 #if OPENTHREAD_FTD || OPENTHREAD_MTD
346 VerifyOrExit(!Get<ThreadNetif>().IsUp(), error = kErrorInvalidState);
347 #endif
348
349 otPlatDiagChannelSet(mChannel);
350 otPlatDiagTxPowerSet(mTxPower);
351
352 IgnoreError(Get<Radio>().Enable());
353 Get<Radio>().SetPromiscuous(true);
354 otPlatAlarmMilliStop(&GetInstance());
355 SuccessOrExit(error = Get<Radio>().Receive(mChannel));
356 SuccessOrExit(error = Get<Radio>().SetTransmitPower(mTxPower));
357 otPlatDiagModeSet(true);
358 mStats.Clear();
359 snprintf(aOutput, aOutputMaxLen, "start diagnostics mode\r\nstatus 0x%02x\r\n", error);
360
361 exit:
362 AppendErrorResult(error, aOutput, aOutputMaxLen);
363 return error;
364 }
365
366 Error Diags::ProcessStats(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
367 {
368 Error error = kErrorNone;
369
370 VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState);
371
372 if ((aArgsLength == 1) && (strcmp(aArgs[0], "clear") == 0))
373 {
374 mStats.Clear();
375 snprintf(aOutput, aOutputMaxLen, "stats cleared\r\n");
376 }
377 else
378 {
379 VerifyOrExit(aArgsLength == 0, error = kErrorInvalidArgs);
380 snprintf(aOutput, aOutputMaxLen,
381 "received packets: %d\r\nsent packets: %d\r\n"
382 "first received packet: rssi=%d, lqi=%d\r\n"
383 "last received packet: rssi=%d, lqi=%d\r\n",
384 static_cast<int>(mStats.mReceivedPackets), static_cast<int>(mStats.mSentPackets),
385 static_cast<int>(mStats.mFirstRssi), static_cast<int>(mStats.mFirstLqi),
386 static_cast<int>(mStats.mLastRssi), static_cast<int>(mStats.mLastLqi));
387 }
388
389 exit:
390 AppendErrorResult(error, aOutput, aOutputMaxLen);
391 return error;
392 }
393
394 Error Diags::ProcessStop(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
395 {
396 OT_UNUSED_VARIABLE(aArgsLength);
397 OT_UNUSED_VARIABLE(aArgs);
398
399 Error error = kErrorNone;
400
401 VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState);
402
403 otPlatAlarmMilliStop(&GetInstance());
404 otPlatDiagModeSet(false);
405 Get<Radio>().SetPromiscuous(false);
406
407 snprintf(aOutput, aOutputMaxLen,
408 "received packets: %d\r\nsent packets: %d\r\n"
409 "first received packet: rssi=%d, lqi=%d\r\n"
410 "last received packet: rssi=%d, lqi=%d\r\n"
411 "\nstop diagnostics mode\r\nstatus 0x%02x\r\n",
412 static_cast<int>(mStats.mReceivedPackets), static_cast<int>(mStats.mSentPackets),
413 static_cast<int>(mStats.mFirstRssi), static_cast<int>(mStats.mFirstLqi),
414 static_cast<int>(mStats.mLastRssi), static_cast<int>(mStats.mLastLqi), error);
415
416 exit:
417 AppendErrorResult(error, aOutput, aOutputMaxLen);
418 return error;
419 }
420
421 void Diags::TransmitPacket(void)
422 {
423 mTxPacket->mLength = mTxLen;
424 mTxPacket->mChannel = mChannel;
425
426 for (uint8_t i = 0; i < mTxLen; i++)
427 {
428 mTxPacket->mPsdu[i] = i;
429 }
430
431 mDiagSendOn = true;
432 IgnoreError(Get<Radio>().Transmit(*static_cast<Mac::TxFrame *>(mTxPacket)));
433 }
434
435 Error Diags::ProcessRadio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
436 {
437 Error error = kErrorInvalidArgs;
438
439 VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState);
440 VerifyOrExit(aArgsLength > 0, error = kErrorInvalidArgs);
441
442 if (strcmp(aArgs[0], "sleep") == 0)
443 {
444 SuccessOrExit(error = Get<Radio>().Sleep());
445 snprintf(aOutput, aOutputMaxLen, "set radio from receive to sleep \r\nstatus 0x%02x\r\n", error);
446 }
447 else if (strcmp(aArgs[0], "receive") == 0)
448 {
449 SuccessOrExit(error = Get<Radio>().Receive(mChannel));
450 SuccessOrExit(error = Get<Radio>().SetTransmitPower(mTxPower));
451 otPlatDiagChannelSet(mChannel);
452 otPlatDiagTxPowerSet(mTxPower);
453
454 snprintf(aOutput, aOutputMaxLen, "set radio from sleep to receive on channel %d\r\nstatus 0x%02x\r\n", mChannel,
455 error);
456 }
457 else if (strcmp(aArgs[0], "state") == 0)
458 {
459 otRadioState state = Get<Radio>().GetState();
460
461 error = kErrorNone;
462
463 switch (state)
464 {
465 case OT_RADIO_STATE_DISABLED:
466 snprintf(aOutput, aOutputMaxLen, "disabled\r\n");
467 break;
468
469 case OT_RADIO_STATE_SLEEP:
470 snprintf(aOutput, aOutputMaxLen, "sleep\r\n");
471 break;
472
473 case OT_RADIO_STATE_RECEIVE:
474 snprintf(aOutput, aOutputMaxLen, "receive\r\n");
475 break;
476
477 case OT_RADIO_STATE_TRANSMIT:
478 snprintf(aOutput, aOutputMaxLen, "transmit\r\n");
479 break;
480
481 default:
482 snprintf(aOutput, aOutputMaxLen, "invalid\r\n");
483 break;
484 }
485 }
486
487 exit:
488 AppendErrorResult(error, aOutput, aOutputMaxLen);
489 return error;
490 }
491
492 extern "C" void otPlatDiagAlarmFired(otInstance *aInstance) { AsCoreType(aInstance).Get<Diags>().AlarmFired(); }
493
494 void Diags::AlarmFired(void)
495 {
496 if (mRepeatActive)
497 {
498 uint32_t now = otPlatAlarmMilliGetNow();
499
500 TransmitPacket();
501 otPlatAlarmMilliStartAt(&GetInstance(), now, mTxPeriod);
502 }
503 else
504 {
505 otPlatDiagAlarmCallback(&GetInstance());
506 }
507 }
508
509 void Diags::ReceiveDone(otRadioFrame *aFrame, Error aError)
510 {
511 if (aError == kErrorNone)
512 {
513 // for sensitivity test, only record the rssi and lqi for the first and last packet
514 if (mStats.mReceivedPackets == 0)
515 {
516 mStats.mFirstRssi = aFrame->mInfo.mRxInfo.mRssi;
517 mStats.mFirstLqi = aFrame->mInfo.mRxInfo.mLqi;
518 }
519
520 mStats.mLastRssi = aFrame->mInfo.mRxInfo.mRssi;
521 mStats.mLastLqi = aFrame->mInfo.mRxInfo.mLqi;
522
523 mStats.mReceivedPackets++;
524 }
525
526 otPlatDiagRadioReceived(&GetInstance(), aFrame, aError);
527 }
528
529 void Diags::TransmitDone(Error aError)
530 {
531 VerifyOrExit(mDiagSendOn);
532 mDiagSendOn = false;
533
534 if (aError == kErrorNone)
535 {
536 mStats.mSentPackets++;
537
538 if (mTxPackets > 1)
539 {
540 mTxPackets--;
541 }
542 else
543 {
544 ExitNow();
545 }
546 }
547
548 VerifyOrExit(!mRepeatActive);
549 TransmitPacket();
550
551 exit:
552 return;
553 }
554
555 #endif // OPENTHREAD_RADIO
556
ProcessContinuousWave(uint8_t aArgsLength,char * aArgs[],char * aOutput,size_t aOutputMaxLen)557 Error Diags::ProcessContinuousWave(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
558 {
559 Error error = kErrorInvalidArgs;
560
561 VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState);
562 VerifyOrExit(aArgsLength > 0, error = kErrorInvalidArgs);
563
564 if (strcmp(aArgs[0], "start") == 0)
565 {
566 SuccessOrExit(error = otPlatDiagRadioTransmitCarrier(&GetInstance(), true));
567 }
568 else if (strcmp(aArgs[0], "stop") == 0)
569 {
570 SuccessOrExit(error = otPlatDiagRadioTransmitCarrier(&GetInstance(), false));
571 }
572
573 exit:
574 AppendErrorResult(error, aOutput, aOutputMaxLen);
575 return error;
576 }
577
ProcessStream(uint8_t aArgsLength,char * aArgs[],char * aOutput,size_t aOutputMaxLen)578 Error Diags::ProcessStream(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
579 {
580 Error error = kErrorInvalidArgs;
581
582 VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState);
583 VerifyOrExit(aArgsLength > 0, error = kErrorInvalidArgs);
584
585 if (strcmp(aArgs[0], "start") == 0)
586 {
587 error = otPlatDiagRadioTransmitStream(&GetInstance(), true);
588 }
589 else if (strcmp(aArgs[0], "stop") == 0)
590 {
591 error = otPlatDiagRadioTransmitStream(&GetInstance(), false);
592 }
593
594 exit:
595 AppendErrorResult(error, aOutput, aOutputMaxLen);
596 return error;
597 }
598
GetPowerSettings(uint8_t aChannel,PowerSettings & aPowerSettings)599 Error Diags::GetPowerSettings(uint8_t aChannel, PowerSettings &aPowerSettings)
600 {
601 aPowerSettings.mRawPowerSetting.mLength = RawPowerSetting::kMaxDataSize;
602 return otPlatDiagRadioGetPowerSettings(&GetInstance(), aChannel, &aPowerSettings.mTargetPower,
603 &aPowerSettings.mActualPower, aPowerSettings.mRawPowerSetting.mData,
604 &aPowerSettings.mRawPowerSetting.mLength);
605 }
606
ProcessPowerSettings(uint8_t aArgsLength,char * aArgs[],char * aOutput,size_t aOutputMaxLen)607 Error Diags::ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
608 {
609 Error error = kErrorInvalidArgs;
610 uint8_t channel;
611 PowerSettings powerSettings;
612
613 VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState);
614
615 if (aArgsLength == 0)
616 {
617 bool isPrePowerSettingsValid = false;
618 uint8_t preChannel = 0;
619 PowerSettings prePowerSettings;
620 int n;
621
622 n = snprintf(aOutput, aOutputMaxLen,
623 "| StartCh | EndCh | TargetPower | ActualPower | RawPowerSetting |\r\n"
624 "+---------+-------+-------------+-------------+-----------------+\r\n");
625 VerifyOrExit((n > 0) && (n < static_cast<int>(aOutputMaxLen)), error = kErrorNoBufs);
626 aOutput += n;
627 aOutputMaxLen -= static_cast<size_t>(n);
628
629 for (channel = Radio::kChannelMin; channel <= Radio::kChannelMax + 1; channel++)
630 {
631 error = (channel == Radio::kChannelMax + 1) ? kErrorNotFound : GetPowerSettings(channel, powerSettings);
632
633 if (isPrePowerSettingsValid && ((powerSettings != prePowerSettings) || (error != kErrorNone)))
634 {
635 n = snprintf(aOutput, aOutputMaxLen, "| %7u | %5u | %11d | %11d | %15s |\r\n", preChannel, channel - 1,
636 prePowerSettings.mTargetPower, prePowerSettings.mActualPower,
637 prePowerSettings.mRawPowerSetting.ToString().AsCString());
638 VerifyOrExit((n > 0) && (n < static_cast<int>(aOutputMaxLen)), error = kErrorNoBufs);
639 aOutput += n;
640 aOutputMaxLen -= static_cast<size_t>(n);
641 isPrePowerSettingsValid = false;
642 }
643
644 if ((error == kErrorNone) && (!isPrePowerSettingsValid))
645 {
646 preChannel = channel;
647 prePowerSettings = powerSettings;
648 isPrePowerSettingsValid = true;
649 }
650 }
651
652 error = kErrorNone;
653 }
654 else if (aArgsLength == 1)
655 {
656 SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(aArgs[0], channel));
657 VerifyOrExit(channel >= Radio::kChannelMin && channel <= Radio::kChannelMax, error = kErrorInvalidArgs);
658
659 SuccessOrExit(error = GetPowerSettings(channel, powerSettings));
660 snprintf(aOutput, aOutputMaxLen,
661 "TargetPower(0.01dBm): %d\r\nActualPower(0.01dBm): %d\r\nRawPowerSetting: %s\r\n",
662 powerSettings.mTargetPower, powerSettings.mActualPower,
663 powerSettings.mRawPowerSetting.ToString().AsCString());
664 }
665
666 exit:
667 AppendErrorResult(error, aOutput, aOutputMaxLen);
668 return error;
669 }
670
GetRawPowerSetting(RawPowerSetting & aRawPowerSetting)671 Error Diags::GetRawPowerSetting(RawPowerSetting &aRawPowerSetting)
672 {
673 aRawPowerSetting.mLength = RawPowerSetting::kMaxDataSize;
674 return otPlatDiagRadioGetRawPowerSetting(&GetInstance(), aRawPowerSetting.mData, &aRawPowerSetting.mLength);
675 }
676
ProcessRawPowerSetting(uint8_t aArgsLength,char * aArgs[],char * aOutput,size_t aOutputMaxLen)677 Error Diags::ProcessRawPowerSetting(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
678 {
679 Error error = kErrorInvalidArgs;
680 RawPowerSetting setting;
681
682 VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState);
683
684 if (aArgsLength == 0)
685 {
686 SuccessOrExit(error = GetRawPowerSetting(setting));
687 snprintf(aOutput, aOutputMaxLen, "%s\r\n", setting.ToString().AsCString());
688 }
689 else if (strcmp(aArgs[0], "enable") == 0)
690 {
691 SuccessOrExit(error = otPlatDiagRadioRawPowerSettingEnable(&GetInstance(), true));
692 }
693 else if (strcmp(aArgs[0], "disable") == 0)
694 {
695 SuccessOrExit(error = otPlatDiagRadioRawPowerSettingEnable(&GetInstance(), false));
696 }
697 else
698 {
699 setting.mLength = RawPowerSetting::kMaxDataSize;
700 SuccessOrExit(error = Utils::CmdLineParser::ParseAsHexString(aArgs[0], setting.mLength, setting.mData));
701 SuccessOrExit(error = otPlatDiagRadioSetRawPowerSetting(&GetInstance(), setting.mData, setting.mLength));
702 }
703
704 exit:
705 AppendErrorResult(error, aOutput, aOutputMaxLen);
706 return error;
707 }
708
ProcessGpio(uint8_t aArgsLength,char * aArgs[],char * aOutput,size_t aOutputMaxLen)709 Error Diags::ProcessGpio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
710 {
711 Error error = kErrorInvalidArgs;
712 long value;
713 uint32_t gpio;
714 bool level;
715 otGpioMode mode;
716
717 if ((aArgsLength == 2) && (strcmp(aArgs[0], "get") == 0))
718 {
719 SuccessOrExit(error = ParseLong(aArgs[1], value));
720 gpio = static_cast<uint32_t>(value);
721 SuccessOrExit(error = otPlatDiagGpioGet(gpio, &level));
722 snprintf(aOutput, aOutputMaxLen, "%d\r\n", level);
723 }
724 else if ((aArgsLength == 3) && (strcmp(aArgs[0], "set") == 0))
725 {
726 SuccessOrExit(error = ParseLong(aArgs[1], value));
727 gpio = static_cast<uint32_t>(value);
728 SuccessOrExit(error = ParseBool(aArgs[2], level));
729 SuccessOrExit(error = otPlatDiagGpioSet(gpio, level));
730 }
731 else if ((aArgsLength >= 2) && (strcmp(aArgs[0], "mode") == 0))
732 {
733 SuccessOrExit(error = ParseLong(aArgs[1], value));
734 gpio = static_cast<uint32_t>(value);
735
736 if (aArgsLength == 2)
737 {
738 SuccessOrExit(error = otPlatDiagGpioGetMode(gpio, &mode));
739 if (mode == OT_GPIO_MODE_INPUT)
740 {
741 snprintf(aOutput, aOutputMaxLen, "in\r\n");
742 }
743 else if (mode == OT_GPIO_MODE_OUTPUT)
744 {
745 snprintf(aOutput, aOutputMaxLen, "out\r\n");
746 }
747 }
748 else if ((aArgsLength == 3) && (strcmp(aArgs[2], "in") == 0))
749 {
750 SuccessOrExit(error = otPlatDiagGpioSetMode(gpio, OT_GPIO_MODE_INPUT));
751 }
752 else if ((aArgsLength == 3) && (strcmp(aArgs[2], "out") == 0))
753 {
754 SuccessOrExit(error = otPlatDiagGpioSetMode(gpio, OT_GPIO_MODE_OUTPUT));
755 }
756 }
757
758 exit:
759 AppendErrorResult(error, aOutput, aOutputMaxLen);
760 return error;
761 }
762
AppendErrorResult(Error aError,char * aOutput,size_t aOutputMaxLen)763 void Diags::AppendErrorResult(Error aError, char *aOutput, size_t aOutputMaxLen)
764 {
765 if (aError != kErrorNone)
766 {
767 snprintf(aOutput, aOutputMaxLen, "failed\r\nstatus %#x\r\n", aError);
768 }
769 }
770
ParseLong(char * aString,long & aLong)771 Error Diags::ParseLong(char *aString, long &aLong)
772 {
773 char *endptr;
774 aLong = strtol(aString, &endptr, 0);
775 return (*endptr == '\0') ? kErrorNone : kErrorParse;
776 }
777
ParseBool(char * aString,bool & aBool)778 Error Diags::ParseBool(char *aString, bool &aBool)
779 {
780 Error error;
781 long value;
782
783 SuccessOrExit(error = ParseLong(aString, value));
784 VerifyOrExit((value == 0) || (value == 1), error = kErrorParse);
785 aBool = static_cast<bool>(value);
786
787 exit:
788 return error;
789 }
790
ParseCmd(char * aString,uint8_t & aArgsLength,char * aArgs[])791 Error Diags::ParseCmd(char *aString, uint8_t &aArgsLength, char *aArgs[])
792 {
793 Error error;
794 Utils::CmdLineParser::Arg args[kMaxArgs + 1];
795
796 SuccessOrExit(error = Utils::CmdLineParser::ParseCmd(aString, args));
797 aArgsLength = Utils::CmdLineParser::Arg::GetArgsLength(args);
798 Utils::CmdLineParser::Arg::CopyArgsToStringArray(args, aArgs);
799
800 exit:
801 return error;
802 }
803
ProcessLine(const char * aString,char * aOutput,size_t aOutputMaxLen)804 Error Diags::ProcessLine(const char *aString, char *aOutput, size_t aOutputMaxLen)
805 {
806 constexpr uint16_t kMaxCommandBuffer = OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE;
807
808 Error error = kErrorNone;
809 char buffer[kMaxCommandBuffer];
810 char *args[kMaxArgs];
811 uint8_t argCount = 0;
812
813 VerifyOrExit(StringLength(aString, kMaxCommandBuffer) < kMaxCommandBuffer, error = kErrorNoBufs);
814
815 strcpy(buffer, aString);
816 error = ParseCmd(buffer, argCount, args);
817
818 exit:
819
820 switch (error)
821 {
822 case kErrorNone:
823 aOutput[0] = '\0'; // In case there is no output.
824 error = ProcessCmd(argCount, &args[0], aOutput, aOutputMaxLen);
825 break;
826
827 case kErrorNoBufs:
828 snprintf(aOutput, aOutputMaxLen, "failed: command string too long\r\n");
829 break;
830
831 case kErrorInvalidArgs:
832 snprintf(aOutput, aOutputMaxLen, "failed: command string contains too many arguments\r\n");
833 break;
834
835 default:
836 snprintf(aOutput, aOutputMaxLen, "failed to parse command string\r\n");
837 break;
838 }
839
840 return error;
841 }
842
ProcessCmd(uint8_t aArgsLength,char * aArgs[],char * aOutput,size_t aOutputMaxLen)843 Error Diags::ProcessCmd(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
844 {
845 Error error = kErrorNone;
846
847 // This `rcp` command is for debugging and testing only, building only when NDEBUG is not defined
848 // so that it will be excluded from release build.
849 #if OPENTHREAD_RADIO && !defined(NDEBUG)
850 if (aArgsLength > 0 && !strcmp(aArgs[0], "rcp"))
851 {
852 aArgs++;
853 aArgsLength--;
854 }
855 #endif
856
857 if (aArgsLength == 0)
858 {
859 snprintf(aOutput, aOutputMaxLen, "diagnostics mode is %s\r\n", otPlatDiagModeGet() ? "enabled" : "disabled");
860 ExitNow();
861 }
862 else
863 {
864 aOutput[0] = '\0';
865 }
866
867 for (const Command &command : sCommands)
868 {
869 if (strcmp(aArgs[0], command.mName) == 0)
870 {
871 error = (this->*command.mCommand)(aArgsLength - 1, (aArgsLength > 1) ? &aArgs[1] : nullptr, aOutput,
872 aOutputMaxLen);
873 ExitNow();
874 }
875 }
876
877 // more platform specific features will be processed under platform layer
878 error = otPlatDiagProcess(&GetInstance(), aArgsLength, aArgs, aOutput, aOutputMaxLen);
879
880 exit:
881 // Add more platform specific diagnostics features here.
882 if (error == kErrorInvalidCommand && aArgsLength > 1)
883 {
884 snprintf(aOutput, aOutputMaxLen, "diag feature '%s' is not supported\r\n", aArgs[0]);
885 }
886
887 return error;
888 }
889
IsEnabled(void)890 bool Diags::IsEnabled(void) { return otPlatDiagModeGet(); }
891
892 } // namespace FactoryDiags
893 } // namespace ot
894
otPlatDiagGpioSet(uint32_t aGpio,bool aValue)895 OT_TOOL_WEAK otError otPlatDiagGpioSet(uint32_t aGpio, bool aValue)
896 {
897 OT_UNUSED_VARIABLE(aGpio);
898 OT_UNUSED_VARIABLE(aValue);
899
900 return OT_ERROR_NOT_IMPLEMENTED;
901 }
902
otPlatDiagGpioGet(uint32_t aGpio,bool * aValue)903 OT_TOOL_WEAK otError otPlatDiagGpioGet(uint32_t aGpio, bool *aValue)
904 {
905 OT_UNUSED_VARIABLE(aGpio);
906 OT_UNUSED_VARIABLE(aValue);
907
908 return OT_ERROR_NOT_IMPLEMENTED;
909 }
910
otPlatDiagGpioSetMode(uint32_t aGpio,otGpioMode aMode)911 OT_TOOL_WEAK otError otPlatDiagGpioSetMode(uint32_t aGpio, otGpioMode aMode)
912 {
913 OT_UNUSED_VARIABLE(aGpio);
914 OT_UNUSED_VARIABLE(aMode);
915
916 return OT_ERROR_NOT_IMPLEMENTED;
917 }
918
otPlatDiagGpioGetMode(uint32_t aGpio,otGpioMode * aMode)919 OT_TOOL_WEAK otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode)
920 {
921 OT_UNUSED_VARIABLE(aGpio);
922 OT_UNUSED_VARIABLE(aMode);
923
924 return OT_ERROR_NOT_IMPLEMENTED;
925 }
926
otPlatDiagRadioSetRawPowerSetting(otInstance * aInstance,const uint8_t * aRawPowerSetting,uint16_t aRawPowerSettingLength)927 OT_TOOL_WEAK otError otPlatDiagRadioSetRawPowerSetting(otInstance *aInstance,
928 const uint8_t *aRawPowerSetting,
929 uint16_t aRawPowerSettingLength)
930 {
931 OT_UNUSED_VARIABLE(aInstance);
932 OT_UNUSED_VARIABLE(aRawPowerSetting);
933 OT_UNUSED_VARIABLE(aRawPowerSettingLength);
934
935 return OT_ERROR_NOT_IMPLEMENTED;
936 }
937
otPlatDiagRadioGetRawPowerSetting(otInstance * aInstance,uint8_t * aRawPowerSetting,uint16_t * aRawPowerSettingLength)938 OT_TOOL_WEAK otError otPlatDiagRadioGetRawPowerSetting(otInstance *aInstance,
939 uint8_t *aRawPowerSetting,
940 uint16_t *aRawPowerSettingLength)
941 {
942 OT_UNUSED_VARIABLE(aInstance);
943 OT_UNUSED_VARIABLE(aRawPowerSetting);
944 OT_UNUSED_VARIABLE(aRawPowerSettingLength);
945
946 return OT_ERROR_NOT_IMPLEMENTED;
947 }
948
otPlatDiagRadioRawPowerSettingEnable(otInstance * aInstance,bool aEnable)949 OT_TOOL_WEAK otError otPlatDiagRadioRawPowerSettingEnable(otInstance *aInstance, bool aEnable)
950 {
951 OT_UNUSED_VARIABLE(aInstance);
952 OT_UNUSED_VARIABLE(aEnable);
953
954 return OT_ERROR_NOT_IMPLEMENTED;
955 }
956
otPlatDiagRadioTransmitCarrier(otInstance * aInstance,bool aEnable)957 OT_TOOL_WEAK otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable)
958 {
959 OT_UNUSED_VARIABLE(aInstance);
960 OT_UNUSED_VARIABLE(aEnable);
961
962 return OT_ERROR_NOT_IMPLEMENTED;
963 }
964
otPlatDiagRadioTransmitStream(otInstance * aInstance,bool aEnable)965 OT_TOOL_WEAK otError otPlatDiagRadioTransmitStream(otInstance *aInstance, bool aEnable)
966 {
967 OT_UNUSED_VARIABLE(aInstance);
968 OT_UNUSED_VARIABLE(aEnable);
969
970 return OT_ERROR_NOT_IMPLEMENTED;
971 }
972
otPlatDiagRadioGetPowerSettings(otInstance * aInstance,uint8_t aChannel,int16_t * aTargetPower,int16_t * aActualPower,uint8_t * aRawPowerSetting,uint16_t * aRawPowerSettingLength)973 OT_TOOL_WEAK otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance,
974 uint8_t aChannel,
975 int16_t *aTargetPower,
976 int16_t *aActualPower,
977 uint8_t *aRawPowerSetting,
978 uint16_t *aRawPowerSettingLength)
979 {
980 OT_UNUSED_VARIABLE(aInstance);
981 OT_UNUSED_VARIABLE(aChannel);
982 OT_UNUSED_VARIABLE(aTargetPower);
983 OT_UNUSED_VARIABLE(aActualPower);
984 OT_UNUSED_VARIABLE(aRawPowerSetting);
985 OT_UNUSED_VARIABLE(aRawPowerSettingLength);
986
987 return OT_ERROR_NOT_IMPLEMENTED;
988 }
989 #endif // OPENTHREAD_CONFIG_DIAG_ENABLE
990