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 52 53 54/*--------------------- Static Classes ----------------------------*/ 55 56/*--------------------- Static Variables --------------------------*/ 57static int msglevel =MSG_LEVEL_INFO; 58/*--------------------- Static Functions --------------------------*/ 59 60 61/*--------------------- Export Variables --------------------------*/ 62 63 64/*--------------------- Export Functions --------------------------*/ 65 66/*+ 67 * 68 * Routine Description: 69 * Enable hw power saving functions 70 * 71 * Return Value: 72 * None. 73 * 74-*/ 75 76 77void 78PSvEnablePowerSaving( 79 void *hDeviceContext, 80 unsigned short wListenInterval 81 ) 82{ 83 PSDevice pDevice = (PSDevice)hDeviceContext; 84 PSMgmtObject pMgmt = pDevice->pMgmt; 85 unsigned short wAID = pMgmt->wCurrAID | BIT14 | BIT15; 86 87 // set period of power up before TBTT 88 VNSvOutPortW(pDevice->PortOffset + MAC_REG_PWBT, C_PWBT); 89 if (pDevice->eOPMode != OP_MODE_ADHOC) { 90 // set AID 91 VNSvOutPortW(pDevice->PortOffset + MAC_REG_AIDATIM, wAID); 92 } else { 93 // set ATIM Window 94 MACvWriteATIMW(pDevice->PortOffset, pMgmt->wCurrATIMWindow); 95 } 96 // Set AutoSleep 97 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCFG, PSCFG_AUTOSLEEP); 98 // Set HWUTSF 99 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_TFTCTL, TFTCTL_HWUTSF); 100 101 if (wListenInterval >= 2) { 102 // clear always listen beacon 103 MACvRegBitsOff(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_ALBCN); 104 //pDevice->wCFG &= ~CFG_ALB; 105 // first time set listen next beacon 106 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_LNBCN); 107 pMgmt->wCountToWakeUp = wListenInterval; 108 } 109 else { 110 // always listen beacon 111 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_ALBCN); 112 //pDevice->wCFG |= CFG_ALB; 113 pMgmt->wCountToWakeUp = 0; 114 } 115 116 // enable power saving hw function 117 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_PSEN); 118 pDevice->bEnablePSMode = true; 119 120 if (pDevice->eOPMode == OP_MODE_ADHOC) { 121// bMgrPrepareBeaconToSend((void *)pDevice, pMgmt); 122 } 123 // We don't send null pkt in ad hoc mode since beacon will handle this. 124 else if (pDevice->eOPMode == OP_MODE_INFRASTRUCTURE) { 125 PSbSendNullPacket(pDevice); 126 } 127 pDevice->bPWBitOn = true; 128 DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "PS:Power Saving Mode Enable... \n"); 129 return; 130} 131 132 133 134 135 136 137/*+ 138 * 139 * Routine Description: 140 * Disable hw power saving functions 141 * 142 * Return Value: 143 * None. 144 * 145-*/ 146 147void 148PSvDisablePowerSaving( 149 void *hDeviceContext 150 ) 151{ 152 PSDevice pDevice = (PSDevice)hDeviceContext; 153// PSMgmtObject pMgmt = pDevice->pMgmt; 154 155 // disable power saving hw function 156 MACbPSWakeup(pDevice->PortOffset); 157 //clear AutoSleep 158 MACvRegBitsOff(pDevice->PortOffset, MAC_REG_PSCFG, PSCFG_AUTOSLEEP); 159 //clear HWUTSF 160 MACvRegBitsOff(pDevice->PortOffset, MAC_REG_TFTCTL, TFTCTL_HWUTSF); 161 // set always listen beacon 162 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_ALBCN); 163 164 pDevice->bEnablePSMode = false; 165 166 if (pDevice->eOPMode == OP_MODE_INFRASTRUCTURE) { 167 PSbSendNullPacket(pDevice); 168 } 169 pDevice->bPWBitOn = false; 170 return; 171} 172 173 174/*+ 175 * 176 * Routine Description: 177 * Consider to power down when no more packets to tx or rx. 178 * 179 * Return Value: 180 * true, if power down success 181 * false, if fail 182-*/ 183 184 185bool 186PSbConsiderPowerDown( 187 void *hDeviceContext, 188 bool bCheckRxDMA, 189 bool bCheckCountToWakeUp 190 ) 191{ 192 PSDevice pDevice = (PSDevice)hDeviceContext; 193 PSMgmtObject pMgmt = pDevice->pMgmt; 194 unsigned int uIdx; 195 196 // check if already in Doze mode 197 if (MACbIsRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_PS)) 198 return true; 199 200 if (pMgmt->eCurrMode != WMAC_MODE_IBSS_STA) { 201 // check if in TIM wake period 202 if (pMgmt->bInTIMWake) 203 return false; 204 } 205 206 // check scan state 207 if (pDevice->bCmdRunning) 208 return false; 209 210 // Froce PSEN on 211 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_PSEN); 212 213 // check if all TD are empty, 214 for (uIdx = 0; uIdx < TYPE_MAXTD; uIdx ++) { 215 if (pDevice->iTDUsed[uIdx] != 0) 216 return false; 217 } 218 219 // check if rx isr is clear 220 if (bCheckRxDMA && 221 ((pDevice->dwIsr& ISR_RXDMA0) != 0) && 222 ((pDevice->dwIsr & ISR_RXDMA1) != 0)){ 223 return false; 224 } 225 226 if (pMgmt->eCurrMode != WMAC_MODE_IBSS_STA) { 227 if (bCheckCountToWakeUp && 228 (pMgmt->wCountToWakeUp == 0 || pMgmt->wCountToWakeUp == 1)) { 229 return false; 230 } 231 } 232 233 // no Tx, no Rx isr, now go to Doze 234 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_GO2DOZE); 235 DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Go to Doze ZZZZZZZZZZZZZZZ\n"); 236 return true; 237} 238 239 240 241/*+ 242 * 243 * Routine Description: 244 * Send PS-POLL packet 245 * 246 * Return Value: 247 * None. 248 * 249-*/ 250 251 252 253void 254PSvSendPSPOLL( 255 void *hDeviceContext 256 ) 257{ 258 PSDevice pDevice = (PSDevice)hDeviceContext; 259 PSMgmtObject pMgmt = pDevice->pMgmt; 260 PSTxMgmtPacket pTxPacket = NULL; 261 262 263 memset(pMgmt->pbyPSPacketPool, 0, sizeof(STxMgmtPacket) + WLAN_HDR_ADDR2_LEN); 264 pTxPacket = (PSTxMgmtPacket)pMgmt->pbyPSPacketPool; 265 pTxPacket->p80211Header = (PUWLAN_80211HDR)((unsigned char *)pTxPacket + sizeof(STxMgmtPacket)); 266 pTxPacket->p80211Header->sA2.wFrameCtl = cpu_to_le16( 267 ( 268 WLAN_SET_FC_FTYPE(WLAN_TYPE_CTL) | 269 WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_PSPOLL) | 270 WLAN_SET_FC_PWRMGT(0) 271 )); 272 pTxPacket->p80211Header->sA2.wDurationID = pMgmt->wCurrAID | BIT14 | BIT15; 273 memcpy(pTxPacket->p80211Header->sA2.abyAddr1, pMgmt->abyCurrBSSID, WLAN_ADDR_LEN); 274 memcpy(pTxPacket->p80211Header->sA2.abyAddr2, pMgmt->abyMACAddr, WLAN_ADDR_LEN); 275 pTxPacket->cbMPDULen = WLAN_HDR_ADDR2_LEN; 276 pTxPacket->cbPayloadLen = 0; 277 // send the frame 278 if (csMgmt_xmit(pDevice, pTxPacket) != CMD_STATUS_PENDING) { 279 DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Send PS-Poll packet failed..\n"); 280 } 281 else { 282// DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Send PS-Poll packet success..\n"); 283 }; 284 285 return; 286} 287 288 289 290/*+ 291 * 292 * Routine Description: 293 * Send NULL packet to AP for notification power state of STA 294 * 295 * Return Value: 296 * None. 297 * 298-*/ 299bool 300PSbSendNullPacket( 301 void *hDeviceContext 302 ) 303{ 304 PSDevice pDevice = (PSDevice)hDeviceContext; 305 PSTxMgmtPacket pTxPacket = NULL; 306 PSMgmtObject pMgmt = pDevice->pMgmt; 307 unsigned int uIdx; 308 309 310 if (pDevice->bLinkPass == false) { 311 return false; 312 } 313 #ifdef TxInSleep 314 if ((pDevice->bEnablePSMode == false) && 315 (pDevice->fTxDataInSleep == false)){ 316 return false; 317 } 318#else 319 if (pDevice->bEnablePSMode == false) { 320 return false; 321 } 322#endif 323 if (pDevice->bEnablePSMode) { 324 for (uIdx = 0; uIdx < TYPE_MAXTD; uIdx ++) { 325 if (pDevice->iTDUsed[uIdx] != 0) 326 return false; 327 } 328 } 329 330 memset(pMgmt->pbyPSPacketPool, 0, sizeof(STxMgmtPacket) + WLAN_NULLDATA_FR_MAXLEN); 331 pTxPacket = (PSTxMgmtPacket)pMgmt->pbyPSPacketPool; 332 pTxPacket->p80211Header = (PUWLAN_80211HDR)((unsigned char *)pTxPacket + sizeof(STxMgmtPacket)); 333 334 if (pDevice->bEnablePSMode) { 335 336 pTxPacket->p80211Header->sA3.wFrameCtl = cpu_to_le16( 337 ( 338 WLAN_SET_FC_FTYPE(WLAN_TYPE_DATA) | 339 WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_NULL) | 340 WLAN_SET_FC_PWRMGT(1) 341 )); 342 } 343 else { 344 pTxPacket->p80211Header->sA3.wFrameCtl = cpu_to_le16( 345 ( 346 WLAN_SET_FC_FTYPE(WLAN_TYPE_DATA) | 347 WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_NULL) | 348 WLAN_SET_FC_PWRMGT(0) 349 )); 350 } 351 352 if(pMgmt->eCurrMode != WMAC_MODE_IBSS_STA) { 353 pTxPacket->p80211Header->sA3.wFrameCtl |= cpu_to_le16((unsigned short)WLAN_SET_FC_TODS(1)); 354 } 355 356 memcpy(pTxPacket->p80211Header->sA3.abyAddr1, pMgmt->abyCurrBSSID, WLAN_ADDR_LEN); 357 memcpy(pTxPacket->p80211Header->sA3.abyAddr2, pMgmt->abyMACAddr, WLAN_ADDR_LEN); 358 memcpy(pTxPacket->p80211Header->sA3.abyAddr3, pMgmt->abyCurrBSSID, WLAN_BSSID_LEN); 359 pTxPacket->cbMPDULen = WLAN_HDR_ADDR3_LEN; 360 pTxPacket->cbPayloadLen = 0; 361 // send the frame 362 if (csMgmt_xmit(pDevice, pTxPacket) != CMD_STATUS_PENDING) { 363 DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Send Null Packet failed !\n"); 364 return false; 365 } 366 else { 367 368// DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Send Null Packet success....\n"); 369 } 370 371 372 return true ; 373} 374 375/*+ 376 * 377 * Routine Description: 378 * Check if Next TBTT must wake up 379 * 380 * Return Value: 381 * None. 382 * 383-*/ 384 385bool 386PSbIsNextTBTTWakeUp( 387 void *hDeviceContext 388 ) 389{ 390 391 PSDevice pDevice = (PSDevice)hDeviceContext; 392 PSMgmtObject pMgmt = pDevice->pMgmt; 393 bool bWakeUp = false; 394 395 if (pMgmt->wListenInterval >= 2) { 396 if (pMgmt->wCountToWakeUp == 0) { 397 pMgmt->wCountToWakeUp = pMgmt->wListenInterval; 398 } 399 400 pMgmt->wCountToWakeUp --; 401 402 if (pMgmt->wCountToWakeUp == 1) { 403 // Turn on wake up to listen next beacon 404 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_LNBCN); 405 bWakeUp = true; 406 } 407 408 } 409 410 return bWakeUp; 411} 412 413