• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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