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  *  ======== RCL.c ========
34  */
35 
36 #include <stdint.h>
37 #include <stddef.h>
38 #include <stdbool.h>
39 #include <string.h>
40 
41 #include <ti/drivers/rcl/hal/hal.h>
42 #include <ti/drivers/rcl/RCL.h>
43 #include <ti/drivers/rcl/LRF.h>
44 #include <ti/drivers/rcl/RCL_Scheduler.h>
45 #include <ti/drivers/rcl/RCL_Profiling.h>
46 #include <ti/drivers/rcl/RCL_Tracer.h>
47 #include <ti/drivers/rcl/RCL_Debug.h>
48 #include <ti/drivers/rcl/RCL_Version.h>
49 
50 #include <ti/drivers/dpl/SemaphoreP.h>
51 #include <ti/drivers/dpl/HwiP.h>
52 
53 #include <ti/log/Log.h>
54 
55 
56 /* Globals */
57 static bool isInitialized = 0;
58 RCL rclState;
59 
60 static void rclCommandHwi(void);
61 static void rclDispatchHwi(void);
62 static void rclSchedulerHwi(void);
63 static void rclPowerNotify(RCL_PowerEvent eventType);
64 static RCL_CommandStatus rclStop(RCL_Command_Handle c, RCL_StopType stopType, RCL_SchedulerStopReason stopReason);
65 
66 /* Hooks */
67 
68 /**
69  * @brief Hook function to determine what stop type the ongoing command should get
70  *
71  * @param[in] currentCmd Currently running command
72  * @param[in] newCmd New command to potentially take its place
73  *
74  * @return ::RCL_StopType How the ongoing command will be terminated if not already terminated when needed
75  */
76 RCL_StopType policyHook(RCL_Command *currentCmd, RCL_Command *newCmd);
77 
78 /**
79  * @brief Hook function to allow dynamic PHY change
80  *
81  * @param[in] rclState RCL state
82  * @param[in] client Current RCL client
83  * @param[in] cmd Currently running command
84  *
85  */
86 void phyHook(RCL *rclState, RCL_Handle client, RCL_Command *cmd);
87 
88 /**
89  * @brief Hook function to schedule new command
90  *
91  * @param[in] rclSchedulerState Scheduler state
92  * @param[in] client Current RCL client
93  * @param[in] cmd Command to be scheduled
94  *
95  */
96 void scheduleHook(RCL_SchedulerState *rclSchedulerState, RCL_Handle client, RCL_Command *cmd);
97 
98 /* command - highest pri hwi  */
rclCommandHwi(void)99 static void rclCommandHwi(void)
100 {
101     RCL_Command *cmd = rclSchedulerState.currCmd;
102     /* We expect no call with NULL pointer, but if debug is off, handle the error by returning safely. */
103     RCL_Debug_assert(NULL != cmd);
104 
105     /* If command is already finished, return without doing anything */
106     if (NULL == cmd || cmd->status >= RCL_CommandStatus_Finished)
107     {
108         return;
109     }
110 
111     RCL_Client *client = cmd->runtime.client;
112 
113     /*** 1. Get events */
114     /* 1a. Posted RCL events */
115     RCL_Events rclEventsIn = rclSchedulerState.postedRclEvents;
116     RCL_Events rclEventsOut = RCL_EventNone;
117     rclSchedulerState.postedRclEvents = RCL_EventNone;
118 
119     /* 1b. Hardware LRF events */
120     LRF_Events lrfEvents = {.value = hal_get_command_ifg_reg()};
121 
122     /* 1c. Timer events mapped to RCL events */
123     HalTimerEvent timerEvt = hal_check_clear_timer_compare();
124     switch (timerEvt)
125     {
126         case HAL_TIMER_EVT_SETUP:
127             rclEventsIn.setup = 1;
128             break;
129         case HAL_TIMER_EVT_START:
130             rclEventsIn.timerStart = 1;
131             break;
132         case HAL_TIMER_EVT_HARD_STOP:
133             rclEventsIn.hardStop = 1;
134             break;
135         case HAL_TIMER_EVT_PACKET_TIMEOUT:
136             rclEventsIn.packetTimeout = 1;
137             break;
138         case HAL_TIMER_EVT_GRACEFUL_STOP:
139             rclEventsIn.gracefulStop = 1;
140             break;
141         default:
142             break;
143     }
144 
145     Log_printf(RclCore, Log_DEBUG, "Command input events RCL: 0x%08X; LRF: 0x%08X", rclEventsIn.value, lrfEvents.value);
146 
147     /*** 2. Handle stop or setup event (either from timer or posted SW event) */
148     /* Hardstop immediately, graceful is up to the handler */
149     if (rclEventsIn.gracefulStop || rclEventsIn.hardStop || rclEventsIn.descheduleStop)
150     {
151         /* If not started yet, just cancel timer and increment status to
152          * finished; bypasses handler later */
153         if (cmd->status == RCL_CommandStatus_Scheduled)
154         {
155             hal_cancel_start_time();
156             if (rclEventsIn.hardStop)
157             {
158                 rclSchedulerState.descheduleReason = rclSchedulerState.hardStopInfo.stopReason;
159             }
160             else if (rclEventsIn.gracefulStop)
161             {
162                 rclSchedulerState.descheduleReason = rclSchedulerState.gracefulStopInfo.stopReason;
163             }
164             else
165             {
166                 /* The event is descheduleStop - source already set */
167             }
168             cmd->status = RCL_Scheduler_findStopStatus(RCL_StopType_DescheduleOnly);
169             /* This codepath skips the handler call below which is normally responsible for setting
170              * this (see comment above). These eventsOut are stored into deferredEvents and eventually
171              * trigger a callback to the client telling them about the requested stop on this command.
172              */
173             rclEventsOut.lastCmdDone = 1;
174         }
175         else /* Command started, handle hardStop here */
176         {
177             if (rclEventsIn.hardStop)
178             {
179                 LRF_hardStop();
180             }
181         }
182     }
183     else if (rclEventsIn.setup)
184     {
185         /* Load TOPsm images, settings, etc */
186         LRF_RadioState lrfState = rclState.lrfState;
187         if (rclState.lrfConfig != client->lrfConfig)
188         {
189             /* Different config than last time: Ensure settings are reloaded */
190             rclState.lrfConfig = client->lrfConfig;
191             if (lrfState > RadioState_ImagesLoaded)
192             {
193                 lrfState = RadioState_ImagesLoaded;
194             }
195         }
196         LRF_SetupResult result = LRF_setupRadio(rclState.lrfConfig, cmd->phyFeatures, lrfState);
197         if (result != SetupResult_Ok)
198         {
199             Log_printf(RclCoreShort, Log_ERROR, "Setup failed with code %1d", result);
200             cmd->status = RCL_CommandStatus_Error_Setup;
201             rclEventsOut.lastCmdDone = 1;
202             rclState.lrfState = RadioState_Down;
203         }
204         else
205         {
206             rclState.lrfState = RadioState_Configured;
207         }
208 
209         /* Set power constraints since the radio has been set up */
210         if (rclState.powerState == RCL_standbyAllow)
211         {
212             hal_power_set_constraint();
213             rclState.powerState = RCL_standbyDisallow;
214         }
215     }
216 
217     /*** 3. Invoke handler FSM with new events */
218     if (cmd->status >= RCL_CommandStatus_Scheduled && cmd->status < RCL_CommandStatus_Finished)
219     {
220         rclEventsOut = cmd->runtime.handler(cmd, lrfEvents, rclEventsIn);
221     }
222     Log_printf(RclCore, Log_DEBUG, "RCL out: 0x%08X", rclEventsOut.value);
223 
224     /*** 4. If the command was caused to start now, configure end timeouts */
225     if (rclEventsOut.cmdStarted)
226     {
227         RCL_StopType immediateStop = RCL_Scheduler_setStopTimes();
228         if (immediateStop != RCL_StopType_None)
229         {
230             rclStop(rclSchedulerState.currCmd, immediateStop, RCL_SchedulerStopReason_Timeout);
231         }
232     }
233     /*** 5. If the command raises an event indicating the need for partial radio setup, do it */
234     if (rclEventsOut.partialSetup)
235     {
236         /* Rerun the radio setup */
237         LRF_RadioState lrfState = rclState.lrfState;
238 
239         LRF_SetupResult result = LRF_setupRadio(rclState.lrfConfig, rclSchedulerState.requestedPhyFeatures, lrfState);
240         if (result != SetupResult_Ok)
241         {
242             Log_printf(RclCoreShort, Log_ERROR, "Setup failed with code %1d", result);
243             cmd->status = RCL_CommandStatus_Error_Setup;
244             rclEventsOut.lastCmdDone = 1;
245             rclState.lrfState = RadioState_Down;
246         }
247         else
248         {
249             rclState.lrfState = RadioState_Configured;
250         }
251         /* Invoke command handler again to resume the ongoing operation */
252         RCL_Scheduler_postEvent(rclSchedulerState.currCmd, RCL_EventHandlerCmdUpdate);
253     }
254 
255     /*** 6. If stop did not happen, queued event might be delayed */
256     if (rclEventsIn.gracefulStop && cmd->status < RCL_CommandStatus_Finished)
257     {
258         rclEventsOut.stopDelayed = 1;
259     }
260     if (rclEventsIn.descheduleStop && cmd->status < RCL_CommandStatus_Finished)
261     {
262         rclEventsOut.stopRejected = 1;
263     }
264 
265     /* We pass the stop things to dispatch even if not subscribed to current,
266      * since they affect the next queued command
267      */
268     RCL_Events rclEventMask = cmd->runtime.rclCallbackMask;
269     rclEventMask.stopDelayed = 1;
270     rclEventMask.stopRejected = 1;
271 
272     /*** 7. Filter events, pass stop-rejected to setup, to alert nextCmd */
273     client->deferredRclEvents.value |= rclEventMask.value & rclEventsOut.value;
274 
275     /*** 8. If finished or have events, invoke setup */
276     if (cmd->status >= RCL_CommandStatus_Finished || client->deferredRclEvents.value)
277     {
278         if (cmd->status >= RCL_CommandStatus_Finished)
279         {
280             hal_cancel_graceful_stop_time();
281             hal_cancel_hard_stop_time();
282             hal_disable_all_command_radio_interrupts();
283 
284             /* It's now safe to go into standby */
285             if (rclState.powerState != RCL_standbyAllow)
286             {
287                 hal_power_release_constraint();
288                 rclState.powerState = RCL_standbyAllow;
289             }
290         }
291         hal_trigger_dispatch_fsm();
292     }
293 }
294 
rclDispatchHwi(void)295 static void rclDispatchHwi(void)
296 {
297     /* Keep track for callback */
298     RCL_Command *currCmd = rclSchedulerState.currCmd;
299     if (NULL == currCmd)
300     {
301         /* An extra pending IRQ may happen if the command HWI comes in and
302            finishes the command during this ISR. If so, the extra IRQ can be ignored. */
303         return;
304     }
305     RCL_Client *currClient = currCmd->runtime.client;
306     RCL_Debug_assert(currClient != NULL);
307 
308     /* Get hardware LRF events that should produce callback */
309     LRF_Events lrfEvents = { .value = hal_get_dispatch_ifg_reg() };
310 
311     /* If command completed, take out of circulation for possible reuse and
312         * trigger scheduler in any case to tear down / start next.
313         */
314     if (currCmd->status >= RCL_CommandStatus_Finished)
315     {
316         /* It's done or failed */
317         Log_printf(RclCore, Log_DEBUG, "Finished; Clearing currCmd, calling scheduleHwi");
318         RCL_Command *doneCmd = rclSchedulerState.currCmd;
319         rclSchedulerState.currCmd = NULL;
320 
321         if (doneCmd->runtime.client->pendCmd == doneCmd)
322         {
323             Log_printf(RclCore, Log_INFO, "Unpending client: 0x%08X", doneCmd->runtime.client);
324             doneCmd->runtime.client->pendCmd = NULL;
325             SemaphoreP_post(&doneCmd->runtime.client->pendSem);
326         }
327 
328         /* See if more waiting, schedule-fsm runs after this hwi */
329         hal_trigger_scheduler_fsm();
330         /* Now rclSchedulerState.currCmd is free in case client needs to queue something new before scheduler */
331         /* Disable the radio HW interrupts for dispatch */
332         hal_disable_all_dispatch_radio_interrupts();
333     }
334 
335     /* Atomically retrieve what is supposed to get done since higher priority
336      * interrupt sets these
337      */
338     uintptr_t key = HwiP_disable();
339     RCL_Events rclEvents = { .value = currClient->deferredRclEvents.value & currCmd->runtime.rclCallbackMask.value };
340     RCL_Events stopEvents = { .value = currClient->deferredRclEvents.value &  ((RCL_Events){.stopDelayed = 1, .stopRejected = 1}).value };
341     RCL_Callback callback = currCmd->runtime.callback;
342 
343     currClient->deferredRclEvents = RCL_EventNone;
344     HwiP_restore(key);
345 
346     /* Check if failed stop is a problem for the next command */
347     if (stopEvents.value) {
348         RCL_Command *nextCmd = rclState.nextCmd;
349         if (nextCmd && !nextCmd->allowDelay && rclSchedulerState.nextWantsStop)
350         {
351             Log_printf(RclCore, Log_DEBUG, "Command deplanned due to rejected start: 0x%08X", nextCmd);
352 
353             /* Next command does not allow delay, and stop did not take immediate effect */
354             rclState.nextCmd = NULL;
355             rclSchedulerState.nextWantsStop = false;
356             /* In a world where there can be a queue of commands, get a new next command, lower pri executes after this hwi */
357             hal_trigger_scheduler_fsm();
358 
359             /* Synthesize rejected start */
360             nextCmd->status = RCL_CommandStatus_RejectedStart;
361             RCL_Events rejectedEvents = RCL_EventNone;
362             rejectedEvents.lastCmdDone = 1;
363             rejectedEvents.startRejected = 1;
364 
365             /* Notify owner that command was rejected, if they care */
366             RCL_Callback callback = nextCmd->runtime.callback;
367             if (callback && rejectedEvents.value & nextCmd->runtime.rclCallbackMask.value)
368             {
369                 callback(nextCmd, LRF_EventNone, rejectedEvents);
370             }
371         }
372     }
373 
374     /* Notify owner about events */
375     Log_printf(RclCore, Log_INFO, "Client callback: LRF: 0x%08X, RCL: 0x%08X", lrfEvents.value, rclEvents.value);
376     if ((lrfEvents.value || rclEvents.value) && callback)
377     {
378         callback(currCmd, lrfEvents, rclEvents);
379     }
380 }
381 
382 /*
383  *  ======== policyHook ========
384  */
policyHook(RCL_Command * currentCmd,RCL_Command * newCmd)385 __attribute__((weak)) RCL_StopType policyHook(RCL_Command *currentCmd, RCL_Command *newCmd)
386 {
387     (void) currentCmd;
388 
389     switch(newCmd->conflictPolicy)
390     {
391         case RCL_ConflictPolicy_AlwaysInterrupt:
392             return RCL_StopType_Hard;
393         case RCL_ConflictPolicy_Polite:
394             return RCL_StopType_Graceful;
395         case RCL_ConflictPolicy_NeverInterrupt:
396             return RCL_StopType_DescheduleOnly;
397         default:
398             return RCL_StopType_None;
399     }
400 }
401 
402 /*
403  *  ======== phyHook ========
404  */
phyHook(RCL * rclState,RCL_Handle client,RCL_Command * cmd)405 __attribute__((weak)) void phyHook(RCL *rclState, RCL_Handle client, RCL_Command *cmd)
406 {
407     (void)rclState;
408     (void)client;
409     (void)cmd;
410 }
411 
412 /*
413  *  ======== scheduleHook ========
414  */
scheduleHook(RCL_SchedulerState * rclSchedulerState,RCL_Handle client,RCL_Command * cmd)415 __attribute__((weak)) void scheduleHook(RCL_SchedulerState *rclSchedulerState, RCL_Handle client, RCL_Command *cmd)
416 {
417     (void) client;
418 
419     RCL_StopType stopType = policyHook(rclSchedulerState->currCmd, cmd);
420 
421     if (rclSchedulerState->currCmd && stopType > RCL_StopType_None)
422     {
423         uint32_t now = RCL_Scheduler_getCurrentTime();
424         uint32_t then = cmd->timing.absStartTime - RCL_SCHEDULER_MARGIN_LOAD*2;
425         bool urgent = !RCL_Scheduler_isLater(now, then);
426 
427         rclSchedulerState->nextWantsStop = true;
428         if (cmd->scheduling == RCL_Schedule_Now || urgent)
429         {
430             Log_printf(RclCore, Log_VERBOSE, "Stopping old command immediately, urgent=%d", urgent);
431             rclStop(rclSchedulerState->currCmd, stopType, RCL_SchedulerStopReason_Scheduling);
432         }
433         else
434         {
435             Log_printf(RclCore, Log_VERBOSE, "Setting running command stop-time to 0x%08X", then);
436             RCL_SchedulerStopInfo *stopInfo;
437             switch (stopType)
438             {
439                 case RCL_StopType_Hard:
440                     stopInfo = &rclSchedulerState->hardStopInfo;
441                     break;
442                 case RCL_StopType_Graceful:
443                     stopInfo = &rclSchedulerState->gracefulStopInfo;
444                     break;
445                 default:
446                     stopInfo = NULL;
447                     break;
448             }
449             if (stopInfo != NULL)
450             {
451                 /* Set selected scheduler stop */
452                 /* Check if this changes active stop times */
453                 RCL_StopType immediateStop = RCL_Scheduler_setSchedStopTime(stopInfo, then);
454                 /* Stop now if new stop time is in the past */
455                 if (immediateStop != RCL_StopType_None)
456                 {
457                     rclStop(rclSchedulerState->currCmd, immediateStop, RCL_SchedulerStopReason_Scheduling);
458                 }
459             }
460         }
461     }
462 }
463 
464 /* scheduler - lowest pri hwi */
rclSchedulerHwi(void)465 static void rclSchedulerHwi(void)
466 {
467     /* Find next command */
468     /* TODO: See RCL-344 */
469     RCL_Command *nextCmd = rclState.nextCmd;
470     Log_printf(RclCore, Log_VERBOSE, "SchedulerHwi nextCmd: 0x%08X", nextCmd);
471 
472     /* If nothing is pending, pack up */
473     if (NULL == nextCmd)
474     {
475         RCL_Profiling_eventHook(RCL_ProfilingEvent_PostprocStop);
476         return;
477     }
478 
479     int32_t deltaTime = (nextCmd->scheduling == RCL_Schedule_Now)
480                             ? (int32_t)RCL_SCHEDULER_TRIG_NOW_DELAY
481                             : RCL_Scheduler_delta(RCL_Scheduler_getCurrentTime(), nextCmd->timing.absStartTime);
482     /* Event must be in future */
483     if (false == nextCmd->allowDelay && deltaTime < (int32_t)RCL_SCHEDULER_TRIG_NOW_DELAY)
484     {
485         nextCmd->status = RCL_CommandStatus_Error_StartTooLate;
486         return;
487     }
488 
489     /* ScheduleHook might immediately terminate running command and cmdHwi might update currCmd to NULL */
490     scheduleHook(&rclSchedulerState, nextCmd->runtime.client, nextCmd);
491 
492     /* Determine if can progress nextCmd to currCmd and invoke cmdHwi */
493     if (rclSchedulerState.currCmd)
494     {
495         /* If there is a current command, we will be triggered again after it is
496          * finished.
497          */
498         Log_printf(RclCore, Log_VERBOSE, "Could not promote; command 0x%08X running, status 0x%02X",
499                                 rclSchedulerState.currCmd,
500                                 rclSchedulerState.currCmd->status);
501         /* A finished command should not be the current command */
502         RCL_Debug_assert(rclSchedulerState.currCmd->status < RCL_CommandStatus_Finished);
503         return;
504     }
505 
506     /* Adopt new command */
507     rclState.nextCmd = NULL;
508     memset((void *)&rclSchedulerState, 0, sizeof(rclSchedulerState));
509     rclSchedulerState.currCmd = nextCmd;
510     /* Set up callback interrupts */
511     hal_init_dispatch_radio_interrupts(nextCmd->runtime.lrfCallbackMask.value);
512 
513     /* Next command may need different PHY applied; prepare this */
514     phyHook(&rclState, nextCmd->runtime.client, rclSchedulerState.currCmd);
515 
516     if (deltaTime <= (int32_t)RCL_SCHEDULER_SLEEP_CUTOFF)
517     {
518         Log_printf(RclCore, Log_DEBUG, "Calling setup immediately, %d µs until event", deltaTime >> 2);
519 
520         /* Command handler does last mile config and trigger */
521         RCL_Scheduler_postEvent(rclSchedulerState.currCmd, RCL_EventSetup);
522     }
523     else
524     {
525         /* TODO: See RCL-275 */
526         uint32_t margin = RCL_SCHEDULER_MARGIN_ARM + RCL_SCHEDULER_WAKEUP_MARGIN;
527         if (rclState.lrfState < RadioState_Configured ||
528             nextCmd->runtime.client->lrfConfig != rclState.lrfConfig)
529         {
530             margin += RCL_SCHEDULER_MARGIN_CONFIGURE;
531         }
532         if (rclState.lrfState < RadioState_ImagesLoaded ||
533             LRF_imagesNeedUpdate(nextCmd->runtime.client->lrfConfig))
534         {
535             margin += RCL_SCHEDULER_MARGIN_LOAD;
536         }
537 
538         /* Use LRF:systim0 (SYSTIM:CH2) as wakeup source */
539         hal_setup_setup_time(rclSchedulerState.currCmd->timing.absStartTime - margin);
540 
541         /* SetupFSM triggers command handler due to timer */
542         Log_printf(RclCore, Log_DEBUG, "Wakeup scheduled at 0x%08X (.25µs) with margin subtracted from deltaTime: %d µs", rclSchedulerState.currCmd->timing.absStartTime, deltaTime >> 2);
543     }
544 }
545 
546 /* Power event routine */
rclPowerNotify(RCL_PowerEvent eventType)547 static void rclPowerNotify(RCL_PowerEvent eventType)
548 {
549     if (eventType == RCL_POWER_STANDBY_AWAKE)
550     {
551         /*
552         * Executed every time the device exits the standby sleep state.
553         */
554         /* Reinitialize the tracer */
555         RCL_Tracer_wakeup();
556 
557         /* The rest is only done if at least one client is open */
558         if (rclState.numClients > 0)
559         {
560             RCL_GPIO_enable();
561 
562             /* Mark radio as not configured */
563             if (rclState.lrfState > RadioState_ImagesLoaded)
564             {
565                 rclState.lrfState = RadioState_ImagesLoaded;
566             }
567             RCL_Profiling_eventHook(RCL_ProfilingEvent_PreprocStart);
568             /* Check if there is an active command at the time of wakeup */
569             /* If so, enable start timer interrupt */
570             if (rclSchedulerState.currCmd != NULL)
571             {
572                 /* Restore the DBELL interrupt masks */
573                 hal_enable_setup_time_irq();
574                 /* Set up callback interrupts */
575                 hal_init_dispatch_radio_interrupts(rclSchedulerState.currCmd->runtime.lrfCallbackMask.value);
576             }
577         }
578     }
579     else if (eventType == RCL_POWER_STANDBY_ENTER)
580     {
581         RCL_Tracer_standby();
582         /* The rest is only done if at least one client is open */
583         if (rclState.numClients > 0)
584         {
585             RCL_GPIO_disable();
586         }
587     }
588     else
589     {
590         /* No action */
591     }
592 }
593 
594 /*
595  *  ======== RCL_init ========
596  */
RCL_init(void)597 void RCL_init(void)
598 {
599     if (!isInitialized)
600     {
601         /* Initialize state  */
602         rclState = (RCL){
603             .numClients = 0,
604             .powerNotifyEnableCount = 0,
605             .lrfState = RadioState_Down,
606             .lrfConfig = NULL,
607         };
608         hal_init_fsm(rclDispatchHwi, rclSchedulerHwi, rclCommandHwi);
609         /* Ensure temperature compensation of TX output power and RF Trims */
610         hal_temperature_init();
611         isInitialized = true;
612         /* Initialize the RF Tracer */
613         RCL_Tracer_enable();
614     }
615 }
616 
617 /*
618  *  ======== RCL_openPowerNotifications ========
619  */
RCL_openPowerNotifications(void)620 void RCL_openPowerNotifications(void)
621 {
622     if (rclState.powerNotifyEnableCount == 0)
623     {
624         hal_power_open(&rclPowerNotify);
625     }
626     rclState.powerNotifyEnableCount++;
627     /* Check for overflow */
628     RCL_Debug_assert(rclState.powerNotifyEnableCount > 0);
629 }
630 
631 /*
632  *  ======== RCL_closePowerNotifications ========
633  */
RCL_closePowerNotifications(void)634 void RCL_closePowerNotifications(void)
635 {
636     /* Check for underflow */
637     RCL_Debug_assert(rclState.powerNotifyEnableCount > 0);
638     rclState.powerNotifyEnableCount--;
639     if (rclState.powerNotifyEnableCount == 0)
640     {
641         hal_power_close();
642     }
643 }
644 
645 /*
646  *  ======== RCL_open ========
647  */
RCL_open(RCL_Client * c,const LRF_Config * lrfConfig)648 RCL_Handle RCL_open(RCL_Client *c, const LRF_Config *lrfConfig)
649 {
650     *c = (RCL_Client){ 0 };
651 
652     SemaphoreP_Params sp;
653     SemaphoreP_Params_init(&sp);
654     sp.mode = SemaphoreP_Mode_BINARY;
655     SemaphoreP_construct(&c->pendSem, 0, &sp);
656 
657     if (rclState.numClients == 0)
658     {
659         /* Do the operations below if no client was open prior to this one */
660         RCL_openPowerNotifications();
661 
662         /* Temporary solution: Enable all needed clocks here */
663         LRF_rclEnableRadioClocks();
664 
665         /* Initialize the RCL GPIOs */
666         RCL_GPIO_enable();
667 
668         /* Temporary solution: Enable the high performance clock buffer.
669          * This could be done together with synth REFSYS at the start and end of the command */
670         hal_enable_clk_buffer();
671     }
672 
673     Log_printf(RclCore, Log_DEBUG,"Git SHA:  %08x%08x", (uint32_t) (RCL_VERSION_SHA >> 32),  RCL_VERSION_SHA);
674     Log_printf(RclCore, Log_DEBUG,"RCL Version: %08x, Channel number: %x", RCL_VERSION, RCL_VERSION_CHANNEL_NUMBER);
675 
676     rclState.numClients += 1;
677     c->lrfConfig = lrfConfig;
678 
679     return c;
680 }
681 
682 /*
683  *  ======== RCL_close ========
684  */
RCL_close(RCL_Handle h)685 void RCL_close(RCL_Handle h)
686 {
687     rclState.numClients -= 1;
688 
689     if (h->lrfConfig == rclState.lrfConfig)
690     {
691         /* Closing a client using the current LRF config */
692         /* Invalidate config, in case it gets changed before reused in a new client */
693         rclState.lrfConfig = NULL;
694     }
695 
696     if (rclState.numClients == 0)
697     {
698         /* Disable RCL GPIO pins*/
699         RCL_GPIO_disable();
700 
701         /* Temporary solution: Disable clocks here */
702         LRF_rclDisableRadioClocks();
703 
704         RCL_closePowerNotifications();
705     }
706 
707     return;
708 }
709 
710 /*
711  *  ======== submitHook ========
712  */
submitHook(RCL * rclState,RCL_Handle h,RCL_Command * c)713 __attribute__((weak)) bool submitHook(RCL *rclState, RCL_Handle h, RCL_Command *c)
714 {
715     (void) h;
716     /* Reject if already pending, can't be bothered with list  */
717     if (rclState->nextCmd != NULL)
718     {
719         c->status = RCL_CommandStatus_Error_CommandQueueFull;
720         return false;
721     }
722 
723     /* Schedule command */
724     rclState->nextCmd = c;
725     c->status = RCL_CommandStatus_Scheduled;
726 
727     return true;
728 }
729 
730 /*
731  *  ======== RCL_Command_submit ========
732  */
RCL_Command_submit(RCL_Handle h,RCL_Command_Handle c)733 RCL_CommandStatus RCL_Command_submit(RCL_Handle h, RCL_Command_Handle c)
734 {
735     RCL_Command *cmd = (RCL_Command *)c;
736 
737     /* Can't submit again if already submitted */
738     if (cmd->status != RCL_CommandStatus_Idle && cmd->status < RCL_CommandStatus_Finished)
739     {
740         return RCL_CommandStatus_Error_AlreadySubmitted;
741     }
742 
743     /* Extra check in case user modified status field */
744     if (cmd == rclSchedulerState.currCmd || cmd == rclState.nextCmd)
745     {
746         return RCL_CommandStatus_Error_AlreadySubmitted;
747     }
748 
749     /* Point back to originator */
750     cmd->runtime.client = h;
751 
752     /* Try to submit */
753     if (!submitHook(&rclState, h, cmd))
754     {
755         return RCL_CommandStatus_Error;
756     }
757 
758     /* Trigger scheduling FSM */
759     hal_trigger_scheduler_fsm();
760 
761     return cmd->status;
762 }
763 
764 /*
765  *  ======== RCL_Command_pend ========
766  */
RCL_Command_pend(RCL_Command_Handle c)767 RCL_CommandStatus RCL_Command_pend(RCL_Command_Handle c)
768 {
769     RCL_Command *cmd = (RCL_Command *)c;
770     RCL_Handle h = cmd->runtime.client;
771 
772     RCL_Debug_assert(cmd != NULL);
773     RCL_Debug_assert(h != NULL);
774 
775     /* Indicate interest */
776     h->pendCmd = cmd;
777 
778     /* Check if already completed. */
779     if (cmd->status > RCL_CommandStatus_Active)
780     {
781         h->pendCmd = NULL;
782         return cmd->status;
783     }
784 
785     /* Wait */
786     SemaphoreP_pend(&h->pendSem, SemaphoreP_WAIT_FOREVER);
787 
788     return cmd->status;
789 }
790 
791 /*
792  *  ======== RCL_Command_stop ========
793  */
RCL_Command_stop(RCL_Command_Handle c,RCL_StopType stopType)794 RCL_CommandStatus RCL_Command_stop(RCL_Command_Handle c, RCL_StopType stopType)
795 {
796     return rclStop(c, stopType, RCL_SchedulerStopReason_Api);
797 }
798 
799 /*
800  *  ======== rclStop ========
801  */
rclStop(RCL_Command_Handle c,RCL_StopType stopType,RCL_SchedulerStopReason stopReason)802 static RCL_CommandStatus rclStop(RCL_Command_Handle c, RCL_StopType stopType, RCL_SchedulerStopReason stopReason)
803 {
804     RCL_Command *cmd = (RCL_Command *)c;
805 
806     /* Check if command is already finished or no stop is done*/
807     uintptr_t key = HwiP_disable();
808     if (cmd->status < RCL_CommandStatus_Queued || cmd->status >= RCL_CommandStatus_Finished || stopType == RCL_StopType_None)
809     {
810         HwiP_restore(key);
811         Log_printf(RclCoreShort, Log_DEBUG, "Stop called with type: %d, resulting status: 0x%02X", stopType, cmd->status);
812         return cmd->status;
813     }
814 
815     if (cmd->status == RCL_CommandStatus_Queued)
816     {
817         rclState.nextCmd = NULL;
818         cmd->status = RCL_Scheduler_findStopStatus(RCL_StopType_DescheduleOnly);
819         /* Cancel scheduler stop of current command */
820         RCL_StopType stopType;
821         /* In the unlikely case that the cmd stop time was very shortly after the canceled sched stop time,
822         the event could be missed and needs to be handled */
823         stopType = RCL_Scheduler_cancelSchedStopTime(&rclSchedulerState.hardStopInfo);
824         if (stopType == RCL_StopType_Hard && rclSchedulerState.currCmd != NULL)
825         {
826             /* Stop currently running command (not the one being canceled) immediately,
827              * as command stop time must have been passed */
828             if (rclSchedulerState.hardStopInfo.apiStopEnabled == 0)
829             {
830                 LRF_sendHardStop();
831                 rclSchedulerState.hardStopInfo.apiStopEnabled = 1;
832             }
833             RCL_Scheduler_postEvent(rclSchedulerState.currCmd, RCL_EventHardStop);
834         }
835         else
836         {
837             stopType = RCL_Scheduler_cancelSchedStopTime(&rclSchedulerState.gracefulStopInfo);
838             if (stopType == RCL_StopType_Graceful && rclSchedulerState.currCmd != NULL)
839             {
840                 /* Stop currently running command (not the one being canceled) gracefully now,
841                  * as command stop time must have been passed */
842                 /* Do not send graceful stop if any stop is already sent */
843                 if (rclSchedulerState.gracefulStopInfo.apiStopEnabled == 0 &&
844                     rclSchedulerState.hardStopInfo.apiStopEnabled == 0)
845                 {
846                     LRF_sendGracefulStop();
847                     rclSchedulerState.gracefulStopInfo.apiStopEnabled = 1;
848                 }
849                 RCL_Scheduler_postEvent(rclSchedulerState.currCmd, RCL_EventGracefulStop);
850             }
851         }
852     }
853     else
854     {
855         RCL_Events rclEvent = RCL_EventNone;
856         switch (stopType)
857         {
858             case RCL_StopType_DescheduleOnly:
859                 rclSchedulerState.descheduleReason = stopReason;
860                 rclEvent.descheduleStop = 1;
861                 break;
862             case RCL_StopType_Graceful:
863                 rclEvent.gracefulStop = 1;
864                 rclSchedulerState.gracefulStopInfo.stopReason = stopReason;
865                 /* Do not send graceful stop if any stop is already sent */
866                 if (rclSchedulerState.gracefulStopInfo.apiStopEnabled == 0 &&
867                     rclSchedulerState.hardStopInfo.apiStopEnabled == 0)
868                 {
869                     LRF_sendGracefulStop();
870                     rclSchedulerState.gracefulStopInfo.apiStopEnabled = 1;
871                 }
872                 break;
873             case RCL_StopType_Hard:
874                 /* Do not send hard stop if already sent (but send if graceful stop is sent) */
875                 rclEvent.hardStop = 1;
876                 rclSchedulerState.hardStopInfo.stopReason = stopReason;
877                 if (rclSchedulerState.hardStopInfo.apiStopEnabled == 0)
878                 {
879                     LRF_sendHardStop();
880                     rclSchedulerState.hardStopInfo.apiStopEnabled = 1;
881                 }
882                 break;
883             case RCL_StopType_None:
884             default:
885                 RCL_Debug_assert(1 == 0);
886                 break;
887         }
888         RCL_Scheduler_postEvent(cmd, rclEvent);
889     }
890     HwiP_restore(key);
891 
892     Log_printf(RclCoreShort, Log_DEBUG, "Stop called with type: %d, resulting status: 0x%02X", stopType, cmd->status);
893     return cmd->status;
894 }
895 
896 /*
897  *  ======== RCL_readRssi ========
898  */
RCL_readRssi(void)899 int8_t RCL_readRssi(void)
900 {
901     int8_t rssiVal;
902 
903     if (rclState.lrfState < RadioState_Configured)
904     {
905         rssiVal = LRF_RSSI_INVALID;
906     }
907     else
908     {
909         rssiVal = LRF_readRssi();
910     }
911     return rssiVal;
912 }
913