18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright(c) 2009-2012  Realtek Corporation.*/
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include "wifi.h"
58c2ecf20Sopenharmony_ci#include "base.h"
68c2ecf20Sopenharmony_ci#include "ps.h"
78c2ecf20Sopenharmony_ci#include <linux/export.h>
88c2ecf20Sopenharmony_ci#include "btcoexist/rtl_btc.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_cibool rtl_ps_enable_nic(struct ieee80211_hw *hw)
118c2ecf20Sopenharmony_ci{
128c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
138c2ecf20Sopenharmony_ci	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
148c2ecf20Sopenharmony_ci	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
158c2ecf20Sopenharmony_ci	struct rtl_mac *rtlmac = rtl_mac(rtl_priv(hw));
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci	/*<1> reset trx ring */
188c2ecf20Sopenharmony_ci	if (rtlhal->interface == INTF_PCI)
198c2ecf20Sopenharmony_ci		rtlpriv->intf_ops->reset_trx_ring(hw);
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	if (is_hal_stop(rtlhal))
228c2ecf20Sopenharmony_ci		rtl_dbg(rtlpriv, COMP_ERR, DBG_WARNING,
238c2ecf20Sopenharmony_ci			"Driver is already down!\n");
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	/*<2> Enable Adapter */
268c2ecf20Sopenharmony_ci	if (rtlpriv->cfg->ops->hw_init(hw))
278c2ecf20Sopenharmony_ci		return false;
288c2ecf20Sopenharmony_ci	rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RETRY_LIMIT,
298c2ecf20Sopenharmony_ci			&rtlmac->retry_long);
308c2ecf20Sopenharmony_ci	RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	rtlpriv->cfg->ops->switch_channel(hw);
338c2ecf20Sopenharmony_ci	rtlpriv->cfg->ops->set_channel_access(hw);
348c2ecf20Sopenharmony_ci	rtlpriv->cfg->ops->set_bw_mode(hw,
358c2ecf20Sopenharmony_ci			cfg80211_get_chandef_type(&hw->conf.chandef));
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	/*<3> Enable Interrupt */
388c2ecf20Sopenharmony_ci	rtlpriv->cfg->ops->enable_interrupt(hw);
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	/*<enable timer> */
418c2ecf20Sopenharmony_ci	rtl_watch_dog_timer_callback(&rtlpriv->works.watchdog_timer);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	return true;
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rtl_ps_enable_nic);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cibool rtl_ps_disable_nic(struct ieee80211_hw *hw)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	/*<1> Stop all timer */
528c2ecf20Sopenharmony_ci	rtl_deinit_deferred_work(hw, true);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	/*<2> Disable Interrupt */
558c2ecf20Sopenharmony_ci	rtlpriv->cfg->ops->disable_interrupt(hw);
568c2ecf20Sopenharmony_ci	tasklet_kill(&rtlpriv->works.irq_tasklet);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	/*<3> Disable Adapter */
598c2ecf20Sopenharmony_ci	rtlpriv->cfg->ops->hw_disable(hw);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	return true;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rtl_ps_disable_nic);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic bool rtl_ps_set_rf_state(struct ieee80211_hw *hw,
668c2ecf20Sopenharmony_ci				enum rf_pwrstate state_toset,
678c2ecf20Sopenharmony_ci				u32 changesource)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
708c2ecf20Sopenharmony_ci	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
718c2ecf20Sopenharmony_ci	bool actionallowed = false;
728c2ecf20Sopenharmony_ci	u16 rfwait_cnt = 0;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	/*Only one thread can change
758c2ecf20Sopenharmony_ci	 *the RF state at one time, and others
768c2ecf20Sopenharmony_ci	 *should wait to be executed.
778c2ecf20Sopenharmony_ci	 */
788c2ecf20Sopenharmony_ci	while (true) {
798c2ecf20Sopenharmony_ci		spin_lock(&rtlpriv->locks.rf_ps_lock);
808c2ecf20Sopenharmony_ci		if (ppsc->rfchange_inprogress) {
818c2ecf20Sopenharmony_ci			spin_unlock(&rtlpriv->locks.rf_ps_lock);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci			rtl_dbg(rtlpriv, COMP_ERR, DBG_WARNING,
848c2ecf20Sopenharmony_ci				"RF Change in progress! Wait to set..state_toset(%d).\n",
858c2ecf20Sopenharmony_ci				state_toset);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci			/* Set RF after the previous action is done.  */
888c2ecf20Sopenharmony_ci			while (ppsc->rfchange_inprogress) {
898c2ecf20Sopenharmony_ci				rfwait_cnt++;
908c2ecf20Sopenharmony_ci				mdelay(1);
918c2ecf20Sopenharmony_ci				/*Wait too long, return false to avoid
928c2ecf20Sopenharmony_ci				 *to be stuck here.
938c2ecf20Sopenharmony_ci				 */
948c2ecf20Sopenharmony_ci				if (rfwait_cnt > 100)
958c2ecf20Sopenharmony_ci					return false;
968c2ecf20Sopenharmony_ci			}
978c2ecf20Sopenharmony_ci		} else {
988c2ecf20Sopenharmony_ci			ppsc->rfchange_inprogress = true;
998c2ecf20Sopenharmony_ci			spin_unlock(&rtlpriv->locks.rf_ps_lock);
1008c2ecf20Sopenharmony_ci			break;
1018c2ecf20Sopenharmony_ci		}
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	switch (state_toset) {
1058c2ecf20Sopenharmony_ci	case ERFON:
1068c2ecf20Sopenharmony_ci		ppsc->rfoff_reason &= (~changesource);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci		if ((changesource == RF_CHANGE_BY_HW) &&
1098c2ecf20Sopenharmony_ci		    (ppsc->hwradiooff)) {
1108c2ecf20Sopenharmony_ci			ppsc->hwradiooff = false;
1118c2ecf20Sopenharmony_ci		}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci		if (!ppsc->rfoff_reason) {
1148c2ecf20Sopenharmony_ci			ppsc->rfoff_reason = 0;
1158c2ecf20Sopenharmony_ci			actionallowed = true;
1168c2ecf20Sopenharmony_ci		}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci		break;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	case ERFOFF:
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci		if ((changesource == RF_CHANGE_BY_HW) && !ppsc->hwradiooff) {
1238c2ecf20Sopenharmony_ci			ppsc->hwradiooff = true;
1248c2ecf20Sopenharmony_ci		}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci		ppsc->rfoff_reason |= changesource;
1278c2ecf20Sopenharmony_ci		actionallowed = true;
1288c2ecf20Sopenharmony_ci		break;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	case ERFSLEEP:
1318c2ecf20Sopenharmony_ci		ppsc->rfoff_reason |= changesource;
1328c2ecf20Sopenharmony_ci		actionallowed = true;
1338c2ecf20Sopenharmony_ci		break;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	default:
1368c2ecf20Sopenharmony_ci		pr_err("switch case %#x not processed\n", state_toset);
1378c2ecf20Sopenharmony_ci		break;
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	if (actionallowed)
1418c2ecf20Sopenharmony_ci		rtlpriv->cfg->ops->set_rf_power_state(hw, state_toset);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	spin_lock(&rtlpriv->locks.rf_ps_lock);
1448c2ecf20Sopenharmony_ci	ppsc->rfchange_inprogress = false;
1458c2ecf20Sopenharmony_ci	spin_unlock(&rtlpriv->locks.rf_ps_lock);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return actionallowed;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic void _rtl_ps_inactive_ps(struct ieee80211_hw *hw)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
1538c2ecf20Sopenharmony_ci	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
1548c2ecf20Sopenharmony_ci	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	ppsc->swrf_processing = true;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (ppsc->inactive_pwrstate == ERFON &&
1598c2ecf20Sopenharmony_ci	    rtlhal->interface == INTF_PCI) {
1608c2ecf20Sopenharmony_ci		if ((ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM) &&
1618c2ecf20Sopenharmony_ci		    RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
1628c2ecf20Sopenharmony_ci			rtlpriv->intf_ops->disable_aspm(hw);
1638c2ecf20Sopenharmony_ci			RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
1648c2ecf20Sopenharmony_ci		}
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	rtl_ps_set_rf_state(hw, ppsc->inactive_pwrstate,
1688c2ecf20Sopenharmony_ci			    RF_CHANGE_BY_IPS);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	if (ppsc->inactive_pwrstate == ERFOFF &&
1718c2ecf20Sopenharmony_ci	    rtlhal->interface == INTF_PCI) {
1728c2ecf20Sopenharmony_ci		if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM &&
1738c2ecf20Sopenharmony_ci		    !RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
1748c2ecf20Sopenharmony_ci			rtlpriv->intf_ops->enable_aspm(hw);
1758c2ecf20Sopenharmony_ci			RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
1768c2ecf20Sopenharmony_ci		}
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	ppsc->swrf_processing = false;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_civoid rtl_ips_nic_off_wq_callback(struct work_struct *work)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	struct rtl_works *rtlworks = container_of(work, struct rtl_works,
1858c2ecf20Sopenharmony_ci						  ips_nic_off_wq.work);
1868c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = rtlworks->hw;
1878c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
1888c2ecf20Sopenharmony_ci	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
1898c2ecf20Sopenharmony_ci	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
1908c2ecf20Sopenharmony_ci	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
1918c2ecf20Sopenharmony_ci	enum rf_pwrstate rtstate;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	if (mac->opmode != NL80211_IFTYPE_STATION) {
1948c2ecf20Sopenharmony_ci		rtl_dbg(rtlpriv, COMP_ERR, DBG_WARNING,
1958c2ecf20Sopenharmony_ci			"not station return\n");
1968c2ecf20Sopenharmony_ci		return;
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	if (mac->p2p_in_use)
2008c2ecf20Sopenharmony_ci		return;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (mac->link_state > MAC80211_NOLINK)
2038c2ecf20Sopenharmony_ci		return;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (is_hal_stop(rtlhal))
2068c2ecf20Sopenharmony_ci		return;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	if (rtlpriv->sec.being_setkey)
2098c2ecf20Sopenharmony_ci		return;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (rtlpriv->cfg->ops->bt_coex_off_before_lps)
2128c2ecf20Sopenharmony_ci		rtlpriv->cfg->ops->bt_coex_off_before_lps(hw);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (ppsc->inactiveps) {
2158c2ecf20Sopenharmony_ci		rtstate = ppsc->rfpwr_state;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci		/*
2188c2ecf20Sopenharmony_ci		 *Do not enter IPS in the following conditions:
2198c2ecf20Sopenharmony_ci		 *(1) RF is already OFF or Sleep
2208c2ecf20Sopenharmony_ci		 *(2) swrf_processing (indicates the IPS is still under going)
2218c2ecf20Sopenharmony_ci		 *(3) Connectted (only disconnected can trigger IPS)
2228c2ecf20Sopenharmony_ci		 *(4) IBSS (send Beacon)
2238c2ecf20Sopenharmony_ci		 *(5) AP mode (send Beacon)
2248c2ecf20Sopenharmony_ci		 *(6) monitor mode (rcv packet)
2258c2ecf20Sopenharmony_ci		 */
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci		if (rtstate == ERFON &&
2288c2ecf20Sopenharmony_ci		    !ppsc->swrf_processing &&
2298c2ecf20Sopenharmony_ci		    (mac->link_state == MAC80211_NOLINK) &&
2308c2ecf20Sopenharmony_ci		    !mac->act_scanning) {
2318c2ecf20Sopenharmony_ci			rtl_dbg(rtlpriv, COMP_RF, DBG_TRACE,
2328c2ecf20Sopenharmony_ci				"IPSEnter(): Turn off RF\n");
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci			ppsc->inactive_pwrstate = ERFOFF;
2358c2ecf20Sopenharmony_ci			ppsc->in_powersavemode = true;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci			/* call before RF off */
2388c2ecf20Sopenharmony_ci			if (rtlpriv->cfg->ops->get_btc_status())
2398c2ecf20Sopenharmony_ci				rtlpriv->btcoexist.btc_ops->btc_ips_notify(rtlpriv,
2408c2ecf20Sopenharmony_ci									ppsc->inactive_pwrstate);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci			/*rtl_pci_reset_trx_ring(hw); */
2438c2ecf20Sopenharmony_ci			_rtl_ps_inactive_ps(hw);
2448c2ecf20Sopenharmony_ci		}
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_civoid rtl_ips_nic_off(struct ieee80211_hw *hw)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	/* because when link with ap, mac80211 will ask us
2538c2ecf20Sopenharmony_ci	 * to disable nic quickly after scan before linking,
2548c2ecf20Sopenharmony_ci	 * this will cause link failed, so we delay 100ms here
2558c2ecf20Sopenharmony_ci	 */
2568c2ecf20Sopenharmony_ci	queue_delayed_work(rtlpriv->works.rtl_wq,
2578c2ecf20Sopenharmony_ci			   &rtlpriv->works.ips_nic_off_wq, MSECS(100));
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci/* NOTICE: any opmode should exc nic_on, or disable without
2618c2ecf20Sopenharmony_ci * nic_on may something wrong, like adhoc TP
2628c2ecf20Sopenharmony_ci */
2638c2ecf20Sopenharmony_civoid rtl_ips_nic_on(struct ieee80211_hw *hw)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
2668c2ecf20Sopenharmony_ci	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
2678c2ecf20Sopenharmony_ci	enum rf_pwrstate rtstate;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&rtlpriv->works.ips_nic_off_wq);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	mutex_lock(&rtlpriv->locks.ips_mutex);
2728c2ecf20Sopenharmony_ci	if (ppsc->inactiveps) {
2738c2ecf20Sopenharmony_ci		rtstate = ppsc->rfpwr_state;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		if (rtstate != ERFON &&
2768c2ecf20Sopenharmony_ci		    !ppsc->swrf_processing &&
2778c2ecf20Sopenharmony_ci		    ppsc->rfoff_reason <= RF_CHANGE_BY_IPS) {
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci			ppsc->inactive_pwrstate = ERFON;
2808c2ecf20Sopenharmony_ci			ppsc->in_powersavemode = false;
2818c2ecf20Sopenharmony_ci			_rtl_ps_inactive_ps(hw);
2828c2ecf20Sopenharmony_ci			/* call after RF on */
2838c2ecf20Sopenharmony_ci			if (rtlpriv->cfg->ops->get_btc_status())
2848c2ecf20Sopenharmony_ci				rtlpriv->btcoexist.btc_ops->btc_ips_notify(rtlpriv,
2858c2ecf20Sopenharmony_ci									ppsc->inactive_pwrstate);
2868c2ecf20Sopenharmony_ci		}
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci	mutex_unlock(&rtlpriv->locks.ips_mutex);
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl_ips_nic_on);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci/*for FW LPS*/
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci/*
2958c2ecf20Sopenharmony_ci *Determine if we can set Fw into PS mode
2968c2ecf20Sopenharmony_ci *in current condition.Return TRUE if it
2978c2ecf20Sopenharmony_ci *can enter PS mode.
2988c2ecf20Sopenharmony_ci */
2998c2ecf20Sopenharmony_cistatic bool rtl_get_fwlps_doze(struct ieee80211_hw *hw)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
3028c2ecf20Sopenharmony_ci	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
3038c2ecf20Sopenharmony_ci	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
3048c2ecf20Sopenharmony_ci	u32 ps_timediff;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	ps_timediff = jiffies_to_msecs(jiffies -
3078c2ecf20Sopenharmony_ci				       ppsc->last_delaylps_stamp_jiffies);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (ps_timediff < 2000) {
3108c2ecf20Sopenharmony_ci		rtl_dbg(rtlpriv, COMP_POWER, DBG_LOUD,
3118c2ecf20Sopenharmony_ci			"Delay enter Fw LPS for DHCP, ARP, or EAPOL exchanging state\n");
3128c2ecf20Sopenharmony_ci		return false;
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (mac->link_state != MAC80211_LINKED)
3168c2ecf20Sopenharmony_ci		return false;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (mac->opmode == NL80211_IFTYPE_ADHOC)
3198c2ecf20Sopenharmony_ci		return false;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	return true;
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci/* Change current and default preamble mode.*/
3258c2ecf20Sopenharmony_civoid rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
3288c2ecf20Sopenharmony_ci	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
3298c2ecf20Sopenharmony_ci	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
3308c2ecf20Sopenharmony_ci	bool enter_fwlps;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	if (mac->opmode == NL80211_IFTYPE_ADHOC)
3338c2ecf20Sopenharmony_ci		return;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (mac->link_state != MAC80211_LINKED)
3368c2ecf20Sopenharmony_ci		return;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	if (ppsc->dot11_psmode == rt_psmode && rt_psmode == EACTIVE)
3398c2ecf20Sopenharmony_ci		return;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	/* Update power save mode configured. */
3428c2ecf20Sopenharmony_ci	ppsc->dot11_psmode = rt_psmode;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	/*
3458c2ecf20Sopenharmony_ci	 *<FW control LPS>
3468c2ecf20Sopenharmony_ci	 *1. Enter PS mode
3478c2ecf20Sopenharmony_ci	 *   Set RPWM to Fw to turn RF off and send H2C fw_pwrmode
3488c2ecf20Sopenharmony_ci	 *   cmd to set Fw into PS mode.
3498c2ecf20Sopenharmony_ci	 *2. Leave PS mode
3508c2ecf20Sopenharmony_ci	 *   Send H2C fw_pwrmode cmd to Fw to set Fw into Active
3518c2ecf20Sopenharmony_ci	 *   mode and set RPWM to turn RF on.
3528c2ecf20Sopenharmony_ci	 */
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	if ((ppsc->fwctrl_lps) && ppsc->report_linked) {
3558c2ecf20Sopenharmony_ci		if (ppsc->dot11_psmode == EACTIVE) {
3568c2ecf20Sopenharmony_ci			rtl_dbg(rtlpriv, COMP_RF, DBG_DMESG,
3578c2ecf20Sopenharmony_ci				"FW LPS leave ps_mode:%x\n",
3588c2ecf20Sopenharmony_ci				FW_PS_ACTIVE_MODE);
3598c2ecf20Sopenharmony_ci			enter_fwlps = false;
3608c2ecf20Sopenharmony_ci			ppsc->pwr_mode = FW_PS_ACTIVE_MODE;
3618c2ecf20Sopenharmony_ci			ppsc->smart_ps = 0;
3628c2ecf20Sopenharmony_ci			rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_LPS_ACTION,
3638c2ecf20Sopenharmony_ci						      (u8 *)(&enter_fwlps));
3648c2ecf20Sopenharmony_ci			if (ppsc->p2p_ps_info.opp_ps)
3658c2ecf20Sopenharmony_ci				rtl_p2p_ps_cmd(hw , P2P_PS_ENABLE);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci			if (rtlpriv->cfg->ops->get_btc_status())
3688c2ecf20Sopenharmony_ci				rtlpriv->btcoexist.btc_ops->btc_lps_notify(rtlpriv, rt_psmode);
3698c2ecf20Sopenharmony_ci		} else {
3708c2ecf20Sopenharmony_ci			if (rtl_get_fwlps_doze(hw)) {
3718c2ecf20Sopenharmony_ci				rtl_dbg(rtlpriv, COMP_RF, DBG_DMESG,
3728c2ecf20Sopenharmony_ci					"FW LPS enter ps_mode:%x\n",
3738c2ecf20Sopenharmony_ci					ppsc->fwctrl_psmode);
3748c2ecf20Sopenharmony_ci				if (rtlpriv->cfg->ops->get_btc_status())
3758c2ecf20Sopenharmony_ci					rtlpriv->btcoexist.btc_ops->btc_lps_notify(rtlpriv, rt_psmode);
3768c2ecf20Sopenharmony_ci				enter_fwlps = true;
3778c2ecf20Sopenharmony_ci				ppsc->pwr_mode = ppsc->fwctrl_psmode;
3788c2ecf20Sopenharmony_ci				ppsc->smart_ps = 2;
3798c2ecf20Sopenharmony_ci				rtlpriv->cfg->ops->set_hw_reg(hw,
3808c2ecf20Sopenharmony_ci							HW_VAR_FW_LPS_ACTION,
3818c2ecf20Sopenharmony_ci							(u8 *)(&enter_fwlps));
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci			} else {
3848c2ecf20Sopenharmony_ci				/* Reset the power save related parameters. */
3858c2ecf20Sopenharmony_ci				ppsc->dot11_psmode = EACTIVE;
3868c2ecf20Sopenharmony_ci			}
3878c2ecf20Sopenharmony_ci		}
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci/* Interrupt safe routine to enter the leisure power save mode.*/
3928c2ecf20Sopenharmony_cistatic void rtl_lps_enter_core(struct ieee80211_hw *hw)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
3958c2ecf20Sopenharmony_ci	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
3968c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	if (!ppsc->fwctrl_lps)
3998c2ecf20Sopenharmony_ci		return;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	if (rtlpriv->sec.being_setkey)
4028c2ecf20Sopenharmony_ci		return;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	if (rtlpriv->link_info.busytraffic)
4058c2ecf20Sopenharmony_ci		return;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	/*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */
4088c2ecf20Sopenharmony_ci	if (mac->cnt_after_linked < 5)
4098c2ecf20Sopenharmony_ci		return;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	if (mac->opmode == NL80211_IFTYPE_ADHOC)
4128c2ecf20Sopenharmony_ci		return;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	if (mac->link_state != MAC80211_LINKED)
4158c2ecf20Sopenharmony_ci		return;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	mutex_lock(&rtlpriv->locks.lps_mutex);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	/* Don't need to check (ppsc->dot11_psmode == EACTIVE), because
4208c2ecf20Sopenharmony_ci	 * bt_ccoexist may ask to enter lps.
4218c2ecf20Sopenharmony_ci	 * In normal case, this constraint move to rtl_lps_set_psmode().
4228c2ecf20Sopenharmony_ci	 */
4238c2ecf20Sopenharmony_ci	rtl_dbg(rtlpriv, COMP_POWER, DBG_LOUD,
4248c2ecf20Sopenharmony_ci		"Enter 802.11 power save mode...\n");
4258c2ecf20Sopenharmony_ci	rtl_lps_set_psmode(hw, EAUTOPS);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	mutex_unlock(&rtlpriv->locks.lps_mutex);
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci/* Interrupt safe routine to leave the leisure power save mode.*/
4318c2ecf20Sopenharmony_cistatic void rtl_lps_leave_core(struct ieee80211_hw *hw)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
4348c2ecf20Sopenharmony_ci	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
4358c2ecf20Sopenharmony_ci	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	mutex_lock(&rtlpriv->locks.lps_mutex);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	if (ppsc->fwctrl_lps) {
4408c2ecf20Sopenharmony_ci		if (ppsc->dot11_psmode != EACTIVE) {
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci			/*FIX ME */
4438c2ecf20Sopenharmony_ci			/*rtlpriv->cfg->ops->enable_interrupt(hw); */
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci			if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM &&
4468c2ecf20Sopenharmony_ci			    RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) &&
4478c2ecf20Sopenharmony_ci			    rtlhal->interface == INTF_PCI) {
4488c2ecf20Sopenharmony_ci				rtlpriv->intf_ops->disable_aspm(hw);
4498c2ecf20Sopenharmony_ci				RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
4508c2ecf20Sopenharmony_ci			}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci			rtl_dbg(rtlpriv, COMP_POWER, DBG_LOUD,
4538c2ecf20Sopenharmony_ci				"Busy Traffic,Leave 802.11 power save..\n");
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci			rtl_lps_set_psmode(hw, EACTIVE);
4568c2ecf20Sopenharmony_ci		}
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci	mutex_unlock(&rtlpriv->locks.lps_mutex);
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci/* For sw LPS*/
4628c2ecf20Sopenharmony_civoid rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
4658c2ecf20Sopenharmony_ci	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
4668c2ecf20Sopenharmony_ci	struct ieee80211_hdr *hdr = data;
4678c2ecf20Sopenharmony_ci	struct ieee80211_tim_ie *tim_ie;
4688c2ecf20Sopenharmony_ci	u8 *tim;
4698c2ecf20Sopenharmony_ci	u8 tim_len;
4708c2ecf20Sopenharmony_ci	bool u_buffed;
4718c2ecf20Sopenharmony_ci	bool m_buffed;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	if (mac->opmode != NL80211_IFTYPE_STATION)
4748c2ecf20Sopenharmony_ci		return;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	if (!rtlpriv->psc.swctrl_lps)
4778c2ecf20Sopenharmony_ci		return;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	if (rtlpriv->mac80211.link_state != MAC80211_LINKED)
4808c2ecf20Sopenharmony_ci		return;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	if (!rtlpriv->psc.sw_ps_enabled)
4838c2ecf20Sopenharmony_ci		return;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	if (rtlpriv->psc.fwctrl_lps)
4868c2ecf20Sopenharmony_ci		return;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	if (likely(!(hw->conf.flags & IEEE80211_CONF_PS)))
4898c2ecf20Sopenharmony_ci		return;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	/* check if this really is a beacon */
4928c2ecf20Sopenharmony_ci	if (!ieee80211_is_beacon(hdr->frame_control))
4938c2ecf20Sopenharmony_ci		return;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	/* min. beacon length + FCS_LEN */
4968c2ecf20Sopenharmony_ci	if (len <= 40 + FCS_LEN)
4978c2ecf20Sopenharmony_ci		return;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	/* and only beacons from the associated BSSID, please */
5008c2ecf20Sopenharmony_ci	if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid))
5018c2ecf20Sopenharmony_ci		return;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	rtlpriv->psc.last_beacon = jiffies;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	tim = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_TIM);
5068c2ecf20Sopenharmony_ci	if (!tim)
5078c2ecf20Sopenharmony_ci		return;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	if (tim[1] < sizeof(*tim_ie))
5108c2ecf20Sopenharmony_ci		return;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	tim_len = tim[1];
5138c2ecf20Sopenharmony_ci	tim_ie = (struct ieee80211_tim_ie *) &tim[2];
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	if (!WARN_ON_ONCE(!hw->conf.ps_dtim_period))
5168c2ecf20Sopenharmony_ci		rtlpriv->psc.dtim_counter = tim_ie->dtim_count;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	/* Check whenever the PHY can be turned off again. */
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	/* 1. What about buffered unicast traffic for our AID? */
5218c2ecf20Sopenharmony_ci	u_buffed = ieee80211_check_tim(tim_ie, tim_len,
5228c2ecf20Sopenharmony_ci				       rtlpriv->mac80211.assoc_id);
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	/* 2. Maybe the AP wants to send multicast/broadcast data? */
5258c2ecf20Sopenharmony_ci	m_buffed = tim_ie->bitmap_ctrl & 0x01;
5268c2ecf20Sopenharmony_ci	rtlpriv->psc.multi_buffered = m_buffed;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	/* unicast will process by mac80211 through
5298c2ecf20Sopenharmony_ci	 * set ~IEEE80211_CONF_PS, So we just check
5308c2ecf20Sopenharmony_ci	 * multicast frames here */
5318c2ecf20Sopenharmony_ci	if (!m_buffed) {
5328c2ecf20Sopenharmony_ci		/* back to low-power land. and delay is
5338c2ecf20Sopenharmony_ci		 * prevent null power save frame tx fail */
5348c2ecf20Sopenharmony_ci		queue_delayed_work(rtlpriv->works.rtl_wq,
5358c2ecf20Sopenharmony_ci				   &rtlpriv->works.ps_work, MSECS(5));
5368c2ecf20Sopenharmony_ci	} else {
5378c2ecf20Sopenharmony_ci		rtl_dbg(rtlpriv, COMP_POWER, DBG_DMESG,
5388c2ecf20Sopenharmony_ci			"u_bufferd: %x, m_buffered: %x\n", u_buffed, m_buffed);
5398c2ecf20Sopenharmony_ci	}
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl_swlps_beacon);
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_civoid rtl_swlps_rf_awake(struct ieee80211_hw *hw)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
5468c2ecf20Sopenharmony_ci	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
5478c2ecf20Sopenharmony_ci	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	if (!rtlpriv->psc.swctrl_lps)
5508c2ecf20Sopenharmony_ci		return;
5518c2ecf20Sopenharmony_ci	if (mac->link_state != MAC80211_LINKED)
5528c2ecf20Sopenharmony_ci		return;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM &&
5558c2ecf20Sopenharmony_ci	    RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
5568c2ecf20Sopenharmony_ci		rtlpriv->intf_ops->disable_aspm(hw);
5578c2ecf20Sopenharmony_ci		RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	mutex_lock(&rtlpriv->locks.lps_mutex);
5618c2ecf20Sopenharmony_ci	rtl_ps_set_rf_state(hw, ERFON, RF_CHANGE_BY_PS);
5628c2ecf20Sopenharmony_ci	mutex_unlock(&rtlpriv->locks.lps_mutex);
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_civoid rtl_swlps_rfon_wq_callback(struct work_struct *work)
5668c2ecf20Sopenharmony_ci{
5678c2ecf20Sopenharmony_ci	struct rtl_works *rtlworks = container_of(work, struct rtl_works,
5688c2ecf20Sopenharmony_ci						  ps_rfon_wq.work);
5698c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = rtlworks->hw;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	rtl_swlps_rf_awake(hw);
5728c2ecf20Sopenharmony_ci}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_civoid rtl_swlps_rf_sleep(struct ieee80211_hw *hw)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
5778c2ecf20Sopenharmony_ci	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
5788c2ecf20Sopenharmony_ci	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
5798c2ecf20Sopenharmony_ci	u8 sleep_intv;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	if (!rtlpriv->psc.sw_ps_enabled)
5828c2ecf20Sopenharmony_ci		return;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	if ((rtlpriv->sec.being_setkey) ||
5858c2ecf20Sopenharmony_ci	    (mac->opmode == NL80211_IFTYPE_ADHOC))
5868c2ecf20Sopenharmony_ci		return;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	/*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */
5898c2ecf20Sopenharmony_ci	if ((mac->link_state != MAC80211_LINKED) || (mac->cnt_after_linked < 5))
5908c2ecf20Sopenharmony_ci		return;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	if (rtlpriv->link_info.busytraffic)
5938c2ecf20Sopenharmony_ci		return;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	spin_lock(&rtlpriv->locks.rf_ps_lock);
5968c2ecf20Sopenharmony_ci	if (rtlpriv->psc.rfchange_inprogress) {
5978c2ecf20Sopenharmony_ci		spin_unlock(&rtlpriv->locks.rf_ps_lock);
5988c2ecf20Sopenharmony_ci		return;
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci	spin_unlock(&rtlpriv->locks.rf_ps_lock);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	mutex_lock(&rtlpriv->locks.lps_mutex);
6038c2ecf20Sopenharmony_ci	rtl_ps_set_rf_state(hw, ERFSLEEP, RF_CHANGE_BY_PS);
6048c2ecf20Sopenharmony_ci	mutex_unlock(&rtlpriv->locks.lps_mutex);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM &&
6078c2ecf20Sopenharmony_ci	    !RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
6088c2ecf20Sopenharmony_ci		rtlpriv->intf_ops->enable_aspm(hw);
6098c2ecf20Sopenharmony_ci		RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
6108c2ecf20Sopenharmony_ci	}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	/* here is power save alg, when this beacon is DTIM
6138c2ecf20Sopenharmony_ci	 * we will set sleep time to dtim_period * n;
6148c2ecf20Sopenharmony_ci	 * when this beacon is not DTIM, we will set sleep
6158c2ecf20Sopenharmony_ci	 * time to sleep_intv = rtlpriv->psc.dtim_counter or
6168c2ecf20Sopenharmony_ci	 * MAX_SW_LPS_SLEEP_INTV(default set to 5) */
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	if (rtlpriv->psc.dtim_counter == 0) {
6198c2ecf20Sopenharmony_ci		if (hw->conf.ps_dtim_period == 1)
6208c2ecf20Sopenharmony_ci			sleep_intv = hw->conf.ps_dtim_period * 2;
6218c2ecf20Sopenharmony_ci		else
6228c2ecf20Sopenharmony_ci			sleep_intv = hw->conf.ps_dtim_period;
6238c2ecf20Sopenharmony_ci	} else {
6248c2ecf20Sopenharmony_ci		sleep_intv = rtlpriv->psc.dtim_counter;
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	if (sleep_intv > MAX_SW_LPS_SLEEP_INTV)
6288c2ecf20Sopenharmony_ci		sleep_intv = MAX_SW_LPS_SLEEP_INTV;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	/* this print should always be dtim_conter = 0 &
6318c2ecf20Sopenharmony_ci	 * sleep  = dtim_period, that meaons, we should
6328c2ecf20Sopenharmony_ci	 * awake before every dtim */
6338c2ecf20Sopenharmony_ci	rtl_dbg(rtlpriv, COMP_POWER, DBG_DMESG,
6348c2ecf20Sopenharmony_ci		"dtim_counter:%x will sleep :%d beacon_intv\n",
6358c2ecf20Sopenharmony_ci		rtlpriv->psc.dtim_counter, sleep_intv);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	/* we tested that 40ms is enough for sw & hw sw delay */
6388c2ecf20Sopenharmony_ci	queue_delayed_work(rtlpriv->works.rtl_wq, &rtlpriv->works.ps_rfon_wq,
6398c2ecf20Sopenharmony_ci			MSECS(sleep_intv * mac->vif->bss_conf.beacon_int - 40));
6408c2ecf20Sopenharmony_ci}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_civoid rtl_lps_change_work_callback(struct work_struct *work)
6438c2ecf20Sopenharmony_ci{
6448c2ecf20Sopenharmony_ci	struct rtl_works *rtlworks =
6458c2ecf20Sopenharmony_ci	    container_of(work, struct rtl_works, lps_change_work);
6468c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = rtlworks->hw;
6478c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	if (rtlpriv->enter_ps)
6508c2ecf20Sopenharmony_ci		rtl_lps_enter_core(hw);
6518c2ecf20Sopenharmony_ci	else
6528c2ecf20Sopenharmony_ci		rtl_lps_leave_core(hw);
6538c2ecf20Sopenharmony_ci}
6548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl_lps_change_work_callback);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_civoid rtl_lps_enter(struct ieee80211_hw *hw, bool may_block)
6578c2ecf20Sopenharmony_ci{
6588c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	if (may_block)
6618c2ecf20Sopenharmony_ci		return rtl_lps_enter_core(hw);
6628c2ecf20Sopenharmony_ci	rtlpriv->enter_ps = true;
6638c2ecf20Sopenharmony_ci	schedule_work(&rtlpriv->works.lps_change_work);
6648c2ecf20Sopenharmony_ci}
6658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl_lps_enter);
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_civoid rtl_lps_leave(struct ieee80211_hw *hw, bool may_block)
6688c2ecf20Sopenharmony_ci{
6698c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	if (may_block)
6728c2ecf20Sopenharmony_ci		return rtl_lps_leave_core(hw);
6738c2ecf20Sopenharmony_ci	rtlpriv->enter_ps = false;
6748c2ecf20Sopenharmony_ci	schedule_work(&rtlpriv->works.lps_change_work);
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl_lps_leave);
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_civoid rtl_swlps_wq_callback(struct work_struct *work)
6798c2ecf20Sopenharmony_ci{
6808c2ecf20Sopenharmony_ci	struct rtl_works *rtlworks = container_of(work, struct rtl_works,
6818c2ecf20Sopenharmony_ci						  ps_work.work);
6828c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = rtlworks->hw;
6838c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
6848c2ecf20Sopenharmony_ci	bool ps = false;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	ps = (hw->conf.flags & IEEE80211_CONF_PS);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	/* we can sleep after ps null send ok */
6898c2ecf20Sopenharmony_ci	if (rtlpriv->psc.state_inap) {
6908c2ecf20Sopenharmony_ci		rtl_swlps_rf_sleep(hw);
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci		if (rtlpriv->psc.state && !ps) {
6938c2ecf20Sopenharmony_ci			rtlpriv->psc.sleep_ms = jiffies_to_msecs(jiffies -
6948c2ecf20Sopenharmony_ci						 rtlpriv->psc.last_action);
6958c2ecf20Sopenharmony_ci		}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci		if (ps)
6988c2ecf20Sopenharmony_ci			rtlpriv->psc.last_slept = jiffies;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci		rtlpriv->psc.last_action = jiffies;
7018c2ecf20Sopenharmony_ci		rtlpriv->psc.state = ps;
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_cistatic void rtl_p2p_noa_ie(struct ieee80211_hw *hw, void *data,
7068c2ecf20Sopenharmony_ci			   unsigned int len)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
7098c2ecf20Sopenharmony_ci	struct ieee80211_mgmt *mgmt = data;
7108c2ecf20Sopenharmony_ci	struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info);
7118c2ecf20Sopenharmony_ci	u8 *pos, *end, *ie;
7128c2ecf20Sopenharmony_ci	u16 noa_len;
7138c2ecf20Sopenharmony_ci	static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09};
7148c2ecf20Sopenharmony_ci	u8 noa_num, index , i, noa_index = 0;
7158c2ecf20Sopenharmony_ci	bool find_p2p_ie = false , find_p2p_ps_ie = false;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	pos = (u8 *)mgmt->u.beacon.variable;
7188c2ecf20Sopenharmony_ci	end = data + len;
7198c2ecf20Sopenharmony_ci	ie = NULL;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	while (pos + 1 < end) {
7228c2ecf20Sopenharmony_ci		if (pos + 2 + pos[1] > end)
7238c2ecf20Sopenharmony_ci			return;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci		if (pos[0] == 221 && pos[1] > 4) {
7268c2ecf20Sopenharmony_ci			if (memcmp(&pos[2], p2p_oui_ie_type, 4) == 0) {
7278c2ecf20Sopenharmony_ci				ie = pos + 2+4;
7288c2ecf20Sopenharmony_ci				break;
7298c2ecf20Sopenharmony_ci			}
7308c2ecf20Sopenharmony_ci		}
7318c2ecf20Sopenharmony_ci		pos += 2 + pos[1];
7328c2ecf20Sopenharmony_ci	}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	if (ie == NULL)
7358c2ecf20Sopenharmony_ci		return;
7368c2ecf20Sopenharmony_ci	find_p2p_ie = true;
7378c2ecf20Sopenharmony_ci	/*to find noa ie*/
7388c2ecf20Sopenharmony_ci	while (ie + 1 < end) {
7398c2ecf20Sopenharmony_ci		noa_len = le16_to_cpu(*((__le16 *)&ie[1]));
7408c2ecf20Sopenharmony_ci		if (ie + 3 + ie[1] > end)
7418c2ecf20Sopenharmony_ci			return;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci		if (ie[0] == 12) {
7448c2ecf20Sopenharmony_ci			find_p2p_ps_ie = true;
7458c2ecf20Sopenharmony_ci			if ((noa_len - 2) % 13 != 0) {
7468c2ecf20Sopenharmony_ci				rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
7478c2ecf20Sopenharmony_ci					"P2P notice of absence: invalid length.%d\n",
7488c2ecf20Sopenharmony_ci					noa_len);
7498c2ecf20Sopenharmony_ci				return;
7508c2ecf20Sopenharmony_ci			} else {
7518c2ecf20Sopenharmony_ci				noa_num = (noa_len - 2) / 13;
7528c2ecf20Sopenharmony_ci				if (noa_num > P2P_MAX_NOA_NUM)
7538c2ecf20Sopenharmony_ci					noa_num = P2P_MAX_NOA_NUM;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci			}
7568c2ecf20Sopenharmony_ci			noa_index = ie[3];
7578c2ecf20Sopenharmony_ci			if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode ==
7588c2ecf20Sopenharmony_ci			    P2P_PS_NONE || noa_index != p2pinfo->noa_index) {
7598c2ecf20Sopenharmony_ci				rtl_dbg(rtlpriv, COMP_FW, DBG_LOUD,
7608c2ecf20Sopenharmony_ci					"update NOA ie.\n");
7618c2ecf20Sopenharmony_ci				p2pinfo->noa_index = noa_index;
7628c2ecf20Sopenharmony_ci				p2pinfo->opp_ps = (ie[4] >> 7);
7638c2ecf20Sopenharmony_ci				p2pinfo->ctwindow = ie[4] & 0x7F;
7648c2ecf20Sopenharmony_ci				p2pinfo->noa_num = noa_num;
7658c2ecf20Sopenharmony_ci				index = 5;
7668c2ecf20Sopenharmony_ci				for (i = 0; i < noa_num; i++) {
7678c2ecf20Sopenharmony_ci					p2pinfo->noa_count_type[i] =
7688c2ecf20Sopenharmony_ci					 *(u8 *)(ie + index);
7698c2ecf20Sopenharmony_ci					index += 1;
7708c2ecf20Sopenharmony_ci					p2pinfo->noa_duration[i] =
7718c2ecf20Sopenharmony_ci					 le32_to_cpu(*(__le32 *)(ie + index));
7728c2ecf20Sopenharmony_ci					index += 4;
7738c2ecf20Sopenharmony_ci					p2pinfo->noa_interval[i] =
7748c2ecf20Sopenharmony_ci					 le32_to_cpu(*(__le32 *)(ie + index));
7758c2ecf20Sopenharmony_ci					index += 4;
7768c2ecf20Sopenharmony_ci					p2pinfo->noa_start_time[i] =
7778c2ecf20Sopenharmony_ci					 le32_to_cpu(*(__le32 *)(ie + index));
7788c2ecf20Sopenharmony_ci					index += 4;
7798c2ecf20Sopenharmony_ci				}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci				if (p2pinfo->opp_ps == 1) {
7828c2ecf20Sopenharmony_ci					p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW;
7838c2ecf20Sopenharmony_ci					/* Driver should wait LPS entering
7848c2ecf20Sopenharmony_ci					 * CTWindow
7858c2ecf20Sopenharmony_ci					 */
7868c2ecf20Sopenharmony_ci					if (rtlpriv->psc.fw_current_inpsmode)
7878c2ecf20Sopenharmony_ci						rtl_p2p_ps_cmd(hw,
7888c2ecf20Sopenharmony_ci							       P2P_PS_ENABLE);
7898c2ecf20Sopenharmony_ci				} else if (p2pinfo->noa_num > 0) {
7908c2ecf20Sopenharmony_ci					p2pinfo->p2p_ps_mode = P2P_PS_NOA;
7918c2ecf20Sopenharmony_ci					rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE);
7928c2ecf20Sopenharmony_ci				} else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
7938c2ecf20Sopenharmony_ci					rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
7948c2ecf20Sopenharmony_ci				}
7958c2ecf20Sopenharmony_ci			}
7968c2ecf20Sopenharmony_ci			break;
7978c2ecf20Sopenharmony_ci		}
7988c2ecf20Sopenharmony_ci		ie += 3 + noa_len;
7998c2ecf20Sopenharmony_ci	}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	if (find_p2p_ie == true) {
8028c2ecf20Sopenharmony_ci		if ((p2pinfo->p2p_ps_mode > P2P_PS_NONE) &&
8038c2ecf20Sopenharmony_ci		    (find_p2p_ps_ie == false))
8048c2ecf20Sopenharmony_ci			rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
8058c2ecf20Sopenharmony_ci	}
8068c2ecf20Sopenharmony_ci}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_cistatic void rtl_p2p_action_ie(struct ieee80211_hw *hw, void *data,
8098c2ecf20Sopenharmony_ci			      unsigned int len)
8108c2ecf20Sopenharmony_ci{
8118c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
8128c2ecf20Sopenharmony_ci	struct ieee80211_mgmt *mgmt = data;
8138c2ecf20Sopenharmony_ci	struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info);
8148c2ecf20Sopenharmony_ci	u8 noa_num, index , i , noa_index = 0;
8158c2ecf20Sopenharmony_ci	u8 *pos, *end, *ie;
8168c2ecf20Sopenharmony_ci	u16 noa_len;
8178c2ecf20Sopenharmony_ci	static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09};
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	pos = (u8 *)&mgmt->u.action.category;
8208c2ecf20Sopenharmony_ci	end = data + len;
8218c2ecf20Sopenharmony_ci	ie = NULL;
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	if (pos[0] == 0x7f) {
8248c2ecf20Sopenharmony_ci		if (memcmp(&pos[1], p2p_oui_ie_type, 4) == 0)
8258c2ecf20Sopenharmony_ci			ie = pos + 3+4;
8268c2ecf20Sopenharmony_ci	}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	if (ie == NULL)
8298c2ecf20Sopenharmony_ci		return;
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	rtl_dbg(rtlpriv, COMP_FW, DBG_LOUD, "action frame find P2P IE.\n");
8328c2ecf20Sopenharmony_ci	/*to find noa ie*/
8338c2ecf20Sopenharmony_ci	while (ie + 1 < end) {
8348c2ecf20Sopenharmony_ci		noa_len = le16_to_cpu(*(__le16 *)&ie[1]);
8358c2ecf20Sopenharmony_ci		if (ie + 3 + ie[1] > end)
8368c2ecf20Sopenharmony_ci			return;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci		if (ie[0] == 12) {
8398c2ecf20Sopenharmony_ci			rtl_dbg(rtlpriv, COMP_FW, DBG_LOUD, "find NOA IE.\n");
8408c2ecf20Sopenharmony_ci			RT_PRINT_DATA(rtlpriv, COMP_FW, DBG_LOUD, "noa ie ",
8418c2ecf20Sopenharmony_ci				      ie, noa_len);
8428c2ecf20Sopenharmony_ci			if ((noa_len - 2) % 13 != 0) {
8438c2ecf20Sopenharmony_ci				rtl_dbg(rtlpriv, COMP_FW, DBG_LOUD,
8448c2ecf20Sopenharmony_ci					"P2P notice of absence: invalid length.%d\n",
8458c2ecf20Sopenharmony_ci					noa_len);
8468c2ecf20Sopenharmony_ci				return;
8478c2ecf20Sopenharmony_ci			} else {
8488c2ecf20Sopenharmony_ci				noa_num = (noa_len - 2) / 13;
8498c2ecf20Sopenharmony_ci				if (noa_num > P2P_MAX_NOA_NUM)
8508c2ecf20Sopenharmony_ci					noa_num = P2P_MAX_NOA_NUM;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci			}
8538c2ecf20Sopenharmony_ci			noa_index = ie[3];
8548c2ecf20Sopenharmony_ci			if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode ==
8558c2ecf20Sopenharmony_ci			    P2P_PS_NONE || noa_index != p2pinfo->noa_index) {
8568c2ecf20Sopenharmony_ci				p2pinfo->noa_index = noa_index;
8578c2ecf20Sopenharmony_ci				p2pinfo->opp_ps = (ie[4] >> 7);
8588c2ecf20Sopenharmony_ci				p2pinfo->ctwindow = ie[4] & 0x7F;
8598c2ecf20Sopenharmony_ci				p2pinfo->noa_num = noa_num;
8608c2ecf20Sopenharmony_ci				index = 5;
8618c2ecf20Sopenharmony_ci				for (i = 0; i < noa_num; i++) {
8628c2ecf20Sopenharmony_ci					p2pinfo->noa_count_type[i] =
8638c2ecf20Sopenharmony_ci					 *(u8 *)(ie + index);
8648c2ecf20Sopenharmony_ci					index += 1;
8658c2ecf20Sopenharmony_ci					p2pinfo->noa_duration[i] =
8668c2ecf20Sopenharmony_ci					 le32_to_cpu(*(__le32 *)(ie + index));
8678c2ecf20Sopenharmony_ci					index += 4;
8688c2ecf20Sopenharmony_ci					p2pinfo->noa_interval[i] =
8698c2ecf20Sopenharmony_ci					 le32_to_cpu(*(__le32 *)(ie + index));
8708c2ecf20Sopenharmony_ci					index += 4;
8718c2ecf20Sopenharmony_ci					p2pinfo->noa_start_time[i] =
8728c2ecf20Sopenharmony_ci					 le32_to_cpu(*(__le32 *)(ie + index));
8738c2ecf20Sopenharmony_ci					index += 4;
8748c2ecf20Sopenharmony_ci				}
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci				if (p2pinfo->opp_ps == 1) {
8778c2ecf20Sopenharmony_ci					p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW;
8788c2ecf20Sopenharmony_ci					/* Driver should wait LPS entering
8798c2ecf20Sopenharmony_ci					 * CTWindow
8808c2ecf20Sopenharmony_ci					 */
8818c2ecf20Sopenharmony_ci					if (rtlpriv->psc.fw_current_inpsmode)
8828c2ecf20Sopenharmony_ci						rtl_p2p_ps_cmd(hw,
8838c2ecf20Sopenharmony_ci							       P2P_PS_ENABLE);
8848c2ecf20Sopenharmony_ci				} else if (p2pinfo->noa_num > 0) {
8858c2ecf20Sopenharmony_ci					p2pinfo->p2p_ps_mode = P2P_PS_NOA;
8868c2ecf20Sopenharmony_ci					rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE);
8878c2ecf20Sopenharmony_ci				} else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
8888c2ecf20Sopenharmony_ci					rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
8898c2ecf20Sopenharmony_ci				}
8908c2ecf20Sopenharmony_ci			}
8918c2ecf20Sopenharmony_ci			break;
8928c2ecf20Sopenharmony_ci		}
8938c2ecf20Sopenharmony_ci		ie += 3 + noa_len;
8948c2ecf20Sopenharmony_ci	}
8958c2ecf20Sopenharmony_ci}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_civoid rtl_p2p_ps_cmd(struct ieee80211_hw *hw , u8 p2p_ps_state)
8988c2ecf20Sopenharmony_ci{
8998c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
9008c2ecf20Sopenharmony_ci	struct rtl_ps_ctl *rtlps = rtl_psc(rtl_priv(hw));
9018c2ecf20Sopenharmony_ci	struct rtl_p2p_ps_info  *p2pinfo = &(rtlpriv->psc.p2p_ps_info);
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	rtl_dbg(rtlpriv, COMP_FW, DBG_LOUD, " p2p state %x\n", p2p_ps_state);
9048c2ecf20Sopenharmony_ci	switch (p2p_ps_state) {
9058c2ecf20Sopenharmony_ci	case P2P_PS_DISABLE:
9068c2ecf20Sopenharmony_ci		p2pinfo->p2p_ps_state = p2p_ps_state;
9078c2ecf20Sopenharmony_ci		rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
9088c2ecf20Sopenharmony_ci					      &p2p_ps_state);
9098c2ecf20Sopenharmony_ci		p2pinfo->noa_index = 0;
9108c2ecf20Sopenharmony_ci		p2pinfo->ctwindow = 0;
9118c2ecf20Sopenharmony_ci		p2pinfo->opp_ps = 0;
9128c2ecf20Sopenharmony_ci		p2pinfo->noa_num = 0;
9138c2ecf20Sopenharmony_ci		p2pinfo->p2p_ps_mode = P2P_PS_NONE;
9148c2ecf20Sopenharmony_ci		if (rtlps->fw_current_inpsmode) {
9158c2ecf20Sopenharmony_ci			if (rtlps->smart_ps == 0) {
9168c2ecf20Sopenharmony_ci				rtlps->smart_ps = 2;
9178c2ecf20Sopenharmony_ci				rtlpriv->cfg->ops->set_hw_reg(hw,
9188c2ecf20Sopenharmony_ci					 HW_VAR_H2C_FW_PWRMODE,
9198c2ecf20Sopenharmony_ci					 &rtlps->pwr_mode);
9208c2ecf20Sopenharmony_ci			}
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci		}
9238c2ecf20Sopenharmony_ci		break;
9248c2ecf20Sopenharmony_ci	case P2P_PS_ENABLE:
9258c2ecf20Sopenharmony_ci		if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
9268c2ecf20Sopenharmony_ci			p2pinfo->p2p_ps_state = p2p_ps_state;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci			if (p2pinfo->ctwindow > 0) {
9298c2ecf20Sopenharmony_ci				if (rtlps->smart_ps != 0) {
9308c2ecf20Sopenharmony_ci					rtlps->smart_ps = 0;
9318c2ecf20Sopenharmony_ci					rtlpriv->cfg->ops->set_hw_reg(hw,
9328c2ecf20Sopenharmony_ci						 HW_VAR_H2C_FW_PWRMODE,
9338c2ecf20Sopenharmony_ci						 &rtlps->pwr_mode);
9348c2ecf20Sopenharmony_ci				}
9358c2ecf20Sopenharmony_ci			}
9368c2ecf20Sopenharmony_ci			rtlpriv->cfg->ops->set_hw_reg(hw,
9378c2ecf20Sopenharmony_ci				 HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
9388c2ecf20Sopenharmony_ci				 &p2p_ps_state);
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci		}
9418c2ecf20Sopenharmony_ci		break;
9428c2ecf20Sopenharmony_ci	case P2P_PS_SCAN:
9438c2ecf20Sopenharmony_ci	case P2P_PS_SCAN_DONE:
9448c2ecf20Sopenharmony_ci	case P2P_PS_ALLSTASLEEP:
9458c2ecf20Sopenharmony_ci		if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
9468c2ecf20Sopenharmony_ci			p2pinfo->p2p_ps_state = p2p_ps_state;
9478c2ecf20Sopenharmony_ci			rtlpriv->cfg->ops->set_hw_reg(hw,
9488c2ecf20Sopenharmony_ci				 HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
9498c2ecf20Sopenharmony_ci				 &p2p_ps_state);
9508c2ecf20Sopenharmony_ci		}
9518c2ecf20Sopenharmony_ci		break;
9528c2ecf20Sopenharmony_ci	default:
9538c2ecf20Sopenharmony_ci		break;
9548c2ecf20Sopenharmony_ci	}
9558c2ecf20Sopenharmony_ci	rtl_dbg(rtlpriv, COMP_FW, DBG_LOUD,
9568c2ecf20Sopenharmony_ci		"ctwindow %x oppps %x\n",
9578c2ecf20Sopenharmony_ci		p2pinfo->ctwindow, p2pinfo->opp_ps);
9588c2ecf20Sopenharmony_ci	rtl_dbg(rtlpriv, COMP_FW, DBG_LOUD,
9598c2ecf20Sopenharmony_ci		"count %x duration %x index %x interval %x start time %x noa num %x\n",
9608c2ecf20Sopenharmony_ci		p2pinfo->noa_count_type[0],
9618c2ecf20Sopenharmony_ci		p2pinfo->noa_duration[0],
9628c2ecf20Sopenharmony_ci		p2pinfo->noa_index,
9638c2ecf20Sopenharmony_ci		p2pinfo->noa_interval[0],
9648c2ecf20Sopenharmony_ci		p2pinfo->noa_start_time[0],
9658c2ecf20Sopenharmony_ci		p2pinfo->noa_num);
9668c2ecf20Sopenharmony_ci	rtl_dbg(rtlpriv, COMP_FW, DBG_LOUD, "end\n");
9678c2ecf20Sopenharmony_ci}
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_civoid rtl_p2p_info(struct ieee80211_hw *hw, void *data, unsigned int len)
9708c2ecf20Sopenharmony_ci{
9718c2ecf20Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
9728c2ecf20Sopenharmony_ci	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
9738c2ecf20Sopenharmony_ci	struct ieee80211_hdr *hdr = data;
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	if (!mac->p2p)
9768c2ecf20Sopenharmony_ci		return;
9778c2ecf20Sopenharmony_ci	if (mac->link_state != MAC80211_LINKED)
9788c2ecf20Sopenharmony_ci		return;
9798c2ecf20Sopenharmony_ci	/* min. beacon length + FCS_LEN */
9808c2ecf20Sopenharmony_ci	if (len <= 40 + FCS_LEN)
9818c2ecf20Sopenharmony_ci		return;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	/* and only beacons from the associated BSSID, please */
9848c2ecf20Sopenharmony_ci	if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid))
9858c2ecf20Sopenharmony_ci		return;
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	/* check if this really is a beacon */
9888c2ecf20Sopenharmony_ci	if (!(ieee80211_is_beacon(hdr->frame_control) ||
9898c2ecf20Sopenharmony_ci	      ieee80211_is_probe_resp(hdr->frame_control) ||
9908c2ecf20Sopenharmony_ci	      ieee80211_is_action(hdr->frame_control)))
9918c2ecf20Sopenharmony_ci		return;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	if (ieee80211_is_action(hdr->frame_control))
9948c2ecf20Sopenharmony_ci		rtl_p2p_action_ie(hw , data , len - FCS_LEN);
9958c2ecf20Sopenharmony_ci	else
9968c2ecf20Sopenharmony_ci		rtl_p2p_noa_ie(hw , data , len - FCS_LEN);
9978c2ecf20Sopenharmony_ci}
9988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtl_p2p_info);
999