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  --------------------------*/
57 static 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 
77 void
PSvEnablePowerSaving(void * hDeviceContext,unsigned short wListenInterval)78 PSvEnablePowerSaving(
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 
147 void
PSvDisablePowerSaving(void * hDeviceContext)148 PSvDisablePowerSaving(
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 
185 bool
PSbConsiderPowerDown(void * hDeviceContext,bool bCheckRxDMA,bool bCheckCountToWakeUp)186 PSbConsiderPowerDown(
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 
253 void
PSvSendPSPOLL(void * hDeviceContext)254 PSvSendPSPOLL(
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 -*/
299 bool
PSbSendNullPacket(void * hDeviceContext)300 PSbSendNullPacket(
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 
385 bool
PSbIsNextTBTTWakeUp(void * hDeviceContext)386 PSbIsNextTBTTWakeUp(
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