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