/* * timer.c * * Copyright(c) 1998 - 2009 Texas Instruments. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Texas Instruments nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** \file timer.c * \brief The timers services OS-Independent layer over the OS-API timer services which are OS-Dependent. * * \see timer.h, osapi.c */ #include <linux/kernel.h> #define __FILE_ID__ FILE_ID_0 #include "osApi.h" #include "report.h" #include "queue.h" #include "context.h" #include "timer.h" #define EXPIRY_QUE_SIZE 64 /* support case where all timers expire during recovery or error */ /* The timer module structure (common to all timers) */ typedef struct { TI_HANDLE hOs; TI_HANDLE hReport; TI_HANDLE hContext; TI_UINT32 uContextId; /* The ID allocated to this module on registration to context module */ TI_HANDLE hInitQueue; /* Handle of the Init-Queue */ TI_HANDLE hOperQueue; /* Handle of the Operational-Queue */ TI_BOOL bOperState; /* TRUE when the driver is in operational state (not init or recovery) */ TI_UINT32 uTwdInitCount; /* Increments on each TWD init (i.e. recovery) */ TI_UINT32 uTimersCount; /* Number of created timers */ } TTimerModule; /* Per timer structure */ typedef struct { TI_HANDLE hTimerModule; /* The timer module handle (see TTimerModule, needed on expiry) */ TI_HANDLE hOsTimerObj; /* The OS-API timer object handle */ TQueNodeHdr tQueNodeHdr; /* The header used for queueing the timer */ TTimerCbFunc fExpiryCbFunc; /* The CB-function provided by the timer user for expiration */ TI_HANDLE hExpiryCbHndl; /* The CB-function handle */ TI_UINT32 uIntervalMsec; /* The timer duration in Msec */ TI_BOOL bPeriodic; /* If TRUE, restarted after each expiry */ TI_BOOL bOperStateWhenStarted; /* The bOperState value when the timer was started */ TI_UINT32 uTwdInitCountWhenStarted; /* The uTwdInitCount value when the timer was started */ } TTimerInfo; /** * \fn tmr_Create * \brief Create the timer module * * Allocate and clear the timer module object. * * \note This is NOT a specific timer creation! (see tmr_CreateTimer) * \param hOs - Handle to Os Abstraction Layer * \return Handle of the allocated object * \sa tmr_Destroy */ TI_HANDLE tmr_Create (TI_HANDLE hOs) { TI_HANDLE hTimerModule; /* allocate module object */ hTimerModule = os_memoryAlloc (hOs, sizeof(TTimerModule)); if (!hTimerModule) { WLAN_OS_REPORT (("tmr_Create(): Allocation failed!!\n")); return NULL; } os_memoryZero (hOs, hTimerModule, (sizeof(TTimerModule))); return (hTimerModule); } /** * \fn tmr_Destroy * \brief Destroy the module. * * Free the module's queues and object. * * \note This is NOT a specific timer destruction! (see tmr_DestroyTimer) * \param hTimerModule - The module object * \return TI_OK on success or TI_NOK on failure * \sa tmr_Create */ TI_STATUS tmr_Destroy (TI_HANDLE hTimerModule) { TTimerModule *pTimerModule = (TTimerModule *)hTimerModule; if (!pTimerModule) { WLAN_OS_REPORT (("tmr_Destroy(): ERROR - NULL timer!\n")); return TI_NOK; } /* Alert if there are still timers that were not destroyed */ if (pTimerModule->uTimersCount) { WLAN_OS_REPORT (("tmr_Destroy(): ERROR - Destroying Timer module but not all timers were destroyed!!\n")); } #if 0 /* Clear queues (critical section is used inside these functions) */ tmr_ClearInitQueue (hTimerModule); tmr_ClearOperQueue (hTimerModule); #endif /* Destroy the module's queues (protect in critical section)) */ context_EnterCriticalSection (pTimerModule->hContext); que_Destroy (pTimerModule->hInitQueue); que_Destroy (pTimerModule->hOperQueue); context_LeaveCriticalSection (pTimerModule->hContext); /* free module object */ os_memoryFree (pTimerModule->hOs, pTimerModule, sizeof(TTimerModule)); return TI_OK; } /** * \fn tmr_Free * \brief Free the memory. * * Free the module's queues and object. * * \param hTimerModule - The module object * \return TI_OK on success or TI_NOK on failure * \sa tmr_Create */ TI_STATUS tmr_Free(TI_HANDLE hTimerModule) { TTimerModule *pTimerModule = (TTimerModule *)hTimerModule; if (!pTimerModule) { WLAN_OS_REPORT (("tmr_Free(): ERROR - NULL timer!\n")); return TI_NOK; } /* free module object */ os_memoryFree (pTimerModule->hOs, pTimerModule, sizeof(TTimerModule)); return TI_OK; } /** * \fn tmr_ClearInitQueue & tmr_ClearOperQueue * \brief Clear Init/Operationsl queue * * Dequeue all queued timers. * * \note * \param hTimerModule - The object * \return void * \sa */ void tmr_ClearInitQueue (TI_HANDLE hTimerModule) { TTimerModule *pTimerModule = (TTimerModule *)hTimerModule; TTimerInfo *pTimerInfo; /* The timer handle */ do { context_EnterCriticalSection (pTimerModule->hContext); pTimerInfo = que_Dequeue (pTimerModule->hInitQueue); context_LeaveCriticalSection (pTimerModule->hContext); } while (pTimerInfo != NULL); } void tmr_ClearOperQueue (TI_HANDLE hTimerModule) { TTimerModule *pTimerModule = (TTimerModule *)hTimerModule; TTimerInfo *pTimerInfo; /* The timer handle */ do { context_EnterCriticalSection (pTimerModule->hContext); pTimerInfo = que_Dequeue (pTimerModule->hOperQueue); context_LeaveCriticalSection (pTimerModule->hContext); } while (pTimerInfo != NULL); } /** * \fn tmr_Init * \brief Init required handles * * Init required handles and module variables, create the init-queue and * operational-queue, and register as the context-engine client. * * \note * \param hTimerModule - The queue object * \param hOs - Handle to Os Abstraction Layer * \param hReport - Handle to report module * \param hContext - Handle to context module * \return void * \sa */ void tmr_Init (TI_HANDLE hTimerModule, TI_HANDLE hOs, TI_HANDLE hReport, TI_HANDLE hContext) { TTimerModule *pTimerModule = (TTimerModule *)hTimerModule; TI_UINT32 uNodeHeaderOffset; if (!pTimerModule) { WLAN_OS_REPORT (("tmr_Init(): ERROR - NULL timer!\n")); return; } pTimerModule->hOs = hOs; pTimerModule->hReport = hReport; pTimerModule->hContext = hContext; pTimerModule->bOperState = TI_FALSE; pTimerModule->uTimersCount = 0; pTimerModule->uTwdInitCount = 0; /* The offset of the queue-node-header from timer structure entry is needed by the queue */ uNodeHeaderOffset = TI_FIELD_OFFSET(TTimerInfo, tQueNodeHdr); /* Create and initialize the Init and Operational queues (for timers expiry events) */ pTimerModule->hInitQueue = que_Create (pTimerModule->hOs, pTimerModule->hReport, EXPIRY_QUE_SIZE, uNodeHeaderOffset); pTimerModule->hOperQueue = que_Create (pTimerModule->hOs, pTimerModule->hReport, EXPIRY_QUE_SIZE, uNodeHeaderOffset); /* Register to the context engine and get the client ID */ pTimerModule->uContextId = context_RegisterClient (pTimerModule->hContext, tmr_HandleExpiry, hTimerModule, TI_TRUE, "TIMER", sizeof("TIMER")); } /** * \fn tmr_UpdateDriverState * \brief Update driver state * * Under critical section, update driver state (operational or not), * and if opertional, clear init queue. * Leave critical section and if operational state, request schedule for handling * timer events in driver context (if any). * * \note * \param hTimerModule - The timer module object * \param bOperState - TRUE if driver state is now operational, FALSE if not. * \return void * \sa */ void tmr_UpdateDriverState (TI_HANDLE hTimerModule, TI_BOOL bOperState) { TTimerModule *pTimerModule = (TTimerModule *)hTimerModule; TTimerInfo *pTimerInfo; /* The timer handle */ if (!pTimerModule) { WLAN_OS_REPORT (("tmr_UpdateDriverState(): ERROR - NULL timer!\n")); return; } /* Enter critical section */ context_EnterCriticalSection (pTimerModule->hContext); if (bOperState == pTimerModule->bOperState) { context_LeaveCriticalSection (pTimerModule->hContext); TRACE1(pTimerModule->hReport, REPORT_SEVERITY_ERROR, "tmr_UpdateDriverState(): New bOperState (%d) is as current!\n", bOperState); return; } /* Save new state (TRUE means operational). */ pTimerModule->bOperState = bOperState; /* If new state is operational */ if (bOperState) { /* Increment the TWD initializations counter (for detecting recovery events). */ pTimerModule->uTwdInitCount++; } /* Leave critical section */ context_LeaveCriticalSection (pTimerModule->hContext); /* If new state is operational */ if (bOperState) { /* Empty the init queue (obsolete). */ do { context_EnterCriticalSection (pTimerModule->hContext); pTimerInfo = que_Dequeue (pTimerModule->hInitQueue); context_LeaveCriticalSection (pTimerModule->hContext); } while (pTimerInfo != NULL); } /* If new state is operational, request switch to driver context for handling timer events */ if (bOperState) { context_RequestSchedule (pTimerModule->hContext, pTimerModule->uContextId); } } /** * \fn tmr_CreateTimer * \brief Create a new timer * * Create a new timer object, icluding creating a timer in the OS-API. * * \note This timer creation may be used only after tmr_Create() and tmr_Init() were executed!! * \param hTimerModule - The module handle * \return TI_HANDLE - The created timer handle * \sa tmr_DestroyTimer */ TI_HANDLE tmr_CreateTimer (TI_HANDLE hTimerModule) { TTimerModule *pTimerModule = (TTimerModule *)hTimerModule; /* The timer module handle */ TTimerInfo *pTimerInfo; /* The created timer handle */ if (!pTimerModule) { WLAN_OS_REPORT (("tmr_CreateTimer(): ERROR - NULL timer!\n")); return NULL; } /* Allocate timer object */ pTimerInfo = os_memoryAlloc (pTimerModule->hOs, sizeof(TTimerInfo)); if (!pTimerInfo) { WLAN_OS_REPORT (("tmr_CreateTimer(): Timer allocation failed!!\n")); return NULL; } os_memoryZero (pTimerModule->hOs, pTimerInfo, (sizeof(TTimerInfo))); /* Allocate OS-API timer, providing the common expiry callback with the current timer handle */ pTimerInfo->hOsTimerObj = os_timerCreate(pTimerModule->hOs, tmr_GetExpiry, (TI_HANDLE)pTimerInfo); if (!pTimerInfo->hOsTimerObj) { TRACE0(pTimerModule->hReport, REPORT_SEVERITY_CONSOLE ,"tmr_CreateTimer(): OS-API Timer allocation failed!!\n"); os_memoryFree (pTimerModule->hOs, pTimerInfo, sizeof(TTimerInfo)); WLAN_OS_REPORT (("tmr_CreateTimer(): OS-API Timer allocation failed!!\n")); return NULL; } /* Save the timer module handle in the created timer object (needed for the expiry callback) */ pTimerInfo->hTimerModule = hTimerModule; pTimerModule->uTimersCount++; /* count created timers */ /* Return the created timer handle */ return (TI_HANDLE)pTimerInfo; } /** * \fn tmr_DestroyTimer * \brief Destroy the specified timer * * Destroy the specified timer object, icluding the timer in the OS-API. * * \note This timer destruction function should be used before tmr_Destroy() is executed!! * \param hTimerInfo - The timer handle * \return TI_OK on success or TI_NOK on failure * \sa tmr_CreateTimer */ TI_STATUS tmr_DestroyTimer (TI_HANDLE hTimerInfo) { TTimerInfo *pTimerInfo = (TTimerInfo *)hTimerInfo; /* The timer handle */ TTimerModule *pTimerModule; /* The timer module handle */ if (!pTimerInfo) { return TI_NOK; } pTimerModule = (TTimerModule *)pTimerInfo->hTimerModule; if (!pTimerModule) { WLAN_OS_REPORT (("tmr_DestroyTimer(): ERROR - NULL timer!\n")); return TI_NOK; } /* Free the OS-API timer */ if (pTimerInfo->hOsTimerObj) { os_timerDestroy (pTimerModule->hOs, pTimerInfo->hOsTimerObj); pTimerModule->uTimersCount--; /* update created timers number */ } /* Free the timer object */ os_memoryFree (pTimerModule->hOs, hTimerInfo, sizeof(TTimerInfo)); return TI_OK; } /** * \fn tmr_StartTimer * \brief Start a timer * * Start the specified timer running. * * \note Periodic-Timer may be used by applications that serve the timer expiry * in a single context. * If an application can't finish serving the timer expiry in a single context, * e.g. periodic scan, then it isn't recommended to use the periodic timer service. * If such an application uses the periodic timer then it should protect itself from cases * where the timer expires again before the previous timer expiry processing is finished!! * \param hTimerInfo - The specific timer handle * \param fExpiryCbFunc - The timer's expiry callback function. * \param hExpiryCbHndl - The client's expiry callback function handle. * \param uIntervalMsec - The timer's duration in Msec. * \param bPeriodic - If TRUE, the timer is restarted after expiry. * \return void * \sa tmr_StopTimer, tmr_GetExpiry */ void tmr_StartTimer (TI_HANDLE hTimerInfo, TTimerCbFunc fExpiryCbFunc, TI_HANDLE hExpiryCbHndl, TI_UINT32 uIntervalMsec, TI_BOOL bPeriodic) { TTimerInfo *pTimerInfo = (TTimerInfo *)hTimerInfo; /* The timer handle */ TTimerModule *pTimerModule = (TTimerModule *)pTimerInfo->hTimerModule; /* The timer module handle */ if (!pTimerModule) { WLAN_OS_REPORT (("tmr_StartTimer(): ERROR - NULL timer!\n")); return; } /* Save the timer parameters. */ pTimerInfo->fExpiryCbFunc = fExpiryCbFunc; pTimerInfo->hExpiryCbHndl = hExpiryCbHndl; pTimerInfo->uIntervalMsec = uIntervalMsec; pTimerInfo->bPeriodic = bPeriodic; pTimerInfo->bOperStateWhenStarted = pTimerModule->bOperState; pTimerInfo->uTwdInitCountWhenStarted = pTimerModule->uTwdInitCount; /* Start OS-API timer running */ os_timerStart(pTimerModule->hOs, pTimerInfo->hOsTimerObj, uIntervalMsec); } /** * \fn tmr_StopTimer * \brief Stop a running timer * * Stop the specified timer. * * \note When using this function, it must be considered that timer expiry may happen * right before the timer is stopped, so it can't be assumed that this completely * prevents the timer expiry event! * \param hTimerInfo - The specific timer handle * \return void * \sa tmr_StartTimer */ void tmr_StopTimer (TI_HANDLE hTimerInfo) { TTimerInfo *pTimerInfo = (TTimerInfo *)hTimerInfo; /* The timer handle */ TTimerModule *pTimerModule = (TTimerModule *)pTimerInfo->hTimerModule; /* The timer module handle */ if (!pTimerModule) { WLAN_OS_REPORT (("tmr_StopTimer(): ERROR - NULL timer!\n")); return; } /* Stop OS-API timer running */ os_timerStop(pTimerModule->hOs, pTimerInfo->hOsTimerObj); /* Clear periodic flag to prevent timer restart if we are in tmr_HandleExpiry context. */ pTimerInfo->bPeriodic = TI_FALSE; } /** * \fn tmr_GetExpiry * \brief Called by OS-API upon any timer expiry * * This is the common callback function called upon expiartion of any timer. * It is called by the OS-API in timer expiry context and handles the transition * to the driver's context for handling the expiry event. * * \note * \param hTimerInfo - The specific timer handle * \return void * \sa tmr_HandleExpiry */ void tmr_GetExpiry (TI_HANDLE hTimerInfo) { TTimerInfo *pTimerInfo = (TTimerInfo *)hTimerInfo; /* The timer handle */ TTimerModule *pTimerModule = (TTimerModule *)pTimerInfo->hTimerModule; /* The timer module handle */ if (!pTimerModule) { WLAN_OS_REPORT (("tmr_GetExpiry(): ERROR - NULL timer!\n")); return; } /* Enter critical section */ context_EnterCriticalSection (pTimerModule->hContext); /* * If the expired timer was started when the driver's state was Operational, * insert it to the Operational-queue */ if (pTimerInfo->bOperStateWhenStarted) { que_Enqueue (pTimerModule->hOperQueue, hTimerInfo); } /* * Else (started when driver's state was NOT-Operational), if now the state is still * NOT Operational insert it to the Init-queue. * (If state changed from non-operational to operational the event is ignored) */ else if (!pTimerModule->bOperState) { que_Enqueue (pTimerModule->hInitQueue, hTimerInfo); } /* Leave critical section */ context_LeaveCriticalSection (pTimerModule->hContext); /* Request switch to driver context for handling timer events */ context_RequestSchedule (pTimerModule->hContext, pTimerModule->uContextId); } /** * \fn tmr_HandleExpiry * \brief Handles queued expiry events in driver context * * This is the Timer module's callback that is registered to the ContextEngine module to be invoked * from the driver task (after requested by tmr_GetExpiry through context_RequestSchedule ()). * It dequeues all expiry events from the queue that correlates to the current driver state, * and calls their users callbacks. * * \note * \param hTimerModule - The module object * \return void * \sa tmr_GetExpiry */ void tmr_HandleExpiry (TI_HANDLE hTimerModule) { TTimerModule *pTimerModule = (TTimerModule *)hTimerModule; /* The timer module handle */ TTimerInfo *pTimerInfo; /* The timer handle */ TI_BOOL bTwdInitOccured; /* Indicates if TWD init occured since timer start */ if (!pTimerModule) { WLAN_OS_REPORT (("tmr_HandleExpiry(): ERROR - NULL timer!\n")); return; } while (1) { /* Enter critical section */ context_EnterCriticalSection (pTimerModule->hContext); /* If current driver state is Operational, dequeue timer object from Operational-queue */ if (pTimerModule->bOperState) { pTimerInfo = (TTimerInfo *) que_Dequeue (pTimerModule->hOperQueue); } /* Else (driver state is NOT-Operational), dequeue timer object from Init-queue */ else { pTimerInfo = (TTimerInfo *) que_Dequeue (pTimerModule->hInitQueue); } /* Leave critical section */ context_LeaveCriticalSection (pTimerModule->hContext); /* If no more objects in queue, exit */ if (!pTimerInfo) { return; /** EXIT Point **/ } /* If current TWD-Init-Count is different than when the timer was started, Init occured. */ bTwdInitOccured = (pTimerModule->uTwdInitCount != pTimerInfo->uTwdInitCountWhenStarted); /* Call specific timer callback function */ pTimerInfo->fExpiryCbFunc (pTimerInfo->hExpiryCbHndl, bTwdInitOccured); /* If the expired timer is periodic, start it again. */ if (pTimerInfo->bPeriodic) { tmr_StartTimer ((TI_HANDLE)pTimerInfo, pTimerInfo->fExpiryCbFunc, pTimerInfo->hExpiryCbHndl, pTimerInfo->uIntervalMsec, pTimerInfo->bPeriodic); } } } /** * \fn tmr_PrintModule / tmr_PrintTimer * \brief Print module / timer information * * Print the module's information / a specific timer information. * * \note * \param The module / timer handle * \return void * \sa */ #ifdef TI_DBG void tmr_PrintModule (TI_HANDLE hTimerModule) { TTimerModule *pTimerModule = (TTimerModule *)hTimerModule; if (!pTimerModule) { WLAN_OS_REPORT (("tmr_PrintModule(): ERROR - NULL timer!\n")); return; } /* Print module parameters */ WLAN_OS_REPORT(("tmr_PrintModule(): uContextId=%d, bOperState=%d, uTwdInitCount=%d, uTimersCount=%d\n", pTimerModule->uContextId, pTimerModule->bOperState, pTimerModule->uTwdInitCount, pTimerModule->uTimersCount)); /* Print Init Queue Info */ WLAN_OS_REPORT(("tmr_PrintModule(): Init-Queue:\n")); que_Print(pTimerModule->hInitQueue); /* Print Operational Queue Info */ WLAN_OS_REPORT(("tmr_PrintModule(): Operational-Queue:\n")); que_Print(pTimerModule->hOperQueue); } void tmr_PrintTimer (TI_HANDLE hTimerInfo) { #ifdef REPORT_LOG TTimerInfo *pTimerInfo = (TTimerInfo *)hTimerInfo; /* The timer handle */ WLAN_OS_REPORT(("tmr_PrintTimer(): uIntervalMs=%d, bPeriodic=%d, bOperStateWhenStarted=%d, uTwdInitCountWhenStarted=%d, hOsTimerObj=0x%x, fExpiryCbFunc=0x%x\n", pTimerInfo->uIntervalMsec, pTimerInfo->bPeriodic, pTimerInfo->bOperStateWhenStarted, pTimerInfo->uTwdInitCountWhenStarted, pTimerInfo->hOsTimerObj, pTimerInfo->fExpiryCbFunc)); #endif } #endif /* TI_DBG */