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