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