/*************************************************************************************************/
|
/*!
|
* \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;
|
}
|