• 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 #include <openthread/platform/radio.h>
44 
45 #include "instance/instance.hpp"
46 #include "utils/parse_cmdline.hpp"
47 
48 OT_TOOL_WEAK
otPlatDiagProcess(otInstance * aInstance,uint8_t aArgsLength,char * aArgs[])49 otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[])
50 {
51     OT_UNUSED_VARIABLE(aArgsLength);
52     OT_UNUSED_VARIABLE(aArgs);
53     OT_UNUSED_VARIABLE(aInstance);
54 
55     return ot::kErrorInvalidCommand;
56 }
57 
58 namespace ot {
59 namespace FactoryDiags {
60 
61 #if OPENTHREAD_RADIO && !OPENTHREAD_RADIO_CLI
62 
63 const struct Diags::Command Diags::sCommands[] = {
64     {"channel", &Diags::ProcessChannel},
65     {"cw", &Diags::ProcessContinuousWave},
66     {"echo", &Diags::ProcessEcho},
67     {"gpio", &Diags::ProcessGpio},
68     {"power", &Diags::ProcessPower},
69     {"powersettings", &Diags::ProcessPowerSettings},
70     {"rawpowersetting", &Diags::ProcessRawPowerSetting},
71     {"start", &Diags::ProcessStart},
72     {"stop", &Diags::ProcessStop},
73     {"stream", &Diags::ProcessStream},
74 };
75 
Diags(Instance & aInstance)76 Diags::Diags(Instance &aInstance)
77     : InstanceLocator(aInstance)
78     , mOutputCallback(nullptr)
79     , mOutputContext(nullptr)
80 {
81 }
82 
ProcessChannel(uint8_t aArgsLength,char * aArgs[])83 Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[])
84 {
85     Error   error = kErrorNone;
86     uint8_t channel;
87 
88     VerifyOrExit(aArgsLength == 1, error = kErrorInvalidArgs);
89 
90     SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(aArgs[0], channel));
91     VerifyOrExit(IsChannelValid(channel), error = kErrorInvalidArgs);
92 
93     otPlatDiagChannelSet(channel);
94 
95 exit:
96     return error;
97 }
98 
ProcessPower(uint8_t aArgsLength,char * aArgs[])99 Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[])
100 {
101     Error  error = kErrorNone;
102     int8_t power;
103 
104     VerifyOrExit(aArgsLength == 1, error = kErrorInvalidArgs);
105 
106     SuccessOrExit(error = Utils::CmdLineParser::ParseAsInt8(aArgs[0], power));
107 
108     otPlatDiagTxPowerSet(power);
109 
110 exit:
111     return error;
112 }
113 
ProcessEcho(uint8_t aArgsLength,char * aArgs[])114 Error Diags::ProcessEcho(uint8_t aArgsLength, char *aArgs[])
115 {
116     Error error = kErrorNone;
117 
118     if (aArgsLength == 1)
119     {
120         Output("%s\r\n", aArgs[0]);
121     }
122     else if ((aArgsLength == 2) && StringMatch(aArgs[0], "-n"))
123     {
124         static constexpr uint8_t  kReservedLen  = 1; // 1 byte '\0'
125         static constexpr uint16_t kOutputLen    = OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE;
126         static constexpr uint16_t kOutputMaxLen = kOutputLen - kReservedLen;
127         char                      output[kOutputLen];
128         uint32_t                  i;
129         uint32_t                  number;
130 
131         SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint32(aArgs[1], number));
132         number = Min(number, static_cast<uint32_t>(kOutputMaxLen));
133 
134         for (i = 0; i < number; i++)
135         {
136             output[i] = '0' + i % 10;
137         }
138 
139         output[number] = '\0';
140 
141         Output("%s\r\n", output);
142     }
143     else
144     {
145         error = kErrorInvalidArgs;
146     }
147 
148 exit:
149     return error;
150 }
151 
ProcessStart(uint8_t aArgsLength,char * aArgs[])152 Error Diags::ProcessStart(uint8_t aArgsLength, char *aArgs[])
153 {
154     OT_UNUSED_VARIABLE(aArgsLength);
155     OT_UNUSED_VARIABLE(aArgs);
156 
157     otPlatDiagModeSet(true);
158 
159     return kErrorNone;
160 }
161 
ProcessStop(uint8_t aArgsLength,char * aArgs[])162 Error Diags::ProcessStop(uint8_t aArgsLength, char *aArgs[])
163 {
164     OT_UNUSED_VARIABLE(aArgsLength);
165     OT_UNUSED_VARIABLE(aArgs);
166 
167     otPlatDiagModeSet(false);
168 
169     return kErrorNone;
170 }
171 
otPlatDiagAlarmFired(otInstance * aInstance)172 extern "C" void otPlatDiagAlarmFired(otInstance *aInstance) { otPlatDiagAlarmCallback(aInstance); }
173 
174 #else // OPENTHREAD_RADIO && !OPENTHREAD_RADIO_CLI
175 // For OPENTHREAD_FTD, OPENTHREAD_MTD, OPENTHREAD_RADIO_CLI
176 const struct Diags::Command Diags::sCommands[] = {
177     {"channel", &Diags::ProcessChannel},
178     {"cw", &Diags::ProcessContinuousWave},
179     {"frame", &Diags::ProcessFrame},
180     {"gpio", &Diags::ProcessGpio},
181     {"power", &Diags::ProcessPower},
182     {"powersettings", &Diags::ProcessPowerSettings},
183     {"rawpowersetting", &Diags::ProcessRawPowerSetting},
184     {"radio", &Diags::ProcessRadio},
185     {"repeat", &Diags::ProcessRepeat},
186     {"send", &Diags::ProcessSend},
187     {"start", &Diags::ProcessStart},
188     {"stats", &Diags::ProcessStats},
189     {"stop", &Diags::ProcessStop},
190     {"stream", &Diags::ProcessStream},
191 };
192 
193 Diags::Diags(Instance &aInstance)
194     : InstanceLocator(aInstance)
195     , mTxPacket(&Get<Radio>().GetTransmitBuffer())
196     , mTxPeriod(0)
197     , mTxPackets(0)
198     , mChannel(20)
199     , mTxPower(0)
200     , mTxLen(0)
201     , mCurTxCmd(kTxCmdNone)
202     , mIsTxPacketSet(false)
203     , mIsAsyncSend(false)
204     , mDiagSendOn(false)
205     , mOutputCallback(nullptr)
206     , mOutputContext(nullptr)
207 {
208     mStats.Clear();
209 }
210 
211 void Diags::ResetTxPacket(void)
212 {
213     mIsHeaderUpdated                               = false;
214     mIsSecurityProcessed                           = false;
215     mTxPacket->mInfo.mTxInfo.mTxDelayBaseTime      = 0;
216     mTxPacket->mInfo.mTxInfo.mTxDelay              = 0;
217     mTxPacket->mInfo.mTxInfo.mMaxCsmaBackoffs      = 0;
218     mTxPacket->mInfo.mTxInfo.mMaxFrameRetries      = 0;
219     mTxPacket->mInfo.mTxInfo.mRxChannelAfterTxDone = mChannel;
220     mTxPacket->mInfo.mTxInfo.mTxPower              = OT_RADIO_POWER_INVALID;
221     mTxPacket->mInfo.mTxInfo.mIsHeaderUpdated      = false;
222     mTxPacket->mInfo.mTxInfo.mIsARetx              = false;
223     mTxPacket->mInfo.mTxInfo.mCsmaCaEnabled        = false;
224     mTxPacket->mInfo.mTxInfo.mCslPresent           = false;
225 }
226 
227 Error Diags::ProcessFrame(uint8_t aArgsLength, char *aArgs[])
228 {
229     Error    error                = kErrorNone;
230     uint16_t size                 = OT_RADIO_FRAME_MAX_SIZE;
231     bool     securityProcessed    = false;
232     bool     csmaCaEnabled        = false;
233     bool     isHeaderUpdated      = false;
234     int8_t   txPower              = OT_RADIO_POWER_INVALID;
235     uint8_t  maxFrameRetries      = 0;
236     uint8_t  maxCsmaBackoffs      = 0;
237     uint8_t  rxChannelAfterTxDone = mChannel;
238     uint32_t txDelayBaseTime      = 0;
239     uint32_t txDelay              = 0;
240 
241     while (aArgsLength > 1)
242     {
243         if (StringMatch(aArgs[0], "-b"))
244         {
245             aArgs++;
246             aArgsLength--;
247 
248             VerifyOrExit(aArgsLength > 1, error = kErrorInvalidArgs);
249             SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(aArgs[0], maxCsmaBackoffs));
250         }
251         else if (StringMatch(aArgs[0], "-c"))
252         {
253             csmaCaEnabled = true;
254         }
255         else if (StringMatch(aArgs[0], "-C"))
256         {
257             aArgs++;
258             aArgsLength--;
259 
260             VerifyOrExit(aArgsLength > 1, error = kErrorInvalidArgs);
261             SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(aArgs[0], rxChannelAfterTxDone));
262             VerifyOrExit(IsChannelValid(rxChannelAfterTxDone), error = kErrorInvalidArgs);
263         }
264         else if (StringMatch(aArgs[0], "-d"))
265         {
266             aArgs++;
267             aArgsLength--;
268 
269             VerifyOrExit(aArgsLength > 1, error = kErrorInvalidArgs);
270             SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint32(aArgs[0], txDelay));
271             txDelayBaseTime = static_cast<uint32_t>(otPlatRadioGetNow(&GetInstance()));
272         }
273         else if (StringMatch(aArgs[0], "-p"))
274         {
275             aArgs++;
276             aArgsLength--;
277 
278             VerifyOrExit(aArgsLength > 1, error = kErrorInvalidArgs);
279             SuccessOrExit(error = Utils::CmdLineParser::ParseAsInt8(aArgs[0], txPower));
280         }
281         else if (StringMatch(aArgs[0], "-r"))
282         {
283             aArgs++;
284             aArgsLength--;
285 
286             VerifyOrExit(aArgsLength > 1, error = kErrorInvalidArgs);
287             SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(aArgs[0], maxFrameRetries));
288         }
289         else if (StringMatch(aArgs[0], "-s"))
290         {
291             securityProcessed = true;
292             isHeaderUpdated   = true;
293         }
294         else if (StringMatch(aArgs[0], "-u"))
295         {
296             isHeaderUpdated = true;
297         }
298         else
299         {
300             ExitNow(error = kErrorInvalidArgs);
301         }
302 
303         aArgs++;
304         aArgsLength--;
305     }
306 
307     VerifyOrExit(aArgsLength == 1, error = kErrorInvalidArgs);
308 
309     SuccessOrExit(error = Utils::CmdLineParser::ParseAsHexString(aArgs[0], size, mTxPacket->mPsdu));
310     VerifyOrExit(size <= OT_RADIO_FRAME_MAX_SIZE, error = kErrorInvalidArgs);
311     VerifyOrExit(size >= OT_RADIO_FRAME_MIN_SIZE, error = kErrorInvalidArgs);
312 
313     ResetTxPacket();
314     mTxPacket->mInfo.mTxInfo.mCsmaCaEnabled        = csmaCaEnabled;
315     mTxPacket->mInfo.mTxInfo.mTxPower              = txPower;
316     mTxPacket->mInfo.mTxInfo.mTxDelayBaseTime      = txDelayBaseTime;
317     mTxPacket->mInfo.mTxInfo.mTxDelay              = txDelay;
318     mTxPacket->mInfo.mTxInfo.mMaxFrameRetries      = maxFrameRetries;
319     mTxPacket->mInfo.mTxInfo.mMaxCsmaBackoffs      = maxCsmaBackoffs;
320     mTxPacket->mInfo.mTxInfo.mRxChannelAfterTxDone = rxChannelAfterTxDone;
321     mTxPacket->mLength                             = size;
322     mIsHeaderUpdated                               = isHeaderUpdated;
323     mIsSecurityProcessed                           = securityProcessed;
324     mIsTxPacketSet                                 = true;
325 
326 exit:
327     return error;
328 }
329 
330 Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[])
331 {
332     Error error = kErrorNone;
333 
334     if (aArgsLength == 0)
335     {
336         Output("%u\r\n", mChannel);
337     }
338     else
339     {
340         uint8_t channel;
341 
342         SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(aArgs[0], channel));
343         VerifyOrExit(IsChannelValid(channel), error = kErrorInvalidArgs);
344 
345         mChannel = channel;
346         otPlatDiagChannelSet(mChannel);
347 
348         if (!mIsSleepOn)
349         {
350             IgnoreError(Get<Radio>().Receive(mChannel));
351         }
352     }
353 
354 exit:
355     return error;
356 }
357 
358 Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[])
359 {
360     Error error = kErrorNone;
361 
362     if (aArgsLength == 0)
363     {
364         Output("%d\r\n", mTxPower);
365     }
366     else
367     {
368         int8_t txPower;
369 
370         SuccessOrExit(error = Utils::CmdLineParser::ParseAsInt8(aArgs[0], txPower));
371 
372         mTxPower = txPower;
373         SuccessOrExit(error = Get<Radio>().SetTransmitPower(mTxPower));
374         otPlatDiagTxPowerSet(mTxPower);
375     }
376 
377 exit:
378     return error;
379 }
380 
381 Error Diags::ProcessRepeat(uint8_t aArgsLength, char *aArgs[])
382 {
383     Error error = kErrorNone;
384 
385     VerifyOrExit(aArgsLength > 0, error = kErrorInvalidArgs);
386 
387     if (StringMatch(aArgs[0], "stop"))
388     {
389         otPlatAlarmMilliStop(&GetInstance());
390         mCurTxCmd = kTxCmdNone;
391     }
392     else
393     {
394         uint32_t txPeriod;
395         uint8_t  txLength;
396 
397         VerifyOrExit(aArgsLength >= 1, error = kErrorInvalidArgs);
398         VerifyOrExit(mCurTxCmd == kTxCmdNone, error = kErrorInvalidState);
399 
400         SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint32(aArgs[0], txPeriod));
401         mTxPeriod = txPeriod;
402 
403         if (aArgsLength >= 2)
404         {
405             SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(aArgs[1], txLength));
406             mIsTxPacketSet = false;
407         }
408         else if (mIsTxPacketSet)
409         {
410             txLength = mTxPacket->mLength;
411         }
412         else
413         {
414             ExitNow(error = kErrorInvalidArgs);
415         }
416 
417         VerifyOrExit((txLength >= OT_RADIO_FRAME_MIN_SIZE) && (txLength <= OT_RADIO_FRAME_MAX_SIZE),
418                      error = kErrorInvalidArgs);
419 
420         mTxLen    = txLength;
421         mCurTxCmd = kTxCmdRepeat;
422         otPlatAlarmMilliStartAt(&GetInstance(), otPlatAlarmMilliGetNow(), mTxPeriod);
423     }
424 
425 exit:
426     return error;
427 }
428 
429 Error Diags::ProcessSend(uint8_t aArgsLength, char *aArgs[])
430 {
431     Error    error = kErrorNone;
432     uint32_t txPackets;
433     uint8_t  txLength;
434 
435     VerifyOrExit(aArgsLength >= 1, error = kErrorInvalidArgs);
436     VerifyOrExit(mCurTxCmd == kTxCmdNone, error = kErrorInvalidState);
437 
438     if (StringMatch(aArgs[0], "async"))
439     {
440         aArgs++;
441         aArgsLength--;
442         VerifyOrExit(aArgsLength >= 1, error = kErrorInvalidArgs);
443         mIsAsyncSend = true;
444     }
445     else
446     {
447         mIsAsyncSend = false;
448     }
449 
450     SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint32(aArgs[0], txPackets));
451     mTxPackets = txPackets;
452 
453     if (aArgsLength >= 2)
454     {
455         SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(aArgs[1], txLength));
456         mIsTxPacketSet = false;
457     }
458     else if (mIsTxPacketSet)
459     {
460         txLength = mTxPacket->mLength;
461     }
462     else
463     {
464         ExitNow(error = kErrorInvalidArgs);
465     }
466 
467     VerifyOrExit(txLength <= OT_RADIO_FRAME_MAX_SIZE, error = kErrorInvalidArgs);
468     VerifyOrExit(txLength >= OT_RADIO_FRAME_MIN_SIZE, error = kErrorInvalidArgs);
469     mTxLen = txLength;
470 
471     SuccessOrExit(error = TransmitPacket());
472     mCurTxCmd = kTxCmdSend;
473 
474     if (!mIsAsyncSend)
475     {
476         error = kErrorPending;
477     }
478 
479 exit:
480     return error;
481 }
482 
483 Error Diags::ProcessStart(uint8_t aArgsLength, char *aArgs[])
484 {
485     OT_UNUSED_VARIABLE(aArgsLength);
486     OT_UNUSED_VARIABLE(aArgs);
487 
488     Error error = kErrorNone;
489 
490 #if OPENTHREAD_FTD || OPENTHREAD_MTD
491     VerifyOrExit(!Get<ThreadNetif>().IsUp(), error = kErrorInvalidState);
492 #endif
493 
494     otPlatDiagChannelSet(mChannel);
495     otPlatDiagTxPowerSet(mTxPower);
496 
497     IgnoreError(Get<Radio>().Enable());
498     Get<Radio>().SetPromiscuous(true);
499     Get<Mac::SubMac>().SetRxOnWhenIdle(true);
500     otPlatAlarmMilliStop(&GetInstance());
501     SuccessOrExit(error = Get<Radio>().Receive(mChannel));
502     SuccessOrExit(error = Get<Radio>().SetTransmitPower(mTxPower));
503     otPlatDiagModeSet(true);
504     mStats.Clear();
505 
506 exit:
507     return error;
508 }
509 
510 void Diags::OutputStats(void)
511 {
512     Output("received packets: %lu\r\n"
513            "sent success packets: %lu\r\n"
514            "sent error cca packets: %lu\r\n"
515            "sent error abort packets: %lu\r\n"
516            "sent error invalid state packets: %lu\r\n"
517            "sent error others packets: %lu\r\n"
518            "first received packet: rssi=%d, lqi=%u\r\n"
519            "last received packet: rssi=%d, lqi=%u\r\n",
520            ToUlong(mStats.mReceivedPackets), ToUlong(mStats.mSentSuccessPackets), ToUlong(mStats.mSentErrorCcaPackets),
521            ToUlong(mStats.mSentErrorAbortPackets), ToUlong(mStats.mSentErrorInvalidStatePackets),
522            ToUlong(mStats.mSentErrorOthersPackets), mStats.mFirstRssi, mStats.mFirstLqi, mStats.mLastRssi,
523            mStats.mLastLqi);
524 }
525 
526 Error Diags::ProcessStats(uint8_t aArgsLength, char *aArgs[])
527 {
528     Error error = kErrorNone;
529 
530     if ((aArgsLength == 1) && StringMatch(aArgs[0], "clear"))
531     {
532         mStats.Clear();
533     }
534     else
535     {
536         VerifyOrExit(aArgsLength == 0, error = kErrorInvalidArgs);
537         OutputStats();
538     }
539 
540 exit:
541     return error;
542 }
543 
544 Error Diags::ProcessStop(uint8_t aArgsLength, char *aArgs[])
545 {
546     OT_UNUSED_VARIABLE(aArgsLength);
547     OT_UNUSED_VARIABLE(aArgs);
548 
549     otPlatAlarmMilliStop(&GetInstance());
550     otPlatDiagModeSet(false);
551     Get<Radio>().SetPromiscuous(false);
552     Get<Mac::SubMac>().SetRxOnWhenIdle(false);
553 
554     return kErrorNone;
555 }
556 
557 Error Diags::TransmitPacket(void)
558 {
559     Error error         = kErrorNone;
560     mTxPacket->mChannel = mChannel;
561 
562     if (mIsTxPacketSet)
563     {
564         // The `mInfo.mTxInfo.mIsHeaderUpdated` and `mInfo.mTxInfo.mIsSecurityProcessed` fields may be updated by
565         // the radio driver after the frame is sent. Here sets these fields field before transmitting the frame.
566         mTxPacket->mInfo.mTxInfo.mIsHeaderUpdated     = mIsHeaderUpdated;
567         mTxPacket->mInfo.mTxInfo.mIsSecurityProcessed = mIsSecurityProcessed;
568     }
569     else
570     {
571         ResetTxPacket();
572         mTxPacket->mLength = mTxLen;
573 
574         for (uint8_t i = 0; i < mTxLen; i++)
575         {
576             mTxPacket->mPsdu[i] = i;
577         }
578     }
579 
580     error = Get<Radio>().Transmit(*static_cast<Mac::TxFrame *>(mTxPacket));
581     if (error == kErrorNone)
582     {
583         mDiagSendOn = true;
584     }
585     else
586     {
587         UpdateTxStats(error);
588     }
589 
590     return error;
591 }
592 
593 Error Diags::ParseReceiveConfigFormat(const char *aFormat, ReceiveConfig &aConfig)
594 {
595     Error error = kErrorNone;
596 
597     VerifyOrExit(aFormat != nullptr, error = kErrorInvalidArgs);
598 
599     for (const char *arg = aFormat; *arg != '\0'; arg++)
600     {
601         switch (*arg)
602         {
603         case 'r':
604             aConfig.mShowRssi = true;
605             break;
606 
607         case 'l':
608             aConfig.mShowLqi = true;
609             break;
610 
611         case 'p':
612             aConfig.mShowPsdu = true;
613             break;
614 
615         default:
616             ExitNow(error = OT_ERROR_INVALID_ARGS);
617         }
618     }
619 
620 exit:
621     return error;
622 }
623 
624 Error Diags::RadioReceive(void)
625 {
626     Error error;
627 
628     SuccessOrExit(error = Get<Radio>().Receive(mChannel));
629     SuccessOrExit(error = Get<Radio>().SetTransmitPower(mTxPower));
630     otPlatDiagChannelSet(mChannel);
631     otPlatDiagTxPowerSet(mTxPower);
632     mIsSleepOn = false;
633 
634 exit:
635     return error;
636 }
637 
638 Error Diags::ProcessRadio(uint8_t aArgsLength, char *aArgs[])
639 {
640     Error error = kErrorInvalidArgs;
641 
642     VerifyOrExit(aArgsLength > 0, error = kErrorInvalidArgs);
643 
644     if (StringMatch(aArgs[0], "sleep"))
645     {
646         SuccessOrExit(error = Get<Radio>().Sleep());
647         mIsSleepOn = true;
648     }
649     else if (StringMatch(aArgs[0], "receive"))
650     {
651         ReceiveConfig receiveConfig;
652 
653         aArgs++;
654         aArgsLength--;
655 
656         if (aArgsLength == 0)
657         {
658             SuccessOrExit(error = RadioReceive());
659             ExitNow();
660         }
661 
662         if (StringMatch(aArgs[0], "filter"))
663         {
664             aArgs++;
665             aArgsLength--;
666 
667             VerifyOrExit(aArgsLength > 0);
668 
669             if (StringMatch(aArgs[0], "enable"))
670             {
671                 mReceiveConfig.mIsFilterEnabled = true;
672                 error                           = kErrorNone;
673             }
674             else if (StringMatch(aArgs[0], "disable"))
675             {
676                 mReceiveConfig.mIsFilterEnabled = false;
677                 error                           = kErrorNone;
678             }
679             else
680             {
681                 Mac::Address dstAddress;
682 
683                 if (StringMatch(aArgs[0], "-"))
684                 {
685                     dstAddress.SetNone();
686                     error = kErrorNone;
687                 }
688                 else if (strlen(aArgs[0]) == 2 * sizeof(Mac::ExtAddress))
689                 {
690                     Mac::ExtAddress extAddress;
691 
692                     SuccessOrExit(error = Utils::CmdLineParser::ParseAsHexString(aArgs[0], extAddress.m8));
693                     mReceiveConfig.mFilterAddress.SetExtended(extAddress);
694                 }
695                 else
696                 {
697                     Mac::ShortAddress shortAddress;
698 
699                     SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint16(aArgs[0], shortAddress));
700                     mReceiveConfig.mFilterAddress.SetShort(shortAddress);
701                 }
702             }
703 
704             ExitNow();
705         }
706 
707         if (StringMatch(aArgs[0], "async"))
708         {
709             aArgs++;
710             aArgsLength--;
711             receiveConfig.mIsAsyncCommand = true;
712         }
713 
714         VerifyOrExit(aArgsLength > 0);
715         SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint16(aArgs[0], receiveConfig.mNumFrames));
716         aArgs++;
717         aArgsLength--;
718 
719         if (aArgsLength > 0)
720         {
721             SuccessOrExit(error = ParseReceiveConfigFormat(aArgs[0], receiveConfig));
722         }
723 
724         SuccessOrExit(error = RadioReceive());
725 
726         mReceiveConfig.mIsEnabled      = true;
727         mReceiveConfig.mIsAsyncCommand = receiveConfig.mIsAsyncCommand;
728         mReceiveConfig.mShowRssi       = receiveConfig.mShowRssi;
729         mReceiveConfig.mShowLqi        = receiveConfig.mShowLqi;
730         mReceiveConfig.mShowPsdu       = receiveConfig.mShowPsdu;
731         mReceiveConfig.mReceiveCount   = receiveConfig.mReceiveCount;
732         mReceiveConfig.mNumFrames      = receiveConfig.mNumFrames;
733 
734         if (!mReceiveConfig.mIsAsyncCommand)
735         {
736             error = kErrorPending;
737         }
738     }
739     else if (StringMatch(aArgs[0], "state"))
740     {
741         otRadioState state = Get<Radio>().GetState();
742 
743         error = kErrorNone;
744 
745         switch (state)
746         {
747         case OT_RADIO_STATE_DISABLED:
748             Output("disabled\r\n");
749             break;
750 
751         case OT_RADIO_STATE_SLEEP:
752             Output("sleep\r\n");
753             break;
754 
755         case OT_RADIO_STATE_RECEIVE:
756             Output("receive\r\n");
757             break;
758 
759         case OT_RADIO_STATE_TRANSMIT:
760             Output("transmit\r\n");
761             break;
762 
763         default:
764             Output("invalid\r\n");
765             break;
766         }
767     }
768     else if (StringMatch(aArgs[0], "enable"))
769     {
770         SuccessOrExit(error = Get<Radio>().Enable());
771     }
772     else if (StringMatch(aArgs[0], "disable"))
773     {
774         SuccessOrExit(error = Get<Radio>().Disable());
775     }
776 
777 exit:
778     return error;
779 }
780 
781 extern "C" void otPlatDiagAlarmFired(otInstance *aInstance) { AsCoreType(aInstance).Get<Diags>().AlarmFired(); }
782 
783 void Diags::AlarmFired(void)
784 {
785     if (mCurTxCmd == kTxCmdRepeat)
786     {
787         uint32_t now = otPlatAlarmMilliGetNow();
788 
789         IgnoreError(TransmitPacket());
790         otPlatAlarmMilliStartAt(&GetInstance(), now, mTxPeriod);
791     }
792     else
793     {
794         otPlatDiagAlarmCallback(&GetInstance());
795     }
796 }
797 
798 void Diags::OutputReceivedFrame(const otRadioFrame *aFrame)
799 {
800     VerifyOrExit(mReceiveConfig.mIsEnabled && (aFrame != nullptr));
801 
802     Output("%u", mReceiveConfig.mReceiveCount++);
803 
804     if (mReceiveConfig.mShowRssi)
805     {
806         Output(", rssi:%d", aFrame->mInfo.mRxInfo.mRssi);
807     }
808 
809     if (mReceiveConfig.mShowLqi)
810     {
811         Output(", lqi:%u", aFrame->mInfo.mRxInfo.mLqi);
812     }
813 
814     if (mReceiveConfig.mShowPsdu)
815     {
816         static constexpr uint16_t kBufSize = 255;
817         char                      buf[kBufSize];
818         StringWriter              writer(buf, sizeof(buf));
819 
820         writer.AppendHexBytes(aFrame->mPsdu, aFrame->mLength);
821         Output(", len:%u, psdu:%s", aFrame->mLength, buf);
822     }
823 
824     Output("\r\n");
825 
826     if (mReceiveConfig.mReceiveCount >= mReceiveConfig.mNumFrames)
827     {
828         mReceiveConfig.mIsEnabled = false;
829 
830         if (!mReceiveConfig.mIsAsyncCommand)
831         {
832             Output("OT_ERROR_NONE");
833         }
834     }
835 
836 exit:
837     return;
838 }
839 
840 void Diags::ReceiveDone(otRadioFrame *aFrame, Error aError)
841 {
842     if (aError == kErrorNone)
843     {
844         if (mReceiveConfig.mIsFilterEnabled)
845         {
846             VerifyOrExit(ShouldHandleReceivedFrame(*aFrame));
847         }
848 
849         OutputReceivedFrame(aFrame);
850 
851         // for sensitivity test, only record the rssi and lqi for the first and last packet
852         if (mStats.mReceivedPackets == 0)
853         {
854             mStats.mFirstRssi = aFrame->mInfo.mRxInfo.mRssi;
855             mStats.mFirstLqi  = aFrame->mInfo.mRxInfo.mLqi;
856         }
857 
858         mStats.mLastRssi = aFrame->mInfo.mRxInfo.mRssi;
859         mStats.mLastLqi  = aFrame->mInfo.mRxInfo.mLqi;
860 
861         mStats.mReceivedPackets++;
862     }
863 
864     otPlatDiagRadioReceived(&GetInstance(), aFrame, aError);
865 
866 exit:
867     return;
868 }
869 
870 void Diags::TransmitDone(Error aError)
871 {
872     VerifyOrExit(mDiagSendOn);
873     mDiagSendOn = false;
874 
875     if (mIsSleepOn)
876     {
877         IgnoreError(Get<Radio>().Sleep());
878     }
879 
880     UpdateTxStats(aError);
881     VerifyOrExit((mCurTxCmd == kTxCmdSend) && (mTxPackets > 0));
882 
883     if (mTxPackets > 1)
884     {
885         mTxPackets--;
886         IgnoreError(TransmitPacket());
887     }
888     else
889     {
890         mTxPackets = 0;
891         mCurTxCmd  = kTxCmdNone;
892 
893         if (!mIsAsyncSend)
894         {
895             Output("OT_ERROR_NONE");
896         }
897     }
898 
899 exit:
900     return;
901 }
902 
903 bool Diags::ShouldHandleReceivedFrame(const otRadioFrame &aFrame) const
904 {
905     bool                ret   = false;
906     const Mac::RxFrame &frame = static_cast<const Mac::RxFrame &>(aFrame);
907     Mac::Address        dstAddress;
908 
909     VerifyOrExit(frame.GetDstAddr(dstAddress) == kErrorNone);
910     VerifyOrExit(dstAddress == mReceiveConfig.mFilterAddress);
911     ret = true;
912 
913 exit:
914     return ret;
915 }
916 
917 void Diags::UpdateTxStats(Error aError)
918 {
919     switch (aError)
920     {
921     case kErrorNone:
922         mStats.mSentSuccessPackets++;
923         break;
924 
925     case kErrorChannelAccessFailure:
926         mStats.mSentErrorCcaPackets++;
927         break;
928 
929     case kErrorAbort:
930         mStats.mSentErrorAbortPackets++;
931         break;
932 
933     case kErrorInvalidState:
934         mStats.mSentErrorInvalidStatePackets++;
935         break;
936 
937     default:
938         mStats.mSentErrorOthersPackets++;
939         break;
940     }
941 }
942 
943 #endif // OPENTHREAD_RADIO
944 
ProcessContinuousWave(uint8_t aArgsLength,char * aArgs[])945 Error Diags::ProcessContinuousWave(uint8_t aArgsLength, char *aArgs[])
946 {
947     Error error = kErrorInvalidArgs;
948 
949     VerifyOrExit(aArgsLength > 0, error = kErrorInvalidArgs);
950 
951     if (StringMatch(aArgs[0], "start"))
952     {
953         SuccessOrExit(error = otPlatDiagRadioTransmitCarrier(&GetInstance(), true));
954     }
955     else if (StringMatch(aArgs[0], "stop"))
956     {
957         SuccessOrExit(error = otPlatDiagRadioTransmitCarrier(&GetInstance(), false));
958     }
959 
960 exit:
961     return error;
962 }
963 
ProcessStream(uint8_t aArgsLength,char * aArgs[])964 Error Diags::ProcessStream(uint8_t aArgsLength, char *aArgs[])
965 {
966     Error error = kErrorInvalidArgs;
967 
968     VerifyOrExit(aArgsLength > 0, error = kErrorInvalidArgs);
969 
970     if (StringMatch(aArgs[0], "start"))
971     {
972         error = otPlatDiagRadioTransmitStream(&GetInstance(), true);
973     }
974     else if (StringMatch(aArgs[0], "stop"))
975     {
976         error = otPlatDiagRadioTransmitStream(&GetInstance(), false);
977     }
978 
979 exit:
980     return error;
981 }
982 
GetPowerSettings(uint8_t aChannel,PowerSettings & aPowerSettings)983 Error Diags::GetPowerSettings(uint8_t aChannel, PowerSettings &aPowerSettings)
984 {
985     aPowerSettings.mRawPowerSetting.mLength = RawPowerSetting::kMaxDataSize;
986     return otPlatDiagRadioGetPowerSettings(&GetInstance(), aChannel, &aPowerSettings.mTargetPower,
987                                            &aPowerSettings.mActualPower, aPowerSettings.mRawPowerSetting.mData,
988                                            &aPowerSettings.mRawPowerSetting.mLength);
989 }
990 
ProcessPowerSettings(uint8_t aArgsLength,char * aArgs[])991 Error Diags::ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[])
992 {
993     Error         error = kErrorInvalidArgs;
994     uint8_t       channel;
995     PowerSettings powerSettings;
996 
997     if (aArgsLength == 0)
998     {
999         bool          isPrePowerSettingsValid = false;
1000         uint8_t       preChannel              = 0;
1001         PowerSettings prePowerSettings;
1002 
1003         Output("| StartCh | EndCh | TargetPower | ActualPower | RawPowerSetting |\r\n"
1004                "+---------+-------+-------------+-------------+-----------------+\r\n");
1005 
1006         for (channel = Radio::kChannelMin; channel <= Radio::kChannelMax + 1; channel++)
1007         {
1008             error = (channel == Radio::kChannelMax + 1) ? kErrorNotFound : GetPowerSettings(channel, powerSettings);
1009 
1010             if (isPrePowerSettingsValid && ((powerSettings != prePowerSettings) || (error != kErrorNone)))
1011             {
1012                 Output("| %7u | %5u | %11d | %11d | %15s |\r\n", preChannel, channel - 1, prePowerSettings.mTargetPower,
1013                        prePowerSettings.mActualPower, prePowerSettings.mRawPowerSetting.ToString().AsCString());
1014                 isPrePowerSettingsValid = false;
1015             }
1016 
1017             if ((error == kErrorNone) && (!isPrePowerSettingsValid))
1018             {
1019                 preChannel              = channel;
1020                 prePowerSettings        = powerSettings;
1021                 isPrePowerSettingsValid = true;
1022             }
1023         }
1024 
1025         error = kErrorNone;
1026     }
1027     else if (aArgsLength == 1)
1028     {
1029         SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(aArgs[0], channel));
1030         VerifyOrExit(IsChannelValid(channel), error = kErrorInvalidArgs);
1031 
1032         SuccessOrExit(error = GetPowerSettings(channel, powerSettings));
1033         Output("TargetPower(0.01dBm): %d\r\nActualPower(0.01dBm): %d\r\nRawPowerSetting: %s\r\n",
1034                powerSettings.mTargetPower, powerSettings.mActualPower,
1035                powerSettings.mRawPowerSetting.ToString().AsCString());
1036     }
1037 
1038 exit:
1039     return error;
1040 }
1041 
GetRawPowerSetting(RawPowerSetting & aRawPowerSetting)1042 Error Diags::GetRawPowerSetting(RawPowerSetting &aRawPowerSetting)
1043 {
1044     aRawPowerSetting.mLength = RawPowerSetting::kMaxDataSize;
1045     return otPlatDiagRadioGetRawPowerSetting(&GetInstance(), aRawPowerSetting.mData, &aRawPowerSetting.mLength);
1046 }
1047 
ProcessRawPowerSetting(uint8_t aArgsLength,char * aArgs[])1048 Error Diags::ProcessRawPowerSetting(uint8_t aArgsLength, char *aArgs[])
1049 {
1050     Error           error = kErrorInvalidArgs;
1051     RawPowerSetting setting;
1052 
1053     if (aArgsLength == 0)
1054     {
1055         SuccessOrExit(error = GetRawPowerSetting(setting));
1056         Output("%s\r\n", setting.ToString().AsCString());
1057     }
1058     else if (StringMatch(aArgs[0], "enable"))
1059     {
1060         SuccessOrExit(error = otPlatDiagRadioRawPowerSettingEnable(&GetInstance(), true));
1061     }
1062     else if (StringMatch(aArgs[0], "disable"))
1063     {
1064         SuccessOrExit(error = otPlatDiagRadioRawPowerSettingEnable(&GetInstance(), false));
1065     }
1066     else
1067     {
1068         setting.mLength = RawPowerSetting::kMaxDataSize;
1069         SuccessOrExit(error = Utils::CmdLineParser::ParseAsHexString(aArgs[0], setting.mLength, setting.mData));
1070         SuccessOrExit(error = otPlatDiagRadioSetRawPowerSetting(&GetInstance(), setting.mData, setting.mLength));
1071     }
1072 
1073 exit:
1074     return error;
1075 }
1076 
ProcessGpio(uint8_t aArgsLength,char * aArgs[])1077 Error Diags::ProcessGpio(uint8_t aArgsLength, char *aArgs[])
1078 {
1079     Error      error = kErrorInvalidArgs;
1080     uint32_t   gpio;
1081     bool       level;
1082     otGpioMode mode;
1083 
1084     if ((aArgsLength == 2) && StringMatch(aArgs[0], "get"))
1085     {
1086         SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint32(aArgs[1], gpio));
1087         SuccessOrExit(error = otPlatDiagGpioGet(gpio, &level));
1088         Output("%d\r\n", level);
1089     }
1090     else if ((aArgsLength == 3) && StringMatch(aArgs[0], "set"))
1091     {
1092         SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint32(aArgs[1], gpio));
1093         SuccessOrExit(error = Utils::CmdLineParser::ParseAsBool(aArgs[2], level));
1094         SuccessOrExit(error = otPlatDiagGpioSet(gpio, level));
1095     }
1096     else if ((aArgsLength >= 2) && StringMatch(aArgs[0], "mode"))
1097     {
1098         SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint32(aArgs[1], gpio));
1099 
1100         if (aArgsLength == 2)
1101         {
1102             SuccessOrExit(error = otPlatDiagGpioGetMode(gpio, &mode));
1103             if (mode == OT_GPIO_MODE_INPUT)
1104             {
1105                 Output("in\r\n");
1106             }
1107             else if (mode == OT_GPIO_MODE_OUTPUT)
1108             {
1109                 Output("out\r\n");
1110             }
1111         }
1112         else if ((aArgsLength == 3) && StringMatch(aArgs[2], "in"))
1113         {
1114             SuccessOrExit(error = otPlatDiagGpioSetMode(gpio, OT_GPIO_MODE_INPUT));
1115         }
1116         else if ((aArgsLength == 3) && StringMatch(aArgs[2], "out"))
1117         {
1118             SuccessOrExit(error = otPlatDiagGpioSetMode(gpio, OT_GPIO_MODE_OUTPUT));
1119         }
1120     }
1121 
1122 exit:
1123     return error;
1124 }
1125 
IsChannelValid(uint8_t aChannel)1126 bool Diags::IsChannelValid(uint8_t aChannel)
1127 {
1128     return (aChannel >= Radio::kChannelMin && aChannel <= Radio::kChannelMax);
1129 }
1130 
ParseCmd(char * aString,uint8_t & aArgsLength,char * aArgs[])1131 Error Diags::ParseCmd(char *aString, uint8_t &aArgsLength, char *aArgs[])
1132 {
1133     Error                     error;
1134     Utils::CmdLineParser::Arg args[kMaxArgs + 1];
1135 
1136     SuccessOrExit(error = Utils::CmdLineParser::ParseCmd(aString, args));
1137     aArgsLength = Utils::CmdLineParser::Arg::GetArgsLength(args);
1138     Utils::CmdLineParser::Arg::CopyArgsToStringArray(args, aArgs);
1139 
1140 exit:
1141     return error;
1142 }
1143 
ProcessLine(const char * aString)1144 Error Diags::ProcessLine(const char *aString)
1145 {
1146     constexpr uint16_t kMaxCommandBuffer = OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE;
1147 
1148     Error   error = kErrorNone;
1149     char    buffer[kMaxCommandBuffer];
1150     char   *args[kMaxArgs];
1151     uint8_t argCount = 0;
1152 
1153     VerifyOrExit(StringLength(aString, kMaxCommandBuffer) < kMaxCommandBuffer, error = kErrorNoBufs);
1154 
1155     strcpy(buffer, aString);
1156     error = ParseCmd(buffer, argCount, args);
1157 
1158 exit:
1159 
1160     switch (error)
1161     {
1162     case kErrorNone:
1163         error = ProcessCmd(argCount, &args[0]);
1164         break;
1165 
1166     case kErrorNoBufs:
1167         Output("failed: command string too long\r\n");
1168         break;
1169 
1170     case kErrorInvalidArgs:
1171         Output("failed: command string contains too many arguments\r\n");
1172         break;
1173 
1174     default:
1175         Output("failed to parse command string\r\n");
1176         break;
1177     }
1178 
1179     return error;
1180 }
1181 
ProcessCmd(uint8_t aArgsLength,char * aArgs[])1182 Error Diags::ProcessCmd(uint8_t aArgsLength, char *aArgs[])
1183 {
1184     Error error = kErrorNone;
1185 
1186     // This `rcp` command is for debugging and testing only, building only when NDEBUG is not defined
1187     // so that it will be excluded from release build.
1188 #if OPENTHREAD_RADIO && !defined(NDEBUG)
1189     if (aArgsLength > 0 && StringMatch(aArgs[0], "rcp"))
1190     {
1191         aArgs++;
1192         aArgsLength--;
1193     }
1194 #endif
1195 
1196     if (aArgsLength == 0)
1197     {
1198         Output("diagnostics mode is %s\r\n", otPlatDiagModeGet() ? "enabled" : "disabled");
1199         ExitNow();
1200     }
1201 
1202     if (!otPlatDiagModeGet() && !StringMatch(aArgs[0], "start"))
1203     {
1204         Output("diagnostics mode is disabled\r\n");
1205         ExitNow(error = kErrorInvalidState);
1206     }
1207 
1208     for (const Command &command : sCommands)
1209     {
1210         if (StringMatch(aArgs[0], command.mName))
1211         {
1212             error = (this->*command.mCommand)(aArgsLength - 1, (aArgsLength > 1) ? &aArgs[1] : nullptr);
1213             ExitNow();
1214         }
1215     }
1216 
1217     // more platform specific features will be processed under platform layer
1218     error = otPlatDiagProcess(&GetInstance(), aArgsLength, aArgs);
1219 
1220 exit:
1221     // Add more platform specific diagnostics features here.
1222     if (error == kErrorInvalidCommand && aArgsLength > 1)
1223     {
1224         Output("diag feature '%s' is not supported\r\n", aArgs[0]);
1225     }
1226 
1227     return error;
1228 }
1229 
SetOutputCallback(otDiagOutputCallback aCallback,void * aContext)1230 void Diags::SetOutputCallback(otDiagOutputCallback aCallback, void *aContext)
1231 {
1232     mOutputCallback = aCallback;
1233     mOutputContext  = aContext;
1234 
1235     otPlatDiagSetOutputCallback(&GetInstance(), aCallback, aContext);
1236 }
1237 
Output(const char * aFormat,...)1238 void Diags::Output(const char *aFormat, ...)
1239 {
1240     va_list args;
1241 
1242     va_start(args, aFormat);
1243 
1244     if (mOutputCallback != nullptr)
1245     {
1246         mOutputCallback(aFormat, args, mOutputContext);
1247     }
1248 
1249     va_end(args);
1250 }
1251 
IsEnabled(void)1252 bool Diags::IsEnabled(void) { return otPlatDiagModeGet(); }
1253 
1254 } // namespace FactoryDiags
1255 } // namespace ot
1256 
otPlatDiagGpioSet(uint32_t aGpio,bool aValue)1257 OT_TOOL_WEAK otError otPlatDiagGpioSet(uint32_t aGpio, bool aValue)
1258 {
1259     OT_UNUSED_VARIABLE(aGpio);
1260     OT_UNUSED_VARIABLE(aValue);
1261 
1262     return OT_ERROR_NOT_IMPLEMENTED;
1263 }
1264 
otPlatDiagGpioGet(uint32_t aGpio,bool * aValue)1265 OT_TOOL_WEAK otError otPlatDiagGpioGet(uint32_t aGpio, bool *aValue)
1266 {
1267     OT_UNUSED_VARIABLE(aGpio);
1268     OT_UNUSED_VARIABLE(aValue);
1269 
1270     return OT_ERROR_NOT_IMPLEMENTED;
1271 }
1272 
otPlatDiagGpioSetMode(uint32_t aGpio,otGpioMode aMode)1273 OT_TOOL_WEAK otError otPlatDiagGpioSetMode(uint32_t aGpio, otGpioMode aMode)
1274 {
1275     OT_UNUSED_VARIABLE(aGpio);
1276     OT_UNUSED_VARIABLE(aMode);
1277 
1278     return OT_ERROR_NOT_IMPLEMENTED;
1279 }
1280 
otPlatDiagGpioGetMode(uint32_t aGpio,otGpioMode * aMode)1281 OT_TOOL_WEAK otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode)
1282 {
1283     OT_UNUSED_VARIABLE(aGpio);
1284     OT_UNUSED_VARIABLE(aMode);
1285 
1286     return OT_ERROR_NOT_IMPLEMENTED;
1287 }
1288 
otPlatDiagRadioSetRawPowerSetting(otInstance * aInstance,const uint8_t * aRawPowerSetting,uint16_t aRawPowerSettingLength)1289 OT_TOOL_WEAK otError otPlatDiagRadioSetRawPowerSetting(otInstance    *aInstance,
1290                                                        const uint8_t *aRawPowerSetting,
1291                                                        uint16_t       aRawPowerSettingLength)
1292 {
1293     OT_UNUSED_VARIABLE(aInstance);
1294     OT_UNUSED_VARIABLE(aRawPowerSetting);
1295     OT_UNUSED_VARIABLE(aRawPowerSettingLength);
1296 
1297     return OT_ERROR_NOT_IMPLEMENTED;
1298 }
1299 
otPlatDiagRadioGetRawPowerSetting(otInstance * aInstance,uint8_t * aRawPowerSetting,uint16_t * aRawPowerSettingLength)1300 OT_TOOL_WEAK otError otPlatDiagRadioGetRawPowerSetting(otInstance *aInstance,
1301                                                        uint8_t    *aRawPowerSetting,
1302                                                        uint16_t   *aRawPowerSettingLength)
1303 {
1304     OT_UNUSED_VARIABLE(aInstance);
1305     OT_UNUSED_VARIABLE(aRawPowerSetting);
1306     OT_UNUSED_VARIABLE(aRawPowerSettingLength);
1307 
1308     return OT_ERROR_NOT_IMPLEMENTED;
1309 }
1310 
otPlatDiagRadioRawPowerSettingEnable(otInstance * aInstance,bool aEnable)1311 OT_TOOL_WEAK otError otPlatDiagRadioRawPowerSettingEnable(otInstance *aInstance, bool aEnable)
1312 {
1313     OT_UNUSED_VARIABLE(aInstance);
1314     OT_UNUSED_VARIABLE(aEnable);
1315 
1316     return OT_ERROR_NOT_IMPLEMENTED;
1317 }
1318 
otPlatDiagRadioTransmitCarrier(otInstance * aInstance,bool aEnable)1319 OT_TOOL_WEAK otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable)
1320 {
1321     OT_UNUSED_VARIABLE(aInstance);
1322     OT_UNUSED_VARIABLE(aEnable);
1323 
1324     return OT_ERROR_NOT_IMPLEMENTED;
1325 }
1326 
otPlatDiagRadioTransmitStream(otInstance * aInstance,bool aEnable)1327 OT_TOOL_WEAK otError otPlatDiagRadioTransmitStream(otInstance *aInstance, bool aEnable)
1328 {
1329     OT_UNUSED_VARIABLE(aInstance);
1330     OT_UNUSED_VARIABLE(aEnable);
1331 
1332     return OT_ERROR_NOT_IMPLEMENTED;
1333 }
1334 
otPlatDiagRadioGetPowerSettings(otInstance * aInstance,uint8_t aChannel,int16_t * aTargetPower,int16_t * aActualPower,uint8_t * aRawPowerSetting,uint16_t * aRawPowerSettingLength)1335 OT_TOOL_WEAK otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance,
1336                                                      uint8_t     aChannel,
1337                                                      int16_t    *aTargetPower,
1338                                                      int16_t    *aActualPower,
1339                                                      uint8_t    *aRawPowerSetting,
1340                                                      uint16_t   *aRawPowerSettingLength)
1341 {
1342     OT_UNUSED_VARIABLE(aInstance);
1343     OT_UNUSED_VARIABLE(aChannel);
1344     OT_UNUSED_VARIABLE(aTargetPower);
1345     OT_UNUSED_VARIABLE(aActualPower);
1346     OT_UNUSED_VARIABLE(aRawPowerSetting);
1347     OT_UNUSED_VARIABLE(aRawPowerSettingLength);
1348 
1349     return OT_ERROR_NOT_IMPLEMENTED;
1350 }
1351 
1352 #endif // OPENTHREAD_CONFIG_DIAG_ENABLE
1353