192b96797SForest Bond /* 292b96797SForest Bond * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. 392b96797SForest Bond * All rights reserved. 492b96797SForest Bond * 592b96797SForest Bond * This program is free software; you can redistribute it and/or modify 692b96797SForest Bond * it under the terms of the GNU General Public License as published by 792b96797SForest Bond * the Free Software Foundation; either version 2 of the License, or 892b96797SForest Bond * (at your option) any later version. 992b96797SForest Bond * 1092b96797SForest Bond * This program is distributed in the hope that it will be useful, 1192b96797SForest Bond * but WITHOUT ANY WARRANTY; without even the implied warranty of 1292b96797SForest Bond * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1392b96797SForest Bond * GNU General Public License for more details. 1492b96797SForest Bond * 1592b96797SForest Bond * You should have received a copy of the GNU General Public License along 1692b96797SForest Bond * with this program; if not, write to the Free Software Foundation, Inc., 1792b96797SForest Bond * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 1892b96797SForest Bond * 1992b96797SForest Bond * 2092b96797SForest Bond * File: power.c 2192b96797SForest Bond * 220d743951SUwe Kleine-König * Purpose: Handles 802.11 power management functions 2392b96797SForest Bond * 2492b96797SForest Bond * Author: Lyndon Chen 2592b96797SForest Bond * 2692b96797SForest Bond * Date: July 17, 2002 2792b96797SForest Bond * 2892b96797SForest Bond * Functions: 2992b96797SForest Bond * PSvEnablePowerSaving - Enable Power Saving Mode 3092b96797SForest Bond * PSvDiasblePowerSaving - Disable Power Saving Mode 3192b96797SForest Bond * PSbConsiderPowerDown - Decide if we can Power Down 3292b96797SForest Bond * PSvSendPSPOLL - Send PS-POLL packet 3392b96797SForest Bond * PSbSendNullPacket - Send Null packet 3492b96797SForest Bond * PSbIsNextTBTTWakeUp - Decide if we need to wake up at next Beacon 3592b96797SForest Bond * 3692b96797SForest Bond * Revision History: 3792b96797SForest Bond * 3892b96797SForest Bond */ 3992b96797SForest Bond 4092b96797SForest Bond #include "mac.h" 4192b96797SForest Bond #include "device.h" 4292b96797SForest Bond #include "wmgr.h" 4392b96797SForest Bond #include "power.h" 4492b96797SForest Bond #include "wcmd.h" 4592b96797SForest Bond #include "rxtx.h" 4692b96797SForest Bond #include "card.h" 4762c8526dSMalcolm Priestley #include "usbpipe.h" 4892b96797SForest Bond 4992b96797SForest Bond static int msglevel = MSG_LEVEL_INFO; 5092b96797SForest Bond 517404eab2SPhilip Worrall /* 5292b96797SForest Bond * 5392b96797SForest Bond * Routine Description: 5492b96797SForest Bond * Enable hw power saving functions 5592b96797SForest Bond * 5692b96797SForest Bond * Return Value: 5792b96797SForest Bond * None. 5892b96797SForest Bond * 597404eab2SPhilip Worrall */ 6092b96797SForest Bond 612bac6f98SMalcolm Priestley void PSvEnablePowerSaving(struct vnt_private *priv, u16 listen_interval) 6292b96797SForest Bond { 632bac6f98SMalcolm Priestley struct vnt_manager *mgmt = &priv->vnt_mgmt; 642bac6f98SMalcolm Priestley u16 aid = mgmt->wCurrAID | BIT14 | BIT15; 6592b96797SForest Bond 664846cbc1SAlejandro Emanuel Paredes /* set period of power up before TBTT */ 672bac6f98SMalcolm Priestley vnt_mac_write_word(priv, MAC_REG_PWBT, C_PWBT); 6892b96797SForest Bond 692bac6f98SMalcolm Priestley if (priv->op_mode != NL80211_IFTYPE_ADHOC) { 704846cbc1SAlejandro Emanuel Paredes /* set AID */ 712bac6f98SMalcolm Priestley vnt_mac_write_word(priv, MAC_REG_AIDATIM, aid); 7292b96797SForest Bond } 7392b96797SForest Bond 7464052b78SAybuke Ozdemir /* Warren:06-18-2004,the sequence must follow 7564052b78SAybuke Ozdemir * PSEN->AUTOSLEEP->GO2DOZE 7664052b78SAybuke Ozdemir */ 777404eab2SPhilip Worrall /* enable power saving hw function */ 782bac6f98SMalcolm Priestley vnt_mac_reg_bits_on(priv, MAC_REG_PSCTL, PSCTL_PSEN); 79f9cfbe94SPhilip Worrall 807404eab2SPhilip Worrall /* Set AutoSleep */ 812bac6f98SMalcolm Priestley vnt_mac_reg_bits_on(priv, MAC_REG_PSCFG, PSCFG_AUTOSLEEP); 8292b96797SForest Bond 8364052b78SAybuke Ozdemir /* Warren:MUST turn on this once before turn on AUTOSLEEP ,or the 8464052b78SAybuke Ozdemir * AUTOSLEEP doesn't work 8564052b78SAybuke Ozdemir */ 862bac6f98SMalcolm Priestley vnt_mac_reg_bits_on(priv, MAC_REG_PSCTL, PSCTL_GO2DOZE); 8792b96797SForest Bond 882bac6f98SMalcolm Priestley if (listen_interval >= 2) { 8992b96797SForest Bond 907404eab2SPhilip Worrall /* clear always listen beacon */ 912bac6f98SMalcolm Priestley vnt_mac_reg_bits_off(priv, MAC_REG_PSCTL, PSCTL_ALBCN); 92f9cfbe94SPhilip Worrall 937404eab2SPhilip Worrall /* first time set listen next beacon */ 942bac6f98SMalcolm Priestley vnt_mac_reg_bits_on(priv, MAC_REG_PSCTL, PSCTL_LNBCN); 9592b96797SForest Bond 962bac6f98SMalcolm Priestley mgmt->wCountToWakeUp = listen_interval; 9792b96797SForest Bond 98f9cfbe94SPhilip Worrall } else { 9992b96797SForest Bond 1007404eab2SPhilip Worrall /* always listen beacon */ 1012bac6f98SMalcolm Priestley vnt_mac_reg_bits_on(priv, MAC_REG_PSCTL, PSCTL_ALBCN); 10292b96797SForest Bond 1032bac6f98SMalcolm Priestley mgmt->wCountToWakeUp = 0; 10492b96797SForest Bond } 10592b96797SForest Bond 1062bac6f98SMalcolm Priestley priv->bEnablePSMode = true; 10792b96797SForest Bond 10864052b78SAybuke Ozdemir /* We don't send null pkt in ad hoc mode 10964052b78SAybuke Ozdemir * since beacon will handle this. 11064052b78SAybuke Ozdemir */ 1112bac6f98SMalcolm Priestley if (priv->op_mode == NL80211_IFTYPE_STATION) 1122bac6f98SMalcolm Priestley PSbSendNullPacket(priv); 113f9cfbe94SPhilip Worrall 1142bac6f98SMalcolm Priestley priv->bPWBitOn = true; 11592b96797SForest Bond DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "PS:Power Saving Mode Enable...\n"); 11692b96797SForest Bond } 11792b96797SForest Bond 1187404eab2SPhilip Worrall /* 11992b96797SForest Bond * 12092b96797SForest Bond * Routine Description: 12192b96797SForest Bond * Disable hw power saving functions 12292b96797SForest Bond * 12392b96797SForest Bond * Return Value: 12492b96797SForest Bond * None. 12592b96797SForest Bond * 1267404eab2SPhilip Worrall */ 12792b96797SForest Bond 12845c73bb1SMalcolm Priestley void PSvDisablePowerSaving(struct vnt_private *pDevice) 12992b96797SForest Bond { 13092b96797SForest Bond 1317404eab2SPhilip Worrall /* disable power saving hw function */ 1321390b02aSMalcolm Priestley vnt_control_out(pDevice, MESSAGE_TYPE_DISABLE_PS, 0, 133f9cfbe94SPhilip Worrall 0, 0, NULL); 13492b96797SForest Bond 1357404eab2SPhilip Worrall /* clear AutoSleep */ 136a9bed1dfSMalcolm Priestley vnt_mac_reg_bits_off(pDevice, MAC_REG_PSCFG, PSCFG_AUTOSLEEP); 13792b96797SForest Bond 1387404eab2SPhilip Worrall /* set always listen beacon */ 13936957537SMalcolm Priestley vnt_mac_reg_bits_on(pDevice, MAC_REG_PSCTL, PSCTL_ALBCN); 140e269fc2dSAndres More pDevice->bEnablePSMode = false; 14192b96797SForest Bond 142a0ad2776SMalcolm Priestley if (pDevice->op_mode == NL80211_IFTYPE_STATION) 14392b96797SForest Bond PSbSendNullPacket(pDevice); 144f9cfbe94SPhilip Worrall 145e269fc2dSAndres More pDevice->bPWBitOn = false; 14692b96797SForest Bond } 14792b96797SForest Bond 1487404eab2SPhilip Worrall /* 14992b96797SForest Bond * 15092b96797SForest Bond * Routine Description: 15192b96797SForest Bond * Consider to power down when no more packets to tx or rx. 15292b96797SForest Bond * 15392b96797SForest Bond * Return Value: 1544e9b5e2bSAndres More * true, if power down success 155e269fc2dSAndres More * false, if fail 1567404eab2SPhilip Worrall */ 15792b96797SForest Bond 15845c73bb1SMalcolm Priestley int PSbConsiderPowerDown(struct vnt_private *pDevice, int bCheckRxDMA, 15945c73bb1SMalcolm Priestley int bCheckCountToWakeUp) 16092b96797SForest Bond { 16145c73bb1SMalcolm Priestley struct vnt_manager *pMgmt = &pDevice->vnt_mgmt; 16245c73bb1SMalcolm Priestley u8 byData; 16392b96797SForest Bond 1647404eab2SPhilip Worrall /* check if already in Doze mode */ 16553742906SMalcolm Priestley vnt_control_in_u8(pDevice, MESSAGE_REQUEST_MACREG, 166f9cfbe94SPhilip Worrall MAC_REG_PSCTL, &byData); 167f9cfbe94SPhilip Worrall 16892b96797SForest Bond if ((byData & PSCTL_PS) != 0) 1694e9b5e2bSAndres More return true; 17092b96797SForest Bond 17192b96797SForest Bond if (pMgmt->eCurrMode != WMAC_MODE_IBSS_STA) { 1727404eab2SPhilip Worrall /* check if in TIM wake period */ 17392b96797SForest Bond if (pMgmt->bInTIMWake) 174e269fc2dSAndres More return false; 17592b96797SForest Bond } 17692b96797SForest Bond 1777404eab2SPhilip Worrall /* check scan state */ 17892b96797SForest Bond if (pDevice->bCmdRunning) 179e269fc2dSAndres More return false; 18092b96797SForest Bond 1817404eab2SPhilip Worrall /* Tx Burst */ 18292b96797SForest Bond if (pDevice->bPSModeTxBurst) 183e269fc2dSAndres More return false; 18492b96797SForest Bond 1857404eab2SPhilip Worrall /* Froce PSEN on */ 18636957537SMalcolm Priestley vnt_mac_reg_bits_on(pDevice, MAC_REG_PSCTL, PSCTL_PSEN); 18792b96797SForest Bond 18892b96797SForest Bond if (pMgmt->eCurrMode != WMAC_MODE_IBSS_STA) { 189f9cfbe94SPhilip Worrall if (bCheckCountToWakeUp && (pMgmt->wCountToWakeUp == 0 190f9cfbe94SPhilip Worrall || pMgmt->wCountToWakeUp == 1)) { 191e269fc2dSAndres More return false; 19292b96797SForest Bond } 19392b96797SForest Bond } 19492b96797SForest Bond 1954e9b5e2bSAndres More pDevice->bPSRxBeacon = true; 196f9cfbe94SPhilip Worrall 1977404eab2SPhilip Worrall /* no Tx, no Rx isr, now go to Doze */ 19836957537SMalcolm Priestley vnt_mac_reg_bits_on(pDevice, MAC_REG_PSCTL, PSCTL_GO2DOZE); 19992b96797SForest Bond DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Go to Doze ZZZZZZZZZZZZZZZ\n"); 2004e9b5e2bSAndres More return true; 20192b96797SForest Bond } 20292b96797SForest Bond 2037404eab2SPhilip Worrall /* 20492b96797SForest Bond * 20592b96797SForest Bond * Routine Description: 20692b96797SForest Bond * Send PS-POLL packet 20792b96797SForest Bond * 20892b96797SForest Bond * Return Value: 20992b96797SForest Bond * None. 21092b96797SForest Bond * 2117404eab2SPhilip Worrall */ 21292b96797SForest Bond 21345c73bb1SMalcolm Priestley void PSvSendPSPOLL(struct vnt_private *pDevice) 21492b96797SForest Bond { 21545c73bb1SMalcolm Priestley struct vnt_manager *pMgmt = &pDevice->vnt_mgmt; 21645c73bb1SMalcolm Priestley struct vnt_tx_mgmt *pTxPacket = NULL; 21792b96797SForest Bond 21845c73bb1SMalcolm Priestley memset(pMgmt->pbyPSPacketPool, 0, sizeof(struct vnt_tx_mgmt) 21945c73bb1SMalcolm Priestley + WLAN_HDR_ADDR2_LEN); 22045c73bb1SMalcolm Priestley pTxPacket = (struct vnt_tx_mgmt *)pMgmt->pbyPSPacketPool; 22145c73bb1SMalcolm Priestley pTxPacket->p80211Header = (PUWLAN_80211HDR)((u8 *)pTxPacket 22245c73bb1SMalcolm Priestley + sizeof(struct vnt_tx_mgmt)); 22345c73bb1SMalcolm Priestley 22492b96797SForest Bond pTxPacket->p80211Header->sA2.wFrameCtl = cpu_to_le16( 22592b96797SForest Bond ( 22692b96797SForest Bond WLAN_SET_FC_FTYPE(WLAN_TYPE_CTL) | 22792b96797SForest Bond WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_PSPOLL) | 22892b96797SForest Bond WLAN_SET_FC_PWRMGT(0) 22992b96797SForest Bond )); 230f9cfbe94SPhilip Worrall 23164052b78SAybuke Ozdemir pTxPacket->p80211Header->sA2.wDurationID = 23264052b78SAybuke Ozdemir pMgmt->wCurrAID | BIT14 | BIT15; 23364052b78SAybuke Ozdemir memcpy(pTxPacket->p80211Header->sA2.abyAddr1, pMgmt->abyCurrBSSID, 23464052b78SAybuke Ozdemir WLAN_ADDR_LEN); 23564052b78SAybuke Ozdemir memcpy(pTxPacket->p80211Header->sA2.abyAddr2, pMgmt->abyMACAddr, 23664052b78SAybuke Ozdemir WLAN_ADDR_LEN); 23792b96797SForest Bond pTxPacket->cbMPDULen = WLAN_HDR_ADDR2_LEN; 23892b96797SForest Bond pTxPacket->cbPayloadLen = 0; 239f9cfbe94SPhilip Worrall 240e25b75ecSPhilip Worrall /* log failure if sending failed */ 2410f783c9dSJohannes Löthberg if (csMgmt_xmit(pDevice, pTxPacket) != CMD_STATUS_PENDING) 24264052b78SAybuke Ozdemir DBG_PRT(MSG_LEVEL_DEBUG, 24364052b78SAybuke Ozdemir KERN_INFO "Send PS-Poll packet failed..\n"); 244e25b75ecSPhilip Worrall } 24592b96797SForest Bond 2467404eab2SPhilip Worrall /* 24792b96797SForest Bond * 24892b96797SForest Bond * Routine Description: 24992b96797SForest Bond * Send NULL packet to AP for notification power state of STA 25092b96797SForest Bond * 25192b96797SForest Bond * Return Value: 25292b96797SForest Bond * None. 25392b96797SForest Bond * 2547404eab2SPhilip Worrall */ 2550cbd8d98SAndres More 25645c73bb1SMalcolm Priestley int PSbSendNullPacket(struct vnt_private *pDevice) 25792b96797SForest Bond { 25845c73bb1SMalcolm Priestley struct vnt_tx_mgmt *pTxPacket = NULL; 25945c73bb1SMalcolm Priestley struct vnt_manager *pMgmt = &pDevice->vnt_mgmt; 260b898cf21SPhilip Worrall u16 flags = 0; 26192b96797SForest Bond 262e269fc2dSAndres More if (pDevice->bLinkPass == false) 263e269fc2dSAndres More return false; 26492b96797SForest Bond 26560cc2747SMalcolm Priestley if (pDevice->bEnablePSMode == false && pDevice->tx_trigger == false) 266e269fc2dSAndres More return false; 267feaf03d3SAndres More 26845c73bb1SMalcolm Priestley memset(pMgmt->pbyPSPacketPool, 0, sizeof(struct vnt_tx_mgmt) 26945c73bb1SMalcolm Priestley + WLAN_NULLDATA_FR_MAXLEN); 27045c73bb1SMalcolm Priestley pTxPacket = (struct vnt_tx_mgmt *)pMgmt->pbyPSPacketPool; 27145c73bb1SMalcolm Priestley pTxPacket->p80211Header = (PUWLAN_80211HDR)((u8 *)pTxPacket 27245c73bb1SMalcolm Priestley + sizeof(struct vnt_tx_mgmt)); 27392b96797SForest Bond 274b898cf21SPhilip Worrall flags = WLAN_SET_FC_FTYPE(WLAN_TYPE_DATA) | 275b898cf21SPhilip Worrall WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_NULL); 276b898cf21SPhilip Worrall 277b898cf21SPhilip Worrall if (pDevice->bEnablePSMode) 278b898cf21SPhilip Worrall flags |= WLAN_SET_FC_PWRMGT(1); 279b898cf21SPhilip Worrall else 280b898cf21SPhilip Worrall flags |= WLAN_SET_FC_PWRMGT(0); 281b898cf21SPhilip Worrall 282b898cf21SPhilip Worrall pTxPacket->p80211Header->sA3.wFrameCtl = cpu_to_le16(flags); 28392b96797SForest Bond 284036dba14SPhilip Worrall if (pMgmt->eCurrMode != WMAC_MODE_IBSS_STA) 28564052b78SAybuke Ozdemir pTxPacket->p80211Header->sA3.wFrameCtl |= 28664052b78SAybuke Ozdemir cpu_to_le16((u16)WLAN_SET_FC_TODS(1)); 28792b96797SForest Bond 28864052b78SAybuke Ozdemir memcpy(pTxPacket->p80211Header->sA3.abyAddr1, pMgmt->abyCurrBSSID, 28964052b78SAybuke Ozdemir WLAN_ADDR_LEN); 29064052b78SAybuke Ozdemir memcpy(pTxPacket->p80211Header->sA3.abyAddr2, pMgmt->abyMACAddr, 29164052b78SAybuke Ozdemir WLAN_ADDR_LEN); 29264052b78SAybuke Ozdemir memcpy(pTxPacket->p80211Header->sA3.abyAddr3, pMgmt->abyCurrBSSID, 29364052b78SAybuke Ozdemir WLAN_BSSID_LEN); 29492b96797SForest Bond pTxPacket->cbMPDULen = WLAN_HDR_ADDR3_LEN; 29592b96797SForest Bond pTxPacket->cbPayloadLen = 0; 296e25b75ecSPhilip Worrall /* log error if sending failed */ 29792b96797SForest Bond if (csMgmt_xmit(pDevice, pTxPacket) != CMD_STATUS_PENDING) { 29864052b78SAybuke Ozdemir DBG_PRT(MSG_LEVEL_DEBUG, 29964052b78SAybuke Ozdemir KERN_INFO "Send Null Packet failed !\n"); 300e269fc2dSAndres More return false; 30192b96797SForest Bond } 3024e9b5e2bSAndres More return true; 30392b96797SForest Bond } 30492b96797SForest Bond 3057404eab2SPhilip Worrall /* 30692b96797SForest Bond * 30792b96797SForest Bond * Routine Description: 30892b96797SForest Bond * Check if Next TBTT must wake up 30992b96797SForest Bond * 31092b96797SForest Bond * Return Value: 31192b96797SForest Bond * None. 31292b96797SForest Bond * 3137404eab2SPhilip Worrall */ 31492b96797SForest Bond 31545c73bb1SMalcolm Priestley int PSbIsNextTBTTWakeUp(struct vnt_private *pDevice) 31692b96797SForest Bond { 31745c73bb1SMalcolm Priestley struct vnt_manager *pMgmt = &pDevice->vnt_mgmt; 318e269fc2dSAndres More int bWakeUp = false; 31992b96797SForest Bond 32092b96797SForest Bond if (pMgmt->wListenInterval >= 2) { 321036dba14SPhilip Worrall if (pMgmt->wCountToWakeUp == 0) 32292b96797SForest Bond pMgmt->wCountToWakeUp = pMgmt->wListenInterval; 32392b96797SForest Bond 32492b96797SForest Bond pMgmt->wCountToWakeUp--; 32592b96797SForest Bond 32692b96797SForest Bond if (pMgmt->wCountToWakeUp == 1) { 3277404eab2SPhilip Worrall /* Turn on wake up to listen next beacon */ 32836957537SMalcolm Priestley vnt_mac_reg_bits_on(pDevice, MAC_REG_PSCTL, PSCTL_LNBCN); 329e269fc2dSAndres More pDevice->bPSRxBeacon = false; 3304e9b5e2bSAndres More bWakeUp = true; 33192b96797SForest Bond } else if (!pDevice->bPSRxBeacon) { 3327404eab2SPhilip Worrall /* Listen until RxBeacon */ 33336957537SMalcolm Priestley vnt_mac_reg_bits_on(pDevice, MAC_REG_PSCTL, PSCTL_LNBCN); 33492b96797SForest Bond } 33592b96797SForest Bond } 33692b96797SForest Bond return bWakeUp; 33792b96797SForest Bond } 33892b96797SForest Bond 339