1 /*
2 * Copyright (c) 2021, The OpenThread Authors.
3 * Copyright (c) 2022-2025, NXP.
4 *
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the copyright holder nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 /**
32 * @file
33 * This file implements OpenThread platform driver API in openthread/platform/radio.h.
34 *
35 */
36
37 #include <openthread/platform/radio.h>
38 #include <lib/platform/exit_code.h>
39 #include <lib/spinel/radio_spinel.hpp>
40 #include <lib/spinel/spinel.h>
41 #include <lib/url/url.hpp>
42 #include "hdlc_interface.hpp"
43
44 #include <zephyr/logging/log.h>
45 LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_OPENTHREAD_PLATFORM_LOG_LEVEL);
46 #include <zephyr/kernel.h>
47 #include <zephyr/net/net_pkt.h>
48 #include <openthread-system.h>
49 #include <stdalign.h>
50 #include <common/new.hpp>
51
52 #if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR)
53 #include <openthread/nat64.h>
54 #endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR */
55
56 #define PKT_IS_IPv4(_p) ((NET_IPV6_HDR(_p)->vtc & 0xf0) == 0x40)
57
58 enum pending_events {
59 PENDING_EVENT_FRAME_TO_SEND, /* There is a tx frame to send */
60 PENDING_EVENT_COUNT /* Keep last */
61 };
62
63 ATOMIC_DEFINE(pending_events, PENDING_EVENT_COUNT);
64 K_FIFO_DEFINE(tx_pkt_fifo);
65
66 static ot::Spinel::RadioSpinel *psRadioSpinel;
67 static ot::Url::Url *psRadioUrl;
68 static ot::Hdlc::HdlcInterface *pSpinelInterface;
69 static ot::Spinel::SpinelDriver *psSpinelDriver;
70
71 alignas(ot::Spinel::RadioSpinel) static uint8_t gRadioBuf[sizeof(ot::Spinel::RadioSpinel)];
72 alignas(ot::Url::Url) static uint8_t gUrlBuf[sizeof(ot::Url::Url)];
73 alignas(ot::Hdlc::HdlcInterface) static uint8_t gIfaceBuf[sizeof(ot::Hdlc::HdlcInterface)];
74 alignas(ot::Spinel::SpinelDriver) static uint8_t gDriverBuf[sizeof(ot::Spinel::SpinelDriver)];
75
76 static const otRadioCaps sRequiredRadioCaps =
77 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
78 OT_RADIO_CAPS_TRANSMIT_SEC | OT_RADIO_CAPS_TRANSMIT_TIMING |
79 #endif
80 OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_TRANSMIT_RETRIES | OT_RADIO_CAPS_CSMA_BACKOFF;
81
is_pending_event_set(enum pending_events event)82 static inline bool is_pending_event_set(enum pending_events event)
83 {
84 return atomic_test_bit(pending_events, event);
85 }
86
set_pending_event(enum pending_events event)87 static void set_pending_event(enum pending_events event)
88 {
89 atomic_set_bit(pending_events, event);
90 otSysEventSignalPending();
91 }
92
reset_pending_event(enum pending_events event)93 static void reset_pending_event(enum pending_events event)
94 {
95 atomic_clear_bit(pending_events, event);
96 }
97
openthread_handle_frame_to_send(otInstance * instance,struct net_pkt * pkt)98 static void openthread_handle_frame_to_send(otInstance *instance, struct net_pkt *pkt)
99 {
100 struct net_buf *buf;
101 otMessage *message;
102 otMessageSettings settings;
103 bool is_ip4 = PKT_IS_IPv4(pkt);
104
105 NET_DBG("Sending %s packet to ot stack", is_ip4 ? "IPv4" : "IPv6");
106
107 settings.mPriority = OT_MESSAGE_PRIORITY_NORMAL;
108 settings.mLinkSecurityEnabled = true;
109 #if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR)
110 if (is_ip4) {
111 message = otIp4NewMessage(instance, &settings);
112 } else {
113 #endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR*/
114 message = otIp6NewMessage(instance, &settings);
115 #if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR)
116 }
117 #endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR */
118 if (message == NULL) {
119 goto exit;
120 }
121
122 for (buf = pkt->buffer; buf; buf = buf->frags) {
123 if (otMessageAppend(message, buf->data, buf->len) != OT_ERROR_NONE) {
124 NET_ERR("Error while appending to otMessage");
125 otMessageFree(message);
126 goto exit;
127 }
128 }
129 #if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR)
130 if (is_ip4) {
131 if (otNat64Send(instance, message) != OT_ERROR_NONE) {
132 NET_ERR("Error while calling otNat64Send");
133 goto exit;
134 }
135 } else {
136 #endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR*/
137
138 if (otIp6Send(instance, message) != OT_ERROR_NONE) {
139 NET_ERR("Error while calling otIp6Send");
140 goto exit;
141 }
142 #if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR)
143 }
144 #endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR */
145
146 exit:
147 net_pkt_unref(pkt);
148 }
149
otPlatRadioGetIeeeEui64(otInstance * aInstance,uint8_t * aIeeeEui64)150 void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64)
151 {
152 OT_UNUSED_VARIABLE(aInstance);
153 SuccessOrDie(psRadioSpinel->GetIeeeEui64(aIeeeEui64));
154 }
155
otPlatRadioSetPanId(otInstance * aInstance,uint16_t panid)156 void otPlatRadioSetPanId(otInstance *aInstance, uint16_t panid)
157 {
158 OT_UNUSED_VARIABLE(aInstance);
159 SuccessOrDie(psRadioSpinel->SetPanId(panid));
160 }
161
otPlatRadioSetExtendedAddress(otInstance * aInstance,const otExtAddress * aAddress)162 void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aAddress)
163 {
164 OT_UNUSED_VARIABLE(aInstance);
165 otExtAddress addr;
166
167 for (size_t i = 0; i < sizeof(addr); i++) {
168 addr.m8[i] = aAddress->m8[sizeof(addr) - 1 - i];
169 }
170
171 SuccessOrDie(psRadioSpinel->SetExtendedAddress(addr));
172 }
173
otPlatRadioSetShortAddress(otInstance * aInstance,uint16_t aAddress)174 void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aAddress)
175 {
176 OT_UNUSED_VARIABLE(aInstance);
177 SuccessOrDie(psRadioSpinel->SetShortAddress(aAddress));
178 }
179
otPlatRadioSetPromiscuous(otInstance * aInstance,bool aEnable)180 void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
181 {
182 OT_UNUSED_VARIABLE(aInstance);
183 SuccessOrDie(psRadioSpinel->SetPromiscuous(aEnable));
184 }
185
otPlatRadioIsEnabled(otInstance * aInstance)186 bool otPlatRadioIsEnabled(otInstance *aInstance)
187 {
188 OT_UNUSED_VARIABLE(aInstance);
189 return psRadioSpinel->IsEnabled();
190 }
191
otPlatRadioEnable(otInstance * aInstance)192 otError otPlatRadioEnable(otInstance *aInstance)
193 {
194 return psRadioSpinel->Enable(aInstance);
195 }
196
otPlatRadioDisable(otInstance * aInstance)197 otError otPlatRadioDisable(otInstance *aInstance)
198 {
199 OT_UNUSED_VARIABLE(aInstance);
200 return psRadioSpinel->Disable();
201 }
202
otPlatRadioSleep(otInstance * aInstance)203 otError otPlatRadioSleep(otInstance *aInstance)
204 {
205 OT_UNUSED_VARIABLE(aInstance);
206 return psRadioSpinel->Sleep();
207 }
208
otPlatRadioReceive(otInstance * aInstance,uint8_t aChannel)209 otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
210 {
211 OT_UNUSED_VARIABLE(aInstance);
212 return psRadioSpinel->Receive(aChannel);
213 }
214
otPlatRadioTransmit(otInstance * aInstance,otRadioFrame * aFrame)215 otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
216 {
217 OT_UNUSED_VARIABLE(aInstance);
218 return psRadioSpinel->Transmit(*aFrame);
219 }
220
otPlatRadioGetTransmitBuffer(otInstance * aInstance)221 otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
222 {
223 OT_UNUSED_VARIABLE(aInstance);
224 return &psRadioSpinel->GetTransmitFrame();
225 }
226
otPlatRadioGetRssi(otInstance * aInstance)227 int8_t otPlatRadioGetRssi(otInstance *aInstance)
228 {
229 OT_UNUSED_VARIABLE(aInstance);
230 return psRadioSpinel->GetRssi();
231 }
232
otPlatRadioGetCaps(otInstance * aInstance)233 otRadioCaps otPlatRadioGetCaps(otInstance *aInstance)
234 {
235 OT_UNUSED_VARIABLE(aInstance);
236 return psRadioSpinel->GetRadioCaps();
237 }
238
otPlatRadioGetVersionString(otInstance * aInstance)239 const char *otPlatRadioGetVersionString(otInstance *aInstance)
240 {
241 OT_UNUSED_VARIABLE(aInstance);
242 return psRadioSpinel->GetVersion();
243 }
244
otPlatRadioGetPromiscuous(otInstance * aInstance)245 bool otPlatRadioGetPromiscuous(otInstance *aInstance)
246 {
247 OT_UNUSED_VARIABLE(aInstance);
248 return psRadioSpinel->IsPromiscuous();
249 }
250
otPlatRadioEnableSrcMatch(otInstance * aInstance,bool aEnable)251 void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable)
252 {
253 OT_UNUSED_VARIABLE(aInstance);
254 SuccessOrDie(psRadioSpinel->EnableSrcMatch(aEnable));
255 }
256
otPlatRadioAddSrcMatchShortEntry(otInstance * aInstance,uint16_t aShortAddress)257 otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, uint16_t aShortAddress)
258 {
259 OT_UNUSED_VARIABLE(aInstance);
260 return psRadioSpinel->AddSrcMatchShortEntry(aShortAddress);
261 }
262
otPlatRadioAddSrcMatchExtEntry(otInstance * aInstance,const otExtAddress * aExtAddress)263 otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress)
264 {
265 OT_UNUSED_VARIABLE(aInstance);
266 otExtAddress addr;
267
268 for (size_t i = 0; i < sizeof(addr); i++) {
269 addr.m8[i] = aExtAddress->m8[sizeof(addr) - 1 - i];
270 }
271
272 return psRadioSpinel->AddSrcMatchExtEntry(addr);
273 }
274
otPlatRadioClearSrcMatchShortEntry(otInstance * aInstance,uint16_t aShortAddress)275 otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, uint16_t aShortAddress)
276 {
277 OT_UNUSED_VARIABLE(aInstance);
278 return psRadioSpinel->ClearSrcMatchShortEntry(aShortAddress);
279 }
280
otPlatRadioClearSrcMatchExtEntry(otInstance * aInstance,const otExtAddress * aExtAddress)281 otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress)
282 {
283 OT_UNUSED_VARIABLE(aInstance);
284 otExtAddress addr;
285
286 for (size_t i = 0; i < sizeof(addr); i++) {
287 addr.m8[i] = aExtAddress->m8[sizeof(addr) - 1 - i];
288 }
289
290 return psRadioSpinel->ClearSrcMatchExtEntry(addr);
291 }
292
otPlatRadioClearSrcMatchShortEntries(otInstance * aInstance)293 void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance)
294 {
295 OT_UNUSED_VARIABLE(aInstance);
296 SuccessOrDie(psRadioSpinel->ClearSrcMatchShortEntries());
297 }
298
otPlatRadioClearSrcMatchExtEntries(otInstance * aInstance)299 void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance)
300 {
301 OT_UNUSED_VARIABLE(aInstance);
302 SuccessOrDie(psRadioSpinel->ClearSrcMatchExtEntries());
303 }
304
otPlatRadioEnergyScan(otInstance * aInstance,uint8_t aScanChannel,uint16_t aScanDuration)305 otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration)
306 {
307 OT_UNUSED_VARIABLE(aInstance);
308 return psRadioSpinel->EnergyScan(aScanChannel, aScanDuration);
309 }
310
otPlatRadioGetTransmitPower(otInstance * aInstance,int8_t * aPower)311 otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower)
312 {
313 otError error;
314
315 OT_UNUSED_VARIABLE(aInstance);
316 VerifyOrExit(aPower != NULL, error = OT_ERROR_INVALID_ARGS);
317 error = psRadioSpinel->GetTransmitPower(*aPower);
318
319 exit:
320 return error;
321 }
322
otPlatRadioSetTransmitPower(otInstance * aInstance,int8_t aPower)323 otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower)
324 {
325 OT_UNUSED_VARIABLE(aInstance);
326 return psRadioSpinel->SetTransmitPower(aPower);
327 }
328
otPlatRadioGetCcaEnergyDetectThreshold(otInstance * aInstance,int8_t * aThreshold)329 otError otPlatRadioGetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t *aThreshold)
330 {
331 otError error;
332
333 OT_UNUSED_VARIABLE(aInstance);
334 VerifyOrExit(aThreshold != NULL, error = OT_ERROR_INVALID_ARGS);
335 error = psRadioSpinel->GetCcaEnergyDetectThreshold(*aThreshold);
336
337 exit:
338 return error;
339 }
340
otPlatRadioSetCcaEnergyDetectThreshold(otInstance * aInstance,int8_t aThreshold)341 otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t aThreshold)
342 {
343 OT_UNUSED_VARIABLE(aInstance);
344 return psRadioSpinel->SetCcaEnergyDetectThreshold(aThreshold);
345 }
346
otPlatRadioGetReceiveSensitivity(otInstance * aInstance)347 int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance)
348 {
349 OT_UNUSED_VARIABLE(aInstance);
350 return psRadioSpinel->GetReceiveSensitivity();
351 }
352
353 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
otPlatRadioSetCoexEnabled(otInstance * aInstance,bool aEnabled)354 otError otPlatRadioSetCoexEnabled(otInstance *aInstance, bool aEnabled)
355 {
356 OT_UNUSED_VARIABLE(aInstance);
357 return psRadioSpinel->SetCoexEnabled(aEnabled);
358 }
359
otPlatRadioIsCoexEnabled(otInstance * aInstance)360 bool otPlatRadioIsCoexEnabled(otInstance *aInstance)
361 {
362 OT_UNUSED_VARIABLE(aInstance);
363 return psRadioSpinel->IsCoexEnabled();
364 }
365
otPlatRadioGetCoexMetrics(otInstance * aInstance,otRadioCoexMetrics * aCoexMetrics)366 otError otPlatRadioGetCoexMetrics(otInstance *aInstance, otRadioCoexMetrics *aCoexMetrics)
367 {
368 OT_UNUSED_VARIABLE(aInstance);
369
370 otError error = OT_ERROR_NONE;
371
372 VerifyOrExit(aCoexMetrics != NULL, error = OT_ERROR_INVALID_ARGS);
373
374 error = psRadioSpinel->GetCoexMetrics(*aCoexMetrics);
375
376 exit:
377 return error;
378 }
379 #endif
380
381 #if OPENTHREAD_CONFIG_DIAG_ENABLE
otPlatDiagSetOutputCallback(otInstance * aInstance,otPlatDiagOutputCallback aCallback,void * aContext)382 void otPlatDiagSetOutputCallback(otInstance *aInstance, otPlatDiagOutputCallback aCallback, void *aContext)
383 {
384 OT_UNUSED_VARIABLE(aInstance);
385
386 psRadioSpinel->SetDiagOutputCallback(aCallback, aContext);
387 }
388
otPlatDiagProcess(otInstance * aInstance,int argc,char * argv[])389 otError otPlatDiagProcess(otInstance *aInstance, int argc, char *argv[])
390 {
391 /* Deliver the platform specific diags commands to radio only ncp */
392 OT_UNUSED_VARIABLE(aInstance);
393 char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE] = {'\0'};
394 char *cur = cmd;
395 char *end = cmd + sizeof(cmd);
396
397 for (int index = 0; index < argc; index++) {
398 cur += snprintf(cur, static_cast<size_t>(end - cur), "%s ", argv[index]);
399 }
400
401 return psRadioSpinel->PlatDiagProcess(cmd);
402 }
403
otPlatDiagModeSet(bool aMode)404 void otPlatDiagModeSet(bool aMode)
405 {
406 SuccessOrExit(psRadioSpinel->PlatDiagProcess(aMode ? "start" : "stop"));
407 psRadioSpinel->SetDiagEnabled(aMode);
408
409 exit:
410 return;
411 }
412
otPlatDiagModeGet(void)413 bool otPlatDiagModeGet(void)
414 {
415 return psRadioSpinel->IsDiagEnabled();
416 }
417
otPlatDiagTxPowerSet(int8_t aTxPower)418 void otPlatDiagTxPowerSet(int8_t aTxPower)
419 {
420 char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE];
421
422 snprintf(cmd, sizeof(cmd), "power %d", aTxPower);
423 SuccessOrExit(psRadioSpinel->PlatDiagProcess(cmd));
424
425 exit:
426 return;
427 }
428
otPlatDiagChannelSet(uint8_t aChannel)429 void otPlatDiagChannelSet(uint8_t aChannel)
430 {
431 char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE];
432
433 snprintf(cmd, sizeof(cmd), "channel %d", aChannel);
434 SuccessOrExit(psRadioSpinel->PlatDiagProcess(cmd));
435
436 exit:
437 return;
438 }
439
otPlatDiagRadioReceived(otInstance * aInstance,otRadioFrame * aFrame,otError aError)440 void otPlatDiagRadioReceived(otInstance *aInstance, otRadioFrame *aFrame, otError aError)
441 {
442 OT_UNUSED_VARIABLE(aInstance);
443 OT_UNUSED_VARIABLE(aFrame);
444 OT_UNUSED_VARIABLE(aError);
445 }
446
otPlatDiagAlarmCallback(otInstance * aInstance)447 void otPlatDiagAlarmCallback(otInstance *aInstance)
448 {
449 OT_UNUSED_VARIABLE(aInstance);
450 }
451 #endif /* OPENTHREAD_CONFIG_DIAG_ENABLE */
452
otPlatRadioGetSupportedChannelMask(otInstance * aInstance)453 uint32_t otPlatRadioGetSupportedChannelMask(otInstance *aInstance)
454 {
455 OT_UNUSED_VARIABLE(aInstance);
456 return psRadioSpinel->GetRadioChannelMask(false);
457 }
458
otPlatRadioGetPreferredChannelMask(otInstance * aInstance)459 uint32_t otPlatRadioGetPreferredChannelMask(otInstance *aInstance)
460 {
461 OT_UNUSED_VARIABLE(aInstance);
462 return psRadioSpinel->GetRadioChannelMask(true);
463 }
464
otPlatRadioGetState(otInstance * aInstance)465 otRadioState otPlatRadioGetState(otInstance *aInstance)
466 {
467 OT_UNUSED_VARIABLE(aInstance);
468 return psRadioSpinel->GetState();
469 }
470
otPlatRadioSetMacKey(otInstance * aInstance,uint8_t aKeyIdMode,uint8_t aKeyId,const otMacKeyMaterial * aPrevKey,const otMacKeyMaterial * aCurrKey,const otMacKeyMaterial * aNextKey,otRadioKeyType aKeyType)471 void otPlatRadioSetMacKey(otInstance *aInstance, uint8_t aKeyIdMode, uint8_t aKeyId,
472 const otMacKeyMaterial *aPrevKey, const otMacKeyMaterial *aCurrKey,
473 const otMacKeyMaterial *aNextKey, otRadioKeyType aKeyType)
474 {
475 SuccessOrDie(psRadioSpinel->SetMacKey(aKeyIdMode, aKeyId, aPrevKey, aCurrKey, aNextKey));
476 OT_UNUSED_VARIABLE(aInstance);
477 OT_UNUSED_VARIABLE(aKeyType);
478 }
479
otPlatRadioSetMacFrameCounter(otInstance * aInstance,uint32_t aMacFrameCounter)480 void otPlatRadioSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter)
481 {
482 SuccessOrDie(psRadioSpinel->SetMacFrameCounter(aMacFrameCounter, false));
483 OT_UNUSED_VARIABLE(aInstance);
484 }
485
otPlatRadioSetMacFrameCounterIfLarger(otInstance * aInstance,uint32_t aMacFrameCounter)486 void otPlatRadioSetMacFrameCounterIfLarger(otInstance *aInstance, uint32_t aMacFrameCounter)
487 {
488 SuccessOrDie(psRadioSpinel->SetMacFrameCounter(aMacFrameCounter, true));
489 OT_UNUSED_VARIABLE(aInstance);
490 }
491
otPlatRadioGetNow(otInstance * aInstance)492 uint64_t otPlatRadioGetNow(otInstance *aInstance)
493 {
494 OT_UNUSED_VARIABLE(aInstance);
495 return psRadioSpinel->GetNow();
496 }
497
498 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
otPlatRadioGetCslAccuracy(otInstance * aInstance)499 uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance)
500 {
501 OT_UNUSED_VARIABLE(aInstance);
502
503 return psRadioSpinel->GetCslAccuracy();
504 }
505 #endif
506
507 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
otPlatRadioGetCslUncertainty(otInstance * aInstance)508 uint8_t otPlatRadioGetCslUncertainty(otInstance *aInstance)
509 {
510 OT_UNUSED_VARIABLE(aInstance);
511
512 return psRadioSpinel->GetCslUncertainty();
513 }
514 #endif
515
otPlatRadioSetChannelMaxTransmitPower(otInstance * aInstance,uint8_t aChannel,int8_t aMaxPower)516 otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aChannel,
517 int8_t aMaxPower)
518 {
519 OT_UNUSED_VARIABLE(aInstance);
520 return psRadioSpinel->SetChannelMaxTransmitPower(aChannel, aMaxPower);
521 }
522
otPlatRadioSetRegion(otInstance * aInstance,uint16_t aRegionCode)523 otError otPlatRadioSetRegion(otInstance *aInstance, uint16_t aRegionCode)
524 {
525 OT_UNUSED_VARIABLE(aInstance);
526 return psRadioSpinel->SetRadioRegion(aRegionCode);
527 }
528
otPlatRadioGetRegion(otInstance * aInstance,uint16_t * aRegionCode)529 otError otPlatRadioGetRegion(otInstance *aInstance, uint16_t *aRegionCode)
530 {
531 OT_UNUSED_VARIABLE(aInstance);
532 return psRadioSpinel->GetRadioRegion(aRegionCode);
533 }
534
535 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
otPlatRadioConfigureEnhAckProbing(otInstance * aInstance,otLinkMetrics aLinkMetrics,const otShortAddress aShortAddress,const otExtAddress * aExtAddress)536 otError otPlatRadioConfigureEnhAckProbing(otInstance *aInstance, otLinkMetrics aLinkMetrics,
537 const otShortAddress aShortAddress,
538 const otExtAddress *aExtAddress)
539 {
540 OT_UNUSED_VARIABLE(aInstance);
541
542 return psRadioSpinel->ConfigureEnhAckProbing(aLinkMetrics, aShortAddress, *aExtAddress);
543 }
544 #endif
545
otPlatRadioReceiveAt(otInstance * aInstance,uint8_t aChannel,uint32_t aStart,uint32_t aDuration)546 otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, uint32_t aStart,
547 uint32_t aDuration)
548 {
549 OT_UNUSED_VARIABLE(aInstance);
550 OT_UNUSED_VARIABLE(aChannel);
551 OT_UNUSED_VARIABLE(aStart);
552 OT_UNUSED_VARIABLE(aDuration);
553 return OT_ERROR_NOT_IMPLEMENTED;
554 }
555
platformRadioInit(void)556 extern "C" void platformRadioInit(void)
557 {
558 spinel_iid_t iidList[ot::Spinel::kSpinelHeaderMaxNumIid];
559 struct ot::Spinel::RadioSpinelCallbacks callbacks;
560
561 iidList[0] = 0;
562
563 psRadioSpinel = new(gRadioBuf) ot::Spinel::RadioSpinel();
564 psSpinelDriver = new(gDriverBuf) ot::Spinel::SpinelDriver();
565
566 psRadioUrl = new(gUrlBuf) ot::Url::Url();
567 pSpinelInterface = new(gIfaceBuf) ot::Hdlc::HdlcInterface(*psRadioUrl);
568
569 OT_UNUSED_VARIABLE(psSpinelDriver->Init(*pSpinelInterface, true /* aSoftwareReset */,
570 iidList, OT_ARRAY_LENGTH(iidList)));
571
572 memset(&callbacks, 0, sizeof(callbacks));
573 #if OPENTHREAD_CONFIG_DIAG_ENABLE
574 callbacks.mDiagReceiveDone = otPlatDiagRadioReceiveDone;
575 callbacks.mDiagTransmitDone = otPlatDiagRadioTransmitDone;
576 #endif /* OPENTHREAD_CONFIG_DIAG_ENABLE */
577 callbacks.mEnergyScanDone = otPlatRadioEnergyScanDone;
578 callbacks.mReceiveDone = otPlatRadioReceiveDone;
579 callbacks.mTransmitDone = otPlatRadioTxDone;
580 callbacks.mTxStarted = otPlatRadioTxStarted;
581
582 psRadioSpinel->SetCallbacks(callbacks);
583
584 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 && \
585 OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
586 bool aEnableRcpTimeSync = true;
587 #else
588 bool aEnableRcpTimeSync = false;
589 #endif
590 psRadioSpinel->Init(false /*aSkipRcpCompatibilityCheck*/, true /*aSoftwareReset*/,
591 psSpinelDriver, sRequiredRadioCaps, aEnableRcpTimeSync);
592 psRadioSpinel->SetTimeSyncState(true);
593 }
594
platformRadioDeinit(void)595 extern "C" void platformRadioDeinit(void)
596 {
597 psRadioSpinel->Deinit();
598 psSpinelDriver->Deinit();
599 }
600
notify_new_rx_frame(struct net_pkt * pkt)601 extern "C" int notify_new_rx_frame(struct net_pkt *pkt)
602 {
603 /* The RX frame is handled by Openthread stack */
604 net_pkt_unref(pkt);
605
606 return 0;
607 }
608
notify_new_tx_frame(struct net_pkt * pkt)609 extern "C" int notify_new_tx_frame(struct net_pkt *pkt)
610 {
611 k_fifo_put(&tx_pkt_fifo, pkt);
612 set_pending_event(PENDING_EVENT_FRAME_TO_SEND);
613
614 return 0;
615 }
616
platformRadioProcess(otInstance * aInstance)617 extern "C" void platformRadioProcess(otInstance *aInstance)
618 {
619 if (is_pending_event_set(PENDING_EVENT_FRAME_TO_SEND)) {
620 struct net_pkt *evt_pkt;
621
622 reset_pending_event(PENDING_EVENT_FRAME_TO_SEND);
623 while ((evt_pkt = (struct net_pkt *)k_fifo_get(&tx_pkt_fifo, K_NO_WAIT)) != NULL) {
624 openthread_handle_frame_to_send(aInstance, evt_pkt);
625 }
626 }
627
628 psSpinelDriver->Process(aInstance);
629 psRadioSpinel->Process(aInstance);
630 }
631