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