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