blob: 286296bd02a1abb88835cb02b7e2ec235ccfef6c [file] [edit]
/******************************************************************************
*
* 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 u_int8_t cnmTimerSetTimer(IN struct ADAPTER *prAdapter,
IN OS_SYSTIME rTimeout,
IN enum ENUM_TIMER_WAKELOCK_TYPE_T eType)
{
struct ROOT_TIMER *prRootTimer;
u_int8_t fgNeedWakeLock;
ASSERT(prAdapter);
prRootTimer = &prAdapter->rRootTimer;
kalSetTimer(prAdapter->prGlueInfo, rTimeout);
if ((eType == TIMER_WAKELOCK_REQUEST)
|| (rTimeout <= SEC_TO_SYSTIME(WAKE_LOCK_MAX_TIME)
&& (eType == TIMER_WAKELOCK_AUTO))) {
fgNeedWakeLock = TRUE;
if (!prRootTimer->fgWakeLocked) {
KAL_WAKE_LOCK(prAdapter, prRootTimer->prWakeLock);
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 struct ADAPTER *prAdapter)
{
struct 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->prWakeLock, "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 struct ADAPTER *prAdapter)
{
struct ROOT_TIMER *prRootTimer;
KAL_SPIN_LOCK_DECLARATION();
ASSERT(prAdapter);
prRootTimer = &prAdapter->rRootTimer;
if (prRootTimer->fgWakeLocked) {
KAL_WAKE_UNLOCK(prAdapter, prRootTimer->prWakeLock);
prRootTimer->fgWakeLocked = FALSE;
}
KAL_WAKE_LOCK_DESTROY(prAdapter, prRootTimer->prWakeLock);
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
cnmTimerInitTimerOption(IN struct ADAPTER *prAdapter,
IN struct TIMER *prTimer,
IN PFN_MGMT_TIMEOUT_FUNC pfFunc,
IN unsigned long ulDataPtr,
IN enum ENUM_TIMER_WAKELOCK_TYPE_T eType)
{
ASSERT(prAdapter);
ASSERT(prTimer);
ASSERT((eType >= TIMER_WAKELOCK_AUTO) && (eType < TIMER_WAKELOCK_NUM));
#if DBG
/* Note: NULL function pointer is permitted for HEM POWER */
if (pfFunc == NULL)
log_dbg(CNM, WARN, "Init timer with NULL callback function!\n");
ASSERT(prAdapter->rRootTimer.rLinkHead.prNext);
{
struct LINK *prTimerList;
struct LINK_ENTRY *prLinkEntry;
struct TIMER *prPendingTimer;
prTimerList = &(prAdapter->rRootTimer.rLinkHead);
LINK_FOR_EACH(prLinkEntry, prTimerList) {
prPendingTimer = LINK_ENTRY(prLinkEntry,
struct TIMER, rLinkEntry);
ASSERT(prPendingTimer);
ASSERT(prPendingTimer != prTimer);
}
}
#endif
if (prTimer->pfMgmtTimeOutFunc == pfFunc
&& prTimer->rLinkEntry.prNext) {
log_dbg(CNM, WARN, "re-init timer, func %p\n", pfFunc);
kal_show_stack(prAdapter, NULL, NULL);
}
LINK_ENTRY_INITIALIZE(&prTimer->rLinkEntry);
prTimer->pfMgmtTimeOutFunc = pfFunc;
prTimer->ulDataPtr = ulDataPtr;
prTimer->eType = eType;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This routines is called to stop a timer.
*
* \param[in] prTimer Pointer to a timer structure.
*
* \return (none)
*/
/*----------------------------------------------------------------------------*/
static void cnmTimerStopTimer_impl(IN struct ADAPTER *prAdapter,
IN struct TIMER *prTimer, IN u_int8_t fgAcquireSpinlock)
{
struct 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);
if (LINK_IS_EMPTY(&prRootTimer->rLinkHead)) {
/* kalCancelTimer(prAdapter->prGlueInfo); */
/* Violate rule of del_timer_sync which cause DeadLock
* If no pending timer, let the dummpy timeout happen
* It would happen only one time
* Prevent call del_timer_sync with SPIN_LOCK_TIMER hold
* ===================================================
* Function: del_timer_sync
* Note: For !irqsafe timers, you must not hold locks
* that are held in interrupt context while calling
* this function. Even if the lock has nothing to do
* with the timer in question.
* Here's why:
*
* CPU0 CPU1
* ---- ----
* <SOFTIRQ>
* call_timer_fn();
* base->running_timer = mytimer;
* spin_lock_irq(somelock);
* <IRQ>
* spin_lock(somelock);
* del_timer_sync(mytimer);
* while (base->running_timer == mytimer);
* ==================================================
*/
if (fgAcquireSpinlock && prRootTimer->fgWakeLocked) {
KAL_WAKE_UNLOCK(prAdapter,
prRootTimer->prWakeLock);
prRootTimer->fgWakeLocked = FALSE;
}
}
/* 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 (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 struct ADAPTER *prAdapter, IN struct TIMER *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 struct ADAPTER *prAdapter, IN struct TIMER *prTimer,
IN uint32_t u4TimeoutMs)
{
struct ROOT_TIMER *prRootTimer;
struct LINK *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 <= ((uint32_t) 0xFFFF * MSEC_PER_MIN));
prTimer->u2Minutes = (uint16_t) (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 < (((uint32_t) 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, prTimer->eType);
}
/* 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 struct ADAPTER *prAdapter)
{
struct ROOT_TIMER *prRootTimer;
struct LINK *prTimerList;
struct LINK_ENTRY *prLinkEntry;
struct TIMER *prTimer;
OS_SYSTIME rCurSysTime;
PFN_MGMT_TIMEOUT_FUNC pfMgmtTimeOutFunc;
unsigned long ulTimeoutDataPtr;
u_int8_t fgNeedWakeLock;
enum ENUM_TIMER_WAKELOCK_TYPE_T eType = TIMER_WAKELOCK_NONE;
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) {
if (prLinkEntry == NULL)
break;
prTimer = LINK_ENTRY(prLinkEntry, struct TIMER, rLinkEntry);
ASSERT(prTimer);
if (prLinkEntry->prNext == NULL)
log_dbg(CNM, WARN, "timer was re-inited, func %p\n",
prTimer->pfMgmtTimeOutFunc);
/* 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 = (struct LINK_ENTRY *) prTimerList;
prRootTimer->rNextExpiredSysTime
= rCurSysTime + MGMT_MAX_TIMEOUT_INTERVAL;
} else if (TIME_BEFORE(prTimer->rExpiredSysTime,
prRootTimer->rNextExpiredSysTime)) {
prRootTimer->rNextExpiredSysTime
= prTimer->rExpiredSysTime;
if (prTimer->eType == TIMER_WAKELOCK_REQUEST)
eType = TIMER_WAKELOCK_REQUEST;
else if ((eType != TIMER_WAKELOCK_REQUEST)
&& (prTimer->eType == TIMER_WAKELOCK_AUTO))
eType = TIMER_WAKELOCK_AUTO;
}
} /* 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)((int32_t) prRootTimer->rNextExpiredSysTime
- (int32_t) rCurSysTime),
eType);
}
if (prRootTimer->fgWakeLocked && !fgNeedWakeLock) {
KAL_WAKE_UNLOCK(prAdapter, prRootTimer->prWakeLock);
prRootTimer->fgWakeLocked = FALSE;
}
/* release spin lock */
KAL_RELEASE_SPIN_LOCK(prAdapter, SPIN_LOCK_TIMER);
}