xref: /openbmc/linux/drivers/staging/vt6656/power.c (revision 3b64b188)
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 #include "control.h"
49 #include "rndis.h"
50 
51 /*---------------------  Static Definitions -------------------------*/
52 
53 /*---------------------  Static Classes  ----------------------------*/
54 
55 /*---------------------  Static Variables  --------------------------*/
56 static int msglevel = MSG_LEVEL_INFO;
57 /*---------------------  Static Functions  --------------------------*/
58 
59 /*---------------------  Export Variables  --------------------------*/
60 
61 /*---------------------  Export Functions  --------------------------*/
62 
63 /*
64  *
65  * Routine Description:
66  * Enable hw power saving functions
67  *
68  * Return Value:
69  *    None.
70  *
71  */
72 
73 void PSvEnablePowerSaving(void *hDeviceContext,
74 			  WORD wListenInterval)
75 {
76 	PSDevice pDevice = (PSDevice)hDeviceContext;
77 	PSMgmtObject pMgmt = &(pDevice->sMgmtObj);
78 	WORD wAID = pMgmt->wCurrAID | BIT14 | BIT15;
79 
80 	/* set period of power up before TBTT */
81 	MACvWriteWord(pDevice, MAC_REG_PWBT, C_PWBT);
82 
83 	if (pDevice->eOPMode != OP_MODE_ADHOC) {
84 		/* set AID */
85 		MACvWriteWord(pDevice, MAC_REG_AIDATIM, wAID);
86 	} else {
87 		/* set ATIM Window */
88 		/* MACvWriteATIMW(pDevice->PortOffset, pMgmt->wCurrATIMWindow); */
89 	}
90 
91 	/* Warren:06-18-2004,the sequence must follow PSEN->AUTOSLEEP->GO2DOZE */
92 	/* enable power saving hw function */
93 	MACvRegBitsOn(pDevice, MAC_REG_PSCTL, PSCTL_PSEN);
94 
95 	/* Set AutoSleep */
96 	MACvRegBitsOn(pDevice, MAC_REG_PSCFG, PSCFG_AUTOSLEEP);
97 
98 	/* Warren:MUST turn on this once before turn on AUTOSLEEP ,or the AUTOSLEEP doesn't work */
99 	MACvRegBitsOn(pDevice, MAC_REG_PSCTL, PSCTL_GO2DOZE);
100 
101 	if (wListenInterval >= 2) {
102 
103 		/* clear always listen beacon */
104 		MACvRegBitsOff(pDevice, MAC_REG_PSCTL, PSCTL_ALBCN);
105 
106 		/* first time set listen next beacon */
107 		MACvRegBitsOn(pDevice, MAC_REG_PSCTL, PSCTL_LNBCN);
108 
109 		pMgmt->wCountToWakeUp = wListenInterval;
110 
111 	} else {
112 
113 		/* always listen beacon */
114 		MACvRegBitsOn(pDevice, MAC_REG_PSCTL, PSCTL_ALBCN);
115 
116 		pMgmt->wCountToWakeUp = 0;
117 	}
118 
119 	pDevice->bEnablePSMode = TRUE;
120 
121 	/* We don't send null pkt in ad hoc mode since beacon will handle this. */
122 	if (pDevice->eOPMode == OP_MODE_INFRASTRUCTURE)
123 		PSbSendNullPacket(pDevice);
124 
125 	pDevice->bPWBitOn = TRUE;
126 	DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "PS:Power Saving Mode Enable...\n");
127 }
128 
129 /*
130  *
131  * Routine Description:
132  * Disable hw power saving functions
133  *
134  * Return Value:
135  *    None.
136  *
137  */
138 
139 void PSvDisablePowerSaving(void *hDeviceContext)
140 {
141 	PSDevice pDevice = (PSDevice)hDeviceContext;
142 	/* PSMgmtObject pMgmt = &(pDevice->sMgmtObj); */
143 
144 	/* disable power saving hw function */
145 	CONTROLnsRequestOut(pDevice, MESSAGE_TYPE_DISABLE_PS, 0,
146 						0, 0, NULL);
147 
148 	/* clear AutoSleep */
149 	MACvRegBitsOff(pDevice, MAC_REG_PSCFG, PSCFG_AUTOSLEEP);
150 
151 	/* set always listen beacon */
152 	MACvRegBitsOn(pDevice, MAC_REG_PSCTL, PSCTL_ALBCN);
153 	pDevice->bEnablePSMode = FALSE;
154 
155 	if (pDevice->eOPMode == OP_MODE_INFRASTRUCTURE)
156 		PSbSendNullPacket(pDevice);
157 
158 	pDevice->bPWBitOn = FALSE;
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 PSbConsiderPowerDown(void *hDeviceContext,
172 			  BOOL bCheckRxDMA,
173 			  BOOL bCheckCountToWakeUp)
174 {
175 	PSDevice pDevice = (PSDevice)hDeviceContext;
176 	PSMgmtObject pMgmt = &(pDevice->sMgmtObj);
177 	BYTE byData;
178 
179 	/* check if already in Doze mode */
180 	ControlvReadByte(pDevice, MESSAGE_REQUEST_MACREG,
181 					MAC_REG_PSCTL, &byData);
182 
183 	if ((byData & PSCTL_PS) != 0)
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 	/* Tx Burst */
197 	if (pDevice->bPSModeTxBurst)
198 		return FALSE;
199 
200 	/* Froce PSEN on */
201 	MACvRegBitsOn(pDevice, MAC_REG_PSCTL, PSCTL_PSEN);
202 
203 	if (pMgmt->eCurrMode != WMAC_MODE_IBSS_STA) {
204 		if (bCheckCountToWakeUp && (pMgmt->wCountToWakeUp == 0
205 			|| pMgmt->wCountToWakeUp == 1)) {
206 				return FALSE;
207 		}
208 	}
209 
210 	pDevice->bPSRxBeacon = TRUE;
211 
212 	/* no Tx, no Rx isr, now go to Doze */
213 	MACvRegBitsOn(pDevice, MAC_REG_PSCTL, PSCTL_GO2DOZE);
214 	DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Go to Doze ZZZZZZZZZZZZZZZ\n");
215 	return TRUE;
216 }
217 
218 /*
219  *
220  * Routine Description:
221  * Send PS-POLL packet
222  *
223  * Return Value:
224  *    None.
225  *
226  */
227 
228 void PSvSendPSPOLL(void *hDeviceContext)
229 {
230 	PSDevice pDevice = (PSDevice)hDeviceContext;
231 	PSMgmtObject pMgmt = &(pDevice->sMgmtObj);
232 	PSTxMgmtPacket pTxPacket = NULL;
233 
234 	memset(pMgmt->pbyPSPacketPool, 0, sizeof(STxMgmtPacket) + WLAN_HDR_ADDR2_LEN);
235 	pTxPacket = (PSTxMgmtPacket)pMgmt->pbyPSPacketPool;
236 	pTxPacket->p80211Header = (PUWLAN_80211HDR)((PBYTE)pTxPacket + sizeof(STxMgmtPacket));
237 	pTxPacket->p80211Header->sA2.wFrameCtl = cpu_to_le16(
238 		(
239 			WLAN_SET_FC_FTYPE(WLAN_TYPE_CTL) |
240 			WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_PSPOLL) |
241 			WLAN_SET_FC_PWRMGT(0)
242 		));
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 
250 	/* log failure if sending failed */
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  *
258  * Routine Description:
259  * Send NULL packet to AP for notification power state of STA
260  *
261  * Return Value:
262  *    None.
263  *
264  */
265 
266 BOOL PSbSendNullPacket(void *hDeviceContext)
267 {
268 	PSDevice pDevice = (PSDevice)hDeviceContext;
269 	PSTxMgmtPacket pTxPacket = NULL;
270 	PSMgmtObject pMgmt = &(pDevice->sMgmtObj);
271 	u16 flags = 0;
272 
273 	if (pDevice->bLinkPass == FALSE)
274 		return FALSE;
275 
276 	if ((pDevice->bEnablePSMode == FALSE) &&
277 		(pDevice->fTxDataInSleep == FALSE)) {
278 			return FALSE;
279 	}
280 
281 	memset(pMgmt->pbyPSPacketPool, 0, sizeof(STxMgmtPacket) + WLAN_NULLDATA_FR_MAXLEN);
282 	pTxPacket = (PSTxMgmtPacket)pMgmt->pbyPSPacketPool;
283 	pTxPacket->p80211Header = (PUWLAN_80211HDR)((PBYTE)pTxPacket + sizeof(STxMgmtPacket));
284 
285 	flags = WLAN_SET_FC_FTYPE(WLAN_TYPE_DATA) |
286                         WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_NULL);
287 
288 	if (pDevice->bEnablePSMode)
289 		flags |= WLAN_SET_FC_PWRMGT(1);
290 	else
291 		flags |= WLAN_SET_FC_PWRMGT(0);
292 
293 	pTxPacket->p80211Header->sA3.wFrameCtl = cpu_to_le16(flags);
294 
295 	if (pMgmt->eCurrMode != WMAC_MODE_IBSS_STA)
296 		pTxPacket->p80211Header->sA3.wFrameCtl |= cpu_to_le16((WORD)WLAN_SET_FC_TODS(1));
297 
298 	memcpy(pTxPacket->p80211Header->sA3.abyAddr1, pMgmt->abyCurrBSSID, WLAN_ADDR_LEN);
299 	memcpy(pTxPacket->p80211Header->sA3.abyAddr2, pMgmt->abyMACAddr, WLAN_ADDR_LEN);
300 	memcpy(pTxPacket->p80211Header->sA3.abyAddr3, pMgmt->abyCurrBSSID, WLAN_BSSID_LEN);
301 	pTxPacket->cbMPDULen = WLAN_HDR_ADDR3_LEN;
302 	pTxPacket->cbPayloadLen = 0;
303 	/* log error if sending failed */
304 	if (csMgmt_xmit(pDevice, pTxPacket) != CMD_STATUS_PENDING) {
305 		DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Send Null Packet failed !\n");
306 		return FALSE;
307 	}
308 	return TRUE;
309 }
310 
311 /*
312  *
313  * Routine Description:
314  * Check if Next TBTT must wake up
315  *
316  * Return Value:
317  *    None.
318  *
319  */
320 
321 BOOL PSbIsNextTBTTWakeUp(void *hDeviceContext)
322 {
323 	PSDevice pDevice = (PSDevice)hDeviceContext;
324 	PSMgmtObject pMgmt = &(pDevice->sMgmtObj);
325 	BOOL bWakeUp = FALSE;
326 
327 	if (pMgmt->wListenInterval >= 2) {
328 		if (pMgmt->wCountToWakeUp == 0)
329 			pMgmt->wCountToWakeUp = pMgmt->wListenInterval;
330 
331 		pMgmt->wCountToWakeUp--;
332 
333 		if (pMgmt->wCountToWakeUp == 1) {
334 			/* Turn on wake up to listen next beacon */
335 			MACvRegBitsOn(pDevice, MAC_REG_PSCTL, PSCTL_LNBCN);
336 			pDevice->bPSRxBeacon = FALSE;
337 			bWakeUp = TRUE;
338 		} else if (!pDevice->bPSRxBeacon) {
339 			/* Listen until RxBeacon */
340 			MACvRegBitsOn(pDevice, MAC_REG_PSCTL, PSCTL_LNBCN);
341 		}
342 	}
343 	return bWakeUp;
344 }
345 
346