| /****************************************************************************** |
| * |
| * This file is provided under a dual license. When you use or |
| * distribute this software, you may choose to be licensed under |
| * version 2 of the GNU General Public License ("GPLv2 License") |
| * or BSD License. |
| * |
| * GPLv2 License |
| * |
| * Copyright(C) 2016 MediaTek Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of version 2 of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| * See http://www.gnu.org/licenses/gpl-2.0.html for more details. |
| * |
| * BSD LICENSE |
| * |
| * Copyright(C) 2016 MediaTek Inc. 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 of the copyright holder 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 |
| * HOLDER 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. |
| * |
| *****************************************************************************/ |
| /* |
| ** Id: //Department/DaVinci/BRANCHES/MT6620_WIFI_DRIVER_V2_3/mgmt/cnm_timer.c#1 |
| */ |
| |
| /*! \file "cnm_timer.c" |
| * \brief |
| * |
| */ |
| |
| |
| /******************************************************************************* |
| * C O M P I L E R F L A G S |
| ******************************************************************************** |
| */ |
| |
| /******************************************************************************* |
| * E X T E R N A L R E F E R E N C E S |
| ******************************************************************************** |
| */ |
| #include "precomp.h" |
| |
| /******************************************************************************* |
| * C O N S T A N T S |
| ******************************************************************************** |
| */ |
| |
| /******************************************************************************* |
| * D A T A T Y P E S |
| ******************************************************************************** |
| */ |
| |
| /******************************************************************************* |
| * P U B L I C D A T A |
| ******************************************************************************** |
| */ |
| |
| /******************************************************************************* |
| * P R I V A T E D A T A |
| ******************************************************************************** |
| */ |
| |
| /******************************************************************************* |
| * M A C R O S |
| ******************************************************************************** |
| */ |
| |
| /******************************************************************************* |
| * F U N C T I O N D E C L A R A T I O N S |
| ******************************************************************************** |
| */ |
| |
| /******************************************************************************* |
| * F U N C T I O N S |
| ******************************************************************************** |
| */ |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief This routine is called to set the time to do the time out check. |
| * |
| * \param[in] rTimeout Time out interval from current time. |
| * |
| * \retval TRUE Success. |
| * |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static BOOLEAN cnmTimerSetTimer(IN P_ADAPTER_T prAdapter, IN OS_SYSTIME rTimeout) |
| { |
| P_ROOT_TIMER prRootTimer; |
| BOOLEAN fgNeedWakeLock; |
| |
| ASSERT(prAdapter); |
| |
| prRootTimer = &prAdapter->rRootTimer; |
| |
| kalSetTimer(prAdapter->prGlueInfo, rTimeout); |
| |
| if (rTimeout <= SEC_TO_SYSTIME(WAKE_LOCK_MAX_TIME)) { |
| fgNeedWakeLock = TRUE; |
| |
| if (!prRootTimer->fgWakeLocked) { |
| KAL_WAKE_LOCK(prAdapter, &prRootTimer->rWakeLock); |
| prRootTimer->fgWakeLocked = TRUE; |
| } |
| } else { |
| fgNeedWakeLock = FALSE; |
| } |
| |
| return fgNeedWakeLock; |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief This routines is called to initialize a root timer. |
| * |
| * \param[in] prAdapter |
| * |
| * \return (none) |
| */ |
| /*----------------------------------------------------------------------------*/ |
| VOID cnmTimerInitialize(IN P_ADAPTER_T prAdapter) |
| { |
| P_ROOT_TIMER prRootTimer; |
| |
| KAL_SPIN_LOCK_DECLARATION(); |
| |
| ASSERT(prAdapter); |
| |
| prRootTimer = &prAdapter->rRootTimer; |
| |
| /* Note: glue layer have configured timer */ |
| |
| KAL_ACQUIRE_SPIN_LOCK(prAdapter, SPIN_LOCK_TIMER); |
| LINK_INITIALIZE(&prRootTimer->rLinkHead); |
| KAL_RELEASE_SPIN_LOCK(prAdapter, SPIN_LOCK_TIMER); |
| |
| KAL_WAKE_LOCK_INIT(prAdapter, &prRootTimer->rWakeLock, "WLAN Timer"); |
| prRootTimer->fgWakeLocked = FALSE; |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief This routines is called to destroy a root timer. |
| * When WIFI is off, the token shall be returned back to system. |
| * |
| * \param[in] |
| * |
| * \return (none) |
| */ |
| /*----------------------------------------------------------------------------*/ |
| VOID cnmTimerDestroy(IN P_ADAPTER_T prAdapter) |
| { |
| P_ROOT_TIMER prRootTimer; |
| |
| KAL_SPIN_LOCK_DECLARATION(); |
| |
| ASSERT(prAdapter); |
| |
| prRootTimer = &prAdapter->rRootTimer; |
| |
| if (prRootTimer->fgWakeLocked) { |
| KAL_WAKE_UNLOCK(prAdapter, &prRootTimer->rWakeLock); |
| prRootTimer->fgWakeLocked = FALSE; |
| } |
| KAL_WAKE_LOCK_DESTROY(prAdapter, &prRootTimer->rWakeLock); |
| |
| KAL_ACQUIRE_SPIN_LOCK(prAdapter, SPIN_LOCK_TIMER); |
| LINK_INITIALIZE(&prRootTimer->rLinkHead); |
| KAL_RELEASE_SPIN_LOCK(prAdapter, SPIN_LOCK_TIMER); |
| |
| /* Note: glue layer will be responsible for timer destruction */ |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief This routines is called to initialize a timer. |
| * |
| * \param[in] prTimer Pointer to a timer structure. |
| * \param[in] pfnFunc Pointer to the call back function. |
| * \param[in] u4Data Parameter for call back function. |
| * |
| * \return (none) |
| */ |
| /*----------------------------------------------------------------------------*/ |
| VOID |
| cnmTimerInitTimer(IN P_ADAPTER_T prAdapter, IN P_TIMER_T prTimer, IN PFN_MGMT_TIMEOUT_FUNC pfFunc, IN ULONG ulDataPtr) |
| { |
| ASSERT(prAdapter); |
| |
| ASSERT(prTimer); |
| |
| #if DBG |
| /* Note: NULL function pointer is permitted for HEM POWER */ |
| if (pfFunc == NULL) |
| DBGLOG(CNM, WARN, "Init timer with NULL callback function!\n"); |
| #endif |
| |
| ASSERT(prAdapter->rRootTimer.rLinkHead.prNext); |
| { |
| P_LINK_T prTimerList; |
| P_LINK_ENTRY_T prLinkEntry; |
| P_TIMER_T prPendingTimer; |
| |
| prTimerList = &(prAdapter->rRootTimer.rLinkHead); |
| |
| LINK_FOR_EACH(prLinkEntry, prTimerList) { |
| prPendingTimer = LINK_ENTRY(prLinkEntry, TIMER_T, rLinkEntry); |
| ASSERT(prPendingTimer); |
| ASSERT(prPendingTimer != prTimer); |
| } |
| } |
| |
| LINK_ENTRY_INITIALIZE(&prTimer->rLinkEntry); |
| |
| prTimer->pfMgmtTimeOutFunc = pfFunc; |
| prTimer->ulDataPtr = ulDataPtr; |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief This routines is called to stop a timer. |
| * |
| * \param[in] prTimer Pointer to a timer structure. |
| * |
| * \return (none) |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static VOID cnmTimerStopTimer_impl(IN P_ADAPTER_T prAdapter, IN P_TIMER_T prTimer, IN BOOLEAN fgAcquireSpinlock) |
| { |
| P_ROOT_TIMER prRootTimer; |
| |
| KAL_SPIN_LOCK_DECLARATION(); |
| |
| ASSERT(prAdapter); |
| ASSERT(prTimer); |
| |
| prRootTimer = &prAdapter->rRootTimer; |
| |
| if (fgAcquireSpinlock) |
| KAL_ACQUIRE_SPIN_LOCK(prAdapter, SPIN_LOCK_TIMER); |
| |
| if (timerPendingTimer(prTimer)) { |
| LINK_REMOVE_KNOWN_ENTRY(&prRootTimer->rLinkHead, &prTimer->rLinkEntry); |
| |
| /* Reduce dummy timeout for power saving, especially HIF activity. |
| * If two or more timers exist and being removed timer is smallest, |
| * this dummy timeout will still happen, but it is OK. |
| */ |
| if (LINK_IS_EMPTY(&prRootTimer->rLinkHead)) { |
| kalCancelTimer(prAdapter->prGlueInfo); |
| |
| if (fgAcquireSpinlock && prRootTimer->fgWakeLocked) { |
| KAL_WAKE_UNLOCK(prAdapter, &prRootTimer->rWakeLock); |
| prRootTimer->fgWakeLocked = FALSE; |
| } |
| } |
| } |
| |
| if (fgAcquireSpinlock) |
| KAL_RELEASE_SPIN_LOCK(prAdapter, SPIN_LOCK_TIMER); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief This routines is called to stop a timer. |
| * |
| * \param[in] prTimer Pointer to a timer structure. |
| * |
| * \return (none) |
| */ |
| /*----------------------------------------------------------------------------*/ |
| VOID cnmTimerStopTimer(IN P_ADAPTER_T prAdapter, IN P_TIMER_T prTimer) |
| { |
| ASSERT(prAdapter); |
| ASSERT(prTimer); |
| |
| cnmTimerStopTimer_impl(prAdapter, prTimer, TRUE); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief This routines is called to start a timer with wake_lock. |
| * |
| * \param[in] prTimer Pointer to a timer structure. |
| * \param[in] u4TimeoutMs Timeout to issue the timer and call back function |
| * (unit: ms). |
| * |
| * \return (none) |
| */ |
| /*----------------------------------------------------------------------------*/ |
| VOID cnmTimerStartTimer(IN P_ADAPTER_T prAdapter, IN P_TIMER_T prTimer, IN UINT_32 u4TimeoutMs) |
| { |
| P_ROOT_TIMER prRootTimer; |
| P_LINK_T prTimerList; |
| OS_SYSTIME rExpiredSysTime, rTimeoutSystime; |
| |
| KAL_SPIN_LOCK_DECLARATION(); |
| |
| ASSERT(prAdapter); |
| ASSERT(prTimer); |
| |
| KAL_ACQUIRE_SPIN_LOCK(prAdapter, SPIN_LOCK_TIMER); |
| |
| prRootTimer = &prAdapter->rRootTimer; |
| prTimerList = &prRootTimer->rLinkHead; |
| |
| /* If timeout interval is larger than 1 minute, the mod value is set |
| * to the timeout value first, then per minutue. |
| */ |
| if (u4TimeoutMs > MSEC_PER_MIN) { |
| ASSERT(u4TimeoutMs <= ((UINT_32) 0xFFFF * MSEC_PER_MIN)); |
| |
| prTimer->u2Minutes = (UINT_16) (u4TimeoutMs / MSEC_PER_MIN); |
| u4TimeoutMs -= (prTimer->u2Minutes * MSEC_PER_MIN); |
| if (u4TimeoutMs == 0) { |
| u4TimeoutMs = MSEC_PER_MIN; |
| prTimer->u2Minutes--; |
| } |
| } else { |
| prTimer->u2Minutes = 0; |
| } |
| |
| /* The assertion check if MSEC_TO_SYSTIME() may be overflow. */ |
| ASSERT(u4TimeoutMs < (((UINT_32) 0x80000000 - MSEC_PER_SEC) / KAL_HZ)); |
| rTimeoutSystime = MSEC_TO_SYSTIME(u4TimeoutMs); |
| if (rTimeoutSystime == 0) |
| rTimeoutSystime = 1; |
| rExpiredSysTime = kalGetTimeTick() + rTimeoutSystime; |
| |
| /* If no timer pending or the fast time interval is used. */ |
| if (LINK_IS_EMPTY(prTimerList) || TIME_BEFORE(rExpiredSysTime, prRootTimer->rNextExpiredSysTime)) { |
| |
| prRootTimer->rNextExpiredSysTime = rExpiredSysTime; |
| cnmTimerSetTimer(prAdapter, rTimeoutSystime); |
| } |
| |
| /* Add this timer to checking list */ |
| prTimer->rExpiredSysTime = rExpiredSysTime; |
| |
| if (!timerPendingTimer(prTimer)) |
| LINK_INSERT_TAIL(prTimerList, &prTimer->rLinkEntry); |
| |
| KAL_RELEASE_SPIN_LOCK(prAdapter, SPIN_LOCK_TIMER); |
| } |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief This routines is called to check the timer list. |
| * |
| * \param[in] |
| * |
| * \return (none) |
| */ |
| /*----------------------------------------------------------------------------*/ |
| VOID cnmTimerDoTimeOutCheck(IN P_ADAPTER_T prAdapter) |
| { |
| P_ROOT_TIMER prRootTimer; |
| P_LINK_T prTimerList; |
| P_LINK_ENTRY_T prLinkEntry; |
| P_TIMER_T prTimer; |
| OS_SYSTIME rCurSysTime; |
| PFN_MGMT_TIMEOUT_FUNC pfMgmtTimeOutFunc; |
| ULONG ulTimeoutDataPtr; |
| BOOLEAN fgNeedWakeLock; |
| |
| KAL_SPIN_LOCK_DECLARATION(); |
| |
| ASSERT(prAdapter); |
| |
| /* acquire spin lock */ |
| KAL_ACQUIRE_SPIN_LOCK(prAdapter, SPIN_LOCK_TIMER); |
| |
| prRootTimer = &prAdapter->rRootTimer; |
| prTimerList = &prRootTimer->rLinkHead; |
| |
| rCurSysTime = kalGetTimeTick(); |
| |
| /* Set the permitted max timeout value for new one */ |
| prRootTimer->rNextExpiredSysTime = rCurSysTime + MGMT_MAX_TIMEOUT_INTERVAL; |
| |
| LINK_FOR_EACH(prLinkEntry, prTimerList) { |
| prTimer = LINK_ENTRY(prLinkEntry, TIMER_T, rLinkEntry); |
| ASSERT(prTimer); |
| |
| /* Check if this entry is timeout. */ |
| if (!TIME_BEFORE(rCurSysTime, prTimer->rExpiredSysTime)) { |
| cnmTimerStopTimer_impl(prAdapter, prTimer, FALSE); |
| |
| pfMgmtTimeOutFunc = prTimer->pfMgmtTimeOutFunc; |
| ulTimeoutDataPtr = prTimer->ulDataPtr; |
| |
| if (prTimer->u2Minutes > 0) { |
| prTimer->u2Minutes--; |
| prTimer->rExpiredSysTime = rCurSysTime + MSEC_TO_SYSTIME(MSEC_PER_MIN); |
| LINK_INSERT_TAIL(prTimerList, &prTimer->rLinkEntry); |
| } else if (pfMgmtTimeOutFunc) { |
| KAL_RELEASE_SPIN_LOCK(prAdapter, SPIN_LOCK_TIMER); |
| (pfMgmtTimeOutFunc) (prAdapter, ulTimeoutDataPtr); |
| KAL_ACQUIRE_SPIN_LOCK(prAdapter, SPIN_LOCK_TIMER); |
| } |
| |
| /* Search entire list again because of nest del and add timers |
| * and current MGMT_TIMER could be volatile after stopped |
| */ |
| prLinkEntry = (P_LINK_ENTRY_T) prTimerList; |
| |
| prRootTimer->rNextExpiredSysTime = rCurSysTime + MGMT_MAX_TIMEOUT_INTERVAL; |
| } else if (TIME_BEFORE(prTimer->rExpiredSysTime, prRootTimer->rNextExpiredSysTime)) { |
| prRootTimer->rNextExpiredSysTime = prTimer->rExpiredSysTime; |
| } |
| } /* end of for loop */ |
| |
| /* Setup the prNext timeout event. It is possible the timer was already |
| * set in the above timeout callback function. |
| */ |
| fgNeedWakeLock = FALSE; |
| if (!LINK_IS_EMPTY(prTimerList)) { |
| ASSERT(TIME_AFTER(prRootTimer->rNextExpiredSysTime, rCurSysTime)); |
| |
| fgNeedWakeLock = cnmTimerSetTimer(prAdapter, (OS_SYSTIME) |
| ((INT_32) prRootTimer->rNextExpiredSysTime - (INT_32) rCurSysTime)); |
| } |
| |
| if (prRootTimer->fgWakeLocked && !fgNeedWakeLock) { |
| KAL_WAKE_UNLOCK(prAdapter, &prRootTimer->rWakeLock); |
| prRootTimer->fgWakeLocked = FALSE; |
| } |
| |
| /* release spin lock */ |
| KAL_RELEASE_SPIN_LOCK(prAdapter, SPIN_LOCK_TIMER); |
| } |