ps.c revision 2e8c5e56b307271c2dab6f8bfd1d8a3822ca2390
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 * You should have received a copy of the GNU General Public License along with
150c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * this program; if not, write to the Free Software Foundation, Inc.,
160c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
170c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *
180c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * The full GNU General Public License is included in this distribution in the
190c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * file called LICENSE.
200c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *
210c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * Contact Information:
220c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * wlanfae <wlanfae@realtek.com>
230c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
240c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * Hsinchu 300, Taiwan.
250c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *
260c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * Larry Finger <Larry.Finger@lwfinger.net>
270c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *
280c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *****************************************************************************/
290c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
30ee40fa0656a730491765545ff7550f3c1ceb0fbcPaul Gortmaker#include <linux/export.h>
310c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger#include "wifi.h"
320c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger#include "base.h"
330c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger#include "ps.h"
340c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
350c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingerbool rtl_ps_enable_nic(struct ieee80211_hw *hw)
360c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
370c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
380c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
390c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
400c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
410c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<1> reset trx ring */
420c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (rtlhal->interface == INTF_PCI)
430c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		rtlpriv->intf_ops->reset_trx_ring(hw);
440c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
450c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (is_hal_stop(rtlhal))
460c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
47f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches			 "Driver is already down!\n");
480c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
490c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<2> Enable Adapter */
50b0302aba812bcc39291cdab9ad7e37008f352a91Larry Finger	if (rtlpriv->cfg->ops->hw_init(hw))
512e8c5e56b307271c2dab6f8bfd1d8a3822ca2390Olivier Langlois		return false;
520c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC);
530c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
540c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<3> Enable Interrupt */
550c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	rtlpriv->cfg->ops->enable_interrupt(hw);
560c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
570c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<enable timer> */
580c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	rtl_watch_dog_timer_callback((unsigned long)hw);
590c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
60cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	return true;
610c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
620c8173385e549f95cd80c3fff5aab87b4f881d8dLarry FingerEXPORT_SYMBOL(rtl_ps_enable_nic);
630c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
640c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingerbool rtl_ps_disable_nic(struct ieee80211_hw *hw)
650c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
660c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
670c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
680c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<1> Stop all timer */
690c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	rtl_deinit_deferred_work(hw);
700c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
710c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<2> Disable Interrupt */
720c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	rtlpriv->cfg->ops->disable_interrupt(hw);
7367fc6052a49b781efbcfc138f3b68fe79ddd0c2fMike McCormack	tasklet_kill(&rtlpriv->works.irq_tasklet);
740c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
750c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<3> Disable Adapter */
760c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	rtlpriv->cfg->ops->hw_disable(hw);
770c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
78324732848c42bf79988479ee1b4359e15f08154bLarry Finger	return true;
790c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
800c8173385e549f95cd80c3fff5aab87b4f881d8dLarry FingerEXPORT_SYMBOL(rtl_ps_disable_nic);
810c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
820c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingerbool rtl_ps_set_rf_state(struct ieee80211_hw *hw,
830c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			 enum rf_pwrstate state_toset,
844b9d8d67b44aae18e1c4b71281f5cfc0f2105cf6Mike McCormack			 u32 changesource)
850c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
860c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
870c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
887ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	bool actionallowed = false;
890c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
900c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	switch (state_toset) {
910c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	case ERFON:
920c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		ppsc->rfoff_reason &= (~changesource);
930c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
940c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if ((changesource == RF_CHANGE_BY_HW) &&
95e10542c447abf7c840931ff12f7d0dee976ca2eaMike McCormack		    (ppsc->hwradiooff)) {
967ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger			ppsc->hwradiooff = false;
970c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
980c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
990c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if (!ppsc->rfoff_reason) {
1000c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			ppsc->rfoff_reason = 0;
1017ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger			actionallowed = true;
1020c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
1030c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1040c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		break;
1050c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1060c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	case ERFOFF:
1070c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
10823677ce3172fcb93522a1df077d21019e73ee1e3Joe Perches		if ((changesource == RF_CHANGE_BY_HW) && !ppsc->hwradiooff) {
1097ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger			ppsc->hwradiooff = true;
1100c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
1110c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1120c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		ppsc->rfoff_reason |= changesource;
1137ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger		actionallowed = true;
1140c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		break;
1150c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1160c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	case ERFSLEEP:
1170c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		ppsc->rfoff_reason |= changesource;
1187ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger		actionallowed = true;
1190c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		break;
1200c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1210c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	default:
1220c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
123f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches			 "switch case not processed\n");
1240c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		break;
1250c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
1260c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1277ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	if (actionallowed)
1280c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		rtlpriv->cfg->ops->set_rf_power_state(hw, state_toset);
1290c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1307ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	return actionallowed;
1310c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
1320c8173385e549f95cd80c3fff5aab87b4f881d8dLarry FingerEXPORT_SYMBOL(rtl_ps_set_rf_state);
1330c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1340c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingerstatic void _rtl_ps_inactive_ps(struct ieee80211_hw *hw)
1350c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
1360c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
1370c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
1380c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
1390c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1407ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	ppsc->swrf_processing = true;
1410c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
142099fb8ab1e57e5d609ac686cc0ab6d1835a79155Larry Finger	if (ppsc->inactive_pwrstate == ERFON &&
143cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	    rtlhal->interface == INTF_PCI) {
1440c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if ((ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM) &&
145cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		    RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) &&
1460c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		    rtlhal->interface == INTF_PCI) {
1470c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			rtlpriv->intf_ops->disable_aspm(hw);
148cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
1490c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
1500c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
1510c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1524b9d8d67b44aae18e1c4b71281f5cfc0f2105cf6Mike McCormack	rtl_ps_set_rf_state(hw, ppsc->inactive_pwrstate, RF_CHANGE_BY_IPS);
1530c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1540c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (ppsc->inactive_pwrstate == ERFOFF &&
1550c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	    rtlhal->interface == INTF_PCI) {
156cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM &&
157cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			!RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
1580c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			rtlpriv->intf_ops->enable_aspm(hw);
159cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
1600c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
1610c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
1620c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1637ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	ppsc->swrf_processing = false;
1640c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
1650c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1660c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingervoid rtl_ips_nic_off_wq_callback(void *data)
1670c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
1680c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_works *rtlworks =
1690c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	    container_of_dwork_rtl(data, struct rtl_works, ips_nic_off_wq);
1700c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct ieee80211_hw *hw = rtlworks->hw;
1710c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
1720c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
1730c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
1740c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
1750c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	enum rf_pwrstate rtstate;
1760c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1770c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->opmode != NL80211_IFTYPE_STATION) {
1780c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
179f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches			 "not station return\n");
1800c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
1810c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
1820c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
18326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (mac->p2p_in_use)
18426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		return;
18526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
186cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (mac->link_state > MAC80211_NOLINK)
187cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
188cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
1890c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (is_hal_stop(rtlhal))
1900c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
1910c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1920c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (rtlpriv->sec.being_setkey)
1930c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
1940c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
19526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (rtlpriv->cfg->ops->bt_coex_off_before_lps)
19626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		rtlpriv->cfg->ops->bt_coex_off_before_lps(hw);
19726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
1987ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	if (ppsc->inactiveps) {
1990c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		rtstate = ppsc->rfpwr_state;
2000c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2010c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		/*
2020c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 *Do not enter IPS in the following conditions:
2030c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 *(1) RF is already OFF or Sleep
2047ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger		 *(2) swrf_processing (indicates the IPS is still under going)
2050c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 *(3) Connectted (only disconnected can trigger IPS)
2060c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 *(4) IBSS (send Beacon)
2070c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 *(5) AP mode (send Beacon)
2080c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 *(6) monitor mode (rcv packet)
2090c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 */
2100c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2110c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if (rtstate == ERFON &&
2127ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger		    !ppsc->swrf_processing &&
2130c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		    (mac->link_state == MAC80211_NOLINK) &&
2140c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		    !mac->act_scanning) {
2150c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
216f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches				 "IPSEnter(): Turn off RF\n");
2170c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2180c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			ppsc->inactive_pwrstate = ERFOFF;
2197ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger			ppsc->in_powersavemode = true;
2200c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2210c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			/*rtl_pci_reset_trx_ring(hw); */
2220c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			_rtl_ps_inactive_ps(hw);
2230c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
2240c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
2250c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
2260c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2270c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingervoid rtl_ips_nic_off(struct ieee80211_hw *hw)
2280c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
2290c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
2300c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2310c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*
2320c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *because when link with ap, mac80211 will ask us
2330c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *to disable nic quickly after scan before linking,
2340c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *this will cause link failed, so we delay 100ms here
2350c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 */
2360c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	queue_delayed_work(rtlpriv->works.rtl_wq,
2370c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			   &rtlpriv->works.ips_nic_off_wq, MSECS(100));
2380c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
2390c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
24026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger/* NOTICE: any opmode should exc nic_on, or disable without
24126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger * nic_on may something wrong, like adhoc TP
24226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger */
2430c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingervoid rtl_ips_nic_on(struct ieee80211_hw *hw)
2440c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
2450c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
246cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
2470c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
2480c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	enum rf_pwrstate rtstate;
249b9116b9a2b5db63187d28f99e038f473fad036dcLarry Finger	unsigned long flags;
2500c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
251cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (mac->opmode != NL80211_IFTYPE_STATION)
252cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
253cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
254b9116b9a2b5db63187d28f99e038f473fad036dcLarry Finger	spin_lock_irqsave(&rtlpriv->locks.ips_lock, flags);
2550c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2567ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	if (ppsc->inactiveps) {
2570c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		rtstate = ppsc->rfpwr_state;
2580c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2590c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if (rtstate != ERFON &&
2607ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger		    !ppsc->swrf_processing &&
2610c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		    ppsc->rfoff_reason <= RF_CHANGE_BY_IPS) {
2620c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2630c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			ppsc->inactive_pwrstate = ERFON;
2647ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger			ppsc->in_powersavemode = false;
2650c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2660c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			_rtl_ps_inactive_ps(hw);
2670c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
2680c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
2690c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
270b9116b9a2b5db63187d28f99e038f473fad036dcLarry Finger	spin_unlock_irqrestore(&rtlpriv->locks.ips_lock, flags);
2710c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
2726f334c2b3966f10cbd089bb124ec0e114d8d8c77Larry FingerEXPORT_SYMBOL_GPL(rtl_ips_nic_on);
2730c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2740c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger/*for FW LPS*/
2750c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2760c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger/*
2770c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *Determine if we can set Fw into PS mode
2780c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *in current condition.Return TRUE if it
2790c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *can enter PS mode.
2800c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger */
2810c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingerstatic bool rtl_get_fwlps_doze(struct ieee80211_hw *hw)
2820c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
2830c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
2840c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
2850c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
2860c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	u32 ps_timediff;
2870c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2880c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	ps_timediff = jiffies_to_msecs(jiffies -
2890c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				       ppsc->last_delaylps_stamp_jiffies);
2900c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2910c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (ps_timediff < 2000) {
2920c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
293f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches			 "Delay enter Fw LPS for DHCP, ARP, or EAPOL exchanging state\n");
2940c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return false;
2950c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
2960c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2970c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->link_state != MAC80211_LINKED)
2980c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return false;
2990c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3000c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->opmode == NL80211_IFTYPE_ADHOC)
3010c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return false;
3020c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3030c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	return true;
3040c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
3050c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3060c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger/* Change current and default preamble mode.*/
3070c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingerstatic void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode)
3080c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
3090c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
3100c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
3110c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
31226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	bool enter_fwlps;
3130c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3140c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->opmode == NL80211_IFTYPE_ADHOC)
3150c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
3160c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3170c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->link_state != MAC80211_LINKED)
3180c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
3190c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3200c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (ppsc->dot11_psmode == rt_psmode)
3210c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
3220c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3230c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/* Update power save mode configured. */
3240c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	ppsc->dot11_psmode = rt_psmode;
3250c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3260c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*
3270c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *<FW control LPS>
3280c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *1. Enter PS mode
3290c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *   Set RPWM to Fw to turn RF off and send H2C fw_pwrmode
3300c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *   cmd to set Fw into PS mode.
3310c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *2. Leave PS mode
3320c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *   Send H2C fw_pwrmode cmd to Fw to set Fw into Active
3330c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *   mode and set RPWM to turn RF on.
3340c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 */
3350c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
336cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if ((ppsc->fwctrl_lps) && ppsc->report_linked) {
3370c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if (ppsc->dot11_psmode == EACTIVE) {
3380c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
339f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches				 "FW LPS leave ps_mode:%x\n",
340f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches				 FW_PS_ACTIVE_MODE);
34126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			enter_fwlps = false;
34226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			ppsc->pwr_mode = FW_PS_ACTIVE_MODE;
34326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			ppsc->smart_ps = 0;
3440c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			rtlpriv->cfg->ops->set_hw_reg(hw,
34526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger						HW_VAR_FW_LPS_ACTION,
34626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger						(u8 *)(&enter_fwlps));
34726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			if (ppsc->p2p_ps_info.opp_ps)
34826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE);
3490c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3500c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		} else {
3510c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			if (rtl_get_fwlps_doze(hw)) {
3520c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
353f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches					 "FW LPS enter ps_mode:%x\n",
354f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches					 ppsc->fwctrl_psmode);
35526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				enter_fwlps = true;
35626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				ppsc->pwr_mode = ppsc->fwctrl_psmode;
35726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				ppsc->smart_ps = 2;
3580c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				rtlpriv->cfg->ops->set_hw_reg(hw,
35926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger							HW_VAR_FW_LPS_ACTION,
36026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger							(u8 *)(&enter_fwlps));
3610c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3620c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			} else {
3630c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				/* Reset the power save related parameters. */
3640c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				ppsc->dot11_psmode = EACTIVE;
3650c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			}
3660c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
3670c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
3680c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
3690c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3700c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger/*Enter the leisure power save mode.*/
3710c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingervoid rtl_lps_enter(struct ieee80211_hw *hw)
3720c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
3730c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
3740c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
3750c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
3760c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
377cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!ppsc->fwctrl_lps)
3780c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
3790c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3800c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (rtlpriv->sec.being_setkey)
3810c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
3820c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3837ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	if (rtlpriv->link_info.busytraffic)
3840c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
3850c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3860c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */
3870c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->cnt_after_linked < 5)
3880c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
3890c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3900c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->opmode == NL80211_IFTYPE_ADHOC)
3910c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
3920c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3930c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->link_state != MAC80211_LINKED)
3940c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
3950c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3966539306b2c3ceafbc4094cf68c58094c282da053Stanislaw Gruszka	mutex_lock(&rtlpriv->locks.ps_mutex);
3970c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
398cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* Idle for a while if we connect to AP a while ago. */
399cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (mac->cnt_after_linked >= 2) {
400cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		if (ppsc->dot11_psmode == EACTIVE) {
401cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
402f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches				 "Enter 802.11 power save mode...\n");
4030c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
404cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			rtl_lps_set_psmode(hw, EAUTOPS);
4050c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
4060c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
407cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
4086539306b2c3ceafbc4094cf68c58094c282da053Stanislaw Gruszka	mutex_unlock(&rtlpriv->locks.ps_mutex);
4090c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
4100c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4110c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger/*Leave the leisure power save mode.*/
4120c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingervoid rtl_lps_leave(struct ieee80211_hw *hw)
4130c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
4140c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
4150c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
4160c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
4170c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4186539306b2c3ceafbc4094cf68c58094c282da053Stanislaw Gruszka	mutex_lock(&rtlpriv->locks.ps_mutex);
4190c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
420cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (ppsc->fwctrl_lps) {
4210c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if (ppsc->dot11_psmode != EACTIVE) {
4220c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4230c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			/*FIX ME */
4240c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			rtlpriv->cfg->ops->enable_interrupt(hw);
4250c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4260c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM &&
427cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			    RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) &&
4280c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			    rtlhal->interface == INTF_PCI) {
4290c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				rtlpriv->intf_ops->disable_aspm(hw);
430cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li				RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
4310c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			}
4320c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4330c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
434f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches				 "Busy Traffic,Leave 802.11 power save..\n");
4350c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4360c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			rtl_lps_set_psmode(hw, EACTIVE);
4370c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
4380c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
4396539306b2c3ceafbc4094cf68c58094c282da053Stanislaw Gruszka	mutex_unlock(&rtlpriv->locks.ps_mutex);
4400c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
441cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
442cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li/* For sw LPS*/
443cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Livoid rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len)
444cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li{
445cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_priv *rtlpriv = rtl_priv(hw);
446cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
4472c208890c6d4e16076c6664137703ec813e8fa6cJoe Perches	struct ieee80211_hdr *hdr = data;
448cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct ieee80211_tim_ie *tim_ie;
449cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	u8 *tim;
450cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	u8 tim_len;
451cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	bool u_buffed;
452cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	bool m_buffed;
453cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
454cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (mac->opmode != NL80211_IFTYPE_STATION)
455cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
456cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
457cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!rtlpriv->psc.swctrl_lps)
458cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
459cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
460cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (rtlpriv->mac80211.link_state != MAC80211_LINKED)
461cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
462cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
463cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!rtlpriv->psc.sw_ps_enabled)
464cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
465cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
466cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (rtlpriv->psc.fwctrl_lps)
467cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
468cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
469cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (likely(!(hw->conf.flags & IEEE80211_CONF_PS)))
470cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
471cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
472cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* check if this really is a beacon */
473cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!ieee80211_is_beacon(hdr->frame_control))
474cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
475cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
476cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* min. beacon length + FCS_LEN */
477cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (len <= 40 + FCS_LEN)
478cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
479cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
480cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* and only beacons from the associated BSSID, please */
48190908e1cd14716f1b989b97003007bfcc7be3d13Julia Lawall	if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid))
482cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
483cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
484cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	rtlpriv->psc.last_beacon = jiffies;
485cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
486cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	tim = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_TIM);
487cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!tim)
488cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
489cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
490cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (tim[1] < sizeof(*tim_ie))
491cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
492cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
493cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	tim_len = tim[1];
494cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	tim_ie = (struct ieee80211_tim_ie *) &tim[2];
495cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
496cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!WARN_ON_ONCE(!hw->conf.ps_dtim_period))
497cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		rtlpriv->psc.dtim_counter = tim_ie->dtim_count;
498cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
499cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* Check whenever the PHY can be turned off again. */
500cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
501cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* 1. What about buffered unicast traffic for our AID? */
502cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	u_buffed = ieee80211_check_tim(tim_ie, tim_len,
503cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li				       rtlpriv->mac80211.assoc_id);
504cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
505cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* 2. Maybe the AP wants to send multicast/broadcast data? */
506cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	m_buffed = tim_ie->bitmap_ctrl & 0x01;
507cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	rtlpriv->psc.multi_buffered = m_buffed;
508cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
509cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* unicast will process by mac80211 through
510cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * set ~IEEE80211_CONF_PS, So we just check
511cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * multicast frames here */
512cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!m_buffed) {
513cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		/* back to low-power land. and delay is
514cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		 * prevent null power save frame tx fail */
515cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		queue_delayed_work(rtlpriv->works.rtl_wq,
516cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li				&rtlpriv->works.ps_work, MSECS(5));
517cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	} else {
518f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches		RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG,
519f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches			 "u_bufferd: %x, m_buffered: %x\n", u_buffed, m_buffed);
520cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	}
521cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li}
5226f334c2b3966f10cbd089bb124ec0e114d8d8c77Larry FingerEXPORT_SYMBOL_GPL(rtl_swlps_beacon);
523cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
524cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Livoid rtl_swlps_rf_awake(struct ieee80211_hw *hw)
525cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li{
526cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_priv *rtlpriv = rtl_priv(hw);
527cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
528cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
529cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
530cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!rtlpriv->psc.swctrl_lps)
531cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
532cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (mac->link_state != MAC80211_LINKED)
533cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
534cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
535cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM &&
536cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
537cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		rtlpriv->intf_ops->disable_aspm(hw);
538cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
539cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	}
540cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
5416539306b2c3ceafbc4094cf68c58094c282da053Stanislaw Gruszka	mutex_lock(&rtlpriv->locks.ps_mutex);
5424b9d8d67b44aae18e1c4b71281f5cfc0f2105cf6Mike McCormack	rtl_ps_set_rf_state(hw, ERFON, RF_CHANGE_BY_PS);
5436539306b2c3ceafbc4094cf68c58094c282da053Stanislaw Gruszka	mutex_unlock(&rtlpriv->locks.ps_mutex);
544cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li}
545cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
546cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Livoid rtl_swlps_rfon_wq_callback(void *data)
547cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li{
548cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_works *rtlworks =
549cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	    container_of_dwork_rtl(data, struct rtl_works, ps_rfon_wq);
550cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct ieee80211_hw *hw = rtlworks->hw;
551cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
552cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	rtl_swlps_rf_awake(hw);
553cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li}
554cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
555cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Livoid rtl_swlps_rf_sleep(struct ieee80211_hw *hw)
556cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li{
557cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_priv *rtlpriv = rtl_priv(hw);
558cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
559cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
560cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	u8 sleep_intv;
561cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
562cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!rtlpriv->psc.sw_ps_enabled)
563cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
564cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
565cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if ((rtlpriv->sec.being_setkey) ||
566cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	    (mac->opmode == NL80211_IFTYPE_ADHOC))
567cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
568cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
569cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */
570cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if ((mac->link_state != MAC80211_LINKED) || (mac->cnt_after_linked < 5))
571cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
572cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
573cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (rtlpriv->link_info.busytraffic)
574cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
575cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
5766539306b2c3ceafbc4094cf68c58094c282da053Stanislaw Gruszka	mutex_lock(&rtlpriv->locks.ps_mutex);
5774b9d8d67b44aae18e1c4b71281f5cfc0f2105cf6Mike McCormack	rtl_ps_set_rf_state(hw, ERFSLEEP, RF_CHANGE_BY_PS);
5786539306b2c3ceafbc4094cf68c58094c282da053Stanislaw Gruszka	mutex_unlock(&rtlpriv->locks.ps_mutex);
579cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
580cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM &&
581cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		!RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
582cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		rtlpriv->intf_ops->enable_aspm(hw);
583cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
584cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	}
585cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
586cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* here is power save alg, when this beacon is DTIM
587cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * we will set sleep time to dtim_period * n;
588cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * when this beacon is not DTIM, we will set sleep
589cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * time to sleep_intv = rtlpriv->psc.dtim_counter or
590cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * MAX_SW_LPS_SLEEP_INTV(default set to 5) */
591cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
592cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (rtlpriv->psc.dtim_counter == 0) {
593cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		if (hw->conf.ps_dtim_period == 1)
594cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			sleep_intv = hw->conf.ps_dtim_period * 2;
595cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		else
596cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			sleep_intv = hw->conf.ps_dtim_period;
597cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	} else {
598cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		sleep_intv = rtlpriv->psc.dtim_counter;
599cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	}
600cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
601cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (sleep_intv > MAX_SW_LPS_SLEEP_INTV)
602cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		sleep_intv = MAX_SW_LPS_SLEEP_INTV;
603cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
604cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* this print should always be dtim_conter = 0 &
605cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * sleep  = dtim_period, that meaons, we should
606cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * awake before every dtim */
607cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG,
608f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches		 "dtim_counter:%x will sleep :%d beacon_intv\n",
609f30d7507a8116e2099a9135c873411db8c0a3dc6Joe Perches		 rtlpriv->psc.dtim_counter, sleep_intv);
610cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
611cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* we tested that 40ms is enough for sw & hw sw delay */
612cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	queue_delayed_work(rtlpriv->works.rtl_wq, &rtlpriv->works.ps_rfon_wq,
613cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			MSECS(sleep_intv * mac->vif->bss_conf.beacon_int - 40));
614cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li}
615cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
616bcfb879432094c267c35a7ff75d953d3a66c193aLarry Fingervoid rtl_lps_change_work_callback(struct work_struct *work)
617bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger{
618bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger	struct rtl_works *rtlworks =
619bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger	    container_of(work, struct rtl_works, lps_change_work);
620bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger	struct ieee80211_hw *hw = rtlworks->hw;
621bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
622bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger
623bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger	if (rtlpriv->enter_ps)
624bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger		rtl_lps_enter(hw);
625bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger	else
626bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger		rtl_lps_leave(hw);
627bcfb879432094c267c35a7ff75d953d3a66c193aLarry Finger}
6286f334c2b3966f10cbd089bb124ec0e114d8d8c77Larry FingerEXPORT_SYMBOL_GPL(rtl_lps_change_work_callback);
629cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
630cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Livoid rtl_swlps_wq_callback(void *data)
631cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li{
632cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_works *rtlworks = container_of_dwork_rtl(data,
633cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li				     struct rtl_works,
634cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li				     ps_work);
635cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct ieee80211_hw *hw = rtlworks->hw;
636cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_priv *rtlpriv = rtl_priv(hw);
637cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	bool ps = false;
638cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
639cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	ps = (hw->conf.flags & IEEE80211_CONF_PS);
640cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
641cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* we can sleep after ps null send ok */
642cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (rtlpriv->psc.state_inap) {
643cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		rtl_swlps_rf_sleep(hw);
644cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
645cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		if (rtlpriv->psc.state && !ps) {
646cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			rtlpriv->psc.sleep_ms = jiffies_to_msecs(jiffies -
647cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li					rtlpriv->psc.last_action);
648cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		}
649cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
650cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		if (ps)
651cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			rtlpriv->psc.last_slept = jiffies;
652cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
653cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		rtlpriv->psc.last_action = jiffies;
654cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		rtlpriv->psc.state = ps;
655cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	}
656cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li}
65726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
65826634c4b1868323f49f8cd24c3493b57819867fdLarry Fingerstatic void rtl_p2p_noa_ie(struct ieee80211_hw *hw, void *data,
65926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			   unsigned int len)
66026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger{
66126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
66226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct ieee80211_mgmt *mgmt = (void *)data;
66326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info);
66426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	u8 *pos, *end, *ie;
66526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	u16 noa_len;
66626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09};
66726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	u8 noa_num, index, i, noa_index = 0;
66826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	bool find_p2p_ie = false , find_p2p_ps_ie = false;
66926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	pos = (u8 *)mgmt->u.beacon.variable;
67026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	end = data + len;
67126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	ie = NULL;
67226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
67326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	while (pos + 1 < end) {
67426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if (pos + 2 + pos[1] > end)
67526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			return;
67626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
67726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if (pos[0] == 221 && pos[1] > 4) {
67826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			if (memcmp(&pos[2], p2p_oui_ie_type, 4) == 0) {
67926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				ie = pos + 2+4;
68026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				break;
68126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			}
68226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		}
68326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		pos += 2 + pos[1];
68426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	}
68526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
68626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (ie == NULL)
68726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		return;
68826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	find_p2p_ie = true;
68926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	/*to find noa ie*/
69026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	while (ie + 1 < end) {
691e5b417e73efa52b9179ce2a2b7676f08425c62e3Mark Schulte		noa_len = READEF2BYTE((__le16 *)&ie[1]);
69226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if (ie + 3 + ie[1] > end)
69326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			return;
69426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
69526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if (ie[0] == 12) {
69626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			find_p2p_ps_ie = true;
69726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			if ((noa_len - 2) % 13 != 0) {
69826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
69926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 "P2P notice of absence: invalid length.%d\n",
70026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 noa_len);
70126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				return;
70226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			} else {
70326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				noa_num = (noa_len - 2) / 13;
70426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			}
70526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			noa_index = ie[3];
70626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode ==
70726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			    P2P_PS_NONE || noa_index != p2pinfo->noa_index) {
70826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
70926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 "update NOA ie.\n");
71026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				p2pinfo->noa_index = noa_index;
71126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				p2pinfo->opp_ps = (ie[4] >> 7);
71226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				p2pinfo->ctwindow = ie[4] & 0x7F;
71326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				p2pinfo->noa_num = noa_num;
71426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				index = 5;
71526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				for (i = 0; i < noa_num; i++) {
71626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->noa_count_type[i] =
71726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger						 READEF1BYTE(ie+index);
71826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					index += 1;
71926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->noa_duration[i] =
720e5b417e73efa52b9179ce2a2b7676f08425c62e3Mark Schulte						 READEF4BYTE((__le32 *)ie+index);
72126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					index += 4;
72226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->noa_interval[i] =
723e5b417e73efa52b9179ce2a2b7676f08425c62e3Mark Schulte						 READEF4BYTE((__le32 *)ie+index);
72426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					index += 4;
72526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->noa_start_time[i] =
726e5b417e73efa52b9179ce2a2b7676f08425c62e3Mark Schulte						 READEF4BYTE((__le32 *)ie+index);
72726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					index += 4;
72826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				}
72926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
73026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				if (p2pinfo->opp_ps == 1) {
73126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW;
73226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					/* Driver should wait LPS entering
73326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 * CTWindow
73426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 */
73526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					if (rtlpriv->psc.fw_current_inpsmode)
73626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger						rtl_p2p_ps_cmd(hw,
73726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger							       P2P_PS_ENABLE);
73826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				} else if (p2pinfo->noa_num > 0) {
73926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->p2p_ps_mode = P2P_PS_NOA;
74026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE);
74126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				} else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
74226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
74326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				}
74426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			}
74526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		break;
74626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		}
74726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		ie += 3 + noa_len;
74826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	}
74926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
75026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (find_p2p_ie == true) {
75126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if ((p2pinfo->p2p_ps_mode > P2P_PS_NONE) &&
75226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		    (find_p2p_ps_ie == false))
75326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
75426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	}
75526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger}
75626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
75726634c4b1868323f49f8cd24c3493b57819867fdLarry Fingerstatic void rtl_p2p_action_ie(struct ieee80211_hw *hw, void *data,
75826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			      unsigned int len)
75926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger{
76026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
76126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct ieee80211_mgmt *mgmt = (void *)data;
76226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info);
76326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	u8 noa_num, index, i, noa_index = 0;
76426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	u8 *pos, *end, *ie;
76526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	u16 noa_len;
76626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09};
76726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
76826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	pos = (u8 *)&mgmt->u.action.category;
76926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	end = data + len;
77026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	ie = NULL;
77126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
77226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (pos[0] == 0x7f) {
77326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if (memcmp(&pos[1], p2p_oui_ie_type, 4) == 0)
77426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			ie = pos + 3+4;
77526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	}
77626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
77726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (ie == NULL)
77826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		return;
77926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
78026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "action frame find P2P IE.\n");
78126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	/*to find noa ie*/
78226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	while (ie + 1 < end) {
783e5b417e73efa52b9179ce2a2b7676f08425c62e3Mark Schulte		noa_len = READEF2BYTE((__le16 *)&ie[1]);
78426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if (ie + 3 + ie[1] > end)
78526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			return;
78626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
78726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if (ie[0] == 12) {
78826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "find NOA IE.\n");
78926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			RT_PRINT_DATA(rtlpriv, COMP_FW, DBG_LOUD, "noa ie ",
79026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				      ie, noa_len);
79126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			if ((noa_len - 2) % 13 != 0) {
79226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
79326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 "P2P notice of absence: invalid length.%d\n",
79426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 noa_len);
79526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				return;
79626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			} else {
79726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				noa_num = (noa_len - 2) / 13;
79826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			}
79926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			noa_index = ie[3];
80026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode ==
80126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			    P2P_PS_NONE || noa_index != p2pinfo->noa_index) {
80226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				p2pinfo->noa_index = noa_index;
80326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				p2pinfo->opp_ps = (ie[4] >> 7);
80426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				p2pinfo->ctwindow = ie[4] & 0x7F;
80526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				p2pinfo->noa_num = noa_num;
80626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				index = 5;
80726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				for (i = 0; i < noa_num; i++) {
80826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->noa_count_type[i] =
80926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger							 READEF1BYTE(ie+index);
81026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					index += 1;
81126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->noa_duration[i] =
812e5b417e73efa52b9179ce2a2b7676f08425c62e3Mark Schulte							 READEF4BYTE((__le32 *)ie+index);
81326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					index += 4;
81426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->noa_interval[i] =
815e5b417e73efa52b9179ce2a2b7676f08425c62e3Mark Schulte							 READEF4BYTE((__le32 *)ie+index);
81626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					index += 4;
81726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->noa_start_time[i] =
818e5b417e73efa52b9179ce2a2b7676f08425c62e3Mark Schulte							 READEF4BYTE((__le32 *)ie+index);
81926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					index += 4;
82026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				}
82126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
82226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				if (p2pinfo->opp_ps == 1) {
82326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW;
82426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					/* Driver should wait LPS entering
82526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 * CTWindow
82626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 */
82726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					if (rtlpriv->psc.fw_current_inpsmode)
82826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger						rtl_p2p_ps_cmd(hw,
82926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger							       P2P_PS_ENABLE);
83026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				} else if (p2pinfo->noa_num > 0) {
83126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					p2pinfo->p2p_ps_mode = P2P_PS_NOA;
83226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE);
83326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				} else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
83426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
83526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				}
83626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			}
83726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		break;
83826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		}
83926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		ie += 3 + noa_len;
84026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	}
84126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger}
84226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
84326634c4b1868323f49f8cd24c3493b57819867fdLarry Fingervoid rtl_p2p_ps_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state)
84426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger{
84526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
84626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct rtl_ps_ctl *rtlps = rtl_psc(rtl_priv(hw));
84726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct rtl_p2p_ps_info  *p2pinfo = &(rtlpriv->psc.p2p_ps_info);
84826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
84926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, " p2p state %x\n", p2p_ps_state);
85026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	switch (p2p_ps_state) {
85126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	case P2P_PS_DISABLE:
85226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		p2pinfo->p2p_ps_state = p2p_ps_state;
85326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		rtlpriv->cfg->ops->set_hw_reg(hw,
85426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				 HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
85526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				 (u8 *)(&p2p_ps_state));
85626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
85726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		p2pinfo->noa_index = 0;
85826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		p2pinfo->ctwindow = 0;
85926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		p2pinfo->opp_ps = 0;
86026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		p2pinfo->noa_num = 0;
86126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		p2pinfo->p2p_ps_mode = P2P_PS_NONE;
86226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if (rtlps->fw_current_inpsmode == true) {
86326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			if (rtlps->smart_ps == 0) {
86426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				rtlps->smart_ps = 2;
86526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				rtlpriv->cfg->ops->set_hw_reg(hw,
86626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 HW_VAR_H2C_FW_PWRMODE,
86726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					 (u8 *)(&rtlps->pwr_mode));
86826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			}
86926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		}
87026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		break;
87126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	case P2P_PS_ENABLE:
87226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
87326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			p2pinfo->p2p_ps_state = p2p_ps_state;
87426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
87526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			if (p2pinfo->ctwindow > 0) {
87626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				if (rtlps->smart_ps != 0) {
87726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					rtlps->smart_ps = 0;
87826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger					rtlpriv->cfg->ops->set_hw_reg(hw,
87926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger						 HW_VAR_H2C_FW_PWRMODE,
88026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger						 (u8 *)(&rtlps->pwr_mode));
88126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				}
88226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			}
88326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			rtlpriv->cfg->ops->set_hw_reg(hw,
88426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				 HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
88526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				 (u8 *)(&p2p_ps_state));
88626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		}
88726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		break;
88826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	case P2P_PS_SCAN:
88926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	case P2P_PS_SCAN_DONE:
89026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	case P2P_PS_ALLSTASLEEP:
89126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
89226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			p2pinfo->p2p_ps_state = p2p_ps_state;
89326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger			rtlpriv->cfg->ops->set_hw_reg(hw,
89426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				 HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
89526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger				 (u8 *)(&p2p_ps_state));
89626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		}
89726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		break;
89826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	default:
89926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		break;
90026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	}
90126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
90226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		 "ctwindow %x oppps %x\n", p2pinfo->ctwindow, p2pinfo->opp_ps);
90326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
90426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		 "count %x duration %x index %x interval %x start time %x noa num %x\n",
90526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		 p2pinfo->noa_count_type[0], p2pinfo->noa_duration[0],
90626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		 p2pinfo->noa_index, p2pinfo->noa_interval[0],
90726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		 p2pinfo->noa_start_time[0], p2pinfo->noa_num);
90826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "end\n");
90926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger}
91026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
91126634c4b1868323f49f8cd24c3493b57819867fdLarry Fingervoid rtl_p2p_info(struct ieee80211_hw *hw, void *data, unsigned int len)
91226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger{
91326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
91426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
91526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	struct ieee80211_hdr *hdr = (void *)data;
91626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
91726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (!mac->p2p)
91826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		return;
91926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (mac->link_state != MAC80211_LINKED)
92026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		return;
92126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	/* min. beacon length + FCS_LEN */
92226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (len <= 40 + FCS_LEN)
92326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		return;
92426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
92526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	/* and only beacons from the associated BSSID, please */
92690908e1cd14716f1b989b97003007bfcc7be3d13Julia Lawall	if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid))
92726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		return;
92826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
92926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	/* check if this really is a beacon */
93026634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (!(ieee80211_is_beacon(hdr->frame_control) ||
93126634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	      ieee80211_is_probe_resp(hdr->frame_control) ||
93226634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	      ieee80211_is_action(hdr->frame_control)))
93326634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		return;
93426634c4b1868323f49f8cd24c3493b57819867fdLarry Finger
93526634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	if (ieee80211_is_action(hdr->frame_control))
93626634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		rtl_p2p_action_ie(hw, data, len - FCS_LEN);
93726634c4b1868323f49f8cd24c3493b57819867fdLarry Finger	else
93826634c4b1868323f49f8cd24c3493b57819867fdLarry Finger		rtl_p2p_noa_ie(hw, data, len - FCS_LEN);
93926634c4b1868323f49f8cd24c3493b57819867fdLarry Finger}
9406f334c2b3966f10cbd089bb124ec0e114d8d8c77Larry FingerEXPORT_SYMBOL_GPL(rtl_p2p_info);
941