diff -r 000000000000 -r 10c42ec6c05f utils/timer.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/utils/timer.c Tue Jun 29 12:34:26 2010 +0100 @@ -0,0 +1,600 @@ +/* + * timer.c + * + * Copyright(c) 1998 - 2010 Texas Instruments. All rights reserved. + * All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 or BSD License which accompanies + * this distribution. The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html and the BSD License is as below. + * + * 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 + */ + + +#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),MemoryNormal); + + 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; + + /* 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")); + } + + /* Clear queues */ + tmr_ClearInitQueue (hTimerModule); + tmr_ClearOperQueue (hTimerModule); + + /* Destroy the module's queues */ + que_Destroy (pTimerModule->hInitQueue); + que_Destroy (pTimerModule->hOperQueue); + + /* 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; + + /* 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; + + while (que_Dequeue (pTimerModule->hInitQueue) != NULL) {} +} + +void tmr_ClearOperQueue (TI_HANDLE hTimerModule) +{ + TTimerModule *pTimerModule = (TTimerModule *)hTimerModule; + + while (que_Dequeue (pTimerModule->hOperQueue) != 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; + + 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; + + if (bOperState == pTimerModule->bOperState) + { + TRACE1(pTimerModule->hReport, REPORT_SEVERITY_ERROR, "tmr_UpdateDriverState(): New bOperState (%d) is as current!\n", bOperState); + return; + } + + /* Enter critical section */ + CONTEXT_ENTER_CRITICAL_SECTION (pTimerModule->hContext); + + /* 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++; + + /* Empty the init queue (obsolete). */ + while (que_Dequeue (pTimerModule->hInitQueue) != NULL) {} + } + + /* Leave critical section */ + CONTEXT_LEAVE_CRITICAL_SECTION (pTimerModule->hContext); + + /* 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 */ + + /* Allocate timer object */ + pTimerInfo = os_memoryAlloc (pTimerModule->hOs, sizeof(TTimerInfo),MemoryNormal); + 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) + { + 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 = (TTimerModule *)pTimerInfo->hTimerModule; /* The timer module handle */ + + /* Free the OS-API timer */ + os_timerDestroy (pTimerModule->hOs, pTimerInfo->hOsTimerObj); + + /* Free the timer object */ + os_memoryFree (pTimerModule->hOs, hTimerInfo, sizeof(TTimerInfo)); + + pTimerModule->uTimersCount--; /* update created timers number */ + + 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 */ + + /* 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 */ + + /* 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 */ + + /* Enter critical section */ + CONTEXT_ENTER_CRITICAL_SECTION (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_LEAVE_CRITICAL_SECTION (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 */ + + while (1) + { + /* Enter critical section */ + CONTEXT_ENTER_CRITICAL_SECTION (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_LEAVE_CRITICAL_SECTION (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; + + /* 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) +{ + 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 /* TI_DBG */ + +