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