10c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger/******************************************************************************
20c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *
3a8d760668eebc98915383481cb3d9eaf74c2a615Larry Finger * Copyright(c) 2009-2012  Realtek Corporation.
40c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *
50c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * This program is free software; you can redistribute it and/or modify it
60c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * under the terms of version 2 of the GNU General Public License as
70c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * published by the Free Software Foundation.
80c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *
90c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * This program is distributed in the hope that it will be useful, but WITHOUT
100c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
110c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
120c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * more details.
130c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *
140c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * The full GNU General Public License is included in this distribution in the
150c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * file called LICENSE.
160c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *
170c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * Contact Information:
180c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * wlanfae <wlanfae@realtek.com>
190c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
200c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * Hsinchu 300, Taiwan.
210c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *
220c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * Larry Finger <Larry.Finger@lwfinger.net>
230c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *
240c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *****************************************************************************/
250c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
260c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger#include "wifi.h"
270c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger#include "base.h"
280c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger#include "ps.h"
29d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger#include <linux/export.h>
30d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger#include "btcoexist/rtl_btc.h"
3125b13dbc38a74b76da5746d75867e306b70035bdLarry Finger
320c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingerbool rtl_ps_enable_nic(struct ieee80211_hw *hw)
330c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
340c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
350c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
360c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
370c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
380c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<1> reset trx ring */
390c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (rtlhal->interface == INTF_PCI)
400c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		rtlpriv->intf_ops->reset_trx_ring(hw);
410c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
420c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (is_hal_stop(rtlhal))
430c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
44f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches			 "Driver is already down!\n");
450c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
460c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<2> Enable Adapter */
47b0302aba812bcc39291cdab9ad7e37008f352a91Larry Finger	if (rtlpriv->cfg->ops->hw_init(hw))
482e8c5e56b307271c2dab6f8bfd1d8a3822ca2390Olivier Langlois		return false;
490c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC);
500c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
510c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<3> Enable Interrupt */
520c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	rtlpriv->cfg->ops->enable_interrupt(hw);
530c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
540c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<enable timer> */
550c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	rtl_watch_dog_timer_callback((unsigned long)hw);
560c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
57cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	return true;
580c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
590c8173385e549f95cd80c3fff5aab87b4f881d8dLarry FingerEXPORT_SYMBOL(rtl_ps_enable_nic);
600c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
610c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingerbool rtl_ps_disable_nic(struct ieee80211_hw *hw)
620c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
630c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
640c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
650c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<1> Stop all timer */
660c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	rtl_deinit_deferred_work(hw);
670c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
680c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<2> Disable Interrupt */
690c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	rtlpriv->cfg->ops->disable_interrupt(hw);
7067fc6052a49b781efbcfc138f3b68fe79ddd0c2fMike McCormack	tasklet_kill(&rtlpriv->works.irq_tasklet);
710c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
720c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<3> Disable Adapter */
730c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	rtlpriv->cfg->ops->hw_disable(hw);
740c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
75324732848c42bf79988479ee1b4359e15f08154bLarry Finger	return true;
760c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
770c8173385e549f95cd80c3fff5aab87b4f881d8dLarry FingerEXPORT_SYMBOL(rtl_ps_disable_nic);
780c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
790c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingerbool rtl_ps_set_rf_state(struct ieee80211_hw *hw,
800c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			 enum rf_pwrstate state_toset,
81d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger			 u32 changesource, bool protect_or_not)
820c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
830c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
840c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
85d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	enum rf_pwrstate rtstate;
867ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	bool actionallowed = false;
87d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	u16 rfwait_cnt = 0;
88d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger
89d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	if (protect_or_not)
90d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		goto no_protect;
91d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger
92d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	/*Only one thread can change
93d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	 *the RF state at one time, and others
94d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	 *should wait to be executed.
95d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	 */
96d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	while (true) {
97d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		spin_lock(&rtlpriv->locks.rf_ps_lock);
98d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		if (ppsc->rfchange_inprogress) {
99d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger			spin_unlock(&rtlpriv->locks.rf_ps_lock);
100d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger
101d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger			RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
102d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger				 "RF Change in progress! Wait to set..state_toset(%d).\n",
103d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger				  state_toset);
104d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger
105d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger			/* Set RF after the previous action is done.  */
106d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger			while (ppsc->rfchange_inprogress) {
107d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger				rfwait_cnt++;
108d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger				mdelay(1);
109d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger				/*Wait too long, return false to avoid
110d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger				 *to be stuck here.
111d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger				 */
112d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger				if (rfwait_cnt > 100)
113d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger					return false;
114d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger			}
115d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		} else {
116d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger			ppsc->rfchange_inprogress = true;
117d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger			spin_unlock(&rtlpriv->locks.rf_ps_lock);
118d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger			break;
119d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		}
120d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	}
121d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger
122d3feae41a3473a0f7b431d6af4e092865d586e52Larry Fingerno_protect:
123d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	rtstate = ppsc->rfpwr_state;
1240c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1250c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	switch (state_toset) {
1260c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	case ERFON:
1270c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		ppsc->rfoff_reason &= (~changesource);
1280c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1290c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if ((changesource == RF_CHANGE_BY_HW) &&
130e10542c447abf7c840931ff12f7d0dee976ca2eaMike McCormack		    (ppsc->hwradiooff)) {
1317ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger			ppsc->hwradiooff = false;
1320c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
1330c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1340c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if (!ppsc->rfoff_reason) {
1350c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			ppsc->rfoff_reason = 0;
1367ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger			actionallowed = true;
1370c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
1380c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1390c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		break;
1400c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1410c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	case ERFOFF:
1420c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
14323677ce3172fcb93522a1df077d21019e73ee1e3Joe Perches		if ((changesource == RF_CHANGE_BY_HW) && !ppsc->hwradiooff) {
1447ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger			ppsc->hwradiooff = true;
1450c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
1460c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1470c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		ppsc->rfoff_reason |= changesource;
1487ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger		actionallowed = true;
1490c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		break;
1500c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1510c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	case ERFSLEEP:
1520c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		ppsc->rfoff_reason |= changesource;
1537ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger		actionallowed = true;
1540c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		break;
1550c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1560c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	default:
1570c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
158f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches			 "switch case not processed\n");
1590c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		break;
1600c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
1610c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1627ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	if (actionallowed)
1630c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		rtlpriv->cfg->ops->set_rf_power_state(hw, state_toset);
1640c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
165d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	if (!protect_or_not) {
166d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		spin_lock(&rtlpriv->locks.rf_ps_lock);
167d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		ppsc->rfchange_inprogress = false;
168d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		spin_unlock(&rtlpriv->locks.rf_ps_lock);
169d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	}
170d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger
1717ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	return actionallowed;
1720c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
1730c8173385e549f95cd80c3fff5aab87b4f881d8dLarry FingerEXPORT_SYMBOL(rtl_ps_set_rf_state);
1740c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1750c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingerstatic void _rtl_ps_inactive_ps(struct ieee80211_hw *hw)
1760c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
1770c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
1780c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
1790c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
1800c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1817ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	ppsc->swrf_processing = true;
1820c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
183099fb8ab1e57e5d609ac686cc0ab6d1835a79155Larry Finger	if (ppsc->inactive_pwrstate == ERFON &&
184cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	    rtlhal->interface == INTF_PCI) {
1850c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if ((ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM) &&
186cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		    RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) &&
1870c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		    rtlhal->interface == INTF_PCI) {
1880c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			rtlpriv->intf_ops->disable_aspm(hw);
189cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
1900c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
1910c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
1920c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
193d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	rtl_ps_set_rf_state(hw, ppsc->inactive_pwrstate,
194d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger			    RF_CHANGE_BY_IPS, false);
1950c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1960c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (ppsc->inactive_pwrstate == ERFOFF &&
1970c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	    rtlhal->interface == INTF_PCI) {
198cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM &&
199d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		    !RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
2000c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			rtlpriv->intf_ops->enable_aspm(hw);
201cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
2020c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
2030c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
2040c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2057ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	ppsc->swrf_processing = false;
2060c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
2070c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2080c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingervoid rtl_ips_nic_off_wq_callback(void *data)
2090c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
2100c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_works *rtlworks =
2110c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	    container_of_dwork_rtl(data, struct rtl_works, ips_nic_off_wq);
2120c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct ieee80211_hw *hw = rtlworks->hw;
2130c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
2140c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
2150c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
2160c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
2170c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	enum rf_pwrstate rtstate;
2180c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2190c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->opmode != NL80211_IFTYPE_STATION) {
2200c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
221f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches			 "not station return\n");
2220c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
2230c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
2240c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
22526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (mac->p2p_in_use)
22626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		return;
22726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
228cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (mac->link_state > MAC80211_NOLINK)
229cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
230cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
2310c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (is_hal_stop(rtlhal))
2320c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
2330c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2340c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (rtlpriv->sec.being_setkey)
2350c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
2360c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
23726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (rtlpriv->cfg->ops->bt_coex_off_before_lps)
23826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		rtlpriv->cfg->ops->bt_coex_off_before_lps(hw);
23926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
2407ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	if (ppsc->inactiveps) {
2410c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		rtstate = ppsc->rfpwr_state;
2420c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2430c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		/*
2440c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 *Do not enter IPS in the following conditions:
2450c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 *(1) RF is already OFF or Sleep
2467ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger		 *(2) swrf_processing (indicates the IPS is still under going)
2470c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 *(3) Connectted (only disconnected can trigger IPS)
2480c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 *(4) IBSS (send Beacon)
2490c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 *(5) AP mode (send Beacon)
2500c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 *(6) monitor mode (rcv packet)
2510c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 */
2520c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2530c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if (rtstate == ERFON &&
2547ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger		    !ppsc->swrf_processing &&
2550c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		    (mac->link_state == MAC80211_NOLINK) &&
2560c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		    !mac->act_scanning) {
2570c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
258f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches				 "IPSEnter(): Turn off RF\n");
2590c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2600c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			ppsc->inactive_pwrstate = ERFOFF;
2617ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger			ppsc->in_powersavemode = true;
2620c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
263d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger			/* call before RF off */
264d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger			if (rtlpriv->cfg->ops->get_btc_status())
265d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger				rtlpriv->btcoexist.btc_ops->btc_ips_notify(rtlpriv,
266d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger									ppsc->inactive_pwrstate);
267d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger
2680c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			/*rtl_pci_reset_trx_ring(hw); */
2690c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			_rtl_ps_inactive_ps(hw);
2700c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
2710c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
2720c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
2730c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2740c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingervoid rtl_ips_nic_off(struct ieee80211_hw *hw)
2750c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
2760c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
2770c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
278d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	/* because when link with ap, mac80211 will ask us
279d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	 * to disable nic quickly after scan before linking,
280d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	 * this will cause link failed, so we delay 100ms here
2810c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 */
2820c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	queue_delayed_work(rtlpriv->works.rtl_wq,
2830c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			   &rtlpriv->works.ips_nic_off_wq, MSECS(100));
2840c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
2850c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
28626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger/* NOTICE: any opmode should exc nic_on, or disable without
28726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger * nic_on may something wrong, like adhoc TP
28826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger */
2890c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingervoid rtl_ips_nic_on(struct ieee80211_hw *hw)
2900c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
2910c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
2920c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
2930c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	enum rf_pwrstate rtstate;
294cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
295d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	cancel_delayed_work(&rtlpriv->works.ips_nic_off_wq);
2960c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
297d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	spin_lock(&rtlpriv->locks.ips_lock);
2987ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	if (ppsc->inactiveps) {
2990c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		rtstate = ppsc->rfpwr_state;
3000c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3010c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if (rtstate != ERFON &&
3027ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger		    !ppsc->swrf_processing &&
3030c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		    ppsc->rfoff_reason <= RF_CHANGE_BY_IPS) {
3040c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3050c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			ppsc->inactive_pwrstate = ERFON;
3067ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger			ppsc->in_powersavemode = false;
3070c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			_rtl_ps_inactive_ps(hw);
308d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger			/* call after RF on */
309d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger			if (rtlpriv->cfg->ops->get_btc_status())
310d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger				rtlpriv->btcoexist.btc_ops->btc_ips_notify(rtlpriv,
311d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger									ppsc->inactive_pwrstate);
3120c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
3130c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
314d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	spin_unlock(&rtlpriv->locks.ips_lock);
3150c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
3166f334c2b3966f10cbd089bb124ec0e114d8d8c77Larry FingerEXPORT_SYMBOL_GPL(rtl_ips_nic_on);
3170c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3180c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger/*for FW LPS*/
3190c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3200c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger/*
3210c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *Determine if we can set Fw into PS mode
3220c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *in current condition.Return TRUE if it
3230c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *can enter PS mode.
3240c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger */
3250c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingerstatic bool rtl_get_fwlps_doze(struct ieee80211_hw *hw)
3260c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
3270c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
3280c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
3290c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
3300c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	u32 ps_timediff;
3310c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3320c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	ps_timediff = jiffies_to_msecs(jiffies -
3330c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				       ppsc->last_delaylps_stamp_jiffies);
3340c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3350c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (ps_timediff < 2000) {
3360c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
337f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches			 "Delay enter Fw LPS for DHCP, ARP, or EAPOL exchanging state\n");
3380c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return false;
3390c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
3400c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3410c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->link_state != MAC80211_LINKED)
3420c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return false;
3430c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3440c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->opmode == NL80211_IFTYPE_ADHOC)
3450c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return false;
3460c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3470c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	return true;
3480c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
3490c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3500c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger/* Change current and default preamble mode.*/
351d3feae41a3473a0f7b431d6af4e092865d586e52Larry Fingervoid rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode)
3520c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
3530c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
3540c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
3550c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
35626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	bool enter_fwlps;
3570c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3580c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->opmode == NL80211_IFTYPE_ADHOC)
3590c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
3600c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3610c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->link_state != MAC80211_LINKED)
3620c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
3630c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3640c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (ppsc->dot11_psmode == rt_psmode)
3650c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
3660c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3670c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/* Update power save mode configured. */
3680c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	ppsc->dot11_psmode = rt_psmode;
3690c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3700c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*
3710c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *<FW control LPS>
3720c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *1. Enter PS mode
3730c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *   Set RPWM to Fw to turn RF off and send H2C fw_pwrmode
3740c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *   cmd to set Fw into PS mode.
3750c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *2. Leave PS mode
3760c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *   Send H2C fw_pwrmode cmd to Fw to set Fw into Active
3770c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *   mode and set RPWM to turn RF on.
3780c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 */
3790c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
380cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if ((ppsc->fwctrl_lps) && ppsc->report_linked) {
3810c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if (ppsc->dot11_psmode == EACTIVE) {
3820c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
383f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches				 "FW LPS leave ps_mode:%x\n",
384d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger				  FW_PS_ACTIVE_MODE);
38526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			enter_fwlps = false;
38626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			ppsc->pwr_mode = FW_PS_ACTIVE_MODE;
38726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			ppsc->smart_ps = 0;
388d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger			rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_LPS_ACTION,
389d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger						      (u8 *)(&enter_fwlps));
39026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			if (ppsc->p2p_ps_info.opp_ps)
391d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger				rtl_p2p_ps_cmd(hw , P2P_PS_ENABLE);
3920c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
393d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger			if (rtlpriv->cfg->ops->get_btc_status())
394d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger				rtlpriv->btcoexist.btc_ops->btc_lps_notify(rtlpriv, rt_psmode);
3950c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		} else {
3960c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			if (rtl_get_fwlps_doze(hw)) {
3970c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
398f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches					 "FW LPS enter ps_mode:%x\n",
399f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches					 ppsc->fwctrl_psmode);
400d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger				if (rtlpriv->cfg->ops->get_btc_status())
401d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger					rtlpriv->btcoexist.btc_ops->btc_lps_notify(rtlpriv, rt_psmode);
40226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				enter_fwlps = true;
40326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				ppsc->pwr_mode = ppsc->fwctrl_psmode;
40426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				ppsc->smart_ps = 2;
4050c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				rtlpriv->cfg->ops->set_hw_reg(hw,
40626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger							HW_VAR_FW_LPS_ACTION,
40726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger							(u8 *)(&enter_fwlps));
4080c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4090c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			} else {
4100c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				/* Reset the power save related parameters. */
4110c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				ppsc->dot11_psmode = EACTIVE;
4120c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			}
4130c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
4140c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
4150c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
4160c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4170c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger/*Enter the leisure power save mode.*/
4180c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingervoid rtl_lps_enter(struct ieee80211_hw *hw)
4190c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
4200c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
4210c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
4220c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
423d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	unsigned long flag;
4240c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
425cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!ppsc->fwctrl_lps)
4260c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
4270c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4280c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (rtlpriv->sec.being_setkey)
4290c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
4300c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4317ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	if (rtlpriv->link_info.busytraffic)
4320c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
4330c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4340c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */
4350c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->cnt_after_linked < 5)
4360c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
4370c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4380c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->opmode == NL80211_IFTYPE_ADHOC)
4390c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
4400c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4410c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->link_state != MAC80211_LINKED)
4420c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
4430c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
444d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
4450c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
446cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* Idle for a while if we connect to AP a while ago. */
447cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (mac->cnt_after_linked >= 2) {
448cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		if (ppsc->dot11_psmode == EACTIVE) {
449cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
450f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches				 "Enter 802.11 power save mode...\n");
4510c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
452cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			rtl_lps_set_psmode(hw, EAUTOPS);
4530c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
4540c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
455cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
456d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
4570c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
458d3feae41a3473a0f7b431d6af4e092865d586e52Larry FingerEXPORT_SYMBOL(rtl_lps_enter);
4590c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4600c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger/*Leave the leisure power save mode.*/
4610c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingervoid rtl_lps_leave(struct ieee80211_hw *hw)
4620c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
4630c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
4640c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
4650c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
466d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	unsigned long flag;
4670c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
468d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
4690c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
470cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (ppsc->fwctrl_lps) {
4710c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if (ppsc->dot11_psmode != EACTIVE) {
4720c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4730c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			/*FIX ME */
474d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger			/*rtlpriv->cfg->ops->enable_interrupt(hw); */
4750c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4760c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM &&
477cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			    RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) &&
4780c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			    rtlhal->interface == INTF_PCI) {
4790c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				rtlpriv->intf_ops->disable_aspm(hw);
480cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li				RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
4810c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			}
4820c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4830c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
484f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches				 "Busy Traffic,Leave 802.11 power save..\n");
4850c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4860c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			rtl_lps_set_psmode(hw, EACTIVE);
4870c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
4880c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
489d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
4900c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
491d3feae41a3473a0f7b431d6af4e092865d586e52Larry FingerEXPORT_SYMBOL(rtl_lps_leave);
492cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
493cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li/* For sw LPS*/
494cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Livoid rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len)
495cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li{
496cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_priv *rtlpriv = rtl_priv(hw);
497cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
4982c208890c6d4e16076c6664137703ec813e8fa6cJoe Perches	struct ieee80211_hdr *hdr = data;
499cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct ieee80211_tim_ie *tim_ie;
500cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	u8 *tim;
501cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	u8 tim_len;
502cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	bool u_buffed;
503cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	bool m_buffed;
504cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
505cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (mac->opmode != NL80211_IFTYPE_STATION)
506cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
507cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
508cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!rtlpriv->psc.swctrl_lps)
509cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
510cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
511cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (rtlpriv->mac80211.link_state != MAC80211_LINKED)
512cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
513cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
514cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!rtlpriv->psc.sw_ps_enabled)
515cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
516cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
517cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (rtlpriv->psc.fwctrl_lps)
518cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
519cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
520cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (likely(!(hw->conf.flags & IEEE80211_CONF_PS)))
521cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
522cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
523cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* check if this really is a beacon */
524cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!ieee80211_is_beacon(hdr->frame_control))
525cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
526cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
527cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* min. beacon length + FCS_LEN */
528cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (len <= 40 + FCS_LEN)
529cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
530cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
531cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* and only beacons from the associated BSSID, please */
53290908e1cd14716f1b989b97003007bfcc7be3d13Julia Lawall	if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid))
533cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
534cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
535cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	rtlpriv->psc.last_beacon = jiffies;
536cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
537cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	tim = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_TIM);
538cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!tim)
539cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
540cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
541cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (tim[1] < sizeof(*tim_ie))
542cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
543cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
544cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	tim_len = tim[1];
545cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	tim_ie = (struct ieee80211_tim_ie *) &tim[2];
546cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
547cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!WARN_ON_ONCE(!hw->conf.ps_dtim_period))
548cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		rtlpriv->psc.dtim_counter = tim_ie->dtim_count;
549cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
550cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* Check whenever the PHY can be turned off again. */
551cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
552cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* 1. What about buffered unicast traffic for our AID? */
553cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	u_buffed = ieee80211_check_tim(tim_ie, tim_len,
554cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li				       rtlpriv->mac80211.assoc_id);
555cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
556cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* 2. Maybe the AP wants to send multicast/broadcast data? */
557cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	m_buffed = tim_ie->bitmap_ctrl & 0x01;
558cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	rtlpriv->psc.multi_buffered = m_buffed;
559cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
560cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* unicast will process by mac80211 through
561cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * set ~IEEE80211_CONF_PS, So we just check
562cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * multicast frames here */
563cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!m_buffed) {
564cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		/* back to low-power land. and delay is
565cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		 * prevent null power save frame tx fail */
566cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		queue_delayed_work(rtlpriv->works.rtl_wq,
567d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger				   &rtlpriv->works.ps_work, MSECS(5));
568cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	} else {
569f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches		RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG,
570f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches			 "u_bufferd: %x, m_buffered: %x\n", u_buffed, m_buffed);
571cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	}
572cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li}
5736f334c2b3966f10cbd089bb124ec0e114d8d8c77Larry FingerEXPORT_SYMBOL_GPL(rtl_swlps_beacon);
574cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
575cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Livoid rtl_swlps_rf_awake(struct ieee80211_hw *hw)
576cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li{
577cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_priv *rtlpriv = rtl_priv(hw);
578cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
579cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
580d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	unsigned long flag;
581cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
582cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!rtlpriv->psc.swctrl_lps)
583cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
584cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (mac->link_state != MAC80211_LINKED)
585cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
586cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
587cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM &&
588d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	    RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
589cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		rtlpriv->intf_ops->disable_aspm(hw);
590cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
591cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	}
592cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
593d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
594d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	rtl_ps_set_rf_state(hw, ERFON, RF_CHANGE_BY_PS, false);
595d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
596cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li}
597cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
598cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Livoid rtl_swlps_rfon_wq_callback(void *data)
599cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li{
600cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_works *rtlworks =
601cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	    container_of_dwork_rtl(data, struct rtl_works, ps_rfon_wq);
602cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct ieee80211_hw *hw = rtlworks->hw;
603cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
604cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	rtl_swlps_rf_awake(hw);
605cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li}
606cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
607cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Livoid rtl_swlps_rf_sleep(struct ieee80211_hw *hw)
608cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li{
609cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_priv *rtlpriv = rtl_priv(hw);
610cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
611cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
612d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	unsigned long flag;
613cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	u8 sleep_intv;
614cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
615cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!rtlpriv->psc.sw_ps_enabled)
616cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
617cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
618cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if ((rtlpriv->sec.being_setkey) ||
619cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	    (mac->opmode == NL80211_IFTYPE_ADHOC))
620cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
621cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
622cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */
623cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if ((mac->link_state != MAC80211_LINKED) || (mac->cnt_after_linked < 5))
624cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
625cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
626cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (rtlpriv->link_info.busytraffic)
627cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
628cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
629d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	spin_lock(&rtlpriv->locks.rf_ps_lock);
630d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	if (rtlpriv->psc.rfchange_inprogress) {
631d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		spin_unlock(&rtlpriv->locks.rf_ps_lock);
632d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		return;
633d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	}
634d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	spin_unlock(&rtlpriv->locks.rf_ps_lock);
635d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger
636d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
637d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	rtl_ps_set_rf_state(hw, ERFSLEEP, RF_CHANGE_BY_PS , false);
638d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
639cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
640cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM &&
641d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	    !RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
642cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		rtlpriv->intf_ops->enable_aspm(hw);
643cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
644cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	}
645cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
646cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* here is power save alg, when this beacon is DTIM
647cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * we will set sleep time to dtim_period * n;
648cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * when this beacon is not DTIM, we will set sleep
649cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * time to sleep_intv = rtlpriv->psc.dtim_counter or
650cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * MAX_SW_LPS_SLEEP_INTV(default set to 5) */
651cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
652cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (rtlpriv->psc.dtim_counter == 0) {
653cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		if (hw->conf.ps_dtim_period == 1)
654cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			sleep_intv = hw->conf.ps_dtim_period * 2;
655cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		else
656cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			sleep_intv = hw->conf.ps_dtim_period;
657cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	} else {
658cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		sleep_intv = rtlpriv->psc.dtim_counter;
659cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	}
660cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
661cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (sleep_intv > MAX_SW_LPS_SLEEP_INTV)
662cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		sleep_intv = MAX_SW_LPS_SLEEP_INTV;
663cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
664cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* this print should always be dtim_conter = 0 &
665cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * sleep  = dtim_period, that meaons, we should
666cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * awake before every dtim */
667cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG,
668f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches		 "dtim_counter:%x will sleep :%d beacon_intv\n",
669d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		  rtlpriv->psc.dtim_counter, sleep_intv);
670cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
671cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* we tested that 40ms is enough for sw & hw sw delay */
672cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	queue_delayed_work(rtlpriv->works.rtl_wq, &rtlpriv->works.ps_rfon_wq,
673cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			MSECS(sleep_intv * mac->vif->bss_conf.beacon_int - 40));
674cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li}
675cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
676bcfb879432094c267c35a7ff75d953d3a66c193aLarry Fingervoid rtl_lps_change_work_callback(struct work_struct *work)
677bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger{
678bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger	struct rtl_works *rtlworks =
679bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger	    container_of(work, struct rtl_works, lps_change_work);
680bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger	struct ieee80211_hw *hw = rtlworks->hw;
681bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
682bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger
683bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger	if (rtlpriv->enter_ps)
684bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger		rtl_lps_enter(hw);
685bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger	else
686bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger		rtl_lps_leave(hw);
687bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger}
6886f334c2b3966f10cbd089bb124ec0e114d8d8c77Larry FingerEXPORT_SYMBOL_GPL(rtl_lps_change_work_callback);
689cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
690cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Livoid rtl_swlps_wq_callback(void *data)
691cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li{
692cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_works *rtlworks = container_of_dwork_rtl(data,
693cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li				     struct rtl_works,
694cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li				     ps_work);
695cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct ieee80211_hw *hw = rtlworks->hw;
696cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_priv *rtlpriv = rtl_priv(hw);
697cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	bool ps = false;
698cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
699cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	ps = (hw->conf.flags & IEEE80211_CONF_PS);
700cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
701cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* we can sleep after ps null send ok */
702cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (rtlpriv->psc.state_inap) {
703cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		rtl_swlps_rf_sleep(hw);
704cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
705cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		if (rtlpriv->psc.state && !ps) {
706cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			rtlpriv->psc.sleep_ms = jiffies_to_msecs(jiffies -
707d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger						 rtlpriv->psc.last_action);
708cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		}
709cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
710cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		if (ps)
711cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			rtlpriv->psc.last_slept = jiffies;
712cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
713cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		rtlpriv->psc.last_action = jiffies;
714cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		rtlpriv->psc.state = ps;
715cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	}
716cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li}
71726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
71826634c4b1868323f49f8cd24c3493b57819867fdLarry Fingerstatic void rtl_p2p_noa_ie(struct ieee80211_hw *hw, void *data,
71926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			   unsigned int len)
72026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger{
72126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
7221851cb4a0f08ba0600103203c4b52e53c744f59cJoe Perches	struct ieee80211_mgmt *mgmt = data;
72326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info);
72426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	u8 *pos, *end, *ie;
72526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	u16 noa_len;
72626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09};
727d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	u8 noa_num, index , i, noa_index = 0;
72826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	bool find_p2p_ie = false , find_p2p_ps_ie = false;
72926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	pos = (u8 *)mgmt->u.beacon.variable;
73026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	end = data + len;
73126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	ie = NULL;
73226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
73326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	while (pos + 1 < end) {
73426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if (pos + 2 + pos[1] > end)
73526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			return;
73626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
73726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if (pos[0] == 221 && pos[1] > 4) {
73826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			if (memcmp(&pos[2], p2p_oui_ie_type, 4) == 0) {
73926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				ie = pos + 2+4;
74026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				break;
74126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			}
74226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		}
74326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		pos += 2 + pos[1];
74426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	}
74526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
74626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (ie == NULL)
74726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		return;
74826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	find_p2p_ie = true;
74926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	/*to find noa ie*/
75026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	while (ie + 1 < end) {
751e5b417e73efa52b9179ce2a2b7676f08425c62e3Mark Schulte		noa_len = READEF2BYTE((__le16 *)&ie[1]);
75226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if (ie + 3 + ie[1] > end)
75326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			return;
75426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
75526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if (ie[0] == 12) {
75626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			find_p2p_ps_ie = true;
75726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			if ((noa_len - 2) % 13 != 0) {
75826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
75926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 "P2P notice of absence: invalid length.%d\n",
76026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 noa_len);
76126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				return;
76226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			} else {
76326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				noa_num = (noa_len - 2) / 13;
76426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			}
76526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			noa_index = ie[3];
76626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode ==
76726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			    P2P_PS_NONE || noa_index != p2pinfo->noa_index) {
76826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
76926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 "update NOA ie.\n");
77026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				p2pinfo->noa_index = noa_index;
77126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				p2pinfo->opp_ps = (ie[4] >> 7);
77226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				p2pinfo->ctwindow = ie[4] & 0x7F;
77326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				p2pinfo->noa_num = noa_num;
77426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				index = 5;
77526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				for (i = 0; i < noa_num; i++) {
77626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->noa_count_type[i] =
777d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger							READEF1BYTE(ie+index);
77826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					index += 1;
77926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->noa_duration[i] =
780e5b417e73efa52b9179ce2a2b7676f08425c62e3Mark Schulte						 READEF4BYTE((__le32 *)ie+index);
78126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					index += 4;
78226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->noa_interval[i] =
783e5b417e73efa52b9179ce2a2b7676f08425c62e3Mark Schulte						 READEF4BYTE((__le32 *)ie+index);
78426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					index += 4;
78526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->noa_start_time[i] =
786e5b417e73efa52b9179ce2a2b7676f08425c62e3Mark Schulte						 READEF4BYTE((__le32 *)ie+index);
78726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					index += 4;
78826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				}
78926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
79026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				if (p2pinfo->opp_ps == 1) {
79126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW;
79226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					/* Driver should wait LPS entering
79326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 * CTWindow
79426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 */
79526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					if (rtlpriv->psc.fw_current_inpsmode)
79626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger						rtl_p2p_ps_cmd(hw,
79726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger							       P2P_PS_ENABLE);
79826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				} else if (p2pinfo->noa_num > 0) {
79926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->p2p_ps_mode = P2P_PS_NOA;
80026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE);
80126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				} else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
80226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
80326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				}
80426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			}
805d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger			break;
80626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		}
80726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		ie += 3 + noa_len;
80826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	}
80926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
81026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (find_p2p_ie == true) {
81126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if ((p2pinfo->p2p_ps_mode > P2P_PS_NONE) &&
81226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		    (find_p2p_ps_ie == false))
81326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
81426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	}
81526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger}
81626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
81726634c4b1868323f49f8cd24c3493b57819867fdLarry Fingerstatic void rtl_p2p_action_ie(struct ieee80211_hw *hw, void *data,
81826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			      unsigned int len)
81926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger{
82026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
8211851cb4a0f08ba0600103203c4b52e53c744f59cJoe Perches	struct ieee80211_mgmt *mgmt = data;
82226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info);
823d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	u8 noa_num, index , i , noa_index = 0;
82426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	u8 *pos, *end, *ie;
82526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	u16 noa_len;
82626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09};
82726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
82826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	pos = (u8 *)&mgmt->u.action.category;
82926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	end = data + len;
83026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	ie = NULL;
83126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
83226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (pos[0] == 0x7f) {
83326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if (memcmp(&pos[1], p2p_oui_ie_type, 4) == 0)
83426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			ie = pos + 3+4;
83526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	}
83626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
83726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (ie == NULL)
83826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		return;
83926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
84026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "action frame find P2P IE.\n");
84126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	/*to find noa ie*/
84226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	while (ie + 1 < end) {
843e5b417e73efa52b9179ce2a2b7676f08425c62e3Mark Schulte		noa_len = READEF2BYTE((__le16 *)&ie[1]);
84426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if (ie + 3 + ie[1] > end)
84526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			return;
84626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
84726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if (ie[0] == 12) {
84826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "find NOA IE.\n");
84926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			RT_PRINT_DATA(rtlpriv, COMP_FW, DBG_LOUD, "noa ie ",
85026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				      ie, noa_len);
85126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			if ((noa_len - 2) % 13 != 0) {
85226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
85326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 "P2P notice of absence: invalid length.%d\n",
85426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 noa_len);
85526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				return;
85626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			} else {
85726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				noa_num = (noa_len - 2) / 13;
85826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			}
85926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			noa_index = ie[3];
86026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode ==
86126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			    P2P_PS_NONE || noa_index != p2pinfo->noa_index) {
86226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				p2pinfo->noa_index = noa_index;
86326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				p2pinfo->opp_ps = (ie[4] >> 7);
86426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				p2pinfo->ctwindow = ie[4] & 0x7F;
86526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				p2pinfo->noa_num = noa_num;
86626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				index = 5;
86726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				for (i = 0; i < noa_num; i++) {
86826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->noa_count_type[i] =
869d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger							READEF1BYTE(ie+index);
87026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					index += 1;
87126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->noa_duration[i] =
872e5b417e73efa52b9179ce2a2b7676f08425c62e3Mark Schulte							 READEF4BYTE((__le32 *)ie+index);
87326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					index += 4;
87426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->noa_interval[i] =
875e5b417e73efa52b9179ce2a2b7676f08425c62e3Mark Schulte							 READEF4BYTE((__le32 *)ie+index);
87626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					index += 4;
87726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->noa_start_time[i] =
878e5b417e73efa52b9179ce2a2b7676f08425c62e3Mark Schulte							 READEF4BYTE((__le32 *)ie+index);
87926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					index += 4;
88026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				}
88126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
88226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				if (p2pinfo->opp_ps == 1) {
88326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW;
88426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					/* Driver should wait LPS entering
88526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 * CTWindow
88626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 */
88726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					if (rtlpriv->psc.fw_current_inpsmode)
88826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger						rtl_p2p_ps_cmd(hw,
88926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger							       P2P_PS_ENABLE);
89026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				} else if (p2pinfo->noa_num > 0) {
89126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->p2p_ps_mode = P2P_PS_NOA;
89226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE);
89326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				} else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
89426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
89526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				}
89626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			}
897d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger			break;
89826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		}
89926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		ie += 3 + noa_len;
90026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	}
90126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger}
90226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
903d3feae41a3473a0f7b431d6af4e092865d586e52Larry Fingervoid rtl_p2p_ps_cmd(struct ieee80211_hw *hw , u8 p2p_ps_state)
90426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger{
90526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
90626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct rtl_ps_ctl *rtlps = rtl_psc(rtl_priv(hw));
90726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct rtl_p2p_ps_info  *p2pinfo = &(rtlpriv->psc.p2p_ps_info);
90826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
909d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, " p2p state %x\n" , p2p_ps_state);
91026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	switch (p2p_ps_state) {
91126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	case P2P_PS_DISABLE:
91226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		p2pinfo->p2p_ps_state = p2p_ps_state;
9131851cb4a0f08ba0600103203c4b52e53c744f59cJoe Perches		rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
9141851cb4a0f08ba0600103203c4b52e53c744f59cJoe Perches					      &p2p_ps_state);
91526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		p2pinfo->noa_index = 0;
91626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		p2pinfo->ctwindow = 0;
91726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		p2pinfo->opp_ps = 0;
91826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		p2pinfo->noa_num = 0;
91926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		p2pinfo->p2p_ps_mode = P2P_PS_NONE;
920d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		if (rtlps->fw_current_inpsmode) {
92126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			if (rtlps->smart_ps == 0) {
92226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				rtlps->smart_ps = 2;
92326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				rtlpriv->cfg->ops->set_hw_reg(hw,
92426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 HW_VAR_H2C_FW_PWRMODE,
9251851cb4a0f08ba0600103203c4b52e53c744f59cJoe Perches					 &rtlps->pwr_mode);
92626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			}
927d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger
92826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		}
92926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		break;
93026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	case P2P_PS_ENABLE:
93126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
93226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			p2pinfo->p2p_ps_state = p2p_ps_state;
93326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
93426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			if (p2pinfo->ctwindow > 0) {
93526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				if (rtlps->smart_ps != 0) {
93626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					rtlps->smart_ps = 0;
93726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					rtlpriv->cfg->ops->set_hw_reg(hw,
93826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger						 HW_VAR_H2C_FW_PWRMODE,
9391851cb4a0f08ba0600103203c4b52e53c744f59cJoe Perches						 &rtlps->pwr_mode);
94026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				}
94126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			}
94226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			rtlpriv->cfg->ops->set_hw_reg(hw,
94326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				 HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
9441851cb4a0f08ba0600103203c4b52e53c744f59cJoe Perches				 &p2p_ps_state);
945d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger
94626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		}
94726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		break;
94826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	case P2P_PS_SCAN:
94926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	case P2P_PS_SCAN_DONE:
95026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	case P2P_PS_ALLSTASLEEP:
95126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
95226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			p2pinfo->p2p_ps_state = p2p_ps_state;
95326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			rtlpriv->cfg->ops->set_hw_reg(hw,
95426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				 HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
9551851cb4a0f08ba0600103203c4b52e53c744f59cJoe Perches				 &p2p_ps_state);
95626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		}
95726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		break;
95826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	default:
95926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		break;
96026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	}
96126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
962d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		 "ctwindow %x oppps %x\n",
963d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		 p2pinfo->ctwindow , p2pinfo->opp_ps);
96426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
96526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		 "count %x duration %x index %x interval %x start time %x noa num %x\n",
966d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		 p2pinfo->noa_count_type[0],
967d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		 p2pinfo->noa_duration[0],
968d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		 p2pinfo->noa_index,
969d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		 p2pinfo->noa_interval[0],
970d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		 p2pinfo->noa_start_time[0],
971d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		 p2pinfo->noa_num);
97226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "end\n");
97326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger}
97426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
97526634c4b1868323f49f8cd24c3493b57819867fdLarry Fingervoid rtl_p2p_info(struct ieee80211_hw *hw, void *data, unsigned int len)
97626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger{
97726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
97826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
9791851cb4a0f08ba0600103203c4b52e53c744f59cJoe Perches	struct ieee80211_hdr *hdr = data;
98026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
98126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (!mac->p2p)
98226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		return;
98326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (mac->link_state != MAC80211_LINKED)
98426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		return;
98526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	/* min. beacon length + FCS_LEN */
98626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (len <= 40 + FCS_LEN)
98726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		return;
98826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
98926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	/* and only beacons from the associated BSSID, please */
99090908e1cd14716f1b989b97003007bfcc7be3d13Julia Lawall	if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid))
99126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		return;
99226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
99326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	/* check if this really is a beacon */
99426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (!(ieee80211_is_beacon(hdr->frame_control) ||
99526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	      ieee80211_is_probe_resp(hdr->frame_control) ||
99626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	      ieee80211_is_action(hdr->frame_control)))
99726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		return;
99826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
99926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (ieee80211_is_action(hdr->frame_control))
1000d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		rtl_p2p_action_ie(hw , data , len - FCS_LEN);
100126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	else
1002d3feae41a3473a0f7b431d6af4e092865d586e52Larry Finger		rtl_p2p_noa_ie(hw , data , len - FCS_LEN);
100326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger}
10046f334c2b3966f10cbd089bb124ec0e114d8d8c77Larry FingerEXPORT_SYMBOL_GPL(rtl_p2p_info);
1005