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