1 /*
2  *  Copyright (c) 2020, 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 radio apis on posix platform.
32  */
33 
34 #include "platform-posix.h"
35 
36 #include <string.h>
37 
38 #include <openthread/logging.h>
39 
40 #include "common/code_utils.hpp"
41 #include "common/new.hpp"
42 #include "lib/spinel/radio_spinel.hpp"
43 #include "posix/platform/radio.hpp"
44 #include "utils/parse_cmdline.hpp"
45 
46 #if OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_UART
47 #include "hdlc_interface.hpp"
48 
49 static ot::Spinel::RadioSpinel<ot::Posix::HdlcInterface> sRadioSpinel;
50 #elif OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_SPI
51 #include "spi_interface.hpp"
52 
53 static ot::Spinel::RadioSpinel<ot::Posix::SpiInterface> sRadioSpinel;
54 #elif OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_VENDOR
55 #include "vendor_interface.hpp"
56 
57 static ot::Spinel::RadioSpinel<ot::Posix::VendorInterface> sRadioSpinel;
58 #else
59 #error "OPENTHREAD_POSIX_CONFIG_RCP_BUS only allows OT_POSIX_RCP_BUS_UART, OT_POSIX_RCP_BUS_SPI and " \
60     "OT_POSIX_RCP_BUS_VENDOR!"
61 #endif
62 
63 #if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
64 #include "power_updater.hpp"
65 static ot::Posix::PowerUpdater sPowerUpdater;
66 #endif
67 
68 namespace ot {
69 namespace Posix {
70 
71 namespace {
72 alignas(alignof(ot::Posix::Radio)) char sRadioRaw[sizeof(ot::Posix::Radio)];
73 
platformRadioInit(const char * aUrl)74 extern "C" void platformRadioInit(const char *aUrl)
75 {
76     Radio &radio = *(new (&sRadioRaw) Radio(aUrl));
77 
78     radio.Init();
79 }
80 } // namespace
81 
Radio(const char * aUrl)82 Radio::Radio(const char *aUrl)
83     : mRadioUrl(aUrl)
84 {
85     VerifyOrDie(mRadioUrl.GetPath() != nullptr, OT_EXIT_INVALID_ARGUMENTS);
86 }
87 
Init(void)88 void Radio::Init(void)
89 {
90     bool        resetRadio             = (mRadioUrl.GetValue("no-reset") == nullptr);
91     bool        restoreDataset         = (mRadioUrl.GetValue("ncp-dataset") != nullptr);
92     bool        skipCompatibilityCheck = (mRadioUrl.GetValue("skip-rcp-compatibility-check") != nullptr);
93     const char *parameterValue;
94     const char *region;
95 #if OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE
96     const char *maxPowerTable;
97 #endif
98 
99 #if OPENTHREAD_POSIX_VIRTUAL_TIME
100     // The last argument must be the node id
101     {
102         const char *nodeId = nullptr;
103 
104         for (const char *arg = nullptr; (arg = mRadioUrl.GetValue("forkpty-arg", arg)) != nullptr; nodeId = arg)
105         {
106         }
107 
108         virtualTimeInit(static_cast<uint16_t>(atoi(nodeId)));
109     }
110 #endif
111 
112     if (restoreDataset)
113     {
114         otLogCritPlat("The argument \"ncp-dataset\" is no longer supported");
115         DieNow(OT_ERROR_FAILED);
116     }
117 
118     SuccessOrDie(sRadioSpinel.GetSpinelInterface().Init(mRadioUrl));
119     sRadioSpinel.Init(resetRadio, skipCompatibilityCheck);
120 
121     parameterValue = mRadioUrl.GetValue("fem-lnagain");
122     if (parameterValue != nullptr)
123     {
124         long femLnaGain = strtol(parameterValue, nullptr, 0);
125 
126         VerifyOrDie(INT8_MIN <= femLnaGain && femLnaGain <= INT8_MAX, OT_EXIT_INVALID_ARGUMENTS);
127         SuccessOrDie(sRadioSpinel.SetFemLnaGain(static_cast<int8_t>(femLnaGain)));
128     }
129 
130     parameterValue = mRadioUrl.GetValue("cca-threshold");
131     if (parameterValue != nullptr)
132     {
133         long ccaThreshold = strtol(parameterValue, nullptr, 0);
134 
135         VerifyOrDie(INT8_MIN <= ccaThreshold && ccaThreshold <= INT8_MAX, OT_EXIT_INVALID_ARGUMENTS);
136         SuccessOrDie(sRadioSpinel.SetCcaEnergyDetectThreshold(static_cast<int8_t>(ccaThreshold)));
137     }
138 
139     region = mRadioUrl.GetValue("region");
140     if (region != nullptr)
141     {
142         uint16_t regionCode;
143 
144         VerifyOrDie(strnlen(region, 3) == 2, OT_EXIT_INVALID_ARGUMENTS);
145         regionCode = static_cast<uint16_t>(static_cast<uint16_t>(region[0]) << 8) + static_cast<uint16_t>(region[1]);
146         SuccessOrDie(otPlatRadioSetRegion(gInstance, regionCode));
147     }
148 
149 #if OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE
150     maxPowerTable = mRadioUrl.GetValue("max-power-table");
151     if (maxPowerTable != nullptr)
152     {
153         constexpr int8_t kPowerDefault = 30; // Default power 1 watt (30 dBm).
154         const char      *str           = nullptr;
155         uint8_t          channel       = ot::Radio::kChannelMin;
156         int8_t           power         = kPowerDefault;
157         otError          error;
158 
159         for (str = strtok(const_cast<char *>(maxPowerTable), ","); str != nullptr && channel <= ot::Radio::kChannelMax;
160              str = strtok(nullptr, ","))
161         {
162             power = static_cast<int8_t>(strtol(str, nullptr, 0));
163             error = sRadioSpinel.SetChannelMaxTransmitPower(channel, power);
164             if (error != OT_ERROR_NONE && error != OT_ERROR_NOT_IMPLEMENTED)
165             {
166                 DieNow(OT_ERROR_FAILED);
167             }
168             else if (error == OT_ERROR_NOT_IMPLEMENTED)
169             {
170                 otLogWarnPlat("The RCP doesn't support setting the max transmit power");
171             }
172 
173             ++channel;
174         }
175 
176         // Use the last power if omitted.
177         while (channel <= ot::Radio::kChannelMax)
178         {
179             error = sRadioSpinel.SetChannelMaxTransmitPower(channel, power);
180             if (error != OT_ERROR_NONE && error != OT_ERROR_NOT_IMPLEMENTED)
181             {
182                 DieNow(OT_ERROR_FAILED);
183             }
184             else if (error == OT_ERROR_NOT_IMPLEMENTED)
185             {
186                 otLogWarnPlat("The RCP doesn't support setting the max transmit power");
187             }
188 
189             ++channel;
190         }
191 
192         VerifyOrDie(str == nullptr, OT_EXIT_INVALID_ARGUMENTS);
193     }
194 #endif // OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE
195 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
196     {
197         const char *enableCoex = mRadioUrl.GetValue("enable-coex");
198         if (enableCoex != nullptr)
199         {
200             SuccessOrDie(sRadioSpinel.SetCoexEnabled(enableCoex[0] != '0'));
201         }
202     }
203 #endif // OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
204 }
205 
GetSpinelInstance(void)206 void *Radio::GetSpinelInstance(void) { return &sRadioSpinel; }
207 
208 } // namespace Posix
209 } // namespace ot
210 
platformRadioDeinit(void)211 void platformRadioDeinit(void) { sRadioSpinel.Deinit(); }
212 
otPlatRadioGetIeeeEui64(otInstance * aInstance,uint8_t * aIeeeEui64)213 void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64)
214 {
215     OT_UNUSED_VARIABLE(aInstance);
216     SuccessOrDie(sRadioSpinel.GetIeeeEui64(aIeeeEui64));
217 }
218 
otPlatRadioSetPanId(otInstance * aInstance,uint16_t panid)219 void otPlatRadioSetPanId(otInstance *aInstance, uint16_t panid)
220 {
221     OT_UNUSED_VARIABLE(aInstance);
222     SuccessOrDie(sRadioSpinel.SetPanId(panid));
223 }
224 
otPlatRadioSetExtendedAddress(otInstance * aInstance,const otExtAddress * aAddress)225 void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aAddress)
226 {
227     OT_UNUSED_VARIABLE(aInstance);
228     otExtAddress addr;
229 
230     for (size_t i = 0; i < sizeof(addr); i++)
231     {
232         addr.m8[i] = aAddress->m8[sizeof(addr) - 1 - i];
233     }
234 
235     SuccessOrDie(sRadioSpinel.SetExtendedAddress(addr));
236 }
237 
otPlatRadioSetShortAddress(otInstance * aInstance,uint16_t aAddress)238 void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aAddress)
239 {
240     OT_UNUSED_VARIABLE(aInstance);
241     SuccessOrDie(sRadioSpinel.SetShortAddress(aAddress));
242 }
243 
otPlatRadioSetPromiscuous(otInstance * aInstance,bool aEnable)244 void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
245 {
246     OT_UNUSED_VARIABLE(aInstance);
247     SuccessOrDie(sRadioSpinel.SetPromiscuous(aEnable));
248 }
249 
otPlatRadioIsEnabled(otInstance * aInstance)250 bool otPlatRadioIsEnabled(otInstance *aInstance)
251 {
252     OT_UNUSED_VARIABLE(aInstance);
253     return sRadioSpinel.IsEnabled();
254 }
255 
otPlatRadioEnable(otInstance * aInstance)256 otError otPlatRadioEnable(otInstance *aInstance) { return sRadioSpinel.Enable(aInstance); }
257 
otPlatRadioDisable(otInstance * aInstance)258 otError otPlatRadioDisable(otInstance *aInstance)
259 {
260     OT_UNUSED_VARIABLE(aInstance);
261     return sRadioSpinel.Disable();
262 }
263 
otPlatRadioSleep(otInstance * aInstance)264 otError otPlatRadioSleep(otInstance *aInstance)
265 {
266     OT_UNUSED_VARIABLE(aInstance);
267     return sRadioSpinel.Sleep();
268 }
269 
otPlatRadioReceive(otInstance * aInstance,uint8_t aChannel)270 otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
271 {
272     OT_UNUSED_VARIABLE(aInstance);
273 
274     otError error;
275 
276     SuccessOrExit(error = sRadioSpinel.Receive(aChannel));
277 
278 exit:
279     return error;
280 }
281 
otPlatRadioTransmit(otInstance * aInstance,otRadioFrame * aFrame)282 otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
283 {
284     OT_UNUSED_VARIABLE(aInstance);
285     return sRadioSpinel.Transmit(*aFrame);
286 }
287 
otPlatRadioGetTransmitBuffer(otInstance * aInstance)288 otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
289 {
290     OT_UNUSED_VARIABLE(aInstance);
291     return &sRadioSpinel.GetTransmitFrame();
292 }
293 
otPlatRadioGetRssi(otInstance * aInstance)294 int8_t otPlatRadioGetRssi(otInstance *aInstance)
295 {
296     OT_UNUSED_VARIABLE(aInstance);
297     return sRadioSpinel.GetRssi();
298 }
299 
otPlatRadioGetCaps(otInstance * aInstance)300 otRadioCaps otPlatRadioGetCaps(otInstance *aInstance)
301 {
302     OT_UNUSED_VARIABLE(aInstance);
303     return sRadioSpinel.GetRadioCaps();
304 }
305 
otPlatRadioGetVersionString(otInstance * aInstance)306 const char *otPlatRadioGetVersionString(otInstance *aInstance)
307 {
308     OT_UNUSED_VARIABLE(aInstance);
309     return sRadioSpinel.GetVersion();
310 }
311 
otPlatRadioGetPromiscuous(otInstance * aInstance)312 bool otPlatRadioGetPromiscuous(otInstance *aInstance)
313 {
314     OT_UNUSED_VARIABLE(aInstance);
315     return sRadioSpinel.IsPromiscuous();
316 }
317 
platformRadioUpdateFdSet(otSysMainloopContext * aContext)318 void platformRadioUpdateFdSet(otSysMainloopContext *aContext)
319 {
320     uint64_t now      = otPlatTimeGet();
321     uint64_t deadline = sRadioSpinel.GetNextRadioTimeRecalcStart();
322 
323     if (sRadioSpinel.IsTransmitting())
324     {
325         uint64_t txRadioEndUs = sRadioSpinel.GetTxRadioEndUs();
326 
327         if (txRadioEndUs < deadline)
328         {
329             deadline = txRadioEndUs;
330         }
331     }
332 
333     if (now < deadline)
334     {
335         uint64_t remain = deadline - now;
336 
337         if (remain < (static_cast<uint64_t>(aContext->mTimeout.tv_sec) * US_PER_S +
338                       static_cast<uint64_t>(aContext->mTimeout.tv_usec)))
339         {
340             aContext->mTimeout.tv_sec  = static_cast<time_t>(remain / US_PER_S);
341             aContext->mTimeout.tv_usec = static_cast<suseconds_t>(remain % US_PER_S);
342         }
343     }
344     else
345     {
346         aContext->mTimeout.tv_sec  = 0;
347         aContext->mTimeout.tv_usec = 0;
348     }
349 
350     sRadioSpinel.GetSpinelInterface().UpdateFdSet(aContext);
351 
352     if (sRadioSpinel.HasPendingFrame() || sRadioSpinel.IsTransmitDone())
353     {
354         aContext->mTimeout.tv_sec  = 0;
355         aContext->mTimeout.tv_usec = 0;
356     }
357 }
358 
359 #if OPENTHREAD_POSIX_VIRTUAL_TIME
virtualTimeRadioSpinelProcess(otInstance * aInstance,const struct VirtualTimeEvent * aEvent)360 void virtualTimeRadioSpinelProcess(otInstance *aInstance, const struct VirtualTimeEvent *aEvent)
361 {
362     OT_UNUSED_VARIABLE(aInstance);
363     sRadioSpinel.Process(aEvent);
364 }
365 #else
platformRadioProcess(otInstance * aInstance,const otSysMainloopContext * aContext)366 void platformRadioProcess(otInstance *aInstance, const otSysMainloopContext *aContext)
367 {
368     OT_UNUSED_VARIABLE(aInstance);
369 
370     sRadioSpinel.Process(aContext);
371 }
372 #endif // OPENTHREAD_POSIX_VIRTUAL_TIME
373 
otPlatRadioEnableSrcMatch(otInstance * aInstance,bool aEnable)374 void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable)
375 {
376     OT_UNUSED_VARIABLE(aInstance);
377     SuccessOrDie(sRadioSpinel.EnableSrcMatch(aEnable));
378 }
379 
otPlatRadioAddSrcMatchShortEntry(otInstance * aInstance,uint16_t aShortAddress)380 otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, uint16_t aShortAddress)
381 {
382     OT_UNUSED_VARIABLE(aInstance);
383     return sRadioSpinel.AddSrcMatchShortEntry(aShortAddress);
384 }
385 
otPlatRadioAddSrcMatchExtEntry(otInstance * aInstance,const otExtAddress * aExtAddress)386 otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress)
387 {
388     OT_UNUSED_VARIABLE(aInstance);
389     otExtAddress addr;
390 
391     for (size_t i = 0; i < sizeof(addr); i++)
392     {
393         addr.m8[i] = aExtAddress->m8[sizeof(addr) - 1 - i];
394     }
395 
396     return sRadioSpinel.AddSrcMatchExtEntry(addr);
397 }
398 
otPlatRadioClearSrcMatchShortEntry(otInstance * aInstance,uint16_t aShortAddress)399 otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, uint16_t aShortAddress)
400 {
401     OT_UNUSED_VARIABLE(aInstance);
402     return sRadioSpinel.ClearSrcMatchShortEntry(aShortAddress);
403 }
404 
otPlatRadioClearSrcMatchExtEntry(otInstance * aInstance,const otExtAddress * aExtAddress)405 otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress)
406 {
407     OT_UNUSED_VARIABLE(aInstance);
408     otExtAddress addr;
409 
410     for (size_t i = 0; i < sizeof(addr); i++)
411     {
412         addr.m8[i] = aExtAddress->m8[sizeof(addr) - 1 - i];
413     }
414 
415     return sRadioSpinel.ClearSrcMatchExtEntry(addr);
416 }
417 
otPlatRadioClearSrcMatchShortEntries(otInstance * aInstance)418 void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance)
419 {
420     OT_UNUSED_VARIABLE(aInstance);
421     SuccessOrDie(sRadioSpinel.ClearSrcMatchShortEntries());
422 }
423 
otPlatRadioClearSrcMatchExtEntries(otInstance * aInstance)424 void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance)
425 {
426     OT_UNUSED_VARIABLE(aInstance);
427     SuccessOrDie(sRadioSpinel.ClearSrcMatchExtEntries());
428 }
429 
otPlatRadioEnergyScan(otInstance * aInstance,uint8_t aScanChannel,uint16_t aScanDuration)430 otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration)
431 {
432     OT_UNUSED_VARIABLE(aInstance);
433     return sRadioSpinel.EnergyScan(aScanChannel, aScanDuration);
434 }
435 
otPlatRadioGetTransmitPower(otInstance * aInstance,int8_t * aPower)436 otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower)
437 {
438     OT_UNUSED_VARIABLE(aInstance);
439     assert(aPower != nullptr);
440     return sRadioSpinel.GetTransmitPower(*aPower);
441 }
442 
otPlatRadioSetTransmitPower(otInstance * aInstance,int8_t aPower)443 otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower)
444 {
445     OT_UNUSED_VARIABLE(aInstance);
446     return sRadioSpinel.SetTransmitPower(aPower);
447 }
448 
otPlatRadioGetCcaEnergyDetectThreshold(otInstance * aInstance,int8_t * aThreshold)449 otError otPlatRadioGetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t *aThreshold)
450 {
451     OT_UNUSED_VARIABLE(aInstance);
452     assert(aThreshold != nullptr);
453     return sRadioSpinel.GetCcaEnergyDetectThreshold(*aThreshold);
454 }
455 
otPlatRadioSetCcaEnergyDetectThreshold(otInstance * aInstance,int8_t aThreshold)456 otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t aThreshold)
457 {
458     OT_UNUSED_VARIABLE(aInstance);
459     return sRadioSpinel.SetCcaEnergyDetectThreshold(aThreshold);
460 }
461 
otPlatRadioGetFemLnaGain(otInstance * aInstance,int8_t * aGain)462 otError otPlatRadioGetFemLnaGain(otInstance *aInstance, int8_t *aGain)
463 {
464     OT_UNUSED_VARIABLE(aInstance);
465     assert(aGain != nullptr);
466     return sRadioSpinel.GetFemLnaGain(*aGain);
467 }
468 
otPlatRadioSetFemLnaGain(otInstance * aInstance,int8_t aGain)469 otError otPlatRadioSetFemLnaGain(otInstance *aInstance, int8_t aGain)
470 {
471     OT_UNUSED_VARIABLE(aInstance);
472     return sRadioSpinel.SetFemLnaGain(aGain);
473 }
474 
otPlatRadioGetReceiveSensitivity(otInstance * aInstance)475 int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance)
476 {
477     OT_UNUSED_VARIABLE(aInstance);
478     return sRadioSpinel.GetReceiveSensitivity();
479 }
480 
481 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
otPlatRadioSetCoexEnabled(otInstance * aInstance,bool aEnabled)482 otError otPlatRadioSetCoexEnabled(otInstance *aInstance, bool aEnabled)
483 {
484     OT_UNUSED_VARIABLE(aInstance);
485     return sRadioSpinel.SetCoexEnabled(aEnabled);
486 }
487 
otPlatRadioIsCoexEnabled(otInstance * aInstance)488 bool otPlatRadioIsCoexEnabled(otInstance *aInstance)
489 {
490     OT_UNUSED_VARIABLE(aInstance);
491     return sRadioSpinel.IsCoexEnabled();
492 }
493 
otPlatRadioGetCoexMetrics(otInstance * aInstance,otRadioCoexMetrics * aCoexMetrics)494 otError otPlatRadioGetCoexMetrics(otInstance *aInstance, otRadioCoexMetrics *aCoexMetrics)
495 {
496     OT_UNUSED_VARIABLE(aInstance);
497 
498     otError error = OT_ERROR_NONE;
499 
500     VerifyOrExit(aCoexMetrics != nullptr, error = OT_ERROR_INVALID_ARGS);
501 
502     error = sRadioSpinel.GetCoexMetrics(*aCoexMetrics);
503 
504 exit:
505     return error;
506 }
507 #endif
508 
509 #if OPENTHREAD_CONFIG_DIAG_ENABLE
otPlatDiagProcess(otInstance * aInstance,uint8_t aArgsLength,char * aArgs[],char * aOutput,size_t aOutputMaxLen)510 otError otPlatDiagProcess(otInstance *aInstance,
511                           uint8_t     aArgsLength,
512                           char       *aArgs[],
513                           char       *aOutput,
514                           size_t      aOutputMaxLen)
515 {
516     // deliver the platform specific diags commands to radio only ncp.
517     OT_UNUSED_VARIABLE(aInstance);
518     char  cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE] = {'\0'};
519     char *cur                                              = cmd;
520     char *end                                              = cmd + sizeof(cmd);
521 
522     for (uint8_t index = 0; (index < aArgsLength) && (cur < end); index++)
523     {
524         cur += snprintf(cur, static_cast<size_t>(end - cur), "%s ", aArgs[index]);
525     }
526 
527     return sRadioSpinel.PlatDiagProcess(cmd, aOutput, aOutputMaxLen);
528 }
529 
otPlatDiagModeSet(bool aMode)530 void otPlatDiagModeSet(bool aMode)
531 {
532     SuccessOrExit(sRadioSpinel.PlatDiagProcess(aMode ? "start" : "stop", nullptr, 0));
533     sRadioSpinel.SetDiagEnabled(aMode);
534 
535 exit:
536     return;
537 }
538 
otPlatDiagModeGet(void)539 bool otPlatDiagModeGet(void) { return sRadioSpinel.IsDiagEnabled(); }
540 
otPlatDiagTxPowerSet(int8_t aTxPower)541 void otPlatDiagTxPowerSet(int8_t aTxPower)
542 {
543     char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE];
544 
545     snprintf(cmd, sizeof(cmd), "power %d", aTxPower);
546     SuccessOrExit(sRadioSpinel.PlatDiagProcess(cmd, nullptr, 0));
547 
548 exit:
549     return;
550 }
551 
otPlatDiagChannelSet(uint8_t aChannel)552 void otPlatDiagChannelSet(uint8_t aChannel)
553 {
554     char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE];
555 
556     snprintf(cmd, sizeof(cmd), "channel %d", aChannel);
557     SuccessOrExit(sRadioSpinel.PlatDiagProcess(cmd, nullptr, 0));
558 
559 exit:
560     return;
561 }
562 
otPlatDiagGpioSet(uint32_t aGpio,bool aValue)563 otError otPlatDiagGpioSet(uint32_t aGpio, bool aValue)
564 {
565     otError error;
566     char    cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE];
567 
568     snprintf(cmd, sizeof(cmd), "gpio set %d %d", aGpio, aValue);
569     SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, nullptr, 0));
570 
571 exit:
572     return error;
573 }
574 
otPlatDiagGpioGet(uint32_t aGpio,bool * aValue)575 otError otPlatDiagGpioGet(uint32_t aGpio, bool *aValue)
576 {
577     otError error;
578     char    cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE];
579     char    output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE];
580     char   *str;
581 
582     snprintf(cmd, sizeof(cmd), "gpio get %d", aGpio);
583     SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, output, sizeof(output)));
584     VerifyOrExit((str = strtok(output, "\r")) != nullptr, error = OT_ERROR_FAILED);
585     *aValue = static_cast<bool>(atoi(str));
586 
587 exit:
588     return error;
589 }
590 
otPlatDiagGpioSetMode(uint32_t aGpio,otGpioMode aMode)591 otError otPlatDiagGpioSetMode(uint32_t aGpio, otGpioMode aMode)
592 {
593     otError error;
594     char    cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE];
595 
596     snprintf(cmd, sizeof(cmd), "gpio mode %d %s", aGpio, aMode == OT_GPIO_MODE_INPUT ? "in" : "out");
597     SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, nullptr, 0));
598 
599 exit:
600     return error;
601 }
602 
otPlatDiagGpioGetMode(uint32_t aGpio,otGpioMode * aMode)603 otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode)
604 {
605     otError error;
606     char    cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE];
607     char    output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE];
608     char   *str;
609 
610     snprintf(cmd, sizeof(cmd), "gpio mode %d", aGpio);
611     SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, output, sizeof(output)));
612     VerifyOrExit((str = strtok(output, "\r")) != nullptr, error = OT_ERROR_FAILED);
613 
614     if (strcmp(str, "in") == 0)
615     {
616         *aMode = OT_GPIO_MODE_INPUT;
617     }
618     else if (strcmp(str, "out") == 0)
619     {
620         *aMode = OT_GPIO_MODE_OUTPUT;
621     }
622     else
623     {
624         error = OT_ERROR_FAILED;
625     }
626 
627 exit:
628     return error;
629 }
630 
otPlatDiagRadioGetPowerSettings(otInstance * aInstance,uint8_t aChannel,int16_t * aTargetPower,int16_t * aActualPower,uint8_t * aRawPowerSetting,uint16_t * aRawPowerSettingLength)631 otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance,
632                                         uint8_t     aChannel,
633                                         int16_t    *aTargetPower,
634                                         int16_t    *aActualPower,
635                                         uint8_t    *aRawPowerSetting,
636                                         uint16_t   *aRawPowerSettingLength)
637 {
638     OT_UNUSED_VARIABLE(aInstance);
639     static constexpr uint16_t kRawPowerStringSize = OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE * 2 + 1;
640     static constexpr uint16_t kFmtStringSize      = 100;
641 
642     otError error;
643     char    cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE];
644     char    output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE];
645     int     targetPower;
646     int     actualPower;
647     char    rawPowerSetting[kRawPowerStringSize];
648     char    fmt[kFmtStringSize];
649 
650     assert((aTargetPower != nullptr) && (aActualPower != nullptr) && (aRawPowerSetting != nullptr) &&
651            (aRawPowerSettingLength != nullptr));
652 
653     snprintf(cmd, sizeof(cmd), "powersettings %d", aChannel);
654     SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, output, sizeof(output)));
655     snprintf(fmt, sizeof(fmt), "TargetPower(0.01dBm): %%d\r\nActualPower(0.01dBm): %%d\r\nRawPowerSetting: %%%us\r\n",
656              kRawPowerStringSize);
657     VerifyOrExit(sscanf(output, fmt, &targetPower, &actualPower, rawPowerSetting) == 3, error = OT_ERROR_FAILED);
658     SuccessOrExit(
659         error = ot::Utils::CmdLineParser::ParseAsHexString(rawPowerSetting, *aRawPowerSettingLength, aRawPowerSetting));
660     *aTargetPower = static_cast<int16_t>(targetPower);
661     *aActualPower = static_cast<int16_t>(actualPower);
662 
663 exit:
664     return error;
665 }
666 
otPlatDiagRadioSetRawPowerSetting(otInstance * aInstance,const uint8_t * aRawPowerSetting,uint16_t aRawPowerSettingLength)667 otError otPlatDiagRadioSetRawPowerSetting(otInstance    *aInstance,
668                                           const uint8_t *aRawPowerSetting,
669                                           uint16_t       aRawPowerSettingLength)
670 {
671     OT_UNUSED_VARIABLE(aInstance);
672 
673     otError error;
674     char    cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE];
675     int     nbytes;
676 
677     assert(aRawPowerSetting != nullptr);
678 
679     nbytes = snprintf(cmd, sizeof(cmd), "rawpowersetting ");
680 
681     for (uint16_t i = 0; i < aRawPowerSettingLength; i++)
682     {
683         nbytes += snprintf(cmd + nbytes, sizeof(cmd) - static_cast<size_t>(nbytes), "%02x", aRawPowerSetting[i]);
684         VerifyOrExit(nbytes < static_cast<int>(sizeof(cmd)), error = OT_ERROR_INVALID_ARGS);
685     }
686 
687     SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, nullptr, 0));
688 
689 exit:
690     return error;
691 }
692 
otPlatDiagRadioGetRawPowerSetting(otInstance * aInstance,uint8_t * aRawPowerSetting,uint16_t * aRawPowerSettingLength)693 otError otPlatDiagRadioGetRawPowerSetting(otInstance *aInstance,
694                                           uint8_t    *aRawPowerSetting,
695                                           uint16_t   *aRawPowerSettingLength)
696 {
697     OT_UNUSED_VARIABLE(aInstance);
698     otError error;
699     char    cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE];
700     char    output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE];
701     char   *str;
702 
703     assert((aRawPowerSetting != nullptr) && (aRawPowerSettingLength != nullptr));
704 
705     snprintf(cmd, sizeof(cmd), "rawpowersetting");
706     SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, output, sizeof(output)));
707     VerifyOrExit((str = strtok(output, "\r")) != nullptr, error = OT_ERROR_FAILED);
708     SuccessOrExit(error = ot::Utils::CmdLineParser::ParseAsHexString(str, *aRawPowerSettingLength, aRawPowerSetting));
709 
710 exit:
711     return error;
712 }
713 
otPlatDiagRadioRawPowerSettingEnable(otInstance * aInstance,bool aEnable)714 otError otPlatDiagRadioRawPowerSettingEnable(otInstance *aInstance, bool aEnable)
715 {
716     OT_UNUSED_VARIABLE(aInstance);
717 
718     otError error;
719     char    cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE];
720 
721     snprintf(cmd, sizeof(cmd), "rawpowersetting %s", aEnable ? "enable" : "disable");
722     SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, nullptr, 0));
723 
724 exit:
725     return error;
726 }
727 
otPlatDiagRadioTransmitCarrier(otInstance * aInstance,bool aEnable)728 otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable)
729 {
730     OT_UNUSED_VARIABLE(aInstance);
731 
732     otError error;
733     char    cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE];
734 
735     snprintf(cmd, sizeof(cmd), "cw %s", aEnable ? "start" : "stop");
736     SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, nullptr, 0));
737 
738 exit:
739     return error;
740 }
741 
otPlatDiagRadioTransmitStream(otInstance * aInstance,bool aEnable)742 otError otPlatDiagRadioTransmitStream(otInstance *aInstance, bool aEnable)
743 {
744     OT_UNUSED_VARIABLE(aInstance);
745 
746     char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE];
747 
748     snprintf(cmd, sizeof(cmd), "stream %s", aEnable ? "start" : "stop");
749     return sRadioSpinel.PlatDiagProcess(cmd, nullptr, 0);
750 }
751 
otPlatDiagRadioReceived(otInstance * aInstance,otRadioFrame * aFrame,otError aError)752 void otPlatDiagRadioReceived(otInstance *aInstance, otRadioFrame *aFrame, otError aError)
753 {
754     OT_UNUSED_VARIABLE(aInstance);
755     OT_UNUSED_VARIABLE(aFrame);
756     OT_UNUSED_VARIABLE(aError);
757 }
758 
otPlatDiagAlarmCallback(otInstance * aInstance)759 void otPlatDiagAlarmCallback(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); }
760 #endif // OPENTHREAD_CONFIG_DIAG_ENABLE
761 
otPlatRadioGetSupportedChannelMask(otInstance * aInstance)762 uint32_t otPlatRadioGetSupportedChannelMask(otInstance *aInstance)
763 {
764     OT_UNUSED_VARIABLE(aInstance);
765     return sRadioSpinel.GetRadioChannelMask(false);
766 }
767 
otPlatRadioGetPreferredChannelMask(otInstance * aInstance)768 uint32_t otPlatRadioGetPreferredChannelMask(otInstance *aInstance)
769 {
770     OT_UNUSED_VARIABLE(aInstance);
771     return sRadioSpinel.GetRadioChannelMask(true);
772 }
773 
otPlatRadioGetState(otInstance * aInstance)774 otRadioState otPlatRadioGetState(otInstance *aInstance)
775 {
776     OT_UNUSED_VARIABLE(aInstance);
777     return sRadioSpinel.GetState();
778 }
779 
otPlatRadioSetMacKey(otInstance * aInstance,uint8_t aKeyIdMode,uint8_t aKeyId,const otMacKeyMaterial * aPrevKey,const otMacKeyMaterial * aCurrKey,const otMacKeyMaterial * aNextKey,otRadioKeyType aKeyType)780 void otPlatRadioSetMacKey(otInstance             *aInstance,
781                           uint8_t                 aKeyIdMode,
782                           uint8_t                 aKeyId,
783                           const otMacKeyMaterial *aPrevKey,
784                           const otMacKeyMaterial *aCurrKey,
785                           const otMacKeyMaterial *aNextKey,
786                           otRadioKeyType          aKeyType)
787 {
788     SuccessOrDie(sRadioSpinel.SetMacKey(aKeyIdMode, aKeyId, aPrevKey, aCurrKey, aNextKey));
789     OT_UNUSED_VARIABLE(aInstance);
790     OT_UNUSED_VARIABLE(aKeyType);
791 }
792 
otPlatRadioSetMacFrameCounter(otInstance * aInstance,uint32_t aMacFrameCounter)793 void otPlatRadioSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter)
794 {
795     SuccessOrDie(sRadioSpinel.SetMacFrameCounter(aMacFrameCounter, /* aSetIfLarger */ false));
796     OT_UNUSED_VARIABLE(aInstance);
797 }
798 
otPlatRadioSetMacFrameCounterIfLarger(otInstance * aInstance,uint32_t aMacFrameCounter)799 void otPlatRadioSetMacFrameCounterIfLarger(otInstance *aInstance, uint32_t aMacFrameCounter)
800 {
801     SuccessOrDie(sRadioSpinel.SetMacFrameCounter(aMacFrameCounter, /* aSetIfLarger */ true));
802     OT_UNUSED_VARIABLE(aInstance);
803 }
804 
otPlatRadioGetNow(otInstance * aInstance)805 uint64_t otPlatRadioGetNow(otInstance *aInstance)
806 {
807     OT_UNUSED_VARIABLE(aInstance);
808     return sRadioSpinel.GetNow();
809 }
810 
otPlatRadioGetBusSpeed(otInstance * aInstance)811 uint32_t otPlatRadioGetBusSpeed(otInstance *aInstance)
812 {
813     OT_UNUSED_VARIABLE(aInstance);
814     return sRadioSpinel.GetBusSpeed();
815 }
816 
817 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
otPlatRadioGetCslAccuracy(otInstance * aInstance)818 uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance)
819 {
820     OT_UNUSED_VARIABLE(aInstance);
821 
822     return sRadioSpinel.GetCslAccuracy();
823 }
824 #endif
825 
826 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
otPlatRadioGetCslUncertainty(otInstance * aInstance)827 uint8_t otPlatRadioGetCslUncertainty(otInstance *aInstance)
828 {
829     OT_UNUSED_VARIABLE(aInstance);
830 
831     return sRadioSpinel.GetCslUncertainty();
832 }
833 #endif
834 
otPlatRadioSetChannelMaxTransmitPower(otInstance * aInstance,uint8_t aChannel,int8_t aMaxPower)835 otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aChannel, int8_t aMaxPower)
836 {
837     OT_UNUSED_VARIABLE(aInstance);
838     return sRadioSpinel.SetChannelMaxTransmitPower(aChannel, aMaxPower);
839 }
840 
841 #if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
otPlatRadioAddCalibratedPower(otInstance * aInstance,uint8_t aChannel,int16_t aActualPower,const uint8_t * aRawPowerSetting,uint16_t aRawPowerSettingLength)842 otError otPlatRadioAddCalibratedPower(otInstance    *aInstance,
843                                       uint8_t        aChannel,
844                                       int16_t        aActualPower,
845                                       const uint8_t *aRawPowerSetting,
846                                       uint16_t       aRawPowerSettingLength)
847 {
848     OT_UNUSED_VARIABLE(aInstance);
849     return sRadioSpinel.AddCalibratedPower(aChannel, aActualPower, aRawPowerSetting, aRawPowerSettingLength);
850 }
851 
otPlatRadioClearCalibratedPowers(otInstance * aInstance)852 otError otPlatRadioClearCalibratedPowers(otInstance *aInstance)
853 {
854     OT_UNUSED_VARIABLE(aInstance);
855     return sRadioSpinel.ClearCalibratedPowers();
856 }
857 
otPlatRadioSetChannelTargetPower(otInstance * aInstance,uint8_t aChannel,int16_t aTargetPower)858 otError otPlatRadioSetChannelTargetPower(otInstance *aInstance, uint8_t aChannel, int16_t aTargetPower)
859 {
860     OT_UNUSED_VARIABLE(aInstance);
861     return sRadioSpinel.SetChannelTargetPower(aChannel, aTargetPower);
862 }
863 #endif
864 
otPlatRadioSetRegion(otInstance * aInstance,uint16_t aRegionCode)865 otError otPlatRadioSetRegion(otInstance *aInstance, uint16_t aRegionCode)
866 {
867     OT_UNUSED_VARIABLE(aInstance);
868 #if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
869     return sPowerUpdater.SetRegion(aRegionCode);
870 #else
871     return sRadioSpinel.SetRadioRegion(aRegionCode);
872 #endif
873 }
874 
otPlatRadioGetRegion(otInstance * aInstance,uint16_t * aRegionCode)875 otError otPlatRadioGetRegion(otInstance *aInstance, uint16_t *aRegionCode)
876 {
877     OT_UNUSED_VARIABLE(aInstance);
878 #if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
879     *aRegionCode = sPowerUpdater.GetRegion();
880     return OT_ERROR_NONE;
881 #else
882     return sRadioSpinel.GetRadioRegion(aRegionCode);
883 #endif
884 }
885 
886 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
otPlatRadioConfigureEnhAckProbing(otInstance * aInstance,otLinkMetrics aLinkMetrics,const otShortAddress aShortAddress,const otExtAddress * aExtAddress)887 otError otPlatRadioConfigureEnhAckProbing(otInstance          *aInstance,
888                                           otLinkMetrics        aLinkMetrics,
889                                           const otShortAddress aShortAddress,
890                                           const otExtAddress  *aExtAddress)
891 {
892     OT_UNUSED_VARIABLE(aInstance);
893 
894     return sRadioSpinel.ConfigureEnhAckProbing(aLinkMetrics, aShortAddress, *aExtAddress);
895 }
896 #endif
897 
otPlatRadioReceiveAt(otInstance * aInstance,uint8_t aChannel,uint32_t aStart,uint32_t aDuration)898 otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, uint32_t aStart, uint32_t aDuration)
899 {
900     OT_UNUSED_VARIABLE(aInstance);
901     OT_UNUSED_VARIABLE(aChannel);
902     OT_UNUSED_VARIABLE(aStart);
903     OT_UNUSED_VARIABLE(aDuration);
904     return OT_ERROR_NOT_IMPLEMENTED;
905 }
906 
otSysGetRadioSpinelMetrics(void)907 const otRadioSpinelMetrics *otSysGetRadioSpinelMetrics(void) { return sRadioSpinel.GetRadioSpinelMetrics(); }
908 
otSysGetRcpInterfaceMetrics(void)909 const otRcpInterfaceMetrics *otSysGetRcpInterfaceMetrics(void)
910 {
911     return sRadioSpinel.GetSpinelInterface().GetRcpInterfaceMetrics();
912 }
913