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