1 /*
2 * Copyright (c) 2016-2019, 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 #include "platform-simulation.h"
30
31 #include <errno.h>
32 #include <sys/time.h>
33
34 #include <openthread/cli.h>
35 #include <openthread/dataset.h>
36 #include <openthread/link.h>
37 #include <openthread/random_noncrypto.h>
38 #include <openthread/platform/alarm-micro.h>
39 #include <openthread/platform/alarm-milli.h>
40 #include <openthread/platform/diag.h>
41 #include <openthread/platform/radio.h>
42 #include <openthread/platform/time.h>
43
44 #include "simul_utils.h"
45 #include "lib/platform/exit_code.h"
46 #include "utils/code_utils.h"
47 #include "utils/link_metrics.h"
48 #include "utils/mac_frame.h"
49 #include "utils/soft_source_match_table.h"
50
51 enum
52 {
53 IEEE802154_ACK_LENGTH = 5,
54
55 IEEE802154_FRAME_TYPE_ACK = 2 << 0,
56
57 IEEE802154_FRAME_PENDING = 1 << 4,
58 };
59
60 enum
61 {
62 SIM_RECEIVE_SENSITIVITY = -100, // dBm
63
64 SIM_HIGH_RSSI_SAMPLE = -30, // dBm
65 SIM_LOW_RSSI_SAMPLE = -98, // dBm
66 SIM_HIGH_RSSI_PROB_INC_PER_CHANNEL = 5,
67 };
68
69 #if OPENTHREAD_SIMULATION_VIRTUAL_TIME
70 extern int sSockFd;
71 extern uint16_t sPortBase;
72 extern uint16_t sPortOffset;
73 #else
74 static utilsSocket sSocket;
75 static uint16_t sPortBase = 9000;
76 static uint16_t sPortOffset = 0;
77 #endif
78
79 static int8_t sEnergyScanResult = OT_RADIO_RSSI_INVALID;
80 static bool sEnergyScanning = false;
81 static uint32_t sEnergyScanEndTime = 0;
82
83 enum
84 {
85 SIM_RADIO_CHANNEL_MIN = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN,
86 SIM_RADIO_CHANNEL_MAX = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX,
87 };
88
89 OT_TOOL_PACKED_BEGIN
90 struct RadioMessage
91 {
92 uint8_t mChannel;
93 uint8_t mPsdu[OT_RADIO_FRAME_MAX_SIZE];
94 } OT_TOOL_PACKED_END;
95
96 static void radioTransmit(struct RadioMessage *aMessage, const struct otRadioFrame *aFrame);
97 static void radioSendMessage(otInstance *aInstance);
98 static void radioSendAck(void);
99 static void radioProcessFrame(otInstance *aInstance);
100 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
101 static uint8_t generateAckIeData(uint8_t *aLinkMetricsIeData, uint8_t aLinkMetricsIeDataLen);
102 #endif
103
104 static otRadioState sState = OT_RADIO_STATE_DISABLED;
105 static struct RadioMessage sReceiveMessage;
106 static struct RadioMessage sTransmitMessage;
107 static struct RadioMessage sAckMessage;
108 static otRadioFrame sReceiveFrame;
109 static otRadioFrame sTransmitFrame;
110 static otRadioFrame sAckFrame;
111
112 #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
113 static otRadioIeInfo sTransmitIeInfo;
114 #endif
115
116 static otPanId sPanid;
117 static bool sPromiscuous = false;
118 static bool sTxWait = false;
119 static int8_t sTxPower = 0;
120 static int8_t sCcaEdThresh = -74;
121 static int8_t sLnaGain = 0;
122 static uint16_t sRegionCode = 0;
123
124 enum
125 {
126 kMinChannel = 11,
127 kMaxChannel = 26,
128 };
129 static int8_t sChannelMaxTransmitPower[kMaxChannel - kMinChannel + 1];
130 static uint8_t sCurrentChannel = kMinChannel;
131
132 static bool sSrcMatchEnabled = false;
133
134 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
135 static uint8_t sAckIeData[OT_ACK_IE_MAX_SIZE];
136 static uint8_t sAckIeDataLength = 0;
137 #endif
138
139 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
140 static bool sRadioCoexEnabled = true;
141 #endif
142
143 otRadioCaps gRadioCaps =
144 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
145 OT_RADIO_CAPS_TRANSMIT_SEC;
146 #else
147 OT_RADIO_CAPS_NONE;
148 #endif
149
150 static otRadioContext sRadioContext;
151
152 static int8_t GetRssi(uint16_t aChannel);
153
154 #if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
155
156 static enum {
157 kFilterOff,
158 kFilterDenyList,
159 kFilterAllowList,
160 } sFilterMode = kFilterOff;
161
162 static uint8_t sFilterNodeIdsBitVector[(MAX_NETWORK_SIZE + 7) / 8];
163
FilterContainsId(uint16_t aNodeId)164 static bool FilterContainsId(uint16_t aNodeId)
165 {
166 uint16_t index = aNodeId - 1;
167
168 return (sFilterNodeIdsBitVector[index / 8] & (0x80 >> (index % 8))) != 0;
169 }
170
NodeIdFilterIsConnectable(uint16_t aNodeId)171 static bool NodeIdFilterIsConnectable(uint16_t aNodeId)
172 {
173 bool isConnectable = true;
174
175 otEXPECT_ACTION(aNodeId != gNodeId, isConnectable = false);
176
177 switch (sFilterMode)
178 {
179 case kFilterOff:
180 break;
181 case kFilterDenyList:
182 isConnectable = !FilterContainsId(aNodeId);
183 break;
184 case kFilterAllowList:
185 isConnectable = FilterContainsId(aNodeId);
186 break;
187 }
188
189 exit:
190 return isConnectable;
191 }
192
AddNodeIdToFilter(uint16_t aNodeId)193 static void AddNodeIdToFilter(uint16_t aNodeId)
194 {
195 uint16_t index = aNodeId - 1;
196
197 sFilterNodeIdsBitVector[index / 8] |= 0x80 >> (index % 8);
198 }
199
otCliOutputFormat(const char * aFmt,...)200 OT_TOOL_WEAK void otCliOutputFormat(const char *aFmt, ...) { OT_UNUSED_VARIABLE(aFmt); }
201
ProcessNodeIdFilter(void * aContext,uint8_t aArgsLength,char * aArgs[])202 otError ProcessNodeIdFilter(void *aContext, uint8_t aArgsLength, char *aArgs[])
203 {
204 OT_UNUSED_VARIABLE(aContext);
205
206 otError error = OT_ERROR_NONE;
207 bool deny = false;
208
209 if (aArgsLength == 0)
210 {
211 switch (sFilterMode)
212 {
213 case kFilterOff:
214 otCliOutputFormat("off");
215 break;
216 case kFilterDenyList:
217 otCliOutputFormat("deny-list");
218 break;
219 case kFilterAllowList:
220 otCliOutputFormat("allow-list");
221 break;
222 }
223
224 for (uint16_t nodeId = 0; nodeId <= (uint16_t)MAX_NETWORK_SIZE; nodeId++)
225 {
226 if (FilterContainsId(nodeId))
227 {
228 otCliOutputFormat(" %d", nodeId);
229 }
230 }
231
232 otCliOutputFormat("\r\n");
233 }
234 else if (!strcmp(aArgs[0], "clear"))
235 {
236 otEXPECT_ACTION(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS);
237
238 memset(sFilterNodeIdsBitVector, 0, sizeof(sFilterNodeIdsBitVector));
239 sFilterMode = kFilterOff;
240 }
241 else if ((deny = !strcmp(aArgs[0], "deny")) || !strcmp(aArgs[0], "allow"))
242 {
243 uint16_t nodeId;
244 char *endptr;
245
246 otEXPECT_ACTION(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS);
247
248 nodeId = (uint16_t)strtol(aArgs[1], &endptr, 0);
249
250 otEXPECT_ACTION(*endptr == '\0', error = OT_ERROR_INVALID_ARGS);
251 otEXPECT_ACTION(1 <= nodeId && nodeId <= MAX_NETWORK_SIZE, error = OT_ERROR_INVALID_ARGS);
252
253 otEXPECT_ACTION(sFilterMode != (deny ? kFilterAllowList : kFilterDenyList), error = OT_ERROR_INVALID_STATE);
254
255 AddNodeIdToFilter(nodeId);
256 sFilterMode = deny ? kFilterDenyList : kFilterAllowList;
257 }
258 else
259 {
260 error = OT_ERROR_INVALID_COMMAND;
261 }
262
263 exit:
264 return error;
265 }
266 #else
ProcessNodeIdFilter(void * aContext,uint8_t aArgsLength,char * aArgs[])267 otError ProcessNodeIdFilter(void *aContext, uint8_t aArgsLength, char *aArgs[])
268 {
269 OT_UNUSED_VARIABLE(aContext);
270 OT_UNUSED_VARIABLE(aArgsLength);
271 OT_UNUSED_VARIABLE(aArgs);
272
273 return OT_ERROR_NOT_IMPLEMENTED;
274 }
275 #endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
276
IsTimeAfterOrEqual(uint32_t aTimeA,uint32_t aTimeB)277 static bool IsTimeAfterOrEqual(uint32_t aTimeA, uint32_t aTimeB) { return (aTimeA - aTimeB) < (1U << 31); }
278
ReverseExtAddress(otExtAddress * aReversed,const otExtAddress * aOrigin)279 static void ReverseExtAddress(otExtAddress *aReversed, const otExtAddress *aOrigin)
280 {
281 for (size_t i = 0; i < sizeof(*aReversed); i++)
282 {
283 aReversed->m8[i] = aOrigin->m8[sizeof(*aOrigin) - 1 - i];
284 }
285 }
286
hasFramePending(const otRadioFrame * aFrame)287 static bool hasFramePending(const otRadioFrame *aFrame)
288 {
289 bool rval = false;
290 otMacAddress src;
291
292 otEXPECT_ACTION(sSrcMatchEnabled, rval = true);
293 otEXPECT(otMacFrameGetSrcAddr(aFrame, &src) == OT_ERROR_NONE);
294
295 switch (src.mType)
296 {
297 case OT_MAC_ADDRESS_TYPE_SHORT:
298 rval = utilsSoftSrcMatchShortFindEntry(src.mAddress.mShortAddress) >= 0;
299 break;
300 case OT_MAC_ADDRESS_TYPE_EXTENDED:
301 {
302 otExtAddress extAddr;
303
304 ReverseExtAddress(&extAddr, &src.mAddress.mExtAddress);
305 rval = utilsSoftSrcMatchExtFindEntry(&extAddr) >= 0;
306 break;
307 }
308 default:
309 break;
310 }
311
312 exit:
313 return rval;
314 }
315
crc16_citt(uint16_t aFcs,uint8_t aByte)316 static uint16_t crc16_citt(uint16_t aFcs, uint8_t aByte)
317 {
318 // CRC-16/CCITT, CRC-16/CCITT-TRUE, CRC-CCITT
319 // width=16 poly=0x1021 init=0x0000 refin=true refout=true xorout=0x0000 check=0x2189 name="KERMIT"
320 // http://reveng.sourceforge.net/crc-catalogue/16.htm#crc.cat.kermit
321 static const uint16_t sFcsTable[256] = {
322 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5,
323 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52,
324 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3,
325 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
326 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9,
327 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e,
328 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f,
329 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
330 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862,
331 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb,
332 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948,
333 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
334 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226,
335 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497,
336 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704,
337 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
338 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb,
339 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c,
340 0x3de3, 0x2c6a, 0x1ef1, 0x0f78};
341 return (aFcs >> 8) ^ sFcsTable[(aFcs ^ aByte) & 0xff];
342 }
343
otPlatRadioGetIeeeEui64(otInstance * aInstance,uint8_t * aIeeeEui64)344 void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64)
345 {
346 OT_UNUSED_VARIABLE(aInstance);
347
348 aIeeeEui64[0] = 0x18;
349 aIeeeEui64[1] = 0xb4;
350 aIeeeEui64[2] = 0x30;
351 aIeeeEui64[3] = 0x00;
352 aIeeeEui64[4] = (gNodeId >> 24) & 0xff;
353 aIeeeEui64[5] = (gNodeId >> 16) & 0xff;
354 aIeeeEui64[6] = (gNodeId >> 8) & 0xff;
355 aIeeeEui64[7] = gNodeId & 0xff;
356 }
357
otPlatRadioSetPanId(otInstance * aInstance,otPanId aPanid)358 void otPlatRadioSetPanId(otInstance *aInstance, otPanId aPanid)
359 {
360 OT_UNUSED_VARIABLE(aInstance);
361
362 assert(aInstance != NULL);
363
364 sPanid = aPanid;
365 utilsSoftSrcMatchSetPanId(aPanid);
366 }
367
otPlatRadioSetExtendedAddress(otInstance * aInstance,const otExtAddress * aExtAddress)368 void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aExtAddress)
369 {
370 OT_UNUSED_VARIABLE(aInstance);
371
372 assert(aInstance != NULL);
373
374 ReverseExtAddress(&sRadioContext.mExtAddress, aExtAddress);
375 }
376
otPlatRadioSetShortAddress(otInstance * aInstance,otShortAddress aShortAddress)377 void otPlatRadioSetShortAddress(otInstance *aInstance, otShortAddress aShortAddress)
378 {
379 OT_UNUSED_VARIABLE(aInstance);
380
381 assert(aInstance != NULL);
382
383 sRadioContext.mShortAddress = aShortAddress;
384 }
385
otPlatRadioSetAlternateShortAddress(otInstance * aInstance,otShortAddress aShortAddress)386 void otPlatRadioSetAlternateShortAddress(otInstance *aInstance, otShortAddress aShortAddress)
387 {
388 OT_UNUSED_VARIABLE(aInstance);
389
390 assert(aInstance != NULL);
391
392 sRadioContext.mAlternateShortAddress = aShortAddress;
393 }
394
otPlatRadioSetPromiscuous(otInstance * aInstance,bool aEnable)395 void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
396 {
397 OT_UNUSED_VARIABLE(aInstance);
398
399 assert(aInstance != NULL);
400
401 sPromiscuous = aEnable;
402 }
403
platformRadioInit(void)404 void platformRadioInit(void)
405 {
406 #if !OPENTHREAD_SIMULATION_VIRTUAL_TIME
407 parseFromEnvAsUint16("PORT_BASE", &sPortBase);
408 parseFromEnvAsUint16("PORT_OFFSET", &sPortOffset);
409 sPortOffset *= (MAX_NETWORK_SIZE + 1);
410
411 utilsInitSocket(&sSocket, sPortBase + sPortOffset);
412 #endif
413
414 sReceiveFrame.mPsdu = sReceiveMessage.mPsdu;
415 sTransmitFrame.mPsdu = sTransmitMessage.mPsdu;
416 sAckFrame.mPsdu = sAckMessage.mPsdu;
417
418 #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
419 sTransmitFrame.mInfo.mTxInfo.mIeInfo = &sTransmitIeInfo;
420 #else
421 sTransmitFrame.mInfo.mTxInfo.mIeInfo = NULL;
422 #endif
423 sAckFrame.mInfo.mTxInfo.mIeInfo = NULL;
424
425 for (size_t i = 0; i <= kMaxChannel - kMinChannel; i++)
426 {
427 sChannelMaxTransmitPower[i] = OT_RADIO_POWER_INVALID;
428 }
429
430 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
431 otLinkMetricsInit(SIM_RECEIVE_SENSITIVITY);
432 #endif
433 }
434
otPlatRadioIsEnabled(otInstance * aInstance)435 bool otPlatRadioIsEnabled(otInstance *aInstance)
436 {
437 OT_UNUSED_VARIABLE(aInstance);
438
439 return (sState != OT_RADIO_STATE_DISABLED) ? true : false;
440 }
441
otPlatRadioEnable(otInstance * aInstance)442 otError otPlatRadioEnable(otInstance *aInstance)
443 {
444 if (!otPlatRadioIsEnabled(aInstance))
445 {
446 sState = OT_RADIO_STATE_SLEEP;
447 }
448
449 return OT_ERROR_NONE;
450 }
451
otPlatRadioDisable(otInstance * aInstance)452 otError otPlatRadioDisable(otInstance *aInstance)
453 {
454 otError error = OT_ERROR_NONE;
455
456 otEXPECT(otPlatRadioIsEnabled(aInstance));
457 otEXPECT_ACTION(sState == OT_RADIO_STATE_SLEEP, error = OT_ERROR_INVALID_STATE);
458
459 sState = OT_RADIO_STATE_DISABLED;
460
461 exit:
462 return error;
463 }
464
otPlatRadioSleep(otInstance * aInstance)465 otError otPlatRadioSleep(otInstance *aInstance)
466 {
467 OT_UNUSED_VARIABLE(aInstance);
468
469 assert(aInstance != NULL);
470
471 otError error = OT_ERROR_INVALID_STATE;
472
473 if (sState == OT_RADIO_STATE_SLEEP || sState == OT_RADIO_STATE_RECEIVE)
474 {
475 error = OT_ERROR_NONE;
476 sState = OT_RADIO_STATE_SLEEP;
477 }
478
479 return error;
480 }
481
otPlatRadioReceive(otInstance * aInstance,uint8_t aChannel)482 otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
483 {
484 OT_UNUSED_VARIABLE(aInstance);
485
486 assert(aInstance != NULL);
487
488 otError error = OT_ERROR_INVALID_STATE;
489
490 if (sState != OT_RADIO_STATE_DISABLED)
491 {
492 error = OT_ERROR_NONE;
493 sState = OT_RADIO_STATE_RECEIVE;
494 sTxWait = false;
495 sReceiveFrame.mChannel = aChannel;
496 sCurrentChannel = aChannel;
497 }
498
499 return error;
500 }
501
otPlatRadioTransmit(otInstance * aInstance,otRadioFrame * aFrame)502 otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
503 {
504 OT_UNUSED_VARIABLE(aInstance);
505 OT_UNUSED_VARIABLE(aFrame);
506
507 assert(aInstance != NULL);
508 assert(aFrame != NULL);
509
510 otError error = OT_ERROR_INVALID_STATE;
511
512 if (sState == OT_RADIO_STATE_RECEIVE)
513 {
514 error = OT_ERROR_NONE;
515 sState = OT_RADIO_STATE_TRANSMIT;
516 sCurrentChannel = aFrame->mChannel;
517 }
518
519 return error;
520 }
521
otPlatRadioGetTransmitBuffer(otInstance * aInstance)522 otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
523 {
524 OT_UNUSED_VARIABLE(aInstance);
525
526 assert(aInstance != NULL);
527
528 return &sTransmitFrame;
529 }
530
otPlatRadioGetRssi(otInstance * aInstance)531 int8_t otPlatRadioGetRssi(otInstance *aInstance)
532 {
533 OT_UNUSED_VARIABLE(aInstance);
534 assert(aInstance != NULL);
535
536 return GetRssi(sReceiveFrame.mChannel);
537 }
538
GetRssi(uint16_t aChannel)539 static int8_t GetRssi(uint16_t aChannel)
540 {
541 int8_t rssi = SIM_LOW_RSSI_SAMPLE;
542 uint32_t probabilityThreshold;
543
544 otEXPECT((SIM_RADIO_CHANNEL_MIN <= aChannel) && aChannel <= (SIM_RADIO_CHANNEL_MAX));
545
546 // To emulate a simple interference model, we return either a high or
547 // a low RSSI value with a fixed probability per each channel. The
548 // probability is increased per channel by a constant.
549
550 probabilityThreshold = (aChannel - SIM_RADIO_CHANNEL_MIN) * SIM_HIGH_RSSI_PROB_INC_PER_CHANNEL;
551
552 if (otRandomNonCryptoGetUint16() < (probabilityThreshold * 0xffff / 100))
553 {
554 rssi = SIM_HIGH_RSSI_SAMPLE;
555 }
556
557 exit:
558 return rssi;
559 }
560
otPlatRadioGetCaps(otInstance * aInstance)561 otRadioCaps otPlatRadioGetCaps(otInstance *aInstance)
562 {
563 OT_UNUSED_VARIABLE(aInstance);
564
565 assert(aInstance != NULL);
566
567 return gRadioCaps;
568 }
569
otPlatRadioGetPromiscuous(otInstance * aInstance)570 bool otPlatRadioGetPromiscuous(otInstance *aInstance)
571 {
572 OT_UNUSED_VARIABLE(aInstance);
573
574 assert(aInstance != NULL);
575
576 return sPromiscuous;
577 }
578
radioReceive(otInstance * aInstance)579 static void radioReceive(otInstance *aInstance)
580 {
581 bool isTxDone = false;
582 bool isAck = otMacFrameIsAck(&sReceiveFrame);
583
584 otEXPECT(sReceiveFrame.mChannel == sReceiveMessage.mChannel);
585 otEXPECT(sState == OT_RADIO_STATE_RECEIVE || sState == OT_RADIO_STATE_TRANSMIT);
586
587 // Unable to simulate SFD, so use the rx done timestamp instead.
588 sReceiveFrame.mInfo.mRxInfo.mTimestamp = otPlatTimeGet();
589
590 if (sTxWait)
591 {
592 if (otMacFrameIsAckRequested(&sTransmitFrame))
593 {
594 uint8_t rxSeq;
595 uint8_t txSeq;
596
597 isTxDone = isAck && otMacFrameGetSequence(&sReceiveFrame, &rxSeq) == OT_ERROR_NONE &&
598 otMacFrameGetSequence(&sTransmitFrame, &txSeq) == OT_ERROR_NONE && rxSeq == txSeq;
599 }
600 #if OPENTHREAD_SIMULATION_VIRTUAL_TIME
601 // Simulate tx done when receiving the echo frame.
602 else
603 {
604 isTxDone = !isAck && sTransmitFrame.mLength == sReceiveFrame.mLength &&
605 memcmp(sTransmitFrame.mPsdu, sReceiveFrame.mPsdu, sTransmitFrame.mLength) == 0;
606 }
607 #endif
608 }
609
610 if (isTxDone)
611 {
612 sState = OT_RADIO_STATE_RECEIVE;
613 sTxWait = false;
614
615 #if OPENTHREAD_CONFIG_DIAG_ENABLE
616
617 if (otPlatDiagModeGet())
618 {
619 otPlatDiagRadioTransmitDone(aInstance, &sTransmitFrame, OT_ERROR_NONE);
620 }
621 else
622 #endif
623 {
624 otPlatRadioTxDone(aInstance, &sTransmitFrame, (isAck ? &sReceiveFrame : NULL), OT_ERROR_NONE);
625 }
626 }
627 else if (!isAck || sPromiscuous)
628 {
629 radioProcessFrame(aInstance);
630 }
631
632 exit:
633 return;
634 }
635
radioComputeCrc(struct RadioMessage * aMessage,uint16_t aLength)636 static void radioComputeCrc(struct RadioMessage *aMessage, uint16_t aLength)
637 {
638 uint16_t crc = 0;
639 uint16_t crc_offset = aLength - sizeof(uint16_t);
640
641 for (uint16_t i = 0; i < crc_offset; i++)
642 {
643 crc = crc16_citt(crc, aMessage->mPsdu[i]);
644 }
645
646 aMessage->mPsdu[crc_offset] = crc & 0xff;
647 aMessage->mPsdu[crc_offset + 1] = crc >> 8;
648 }
649
radioSendMessage(otInstance * aInstance)650 void radioSendMessage(otInstance *aInstance)
651 {
652 // This block should be called in SFD ISR
653 {
654 uint64_t sfdTxTime = otPlatTimeGet();
655
656 otEXPECT(otMacFrameProcessTxSfd(&sTransmitFrame, sfdTxTime, &sRadioContext) == OT_ERROR_NONE);
657 }
658
659 sTransmitMessage.mChannel = sTransmitFrame.mChannel;
660 otPlatRadioTxStarted(aInstance, &sTransmitFrame);
661 radioComputeCrc(&sTransmitMessage, sTransmitFrame.mLength);
662 radioTransmit(&sTransmitMessage, &sTransmitFrame);
663
664 #if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
665 sTxWait = otMacFrameIsAckRequested(&sTransmitFrame);
666
667 if (!sTxWait)
668 {
669 sState = OT_RADIO_STATE_RECEIVE;
670
671 #if OPENTHREAD_CONFIG_DIAG_ENABLE
672
673 if (otPlatDiagModeGet())
674 {
675 otPlatDiagRadioTransmitDone(aInstance, &sTransmitFrame, OT_ERROR_NONE);
676 }
677 else
678 #endif
679 {
680 otPlatRadioTxDone(aInstance, &sTransmitFrame, NULL, OT_ERROR_NONE);
681 }
682 }
683 #else
684 // Wait for echo radio in virtual time mode.
685 sTxWait = true;
686 #endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME
687 exit:
688 return;
689 }
690
platformRadioIsTransmitPending(void)691 bool platformRadioIsTransmitPending(void) { return sState == OT_RADIO_STATE_TRANSMIT && !sTxWait; }
692
693 #if OPENTHREAD_SIMULATION_VIRTUAL_TIME
platformRadioReceive(otInstance * aInstance,uint8_t * aBuf,uint16_t aBufLength)694 void platformRadioReceive(otInstance *aInstance, uint8_t *aBuf, uint16_t aBufLength)
695 {
696 assert(sizeof(sReceiveMessage) >= aBufLength);
697
698 memcpy(&sReceiveMessage, aBuf, aBufLength);
699
700 sReceiveFrame.mLength = (uint8_t)(aBufLength - 1);
701
702 radioReceive(aInstance);
703 }
704 #else
platformRadioUpdateFdSet(fd_set * aReadFdSet,fd_set * aWriteFdSet,struct timeval * aTimeout,int * aMaxFd)705 void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd)
706 {
707 if (sState != OT_RADIO_STATE_TRANSMIT || sTxWait)
708 {
709 utilsAddSocketRxFd(&sSocket, aReadFdSet, aMaxFd);
710 }
711
712 if (platformRadioIsTransmitPending())
713 {
714 utilsAddSocketTxFd(&sSocket, aWriteFdSet, aMaxFd);
715 }
716
717 if (sEnergyScanning)
718 {
719 struct timeval tv = {0, 0};
720 uint32_t now = otPlatAlarmMilliGetNow();
721
722 if (IsTimeAfterOrEqual(sEnergyScanEndTime, now))
723 {
724 uint32_t remaining = sEnergyScanEndTime - now;
725
726 tv.tv_sec = remaining / OT_MS_PER_S;
727 tv.tv_usec = (remaining % OT_MS_PER_S) * OT_US_PER_MS;
728 }
729
730 if (timercmp(&tv, aTimeout, <))
731 {
732 *aTimeout = tv;
733 }
734 }
735 }
736
737 // no need to close in virtual time mode.
platformRadioDeinit(void)738 void platformRadioDeinit(void) { utilsDeinitSocket(&sSocket); }
739 #endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME
740
platformRadioProcess(otInstance * aInstance,const fd_set * aReadFdSet,const fd_set * aWriteFdSet)741 void platformRadioProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet)
742 {
743 OT_UNUSED_VARIABLE(aReadFdSet);
744 OT_UNUSED_VARIABLE(aWriteFdSet);
745
746 #if !OPENTHREAD_SIMULATION_VIRTUAL_TIME
747 if (utilsCanSocketReceive(&sSocket, aReadFdSet))
748 {
749 uint16_t senderNodeId;
750 uint16_t len;
751
752 len = utilsReceiveFromSocket(&sSocket, &sReceiveMessage, sizeof(sReceiveMessage), &senderNodeId);
753
754 if (NodeIdFilterIsConnectable(senderNodeId))
755 {
756 sReceiveFrame.mLength = len - 1;
757 radioReceive(aInstance);
758 }
759 }
760 #endif
761
762 if (platformRadioIsTransmitPending())
763 {
764 radioSendMessage(aInstance);
765 }
766
767 if (sEnergyScanning && IsTimeAfterOrEqual(otPlatAlarmMilliGetNow(), sEnergyScanEndTime))
768 {
769 sEnergyScanning = false;
770 otPlatRadioEnergyScanDone(aInstance, sEnergyScanResult);
771 }
772 }
773
radioTransmit(struct RadioMessage * aMessage,const struct otRadioFrame * aFrame)774 void radioTransmit(struct RadioMessage *aMessage, const struct otRadioFrame *aFrame)
775 {
776 #if !OPENTHREAD_SIMULATION_VIRTUAL_TIME
777 utilsSendOverSocket(&sSocket, aMessage, aFrame->mLength + 1); // + 1 is for `mChannel`
778 #else
779 struct Event event;
780
781 event.mDelay = 1; // 1us for now
782 event.mEvent = OT_SIM_EVENT_RADIO_RECEIVED;
783 event.mDataLength = 1 + aFrame->mLength; // include channel in first byte
784 memcpy(event.mData, aMessage, event.mDataLength);
785 otSimSendEvent(&event);
786 #endif
787 }
788
radioSendAck(void)789 void radioSendAck(void)
790 {
791 if (
792 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
793 // Determine if frame pending should be set
794 ((otMacFrameIsVersion2015(&sReceiveFrame) && otMacFrameIsCommand(&sReceiveFrame)) ||
795 otMacFrameIsData(&sReceiveFrame) || otMacFrameIsDataRequest(&sReceiveFrame))
796 #else
797 otMacFrameIsDataRequest(&sReceiveFrame)
798 #endif
799 && hasFramePending(&sReceiveFrame))
800 {
801 sReceiveFrame.mInfo.mRxInfo.mAckedWithFramePending = true;
802 }
803
804 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
805 // Use enh-ack for 802.15.4-2015 frames
806 if (otMacFrameIsVersion2015(&sReceiveFrame))
807 {
808 uint8_t linkMetricsDataLen = 0;
809 uint8_t *dataPtr = NULL;
810
811 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
812 uint8_t linkMetricsData[OT_ENH_PROBING_IE_DATA_MAX_SIZE];
813 otMacAddress macAddress;
814
815 otEXPECT(otMacFrameGetSrcAddr(&sReceiveFrame, &macAddress) == OT_ERROR_NONE);
816
817 linkMetricsDataLen = otLinkMetricsEnhAckGenData(&macAddress, sReceiveFrame.mInfo.mRxInfo.mLqi,
818 sReceiveFrame.mInfo.mRxInfo.mRssi, linkMetricsData);
819
820 if (linkMetricsDataLen > 0)
821 {
822 dataPtr = linkMetricsData;
823 }
824 #endif
825
826 sAckIeDataLength = generateAckIeData(dataPtr, linkMetricsDataLen);
827
828 otEXPECT(otMacFrameGenerateEnhAck(&sReceiveFrame, sReceiveFrame.mInfo.mRxInfo.mAckedWithFramePending,
829 sAckIeData, sAckIeDataLength, &sAckFrame) == OT_ERROR_NONE);
830 otEXPECT(otMacFrameProcessTxSfd(&sAckFrame, otPlatTimeGet(), &sRadioContext) == OT_ERROR_NONE);
831 }
832 else
833 #endif
834 {
835 otMacFrameGenerateImmAck(&sReceiveFrame, sReceiveFrame.mInfo.mRxInfo.mAckedWithFramePending, &sAckFrame);
836 }
837
838 sAckMessage.mChannel = sReceiveFrame.mChannel;
839
840 radioComputeCrc(&sAckMessage, sAckFrame.mLength);
841 radioTransmit(&sAckMessage, &sAckFrame);
842
843 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
844 exit:
845 #endif
846 return;
847 }
848
radioProcessFrame(otInstance * aInstance)849 void radioProcessFrame(otInstance *aInstance)
850 {
851 otError error = OT_ERROR_NONE;
852 otMacAddress macAddress;
853 OT_UNUSED_VARIABLE(macAddress);
854
855 sReceiveFrame.mInfo.mRxInfo.mRssi = -20;
856 sReceiveFrame.mInfo.mRxInfo.mLqi = OT_RADIO_LQI_NONE;
857
858 sReceiveFrame.mInfo.mRxInfo.mAckedWithFramePending = false;
859 sReceiveFrame.mInfo.mRxInfo.mAckedWithSecEnhAck = false;
860
861 otEXPECT(sPromiscuous == false);
862
863 otEXPECT_ACTION(otMacFrameDoesAddrMatchAny(&sReceiveFrame, sPanid, sRadioContext.mShortAddress,
864 sRadioContext.mAlternateShortAddress, &sRadioContext.mExtAddress),
865 error = OT_ERROR_ABORT);
866
867 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
868 otEXPECT_ACTION(otMacFrameGetSrcAddr(&sReceiveFrame, &macAddress) == OT_ERROR_NONE, error = OT_ERROR_PARSE);
869 #endif
870
871 // generate acknowledgment
872 if (otMacFrameIsAckRequested(&sReceiveFrame))
873 {
874 radioSendAck();
875 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
876 if (otMacFrameIsSecurityEnabled(&sAckFrame))
877 {
878 sReceiveFrame.mInfo.mRxInfo.mAckedWithSecEnhAck = true;
879 sReceiveFrame.mInfo.mRxInfo.mAckFrameCounter = otMacFrameGetFrameCounter(&sAckFrame);
880 }
881 #endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
882 }
883
884 exit:
885
886 if (error != OT_ERROR_ABORT)
887 {
888 #if OPENTHREAD_CONFIG_DIAG_ENABLE
889 if (otPlatDiagModeGet())
890 {
891 otPlatDiagRadioReceiveDone(aInstance, error == OT_ERROR_NONE ? &sReceiveFrame : NULL, error);
892 }
893 else
894 #endif
895 {
896 otPlatRadioReceiveDone(aInstance, error == OT_ERROR_NONE ? &sReceiveFrame : NULL, error);
897 }
898 }
899 }
900
otPlatRadioEnableSrcMatch(otInstance * aInstance,bool aEnable)901 void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable)
902 {
903 OT_UNUSED_VARIABLE(aInstance);
904
905 assert(aInstance != NULL);
906
907 sSrcMatchEnabled = aEnable;
908 }
909
otPlatRadioEnergyScan(otInstance * aInstance,uint8_t aScanChannel,uint16_t aScanDuration)910 otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration)
911 {
912 OT_UNUSED_VARIABLE(aInstance);
913 OT_UNUSED_VARIABLE(aScanChannel);
914
915 otError error = OT_ERROR_NONE;
916
917 assert(aInstance != NULL);
918 assert(aScanChannel >= SIM_RADIO_CHANNEL_MIN && aScanChannel <= SIM_RADIO_CHANNEL_MAX);
919 assert(aScanDuration > 0);
920
921 otEXPECT_ACTION((gRadioCaps & OT_RADIO_CAPS_ENERGY_SCAN), error = OT_ERROR_NOT_IMPLEMENTED);
922 otEXPECT_ACTION(!sEnergyScanning, error = OT_ERROR_BUSY);
923
924 sEnergyScanResult = GetRssi(aScanChannel);
925 sEnergyScanning = true;
926 sEnergyScanEndTime = otPlatAlarmMilliGetNow() + aScanDuration;
927
928 exit:
929 return error;
930 }
931
otPlatRadioGetTransmitPower(otInstance * aInstance,int8_t * aPower)932 otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower)
933 {
934 OT_UNUSED_VARIABLE(aInstance);
935
936 int8_t maxPower = sChannelMaxTransmitPower[sCurrentChannel - kMinChannel];
937
938 assert(aInstance != NULL);
939
940 *aPower = sTxPower < maxPower ? sTxPower : maxPower;
941
942 return OT_ERROR_NONE;
943 }
944
otPlatRadioSetTransmitPower(otInstance * aInstance,int8_t aPower)945 otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower)
946 {
947 OT_UNUSED_VARIABLE(aInstance);
948
949 assert(aInstance != NULL);
950
951 sTxPower = aPower;
952
953 return OT_ERROR_NONE;
954 }
955
otPlatRadioGetCcaEnergyDetectThreshold(otInstance * aInstance,int8_t * aThreshold)956 otError otPlatRadioGetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t *aThreshold)
957 {
958 OT_UNUSED_VARIABLE(aInstance);
959
960 assert(aInstance != NULL);
961
962 *aThreshold = sCcaEdThresh;
963
964 return OT_ERROR_NONE;
965 }
966
otPlatRadioSetCcaEnergyDetectThreshold(otInstance * aInstance,int8_t aThreshold)967 otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t aThreshold)
968 {
969 OT_UNUSED_VARIABLE(aInstance);
970
971 assert(aInstance != NULL);
972
973 sCcaEdThresh = aThreshold;
974
975 return OT_ERROR_NONE;
976 }
977
otPlatRadioGetFemLnaGain(otInstance * aInstance,int8_t * aGain)978 otError otPlatRadioGetFemLnaGain(otInstance *aInstance, int8_t *aGain)
979 {
980 OT_UNUSED_VARIABLE(aInstance);
981
982 assert(aInstance != NULL && aGain != NULL);
983
984 *aGain = sLnaGain;
985
986 return OT_ERROR_NONE;
987 }
988
otPlatRadioSetFemLnaGain(otInstance * aInstance,int8_t aGain)989 otError otPlatRadioSetFemLnaGain(otInstance *aInstance, int8_t aGain)
990 {
991 OT_UNUSED_VARIABLE(aInstance);
992
993 assert(aInstance != NULL);
994
995 sLnaGain = aGain;
996
997 return OT_ERROR_NONE;
998 }
999
otPlatRadioGetReceiveSensitivity(otInstance * aInstance)1000 int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance)
1001 {
1002 OT_UNUSED_VARIABLE(aInstance);
1003
1004 assert(aInstance != NULL);
1005
1006 return SIM_RECEIVE_SENSITIVITY;
1007 }
1008
otPlatRadioGetState(otInstance * aInstance)1009 otRadioState otPlatRadioGetState(otInstance *aInstance)
1010 {
1011 OT_UNUSED_VARIABLE(aInstance);
1012
1013 return sState;
1014 }
1015
1016 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
otPlatRadioSetCoexEnabled(otInstance * aInstance,bool aEnabled)1017 otError otPlatRadioSetCoexEnabled(otInstance *aInstance, bool aEnabled)
1018 {
1019 OT_UNUSED_VARIABLE(aInstance);
1020
1021 assert(aInstance != NULL);
1022
1023 sRadioCoexEnabled = aEnabled;
1024 return OT_ERROR_NONE;
1025 }
1026
otPlatRadioIsCoexEnabled(otInstance * aInstance)1027 bool otPlatRadioIsCoexEnabled(otInstance *aInstance)
1028 {
1029 OT_UNUSED_VARIABLE(aInstance);
1030
1031 assert(aInstance != NULL);
1032
1033 return sRadioCoexEnabled;
1034 }
1035
otPlatRadioGetCoexMetrics(otInstance * aInstance,otRadioCoexMetrics * aCoexMetrics)1036 otError otPlatRadioGetCoexMetrics(otInstance *aInstance, otRadioCoexMetrics *aCoexMetrics)
1037 {
1038 OT_UNUSED_VARIABLE(aInstance);
1039
1040 otError error = OT_ERROR_NONE;
1041
1042 assert(aInstance != NULL);
1043 otEXPECT_ACTION(aCoexMetrics != NULL, error = OT_ERROR_INVALID_ARGS);
1044
1045 memset(aCoexMetrics, 0, sizeof(otRadioCoexMetrics));
1046
1047 aCoexMetrics->mStopped = false;
1048 aCoexMetrics->mNumGrantGlitch = 1;
1049 aCoexMetrics->mNumTxRequest = 2;
1050 aCoexMetrics->mNumTxGrantImmediate = 3;
1051 aCoexMetrics->mNumTxGrantWait = 4;
1052 aCoexMetrics->mNumTxGrantWaitActivated = 5;
1053 aCoexMetrics->mNumTxGrantWaitTimeout = 6;
1054 aCoexMetrics->mNumTxGrantDeactivatedDuringRequest = 7;
1055 aCoexMetrics->mNumTxDelayedGrant = 8;
1056 aCoexMetrics->mAvgTxRequestToGrantTime = 9;
1057 aCoexMetrics->mNumRxRequest = 10;
1058 aCoexMetrics->mNumRxGrantImmediate = 11;
1059 aCoexMetrics->mNumRxGrantWait = 12;
1060 aCoexMetrics->mNumRxGrantWaitActivated = 13;
1061 aCoexMetrics->mNumRxGrantWaitTimeout = 14;
1062 aCoexMetrics->mNumRxGrantDeactivatedDuringRequest = 15;
1063 aCoexMetrics->mNumRxDelayedGrant = 16;
1064 aCoexMetrics->mAvgRxRequestToGrantTime = 17;
1065 aCoexMetrics->mNumRxGrantNone = 18;
1066
1067 exit:
1068 return error;
1069 }
1070 #endif
1071
otPlatRadioGetNow(otInstance * aInstance)1072 uint64_t otPlatRadioGetNow(otInstance *aInstance)
1073 {
1074 OT_UNUSED_VARIABLE(aInstance);
1075
1076 return otPlatTimeGet();
1077 }
1078
1079 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
generateAckIeData(uint8_t * aLinkMetricsIeData,uint8_t aLinkMetricsIeDataLen)1080 static uint8_t generateAckIeData(uint8_t *aLinkMetricsIeData, uint8_t aLinkMetricsIeDataLen)
1081 {
1082 OT_UNUSED_VARIABLE(aLinkMetricsIeData);
1083 OT_UNUSED_VARIABLE(aLinkMetricsIeDataLen);
1084
1085 uint8_t offset = 0;
1086
1087 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1088 if (sRadioContext.mCslPeriod > 0)
1089 {
1090 offset += otMacFrameGenerateCslIeTemplate(sAckIeData);
1091 }
1092 #endif
1093
1094 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
1095 if (aLinkMetricsIeData != NULL && aLinkMetricsIeDataLen > 0)
1096 {
1097 offset += otMacFrameGenerateEnhAckProbingIe(sAckIeData, aLinkMetricsIeData, aLinkMetricsIeDataLen);
1098 }
1099 #endif
1100
1101 return offset;
1102 }
1103 #endif
1104
1105 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
otPlatRadioEnableCsl(otInstance * aInstance,uint32_t aCslPeriod,otShortAddress aShortAddr,const otExtAddress * aExtAddr)1106 otError otPlatRadioEnableCsl(otInstance *aInstance,
1107 uint32_t aCslPeriod,
1108 otShortAddress aShortAddr,
1109 const otExtAddress *aExtAddr)
1110 {
1111 OT_UNUSED_VARIABLE(aInstance);
1112 OT_UNUSED_VARIABLE(aShortAddr);
1113 OT_UNUSED_VARIABLE(aExtAddr);
1114
1115 assert(aCslPeriod < UINT16_MAX);
1116 sRadioContext.mCslPeriod = (uint16_t)aCslPeriod;
1117
1118 return OT_ERROR_NONE;
1119 }
1120
otPlatRadioResetCsl(otInstance * aInstance)1121 otError otPlatRadioResetCsl(otInstance *aInstance)
1122 {
1123 OT_UNUSED_VARIABLE(aInstance);
1124
1125 sRadioContext.mCslPeriod = 0;
1126
1127 return OT_ERROR_NONE;
1128 }
1129
otPlatRadioUpdateCslSampleTime(otInstance * aInstance,uint32_t aCslSampleTime)1130 void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime)
1131 {
1132 OT_UNUSED_VARIABLE(aInstance);
1133
1134 sRadioContext.mCslSampleTime = aCslSampleTime;
1135 }
1136
otPlatRadioGetCslAccuracy(otInstance * aInstance)1137 uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance)
1138 {
1139 OT_UNUSED_VARIABLE(aInstance);
1140
1141 return 0;
1142 }
1143 #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1144
otPlatRadioSetMacKey(otInstance * aInstance,uint8_t aKeyIdMode,uint8_t aKeyId,const otMacKeyMaterial * aPrevKey,const otMacKeyMaterial * aCurrKey,const otMacKeyMaterial * aNextKey,otRadioKeyType aKeyType)1145 void otPlatRadioSetMacKey(otInstance *aInstance,
1146 uint8_t aKeyIdMode,
1147 uint8_t aKeyId,
1148 const otMacKeyMaterial *aPrevKey,
1149 const otMacKeyMaterial *aCurrKey,
1150 const otMacKeyMaterial *aNextKey,
1151 otRadioKeyType aKeyType)
1152 {
1153 OT_UNUSED_VARIABLE(aInstance);
1154 OT_UNUSED_VARIABLE(aKeyIdMode);
1155
1156 otEXPECT(aPrevKey != NULL && aCurrKey != NULL && aNextKey != NULL);
1157
1158 sRadioContext.mKeyId = aKeyId;
1159 sRadioContext.mKeyType = aKeyType;
1160 sRadioContext.mPrevMacFrameCounter = sRadioContext.mMacFrameCounter;
1161 sRadioContext.mMacFrameCounter = 0;
1162
1163 memcpy(&sRadioContext.mPrevKey, aPrevKey, sizeof(otMacKeyMaterial));
1164 memcpy(&sRadioContext.mCurrKey, aCurrKey, sizeof(otMacKeyMaterial));
1165 memcpy(&sRadioContext.mNextKey, aNextKey, sizeof(otMacKeyMaterial));
1166
1167 exit:
1168 return;
1169 }
1170
otPlatRadioSetMacFrameCounter(otInstance * aInstance,uint32_t aMacFrameCounter)1171 void otPlatRadioSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter)
1172 {
1173 OT_UNUSED_VARIABLE(aInstance);
1174
1175 sRadioContext.mMacFrameCounter = aMacFrameCounter;
1176 }
1177
otPlatRadioSetChannelMaxTransmitPower(otInstance * aInstance,uint8_t aChannel,int8_t aMaxPower)1178 otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aChannel, int8_t aMaxPower)
1179 {
1180 OT_UNUSED_VARIABLE(aInstance);
1181
1182 otError error = OT_ERROR_NONE;
1183
1184 otEXPECT_ACTION(aChannel >= kMinChannel && aChannel <= kMaxChannel, error = OT_ERROR_INVALID_ARGS);
1185 sChannelMaxTransmitPower[aChannel - kMinChannel] = aMaxPower;
1186
1187 exit:
1188 return error;
1189 }
1190
1191 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
otPlatRadioConfigureEnhAckProbing(otInstance * aInstance,otLinkMetrics aLinkMetrics,const otShortAddress aShortAddress,const otExtAddress * aExtAddress)1192 otError otPlatRadioConfigureEnhAckProbing(otInstance *aInstance,
1193 otLinkMetrics aLinkMetrics,
1194 const otShortAddress aShortAddress,
1195 const otExtAddress *aExtAddress)
1196 {
1197 OT_UNUSED_VARIABLE(aInstance);
1198
1199 return otLinkMetricsConfigureEnhAckProbing(aShortAddress, aExtAddress, aLinkMetrics);
1200 }
1201 #endif
1202
otPlatRadioSetRegion(otInstance * aInstance,uint16_t aRegionCode)1203 otError otPlatRadioSetRegion(otInstance *aInstance, uint16_t aRegionCode)
1204 {
1205 OT_UNUSED_VARIABLE(aInstance);
1206
1207 sRegionCode = aRegionCode;
1208 return OT_ERROR_NONE;
1209 }
1210
otPlatRadioGetRegion(otInstance * aInstance,uint16_t * aRegionCode)1211 otError otPlatRadioGetRegion(otInstance *aInstance, uint16_t *aRegionCode)
1212 {
1213 OT_UNUSED_VARIABLE(aInstance);
1214 otError error = OT_ERROR_NONE;
1215
1216 otEXPECT_ACTION(aRegionCode != NULL, error = OT_ERROR_INVALID_ARGS);
1217
1218 *aRegionCode = sRegionCode;
1219 exit:
1220 return error;
1221 }
1222
parseFromEnvAsUint16(const char * aEnvName,uint16_t * aValue)1223 void parseFromEnvAsUint16(const char *aEnvName, uint16_t *aValue)
1224 {
1225 char *env = getenv(aEnvName);
1226
1227 if (env)
1228 {
1229 char *endptr;
1230
1231 *aValue = (uint16_t)strtol(env, &endptr, 0);
1232
1233 if (*endptr != '\0')
1234 {
1235 fprintf(stderr, "Invalid %s: %s\n", aEnvName, env);
1236 DieNow(OT_EXIT_FAILURE);
1237 }
1238 }
1239 }
1240