/*************************************************************************************************/ /*! * \file wsf_timer.c * * \brief Timer service. * * Copyright (c) 2009-2019 Arm Ltd. All Rights Reserved. * * Copyright (c) 2019-2020 Packetcraft, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /*************************************************************************************************/ #include "wsf_types.h" #include "wsf_queue.h" #include "wsf_timer.h" #include "wsf_assert.h" #include "wsf_cs.h" #include "wsf_trace.h" #include "pal_sys.h" /************************************************************************************************** Macros **************************************************************************************************/ #if (WSF_MS_PER_TICK == 10) /*! \brief WSF timer ticks per second. */ #define WSF_TIMER_TICKS_PER_SEC (1000 / WSF_MS_PER_TICK) /* convert seconds to timer ticks */ #define WSF_TIMER_SEC_TO_TICKS(sec) (WSF_TIMER_TICKS_PER_SEC * (sec)) /* convert milliseconds to timer ticks */ #define WSF_TIMER_MS_TO_TICKS(ms) ((ms) / WSF_MS_PER_TICK) #else #error "WSF_TIMER_MS_TO_TICKS and WSF_TIMER_SEC_TO_TICKS not defined for WSF_MS_PER_TICK" #endif /*! \brief Minimum RTC ticks required to go into sleep. */ #define WSF_TIMER_MIN_MS_FOR_POWERDOWN (2) #define WSF_TIMER_MAX_MS_FOR_POWERDOWN (10000) /************************************************************************************************** Global Variables **************************************************************************************************/ static wsfQueue_t wsfTimerTimerQueue; /*!< Timer queue */ /*! \brief Last SysTick value read. */ static uint32_t wsfTimerSysTickcLastTick = 0; /*************************************************************************************************/ /*! * \brief Remove a timer from queue. Note this function does not lock task scheduling. * * \param pTimer Pointer to timer. */ /*************************************************************************************************/ static void wsfTimerRemove(wsfTimer_t *pTimer) { wsfTimer_t *pElem; wsfTimer_t *pPrev = NULL; pElem = (wsfTimer_t *)wsfTimerTimerQueue.pHead; /* find timer in queue */ while (pElem != NULL) { if (pElem == pTimer) { break; } pPrev = pElem; pElem = pElem->pNext; } /* if timer found remove from queue */ if (pElem != NULL) { WsfQueueRemove(&wsfTimerTimerQueue, pTimer, pPrev); pTimer->isStarted = FALSE; } } /*************************************************************************************************/ /*! * \brief Insert a timer into the queue sorted by the timer expiration. * * \param pTimer Pointer to timer. * \param ticks Timer ticks until expiration. * \param mode Timer work mode. */ /*************************************************************************************************/ static void wsfTimerInsert(wsfTimer_t *pTimer, wsfTimerTicks_t ticks, uint8_t mode) { wsfTimer_t *pElem; wsfTimer_t *pPrev = NULL; /* task schedule lock */ uint32_t lock = WsfTaskLock(); /* if timer is already running stop it first */ if (pTimer->isStarted) { wsfTimerRemove(pTimer); } pTimer->isStarted = TRUE; pTimer->ticks = ticks; pTimer->count = ticks; pTimer->mode = mode; pElem = (wsfTimer_t *)wsfTimerTimerQueue.pHead; /* find insertion point in queue */ while (pElem != NULL) { if (pTimer->ticks < pElem->ticks) { break; } pPrev = pElem; pElem = pElem->pNext; } /* insert timer into queue */ WsfQueueInsert(&wsfTimerTimerQueue, pTimer, pPrev); /* task schedule unlock */ WsfTaskUnlock(lock); } /*************************************************************************************************/ /*! * \brief Return the number of ticks until the next timer expiration. Note that this * function can return zero even if a timer is running, indicating a timer * has expired but has not yet been serviced. * * \param pTimerRunning Returns TRUE if a timer is running, FALSE if no timers running. * * \return The number of ticks until the next timer expiration. */ /*************************************************************************************************/ static wsfTimerTicks_t WsfTimerNextExpiration(bool_t *pTimerRunning) { wsfTimerTicks_t ticks; /* task schedule lock */ uint32_t lock = WsfTaskLock(); if (wsfTimerTimerQueue.pHead == NULL) { *pTimerRunning = FALSE; ticks = 0; } else { *pTimerRunning = TRUE; ticks = ((wsfTimer_t *)wsfTimerTimerQueue.pHead)->ticks; } /* task schedule unlock */ WsfTaskUnlock(lock); return ticks; } /*************************************************************************************************/ /*! * \brief Initialize the timer service. This function should only be called once * upon system initialization. */ /*************************************************************************************************/ void WsfTimerInit(void) { WSF_QUEUE_INIT(&wsfTimerTimerQueue); } /*************************************************************************************************/ /*! * \brief Start a timer in units of seconds. * * \param pTimer Pointer to timer. * \param sec Seconds until expiration. * \param mode Timer work mode. */ /*************************************************************************************************/ void WsfTimerStartSec(wsfTimer_t *pTimer, wsfTimerTicks_t sec, uint8_t mode) { WSF_TRACE_INFO2("WsfTimerStartSec pTimer:0x%x ticks:%u", (uint32_t)pTimer, WSF_TIMER_SEC_TO_TICKS(sec)); /* insert timer into queue */ wsfTimerInsert(pTimer, WSF_TIMER_SEC_TO_TICKS(sec), mode); } /*************************************************************************************************/ /*! * \brief Start a timer in units of milliseconds. * * \param pTimer Pointer to timer. * \param ms Milliseconds until expiration. * \param mode Timer work mode. */ /*************************************************************************************************/ void WsfTimerStartMs(wsfTimer_t *pTimer, wsfTimerTicks_t ms, uint8_t mode) { WSF_TRACE_INFO2("WsfTimerStartMs pTimer:0x%x ticks:%u", (uint32_t)pTimer, WSF_TIMER_MS_TO_TICKS(ms)); /* insert timer into queue */ wsfTimerInsert(pTimer, WSF_TIMER_MS_TO_TICKS(ms), mode); } /*************************************************************************************************/ /*! * \brief Stop a timer. * * \param pTimer Pointer to timer. */ /*************************************************************************************************/ void WsfTimerStop(wsfTimer_t *pTimer) { WSF_TRACE_INFO1("WsfTimerStop pTimer:0x%x", pTimer); /* task schedule lock */ uint32_t lock = WsfTaskLock(); wsfTimerRemove(pTimer); /* task schedule unlock */ WsfTaskUnlock(lock); } /*************************************************************************************************/ /*! * \brief Update the timer service with the number of elapsed ticks. * * \param ticks Number of ticks since last update. */ /*************************************************************************************************/ void WsfTimerUpdate(wsfTimerTicks_t ticks) { wsfTimer_t *pElem; /* task schedule lock */ uint32_t lock = WsfTaskLock(); pElem = (wsfTimer_t *)wsfTimerTimerQueue.pHead; /* iterate over timer queue */ while (pElem != NULL) { /* decrement ticks while preventing underflow */ if (pElem->ticks > ticks) { pElem->ticks -= ticks; } else { pElem->ticks = 0; /* timer expired; set task for this timer as ready */ WsfTaskSetReady(pElem->handlerId, WSF_TIMER_EVENT); } pElem = pElem->pNext; } /* task schedule unlock */ WsfTaskUnlock(lock); } /*************************************************************************************************/ /*! * \brief Service expired timers for the given task. * * \param taskId Task ID. * * \return Pointer to timer or NULL. */ /*************************************************************************************************/ wsfTimer_t *WsfTimerServiceExpired(wsfTaskId_t taskId) { wsfTimer_t *pElem; wsfTimer_t *pPrev = NULL; /* Unused parameters */ (void)taskId; /* task schedule lock */ uint32_t lock = WsfTaskLock(); /* find expired timers in queue */ pElem = (wsfTimer_t *)wsfTimerTimerQueue.pHead; if ((pElem != NULL) && (pElem->ticks == 0)) { if (pElem->mode == WSF_TIMER_PERIODIC) { wsfTimerInsert(pElem, pElem->count, WSF_TIMER_PERIODIC); } else { /* remove timer from queue */ WsfQueueRemove(&wsfTimerTimerQueue, pElem, pPrev); pElem->isStarted = FALSE; } /* task schedule unlock */ WsfTaskUnlock(lock); WSF_TRACE_INFO1("Timer expired pTimer:0x%x", pElem); /* return timer */ return pElem; } /* task schedule unlock */ WsfTaskUnlock(lock); return NULL; } /*************************************************************************************************/ /*! * \brief Function for checking if there is an active timer and if there is enough time to * go to sleep and going to sleep. */ /*************************************************************************************************/ uint8_t WsfTimerSleepCheck(uint32_t *sleep_ms) { wsfTimerTicks_t nextExpiration; /* If PAL system is busy, no need to sleep. */ if (PalSysIsBusy()) { // active return 0; } bool_t running; nextExpiration = WsfTimerNextExpiration(&running); if (running) { uint32_t awake = WSF_MS_PER_TICK * nextExpiration; uint32_t elapsed = PalSysTickElapse(); /* if we have time to sleep before timer expiration */ if ((awake - elapsed) > WSF_TIMER_MIN_MS_FOR_POWERDOWN) { *sleep_ms = awake - elapsed; // Power down return 2; } else { /* Not enough time to go to powerdown. Let the system sleep. */ // Sleep return 1; } } else { *sleep_ms = WSF_TIMER_MAX_MS_FOR_POWERDOWN; // Power down return 2; } } //***************************************************************************** // // Calculate the elapsed time, and update the WSF software timers. // //***************************************************************************** void WsfTimerUpdateTicks(void) { uint32_t ui32CurrentTick, ui32ElapsedTicks; // // Read the continuous systick. // ui32CurrentTick = PalSysTickGet(); // // Figure out how long it has been since the last time we've read the // continuous systick. // if (ui32CurrentTick > wsfTimerSysTickcLastTick) { ui32ElapsedTicks = ui32CurrentTick - wsfTimerSysTickcLastTick; } else { ui32ElapsedTicks = 0xffffffff - wsfTimerSysTickcLastTick + ui32CurrentTick; } WsfTimerUpdate(ui32ElapsedTicks); wsfTimerSysTickcLastTick = ui32CurrentTick; }