1 /* 2 * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. 3 * All rights reserved. 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * 20 * File: power.c 21 * 22 * Purpose: Handles 802.11 power management functions 23 * 24 * Author: Lyndon Chen 25 * 26 * Date: July 17, 2002 27 * 28 * Functions: 29 * PSvEnablePowerSaving - Enable Power Saving Mode 30 * PSvDiasblePowerSaving - Disable Power Saving Mode 31 * PSbConsiderPowerDown - Decide if we can Power Down 32 * PSvSendPSPOLL - Send PS-POLL packet 33 * PSbSendNullPacket - Send Null packet 34 * PSbIsNextTBTTWakeUp - Decide if we need to wake up at next Beacon 35 * 36 * Revision History: 37 * 38 */ 39 40 #include "ttype.h" 41 #include "mac.h" 42 #include "device.h" 43 #include "wmgr.h" 44 #include "power.h" 45 #include "wcmd.h" 46 #include "rxtx.h" 47 #include "card.h" 48 49 /*--------------------- Static Definitions -------------------------*/ 50 51 /*--------------------- Static Classes ----------------------------*/ 52 53 /*--------------------- Static Variables --------------------------*/ 54 static int msglevel = MSG_LEVEL_INFO; 55 /*--------------------- Static Functions --------------------------*/ 56 57 /*--------------------- Export Variables --------------------------*/ 58 59 /*--------------------- Export Functions --------------------------*/ 60 61 /*+ 62 * 63 * Routine Description: 64 * Enable hw power saving functions 65 * 66 * Return Value: 67 * None. 68 * 69 -*/ 70 71 void 72 PSvEnablePowerSaving( 73 void *hDeviceContext, 74 unsigned short wListenInterval 75 ) 76 { 77 PSDevice pDevice = (PSDevice)hDeviceContext; 78 PSMgmtObject pMgmt = pDevice->pMgmt; 79 unsigned short wAID = pMgmt->wCurrAID | BIT14 | BIT15; 80 81 // set period of power up before TBTT 82 VNSvOutPortW(pDevice->PortOffset + MAC_REG_PWBT, C_PWBT); 83 if (pDevice->eOPMode != OP_MODE_ADHOC) { 84 // set AID 85 VNSvOutPortW(pDevice->PortOffset + MAC_REG_AIDATIM, wAID); 86 } else { 87 // set ATIM Window 88 MACvWriteATIMW(pDevice->PortOffset, pMgmt->wCurrATIMWindow); 89 } 90 // Set AutoSleep 91 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCFG, PSCFG_AUTOSLEEP); 92 // Set HWUTSF 93 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_TFTCTL, TFTCTL_HWUTSF); 94 95 if (wListenInterval >= 2) { 96 // clear always listen beacon 97 MACvRegBitsOff(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_ALBCN); 98 // first time set listen next beacon 99 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_LNBCN); 100 pMgmt->wCountToWakeUp = wListenInterval; 101 } else { 102 // always listen beacon 103 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_ALBCN); 104 pMgmt->wCountToWakeUp = 0; 105 } 106 107 // enable power saving hw function 108 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_PSEN); 109 pDevice->bEnablePSMode = true; 110 111 /* We don't send null pkt in ad hoc mode since beacon will handle this. */ 112 if (pDevice->eOPMode != OP_MODE_ADHOC && pDevice->eOPMode == OP_MODE_INFRASTRUCTURE) 113 PSbSendNullPacket(pDevice); 114 115 pDevice->bPWBitOn = true; 116 DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "PS:Power Saving Mode Enable...\n"); 117 } 118 119 /*+ 120 * 121 * Routine Description: 122 * Disable hw power saving functions 123 * 124 * Return Value: 125 * None. 126 * 127 -*/ 128 129 void 130 PSvDisablePowerSaving( 131 void *hDeviceContext 132 ) 133 { 134 PSDevice pDevice = (PSDevice)hDeviceContext; 135 136 // disable power saving hw function 137 MACbPSWakeup(pDevice->PortOffset); 138 //clear AutoSleep 139 MACvRegBitsOff(pDevice->PortOffset, MAC_REG_PSCFG, PSCFG_AUTOSLEEP); 140 //clear HWUTSF 141 MACvRegBitsOff(pDevice->PortOffset, MAC_REG_TFTCTL, TFTCTL_HWUTSF); 142 // set always listen beacon 143 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_ALBCN); 144 145 pDevice->bEnablePSMode = false; 146 147 if (pDevice->eOPMode == OP_MODE_INFRASTRUCTURE) 148 PSbSendNullPacket(pDevice); 149 150 pDevice->bPWBitOn = false; 151 } 152 153 /*+ 154 * 155 * Routine Description: 156 * Consider to power down when no more packets to tx or rx. 157 * 158 * Return Value: 159 * true, if power down success 160 * false, if fail 161 -*/ 162 163 bool 164 PSbConsiderPowerDown( 165 void *hDeviceContext, 166 bool bCheckRxDMA, 167 bool bCheckCountToWakeUp 168 ) 169 { 170 PSDevice pDevice = (PSDevice)hDeviceContext; 171 PSMgmtObject pMgmt = pDevice->pMgmt; 172 unsigned int uIdx; 173 174 // check if already in Doze mode 175 if (MACbIsRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_PS)) 176 return true; 177 178 if (pMgmt->eCurrMode != WMAC_MODE_IBSS_STA) { 179 // check if in TIM wake period 180 if (pMgmt->bInTIMWake) 181 return false; 182 } 183 184 // check scan state 185 if (pDevice->bCmdRunning) 186 return false; 187 188 // Force PSEN on 189 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_PSEN); 190 191 // check if all TD are empty, 192 for (uIdx = 0; uIdx < TYPE_MAXTD; uIdx++) { 193 if (pDevice->iTDUsed[uIdx] != 0) 194 return false; 195 } 196 197 // check if rx isr is clear 198 if (bCheckRxDMA && 199 ((pDevice->dwIsr & ISR_RXDMA0) != 0) && 200 ((pDevice->dwIsr & ISR_RXDMA1) != 0)) { 201 return false; 202 } 203 204 if (pMgmt->eCurrMode != WMAC_MODE_IBSS_STA) { 205 if (bCheckCountToWakeUp && 206 (pMgmt->wCountToWakeUp == 0 || pMgmt->wCountToWakeUp == 1)) { 207 return false; 208 } 209 } 210 211 // no Tx, no Rx isr, now go to Doze 212 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_GO2DOZE); 213 DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Go to Doze ZZZZZZZZZZZZZZZ\n"); 214 return true; 215 } 216 217 /*+ 218 * 219 * Routine Description: 220 * Send PS-POLL packet 221 * 222 * Return Value: 223 * None. 224 * 225 -*/ 226 227 void 228 PSvSendPSPOLL( 229 void *hDeviceContext 230 ) 231 { 232 PSDevice pDevice = (PSDevice)hDeviceContext; 233 PSMgmtObject pMgmt = pDevice->pMgmt; 234 PSTxMgmtPacket pTxPacket = NULL; 235 236 memset(pMgmt->pbyPSPacketPool, 0, sizeof(STxMgmtPacket) + WLAN_HDR_ADDR2_LEN); 237 pTxPacket = (PSTxMgmtPacket)pMgmt->pbyPSPacketPool; 238 pTxPacket->p80211Header = (PUWLAN_80211HDR)((unsigned char *)pTxPacket + sizeof(STxMgmtPacket)); 239 pTxPacket->p80211Header->sA2.wFrameCtl = cpu_to_le16( 240 ( 241 WLAN_SET_FC_FTYPE(WLAN_TYPE_CTL) | 242 WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_PSPOLL) | 243 WLAN_SET_FC_PWRMGT(0) 244 )); 245 pTxPacket->p80211Header->sA2.wDurationID = pMgmt->wCurrAID | BIT14 | BIT15; 246 memcpy(pTxPacket->p80211Header->sA2.abyAddr1, pMgmt->abyCurrBSSID, WLAN_ADDR_LEN); 247 memcpy(pTxPacket->p80211Header->sA2.abyAddr2, pMgmt->abyMACAddr, WLAN_ADDR_LEN); 248 pTxPacket->cbMPDULen = WLAN_HDR_ADDR2_LEN; 249 pTxPacket->cbPayloadLen = 0; 250 // send the frame 251 if (csMgmt_xmit(pDevice, pTxPacket) != CMD_STATUS_PENDING) 252 DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Send PS-Poll packet failed..\n"); 253 } 254 255 /*+ 256 * 257 * Routine Description: 258 * Send NULL packet to AP for notification power state of STA 259 * 260 * Return Value: 261 * None. 262 * 263 -*/ 264 bool 265 PSbSendNullPacket( 266 void *hDeviceContext 267 ) 268 { 269 PSDevice pDevice = (PSDevice)hDeviceContext; 270 PSTxMgmtPacket pTxPacket = NULL; 271 PSMgmtObject pMgmt = pDevice->pMgmt; 272 unsigned int uIdx; 273 274 if (!pDevice->bLinkPass) 275 return false; 276 277 #ifdef TxInSleep 278 if (!pDevice->bEnablePSMode && !pDevice->fTxDataInSleep) 279 return false; 280 #else 281 if (!pDevice->bEnablePSMode) 282 return false; 283 #endif 284 if (pDevice->bEnablePSMode) { 285 for (uIdx = 0; uIdx < TYPE_MAXTD; uIdx++) { 286 if (pDevice->iTDUsed[uIdx] != 0) 287 return false; 288 } 289 } 290 291 memset(pMgmt->pbyPSPacketPool, 0, sizeof(STxMgmtPacket) + WLAN_NULLDATA_FR_MAXLEN); 292 pTxPacket = (PSTxMgmtPacket)pMgmt->pbyPSPacketPool; 293 pTxPacket->p80211Header = (PUWLAN_80211HDR)((unsigned char *)pTxPacket + sizeof(STxMgmtPacket)); 294 295 if (pDevice->bEnablePSMode) { 296 pTxPacket->p80211Header->sA3.wFrameCtl = cpu_to_le16( 297 ( 298 WLAN_SET_FC_FTYPE(WLAN_TYPE_DATA) | 299 WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_NULL) | 300 WLAN_SET_FC_PWRMGT(1) 301 )); 302 } else { 303 pTxPacket->p80211Header->sA3.wFrameCtl = cpu_to_le16( 304 ( 305 WLAN_SET_FC_FTYPE(WLAN_TYPE_DATA) | 306 WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_NULL) | 307 WLAN_SET_FC_PWRMGT(0) 308 )); 309 } 310 311 if (pMgmt->eCurrMode != WMAC_MODE_IBSS_STA) 312 pTxPacket->p80211Header->sA3.wFrameCtl |= cpu_to_le16((unsigned short)WLAN_SET_FC_TODS(1)); 313 314 memcpy(pTxPacket->p80211Header->sA3.abyAddr1, pMgmt->abyCurrBSSID, WLAN_ADDR_LEN); 315 memcpy(pTxPacket->p80211Header->sA3.abyAddr2, pMgmt->abyMACAddr, WLAN_ADDR_LEN); 316 memcpy(pTxPacket->p80211Header->sA3.abyAddr3, pMgmt->abyCurrBSSID, WLAN_BSSID_LEN); 317 pTxPacket->cbMPDULen = WLAN_HDR_ADDR3_LEN; 318 pTxPacket->cbPayloadLen = 0; 319 // send the frame 320 if (csMgmt_xmit(pDevice, pTxPacket) != CMD_STATUS_PENDING) { 321 DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Send Null Packet failed !\n"); 322 return false; 323 } 324 325 return true; 326 } 327 328 /*+ 329 * 330 * Routine Description: 331 * Check if Next TBTT must wake up 332 * 333 * Return Value: 334 * None. 335 * 336 -*/ 337 338 bool 339 PSbIsNextTBTTWakeUp( 340 void *hDeviceContext 341 ) 342 { 343 PSDevice pDevice = (PSDevice)hDeviceContext; 344 PSMgmtObject pMgmt = pDevice->pMgmt; 345 bool bWakeUp = false; 346 347 if (pMgmt->wListenInterval >= 2) { 348 if (pMgmt->wCountToWakeUp == 0) 349 pMgmt->wCountToWakeUp = pMgmt->wListenInterval; 350 351 pMgmt->wCountToWakeUp--; 352 353 if (pMgmt->wCountToWakeUp == 1) { 354 // Turn on wake up to listen next beacon 355 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_LNBCN); 356 bWakeUp = true; 357 } 358 359 } 360 361 return bWakeUp; 362 } 363