diff options
Diffstat (limited to 'drivers/staging/otus/80211core/cpsmgr.c')
-rw-r--r-- | drivers/staging/otus/80211core/cpsmgr.c | 731 |
1 files changed, 731 insertions, 0 deletions
diff --git a/drivers/staging/otus/80211core/cpsmgr.c b/drivers/staging/otus/80211core/cpsmgr.c new file mode 100644 index 00000000000..cf73caca8e5 --- /dev/null +++ b/drivers/staging/otus/80211core/cpsmgr.c @@ -0,0 +1,731 @@ +/* + * 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 */ + |