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