1 /*
2 * Copyright (c) 2021-2024, Texas Instruments Incorporated
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
7 * are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of Texas Instruments Incorporated nor the names of
17 * its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 /*
33 * ======== ieee.c ========
34 */
35
36 #ifndef DeviceFamily_CC27XX
37
38 #include <stdint.h>
39 #include <stdlib.h>
40
41 #include <ti/log/Log.h>
42
43 #include <ti/drivers/dpl/HwiP.h>
44
45 #include <ti/drivers/rcl/RCL_Command.h>
46 #include <ti/drivers/rcl/RCL_Buffer.h>
47 #include <ti/drivers/rcl/RCL_Scheduler.h>
48 #include <ti/drivers/rcl/RCL_Profiling.h>
49
50 #include <ti/drivers/rcl/hal/hal.h>
51 #include <ti/drivers/rcl/commands/ieee.h>
52
53 #include <ti/devices/DeviceFamily.h>
54 #include DeviceFamily_constructPath(inc/hw_lrfdtxf.h)
55 #include DeviceFamily_constructPath(inc/hw_lrfdrxf.h)
56 #include DeviceFamily_constructPath(inc/hw_lrfddbell.h)
57 #include DeviceFamily_constructPath(inc/hw_lrfdpbe.h)
58 #include DeviceFamily_constructPath(inc/hw_lrfdpbe32.h)
59 #include DeviceFamily_constructPath(inc/hw_lrfdmdm.h)
60 #include DeviceFamily_constructPath(inc/hw_lrfdrfe.h)
61 #include DeviceFamily_constructPath(inc/pbe_ieee_ram_regs.h)
62 #include DeviceFamily_constructPath(inc/pbe_common_ram_regs.h)
63 #include DeviceFamily_constructPath(inc/pbe_ieee_regdef_regs.h)
64
65 /** Polynomial to use for PRBS9 data */
66 #define RCL_HANDLER_IEEE_PRBS9_POLY 0x08800000
67 /** Polynomial to use for PRBS15 data */
68 #define RCL_HANDLER_IEEE_PRBS15_POLY 0x80020000
69 /** Polynomial to use for PRBS32 data */
70 #define RCL_HANDLER_IEEE_PRBS32_POLY 0x00400007
71 #define RCL_HANDLER_IEEE_PRBS_INIT 0x0000001F
72
73 #define RCL_HANDLER_IEEE_RESTORE_NONE 0x0000
74 #define RCL_HANDLER_IEEE_RESTORE_MODCTRL 0x0001
75 #define RCL_HANDLER_IEEE_RESTORE_WHITENING 0x0002
76 #define RCL_HANDLER_IEEE_RESTORE_SFD 0x0004
77
78 /* Byte length of IEEE 802.15.4 frame fields */
79 #define IEEE_PHY_HDR_LEN 1
80 #define IEEE_MAC_FCF_LEN 2
81
82 /* Timing constants */
83 /* Backoff period for the O-QPSK PHY per the 802.15.4 standard */
84 #define IEEE_BACKOFF_PERIOD RCL_SCHEDULER_SYSTIM_US(320)
85 /* Necessary margin to start CCA */
86 #define IEEE_CCA_START_TIME_MARGIN RCL_SCHEDULER_SYSTIM_US(192)
87
88 typedef enum
89 {
90 txStateNoTx,
91 txStateNewAction,
92 txStateSetupCca,
93 txStateWaitForCca,
94 txStateStopToSetTx,
95 txStateSetTxTime,
96 txStateWaitForTx,
97 txStateTx,
98 txStateTxRx,
99 txStateTxRxAck,
100 txStateCheckAck,
101 txStateWaitForCmdEnd,
102 txStateFinished,
103 } RCL_Handler_Ieee_TxState;
104
105 typedef enum
106 {
107 rxStateNoRx,
108 rxStateWaitForStart,
109 rxStateRunning
110 } RCL_Handler_Ieee_RxState;
111
112 typedef enum
113 {
114 noEvent,
115 customEvent,
116 customHardStop,
117 } RCL_Handler_Ieee_EventType;
118
119 struct
120 {
121 struct {
122 uint16_t txFifoSize;
123 uint16_t rxFifoSize;
124 RCL_CommandStatus endStatus;
125 bool activeUpdate;
126 bool apiHardStopPending;
127 RCL_MultiBuffer *curBuffer;
128 uint32_t nextEventTime;
129 RCL_Handler_Ieee_EventType eventTimeType;
130 } common;
131 union {
132 struct {
133 uint16_t restoreOpt;
134 uint16_t storedWhitenInit;
135 uint32_t storedWhitenPoly;
136 uint32_t storedMdmSyncA;
137 } txTest;
138 struct {
139 RCL_Handler_Ieee_TxState txState;
140 RCL_Handler_Ieee_RxState rxState;
141 uint8_t ccaContentionWindow;
142 bool waitingForValidRssi;
143 bool allowTxDelay;
144 bool alwaysStoreAck;
145 uint8_t expSeqNo;
146 uint32_t ccaTxStartTime;
147 RCL_StopType txActionStop;
148 } rxTx;
149 };
150 } ieeeHandlerState;
151
152 static void RCL_Handler_Ieee_updateRxCurBufferAndFifo(List_List *rxBuffers);
153 static RCL_CommandStatus RCL_Handler_Ieee_findPbeErrorEndStatus(uint16_t pbeEndStatus);
154 static uint32_t RCL_Handler_Ieee_maskEventsByFifoConf(uint32_t mask, uint16_t fifoConfVal, bool activeUpdate);
155 static void RCL_Handler_Ieee_updateStats(RCL_StatsIeee *stats, uint32_t startTime);
156 static bool RCL_Handler_Ieee_initStats(RCL_StatsIeee *stats, uint32_t startTime);
157 static bool RCL_Handler_Ieee_setCustomEventTime(uint32_t eventTime, uint32_t timeMargin, bool hardStop);
158 static bool RCL_Handler_Ieee_restoreStopTime(void);
159
160 /*
161 * ======== RCL_Handler_Ieee_RxTx ========
162 */
RCL_Handler_Ieee_RxTx(RCL_Command * cmd,LRF_Events lrfEvents,RCL_Events rclEventsIn)163 RCL_Events RCL_Handler_Ieee_RxTx(RCL_Command *cmd, LRF_Events lrfEvents, RCL_Events rclEventsIn)
164 {
165 RCL_CmdIeeeRxTx *ieeeCmd = (RCL_CmdIeeeRxTx *) cmd;
166 RCL_Events rclEvents = {.value = 0};
167 bool startTx = false;
168 bool doCca = false;
169 bool restartRx = false;
170
171 if (rclEventsIn.setup != 0)
172 {
173 uint32_t earliestStartTime;
174
175 ieeeHandlerState.rxTx.rxState = rxStateNoRx;
176 ieeeHandlerState.rxTx.txState = txStateNoTx;
177 ieeeHandlerState.common.eventTimeType = noEvent;
178 ieeeHandlerState.common.apiHardStopPending = false;
179 ieeeHandlerState.rxTx.txActionStop = RCL_StopType_None;
180
181 RCL_CmdIeee_RxAction *rxAction = ieeeCmd->rxAction;
182 RCL_CmdIeee_TxAction *txAction = ieeeCmd->txAction;
183
184 RCL_CmdIeee_CcaMode ccaMode = RCL_CmdIeee_NoCca;
185 if (txAction != NULL)
186 {
187 ccaMode = txAction->ccaMode;
188 }
189
190 if (rxAction == NULL)
191 {
192 if (txAction == NULL)
193 {
194 cmd->status = RCL_CommandStatus_Error_Param;
195 rclEvents.lastCmdDone = 1;
196 return rclEvents;
197 }
198 else
199 {
200 /* Go straight to TX */
201 /* CSMA or RX ACK not possible with pure TX */
202 if (ccaMode != RCL_CmdIeee_NoCca ||
203 txAction->expectImmAck != 0 ||
204 txAction->expectEnhAck != 0)
205 {
206 cmd->status = RCL_CommandStatus_Error_Param;
207 rclEvents.lastCmdDone = 1;
208 return rclEvents;
209 }
210 }
211 }
212 else
213 {
214 if (rxAction->numPan > RCL_CMD_IEEE_MAX_NUM_PAN)
215 {
216 cmd->status = RCL_CommandStatus_Error_Param;
217 rclEvents.lastCmdDone = 1;
218 return rclEvents;
219 }
220 else if (rxAction->numPan == 0 && txAction != NULL &&
221 (txAction->expectImmAck != 0 || txAction->expectEnhAck != 0))
222 {
223 /* RX ACK not supported with promiscuous mode */
224 cmd->status = RCL_CommandStatus_Error_Param;
225 rclEvents.lastCmdDone = 1;
226 return rclEvents;
227 }
228 }
229
230 /* Start by enabling refsys */
231 earliestStartTime = LRF_enableSynthRefsys();
232
233 if (txAction != NULL)
234 {
235 uint32_t cmdTime = (cmd->scheduling == RCL_Schedule_AbsTime) ? cmd->timing.absStartTime : RCL_Scheduler_getCurrentTime();
236 uint32_t txActionTime = (txAction->ccaScheduling == RCL_Schedule_AbsTime) ? txAction->absCcaStartTime : cmdTime;
237 /* Check that TX action start time is not before the command start time */
238 if (!txAction->allowDelay && !RCL_Scheduler_isLater(cmdTime, txActionTime))
239 {
240 txAction->txStatus = RCL_CommandStatus_Error_StartTooLate;
241 ieeeHandlerState.rxTx.txState = txStateFinished;
242 }
243 else
244 {
245 if (ccaMode == RCL_CmdIeee_NoCca)
246 {
247 /* Check if the command should go directly to TX */
248 uint32_t txTime = txActionTime + txAction->relativeTxStartTime;
249 ieeeHandlerState.rxTx.ccaTxStartTime = txTime;
250 ieeeHandlerState.rxTx.allowTxDelay = txAction->allowDelay || txAction->allowTxDelay;
251 if (rxAction == NULL || RCL_Scheduler_delta(cmdTime, txTime) < (2 * IEEE_CCA_START_TIME_MARGIN))
252 {
253 /* TX starts after a short time; don't do RX first */
254 startTx = true;
255 }
256 else
257 {
258 /* Set receiver to stop in time for TX */
259 ieeeHandlerState.rxTx.txState = txStateStopToSetTx;
260 }
261 }
262 else
263 {
264 /* Schedule CCA evaluation */
265 txAction->txStatus = RCL_CommandStatus_Scheduled;
266 ieeeHandlerState.rxTx.txState = txStateSetupCca;
267 }
268 }
269 }
270
271 if (rclEvents.lastCmdDone == 0)
272 {
273 /* Program frequency word */
274 LRF_programFrequency(ieeeCmd->rfFrequency, startTx);
275
276 if (LRF_programTxPower(ieeeCmd->txPower) != TxPowerResult_Ok)
277 {
278 cmd->status = RCL_CommandStatus_Error_Param;
279 rclEvents.lastCmdDone = 1;
280 }
281 }
282
283 if (rclEvents.lastCmdDone == 0)
284 {
285 /* Enable radio */
286 LRF_enable();
287
288 ieeeHandlerState.common.activeUpdate = RCL_Handler_Ieee_initStats(ieeeCmd->stats,
289 rclSchedulerState.actualStartTime);
290
291 RCL_CommandStatus startTimeStatus;
292 if (startTx)
293 {
294 RCL_CommandTiming timing = cmd->timing;
295 timing.absStartTime = ieeeHandlerState.rxTx.ccaTxStartTime;
296 startTimeStatus = RCL_Scheduler_setCustomStartStopTimeEarliestStart(&timing, RCL_Schedule_AbsTime,
297 ieeeHandlerState.rxTx.allowTxDelay, earliestStartTime);
298 ieeeHandlerState.rxTx.txState = txStateWaitForTx;
299 }
300 else
301 {
302 startTimeStatus = RCL_Scheduler_setStartStopTimeEarliestStart(cmd, earliestStartTime);
303 }
304
305 if (startTimeStatus >= RCL_CommandStatus_Finished)
306 {
307 cmd->status = startTimeStatus;
308 rclEvents.lastCmdDone = 1;
309 }
310 }
311
312 if (rclEvents.lastCmdDone == 0 && rxAction != NULL)
313 {
314 /* Prepare receiver */
315 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_RXTIMEOUT) = 0; /* No timeout except from SYSTIM */
316
317 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_PIB) = rxAction->numPan;
318
319 uint32_t panRegOffset = 0;
320 uint32_t sourceMatchHeaderOffset = 0;
321 uint32_t sourceMatchTableOffset = 0;
322 for (int i = 0; i < rxAction->numPan; i++)
323 {
324 RCL_CmdIeee_PanConfig *panConfig = &rxAction->panConfig[i];
325 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + panRegOffset + PBE_IEEE_RAM_O_PANID0) = panConfig->localPanId;
326 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + panRegOffset + PBE_IEEE_RAM_O_SHORTADDR0) = panConfig->localShortAddr;
327 HWREG_WRITE_LRF(LRFD_BUFRAM_BASE + panRegOffset + PBE_IEEE_RAM_O_EXTADDR00) = (uint32_t) panConfig->localExtAddr;
328 HWREG_WRITE_LRF(LRFD_BUFRAM_BASE + panRegOffset + PBE_IEEE_RAM_O_EXTADDR02) = (uint32_t) (panConfig->localExtAddr >> 32);
329 uint32_t frameFilteringOption =
330 (panConfig->maxFrameVersion << PBE_IEEE_RAM_FFOPT0_MAXFRAME_S) |
331 (panConfig->panCoord << PBE_IEEE_RAM_FFOPT0_PANCOORD_S) |
332 (panConfig->defaultPend << PBE_IEEE_RAM_FFOPT0_DEFPEND_S);
333
334 switch (panConfig->autoAckMode)
335 {
336 case RCL_CmdIeee_AutoAck_Off:
337 default:
338 frameFilteringOption |= PBE_IEEE_RAM_FFOPT0_AUTOACK_DISABLE |
339 PBE_IEEE_RAM_FFOPT0_AUTOPEND_DISABLE |
340 PBE_IEEE_RAM_FFOPT0_PREQONLY_ANY;
341 break;
342
343 case RCL_CmdIeee_AutoAck_ImmAckNoAutoPend:
344 frameFilteringOption |= PBE_IEEE_RAM_FFOPT0_AUTOACK_EN |
345 PBE_IEEE_RAM_FFOPT0_AUTOPEND_DISABLE |
346 PBE_IEEE_RAM_FFOPT0_PREQONLY_ANY;
347 break;
348
349 case RCL_CmdIeee_AutoAck_ImmAckAutoPendAll:
350 frameFilteringOption |= PBE_IEEE_RAM_FFOPT0_AUTOACK_EN |
351 PBE_IEEE_RAM_FFOPT0_AUTOPEND_EN |
352 PBE_IEEE_RAM_FFOPT0_PREQONLY_ANY;
353 break;
354
355 case RCL_CmdIeee_AutoAck_ImmAckAutoPendDataReq:
356 frameFilteringOption |= PBE_IEEE_RAM_FFOPT0_AUTOACK_EN |
357 PBE_IEEE_RAM_FFOPT0_AUTOPEND_EN |
358 PBE_IEEE_RAM_FFOPT0_PREQONLY_DATAREQ;
359 break;
360 }
361
362 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + panRegOffset + PBE_IEEE_RAM_O_FFOPT0) = frameFilteringOption;
363
364 /* Set up source matching */
365 if (panConfig->sourceMatchingTableExt != NULL)
366 {
367 /* Extended source matching: Not yet supported */
368 cmd->status = RCL_CommandStatus_Error_Param;
369 rclEvents.lastCmdDone = 1;
370 break;
371 }
372 if (panConfig->sourceMatchingTableShort != NULL)
373 {
374 RCL_CmdIeee_SourceMatchingTableShort *sourceMatchingTable = panConfig->sourceMatchingTableShort;
375 uint32_t numEntries = sourceMatchingTable->numEntries;
376 if (numEntries > RCL_CMD_IEEE_SOURCE_MATCH_TABLE_SHORT_MAX_LEN)
377 {
378 cmd->status = RCL_CommandStatus_Error_Param;
379 rclEvents.lastCmdDone = 1;
380 break;
381 }
382 uint32_t entryNo = 0;
383 uint32_t index = 0;
384 while (entryNo < numEntries)
385 {
386 uint16_t mask = 0xFFFF;
387 if ((numEntries - entryNo) < 16)
388 {
389 mask >>= (16 - (numEntries - entryNo));
390 }
391 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + sourceMatchHeaderOffset + PBE_IEEE_RAM_O_ENTRYENABLE00 + (index << 1)) =
392 sourceMatchingTable->entryEnable[index] & mask;
393 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + sourceMatchHeaderOffset + PBE_IEEE_RAM_O_FRAMEPENDING00 + (index << 1)) =
394 sourceMatchingTable->framePending[index] & mask;
395 index++;
396 entryNo += 16;
397 }
398 while (index < RCL_CMD_IEEE_SOURCE_MATCH_TABLE_SHORT_NUM_WORDS)
399 {
400 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_ENTRYENABLE00 + (index << 1)) = 0;
401 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_FRAMEPENDING00 + (index << 1)) = 0;
402 index++;
403 }
404 for (entryNo = 0; entryNo < numEntries; entryNo++)
405 {
406 HWREG_WRITE_LRF(LRFD_BUFRAM_BASE + sourceMatchTableOffset + PBE_IEEE_RAM_O_PAN0_SRC_MATCH_SHORT_START + (entryNo << 2)) =
407 sourceMatchingTable->shortEntry[entryNo].combined;
408 }
409 }
410 else
411 {
412 for (int i = 0; i < RCL_CMD_IEEE_SOURCE_MATCH_TABLE_SHORT_NUM_WORDS; i++)
413 {
414 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + sourceMatchHeaderOffset + PBE_IEEE_RAM_O_ENTRYENABLE00 + (i << 1)) = 0;
415 }
416 }
417
418 panRegOffset += PBE_IEEE_RAM_O_PANID1 - PBE_IEEE_RAM_O_PANID0;
419 sourceMatchHeaderOffset += PBE_IEEE_RAM_O_ENTRYENABLE10 - PBE_IEEE_RAM_O_ENTRYENABLE00;
420 sourceMatchTableOffset += PBE_IEEE_RAM_O_PAN1_SRC_MATCH_SHORT_START - PBE_IEEE_RAM_O_PAN0_SRC_MATCH_SHORT_START;
421 }
422 uint16_t ffType =
423 PBE_IEEE_RAM_FFTYPE_MACCMD1_M |
424 PBE_IEEE_RAM_FFTYPE_DATA1_M |
425 PBE_IEEE_RAM_FFTYPE_BEACON1_M |
426 PBE_IEEE_RAM_FFTYPE_MACCMD0_M |
427 PBE_IEEE_RAM_FFTYPE_DATA0_M |
428 PBE_IEEE_RAM_FFTYPE_BEACON0_M;
429 ieeeHandlerState.rxTx.alwaysStoreAck = rxAction->alwaysStoreAck;
430 if (ieeeHandlerState.rxTx.alwaysStoreAck)
431 {
432 ffType |= PBE_IEEE_RAM_FFTYPE_ACK1_M | PBE_IEEE_RAM_FFTYPE_ACK0_M;
433 }
434 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_FFTYPE) = ffType;
435 }
436
437 if (rclEvents.lastCmdDone == 0)
438 {
439 if (rxAction != NULL)
440 {
441 /* Start receiver */
442 /* Set up sync found capture */
443 hal_setup_sync_found_cap();
444 /* Initialize RF FIFOs */
445 ieeeHandlerState.common.rxFifoSize = LRF_prepareRxFifo();
446 ieeeHandlerState.common.curBuffer = NULL;
447 RCL_Handler_Ieee_updateRxCurBufferAndFifo(&rxAction->rxBuffers);
448
449 /* Enable interrupts */
450 uint16_t fifoCfg = HWREGH_READ_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_FIFOCFG);
451 LRF_enableHwInterrupt(RCL_Handler_Ieee_maskEventsByFifoConf(LRF_EventOpDone.value | LRF_EventOpError.value |
452 LRF_EventRxOk.value | LRF_EventRxNok.value |
453 LRF_EventRxIgnored.value | LRF_EventRxBufFull.value |
454 (ieeeHandlerState.common.activeUpdate ? LRF_EventTxAck.value : 0),
455 fifoCfg, ieeeHandlerState.common.activeUpdate));
456 if (!startTx)
457 {
458 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_OPCFG) =
459 PBE_IEEE_RAM_OPCFG_STOP_SOFTEND |
460 PBE_IEEE_RAM_OPCFG_RXREPEATOK_YES |
461 PBE_IEEE_RAM_OPCFG_RXREPEATNOK_YES |
462 PBE_IEEE_RAM_OPCFG_TXINFINITE_NO |
463 PBE_IEEE_RAM_OPCFG_TXPATTERN_NO |
464 PBE_IEEE_RAM_OPCFG_TXFCMD_NONE |
465 PBE_IEEE_RAM_OPCFG_START_SYNC |
466 PBE_IEEE_RAM_OPCFG_SINGLE_DIS |
467 PBE_IEEE_RAM_OPCFG_IFSPERIOD_EN;
468 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_RXTIMEOUT) = 0;
469 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_CFGAUTOACK) =
470 PBE_IEEE_RAM_CFGAUTOACK_ACKMODE_NOFILT | PBE_IEEE_RAM_CFGAUTOACK_FLAGREQ_DIS;
471 /* Post cmd */
472 Log_printf(RclCore, Log_VERBOSE, "Starting IEEE RX");
473 LRF_waitForTopsmReady();
474 RCL_Profiling_eventHook(RCL_ProfilingEvent_PreprocStop);
475 HWREG_WRITE_LRF(LRFDPBE_BASE + LRFDPBE_O_API) = PBE_IEEE_REGDEF_API_OP_RX;
476 /* Clear RSSI valid interrupt flag */
477 LRF_clearHwInterrupt(LRF_EventRfesoft0.value);
478 ieeeHandlerState.rxTx.rxState = rxStateWaitForStart;
479 }
480 }
481 /* Mark as active */
482 cmd->status = RCL_CommandStatus_Active;
483
484 /* End status not determined */
485 ieeeHandlerState.common.endStatus = RCL_CommandStatus_Active;
486 }
487 }
488
489 if (cmd->status == RCL_CommandStatus_Active)
490 {
491 if (rclEventsIn.stopTimesUpdated)
492 {
493 if (ieeeHandlerState.common.eventTimeType != noEvent)
494 {
495 bool hardStop = (ieeeHandlerState.common.eventTimeType == customHardStop);
496 /* Event has been turned off by stop time modification */
497 ieeeHandlerState.common.eventTimeType = noEvent;
498 /* Turn back on if possible */
499 RCL_Handler_Ieee_setCustomEventTime(ieeeHandlerState.common.nextEventTime, IEEE_CCA_START_TIME_MARGIN, hardStop);
500 }
501 }
502 if (rclEventsIn.handlerCmdUpdate)
503 {
504 RCL_CmdIeee_TxAction *txAction = ieeeCmd->txAction;
505 RCL_StopType txActionStop = ieeeHandlerState.rxTx.txActionStop;
506 if (txActionStop != RCL_StopType_None && txAction != NULL)
507 {
508 if (txAction->txStatus < RCL_CommandStatus_Active)
509 {
510 Log_printf(RclCore, Log_VERBOSE, "Descheduling pending TX action");
511 /* TX action can be descheduled */
512 txAction->txStatus = RCL_CommandStatus_DescheduledApi;
513 ieeeHandlerState.rxTx.txState = txStateFinished;
514 }
515 else if (ieeeHandlerState.rxTx.txState <= txStateWaitForTx)
516 {
517 /* Waiting for CCA or TX to start; hard or graceful stop possible */
518 if (txActionStop == RCL_StopType_Graceful || txActionStop == RCL_StopType_Hard)
519 {
520 RCL_Handler_Ieee_restoreStopTime();
521 Log_printf(RclCore, Log_VERBOSE, "Stopping pending CCA or TX");
522 txAction->txStatus = (txActionStop == RCL_StopType_Graceful) ? RCL_CommandStatus_GracefulStopApi : RCL_CommandStatus_HardStopApi;
523 ieeeHandlerState.rxTx.txState = txStateFinished;
524 startTx = false;
525 }
526 }
527 else if ((ieeeHandlerState.rxTx.txState == txStateTx ||
528 ieeeHandlerState.rxTx.txState == txStateTxRx ||
529 ieeeHandlerState.rxTx.txState == txStateTxRxAck) &&
530 (HWREGH_READ_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_RXSTATUS) & PBE_IEEE_RAM_RXSTATUS_TXINPROGRESS) != 0)
531 {
532 if (txActionStop == RCL_StopType_Hard)
533 {
534 Log_printf(RclCore, Log_VERBOSE, "Stopping TX due to hard stop");
535 /* Send hard stop to PBE */
536 LRF_sendHardStop();
537 /* TX action will end when PBE is finished */
538 ieeeHandlerState.common.apiHardStopPending = true;
539 }
540 }
541 else if (ieeeHandlerState.rxTx.txState == txStateTxRxAck)
542 {
543 if (txActionStop == RCL_StopType_Hard)
544 {
545 /* Hard stop - give up on TX action, but keep receiver alive */
546 txAction->txStatus = RCL_CommandStatus_HardStopApi;
547 ieeeHandlerState.rxTx.txState = txStateFinished;
548 }
549 }
550 else
551 {
552 /* No action needed, TX action should end very soon */
553 }
554 /* Clear stop action */
555 ieeeHandlerState.rxTx.txActionStop = RCL_StopType_None;
556 }
557 else if (ieeeHandlerState.rxTx.txState == txStateNewAction && txAction != NULL)
558 {
559 uint32_t currentTime = RCL_Scheduler_getCurrentTime();
560 uint32_t txActionTime = (txAction->ccaScheduling == RCL_Schedule_AbsTime) ? txAction->absCcaStartTime : currentTime;
561 /* Check that TX action start time is not in the past */
562 if (txAction->ccaScheduling == RCL_Schedule_AbsTime && !txAction->allowDelay &&
563 !RCL_Scheduler_isLater(currentTime, txActionTime))
564 {
565 txAction->txStatus = RCL_CommandStatus_Error_StartTooLate;
566 ieeeHandlerState.rxTx.txState = txStateFinished;
567 }
568 else if (ieeeCmd->rxAction->numPan == 0 &&
569 (txAction->expectImmAck != 0 || txAction->expectEnhAck != 0))
570 {
571 txAction->txStatus = RCL_CommandStatus_Error_Param;
572 ieeeHandlerState.rxTx.txState = txStateFinished;
573 }
574 else
575 {
576 txAction->txStatus = RCL_CommandStatus_Scheduled;
577 if (txAction->ccaMode == RCL_CmdIeee_NoCca)
578 {
579 /* Check if the command should go directly to TX */
580 uint32_t txTime = txActionTime + txAction->relativeTxStartTime;
581 ieeeHandlerState.rxTx.ccaTxStartTime = txTime;
582 ieeeHandlerState.rxTx.allowTxDelay = txAction->allowDelay || txAction->allowTxDelay;
583
584 if (RCL_Scheduler_delta(currentTime, txTime) < IEEE_CCA_START_TIME_MARGIN)
585 {
586 /* TX starts after a short time; stop RX now */
587 ieeeHandlerState.rxTx.txState = txStateSetTxTime;
588 /* Stop running RX */
589 LRF_sendHardStop();
590 }
591 else
592 {
593 /* Set receiver to stop in time for TX */
594 ieeeHandlerState.rxTx.txState = txStateStopToSetTx;
595 }
596 }
597 else
598 {
599 /* Schedule CCA evaluation */
600 ieeeHandlerState.rxTx.txState = txStateSetupCca;
601 }
602 }
603 }
604 }
605 if (rclEventsIn.timerStart != 0)
606 {
607 rclEvents.cmdStarted = 1;
608 if (ieeeHandlerState.rxTx.rxState == rxStateWaitForStart)
609 {
610 ieeeHandlerState.rxTx.rxState = rxStateRunning;
611 }
612 }
613 if (rclEventsIn.rxBufferUpdate != 0)
614 {
615 RCL_CmdIeee_RxAction *rxAction = ieeeCmd->rxAction;
616 if (rxAction != NULL)
617 {
618 RCL_Handler_Ieee_updateRxCurBufferAndFifo(&rxAction->rxBuffers);
619 }
620 }
621
622 if (ieeeHandlerState.rxTx.txState == txStateStopToSetTx && ieeeHandlerState.rxTx.rxState == rxStateRunning)
623 {
624 /* Set receiver to stop in time for TX */
625 bool timeSet = RCL_Handler_Ieee_setCustomEventTime(ieeeHandlerState.rxTx.ccaTxStartTime - IEEE_CCA_START_TIME_MARGIN, IEEE_CCA_START_TIME_MARGIN, true);
626 if (timeSet)
627 {
628 ieeeHandlerState.rxTx.txState = txStateSetTxTime;
629 }
630 else
631 {
632 /* Command will end soon - park TX action until then */
633 ieeeHandlerState.rxTx.txState = txStateWaitForCmdEnd;
634 }
635 }
636
637 if (ieeeHandlerState.rxTx.txState == txStateSetupCca && ieeeHandlerState.rxTx.rxState == rxStateRunning)
638 {
639 RCL_CmdIeee_TxAction *txAction = ieeeCmd->txAction;
640 uint32_t ccaTime = txAction->absCcaStartTime;
641 RCL_ScheduleType ccaScheduling = txAction->ccaScheduling;
642 bool allowDelay = (ccaScheduling == RCL_Schedule_Now) ? true : txAction->allowDelay;
643
644 uint32_t currentTime = RCL_Scheduler_getCurrentTime();
645 ieeeHandlerState.rxTx.waitingForValidRssi = false;
646
647 uint8_t ccaContentionWindow = txAction->ccaContentionWindow;
648 if (ccaContentionWindow < 1)
649 {
650 ccaContentionWindow = 1;
651 }
652 ieeeHandlerState.rxTx.ccaContentionWindow = ccaContentionWindow;
653 uint32_t ccaDuration = (ccaContentionWindow - 1) * IEEE_BACKOFF_PERIOD;
654 if (ccaScheduling == RCL_Schedule_AbsTime &&
655 RCL_Scheduler_isLater(currentTime + IEEE_CCA_START_TIME_MARGIN, ccaTime))
656 {
657 bool timeSet = RCL_Handler_Ieee_setCustomEventTime(ccaTime, ccaDuration + IEEE_CCA_START_TIME_MARGIN, false);
658 ieeeHandlerState.rxTx.ccaTxStartTime = ccaTime;
659 if (timeSet)
660 {
661 ieeeHandlerState.rxTx.txState = txStateWaitForCca;
662 }
663 }
664 else if (allowDelay || ccaScheduling == RCL_Schedule_Now)
665 {
666 if (ccaScheduling == RCL_Schedule_Now)
667 {
668 ieeeHandlerState.rxTx.ccaTxStartTime = currentTime;
669 }
670 else
671 {
672 /* Use programmed CCA time for future calculations even if it was in the past */
673 ieeeHandlerState.rxTx.ccaTxStartTime = ccaTime;
674 }
675 ieeeHandlerState.rxTx.txState = txStateWaitForCca;
676 /* Start immediately */
677 doCca = true;
678 }
679 else
680 {
681 /* Requested too late */
682 txAction->txStatus = RCL_CommandStatus_Error_StartTooLate;
683 /* Signal end of action */
684 ieeeHandlerState.rxTx.txState = txStateFinished;
685 }
686 }
687
688 if ((rclEventsIn.hardStop && ieeeHandlerState.common.eventTimeType == customEvent) ||
689 (lrfEvents.rfesoft0 && LRF_readRssi() != LRF_RSSI_INVALID))
690 {
691 if (ieeeHandlerState.rxTx.txState == txStateWaitForCca)
692 {
693 /* The hard stop event means "do CCA" and will not cause PBE to stop */
694 doCca = true;
695 Log_printf(RclCore, Log_VERBOSE, "Perform CCA");
696 /* Set back stop event */
697 RCL_Handler_Ieee_restoreStopTime();
698 }
699 }
700
701 if (doCca)
702 {
703 RCL_CmdIeee_TxAction *txAction = ieeeCmd->txAction;
704 RCL_CmdIeee_CcaMode ccaMode = txAction->ccaMode;
705 txAction->txStatus = RCL_CommandStatus_Active;
706
707 bool busy = false;
708 /* All CCA modes shall report busy if packet is in progress */
709 if (HWREGH_READ_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_RXSTATUS) != 0)
710 {
711 busy = true;
712 Log_printf(RclCore, Log_VERBOSE, "CCA busy because packet is in progress");
713 if (ieeeHandlerState.rxTx.waitingForValidRssi)
714 {
715 LRF_disableHwInterrupt(LRF_EventRfesoft0.value);
716 ieeeHandlerState.rxTx.waitingForValidRssi = false;
717 }
718 }
719
720 if (!busy && (ccaMode != RCL_CmdIeee_CcaMode4Aloha))
721 {
722 /* Check RSSI */
723 int8_t rssi = LRF_readRssi();
724 /* RSSI is checked even for CCA mode 2, as invalid RSSI means that correlation result is not yet ready */
725 if (rssi == LRF_RSSI_INVALID && !ieeeHandlerState.rxTx.waitingForValidRssi)
726 {
727 /* Wait for RSSI valid */
728 Log_printf(RclCore, Log_VERBOSE, "CCA invalid; check again");
729
730 ieeeHandlerState.rxTx.waitingForValidRssi = true;
731 /* Wait 1 backoff period for valid RSSI */
732 uint32_t ccaTime = ieeeHandlerState.rxTx.ccaTxStartTime;
733 ccaTime += IEEE_BACKOFF_PERIOD;
734 if (txAction->ccaContentionWindow <= 1)
735 {
736 /* Non-slotted CSMA - wait only until the RSSI is ready, but use the backoff period as a timeout */
737 /* Enable notification on RSSI available */
738 LRF_enableHwInterrupt(LRF_EventRfesoft0.value);
739 }
740 else
741 {
742 /* Store updated CCA time only for slotted CCA */
743 ieeeHandlerState.rxTx.ccaTxStartTime = ccaTime;
744 }
745
746 /* Set new compare time */
747 RCL_Handler_Ieee_setCustomEventTime(ccaTime, IEEE_CCA_START_TIME_MARGIN, false);
748
749 /* If RSSI is not valid the second time around, treat as busy */
750 }
751 else
752 {
753 bool busyRssi = false;
754 bool busySignal = false;
755
756 LRF_disableHwInterrupt(LRF_EventRfesoft0.value);
757 ieeeHandlerState.rxTx.waitingForValidRssi = false;
758 if (rssi >= txAction->rssiLimit)
759 {
760 busyRssi = true;
761 }
762
763 uint16_t corrCount = HWREGH_READ_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_CORRCNT);
764
765 if (corrCount > txAction->ccaCorrThresh || rssi == LRF_RSSI_INVALID)
766 {
767 busySignal = true;
768 }
769
770 switch (ccaMode)
771 {
772 case RCL_CmdIeee_CcaMode1Energy:
773 busy = busyRssi;
774 break;
775
776 case RCL_CmdIeee_CcaMode2Signal:
777 busy = busySignal;
778 break;
779
780 case RCL_CmdIeee_CcaMode3EnergyOrSignal:
781 busy = busyRssi || busySignal;
782 break;
783
784 case RCL_CmdIeee_CcaMode3EnergyAndSignal:
785 busy = busyRssi && busySignal;
786 break;
787
788 default:
789 /* Other values are invalid or should not be processed here - treat as busy */
790 busy = true;
791 break;
792 }
793 if (busy)
794 {
795 Log_printf(RclCore, Log_VERBOSE, "CCA busy; RSSI = %1d dBm, correlation top count = %1d", rssi, corrCount);
796 }
797 else
798 {
799 Log_printf(RclCore, Log_VERBOSE, "CCA idle; RSSI = %1d dBm, correlation top count = %1d", rssi, corrCount);
800 }
801 }
802 }
803
804 if (!ieeeHandlerState.rxTx.waitingForValidRssi)
805 {
806 if (busy)
807 {
808 txAction->txStatus = RCL_CommandStatus_ChannelBusy;
809 /* Signal end of action */
810 ieeeHandlerState.rxTx.txState = txStateFinished;
811 }
812 else
813 {
814 uint32_t ccaTime = ieeeHandlerState.rxTx.ccaTxStartTime;
815 ieeeHandlerState.rxTx.ccaContentionWindow -= 1;
816 if (ieeeHandlerState.rxTx.ccaContentionWindow == 0)
817 {
818 /* Channel idle - transmit */
819 ieeeHandlerState.rxTx.txState = txStateSetTxTime;
820 ieeeHandlerState.rxTx.ccaTxStartTime = ccaTime + txAction->relativeTxStartTime;
821 ieeeHandlerState.rxTx.allowTxDelay = txAction->allowTxDelay;
822 Log_printf(RclCore, Log_VERBOSE, "Stop RX to go to TX");
823 /* Stop running RX */
824 LRF_sendHardStop();
825 }
826 else
827 {
828 /* Slotted CCA - check again after 1 backoff period */
829 ccaTime += IEEE_BACKOFF_PERIOD;
830 /* Set new compare time */
831 RCL_Handler_Ieee_setCustomEventTime(ccaTime, IEEE_CCA_START_TIME_MARGIN, false);
832 ieeeHandlerState.rxTx.ccaTxStartTime = ccaTime;
833 }
834 }
835 }
836 }
837
838 if (lrfEvents.rxEmpty != 0)
839 {
840 /* Timeout or ACK reception */
841 LRF_disableHwInterrupt(LRF_EventRxEmpty.value);
842 if (ieeeHandlerState.rxTx.txState == txStateTxRxAck)
843 {
844 uint16_t ackStatus = HWREGH_READ_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_ACKSTATUS);
845
846 if (ackStatus & PBE_IEEE_RAM_ACKSTATUS_SYNCTIMEOUT_M)
847 {
848 ieeeCmd->txAction->txStatus = RCL_CommandStatus_NoSync;
849 ieeeHandlerState.rxTx.txState = txStateFinished;
850 }
851 else if (ackStatus & PBE_IEEE_RAM_ACKSTATUS_CRCOK_M)
852 {
853 if (ieeeHandlerState.rxTx.alwaysStoreAck)
854 {
855 /* Need to check sequence number below */
856 ieeeHandlerState.rxTx.txState = txStateCheckAck;
857 }
858 else
859 {
860 ieeeCmd->txAction->txStatus = RCL_CommandStatus_Finished;
861 ieeeHandlerState.rxTx.txState = txStateFinished;
862 }
863 }
864 else if (ackStatus &
865 (PBE_IEEE_RAM_ACKSTATUS_IGNORED_M | PBE_IEEE_RAM_ACKSTATUS_OTHERFRM_M |
866 PBE_IEEE_RAM_ACKSTATUS_CRCERR_M))
867 {
868 ieeeCmd->txAction->txStatus = RCL_CommandStatus_RxErr;
869 ieeeHandlerState.rxTx.txState = txStateFinished;
870 }
871 else
872 {
873 /* ERROR: ACK status gives no known status; should not happen */
874 ieeeCmd->txAction->txStatus = RCL_CommandStatus_Error;
875 ieeeHandlerState.rxTx.txState = txStateFinished;
876 }
877 }
878 }
879 if (lrfEvents.txDone != 0)
880 {
881 LRF_disableHwInterrupt(LRF_EventTxDone.value);
882 if (ieeeHandlerState.rxTx.txState == txStateTx || ieeeHandlerState.rxTx.txState == txStateTxRx)
883 {
884 ieeeCmd->txAction->txStatus = RCL_CommandStatus_Finished;
885 ieeeHandlerState.rxTx.txState = txStateFinished;
886 }
887 }
888 if (lrfEvents.opDone != 0 || lrfEvents.opError != 0)
889 {
890 uint16_t endCause = HWREGH_READ_LRF(LRFD_BUFRAM_BASE + PBE_COMMON_RAM_O_ENDCAUSE);
891 ieeeHandlerState.rxTx.rxState = rxStateNoRx;
892 if (ieeeHandlerState.rxTx.txState == txStateSetTxTime)
893 {
894 /* Set up TX start time */
895 RCL_CommandStatus status = RCL_Scheduler_setNewStartAbsTime(ieeeHandlerState.rxTx.ccaTxStartTime, ieeeHandlerState.rxTx.allowTxDelay);
896 if (status != RCL_CommandStatus_Active)
897 {
898 ieeeCmd->txAction->txStatus = status;
899 ieeeHandlerState.rxTx.txState = txStateFinished;
900 if (RCL_CommandStatus_isAnyStop(status))
901 {
902 /* Entire command to stop */
903 cmd->status = status;
904 rclEvents.lastCmdDone = 1;
905 }
906 }
907 else
908 {
909 LRF_enable();
910 startTx = true;
911 ieeeHandlerState.rxTx.txState = txStateWaitForTx;
912 }
913 }
914 else if (ieeeHandlerState.rxTx.txState == txStateTx)
915 {
916 if (lrfEvents.opDone != 0)
917 {
918 ieeeCmd->txAction->txStatus = RCL_CommandStatus_Finished;
919 ieeeHandlerState.rxTx.txState = txStateFinished;
920
921 RCL_Profiling_eventHook(RCL_ProfilingEvent_PostprocStart);
922 }
923 else
924 {
925 RCL_CommandStatus endStatus = ieeeHandlerState.common.endStatus;
926 if (endStatus == RCL_CommandStatus_Active)
927 {
928 cmd->status = RCL_Handler_Ieee_findPbeErrorEndStatus(endCause);
929 }
930 else
931 {
932 cmd->status = endStatus;
933 }
934 rclEvents.lastCmdDone = 1;
935 }
936 }
937 else if ((ieeeHandlerState.rxTx.txState >= txStateTxRx && ieeeHandlerState.rxTx.txState <= txStateCheckAck) &&
938 endCause == PBE_COMMON_RAM_ENDCAUSE_STAT_ERR_STOP && ieeeHandlerState.common.apiHardStopPending)
939 {
940 /* Hard stop of ongoing TX action */
941 ieeeCmd->txAction->txStatus = RCL_CommandStatus_HardStopApi;
942 ieeeHandlerState.rxTx.txState = txStateFinished;
943 /* Stop is now done */
944 ieeeHandlerState.common.apiHardStopPending = false;
945 }
946 else
947 {
948 RCL_CommandStatus endStatus = ieeeHandlerState.common.endStatus;
949 rclEvents.lastCmdDone = 1;
950 if (endStatus == RCL_CommandStatus_Active)
951 {
952 if (lrfEvents.opError != 0)
953 {
954 endStatus = RCL_Handler_Ieee_findPbeErrorEndStatus(endCause);
955 }
956 else if (endCause == PBE_COMMON_RAM_ENDCAUSE_STAT_EOPSTOP)
957 {
958 endStatus = RCL_Scheduler_findStopStatus(RCL_StopType_Graceful);
959 }
960 else
961 {
962 endStatus = RCL_CommandStatus_Finished;
963 }
964 }
965 cmd->status = endStatus;
966 RCL_Profiling_eventHook(RCL_ProfilingEvent_PostprocStart);
967 }
968 }
969
970 if (startTx)
971 {
972 RCL_CmdIeee_TxAction *txAction = ieeeCmd->txAction;
973 txAction->txStatus = RCL_CommandStatus_Active;
974
975 ieeeHandlerState.common.txFifoSize = LRF_prepareTxFifo();
976
977 if (ieeeCmd->rxAction != NULL && (txAction->expectImmAck || !txAction->endCmdWhenDone))
978 {
979 /* Set TX to proceed with RX */
980 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_OPCFG) =
981 PBE_IEEE_RAM_OPCFG_STOP_SOFTEND |
982 PBE_IEEE_RAM_OPCFG_RXREPEATOK_YES |
983 PBE_IEEE_RAM_OPCFG_RXREPEATNOK_YES |
984 PBE_IEEE_RAM_OPCFG_TXINFINITE_NO |
985 PBE_IEEE_RAM_OPCFG_TXPATTERN_NO |
986 PBE_IEEE_RAM_OPCFG_TXFCMD_NONE |
987 PBE_IEEE_RAM_OPCFG_START_SYNC |
988 PBE_IEEE_RAM_OPCFG_SINGLE_DIS |
989 PBE_IEEE_RAM_OPCFG_IFSPERIOD_EN;
990 /* RX will be running when TX is done */
991 ieeeHandlerState.rxTx.rxState = rxStateWaitForStart;
992 }
993 else
994 {
995 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_OPCFG) =
996 PBE_IEEE_RAM_OPCFG_STOP_SOFTEND |
997 PBE_IEEE_RAM_OPCFG_RXREPEATOK_YES |
998 PBE_IEEE_RAM_OPCFG_RXREPEATNOK_YES |
999 PBE_IEEE_RAM_OPCFG_TXINFINITE_NO |
1000 PBE_IEEE_RAM_OPCFG_TXPATTERN_NO |
1001 PBE_IEEE_RAM_OPCFG_TXFCMD_NONE |
1002 PBE_IEEE_RAM_OPCFG_START_SYNC |
1003 PBE_IEEE_RAM_OPCFG_SINGLE_EN |
1004 PBE_IEEE_RAM_OPCFG_IFSPERIOD_DIS;
1005 }
1006
1007 /* Enter payload */
1008 RCL_Buffer_DataEntry *txEntry = txAction->txEntry;
1009 if (txEntry == NULL)
1010 {
1011 txAction->txStatus = RCL_CommandStatus_Error_MissingTxBuffer;
1012 ieeeHandlerState.rxTx.txState = txStateFinished;
1013 }
1014 else
1015 {
1016 uint32_t wordLength = RCL_Buffer_DataEntry_paddedLen(txEntry->length) / 4;
1017
1018 if (wordLength > LRF_getTxFifoWritable() / 4)
1019 {
1020 /* Packet will not fit - probably an error in the packet structure */
1021 txAction->txStatus = RCL_CommandStatus_Error_TxBufferCorruption;
1022 ieeeHandlerState.rxTx.txState = txStateFinished;
1023 }
1024 else
1025 {
1026 uint32_t *data32 = (uint32_t *) (txEntry);
1027
1028 /* Copy packet into FIFO */
1029 LRF_writeTxFifoWords(data32, wordLength);
1030
1031 /* Enable interrupts */
1032 LRF_Events interrupts = {.value = (LRF_EventOpDone.value | LRF_EventOpError.value)};
1033
1034 if (txAction->expectImmAck)
1035 {
1036 /* Find sequence number from transmitted frame */
1037 uint8_t seqNo = 0;
1038 uint32_t pos = txEntry->numPad - sizeof(txEntry->pad0) + IEEE_PHY_HDR_LEN + IEEE_MAC_FCF_LEN;
1039 if (txEntry->length >= sizeof(txEntry->numPad) + sizeof(txEntry->pad0) + pos)
1040 {
1041 seqNo = txEntry->data[pos];
1042 }
1043
1044 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_RXTIMEOUT) = txAction->ackTimeout;
1045 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_CFGAUTOACK) =
1046 (ieeeHandlerState.rxTx.alwaysStoreAck ?
1047 PBE_IEEE_RAM_CFGAUTOACK_ACKMODE_NOFILT :
1048 PBE_IEEE_RAM_CFGAUTOACK_ACKMODE_FILT) |
1049 PBE_IEEE_RAM_CFGAUTOACK_FLAGREQ_EN |
1050 (seqNo << PBE_IEEE_RAM_CFGAUTOACK_EXPSEQNM_S);
1051 /* Get informed on ACK result */
1052 interrupts.rxEmpty = 1;
1053 ieeeHandlerState.rxTx.txState = txStateTxRxAck;
1054 ieeeHandlerState.rxTx.expSeqNo = seqNo;
1055 }
1056 else
1057 {
1058 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_RXTIMEOUT) = 0;
1059 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_CFGAUTOACK) =
1060 PBE_IEEE_RAM_CFGAUTOACK_ACKMODE_NOFILT | PBE_IEEE_RAM_CFGAUTOACK_FLAGREQ_DIS;
1061
1062 if (ieeeCmd->rxAction != NULL && !txAction->endCmdWhenDone)
1063 {
1064 /* RX will go on, but TX action is finished at the end of TX */
1065 interrupts.txDone = 1;
1066 ieeeHandlerState.rxTx.txState = txStateTxRx;
1067 }
1068 else
1069 {
1070 ieeeHandlerState.rxTx.txState = txStateTx;
1071 }
1072 }
1073 /* Clear and enable interrupts */
1074 LRF_clearHwInterrupt(interrupts.value);
1075 LRF_enableHwInterrupt(interrupts.value);
1076
1077 /* Post cmd */
1078 Log_printf(RclCore, Log_VERBOSE, "Starting IEEE TX");
1079 LRF_waitForTopsmReady();
1080 RCL_Profiling_eventHook(RCL_ProfilingEvent_PreprocStop);
1081 HWREG_WRITE_LRF(LRFDPBE_BASE + LRFDPBE_O_API) = PBE_IEEE_REGDEF_API_OP_TX;
1082 }
1083 }
1084 }
1085
1086 if ((lrfEvents.rxOk != 0 || lrfEvents.rxNok != 0 || lrfEvents.rxIgnored != 0) && ieeeCmd->rxAction != NULL)
1087 {
1088 /* Copy received packet from PBE FIFO to buffer */
1089 /* First, check that there is actually a buffer available */
1090 uint32_t rxFifoReadable = HWREG_READ_LRF(LRFDPBE_BASE + LRFDPBE_O_RXFREADABLE);
1091 while (rxFifoReadable >= 4)
1092 {
1093 /* Check length of received buffer by peeking */
1094 uint32_t fifoWord = LRF_peekRxFifo(0);
1095 uint32_t wordLength = RCL_Buffer_DataEntry_paddedLen(fifoWord & 0xFFFF) / 4;
1096 if (wordLength > 0)
1097 {
1098 RCL_MultiBuffer *curBuffer;
1099 if (wordLength * 4 > rxFifoReadable)
1100 {
1101 /* Error */
1102 curBuffer = NULL;
1103 }
1104 else
1105 {
1106 curBuffer = RCL_MultiBuffer_getBuffer(ieeeHandlerState.common.curBuffer,
1107 wordLength * 4);
1108 if (curBuffer != ieeeHandlerState.common.curBuffer) {
1109 rclEvents.rxBufferFinished = 1;
1110 ieeeHandlerState.common.curBuffer = curBuffer;
1111 }
1112 }
1113 if (curBuffer == NULL) {
1114 /* Error */
1115 ieeeHandlerState.common.endStatus = RCL_CommandStatus_Error_RxBufferCorruption;
1116 /* Send abort */
1117 HWREG_WRITE_LRF(LRFDPBE_BASE + LRFDPBE_O_API) = PBE_IEEE_REGDEF_API_OP_STOP;
1118 /* Do not check for more packets from the RX FIFO */
1119 break;
1120 }
1121 else {
1122 uint32_t *data32;
1123 data32 = (uint32_t *)RCL_MultiBuffer_getNextWritableByte(curBuffer);
1124 LRF_readRxFifoWords(data32, wordLength);
1125 RCL_MultiBuffer_commitBytes(curBuffer, wordLength * 4);
1126 /* Raise event */
1127 rclEvents.rxEntryAvail = 1;
1128 /* Adjust effective FIFO size */
1129 RCL_Handler_Ieee_updateRxCurBufferAndFifo(&ieeeCmd->rxAction->rxBuffers);
1130 if (ieeeHandlerState.rxTx.txState == txStateCheckAck)
1131 {
1132 if (lrfEvents.rxOk && wordLength >= 2)
1133 {
1134 /* Read out sequence number; expect rest of the frame to be checked OK by PBE */
1135 RCL_Buffer_DataEntry *entry = (RCL_Buffer_DataEntry *) data32;
1136 uint8_t seqNo = entry->data[entry->numPad - sizeof(entry->pad0) + IEEE_PHY_HDR_LEN + IEEE_MAC_FCF_LEN];
1137 if (seqNo == ieeeHandlerState.rxTx.expSeqNo)
1138 {
1139 ieeeCmd->txAction->txStatus = RCL_CommandStatus_Finished;
1140 }
1141 else
1142 {
1143 ieeeCmd->txAction->txStatus = RCL_CommandStatus_RxErr;
1144 }
1145 }
1146 else
1147 {
1148 ieeeCmd->txAction->txStatus = RCL_CommandStatus_RxErr;
1149 }
1150 ieeeHandlerState.rxTx.txState = txStateFinished;
1151 }
1152 }
1153 }
1154 rxFifoReadable = HWREG_READ_LRF(LRFDPBE_BASE + LRFDPBE_O_RXFREADABLE);
1155 }
1156 if (ieeeHandlerState.common.activeUpdate)
1157 {
1158 RCL_Handler_Ieee_updateStats(ieeeCmd->stats, rclSchedulerState.actualStartTime);
1159 }
1160 }
1161 if (lrfEvents.txAck != 0 && ieeeCmd->rxAction != NULL)
1162 {
1163 if (ieeeHandlerState.common.activeUpdate)
1164 {
1165 RCL_Handler_Ieee_updateStats(ieeeCmd->stats, rclSchedulerState.actualStartTime);
1166 }
1167 }
1168
1169 if (ieeeHandlerState.rxTx.txState == txStateFinished && cmd->status == RCL_CommandStatus_Active)
1170 {
1171 rclEvents.cmdStepDone = 1;
1172 LRF_disableHwInterrupt(LRF_EventRfesoft0.value);
1173
1174 if (ieeeCmd->txAction->endCmdWhenDone || ieeeCmd->rxAction == NULL)
1175 {
1176 if (ieeeHandlerState.rxTx.rxState != rxStateNoRx)
1177 {
1178 /* Stop running RX and let it finish */
1179 ieeeHandlerState.common.endStatus = ieeeCmd->txAction->txStatus;
1180 Log_printf(RclCore, Log_VERBOSE, "Stop RX as command should end");
1181 LRF_sendHardStop();
1182 ieeeHandlerState.rxTx.txState = txStateWaitForCmdEnd;
1183 }
1184 else
1185 {
1186 /* End now */
1187 cmd->status = ieeeCmd->txAction->txStatus;
1188 rclEvents.lastCmdDone = 1;
1189 ieeeHandlerState.rxTx.txState = txStateNoTx;
1190 }
1191 }
1192 else
1193 {
1194 if (ieeeHandlerState.rxTx.rxState == rxStateNoRx)
1195 {
1196 LRF_enable();
1197 /* Restart RX */
1198 restartRx = true;
1199 }
1200 ieeeHandlerState.rxTx.txState = txStateNoTx;
1201 }
1202 if (ieeeHandlerState.common.activeUpdate)
1203 {
1204 RCL_Handler_Ieee_updateStats(ieeeCmd->stats, rclSchedulerState.actualStartTime);
1205 }
1206 }
1207 }
1208 if (restartRx)
1209 {
1210 RCL_CommandStatus status = RCL_Scheduler_setNewStartNow();
1211 if (status == RCL_CommandStatus_Active)
1212 {
1213 /* Set up for RX */
1214 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_OPCFG) =
1215 PBE_IEEE_RAM_OPCFG_STOP_SOFTEND |
1216 PBE_IEEE_RAM_OPCFG_RXREPEATOK_YES |
1217 PBE_IEEE_RAM_OPCFG_RXREPEATNOK_YES |
1218 PBE_IEEE_RAM_OPCFG_TXINFINITE_NO |
1219 PBE_IEEE_RAM_OPCFG_TXPATTERN_NO |
1220 PBE_IEEE_RAM_OPCFG_TXFCMD_NONE |
1221 PBE_IEEE_RAM_OPCFG_START_SYNC |
1222 PBE_IEEE_RAM_OPCFG_SINGLE_DIS |
1223 PBE_IEEE_RAM_OPCFG_IFSPERIOD_EN;
1224 /* Post cmd */
1225 Log_printf(RclCore, Log_VERBOSE, "Restarting IEEE RX");
1226 LRF_waitForTopsmReady();
1227 HWREG_WRITE_LRF(LRFDPBE_BASE + LRFDPBE_O_API) = PBE_IEEE_REGDEF_API_OP_RX;
1228 /* Clear RSSI valid interrupt flag */
1229 LRF_clearHwInterrupt(LRF_EventRfesoft0.value);
1230 ieeeHandlerState.rxTx.rxState = rxStateRunning;
1231 }
1232 else
1233 {
1234 cmd->status = status;
1235 rclEvents.lastCmdDone = 1;
1236 }
1237 }
1238
1239 if (rclEvents.lastCmdDone != 0)
1240 {
1241 /* Check if TX action has finished */
1242 if (ieeeHandlerState.rxTx.txState != txStateNoTx)
1243 {
1244 Log_printf(RclCore, Log_VERBOSE, "TX action ending because command ended");
1245 if (ieeeCmd->txAction != NULL && ieeeCmd->txAction->txStatus < RCL_CommandStatus_Finished)
1246 {
1247 /* End status not set - use command end status to show it ended with command */
1248 ieeeCmd->txAction->txStatus = cmd->status;
1249 }
1250 rclEvents.cmdStepDone = 1;
1251 }
1252 LRF_disable();
1253 LRF_disableSynthRefsys();
1254 RCL_Handler_Ieee_updateStats(ieeeCmd->stats, rclSchedulerState.actualStartTime);
1255 }
1256 return rclEvents;
1257 }
1258
1259 /*
1260 * ======== RCL_Handler_Ieee_TxTest ========
1261 */
RCL_Handler_Ieee_TxTest(RCL_Command * cmd,LRF_Events lrfEvents,RCL_Events rclEventsIn)1262 RCL_Events RCL_Handler_Ieee_TxTest(RCL_Command *cmd, LRF_Events lrfEvents, RCL_Events rclEventsIn)
1263 {
1264 RCL_CmdIeeeTxTest *txCmd = (RCL_CmdIeeeTxTest *) cmd;
1265 RCL_Events rclEvents = {.value = 0};
1266
1267 if (rclEventsIn.setup != 0)
1268 {
1269 uint32_t earliestStartTime;
1270
1271 /* Start by enabling refsys */
1272 earliestStartTime = LRF_enableSynthRefsys();
1273 ieeeHandlerState.txTest.restoreOpt = RCL_HANDLER_IEEE_RESTORE_NONE;
1274 ieeeHandlerState.common.apiHardStopPending = false;
1275 if ((txCmd->rfFrequency == 0) && ((HWREG_READ_LRF(LRFDRFE_BASE + LRFDRFE_O_SPARE4) & 0x0001) == 0))
1276 {
1277 /* Synth not to be programmed, but not already locked */
1278 cmd->status = RCL_CommandStatus_Error_Synth;
1279 rclEvents.lastCmdDone = 1;
1280 }
1281 else
1282 {
1283 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_OPCFG) =
1284 PBE_IEEE_RAM_OPCFG_RXREPEATOK_NO |
1285 PBE_IEEE_RAM_OPCFG_RXREPEATNOK_NO |
1286 PBE_IEEE_RAM_OPCFG_TXINFINITE_YES |
1287 PBE_IEEE_RAM_OPCFG_TXPATTERN_YES |
1288 PBE_IEEE_RAM_OPCFG_TXFCMD_NONE |
1289 PBE_IEEE_RAM_OPCFG_START_SYNC |
1290 PBE_IEEE_RAM_OPCFG_SINGLE_EN |
1291 PBE_IEEE_RAM_OPCFG_IFSPERIOD_DIS;
1292
1293 /* Mark as active */
1294 cmd->status = RCL_CommandStatus_Active;
1295 /* End status not determined */
1296 ieeeHandlerState.common.endStatus = RCL_CommandStatus_Active;
1297
1298 if (LRF_programTxPower(txCmd->txPower) != TxPowerResult_Ok)
1299 {
1300 cmd->status = RCL_CommandStatus_Error_Param;
1301 rclEvents.lastCmdDone = 1;
1302 }
1303
1304 /* Enter configuration */
1305 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_PATTERN) = txCmd->txWord;
1306 if (txCmd->config.sendCw != 0)
1307 {
1308 HWREG_WRITE_LRF(LRFDMDM_BASE + LRFDMDM_O_MODCTRL) = HWREG_READ_LRF(LRFDMDM_BASE + LRFDMDM_O_MODCTRL) | LRFDMDM_MODCTRL_TONEINSERT_M;
1309 ieeeHandlerState.txTest.restoreOpt = RCL_HANDLER_IEEE_RESTORE_MODCTRL;
1310 }
1311 else
1312 {
1313 uint32_t whitenMode = txCmd->config.whitenMode;
1314 /* Configure whitening */
1315 if (whitenMode != RCL_CMD_IEEE_WH_MODE_OFF)
1316 {
1317 ieeeHandlerState.txTest.restoreOpt = RCL_HANDLER_IEEE_RESTORE_WHITENING;
1318 ieeeHandlerState.txTest.storedWhitenPoly = HWREG_READ_LRF(LRFDPBE32_BASE + LRFDPBE32_O_POLY0);
1319 ieeeHandlerState.txTest.storedWhitenInit = HWREGH_READ_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_WHITEINIT);
1320 uint32_t whitenPoly;
1321 switch (whitenMode)
1322 {
1323 case RCL_CMD_IEEE_WH_MODE_PRBS9:
1324 default:
1325 whitenPoly = RCL_HANDLER_IEEE_PRBS9_POLY;
1326 break;
1327 case RCL_CMD_IEEE_WH_MODE_PRBS15:
1328 whitenPoly = RCL_HANDLER_IEEE_PRBS15_POLY;
1329 break;
1330 case RCL_CMD_IEEE_WH_MODE_PRBS32:
1331 whitenPoly = RCL_HANDLER_IEEE_PRBS32_POLY;
1332 break;
1333 }
1334 HWREG_WRITE_LRF(LRFDPBE32_BASE + LRFDPBE32_O_POLY0) = whitenPoly;
1335 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_WHITEINIT) = RCL_HANDLER_IEEE_PRBS_INIT;
1336 }
1337
1338 ieeeHandlerState.txTest.restoreOpt |= RCL_HANDLER_IEEE_RESTORE_SFD;
1339 ieeeHandlerState.txTest.storedMdmSyncA = HWREG_READ_LRF(LRFDPBE32_BASE + LRFDPBE32_O_MDMSYNCA);
1340 /* Set non-standard SFD */
1341 HWREG_WRITE_LRF(LRFDPBE32_BASE + LRFDPBE32_O_MDMSYNCA) = ieeeHandlerState.txTest.storedMdmSyncA ^ 0x00FF;
1342 }
1343 /* Enable radio */
1344 LRF_enable();
1345
1346 RCL_CommandStatus startTimeStatus = RCL_Scheduler_setStartStopTimeEarliestStart(cmd, earliestStartTime);
1347 if (startTimeStatus >= RCL_CommandStatus_Finished)
1348 {
1349 cmd->status = startTimeStatus;
1350 rclEvents.lastCmdDone = 1;
1351 }
1352 else
1353 {
1354 /* Program frequency word */
1355 LRF_programFrequency(txCmd->rfFrequency, true);
1356
1357 /* Enable interrupts */
1358 LRF_enableHwInterrupt(LRF_EventOpDone.value | LRF_EventOpError.value);
1359
1360 /* Post cmd */
1361 Log_printf(RclCore, Log_VERBOSE, "Starting infinite TX");
1362
1363 LRF_waitForTopsmReady();
1364 HWREG_WRITE_LRF(LRFDPBE_BASE + LRFDPBE_O_API) = PBE_IEEE_REGDEF_API_OP_TX;
1365 }
1366 }
1367 }
1368
1369 if (cmd->status == RCL_CommandStatus_Active)
1370 {
1371 if (rclEventsIn.timerStart != 0)
1372 {
1373 rclEvents.cmdStarted = 1;
1374 }
1375 if (lrfEvents.opDone != 0)
1376 {
1377 RCL_CommandStatus endStatus = ieeeHandlerState.common.endStatus;
1378 if (endStatus == RCL_CommandStatus_Active)
1379 {
1380 cmd->status = RCL_CommandStatus_Finished;
1381 }
1382 else
1383 {
1384 cmd->status = endStatus;
1385 }
1386 rclEvents.lastCmdDone = 1;
1387 }
1388 else if (lrfEvents.opError != 0)
1389 {
1390 RCL_CommandStatus endStatus = ieeeHandlerState.common.endStatus;
1391 if (endStatus == RCL_CommandStatus_Active)
1392 {
1393 cmd->status = RCL_Handler_Ieee_findPbeErrorEndStatus(HWREGH_READ_LRF(LRFD_BUFRAM_BASE + PBE_COMMON_RAM_O_ENDCAUSE));
1394 }
1395 else
1396 {
1397 cmd->status = endStatus;
1398 }
1399 rclEvents.lastCmdDone = 1;
1400 }
1401 else
1402 {
1403 /* Other events need to be handled unconditionally */
1404 }
1405 }
1406
1407 if (rclEvents.lastCmdDone != 0)
1408 {
1409 LRF_disable();
1410 LRF_disableSynthRefsys();
1411 if ((ieeeHandlerState.txTest.restoreOpt & RCL_HANDLER_IEEE_RESTORE_MODCTRL) != 0)
1412 {
1413 HWREG_WRITE_LRF(LRFDMDM_BASE + LRFDMDM_O_MODCTRL) = HWREG_READ_LRF(LRFDMDM_BASE + LRFDMDM_O_MODCTRL) & (~LRFDMDM_MODCTRL_TONEINSERT_M);
1414 }
1415 if ((ieeeHandlerState.txTest.restoreOpt & RCL_HANDLER_IEEE_RESTORE_WHITENING) != 0)
1416 {
1417 HWREG_WRITE_LRF(LRFDPBE32_BASE + LRFDPBE32_O_POLY0) = ieeeHandlerState.txTest.storedWhitenPoly;
1418 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_WHITEINIT) = ieeeHandlerState.txTest.storedWhitenInit;
1419 }
1420 if ((ieeeHandlerState.txTest.restoreOpt & RCL_HANDLER_IEEE_RESTORE_SFD) != 0)
1421 {
1422 HWREG_WRITE_LRF(LRFDPBE32_BASE + LRFDPBE32_O_MDMSYNCA) = ieeeHandlerState.txTest.storedMdmSyncA;
1423 }
1424 }
1425
1426 return rclEvents;
1427 }
1428
1429 /*
1430 * ======== RCL_IEEE_Tx_submit ========
1431 */
RCL_IEEE_Tx_submit(RCL_CmdIeeeRxTx * cmd,RCL_CmdIeee_TxAction * txAction)1432 RCL_CommandStatus RCL_IEEE_Tx_submit(RCL_CmdIeeeRxTx *cmd, RCL_CmdIeee_TxAction *txAction)
1433 {
1434 RCL_CommandStatus status = RCL_CommandStatus_Idle;
1435 /* Can't submit action again if already submitted */
1436 if (txAction->txStatus != RCL_CommandStatus_Idle && txAction->txStatus < RCL_CommandStatus_Finished)
1437 {
1438 return RCL_CommandStatus_Error_AlreadySubmitted;
1439 }
1440
1441 uintptr_t key = HwiP_disable();
1442 /* Check if command is finished */
1443 if (cmd == NULL || cmd->common.status >= RCL_CommandStatus_Finished)
1444 {
1445 /* TODO: New status */
1446 Log_printf(RclCore, Log_ERROR, "Command ended before TX action was submitted");
1447 status = RCL_CommandStatus_Error;
1448 }
1449 else if (cmd->txAction != NULL && cmd->txAction->txStatus != RCL_CommandStatus_Idle && cmd->txAction->txStatus < RCL_CommandStatus_Finished)
1450 {
1451 /* Another TX action is already running */
1452 status = RCL_CommandStatus_Error_AlreadySubmitted;
1453 }
1454 /* Extra check in case user modified status field */
1455 else if (rclSchedulerState.currCmd == &cmd->common && ieeeHandlerState.rxTx.txState != txStateNoTx)
1456 {
1457 status = RCL_CommandStatus_Error_AlreadySubmitted;
1458 }
1459
1460 if (status != RCL_CommandStatus_Idle)
1461 {
1462 HwiP_restore(key);
1463
1464 return status;
1465 }
1466 else
1467 {
1468 /* Insert TX action */
1469 txAction->txStatus = RCL_CommandStatus_Idle;
1470 cmd->txAction = txAction;
1471
1472 if (rclSchedulerState.currCmd == &cmd->common)
1473 {
1474 /* Trigger handler */
1475 ieeeHandlerState.rxTx.txState = txStateNewAction;
1476 RCL_Scheduler_postEvent(&cmd->common, RCL_EventHandlerCmdUpdate);
1477 }
1478 HwiP_restore(key);
1479
1480 return txAction->txStatus;
1481 }
1482 }
1483
1484 /*
1485 * ======== RCL_IEEE_Tx_stop ========
1486 */
RCL_IEEE_Tx_stop(RCL_CmdIeeeRxTx * cmd,RCL_StopType stopType)1487 RCL_CommandStatus RCL_IEEE_Tx_stop(RCL_CmdIeeeRxTx *cmd, RCL_StopType stopType)
1488 {
1489 if (cmd == NULL)
1490 {
1491 return RCL_CommandStatus_Error_Param;
1492 }
1493 uintptr_t key = HwiP_disable();
1494 RCL_CmdIeee_TxAction *txAction = cmd->txAction;
1495 if (txAction == NULL)
1496 {
1497 /* No TX action at all */
1498 HwiP_restore(key);
1499 return RCL_CommandStatus_Error_Param;
1500 }
1501 /* Check command state */
1502 else if (cmd->common.status < RCL_CommandStatus_Active)
1503 {
1504 /* TX action can be descheduled without any other action */
1505 txAction->txStatus = RCL_CommandStatus_DescheduledApi;
1506 /* In this case, we have to set the TX action to NULL to avoid it starting */
1507 cmd->txAction = NULL;
1508 }
1509 else if (cmd->common.status < RCL_CommandStatus_Finished)
1510 {
1511 /* Inform handler about stop */
1512 ieeeHandlerState.rxTx.txActionStop = stopType;
1513 RCL_Scheduler_postEvent(&cmd->common, RCL_EventHandlerCmdUpdate);
1514 }
1515 else
1516 {
1517 /* Command finished; nothing to do about TX action */
1518 }
1519 HwiP_restore(key);
1520
1521 return txAction->txStatus;
1522 }
1523
1524 /*
1525 * ======== RCL_Handler_Ieee_updateRxCurBufferAndFifo ========
1526 */
RCL_Handler_Ieee_updateRxCurBufferAndFifo(List_List * rxBuffers)1527 static void RCL_Handler_Ieee_updateRxCurBufferAndFifo(List_List *rxBuffers)
1528 {
1529 RCL_MultiBuffer *curBuffer = ieeeHandlerState.common.curBuffer;
1530
1531 if (curBuffer == NULL)
1532 {
1533 curBuffer = RCL_MultiBuffer_findFirstWritableBuffer((RCL_MultiBuffer *)rxBuffers->head);
1534 }
1535 ieeeHandlerState.common.curBuffer = curBuffer;
1536
1537 uint32_t rxSpace = RCL_MultiBuffer_findAvailableRxSpace(curBuffer);
1538
1539 LRF_setRxFifoEffSz(rxSpace);
1540 }
1541
1542 /*
1543 * ======== RCL_Handler_Ieee_findPbeErrorEndStatus ========
1544 */
RCL_Handler_Ieee_findPbeErrorEndStatus(uint16_t pbeEndStatus)1545 static RCL_CommandStatus RCL_Handler_Ieee_findPbeErrorEndStatus(uint16_t pbeEndStatus)
1546 {
1547 RCL_CommandStatus status;
1548 switch (pbeEndStatus)
1549 {
1550 case PBE_COMMON_RAM_ENDCAUSE_STAT_ERR_RXF:
1551 status = RCL_CommandStatus_Error_RxFifo;
1552 break;
1553 case PBE_COMMON_RAM_ENDCAUSE_STAT_ERR_TXF:
1554 status = RCL_CommandStatus_Error_TxFifo;
1555 break;
1556 case PBE_COMMON_RAM_ENDCAUSE_STAT_ERR_SYNTH:
1557 status = RCL_CommandStatus_Error_Synth;
1558 break;
1559 case PBE_COMMON_RAM_ENDCAUSE_STAT_RXTIMEOUT:
1560 status = RCL_CommandStatus_RxTimeout;
1561 break;
1562 case PBE_COMMON_RAM_ENDCAUSE_STAT_EOPSTOP:
1563 status = RCL_Scheduler_findStopStatus(RCL_StopType_Graceful);
1564 break;
1565 case PBE_COMMON_RAM_ENDCAUSE_STAT_ERR_STOP:
1566 if (ieeeHandlerState.common.apiHardStopPending)
1567 {
1568 status = RCL_CommandStatus_HardStopApi;
1569 }
1570 else
1571 {
1572 status = RCL_Scheduler_findStopStatus(RCL_StopType_Hard);
1573 }
1574 break;
1575 case PBE_COMMON_RAM_ENDCAUSE_STAT_ERR_BADOP:
1576 status = RCL_CommandStatus_Error_UnknownOp;
1577 break;
1578 default:
1579 Log_printf(RclCore, Log_ERROR, "Unexpected error 0x%04X from PBE", pbeEndStatus);
1580 status = RCL_CommandStatus_Error;
1581 break;
1582 }
1583 return status;
1584 }
1585
1586 /*
1587 * ======== RCL_Handler_Ieee_maskEventsByFifoConf ========
1588 */
RCL_Handler_Ieee_maskEventsByFifoConf(uint32_t mask,uint16_t fifoConfVal,bool activeUpdate)1589 static uint32_t RCL_Handler_Ieee_maskEventsByFifoConf(uint32_t mask, uint16_t fifoConfVal, bool activeUpdate)
1590 {
1591 /* Remove events that will not give an entry in the RX FIFO, based on FIFOCFG, unless active update is used */
1592 if (!activeUpdate)
1593 {
1594 /* Remove events that will not give an entry in the RX FIFO, based on FIFOCFG. */
1595 mask &= ~(((fifoConfVal & PBE_IEEE_RAM_FIFOCFG_AUTOFLUSHIGN_M) ? LRF_EventRxIgnored.value : 0) |
1596 ((fifoConfVal & PBE_IEEE_RAM_FIFOCFG_AUTOFLUSHCRC_M) ? LRF_EventRxNok.value : 0) |
1597 LRF_EventRxBufFull.value);
1598 }
1599 return mask;
1600 }
1601
1602 /*
1603 * ======== RCL_Handler_Ieee_updateRxStats ========
1604 */
RCL_Handler_Ieee_updateStats(RCL_StatsIeee * stats,uint32_t startTime)1605 static void RCL_Handler_Ieee_updateStats(RCL_StatsIeee *stats, uint32_t startTime)
1606 {
1607 if (stats != NULL)
1608 {
1609 /* Read LASTTIMESTAMP andf LASTTIMESTAMPH as one unit */
1610 uint32_t lastTimestamp = HWREG_READ_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_LASTTIMESTAMPL);
1611
1612 /* Check if a new value is found in the first timestamp */
1613 if (lastTimestamp == startTime)
1614 {
1615 stats->timestampValid = false;
1616 }
1617 else {
1618 stats->timestampValid = true;
1619 stats->lastTimestamp = lastTimestamp;
1620 }
1621 stats->lastRssi = HWREGH_READ_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_LASTRSSI);
1622 stats->nRxNok = HWREGH_READ_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NRXNOK);
1623 stats->nRxFifoFull = HWREGH_READ_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NRXFIFOFULL);
1624 stats->nRxOk = HWREGH_READ_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NRXOK);
1625 stats->nRxIgnored = HWREGH_READ_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NRXIGNORED);
1626 stats->nTx = HWREGH_READ_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NTX);
1627 stats->nTxAck = HWREGH_READ_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NTXACK);
1628 }
1629 }
1630
1631 /*
1632 * ======== RCL_Handler_Ieee_initStats ========
1633 */
RCL_Handler_Ieee_initStats(RCL_StatsIeee * stats,uint32_t startTime)1634 static bool RCL_Handler_Ieee_initStats(RCL_StatsIeee *stats, uint32_t startTime)
1635 {
1636 if (stats != NULL)
1637 {
1638 /* Set timestamp to start time of command (will not occur again) to know if a valid value has been found */
1639 /* This accesses PBE_IEEE_RAM_O_LASTTIMESTAMPL and PBE_IEEE_RAM_O_LASTTIMESTAMPH */
1640 HWREG_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_LASTTIMESTAMPL) = startTime;
1641 stats->timestampValid = false;
1642 stats->lastRssi = LRF_RSSI_INVALID;
1643 if (stats->config.accumulate != 0)
1644 {
1645 /* Copy existing values into PBE */
1646 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NRXNOK) = stats->nRxNok;
1647 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NRXFIFOFULL) = stats->nRxFifoFull;
1648 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NRXOK) = stats->nRxOk;
1649 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NRXIGNORED) = stats->nRxIgnored;
1650 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NTX) = stats->nTx;
1651 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NTXACK) = stats->nTxAck;
1652 }
1653 else
1654 {
1655 /* Reset existing values in PBE */
1656 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NRXNOK) = 0;
1657 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NRXFIFOFULL) = 0;
1658 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NRXOK) = 0;
1659 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NRXIGNORED) = 0;
1660 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NTX) = 0;
1661 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NTXACK) = 0;
1662
1663 stats->nRxNok = 0;
1664 stats->nRxFifoFull = 0;
1665 stats->nRxOk = 0;
1666 stats->nRxIgnored = 0;
1667 stats->nTx = 0;
1668 stats->nTxAck = 0;
1669 }
1670 return stats->config.activeUpdate;
1671 }
1672 else
1673 {
1674 /* Reset existing values in PBE */
1675 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NRXNOK) = 0;
1676 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NRXFIFOFULL) = 0;
1677 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NRXOK) = 0;
1678 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NRXIGNORED) = 0;
1679 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_NTX) = 0;
1680
1681 return false;
1682 }
1683 }
1684
RCL_Handler_Ieee_setCustomEventTime(uint32_t eventTime,uint32_t timeMargin,bool hardStop)1685 static bool RCL_Handler_Ieee_setCustomEventTime(uint32_t eventTime, uint32_t timeMargin, bool hardStop)
1686 {
1687 uint32_t activeStopTime;
1688 if (rclSchedulerState.hardStopInfo.stopReason != RCL_SchedulerStopReason_None)
1689 {
1690 if (rclSchedulerState.hardStopInfo.stopReason == RCL_SchedulerStopReason_Timeout)
1691 {
1692 activeStopTime = rclSchedulerState.hardStopInfo.cmdStopTime;
1693 }
1694 else if (rclSchedulerState.hardStopInfo.stopReason == RCL_SchedulerStopReason_Scheduling)
1695 {
1696 activeStopTime = rclSchedulerState.hardStopInfo.schedStopTime;
1697 }
1698 else
1699 {
1700 /* Otherwise API stop is active and command should stop shortly */
1701 activeStopTime = RCL_Scheduler_getCurrentTime();
1702 }
1703 }
1704 if (rclSchedulerState.hardStopInfo.stopReason == RCL_SchedulerStopReason_None ||
1705 RCL_Scheduler_isLater(eventTime + timeMargin, activeStopTime))
1706 {
1707 if (hardStop)
1708 {
1709 /* Program hard stop time */
1710 hal_setup_hard_stop_time(eventTime);
1711 /* Flag as custom time */
1712 ieeeHandlerState.common.nextEventTime = eventTime;
1713 ieeeHandlerState.common.eventTimeType = customHardStop;
1714 }
1715 else
1716 {
1717 /* Change the stop time and set it to a soft timing */
1718 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_OPCFG) =
1719 HWREGH_READ_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_OPCFG) | PBE_IEEE_RAM_OPCFG_SYSTIM0BEH_M;
1720 hal_setup_hard_stop_time(eventTime);
1721 hal_enable_hard_stop_time_irq();
1722 ieeeHandlerState.common.nextEventTime = eventTime;
1723 ieeeHandlerState.common.eventTimeType = customEvent;
1724 }
1725 return true;
1726 }
1727 else
1728 {
1729 /* Command will stop very soon, so time not set */
1730 if (ieeeHandlerState.common.eventTimeType != noEvent)
1731 {
1732 RCL_Handler_Ieee_restoreStopTime();
1733 }
1734 return false;
1735 }
1736 }
1737
RCL_Handler_Ieee_restoreStopTime(void)1738 static bool RCL_Handler_Ieee_restoreStopTime(void)
1739 {
1740 if (ieeeHandlerState.common.eventTimeType != noEvent)
1741 {
1742 hal_cancel_hard_stop_time();
1743 /* Set back stop event */
1744 RCL_StopType stopType = RCL_Scheduler_setStopTimes();
1745
1746 /* Clear systimer event in TOPsm to avoid an old event stopping the RX */
1747 HWREGH_WRITE_LRF(LRFDPBE_BASE + LRFDPBE_O_EVTCLR0) = LRFDPBE_EVTCLR0_SYSTCMP0_M;
1748
1749 /* Set stop time back to hard stop */
1750 HWREGH_WRITE_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_OPCFG) =
1751 HWREGH_READ_LRF(LRFD_BUFRAM_BASE + PBE_IEEE_RAM_O_OPCFG) & ~PBE_IEEE_RAM_OPCFG_SYSTIM0BEH_M;
1752
1753 ieeeHandlerState.common.eventTimeType = noEvent;
1754 /* If hard stop already occurred, it needs to be executed (unless already planned) */
1755 /* Other stop types returned should not need special handling */
1756 if (stopType == RCL_StopType_Hard)
1757 {
1758 if (rclSchedulerState.hardStopInfo.apiStopEnabled == 0)
1759 {
1760 LRF_sendHardStop();
1761 rclSchedulerState.hardStopInfo.apiStopEnabled = 1;
1762 }
1763 return true;
1764 }
1765 }
1766 return false;
1767 }
1768 #endif
1769