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