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