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/code_utils.hpp"
45 #include "common/instance.hpp"
46 #include "common/locator_getters.hpp"
47 #include "radio/radio.hpp"
48 #include "utils/parse_cmdline.hpp"
49
50 OT_TOOL_WEAK
otPlatDiagProcess(otInstance * aInstance,uint8_t aArgsLength,char * aArgs[],char * aOutput,size_t aOutputMaxLen)51 otError otPlatDiagProcess(otInstance *aInstance,
52 uint8_t aArgsLength,
53 char * aArgs[],
54 char * aOutput,
55 size_t aOutputMaxLen)
56 {
57 OT_UNUSED_VARIABLE(aArgsLength);
58 OT_UNUSED_VARIABLE(aArgs);
59 OT_UNUSED_VARIABLE(aInstance);
60 OT_UNUSED_VARIABLE(aOutput);
61 OT_UNUSED_VARIABLE(aOutputMaxLen);
62
63 return ot::kErrorInvalidCommand;
64 }
65
66 namespace ot {
67 namespace FactoryDiags {
68
69 #if OPENTHREAD_RADIO
70
71 const struct Diags::Command Diags::sCommands[] = {
72 {"channel", &Diags::ProcessChannel},
73 {"power", &Diags::ProcessPower},
74 {"start", &Diags::ProcessStart},
75 {"stop", &Diags::ProcessStop},
76 };
77
Diags(Instance & aInstance)78 Diags::Diags(Instance &aInstance)
79 : InstanceLocator(aInstance)
80 {
81 }
82
ProcessChannel(uint8_t aArgsLength,char * aArgs[],char * aOutput,size_t aOutputMaxLen)83 Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
84 {
85 Error error = kErrorNone;
86 long value;
87
88 VerifyOrExit(aArgsLength == 1, error = kErrorInvalidArgs);
89
90 SuccessOrExit(error = ParseLong(aArgs[0], value));
91 VerifyOrExit(value >= Radio::kChannelMin && value <= Radio::kChannelMax, error = kErrorInvalidArgs);
92
93 otPlatDiagChannelSet(static_cast<uint8_t>(value));
94
95 exit:
96 AppendErrorResult(error, aOutput, aOutputMaxLen);
97 return error;
98 }
99
ProcessPower(uint8_t aArgsLength,char * aArgs[],char * aOutput,size_t aOutputMaxLen)100 Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
101 {
102 Error error = kErrorNone;
103 long value;
104
105 VerifyOrExit(aArgsLength == 1, error = kErrorInvalidArgs);
106
107 SuccessOrExit(error = ParseLong(aArgs[0], value));
108
109 otPlatDiagTxPowerSet(static_cast<int8_t>(value));
110
111 exit:
112 AppendErrorResult(error, aOutput, aOutputMaxLen);
113 return error;
114 }
115
ProcessStart(uint8_t aArgsLength,char * aArgs[],char * aOutput,size_t aOutputMaxLen)116 Error Diags::ProcessStart(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
117 {
118 OT_UNUSED_VARIABLE(aArgsLength);
119 OT_UNUSED_VARIABLE(aArgs);
120 OT_UNUSED_VARIABLE(aOutput);
121 OT_UNUSED_VARIABLE(aOutputMaxLen);
122
123 otPlatDiagModeSet(true);
124
125 return kErrorNone;
126 }
127
ProcessStop(uint8_t aArgsLength,char * aArgs[],char * aOutput,size_t aOutputMaxLen)128 Error Diags::ProcessStop(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
129 {
130 OT_UNUSED_VARIABLE(aArgsLength);
131 OT_UNUSED_VARIABLE(aArgs);
132 OT_UNUSED_VARIABLE(aOutput);
133 OT_UNUSED_VARIABLE(aOutputMaxLen);
134
135 otPlatDiagModeSet(false);
136
137 return kErrorNone;
138 }
139
otPlatDiagAlarmFired(otInstance * aInstance)140 extern "C" void otPlatDiagAlarmFired(otInstance *aInstance)
141 {
142 otPlatDiagAlarmCallback(aInstance);
143 }
144
145 #else // OPENTHREAD_RADIO
146
147 const struct Diags::Command Diags::sCommands[] = {
148 {"channel", &Diags::ProcessChannel}, {"power", &Diags::ProcessPower}, {"radio", &Diags::ProcessRadio},
149 {"repeat", &Diags::ProcessRepeat}, {"send", &Diags::ProcessSend}, {"start", &Diags::ProcessStart},
150 {"stats", &Diags::ProcessStats}, {"stop", &Diags::ProcessStop},
151 };
152
153 Diags::Diags(Instance &aInstance)
154 : InstanceLocator(aInstance)
155 , mTxPacket(&Get<Radio>().GetTransmitBuffer())
156 , mTxPeriod(0)
157 , mTxPackets(0)
158 , mChannel(20)
159 , mTxPower(0)
160 , mTxLen(0)
161 , mRepeatActive(false)
162 {
163 mStats.Clear();
164 }
165
166 Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
167 {
168 Error error = kErrorNone;
169
170 VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState);
171
172 if (aArgsLength == 0)
173 {
174 snprintf(aOutput, aOutputMaxLen, "channel: %d\r\n", mChannel);
175 }
176 else
177 {
178 long value;
179
180 SuccessOrExit(error = ParseLong(aArgs[0], value));
181 VerifyOrExit(value >= Radio::kChannelMin && value <= Radio::kChannelMax, error = kErrorInvalidArgs);
182
183 mChannel = static_cast<uint8_t>(value);
184 IgnoreError(Get<Radio>().Receive(mChannel));
185 otPlatDiagChannelSet(mChannel);
186
187 snprintf(aOutput, aOutputMaxLen, "set channel to %d\r\nstatus 0x%02x\r\n", mChannel, error);
188 }
189
190 exit:
191 AppendErrorResult(error, aOutput, aOutputMaxLen);
192 return error;
193 }
194
195 Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
196 {
197 Error error = kErrorNone;
198
199 VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState);
200
201 if (aArgsLength == 0)
202 {
203 snprintf(aOutput, aOutputMaxLen, "tx power: %d dBm\r\n", mTxPower);
204 }
205 else
206 {
207 long value;
208
209 SuccessOrExit(error = ParseLong(aArgs[0], value));
210
211 mTxPower = static_cast<int8_t>(value);
212 SuccessOrExit(error = Get<Radio>().SetTransmitPower(mTxPower));
213 otPlatDiagTxPowerSet(mTxPower);
214
215 snprintf(aOutput, aOutputMaxLen, "set tx power to %d dBm\r\nstatus 0x%02x\r\n", mTxPower, error);
216 }
217
218 exit:
219 AppendErrorResult(error, aOutput, aOutputMaxLen);
220 return error;
221 }
222
223 Error Diags::ProcessRepeat(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
224 {
225 Error error = kErrorNone;
226
227 VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState);
228 VerifyOrExit(aArgsLength > 0, error = kErrorInvalidArgs);
229
230 if (strcmp(aArgs[0], "stop") == 0)
231 {
232 otPlatAlarmMilliStop(&GetInstance());
233 mRepeatActive = false;
234 snprintf(aOutput, aOutputMaxLen, "repeated packet transmission is stopped\r\nstatus 0x%02x\r\n", error);
235 }
236 else
237 {
238 long value;
239
240 VerifyOrExit(aArgsLength == 2, error = kErrorInvalidArgs);
241
242 SuccessOrExit(error = ParseLong(aArgs[0], value));
243 mTxPeriod = static_cast<uint32_t>(value);
244
245 SuccessOrExit(error = ParseLong(aArgs[1], value));
246 VerifyOrExit(value <= OT_RADIO_FRAME_MAX_SIZE, error = kErrorInvalidArgs);
247 VerifyOrExit(value >= OT_RADIO_FRAME_MIN_SIZE, error = kErrorInvalidArgs);
248 mTxLen = static_cast<uint8_t>(value);
249
250 mRepeatActive = true;
251 uint32_t now = otPlatAlarmMilliGetNow();
252 otPlatAlarmMilliStartAt(&GetInstance(), now, mTxPeriod);
253 snprintf(aOutput, aOutputMaxLen, "sending packets of length %#x at the delay of %#x ms\r\nstatus 0x%02x\r\n",
254 static_cast<int>(mTxLen), static_cast<int>(mTxPeriod), error);
255 }
256
257 exit:
258 AppendErrorResult(error, aOutput, aOutputMaxLen);
259 return error;
260 }
261
262 Error Diags::ProcessSend(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
263 {
264 Error error = kErrorNone;
265 long value;
266
267 VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState);
268 VerifyOrExit(aArgsLength == 2, error = kErrorInvalidArgs);
269
270 SuccessOrExit(error = ParseLong(aArgs[0], value));
271 mTxPackets = static_cast<uint32_t>(value);
272
273 SuccessOrExit(error = ParseLong(aArgs[1], value));
274 VerifyOrExit(value <= OT_RADIO_FRAME_MAX_SIZE, error = kErrorInvalidArgs);
275 VerifyOrExit(value >= OT_RADIO_FRAME_MIN_SIZE, error = kErrorInvalidArgs);
276 mTxLen = static_cast<uint8_t>(value);
277
278 snprintf(aOutput, aOutputMaxLen, "sending %#x packet(s), length %#x\r\nstatus 0x%02x\r\n",
279 static_cast<int>(mTxPackets), static_cast<int>(mTxLen), error);
280 TransmitPacket();
281
282 exit:
283 AppendErrorResult(error, aOutput, aOutputMaxLen);
284 return error;
285 }
286
287 Error Diags::ProcessStart(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
288 {
289 OT_UNUSED_VARIABLE(aArgsLength);
290 OT_UNUSED_VARIABLE(aArgs);
291
292 Error error = kErrorNone;
293
294 VerifyOrExit(!Get<ThreadNetif>().IsUp(), error = kErrorInvalidState);
295
296 otPlatDiagChannelSet(mChannel);
297 otPlatDiagTxPowerSet(mTxPower);
298
299 IgnoreError(Get<Radio>().Enable());
300 Get<Radio>().SetPromiscuous(true);
301 otPlatAlarmMilliStop(&GetInstance());
302 SuccessOrExit(error = Get<Radio>().Receive(mChannel));
303 SuccessOrExit(error = Get<Radio>().SetTransmitPower(mTxPower));
304 otPlatDiagModeSet(true);
305 mStats.Clear();
306 snprintf(aOutput, aOutputMaxLen, "start diagnostics mode\r\nstatus 0x%02x\r\n", error);
307
308 exit:
309 AppendErrorResult(error, aOutput, aOutputMaxLen);
310 return error;
311 }
312
313 Error Diags::ProcessStats(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
314 {
315 Error error = kErrorNone;
316
317 VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState);
318
319 if ((aArgsLength == 1) && (strcmp(aArgs[0], "clear") == 0))
320 {
321 mStats.Clear();
322 snprintf(aOutput, aOutputMaxLen, "stats cleared\r\n");
323 }
324 else
325 {
326 VerifyOrExit(aArgsLength == 0, error = kErrorInvalidArgs);
327 snprintf(aOutput, aOutputMaxLen,
328 "received packets: %d\r\nsent packets: %d\r\n"
329 "first received packet: rssi=%d, lqi=%d\r\n"
330 "last received packet: rssi=%d, lqi=%d\r\n",
331 static_cast<int>(mStats.mReceivedPackets), static_cast<int>(mStats.mSentPackets),
332 static_cast<int>(mStats.mFirstRssi), static_cast<int>(mStats.mFirstLqi),
333 static_cast<int>(mStats.mLastRssi), static_cast<int>(mStats.mLastLqi));
334 }
335
336 exit:
337 AppendErrorResult(error, aOutput, aOutputMaxLen);
338 return error;
339 }
340
341 Error Diags::ProcessStop(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
342 {
343 OT_UNUSED_VARIABLE(aArgsLength);
344 OT_UNUSED_VARIABLE(aArgs);
345
346 Error error = kErrorNone;
347
348 VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState);
349
350 otPlatAlarmMilliStop(&GetInstance());
351 otPlatDiagModeSet(false);
352 Get<Radio>().SetPromiscuous(false);
353
354 snprintf(aOutput, aOutputMaxLen,
355 "received packets: %d\r\nsent packets: %d\r\n"
356 "first received packet: rssi=%d, lqi=%d\r\n"
357 "last received packet: rssi=%d, lqi=%d\r\n"
358 "\nstop diagnostics mode\r\nstatus 0x%02x\r\n",
359 static_cast<int>(mStats.mReceivedPackets), static_cast<int>(mStats.mSentPackets),
360 static_cast<int>(mStats.mFirstRssi), static_cast<int>(mStats.mFirstLqi),
361 static_cast<int>(mStats.mLastRssi), static_cast<int>(mStats.mLastLqi), error);
362
363 exit:
364 AppendErrorResult(error, aOutput, aOutputMaxLen);
365 return error;
366 }
367
368 void Diags::TransmitPacket(void)
369 {
370 mTxPacket->mLength = mTxLen;
371 mTxPacket->mChannel = mChannel;
372
373 for (uint8_t i = 0; i < mTxLen; i++)
374 {
375 mTxPacket->mPsdu[i] = i;
376 }
377
378 IgnoreError(Get<Radio>().Transmit(*static_cast<Mac::TxFrame *>(mTxPacket)));
379 }
380
381 Error Diags::ProcessRadio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
382 {
383 Error error = kErrorInvalidArgs;
384
385 VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState);
386 VerifyOrExit(aArgsLength > 0, error = kErrorInvalidArgs);
387
388 if (strcmp(aArgs[0], "sleep") == 0)
389 {
390 SuccessOrExit(error = Get<Radio>().Sleep());
391 snprintf(aOutput, aOutputMaxLen, "set radio from receive to sleep \r\nstatus 0x%02x\r\n", error);
392 }
393 else if (strcmp(aArgs[0], "receive") == 0)
394 {
395 SuccessOrExit(error = Get<Radio>().Receive(mChannel));
396 SuccessOrExit(error = Get<Radio>().SetTransmitPower(mTxPower));
397 otPlatDiagChannelSet(mChannel);
398 otPlatDiagTxPowerSet(mTxPower);
399
400 snprintf(aOutput, aOutputMaxLen, "set radio from sleep to receive on channel %d\r\nstatus 0x%02x\r\n", mChannel,
401 error);
402 }
403 else if (strcmp(aArgs[0], "state") == 0)
404 {
405 otRadioState state = Get<Radio>().GetState();
406
407 error = kErrorNone;
408
409 switch (state)
410 {
411 case OT_RADIO_STATE_DISABLED:
412 snprintf(aOutput, aOutputMaxLen, "disabled\r\n");
413 break;
414
415 case OT_RADIO_STATE_SLEEP:
416 snprintf(aOutput, aOutputMaxLen, "sleep\r\n");
417 break;
418
419 case OT_RADIO_STATE_RECEIVE:
420 snprintf(aOutput, aOutputMaxLen, "receive\r\n");
421 break;
422
423 case OT_RADIO_STATE_TRANSMIT:
424 snprintf(aOutput, aOutputMaxLen, "transmit\r\n");
425 break;
426
427 default:
428 snprintf(aOutput, aOutputMaxLen, "invalid\r\n");
429 break;
430 }
431 }
432
433 exit:
434 AppendErrorResult(error, aOutput, aOutputMaxLen);
435 return error;
436 }
437
438 extern "C" void otPlatDiagAlarmFired(otInstance *aInstance)
439 {
440 Instance *instance = static_cast<Instance *>(aInstance);
441
442 instance->Get<Diags>().AlarmFired();
443 }
444
445 void Diags::AlarmFired(void)
446 {
447 if (mRepeatActive)
448 {
449 uint32_t now = otPlatAlarmMilliGetNow();
450
451 TransmitPacket();
452 otPlatAlarmMilliStartAt(&GetInstance(), now, mTxPeriod);
453 }
454 else
455 {
456 otPlatDiagAlarmCallback(&GetInstance());
457 }
458 }
459
460 void Diags::ReceiveDone(otRadioFrame *aFrame, Error aError)
461 {
462 if (aError == kErrorNone)
463 {
464 // for sensitivity test, only record the rssi and lqi for the first and last packet
465 if (mStats.mReceivedPackets == 0)
466 {
467 mStats.mFirstRssi = aFrame->mInfo.mRxInfo.mRssi;
468 mStats.mFirstLqi = aFrame->mInfo.mRxInfo.mLqi;
469 }
470
471 mStats.mLastRssi = aFrame->mInfo.mRxInfo.mRssi;
472 mStats.mLastLqi = aFrame->mInfo.mRxInfo.mLqi;
473
474 mStats.mReceivedPackets++;
475 }
476
477 otPlatDiagRadioReceived(&GetInstance(), aFrame, aError);
478 }
479
480 void Diags::TransmitDone(Error aError)
481 {
482 if (aError == kErrorNone)
483 {
484 mStats.mSentPackets++;
485
486 if (mTxPackets > 1)
487 {
488 mTxPackets--;
489 }
490 else
491 {
492 ExitNow();
493 }
494 }
495
496 VerifyOrExit(!mRepeatActive);
497 TransmitPacket();
498
499 exit:
500 return;
501 }
502
503 #endif // OPENTHREAD_RADIO
504
AppendErrorResult(Error aError,char * aOutput,size_t aOutputMaxLen)505 void Diags::AppendErrorResult(Error aError, char *aOutput, size_t aOutputMaxLen)
506 {
507 if (aError != kErrorNone)
508 {
509 snprintf(aOutput, aOutputMaxLen, "failed\r\nstatus %#x\r\n", aError);
510 }
511 }
512
ParseLong(char * aString,long & aLong)513 Error Diags::ParseLong(char *aString, long &aLong)
514 {
515 char *endptr;
516 aLong = strtol(aString, &endptr, 0);
517 return (*endptr == '\0') ? kErrorNone : kErrorParse;
518 }
519
ParseCmd(char * aString,uint8_t & aArgsLength,char * aArgs[])520 Error Diags::ParseCmd(char *aString, uint8_t &aArgsLength, char *aArgs[])
521 {
522 Error error;
523 Utils::CmdLineParser::Arg args[kMaxArgs + 1];
524
525 SuccessOrExit(error = Utils::CmdLineParser::ParseCmd(aString, args));
526 aArgsLength = Utils::CmdLineParser::Arg::GetArgsLength(args);
527 Utils::CmdLineParser::Arg::CopyArgsToStringArray(args, aArgs);
528
529 exit:
530 return error;
531 }
532
ProcessLine(const char * aString,char * aOutput,size_t aOutputMaxLen)533 void Diags::ProcessLine(const char *aString, char *aOutput, size_t aOutputMaxLen)
534 {
535 constexpr uint16_t kMaxCommandBuffer = OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE;
536
537 Error error = kErrorNone;
538 char buffer[kMaxCommandBuffer];
539 char * args[kMaxArgs];
540 uint8_t argCount = 0;
541
542 VerifyOrExit(StringLength(aString, kMaxCommandBuffer) < kMaxCommandBuffer, error = kErrorNoBufs);
543
544 strcpy(buffer, aString);
545 error = ParseCmd(buffer, argCount, args);
546
547 exit:
548
549 switch (error)
550 {
551 case kErrorNone:
552 aOutput[0] = '\0'; // In case there is no output.
553 IgnoreError(ProcessCmd(argCount, &args[0], aOutput, aOutputMaxLen));
554 break;
555
556 case kErrorNoBufs:
557 snprintf(aOutput, aOutputMaxLen, "failed: command string too long\r\n");
558 break;
559
560 case kErrorInvalidArgs:
561 snprintf(aOutput, aOutputMaxLen, "failed: command string contains too many arguments\r\n");
562 break;
563
564 default:
565 snprintf(aOutput, aOutputMaxLen, "failed to parse command string\r\n");
566 break;
567 }
568 }
569
ProcessCmd(uint8_t aArgsLength,char * aArgs[],char * aOutput,size_t aOutputMaxLen)570 Error Diags::ProcessCmd(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
571 {
572 Error error = kErrorNone;
573
574 // This `rcp` command is for debugging and testing only, building only when NDEBUG is not defined
575 // so that it will be excluded from release build.
576 #if !defined(NDEBUG) && defined(OPENTHREAD_RADIO)
577 if (aArgsLength > 0 && !strcmp(aArgs[0], "rcp"))
578 {
579 aArgs++;
580 aArgsLength--;
581 }
582 #endif
583
584 if (aArgsLength == 0)
585 {
586 snprintf(aOutput, aOutputMaxLen, "diagnostics mode is %s\r\n", otPlatDiagModeGet() ? "enabled" : "disabled");
587 ExitNow();
588 }
589 else
590 {
591 aOutput[0] = '\0';
592 }
593
594 for (const Command &command : sCommands)
595 {
596 if (strcmp(aArgs[0], command.mName) == 0)
597 {
598 error = (this->*command.mCommand)(aArgsLength - 1, (aArgsLength > 1) ? &aArgs[1] : nullptr, aOutput,
599 aOutputMaxLen);
600 ExitNow();
601 }
602 }
603
604 // more platform specific features will be processed under platform layer
605 error = otPlatDiagProcess(&GetInstance(), aArgsLength, aArgs, aOutput, aOutputMaxLen);
606
607 exit:
608 // Add more platform specific diagnostics features here.
609 if (error == kErrorInvalidCommand && aArgsLength > 1)
610 {
611 snprintf(aOutput, aOutputMaxLen, "diag feature '%s' is not supported\r\n", aArgs[0]);
612 }
613
614 return error;
615 }
616
IsEnabled(void)617 bool Diags::IsEnabled(void)
618 {
619 return otPlatDiagModeGet();
620 }
621
622 } // namespace FactoryDiags
623 } // namespace ot
624
625 #endif // OPENTHREAD_CONFIG_DIAG_ENABLE
626