blob: cf73caca8e52f18e27d5ff0249543a9ce1565adc [file] [log] [blame]
/*
* Copyright (c) 2007-2008 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
* The power saving manager is to save the power as much as possible.
* Generally speaking, it controls:
*
* - when to sleep
* -
*
*/
#include "cprecomp.h"
void zfPowerSavingMgrInit(zdev_t* dev)
{
zmw_get_wlan_dev(dev);
wd->sta.powerSaveMode = ZM_STA_PS_NONE;
wd->sta.psMgr.state = ZM_PS_MSG_STATE_ACTIVE;
wd->sta.psMgr.isSleepAllowed = 0;
wd->sta.psMgr.maxSleepPeriods = 1;
wd->sta.psMgr.ticks = 0;
wd->sta.psMgr.sleepAllowedtick = 0;
}
static u16_t zfPowerSavingMgrHandlePsNone(zdev_t* dev, u8_t *isWakeUpRequired)
{
u16_t ret = 0;
zmw_get_wlan_dev(dev);
switch(wd->sta.psMgr.state)
{
case ZM_PS_MSG_STATE_ACTIVE:
*isWakeUpRequired = 0;
break;
case ZM_PS_MSG_STATE_T1:
case ZM_PS_MSG_STATE_T2:
case ZM_PS_MSG_STATE_SLEEP:
default:
*isWakeUpRequired = 1;
zm_debug_msg0("zfPowerSavingMgrHandlePsNone: Wake up now\n");
if ( zfStaIsConnected(dev) )
{
zm_debug_msg0("zfPowerSavingMgrOnHandleT1 send Null data\n");
//zfSendNullData(dev, 0);
ret = 1;
}
wd->sta.psMgr.state = ZM_PS_MSG_STATE_ACTIVE;
break;
}
return ret;
}
static void zfPowerSavingMgrHandlePs(zdev_t* dev)
{
zmw_get_wlan_dev(dev);
switch(wd->sta.psMgr.state)
{
case ZM_PS_MSG_STATE_ACTIVE:
//zm_debug_msg0("zfPowerSavingMgrHandlePs: Prepare to sleep...\n");
//wd->sta.psMgr.state = ZM_PS_MSG_STATE_T1;
break;
case ZM_PS_MSG_STATE_T1:
case ZM_PS_MSG_STATE_T2:
case ZM_PS_MSG_STATE_SLEEP:
default:
break;
}
}
void zfPowerSavingMgrSetMode(zdev_t* dev, u8_t mode)
{
u16_t sendNull = 0;
u8_t isWakeUpRequired = 0;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
zm_debug_msg1("mode = ", mode);
if (mode > ZM_STA_PS_LIGHT)
{
zm_debug_msg0("return - wrong power save mode");
return;
}
zmw_enter_critical_section(dev);
#if 1
switch(mode)
{
case ZM_STA_PS_NONE:
sendNull = zfPowerSavingMgrHandlePsNone(dev, &isWakeUpRequired);
break;
case ZM_STA_PS_FAST:
case ZM_STA_PS_LIGHT:
wd->sta.psMgr.maxSleepPeriods = 1;
zfPowerSavingMgrHandlePs(dev);
break;
case ZM_STA_PS_MAX:
wd->sta.psMgr.maxSleepPeriods = ZM_PS_MAX_SLEEP_PERIODS;
zfPowerSavingMgrHandlePs(dev);
break;
}
#else
switch(wd->sta.psMgr.state)
{
case ZM_PS_MSG_STATE_ACTIVE:
if ( mode != ZM_STA_PS_NONE )
{
zm_debug_msg0("zfPowerSavingMgrSetMode: switch from ZM_PS_MSG_STATE_ACTIVE to ZM_PS_MSG_STATE_T1\n");
// Stall the TX & start to wait the pending TX to be completed
wd->sta.psMgr.state = ZM_PS_MSG_STATE_T1;
}
break;
case ZM_PS_MSG_STATE_SLEEP:
break;
}
#endif
wd->sta.powerSaveMode = mode;
zmw_leave_critical_section(dev);
if ( isWakeUpRequired )
{
zfHpPowerSaveSetState(dev, 0);
wd->sta.psMgr.tempWakeUp = 0;
}
if ( zfStaIsConnected(dev)
&& (wd->wlanMode == ZM_MODE_INFRASTRUCTURE) )
{
switch(mode)
{
case ZM_STA_PS_NONE:
zfHpPowerSaveSetMode(dev, 0, 0, wd->beaconInterval);
break;
case ZM_STA_PS_FAST:
case ZM_STA_PS_MAX:
case ZM_STA_PS_LIGHT:
zfHpPowerSaveSetMode(dev, 0, 1, wd->beaconInterval);
break;
default:
zfHpPowerSaveSetMode(dev, 0, 0, wd->beaconInterval);
break;
}
}
if (sendNull == 1)
{
zfSendNullData(dev, 0);
}
return;
}
static void zfPowerSavingMgrNotifyPSToAP(zdev_t *dev)
{
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
if ( (wd->sta.psMgr.tempWakeUp != 1)&&
(wd->sta.psMgr.lastTxUnicastFrm != wd->commTally.txUnicastFrm ||
wd->sta.psMgr.lastTxBroadcastFrm != wd->commTally.txBroadcastFrm ||
wd->sta.psMgr.lastTxMulticastFrm != wd->commTally.txMulticastFrm) )
{
zmw_enter_critical_section(dev);
wd->sta.psMgr.lastTxUnicastFrm = wd->commTally.txUnicastFrm;
wd->sta.psMgr.lastTxBroadcastFrm = wd->commTally.txBroadcastFrm;
wd->sta.psMgr.lastTxMulticastFrm = wd->commTally.txMulticastFrm;
zmw_leave_critical_section(dev);
zfSendNullData(dev, 1);
}
}
static void zfPowerSavingMgrOnHandleT1(zdev_t* dev)
{
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
// If the tx Q is not empty...return
if ( zfIsVtxqEmpty(dev) == FALSE )
{
return;
}
zm_debug_msg0("VtxQ is empty now...Check if HAL TXQ is empty\n");
// The the HAL TX Q is not empty...return
if ( zfHpGetFreeTxdCount(dev) != zfHpGetMaxTxdCount(dev) )
{
return;
}
zm_debug_msg0("HAL TXQ is empty now...Could go to sleep...\n");
zmw_enter_critical_section(dev);
if (wd->sta.powerSaveMode == ZM_STA_PS_LIGHT)
{
if (wd->sta.ReceivedPktRatePerSecond > 200)
{
zmw_leave_critical_section(dev);
return;
}
if ( zfStaIsConnected(dev)
&& (wd->wlanMode == ZM_MODE_INFRASTRUCTURE) )
{
if (wd->sta.psMgr.sleepAllowedtick) {
wd->sta.psMgr.sleepAllowedtick--;
zmw_leave_critical_section(dev);
return;
}
}
}
wd->sta.psMgr.state = ZM_PS_MSG_STATE_T2;
zmw_leave_critical_section(dev);
// Send the Null pkt to AP to notify that I'm going to sleep
if ( zfStaIsConnected(dev) )
{
zm_debug_msg0("zfPowerSavingMgrOnHandleT1 send Null data\n");
zfPowerSavingMgrNotifyPSToAP(dev);
}
// Stall the TX now
// zfTxEngineStop(dev);
}
static void zfPowerSavingMgrOnHandleT2(zdev_t* dev)
{
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
// Wait until the Null pkt is transmitted
if ( zfHpGetFreeTxdCount(dev) != zfHpGetMaxTxdCount(dev) )
{
return;
}
zmw_enter_critical_section(dev);
wd->sta.psMgr.state = ZM_PS_MSG_STATE_SLEEP;
wd->sta.psMgr.lastTxUnicastFrm = wd->commTally.txUnicastFrm;
wd->sta.psMgr.lastTxBroadcastFrm = wd->commTally.txBroadcastFrm;
wd->sta.psMgr.lastTxMulticastFrm = wd->commTally.txMulticastFrm;
zmw_leave_critical_section(dev);
// Let CHIP sleep now
zm_debug_msg0("zfPowerSavingMgrOnHandleT2 zzzz....\n");
zfHpPowerSaveSetState(dev, 1);
wd->sta.psMgr.tempWakeUp = 0;
}
u8_t zfPowerSavingMgrIsSleeping(zdev_t *dev)
{
u8_t isSleeping = FALSE;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
zmw_enter_critical_section(dev);
if ( wd->sta.psMgr.state == ZM_PS_MSG_STATE_SLEEP ||
wd->sta.psMgr.state == ZM_PS_MSG_STATE_T2)
{
isSleeping = TRUE;
}
zmw_leave_critical_section(dev);
return isSleeping;
}
static u8_t zfPowerSavingMgrIsIdle(zdev_t *dev)
{
u8_t isIdle = 0;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
zmw_enter_critical_section(dev);
if ( zfStaIsConnected(dev) && wd->sta.psMgr.isSleepAllowed == 0 )
{
goto done;
}
if ( wd->sta.bChannelScan )
{
goto done;
}
if ( zfStaIsConnecting(dev) )
{
goto done;
}
if (wd->sta.powerSaveMode == ZM_STA_PS_LIGHT)
{
if (wd->sta.ReceivedPktRatePerSecond > 200)
{
goto done;
}
if ( zfStaIsConnected(dev)
&& (wd->wlanMode == ZM_MODE_INFRASTRUCTURE) )
{
if (wd->sta.psMgr.sleepAllowedtick) {
wd->sta.psMgr.sleepAllowedtick--;
goto done;
}
}
}
isIdle = 1;
done:
zmw_leave_critical_section(dev);
if ( zfIsVtxqEmpty(dev) == FALSE )
{
isIdle = 0;
}
return isIdle;
}
static void zfPowerSavingMgrSleepIfIdle(zdev_t *dev)
{
u8_t isIdle;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
isIdle = zfPowerSavingMgrIsIdle(dev);
if ( isIdle == 0 )
{
return;
}
zmw_enter_critical_section(dev);
switch(wd->sta.powerSaveMode)
{
case ZM_STA_PS_NONE:
break;
case ZM_STA_PS_MAX:
case ZM_STA_PS_FAST:
case ZM_STA_PS_LIGHT:
zm_debug_msg0("zfPowerSavingMgrSleepIfIdle: IDLE so slep now...\n");
wd->sta.psMgr.state = ZM_PS_MSG_STATE_T1;
break;
}
zmw_leave_critical_section(dev);
}
static void zfPowerSavingMgrDisconnectMain(zdev_t* dev)
{
zmw_get_wlan_dev(dev);
#ifdef ZM_ENABLE_DISCONNECT_PS
switch(wd->sta.psMgr.state)
{
case ZM_PS_MSG_STATE_ACTIVE:
zfPowerSavingMgrSleepIfIdle(dev);
break;
case ZM_PS_MSG_STATE_SLEEP:
break;
case ZM_PS_MSG_STATE_T1:
zfPowerSavingMgrOnHandleT1(dev);
break;
case ZM_PS_MSG_STATE_T2:
zfPowerSavingMgrOnHandleT2(dev);
break;
}
#else
zfPowerSavingMgrWakeup(dev);
#endif
}
static void zfPowerSavingMgrInfraMain(zdev_t* dev)
{
zmw_get_wlan_dev(dev);
switch(wd->sta.psMgr.state)
{
case ZM_PS_MSG_STATE_ACTIVE:
zfPowerSavingMgrSleepIfIdle(dev);
break;
case ZM_PS_MSG_STATE_SLEEP:
break;
case ZM_PS_MSG_STATE_T1:
zfPowerSavingMgrOnHandleT1(dev);
break;
case ZM_PS_MSG_STATE_T2:
zfPowerSavingMgrOnHandleT2(dev);
break;
}
}
void zfPowerSavingMgrAtimWinExpired(zdev_t* dev)
{
zmw_get_wlan_dev(dev);
//printk("zfPowerSavingMgrAtimWinExpired #1\n");
if ( wd->sta.powerSaveMode == ZM_STA_PS_NONE )
{
return;
}
//printk("zfPowerSavingMgrAtimWinExpired #2\n");
// if we received any ATIM window from the others to indicate we have buffered data
// at the other station, we can't go to sleep
if ( wd->sta.recvAtim )
{
wd->sta.recvAtim = 0;
zm_debug_msg0("Can't sleep due to receving ATIM window!");
return;
}
// if we are the one to tx beacon during last beacon interval. we can't go to sleep
// since we need to be alive to respond the probe request!
if ( wd->sta.txBeaconInd )
{
zm_debug_msg0("Can't sleep due to just transmit a beacon!");
return;
}
// If we buffer any data for the other stations. we could not go to sleep
if ( wd->sta.ibssPrevPSDataCount != 0 )
{
zm_debug_msg0("Can't sleep due to buffering data for the others!");
return;
}
// before sleeping, we still need to notify the others by transmitting null
// pkt with power mgmt bit turned on.
zfPowerSavingMgrOnHandleT1(dev);
}
static void zfPowerSavingMgrIBSSMain(zdev_t* dev)
{
// wait for the end of
// if need to wait to know if we are the one to transmit the beacon
// during the beacon interval. If it's me, we can't go to sleep.
zmw_get_wlan_dev(dev);
switch(wd->sta.psMgr.state)
{
case ZM_PS_MSG_STATE_ACTIVE:
case ZM_PS_MSG_STATE_SLEEP:
case ZM_PS_MSG_STATE_T1:
break;
case ZM_PS_MSG_STATE_T2:
zfPowerSavingMgrOnHandleT2(dev);
break;
}
return;
}
#if 1
void zfPowerSavingMgrMain(zdev_t* dev)
{
zmw_get_wlan_dev(dev);
switch (wd->sta.adapterState)
{
case ZM_STA_STATE_DISCONNECT:
zfPowerSavingMgrDisconnectMain(dev);
break;
case ZM_STA_STATE_CONNECTED:
{
if (wd->wlanMode == ZM_MODE_INFRASTRUCTURE) {
zfPowerSavingMgrInfraMain(dev);
} else if (wd->wlanMode == ZM_MODE_IBSS) {
zfPowerSavingMgrIBSSMain(dev);
}
}
break;
case ZM_STA_STATE_CONNECTING:
default:
break;
}
}
#else
void zfPowerSavingMgrMain(zdev_t* dev)
{
zmw_get_wlan_dev(dev);
if ( wd->wlanMode != ZM_MODE_INFRASTRUCTURE )
{
return;
}
switch(wd->sta.psMgr.state)
{
case ZM_PS_MSG_STATE_ACTIVE:
goto check_sleep;
break;
case ZM_PS_MSG_STATE_SLEEP:
goto sleeping;
break;
case ZM_PS_MSG_STATE_T1:
zfPowerSavingMgrOnHandleT1(dev);
break;
case ZM_PS_MSG_STATE_T2:
zfPowerSavingMgrOnHandleT2(dev);
break;
}
return;
sleeping:
return;
check_sleep:
zfPowerSavingMgrSleepIfIdle(dev);
return;
}
#endif
#ifdef ZM_ENABLE_POWER_SAVE
void zfPowerSavingMgrWakeup(zdev_t* dev)
{
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
//zm_debug_msg0("zfPowerSavingMgrWakeup");
//if ( wd->sta.psMgr.state != ZM_PS_MSG_STATE_ACTIVE && ( zfPowerSavingMgrIsIdle(dev) == 0 ))
if ( wd->sta.psMgr.state != ZM_PS_MSG_STATE_ACTIVE )
{
zmw_enter_critical_section(dev);
wd->sta.psMgr.isSleepAllowed = 0;
wd->sta.psMgr.state = ZM_PS_MSG_STATE_ACTIVE;
if ( wd->sta.powerSaveMode > ZM_STA_PS_NONE )
wd->sta.psMgr.tempWakeUp = 1;
zmw_leave_critical_section(dev);
// Wake up the CHIP now!!
zfHpPowerSaveSetState(dev, 0);
}
}
#else
void zfPowerSavingMgrWakeup(zdev_t* dev)
{
}
#endif
void zfPowerSavingMgrProcessBeacon(zdev_t* dev, zbuf_t* buf)
{
u8_t length, bitmap;
u16_t offset, n1, n2, q, r;
zbuf_t* psBuf;
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
if ( wd->sta.powerSaveMode == ZM_STA_PS_NONE )
//if ( wd->sta.psMgr.state != ZM_PS_MSG_STATE_SLEEP )
{
return;
}
wd->sta.psMgr.isSleepAllowed = 1;
if ( (offset=zfFindElement(dev, buf, ZM_WLAN_EID_TIM)) != 0xffff )
{
length = zmw_rx_buf_readb(dev, buf, offset+1);
if ( length > 3 )
{
n1 = zmw_rx_buf_readb(dev, buf, offset+4) & (~ZM_BIT_0);
n2 = length + n1 - 4;
q = wd->sta.aid >> 3;
r = wd->sta.aid & 7;
if ((q >= n1) && (q <= n2))
{
bitmap = zmw_rx_buf_readb(dev, buf, offset+5+q-n1);
if ( (bitmap >> r) & ZM_BIT_0 )
{
//if ( wd->sta.powerSaveMode == ZM_STA_PS_FAST )
if ( 0 )
{
wd->sta.psMgr.state = ZM_PS_MSG_STATE_S1;
//zfSendPSPoll(dev);
zfSendNullData(dev, 0);
}
else
{
if ((wd->sta.qosInfo&0xf) != 0xf)
{
/* send ps-poll */
//printk("zfSendPSPoll #1\n");
wd->sta.psMgr.isSleepAllowed = 0;
switch (wd->sta.powerSaveMode)
{
case ZM_STA_PS_MAX:
case ZM_STA_PS_FAST:
//zm_debug_msg0("wake up and send PS-Poll\n");
zfSendPSPoll(dev);
break;
case ZM_STA_PS_LIGHT:
zm_debug_msg0("wake up and send null data\n");
zmw_enter_critical_section(dev);
wd->sta.psMgr.sleepAllowedtick = 400;
zmw_leave_critical_section(dev);
zfSendNullData(dev, 0);
break;
}
wd->sta.psMgr.tempWakeUp = 0;
}
}
}
}
}
}
while ((psBuf = zfQueueGet(dev, wd->sta.uapsdQ)) != NULL)
{
zfTxSendEth(dev, psBuf, 0, ZM_EXTERNAL_ALLOC_BUF, 0);
}
//printk("zfPowerSavingMgrProcessBeacon #1\n");
zfPowerSavingMgrMain(dev);
}
void zfPowerSavingMgrConnectNotify(zdev_t *dev)
{
zmw_get_wlan_dev(dev);
if ( wd->wlanMode == ZM_MODE_INFRASTRUCTURE )
{
switch(wd->sta.powerSaveMode)
{
case ZM_STA_PS_NONE:
zfHpPowerSaveSetMode(dev, 0, 0, wd->beaconInterval);
break;
case ZM_STA_PS_FAST:
case ZM_STA_PS_MAX:
case ZM_STA_PS_LIGHT:
zfHpPowerSaveSetMode(dev, 0, 1, wd->beaconInterval);
break;
default:
zfHpPowerSaveSetMode(dev, 0, 0, wd->beaconInterval);
break;
}
}
}
void zfPowerSavingMgrPreTBTTInterrupt(zdev_t *dev)
{
zmw_get_wlan_dev(dev);
zmw_declare_for_critical_section();
/* disable TBTT interrupt when change from connection to disconnect */
if (zfStaIsDisconnect(dev)) {
zfHpPowerSaveSetMode(dev, 0, 0, 0);
zfPowerSavingMgrWakeup(dev);
return;
}
zmw_enter_critical_section(dev);
wd->sta.psMgr.ticks++;
if ( wd->sta.psMgr.ticks < wd->sta.psMgr.maxSleepPeriods )
{
zmw_leave_critical_section(dev);
return;
}
else
{
wd->sta.psMgr.ticks = 0;
}
zmw_leave_critical_section(dev);
zfPowerSavingMgrWakeup(dev);
}
/* Leave an empty line below to remove warning message on some compiler */