blob: 08241b91777770a65d3c467c38e78dc0b7e34972 [file] [log] [blame]
Forest Bond5449c682009-04-25 10:30:44 -04001/*
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 *
Jim Lieb612822f2009-08-12 14:54:03 -070019 *
Forest Bond5449c682009-04-25 10:30:44 -040020 * File: power.c
21 *
Uwe Kleine-König658ce9d2009-07-23 08:33:56 +020022 * Purpose: Handles 802.11 power management functions
Forest Bond5449c682009-04-25 10:30:44 -040023 *
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
Forest Bond5449c682009-04-25 10:30:44 -040040#include "ttype.h"
Forest Bond5449c682009-04-25 10:30:44 -040041#include "mac.h"
Forest Bond5449c682009-04-25 10:30:44 -040042#include "device.h"
Forest Bond5449c682009-04-25 10:30:44 -040043#include "wmgr.h"
Forest Bond5449c682009-04-25 10:30:44 -040044#include "power.h"
Forest Bond5449c682009-04-25 10:30:44 -040045#include "wcmd.h"
Forest Bond5449c682009-04-25 10:30:44 -040046#include "rxtx.h"
Forest Bond5449c682009-04-25 10:30:44 -040047#include "card.h"
Forest Bond5449c682009-04-25 10:30:44 -040048
49/*--------------------- Static Definitions -------------------------*/
50
Forest Bond5449c682009-04-25 10:30:44 -040051/*--------------------- Static Classes ----------------------------*/
52
Forest Bond5449c682009-04-25 10:30:44 -040053/*--------------------- Static Functions --------------------------*/
54
Forest Bond5449c682009-04-25 10:30:44 -040055/*--------------------- Export Variables --------------------------*/
56
Forest Bond5449c682009-04-25 10:30:44 -040057/*--------------------- Export Functions --------------------------*/
58
59/*+
60 *
61 * Routine Description:
62 * Enable hw power saving functions
63 *
64 * Return Value:
65 * None.
66 *
Joe Perches474f0f82013-03-18 10:44:58 -070067 -*/
Forest Bond5449c682009-04-25 10:30:44 -040068
Charles Clément6b35b7b2010-05-07 12:30:19 -070069void
Forest Bond5449c682009-04-25 10:30:44 -040070PSvEnablePowerSaving(
Joe Perches474f0f82013-03-18 10:44:58 -070071 void *hDeviceContext,
72 unsigned short wListenInterval
73)
Forest Bond5449c682009-04-25 10:30:44 -040074{
Malcolm Priestleycf76dc42014-08-10 15:46:59 +010075 struct vnt_private *pDevice = hDeviceContext;
Joe Perches474f0f82013-03-18 10:44:58 -070076 PSMgmtObject pMgmt = pDevice->pMgmt;
77 unsigned short wAID = pMgmt->wCurrAID | BIT14 | BIT15;
Forest Bond5449c682009-04-25 10:30:44 -040078
Joe Perches474f0f82013-03-18 10:44:58 -070079 // set period of power up before TBTT
80 VNSvOutPortW(pDevice->PortOffset + MAC_REG_PWBT, C_PWBT);
Malcolm Priestleya9873672014-08-30 22:25:49 +010081 if (pDevice->op_mode != NL80211_IFTYPE_ADHOC) {
Joe Perches474f0f82013-03-18 10:44:58 -070082 // set AID
83 VNSvOutPortW(pDevice->PortOffset + MAC_REG_AIDATIM, wAID);
84 } else {
85 // set ATIM Window
86 MACvWriteATIMW(pDevice->PortOffset, pMgmt->wCurrATIMWindow);
87 }
88 // Set AutoSleep
89 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCFG, PSCFG_AUTOSLEEP);
90 // Set HWUTSF
91 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_TFTCTL, TFTCTL_HWUTSF);
Forest Bond5449c682009-04-25 10:30:44 -040092
Joe Perches474f0f82013-03-18 10:44:58 -070093 if (wListenInterval >= 2) {
94 // clear always listen beacon
95 MACvRegBitsOff(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_ALBCN);
Joe Perches474f0f82013-03-18 10:44:58 -070096 // first time set listen next beacon
97 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_LNBCN);
98 pMgmt->wCountToWakeUp = wListenInterval;
Joe Perches5e0cc8a2013-03-18 20:55:37 -070099 } else {
Joe Perches474f0f82013-03-18 10:44:58 -0700100 // always listen beacon
101 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_ALBCN);
Joe Perches474f0f82013-03-18 10:44:58 -0700102 pMgmt->wCountToWakeUp = 0;
103 }
Forest Bond5449c682009-04-25 10:30:44 -0400104
Joe Perches474f0f82013-03-18 10:44:58 -0700105 // enable power saving hw function
106 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_PSEN);
107 pDevice->bEnablePSMode = true;
Forest Bond5449c682009-04-25 10:30:44 -0400108
Guido Martínez4e8a7e52014-04-19 16:44:59 -0300109 /* We don't send null pkt in ad hoc mode since beacon will handle this. */
Malcolm Priestleya9873672014-08-30 22:25:49 +0100110 if (pDevice->op_mode != NL80211_IFTYPE_ADHOC &&
111 pDevice->op_mode == NL80211_IFTYPE_STATION)
Joe Perches474f0f82013-03-18 10:44:58 -0700112 PSbSendNullPacket(pDevice);
Guido Martínez4e8a7e52014-04-19 16:44:59 -0300113
Joe Perches474f0f82013-03-18 10:44:58 -0700114 pDevice->bPWBitOn = true;
Joe Perches48caf5a2014-08-17 09:17:04 -0700115 pr_debug("PS:Power Saving Mode Enable...\n");
Forest Bond5449c682009-04-25 10:30:44 -0400116}
117
Forest Bond5449c682009-04-25 10:30:44 -0400118/*+
119 *
120 * Routine Description:
121 * Disable hw power saving functions
122 *
123 * Return Value:
124 * None.
125 *
Joe Perches474f0f82013-03-18 10:44:58 -0700126 -*/
Forest Bond5449c682009-04-25 10:30:44 -0400127
Charles Clément6b35b7b2010-05-07 12:30:19 -0700128void
Forest Bond5449c682009-04-25 10:30:44 -0400129PSvDisablePowerSaving(
Joe Perches474f0f82013-03-18 10:44:58 -0700130 void *hDeviceContext
131)
Forest Bond5449c682009-04-25 10:30:44 -0400132{
Malcolm Priestleycf76dc42014-08-10 15:46:59 +0100133 struct vnt_private *pDevice = hDeviceContext;
Forest Bond5449c682009-04-25 10:30:44 -0400134
Joe Perches474f0f82013-03-18 10:44:58 -0700135 // disable power saving hw function
136 MACbPSWakeup(pDevice->PortOffset);
137 //clear AutoSleep
138 MACvRegBitsOff(pDevice->PortOffset, MAC_REG_PSCFG, PSCFG_AUTOSLEEP);
139 //clear HWUTSF
140 MACvRegBitsOff(pDevice->PortOffset, MAC_REG_TFTCTL, TFTCTL_HWUTSF);
141 // set always listen beacon
142 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_ALBCN);
Forest Bond5449c682009-04-25 10:30:44 -0400143
Joe Perches474f0f82013-03-18 10:44:58 -0700144 pDevice->bEnablePSMode = false;
Forest Bond5449c682009-04-25 10:30:44 -0400145
Malcolm Priestleya9873672014-08-30 22:25:49 +0100146 if (pDevice->op_mode == NL80211_IFTYPE_STATION)
Joe Perches474f0f82013-03-18 10:44:58 -0700147 PSbSendNullPacket(pDevice);
Guido Martínezbc5cf652014-04-19 16:45:00 -0300148
Joe Perches474f0f82013-03-18 10:44:58 -0700149 pDevice->bPWBitOn = false;
Forest Bond5449c682009-04-25 10:30:44 -0400150}
151
Forest Bond5449c682009-04-25 10:30:44 -0400152/*+
153 *
154 * Routine Description:
155 * Consider to power down when no more packets to tx or rx.
156 *
157 * Return Value:
Charles Clément1b120682010-08-01 17:15:48 +0200158 * true, if power down success
Charles Clément5a5a2a62010-08-01 17:15:49 +0200159 * false, if fail
Joe Perches474f0f82013-03-18 10:44:58 -0700160 -*/
Forest Bond5449c682009-04-25 10:30:44 -0400161
Charles Clément7b6a0012010-08-01 17:15:50 +0200162bool
Forest Bond5449c682009-04-25 10:30:44 -0400163PSbConsiderPowerDown(
Joe Perches474f0f82013-03-18 10:44:58 -0700164 void *hDeviceContext,
165 bool bCheckRxDMA,
166 bool bCheckCountToWakeUp
167)
Forest Bond5449c682009-04-25 10:30:44 -0400168{
Malcolm Priestleycf76dc42014-08-10 15:46:59 +0100169 struct vnt_private *pDevice = hDeviceContext;
Joe Perches474f0f82013-03-18 10:44:58 -0700170 PSMgmtObject pMgmt = pDevice->pMgmt;
171 unsigned int uIdx;
Forest Bond5449c682009-04-25 10:30:44 -0400172
Joe Perches474f0f82013-03-18 10:44:58 -0700173 // check if already in Doze mode
174 if (MACbIsRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_PS))
175 return true;
Forest Bond5449c682009-04-25 10:30:44 -0400176
Joe Perches474f0f82013-03-18 10:44:58 -0700177 if (pMgmt->eCurrMode != WMAC_MODE_IBSS_STA) {
178 // check if in TIM wake period
179 if (pMgmt->bInTIMWake)
180 return false;
181 }
Forest Bond5449c682009-04-25 10:30:44 -0400182
Joe Perches474f0f82013-03-18 10:44:58 -0700183 // check scan state
184 if (pDevice->bCmdRunning)
185 return false;
Forest Bond5449c682009-04-25 10:30:44 -0400186
Joe Perches474f0f82013-03-18 10:44:58 -0700187 // Force PSEN on
188 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_PSEN);
Forest Bond5449c682009-04-25 10:30:44 -0400189
Joe Perches474f0f82013-03-18 10:44:58 -0700190 // check if all TD are empty,
191 for (uIdx = 0; uIdx < TYPE_MAXTD; uIdx++) {
192 if (pDevice->iTDUsed[uIdx] != 0)
193 return false;
194 }
Forest Bond5449c682009-04-25 10:30:44 -0400195
Joe Perches474f0f82013-03-18 10:44:58 -0700196 // check if rx isr is clear
197 if (bCheckRxDMA &&
198 ((pDevice->dwIsr & ISR_RXDMA0) != 0) &&
199 ((pDevice->dwIsr & ISR_RXDMA1) != 0)) {
200 return false;
201 }
Forest Bond5449c682009-04-25 10:30:44 -0400202
Joe Perches474f0f82013-03-18 10:44:58 -0700203 if (pMgmt->eCurrMode != WMAC_MODE_IBSS_STA) {
204 if (bCheckCountToWakeUp &&
205 (pMgmt->wCountToWakeUp == 0 || pMgmt->wCountToWakeUp == 1)) {
206 return false;
207 }
208 }
Forest Bond5449c682009-04-25 10:30:44 -0400209
Joe Perches474f0f82013-03-18 10:44:58 -0700210 // no Tx, no Rx isr, now go to Doze
211 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_GO2DOZE);
Joe Perches48caf5a2014-08-17 09:17:04 -0700212 pr_debug("Go to Doze ZZZZZZZZZZZZZZZ\n");
Joe Perches474f0f82013-03-18 10:44:58 -0700213 return true;
Forest Bond5449c682009-04-25 10:30:44 -0400214}
215
Forest Bond5449c682009-04-25 10:30:44 -0400216/*+
217 *
218 * Routine Description:
219 * Send PS-POLL packet
220 *
221 * Return Value:
222 * None.
223 *
Joe Perches474f0f82013-03-18 10:44:58 -0700224 -*/
Forest Bond5449c682009-04-25 10:30:44 -0400225
Charles Clément6b35b7b2010-05-07 12:30:19 -0700226void
Forest Bond5449c682009-04-25 10:30:44 -0400227PSvSendPSPOLL(
Joe Perches474f0f82013-03-18 10:44:58 -0700228 void *hDeviceContext
229)
Forest Bond5449c682009-04-25 10:30:44 -0400230{
Malcolm Priestleycf76dc42014-08-10 15:46:59 +0100231 struct vnt_private *pDevice = hDeviceContext;
Joe Perches474f0f82013-03-18 10:44:58 -0700232 PSMgmtObject pMgmt = pDevice->pMgmt;
233 PSTxMgmtPacket pTxPacket = NULL;
Forest Bond5449c682009-04-25 10:30:44 -0400234
Joe Perches474f0f82013-03-18 10:44:58 -0700235 memset(pMgmt->pbyPSPacketPool, 0, sizeof(STxMgmtPacket) + WLAN_HDR_ADDR2_LEN);
236 pTxPacket = (PSTxMgmtPacket)pMgmt->pbyPSPacketPool;
237 pTxPacket->p80211Header = (PUWLAN_80211HDR)((unsigned char *)pTxPacket + sizeof(STxMgmtPacket));
238 pTxPacket->p80211Header->sA2.wFrameCtl = cpu_to_le16(
239 (
240 WLAN_SET_FC_FTYPE(WLAN_TYPE_CTL) |
241 WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_PSPOLL) |
242 WLAN_SET_FC_PWRMGT(0)
243));
244 pTxPacket->p80211Header->sA2.wDurationID = pMgmt->wCurrAID | BIT14 | BIT15;
245 memcpy(pTxPacket->p80211Header->sA2.abyAddr1, pMgmt->abyCurrBSSID, WLAN_ADDR_LEN);
246 memcpy(pTxPacket->p80211Header->sA2.abyAddr2, pMgmt->abyMACAddr, WLAN_ADDR_LEN);
247 pTxPacket->cbMPDULen = WLAN_HDR_ADDR2_LEN;
248 pTxPacket->cbPayloadLen = 0;
249 // send the frame
Guillaume Clementcba60902014-07-25 01:06:25 +0200250 if (csMgmt_xmit(pDevice, pTxPacket) != CMD_STATUS_PENDING)
Joe Perches48caf5a2014-08-17 09:17:04 -0700251 pr_debug("Send PS-Poll packet failed..\n");
Forest Bond5449c682009-04-25 10:30:44 -0400252}
253
Forest Bond5449c682009-04-25 10:30:44 -0400254/*+
255 *
256 * Routine Description:
257 * Send NULL packet to AP for notification power state of STA
258 *
259 * Return Value:
260 * None.
261 *
Joe Perches474f0f82013-03-18 10:44:58 -0700262 -*/
Charles Clément7b6a0012010-08-01 17:15:50 +0200263bool
Forest Bond5449c682009-04-25 10:30:44 -0400264PSbSendNullPacket(
Joe Perches474f0f82013-03-18 10:44:58 -0700265 void *hDeviceContext
266)
Forest Bond5449c682009-04-25 10:30:44 -0400267{
Malcolm Priestleycf76dc42014-08-10 15:46:59 +0100268 struct vnt_private *pDevice = hDeviceContext;
Joe Perches474f0f82013-03-18 10:44:58 -0700269 PSTxMgmtPacket pTxPacket = NULL;
270 PSMgmtObject pMgmt = pDevice->pMgmt;
271 unsigned int uIdx;
Forest Bond5449c682009-04-25 10:30:44 -0400272
Guido Martínezbc5cf652014-04-19 16:45:00 -0300273 if (!pDevice->bLinkPass)
Joe Perches474f0f82013-03-18 10:44:58 -0700274 return false;
Guido Martínezbc5cf652014-04-19 16:45:00 -0300275
Guido Martínezbc5cf652014-04-19 16:45:00 -0300276 if (!pDevice->bEnablePSMode && !pDevice->fTxDataInSleep)
Joe Perches474f0f82013-03-18 10:44:58 -0700277 return false;
Malcolm Priestleyf2af99e2014-08-10 12:21:56 +0100278
Joe Perches474f0f82013-03-18 10:44:58 -0700279 if (pDevice->bEnablePSMode) {
280 for (uIdx = 0; uIdx < TYPE_MAXTD; uIdx++) {
281 if (pDevice->iTDUsed[uIdx] != 0)
282 return false;
283 }
284 }
Forest Bond5449c682009-04-25 10:30:44 -0400285
Joe Perches474f0f82013-03-18 10:44:58 -0700286 memset(pMgmt->pbyPSPacketPool, 0, sizeof(STxMgmtPacket) + WLAN_NULLDATA_FR_MAXLEN);
287 pTxPacket = (PSTxMgmtPacket)pMgmt->pbyPSPacketPool;
288 pTxPacket->p80211Header = (PUWLAN_80211HDR)((unsigned char *)pTxPacket + sizeof(STxMgmtPacket));
Forest Bond5449c682009-04-25 10:30:44 -0400289
Joe Perches474f0f82013-03-18 10:44:58 -0700290 if (pDevice->bEnablePSMode) {
Joe Perches474f0f82013-03-18 10:44:58 -0700291 pTxPacket->p80211Header->sA3.wFrameCtl = cpu_to_le16(
292 (
293 WLAN_SET_FC_FTYPE(WLAN_TYPE_DATA) |
294 WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_NULL) |
295 WLAN_SET_FC_PWRMGT(1)
296));
Joe Perches5e0cc8a2013-03-18 20:55:37 -0700297 } else {
Joe Perches474f0f82013-03-18 10:44:58 -0700298 pTxPacket->p80211Header->sA3.wFrameCtl = cpu_to_le16(
299 (
300 WLAN_SET_FC_FTYPE(WLAN_TYPE_DATA) |
301 WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_NULL) |
302 WLAN_SET_FC_PWRMGT(0)
303));
304 }
Forest Bond5449c682009-04-25 10:30:44 -0400305
Guido Martínezbc5cf652014-04-19 16:45:00 -0300306 if (pMgmt->eCurrMode != WMAC_MODE_IBSS_STA)
Joe Perches474f0f82013-03-18 10:44:58 -0700307 pTxPacket->p80211Header->sA3.wFrameCtl |= cpu_to_le16((unsigned short)WLAN_SET_FC_TODS(1));
Forest Bond5449c682009-04-25 10:30:44 -0400308
Joe Perches474f0f82013-03-18 10:44:58 -0700309 memcpy(pTxPacket->p80211Header->sA3.abyAddr1, pMgmt->abyCurrBSSID, WLAN_ADDR_LEN);
310 memcpy(pTxPacket->p80211Header->sA3.abyAddr2, pMgmt->abyMACAddr, WLAN_ADDR_LEN);
311 memcpy(pTxPacket->p80211Header->sA3.abyAddr3, pMgmt->abyCurrBSSID, WLAN_BSSID_LEN);
312 pTxPacket->cbMPDULen = WLAN_HDR_ADDR3_LEN;
313 pTxPacket->cbPayloadLen = 0;
314 // send the frame
315 if (csMgmt_xmit(pDevice, pTxPacket) != CMD_STATUS_PENDING) {
Joe Perches48caf5a2014-08-17 09:17:04 -0700316 pr_debug("Send Null Packet failed !\n");
Joe Perches474f0f82013-03-18 10:44:58 -0700317 return false;
Joe Perches474f0f82013-03-18 10:44:58 -0700318 }
Forest Bond5449c682009-04-25 10:30:44 -0400319
Joe Perches474f0f82013-03-18 10:44:58 -0700320 return true;
Forest Bond5449c682009-04-25 10:30:44 -0400321}
322
323/*+
324 *
325 * Routine Description:
326 * Check if Next TBTT must wake up
327 *
328 * Return Value:
329 * None.
330 *
Joe Perches474f0f82013-03-18 10:44:58 -0700331 -*/
Forest Bond5449c682009-04-25 10:30:44 -0400332
Charles Clément7b6a0012010-08-01 17:15:50 +0200333bool
Forest Bond5449c682009-04-25 10:30:44 -0400334PSbIsNextTBTTWakeUp(
Joe Perches474f0f82013-03-18 10:44:58 -0700335 void *hDeviceContext
336)
Forest Bond5449c682009-04-25 10:30:44 -0400337{
Malcolm Priestleycf76dc42014-08-10 15:46:59 +0100338 struct vnt_private *pDevice = hDeviceContext;
Joe Perches474f0f82013-03-18 10:44:58 -0700339 PSMgmtObject pMgmt = pDevice->pMgmt;
340 bool bWakeUp = false;
Forest Bond5449c682009-04-25 10:30:44 -0400341
Joe Perches474f0f82013-03-18 10:44:58 -0700342 if (pMgmt->wListenInterval >= 2) {
Guido Martínezbc5cf652014-04-19 16:45:00 -0300343 if (pMgmt->wCountToWakeUp == 0)
Joe Perches474f0f82013-03-18 10:44:58 -0700344 pMgmt->wCountToWakeUp = pMgmt->wListenInterval;
Forest Bond5449c682009-04-25 10:30:44 -0400345
Joe Perches474f0f82013-03-18 10:44:58 -0700346 pMgmt->wCountToWakeUp--;
Forest Bond5449c682009-04-25 10:30:44 -0400347
Joe Perches474f0f82013-03-18 10:44:58 -0700348 if (pMgmt->wCountToWakeUp == 1) {
349 // Turn on wake up to listen next beacon
350 MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_LNBCN);
351 bWakeUp = true;
352 }
Forest Bond5449c682009-04-25 10:30:44 -0400353
Joe Perches474f0f82013-03-18 10:44:58 -0700354 }
Forest Bond5449c682009-04-25 10:30:44 -0400355
Joe Perches474f0f82013-03-18 10:44:58 -0700356 return bWakeUp;
Forest Bond5449c682009-04-25 10:30:44 -0400357}