1f1d2b4d3SLarry Finger /****************************************************************************** 2f1d2b4d3SLarry Finger * 3f1d2b4d3SLarry Finger * Copyright(c) 2009-2012 Realtek Corporation. 4f1d2b4d3SLarry Finger * 5f1d2b4d3SLarry Finger * This program is free software; you can redistribute it and/or modify it 6f1d2b4d3SLarry Finger * under the terms of version 2 of the GNU General Public License as 7f1d2b4d3SLarry Finger * published by the Free Software Foundation. 8f1d2b4d3SLarry Finger * 9f1d2b4d3SLarry Finger * This program is distributed in the hope that it will be useful, but WITHOUT 10f1d2b4d3SLarry Finger * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11f1d2b4d3SLarry Finger * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12f1d2b4d3SLarry Finger * more details. 13f1d2b4d3SLarry Finger * 14f1d2b4d3SLarry Finger * The full GNU General Public License is included in this distribution in the 15f1d2b4d3SLarry Finger * file called LICENSE. 16f1d2b4d3SLarry Finger * 17f1d2b4d3SLarry Finger * Contact Information: 18f1d2b4d3SLarry Finger * wlanfae <wlanfae@realtek.com> 19f1d2b4d3SLarry Finger * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, 20f1d2b4d3SLarry Finger * Hsinchu 300, Taiwan. 21f1d2b4d3SLarry Finger * 22f1d2b4d3SLarry Finger * Larry Finger <Larry.Finger@lwfinger.net> 23f1d2b4d3SLarry Finger * 24f1d2b4d3SLarry Finger *****************************************************************************/ 25f1d2b4d3SLarry Finger 26f1d2b4d3SLarry Finger #include "wifi.h" 27f1d2b4d3SLarry Finger #include "base.h" 28f1d2b4d3SLarry Finger #include "ps.h" 29f1d2b4d3SLarry Finger #include <linux/export.h> 30f1d2b4d3SLarry Finger #include "btcoexist/rtl_btc.h" 31f1d2b4d3SLarry Finger 32f1d2b4d3SLarry Finger bool rtl_ps_enable_nic(struct ieee80211_hw *hw) 33f1d2b4d3SLarry Finger { 34f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 35f1d2b4d3SLarry Finger struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); 36f1d2b4d3SLarry Finger struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); 378d0d43e3SPing-Ke Shih struct rtl_mac *rtlmac = rtl_mac(rtl_priv(hw)); 38f1d2b4d3SLarry Finger 39f1d2b4d3SLarry Finger /*<1> reset trx ring */ 40f1d2b4d3SLarry Finger if (rtlhal->interface == INTF_PCI) 41f1d2b4d3SLarry Finger rtlpriv->intf_ops->reset_trx_ring(hw); 42f1d2b4d3SLarry Finger 43f1d2b4d3SLarry Finger if (is_hal_stop(rtlhal)) 44f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, 45f1d2b4d3SLarry Finger "Driver is already down!\n"); 46f1d2b4d3SLarry Finger 47f1d2b4d3SLarry Finger /*<2> Enable Adapter */ 48f1d2b4d3SLarry Finger if (rtlpriv->cfg->ops->hw_init(hw)) 49f1d2b4d3SLarry Finger return false; 508d0d43e3SPing-Ke Shih rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RETRY_LIMIT, 518d0d43e3SPing-Ke Shih &rtlmac->retry_long); 52f1d2b4d3SLarry Finger RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); 53f1d2b4d3SLarry Finger 54f1d2b4d3SLarry Finger /*<3> Enable Interrupt */ 55f1d2b4d3SLarry Finger rtlpriv->cfg->ops->enable_interrupt(hw); 56f1d2b4d3SLarry Finger 57f1d2b4d3SLarry Finger /*<enable timer> */ 587c51d17cSKees Cook rtl_watch_dog_timer_callback(&rtlpriv->works.watchdog_timer); 59f1d2b4d3SLarry Finger 60f1d2b4d3SLarry Finger return true; 61f1d2b4d3SLarry Finger } 62f1d2b4d3SLarry Finger EXPORT_SYMBOL(rtl_ps_enable_nic); 63f1d2b4d3SLarry Finger 64f1d2b4d3SLarry Finger bool rtl_ps_disable_nic(struct ieee80211_hw *hw) 65f1d2b4d3SLarry Finger { 66f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 67f1d2b4d3SLarry Finger 68f1d2b4d3SLarry Finger /*<1> Stop all timer */ 69f1d2b4d3SLarry Finger rtl_deinit_deferred_work(hw); 70f1d2b4d3SLarry Finger 71f1d2b4d3SLarry Finger /*<2> Disable Interrupt */ 72f1d2b4d3SLarry Finger rtlpriv->cfg->ops->disable_interrupt(hw); 73f1d2b4d3SLarry Finger tasklet_kill(&rtlpriv->works.irq_tasklet); 74f1d2b4d3SLarry Finger 75f1d2b4d3SLarry Finger /*<3> Disable Adapter */ 76f1d2b4d3SLarry Finger rtlpriv->cfg->ops->hw_disable(hw); 77f1d2b4d3SLarry Finger 78f1d2b4d3SLarry Finger return true; 79f1d2b4d3SLarry Finger } 80f1d2b4d3SLarry Finger EXPORT_SYMBOL(rtl_ps_disable_nic); 81f1d2b4d3SLarry Finger 8230462b51SLarry Finger static bool rtl_ps_set_rf_state(struct ieee80211_hw *hw, 83f1d2b4d3SLarry Finger enum rf_pwrstate state_toset, 8430462b51SLarry Finger u32 changesource) 85f1d2b4d3SLarry Finger { 86f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 87f1d2b4d3SLarry Finger struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); 88f1d2b4d3SLarry Finger enum rf_pwrstate rtstate; 89f1d2b4d3SLarry Finger bool actionallowed = false; 90f1d2b4d3SLarry Finger u16 rfwait_cnt = 0; 91f1d2b4d3SLarry Finger 92f1d2b4d3SLarry Finger /*Only one thread can change 93f1d2b4d3SLarry Finger *the RF state at one time, and others 94f1d2b4d3SLarry Finger *should wait to be executed. 95f1d2b4d3SLarry Finger */ 96f1d2b4d3SLarry Finger while (true) { 97f1d2b4d3SLarry Finger spin_lock(&rtlpriv->locks.rf_ps_lock); 98f1d2b4d3SLarry Finger if (ppsc->rfchange_inprogress) { 99f1d2b4d3SLarry Finger spin_unlock(&rtlpriv->locks.rf_ps_lock); 100f1d2b4d3SLarry Finger 101f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, 102f1d2b4d3SLarry Finger "RF Change in progress! Wait to set..state_toset(%d).\n", 103f1d2b4d3SLarry Finger state_toset); 104f1d2b4d3SLarry Finger 105f1d2b4d3SLarry Finger /* Set RF after the previous action is done. */ 106f1d2b4d3SLarry Finger while (ppsc->rfchange_inprogress) { 107f1d2b4d3SLarry Finger rfwait_cnt++; 108f1d2b4d3SLarry Finger mdelay(1); 109f1d2b4d3SLarry Finger /*Wait too long, return false to avoid 110f1d2b4d3SLarry Finger *to be stuck here. 111f1d2b4d3SLarry Finger */ 112f1d2b4d3SLarry Finger if (rfwait_cnt > 100) 113f1d2b4d3SLarry Finger return false; 114f1d2b4d3SLarry Finger } 115f1d2b4d3SLarry Finger } else { 116f1d2b4d3SLarry Finger ppsc->rfchange_inprogress = true; 117f1d2b4d3SLarry Finger spin_unlock(&rtlpriv->locks.rf_ps_lock); 118f1d2b4d3SLarry Finger break; 119f1d2b4d3SLarry Finger } 120f1d2b4d3SLarry Finger } 121f1d2b4d3SLarry Finger 122f1d2b4d3SLarry Finger rtstate = ppsc->rfpwr_state; 123f1d2b4d3SLarry Finger 124f1d2b4d3SLarry Finger switch (state_toset) { 125f1d2b4d3SLarry Finger case ERFON: 126f1d2b4d3SLarry Finger ppsc->rfoff_reason &= (~changesource); 127f1d2b4d3SLarry Finger 128f1d2b4d3SLarry Finger if ((changesource == RF_CHANGE_BY_HW) && 129f1d2b4d3SLarry Finger (ppsc->hwradiooff)) { 130f1d2b4d3SLarry Finger ppsc->hwradiooff = false; 131f1d2b4d3SLarry Finger } 132f1d2b4d3SLarry Finger 133f1d2b4d3SLarry Finger if (!ppsc->rfoff_reason) { 134f1d2b4d3SLarry Finger ppsc->rfoff_reason = 0; 135f1d2b4d3SLarry Finger actionallowed = true; 136f1d2b4d3SLarry Finger } 137f1d2b4d3SLarry Finger 138f1d2b4d3SLarry Finger break; 139f1d2b4d3SLarry Finger 140f1d2b4d3SLarry Finger case ERFOFF: 141f1d2b4d3SLarry Finger 142f1d2b4d3SLarry Finger if ((changesource == RF_CHANGE_BY_HW) && !ppsc->hwradiooff) { 143f1d2b4d3SLarry Finger ppsc->hwradiooff = true; 144f1d2b4d3SLarry Finger } 145f1d2b4d3SLarry Finger 146f1d2b4d3SLarry Finger ppsc->rfoff_reason |= changesource; 147f1d2b4d3SLarry Finger actionallowed = true; 148f1d2b4d3SLarry Finger break; 149f1d2b4d3SLarry Finger 150f1d2b4d3SLarry Finger case ERFSLEEP: 151f1d2b4d3SLarry Finger ppsc->rfoff_reason |= changesource; 152f1d2b4d3SLarry Finger actionallowed = true; 153f1d2b4d3SLarry Finger break; 154f1d2b4d3SLarry Finger 155f1d2b4d3SLarry Finger default: 156b03d968bSLarry Finger pr_err("switch case %#x not processed\n", state_toset); 157f1d2b4d3SLarry Finger break; 158f1d2b4d3SLarry Finger } 159f1d2b4d3SLarry Finger 160f1d2b4d3SLarry Finger if (actionallowed) 161f1d2b4d3SLarry Finger rtlpriv->cfg->ops->set_rf_power_state(hw, state_toset); 162f1d2b4d3SLarry Finger 163f1d2b4d3SLarry Finger spin_lock(&rtlpriv->locks.rf_ps_lock); 164f1d2b4d3SLarry Finger ppsc->rfchange_inprogress = false; 165f1d2b4d3SLarry Finger spin_unlock(&rtlpriv->locks.rf_ps_lock); 166f1d2b4d3SLarry Finger 167f1d2b4d3SLarry Finger return actionallowed; 168f1d2b4d3SLarry Finger } 169f1d2b4d3SLarry Finger 170f1d2b4d3SLarry Finger static void _rtl_ps_inactive_ps(struct ieee80211_hw *hw) 171f1d2b4d3SLarry Finger { 172f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 173f1d2b4d3SLarry Finger struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); 174f1d2b4d3SLarry Finger struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); 175f1d2b4d3SLarry Finger 176f1d2b4d3SLarry Finger ppsc->swrf_processing = true; 177f1d2b4d3SLarry Finger 178f1d2b4d3SLarry Finger if (ppsc->inactive_pwrstate == ERFON && 179f1d2b4d3SLarry Finger rtlhal->interface == INTF_PCI) { 180f1d2b4d3SLarry Finger if ((ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM) && 181f1d2b4d3SLarry Finger RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) && 182f1d2b4d3SLarry Finger rtlhal->interface == INTF_PCI) { 183f1d2b4d3SLarry Finger rtlpriv->intf_ops->disable_aspm(hw); 184f1d2b4d3SLarry Finger RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); 185f1d2b4d3SLarry Finger } 186f1d2b4d3SLarry Finger } 187f1d2b4d3SLarry Finger 188f1d2b4d3SLarry Finger rtl_ps_set_rf_state(hw, ppsc->inactive_pwrstate, 18930462b51SLarry Finger RF_CHANGE_BY_IPS); 190f1d2b4d3SLarry Finger 191f1d2b4d3SLarry Finger if (ppsc->inactive_pwrstate == ERFOFF && 192f1d2b4d3SLarry Finger rtlhal->interface == INTF_PCI) { 193f1d2b4d3SLarry Finger if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM && 194f1d2b4d3SLarry Finger !RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) { 195f1d2b4d3SLarry Finger rtlpriv->intf_ops->enable_aspm(hw); 196f1d2b4d3SLarry Finger RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); 197f1d2b4d3SLarry Finger } 198f1d2b4d3SLarry Finger } 199f1d2b4d3SLarry Finger 200f1d2b4d3SLarry Finger ppsc->swrf_processing = false; 201f1d2b4d3SLarry Finger } 202f1d2b4d3SLarry Finger 203f1d2b4d3SLarry Finger void rtl_ips_nic_off_wq_callback(void *data) 204f1d2b4d3SLarry Finger { 205f1d2b4d3SLarry Finger struct rtl_works *rtlworks = 206f1d2b4d3SLarry Finger container_of_dwork_rtl(data, struct rtl_works, ips_nic_off_wq); 207f1d2b4d3SLarry Finger struct ieee80211_hw *hw = rtlworks->hw; 208f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 209f1d2b4d3SLarry Finger struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); 210f1d2b4d3SLarry Finger struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); 211f1d2b4d3SLarry Finger struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); 212f1d2b4d3SLarry Finger enum rf_pwrstate rtstate; 213f1d2b4d3SLarry Finger 214f1d2b4d3SLarry Finger if (mac->opmode != NL80211_IFTYPE_STATION) { 215f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, 216f1d2b4d3SLarry Finger "not station return\n"); 217f1d2b4d3SLarry Finger return; 218f1d2b4d3SLarry Finger } 219f1d2b4d3SLarry Finger 220f1d2b4d3SLarry Finger if (mac->p2p_in_use) 221f1d2b4d3SLarry Finger return; 222f1d2b4d3SLarry Finger 223f1d2b4d3SLarry Finger if (mac->link_state > MAC80211_NOLINK) 224f1d2b4d3SLarry Finger return; 225f1d2b4d3SLarry Finger 226f1d2b4d3SLarry Finger if (is_hal_stop(rtlhal)) 227f1d2b4d3SLarry Finger return; 228f1d2b4d3SLarry Finger 229f1d2b4d3SLarry Finger if (rtlpriv->sec.being_setkey) 230f1d2b4d3SLarry Finger return; 231f1d2b4d3SLarry Finger 232f1d2b4d3SLarry Finger if (rtlpriv->cfg->ops->bt_coex_off_before_lps) 233f1d2b4d3SLarry Finger rtlpriv->cfg->ops->bt_coex_off_before_lps(hw); 234f1d2b4d3SLarry Finger 235f1d2b4d3SLarry Finger if (ppsc->inactiveps) { 236f1d2b4d3SLarry Finger rtstate = ppsc->rfpwr_state; 237f1d2b4d3SLarry Finger 238f1d2b4d3SLarry Finger /* 239f1d2b4d3SLarry Finger *Do not enter IPS in the following conditions: 240f1d2b4d3SLarry Finger *(1) RF is already OFF or Sleep 241f1d2b4d3SLarry Finger *(2) swrf_processing (indicates the IPS is still under going) 242f1d2b4d3SLarry Finger *(3) Connectted (only disconnected can trigger IPS) 243f1d2b4d3SLarry Finger *(4) IBSS (send Beacon) 244f1d2b4d3SLarry Finger *(5) AP mode (send Beacon) 245f1d2b4d3SLarry Finger *(6) monitor mode (rcv packet) 246f1d2b4d3SLarry Finger */ 247f1d2b4d3SLarry Finger 248f1d2b4d3SLarry Finger if (rtstate == ERFON && 249f1d2b4d3SLarry Finger !ppsc->swrf_processing && 250f1d2b4d3SLarry Finger (mac->link_state == MAC80211_NOLINK) && 251f1d2b4d3SLarry Finger !mac->act_scanning) { 252f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, 253f1d2b4d3SLarry Finger "IPSEnter(): Turn off RF\n"); 254f1d2b4d3SLarry Finger 255f1d2b4d3SLarry Finger ppsc->inactive_pwrstate = ERFOFF; 256f1d2b4d3SLarry Finger ppsc->in_powersavemode = true; 257f1d2b4d3SLarry Finger 258f1d2b4d3SLarry Finger /* call before RF off */ 259f1d2b4d3SLarry Finger if (rtlpriv->cfg->ops->get_btc_status()) 260f1d2b4d3SLarry Finger rtlpriv->btcoexist.btc_ops->btc_ips_notify(rtlpriv, 261f1d2b4d3SLarry Finger ppsc->inactive_pwrstate); 262f1d2b4d3SLarry Finger 263f1d2b4d3SLarry Finger /*rtl_pci_reset_trx_ring(hw); */ 264f1d2b4d3SLarry Finger _rtl_ps_inactive_ps(hw); 265f1d2b4d3SLarry Finger } 266f1d2b4d3SLarry Finger } 267f1d2b4d3SLarry Finger } 268f1d2b4d3SLarry Finger 269f1d2b4d3SLarry Finger void rtl_ips_nic_off(struct ieee80211_hw *hw) 270f1d2b4d3SLarry Finger { 271f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 272f1d2b4d3SLarry Finger 273f1d2b4d3SLarry Finger /* because when link with ap, mac80211 will ask us 274f1d2b4d3SLarry Finger * to disable nic quickly after scan before linking, 275f1d2b4d3SLarry Finger * this will cause link failed, so we delay 100ms here 276f1d2b4d3SLarry Finger */ 277f1d2b4d3SLarry Finger queue_delayed_work(rtlpriv->works.rtl_wq, 278f1d2b4d3SLarry Finger &rtlpriv->works.ips_nic_off_wq, MSECS(100)); 279f1d2b4d3SLarry Finger } 280f1d2b4d3SLarry Finger 281f1d2b4d3SLarry Finger /* NOTICE: any opmode should exc nic_on, or disable without 282f1d2b4d3SLarry Finger * nic_on may something wrong, like adhoc TP 283f1d2b4d3SLarry Finger */ 284f1d2b4d3SLarry Finger void rtl_ips_nic_on(struct ieee80211_hw *hw) 285f1d2b4d3SLarry Finger { 286f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 287f1d2b4d3SLarry Finger struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); 288f1d2b4d3SLarry Finger enum rf_pwrstate rtstate; 289f1d2b4d3SLarry Finger 290f1d2b4d3SLarry Finger cancel_delayed_work(&rtlpriv->works.ips_nic_off_wq); 291f1d2b4d3SLarry Finger 292*a3fa3669SPing-Ke Shih mutex_lock(&rtlpriv->locks.ips_mutex); 293f1d2b4d3SLarry Finger if (ppsc->inactiveps) { 294f1d2b4d3SLarry Finger rtstate = ppsc->rfpwr_state; 295f1d2b4d3SLarry Finger 296f1d2b4d3SLarry Finger if (rtstate != ERFON && 297f1d2b4d3SLarry Finger !ppsc->swrf_processing && 298f1d2b4d3SLarry Finger ppsc->rfoff_reason <= RF_CHANGE_BY_IPS) { 299f1d2b4d3SLarry Finger 300f1d2b4d3SLarry Finger ppsc->inactive_pwrstate = ERFON; 301f1d2b4d3SLarry Finger ppsc->in_powersavemode = false; 302f1d2b4d3SLarry Finger _rtl_ps_inactive_ps(hw); 303f1d2b4d3SLarry Finger /* call after RF on */ 304f1d2b4d3SLarry Finger if (rtlpriv->cfg->ops->get_btc_status()) 305f1d2b4d3SLarry Finger rtlpriv->btcoexist.btc_ops->btc_ips_notify(rtlpriv, 306f1d2b4d3SLarry Finger ppsc->inactive_pwrstate); 307f1d2b4d3SLarry Finger } 308f1d2b4d3SLarry Finger } 309*a3fa3669SPing-Ke Shih mutex_unlock(&rtlpriv->locks.ips_mutex); 310f1d2b4d3SLarry Finger } 311f1d2b4d3SLarry Finger EXPORT_SYMBOL_GPL(rtl_ips_nic_on); 312f1d2b4d3SLarry Finger 313f1d2b4d3SLarry Finger /*for FW LPS*/ 314f1d2b4d3SLarry Finger 315f1d2b4d3SLarry Finger /* 316f1d2b4d3SLarry Finger *Determine if we can set Fw into PS mode 317f1d2b4d3SLarry Finger *in current condition.Return TRUE if it 318f1d2b4d3SLarry Finger *can enter PS mode. 319f1d2b4d3SLarry Finger */ 320f1d2b4d3SLarry Finger static bool rtl_get_fwlps_doze(struct ieee80211_hw *hw) 321f1d2b4d3SLarry Finger { 322f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 323f1d2b4d3SLarry Finger struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); 324f1d2b4d3SLarry Finger struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); 325f1d2b4d3SLarry Finger u32 ps_timediff; 326f1d2b4d3SLarry Finger 327f1d2b4d3SLarry Finger ps_timediff = jiffies_to_msecs(jiffies - 328f1d2b4d3SLarry Finger ppsc->last_delaylps_stamp_jiffies); 329f1d2b4d3SLarry Finger 330f1d2b4d3SLarry Finger if (ps_timediff < 2000) { 331f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, 332f1d2b4d3SLarry Finger "Delay enter Fw LPS for DHCP, ARP, or EAPOL exchanging state\n"); 333f1d2b4d3SLarry Finger return false; 334f1d2b4d3SLarry Finger } 335f1d2b4d3SLarry Finger 336f1d2b4d3SLarry Finger if (mac->link_state != MAC80211_LINKED) 337f1d2b4d3SLarry Finger return false; 338f1d2b4d3SLarry Finger 339f1d2b4d3SLarry Finger if (mac->opmode == NL80211_IFTYPE_ADHOC) 340f1d2b4d3SLarry Finger return false; 341f1d2b4d3SLarry Finger 342f1d2b4d3SLarry Finger return true; 343f1d2b4d3SLarry Finger } 344f1d2b4d3SLarry Finger 345f1d2b4d3SLarry Finger /* Change current and default preamble mode.*/ 346f1d2b4d3SLarry Finger void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode) 347f1d2b4d3SLarry Finger { 348f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 349f1d2b4d3SLarry Finger struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); 350f1d2b4d3SLarry Finger struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); 351f1d2b4d3SLarry Finger bool enter_fwlps; 352f1d2b4d3SLarry Finger 353f1d2b4d3SLarry Finger if (mac->opmode == NL80211_IFTYPE_ADHOC) 354f1d2b4d3SLarry Finger return; 355f1d2b4d3SLarry Finger 356f1d2b4d3SLarry Finger if (mac->link_state != MAC80211_LINKED) 357f1d2b4d3SLarry Finger return; 358f1d2b4d3SLarry Finger 359135f4fbdSPing-Ke Shih if (ppsc->dot11_psmode == rt_psmode && rt_psmode == EACTIVE) 360f1d2b4d3SLarry Finger return; 361f1d2b4d3SLarry Finger 362f1d2b4d3SLarry Finger /* Update power save mode configured. */ 363f1d2b4d3SLarry Finger ppsc->dot11_psmode = rt_psmode; 364f1d2b4d3SLarry Finger 365f1d2b4d3SLarry Finger /* 366f1d2b4d3SLarry Finger *<FW control LPS> 367f1d2b4d3SLarry Finger *1. Enter PS mode 368f1d2b4d3SLarry Finger * Set RPWM to Fw to turn RF off and send H2C fw_pwrmode 369f1d2b4d3SLarry Finger * cmd to set Fw into PS mode. 370f1d2b4d3SLarry Finger *2. Leave PS mode 371f1d2b4d3SLarry Finger * Send H2C fw_pwrmode cmd to Fw to set Fw into Active 372f1d2b4d3SLarry Finger * mode and set RPWM to turn RF on. 373f1d2b4d3SLarry Finger */ 374f1d2b4d3SLarry Finger 375f1d2b4d3SLarry Finger if ((ppsc->fwctrl_lps) && ppsc->report_linked) { 376f1d2b4d3SLarry Finger if (ppsc->dot11_psmode == EACTIVE) { 377f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, 378f1d2b4d3SLarry Finger "FW LPS leave ps_mode:%x\n", 379f1d2b4d3SLarry Finger FW_PS_ACTIVE_MODE); 380f1d2b4d3SLarry Finger enter_fwlps = false; 381f1d2b4d3SLarry Finger ppsc->pwr_mode = FW_PS_ACTIVE_MODE; 382f1d2b4d3SLarry Finger ppsc->smart_ps = 0; 383f1d2b4d3SLarry Finger rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_LPS_ACTION, 384f1d2b4d3SLarry Finger (u8 *)(&enter_fwlps)); 385f1d2b4d3SLarry Finger if (ppsc->p2p_ps_info.opp_ps) 386f1d2b4d3SLarry Finger rtl_p2p_ps_cmd(hw , P2P_PS_ENABLE); 387f1d2b4d3SLarry Finger 388f1d2b4d3SLarry Finger if (rtlpriv->cfg->ops->get_btc_status()) 389f1d2b4d3SLarry Finger rtlpriv->btcoexist.btc_ops->btc_lps_notify(rtlpriv, rt_psmode); 390f1d2b4d3SLarry Finger } else { 391f1d2b4d3SLarry Finger if (rtl_get_fwlps_doze(hw)) { 392f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, 393f1d2b4d3SLarry Finger "FW LPS enter ps_mode:%x\n", 394f1d2b4d3SLarry Finger ppsc->fwctrl_psmode); 395f1d2b4d3SLarry Finger if (rtlpriv->cfg->ops->get_btc_status()) 396f1d2b4d3SLarry Finger rtlpriv->btcoexist.btc_ops->btc_lps_notify(rtlpriv, rt_psmode); 397f1d2b4d3SLarry Finger enter_fwlps = true; 398f1d2b4d3SLarry Finger ppsc->pwr_mode = ppsc->fwctrl_psmode; 399f1d2b4d3SLarry Finger ppsc->smart_ps = 2; 400f1d2b4d3SLarry Finger rtlpriv->cfg->ops->set_hw_reg(hw, 401f1d2b4d3SLarry Finger HW_VAR_FW_LPS_ACTION, 402f1d2b4d3SLarry Finger (u8 *)(&enter_fwlps)); 403f1d2b4d3SLarry Finger 404f1d2b4d3SLarry Finger } else { 405f1d2b4d3SLarry Finger /* Reset the power save related parameters. */ 406f1d2b4d3SLarry Finger ppsc->dot11_psmode = EACTIVE; 407f1d2b4d3SLarry Finger } 408f1d2b4d3SLarry Finger } 409f1d2b4d3SLarry Finger } 410f1d2b4d3SLarry Finger } 411f1d2b4d3SLarry Finger 412ba9f93f8SLarry Finger /* Interrupt safe routine to enter the leisure power save mode.*/ 413ba9f93f8SLarry Finger static void rtl_lps_enter_core(struct ieee80211_hw *hw) 414f1d2b4d3SLarry Finger { 415f1d2b4d3SLarry Finger struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); 416f1d2b4d3SLarry Finger struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); 417f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 418f1d2b4d3SLarry Finger 419f1d2b4d3SLarry Finger if (!ppsc->fwctrl_lps) 420f1d2b4d3SLarry Finger return; 421f1d2b4d3SLarry Finger 422f1d2b4d3SLarry Finger if (rtlpriv->sec.being_setkey) 423f1d2b4d3SLarry Finger return; 424f1d2b4d3SLarry Finger 425f1d2b4d3SLarry Finger if (rtlpriv->link_info.busytraffic) 426f1d2b4d3SLarry Finger return; 427f1d2b4d3SLarry Finger 428f1d2b4d3SLarry Finger /*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */ 429f1d2b4d3SLarry Finger if (mac->cnt_after_linked < 5) 430f1d2b4d3SLarry Finger return; 431f1d2b4d3SLarry Finger 432f1d2b4d3SLarry Finger if (mac->opmode == NL80211_IFTYPE_ADHOC) 433f1d2b4d3SLarry Finger return; 434f1d2b4d3SLarry Finger 435f1d2b4d3SLarry Finger if (mac->link_state != MAC80211_LINKED) 436f1d2b4d3SLarry Finger return; 437f1d2b4d3SLarry Finger 438*a3fa3669SPing-Ke Shih mutex_lock(&rtlpriv->locks.lps_mutex); 439f1d2b4d3SLarry Finger 440135f4fbdSPing-Ke Shih /* Don't need to check (ppsc->dot11_psmode == EACTIVE), because 441135f4fbdSPing-Ke Shih * bt_ccoexist may ask to enter lps. 442135f4fbdSPing-Ke Shih * In normal case, this constraint move to rtl_lps_set_psmode(). 443135f4fbdSPing-Ke Shih */ 444f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, 445f1d2b4d3SLarry Finger "Enter 802.11 power save mode...\n"); 446f1d2b4d3SLarry Finger rtl_lps_set_psmode(hw, EAUTOPS); 447f1d2b4d3SLarry Finger 448*a3fa3669SPing-Ke Shih mutex_unlock(&rtlpriv->locks.lps_mutex); 449f1d2b4d3SLarry Finger } 450f1d2b4d3SLarry Finger 451ba9f93f8SLarry Finger /* Interrupt safe routine to leave the leisure power save mode.*/ 452ba9f93f8SLarry Finger static void rtl_lps_leave_core(struct ieee80211_hw *hw) 453f1d2b4d3SLarry Finger { 454f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 455f1d2b4d3SLarry Finger struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); 456f1d2b4d3SLarry Finger struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); 457f1d2b4d3SLarry Finger 458*a3fa3669SPing-Ke Shih mutex_lock(&rtlpriv->locks.lps_mutex); 459f1d2b4d3SLarry Finger 460f1d2b4d3SLarry Finger if (ppsc->fwctrl_lps) { 461f1d2b4d3SLarry Finger if (ppsc->dot11_psmode != EACTIVE) { 462f1d2b4d3SLarry Finger 463f1d2b4d3SLarry Finger /*FIX ME */ 464f1d2b4d3SLarry Finger /*rtlpriv->cfg->ops->enable_interrupt(hw); */ 465f1d2b4d3SLarry Finger 466f1d2b4d3SLarry Finger if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM && 467f1d2b4d3SLarry Finger RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) && 468f1d2b4d3SLarry Finger rtlhal->interface == INTF_PCI) { 469f1d2b4d3SLarry Finger rtlpriv->intf_ops->disable_aspm(hw); 470f1d2b4d3SLarry Finger RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); 471f1d2b4d3SLarry Finger } 472f1d2b4d3SLarry Finger 473f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, 474f1d2b4d3SLarry Finger "Busy Traffic,Leave 802.11 power save..\n"); 475f1d2b4d3SLarry Finger 476f1d2b4d3SLarry Finger rtl_lps_set_psmode(hw, EACTIVE); 477f1d2b4d3SLarry Finger } 478f1d2b4d3SLarry Finger } 479*a3fa3669SPing-Ke Shih mutex_unlock(&rtlpriv->locks.lps_mutex); 480f1d2b4d3SLarry Finger } 481f1d2b4d3SLarry Finger 482f1d2b4d3SLarry Finger /* For sw LPS*/ 483f1d2b4d3SLarry Finger void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len) 484f1d2b4d3SLarry Finger { 485f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 486f1d2b4d3SLarry Finger struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); 487f1d2b4d3SLarry Finger struct ieee80211_hdr *hdr = data; 488f1d2b4d3SLarry Finger struct ieee80211_tim_ie *tim_ie; 489f1d2b4d3SLarry Finger u8 *tim; 490f1d2b4d3SLarry Finger u8 tim_len; 491f1d2b4d3SLarry Finger bool u_buffed; 492f1d2b4d3SLarry Finger bool m_buffed; 493f1d2b4d3SLarry Finger 494f1d2b4d3SLarry Finger if (mac->opmode != NL80211_IFTYPE_STATION) 495f1d2b4d3SLarry Finger return; 496f1d2b4d3SLarry Finger 497f1d2b4d3SLarry Finger if (!rtlpriv->psc.swctrl_lps) 498f1d2b4d3SLarry Finger return; 499f1d2b4d3SLarry Finger 500f1d2b4d3SLarry Finger if (rtlpriv->mac80211.link_state != MAC80211_LINKED) 501f1d2b4d3SLarry Finger return; 502f1d2b4d3SLarry Finger 503f1d2b4d3SLarry Finger if (!rtlpriv->psc.sw_ps_enabled) 504f1d2b4d3SLarry Finger return; 505f1d2b4d3SLarry Finger 506f1d2b4d3SLarry Finger if (rtlpriv->psc.fwctrl_lps) 507f1d2b4d3SLarry Finger return; 508f1d2b4d3SLarry Finger 509f1d2b4d3SLarry Finger if (likely(!(hw->conf.flags & IEEE80211_CONF_PS))) 510f1d2b4d3SLarry Finger return; 511f1d2b4d3SLarry Finger 512f1d2b4d3SLarry Finger /* check if this really is a beacon */ 513f1d2b4d3SLarry Finger if (!ieee80211_is_beacon(hdr->frame_control)) 514f1d2b4d3SLarry Finger return; 515f1d2b4d3SLarry Finger 516f1d2b4d3SLarry Finger /* min. beacon length + FCS_LEN */ 517f1d2b4d3SLarry Finger if (len <= 40 + FCS_LEN) 518f1d2b4d3SLarry Finger return; 519f1d2b4d3SLarry Finger 520f1d2b4d3SLarry Finger /* and only beacons from the associated BSSID, please */ 521f1d2b4d3SLarry Finger if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid)) 522f1d2b4d3SLarry Finger return; 523f1d2b4d3SLarry Finger 524f1d2b4d3SLarry Finger rtlpriv->psc.last_beacon = jiffies; 525f1d2b4d3SLarry Finger 526f1d2b4d3SLarry Finger tim = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_TIM); 527f1d2b4d3SLarry Finger if (!tim) 528f1d2b4d3SLarry Finger return; 529f1d2b4d3SLarry Finger 530f1d2b4d3SLarry Finger if (tim[1] < sizeof(*tim_ie)) 531f1d2b4d3SLarry Finger return; 532f1d2b4d3SLarry Finger 533f1d2b4d3SLarry Finger tim_len = tim[1]; 534f1d2b4d3SLarry Finger tim_ie = (struct ieee80211_tim_ie *) &tim[2]; 535f1d2b4d3SLarry Finger 536f1d2b4d3SLarry Finger if (!WARN_ON_ONCE(!hw->conf.ps_dtim_period)) 537f1d2b4d3SLarry Finger rtlpriv->psc.dtim_counter = tim_ie->dtim_count; 538f1d2b4d3SLarry Finger 539f1d2b4d3SLarry Finger /* Check whenever the PHY can be turned off again. */ 540f1d2b4d3SLarry Finger 541f1d2b4d3SLarry Finger /* 1. What about buffered unicast traffic for our AID? */ 542f1d2b4d3SLarry Finger u_buffed = ieee80211_check_tim(tim_ie, tim_len, 543f1d2b4d3SLarry Finger rtlpriv->mac80211.assoc_id); 544f1d2b4d3SLarry Finger 545f1d2b4d3SLarry Finger /* 2. Maybe the AP wants to send multicast/broadcast data? */ 546f1d2b4d3SLarry Finger m_buffed = tim_ie->bitmap_ctrl & 0x01; 547f1d2b4d3SLarry Finger rtlpriv->psc.multi_buffered = m_buffed; 548f1d2b4d3SLarry Finger 549f1d2b4d3SLarry Finger /* unicast will process by mac80211 through 550f1d2b4d3SLarry Finger * set ~IEEE80211_CONF_PS, So we just check 551f1d2b4d3SLarry Finger * multicast frames here */ 552f1d2b4d3SLarry Finger if (!m_buffed) { 553f1d2b4d3SLarry Finger /* back to low-power land. and delay is 554f1d2b4d3SLarry Finger * prevent null power save frame tx fail */ 555f1d2b4d3SLarry Finger queue_delayed_work(rtlpriv->works.rtl_wq, 556f1d2b4d3SLarry Finger &rtlpriv->works.ps_work, MSECS(5)); 557f1d2b4d3SLarry Finger } else { 558f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG, 559f1d2b4d3SLarry Finger "u_bufferd: %x, m_buffered: %x\n", u_buffed, m_buffed); 560f1d2b4d3SLarry Finger } 561f1d2b4d3SLarry Finger } 562f1d2b4d3SLarry Finger EXPORT_SYMBOL_GPL(rtl_swlps_beacon); 563f1d2b4d3SLarry Finger 564f1d2b4d3SLarry Finger void rtl_swlps_rf_awake(struct ieee80211_hw *hw) 565f1d2b4d3SLarry Finger { 566f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 567f1d2b4d3SLarry Finger struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); 568f1d2b4d3SLarry Finger struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); 569f1d2b4d3SLarry Finger 570f1d2b4d3SLarry Finger if (!rtlpriv->psc.swctrl_lps) 571f1d2b4d3SLarry Finger return; 572f1d2b4d3SLarry Finger if (mac->link_state != MAC80211_LINKED) 573f1d2b4d3SLarry Finger return; 574f1d2b4d3SLarry Finger 575f1d2b4d3SLarry Finger if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM && 576f1d2b4d3SLarry Finger RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) { 577f1d2b4d3SLarry Finger rtlpriv->intf_ops->disable_aspm(hw); 578f1d2b4d3SLarry Finger RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); 579f1d2b4d3SLarry Finger } 580f1d2b4d3SLarry Finger 581*a3fa3669SPing-Ke Shih mutex_lock(&rtlpriv->locks.lps_mutex); 58230462b51SLarry Finger rtl_ps_set_rf_state(hw, ERFON, RF_CHANGE_BY_PS); 583*a3fa3669SPing-Ke Shih mutex_unlock(&rtlpriv->locks.lps_mutex); 584f1d2b4d3SLarry Finger } 585f1d2b4d3SLarry Finger 586f1d2b4d3SLarry Finger void rtl_swlps_rfon_wq_callback(void *data) 587f1d2b4d3SLarry Finger { 588f1d2b4d3SLarry Finger struct rtl_works *rtlworks = 589f1d2b4d3SLarry Finger container_of_dwork_rtl(data, struct rtl_works, ps_rfon_wq); 590f1d2b4d3SLarry Finger struct ieee80211_hw *hw = rtlworks->hw; 591f1d2b4d3SLarry Finger 592f1d2b4d3SLarry Finger rtl_swlps_rf_awake(hw); 593f1d2b4d3SLarry Finger } 594f1d2b4d3SLarry Finger 595f1d2b4d3SLarry Finger void rtl_swlps_rf_sleep(struct ieee80211_hw *hw) 596f1d2b4d3SLarry Finger { 597f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 598f1d2b4d3SLarry Finger struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); 599f1d2b4d3SLarry Finger struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); 600f1d2b4d3SLarry Finger u8 sleep_intv; 601f1d2b4d3SLarry Finger 602f1d2b4d3SLarry Finger if (!rtlpriv->psc.sw_ps_enabled) 603f1d2b4d3SLarry Finger return; 604f1d2b4d3SLarry Finger 605f1d2b4d3SLarry Finger if ((rtlpriv->sec.being_setkey) || 606f1d2b4d3SLarry Finger (mac->opmode == NL80211_IFTYPE_ADHOC)) 607f1d2b4d3SLarry Finger return; 608f1d2b4d3SLarry Finger 609f1d2b4d3SLarry Finger /*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */ 610f1d2b4d3SLarry Finger if ((mac->link_state != MAC80211_LINKED) || (mac->cnt_after_linked < 5)) 611f1d2b4d3SLarry Finger return; 612f1d2b4d3SLarry Finger 613f1d2b4d3SLarry Finger if (rtlpriv->link_info.busytraffic) 614f1d2b4d3SLarry Finger return; 615f1d2b4d3SLarry Finger 616f1d2b4d3SLarry Finger spin_lock(&rtlpriv->locks.rf_ps_lock); 617f1d2b4d3SLarry Finger if (rtlpriv->psc.rfchange_inprogress) { 618f1d2b4d3SLarry Finger spin_unlock(&rtlpriv->locks.rf_ps_lock); 619f1d2b4d3SLarry Finger return; 620f1d2b4d3SLarry Finger } 621f1d2b4d3SLarry Finger spin_unlock(&rtlpriv->locks.rf_ps_lock); 622f1d2b4d3SLarry Finger 623*a3fa3669SPing-Ke Shih mutex_lock(&rtlpriv->locks.lps_mutex); 62430462b51SLarry Finger rtl_ps_set_rf_state(hw, ERFSLEEP, RF_CHANGE_BY_PS); 625*a3fa3669SPing-Ke Shih mutex_unlock(&rtlpriv->locks.lps_mutex); 626f1d2b4d3SLarry Finger 627f1d2b4d3SLarry Finger if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM && 628f1d2b4d3SLarry Finger !RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) { 629f1d2b4d3SLarry Finger rtlpriv->intf_ops->enable_aspm(hw); 630f1d2b4d3SLarry Finger RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); 631f1d2b4d3SLarry Finger } 632f1d2b4d3SLarry Finger 633f1d2b4d3SLarry Finger /* here is power save alg, when this beacon is DTIM 634f1d2b4d3SLarry Finger * we will set sleep time to dtim_period * n; 635f1d2b4d3SLarry Finger * when this beacon is not DTIM, we will set sleep 636f1d2b4d3SLarry Finger * time to sleep_intv = rtlpriv->psc.dtim_counter or 637f1d2b4d3SLarry Finger * MAX_SW_LPS_SLEEP_INTV(default set to 5) */ 638f1d2b4d3SLarry Finger 639f1d2b4d3SLarry Finger if (rtlpriv->psc.dtim_counter == 0) { 640f1d2b4d3SLarry Finger if (hw->conf.ps_dtim_period == 1) 641f1d2b4d3SLarry Finger sleep_intv = hw->conf.ps_dtim_period * 2; 642f1d2b4d3SLarry Finger else 643f1d2b4d3SLarry Finger sleep_intv = hw->conf.ps_dtim_period; 644f1d2b4d3SLarry Finger } else { 645f1d2b4d3SLarry Finger sleep_intv = rtlpriv->psc.dtim_counter; 646f1d2b4d3SLarry Finger } 647f1d2b4d3SLarry Finger 648f1d2b4d3SLarry Finger if (sleep_intv > MAX_SW_LPS_SLEEP_INTV) 649f1d2b4d3SLarry Finger sleep_intv = MAX_SW_LPS_SLEEP_INTV; 650f1d2b4d3SLarry Finger 651f1d2b4d3SLarry Finger /* this print should always be dtim_conter = 0 & 652f1d2b4d3SLarry Finger * sleep = dtim_period, that meaons, we should 653f1d2b4d3SLarry Finger * awake before every dtim */ 654f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG, 655f1d2b4d3SLarry Finger "dtim_counter:%x will sleep :%d beacon_intv\n", 656f1d2b4d3SLarry Finger rtlpriv->psc.dtim_counter, sleep_intv); 657f1d2b4d3SLarry Finger 658f1d2b4d3SLarry Finger /* we tested that 40ms is enough for sw & hw sw delay */ 659f1d2b4d3SLarry Finger queue_delayed_work(rtlpriv->works.rtl_wq, &rtlpriv->works.ps_rfon_wq, 660f1d2b4d3SLarry Finger MSECS(sleep_intv * mac->vif->bss_conf.beacon_int - 40)); 661f1d2b4d3SLarry Finger } 662f1d2b4d3SLarry Finger 663f1d2b4d3SLarry Finger void rtl_lps_change_work_callback(struct work_struct *work) 664f1d2b4d3SLarry Finger { 665f1d2b4d3SLarry Finger struct rtl_works *rtlworks = 666f1d2b4d3SLarry Finger container_of(work, struct rtl_works, lps_change_work); 667f1d2b4d3SLarry Finger struct ieee80211_hw *hw = rtlworks->hw; 668f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 669f1d2b4d3SLarry Finger 670f1d2b4d3SLarry Finger if (rtlpriv->enter_ps) 671ba9f93f8SLarry Finger rtl_lps_enter_core(hw); 672f1d2b4d3SLarry Finger else 673ba9f93f8SLarry Finger rtl_lps_leave_core(hw); 674f1d2b4d3SLarry Finger } 675f1d2b4d3SLarry Finger EXPORT_SYMBOL_GPL(rtl_lps_change_work_callback); 676f1d2b4d3SLarry Finger 677ba9f93f8SLarry Finger void rtl_lps_enter(struct ieee80211_hw *hw) 678ba9f93f8SLarry Finger { 679ba9f93f8SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 680ba9f93f8SLarry Finger 681ba9f93f8SLarry Finger if (!in_interrupt()) 682ba9f93f8SLarry Finger return rtl_lps_enter_core(hw); 683ba9f93f8SLarry Finger rtlpriv->enter_ps = true; 684ba9f93f8SLarry Finger schedule_work(&rtlpriv->works.lps_change_work); 685ba9f93f8SLarry Finger } 686ba9f93f8SLarry Finger EXPORT_SYMBOL_GPL(rtl_lps_enter); 687ba9f93f8SLarry Finger 688ba9f93f8SLarry Finger void rtl_lps_leave(struct ieee80211_hw *hw) 689ba9f93f8SLarry Finger { 690ba9f93f8SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 691ba9f93f8SLarry Finger 692ba9f93f8SLarry Finger if (!in_interrupt()) 693ba9f93f8SLarry Finger return rtl_lps_leave_core(hw); 694ba9f93f8SLarry Finger rtlpriv->enter_ps = false; 695ba9f93f8SLarry Finger schedule_work(&rtlpriv->works.lps_change_work); 696ba9f93f8SLarry Finger } 697ba9f93f8SLarry Finger EXPORT_SYMBOL_GPL(rtl_lps_leave); 698ba9f93f8SLarry Finger 699f1d2b4d3SLarry Finger void rtl_swlps_wq_callback(void *data) 700f1d2b4d3SLarry Finger { 701f1d2b4d3SLarry Finger struct rtl_works *rtlworks = container_of_dwork_rtl(data, 702f1d2b4d3SLarry Finger struct rtl_works, 703f1d2b4d3SLarry Finger ps_work); 704f1d2b4d3SLarry Finger struct ieee80211_hw *hw = rtlworks->hw; 705f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 706f1d2b4d3SLarry Finger bool ps = false; 707f1d2b4d3SLarry Finger 708f1d2b4d3SLarry Finger ps = (hw->conf.flags & IEEE80211_CONF_PS); 709f1d2b4d3SLarry Finger 710f1d2b4d3SLarry Finger /* we can sleep after ps null send ok */ 711f1d2b4d3SLarry Finger if (rtlpriv->psc.state_inap) { 712f1d2b4d3SLarry Finger rtl_swlps_rf_sleep(hw); 713f1d2b4d3SLarry Finger 714f1d2b4d3SLarry Finger if (rtlpriv->psc.state && !ps) { 715f1d2b4d3SLarry Finger rtlpriv->psc.sleep_ms = jiffies_to_msecs(jiffies - 716f1d2b4d3SLarry Finger rtlpriv->psc.last_action); 717f1d2b4d3SLarry Finger } 718f1d2b4d3SLarry Finger 719f1d2b4d3SLarry Finger if (ps) 720f1d2b4d3SLarry Finger rtlpriv->psc.last_slept = jiffies; 721f1d2b4d3SLarry Finger 722f1d2b4d3SLarry Finger rtlpriv->psc.last_action = jiffies; 723f1d2b4d3SLarry Finger rtlpriv->psc.state = ps; 724f1d2b4d3SLarry Finger } 725f1d2b4d3SLarry Finger } 726f1d2b4d3SLarry Finger 727f1d2b4d3SLarry Finger static void rtl_p2p_noa_ie(struct ieee80211_hw *hw, void *data, 728f1d2b4d3SLarry Finger unsigned int len) 729f1d2b4d3SLarry Finger { 730f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 731f1d2b4d3SLarry Finger struct ieee80211_mgmt *mgmt = data; 732f1d2b4d3SLarry Finger struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info); 733f1d2b4d3SLarry Finger u8 *pos, *end, *ie; 734f1d2b4d3SLarry Finger u16 noa_len; 735f1d2b4d3SLarry Finger static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09}; 736f1d2b4d3SLarry Finger u8 noa_num, index , i, noa_index = 0; 737f1d2b4d3SLarry Finger bool find_p2p_ie = false , find_p2p_ps_ie = false; 738f1d2b4d3SLarry Finger pos = (u8 *)mgmt->u.beacon.variable; 739f1d2b4d3SLarry Finger end = data + len; 740f1d2b4d3SLarry Finger ie = NULL; 741f1d2b4d3SLarry Finger 742f1d2b4d3SLarry Finger while (pos + 1 < end) { 743f1d2b4d3SLarry Finger if (pos + 2 + pos[1] > end) 744f1d2b4d3SLarry Finger return; 745f1d2b4d3SLarry Finger 746f1d2b4d3SLarry Finger if (pos[0] == 221 && pos[1] > 4) { 747f1d2b4d3SLarry Finger if (memcmp(&pos[2], p2p_oui_ie_type, 4) == 0) { 748f1d2b4d3SLarry Finger ie = pos + 2+4; 749f1d2b4d3SLarry Finger break; 750f1d2b4d3SLarry Finger } 751f1d2b4d3SLarry Finger } 752f1d2b4d3SLarry Finger pos += 2 + pos[1]; 753f1d2b4d3SLarry Finger } 754f1d2b4d3SLarry Finger 755f1d2b4d3SLarry Finger if (ie == NULL) 756f1d2b4d3SLarry Finger return; 757f1d2b4d3SLarry Finger find_p2p_ie = true; 758f1d2b4d3SLarry Finger /*to find noa ie*/ 759f1d2b4d3SLarry Finger while (ie + 1 < end) { 760f1d2b4d3SLarry Finger noa_len = READEF2BYTE((__le16 *)&ie[1]); 761f1d2b4d3SLarry Finger if (ie + 3 + ie[1] > end) 762f1d2b4d3SLarry Finger return; 763f1d2b4d3SLarry Finger 764f1d2b4d3SLarry Finger if (ie[0] == 12) { 765f1d2b4d3SLarry Finger find_p2p_ps_ie = true; 766f1d2b4d3SLarry Finger if ((noa_len - 2) % 13 != 0) { 767f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, 768f1d2b4d3SLarry Finger "P2P notice of absence: invalid length.%d\n", 769f1d2b4d3SLarry Finger noa_len); 770f1d2b4d3SLarry Finger return; 771f1d2b4d3SLarry Finger } else { 772f1d2b4d3SLarry Finger noa_num = (noa_len - 2) / 13; 773f1d2b4d3SLarry Finger } 774f1d2b4d3SLarry Finger noa_index = ie[3]; 775f1d2b4d3SLarry Finger if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode == 776f1d2b4d3SLarry Finger P2P_PS_NONE || noa_index != p2pinfo->noa_index) { 777f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, 778f1d2b4d3SLarry Finger "update NOA ie.\n"); 779f1d2b4d3SLarry Finger p2pinfo->noa_index = noa_index; 780f1d2b4d3SLarry Finger p2pinfo->opp_ps = (ie[4] >> 7); 781f1d2b4d3SLarry Finger p2pinfo->ctwindow = ie[4] & 0x7F; 782f1d2b4d3SLarry Finger p2pinfo->noa_num = noa_num; 783f1d2b4d3SLarry Finger index = 5; 784f1d2b4d3SLarry Finger for (i = 0; i < noa_num; i++) { 785f1d2b4d3SLarry Finger p2pinfo->noa_count_type[i] = 786f1d2b4d3SLarry Finger READEF1BYTE(ie+index); 787f1d2b4d3SLarry Finger index += 1; 788f1d2b4d3SLarry Finger p2pinfo->noa_duration[i] = 789f1d2b4d3SLarry Finger READEF4BYTE((__le32 *)ie+index); 790f1d2b4d3SLarry Finger index += 4; 791f1d2b4d3SLarry Finger p2pinfo->noa_interval[i] = 792f1d2b4d3SLarry Finger READEF4BYTE((__le32 *)ie+index); 793f1d2b4d3SLarry Finger index += 4; 794f1d2b4d3SLarry Finger p2pinfo->noa_start_time[i] = 795f1d2b4d3SLarry Finger READEF4BYTE((__le32 *)ie+index); 796f1d2b4d3SLarry Finger index += 4; 797f1d2b4d3SLarry Finger } 798f1d2b4d3SLarry Finger 799f1d2b4d3SLarry Finger if (p2pinfo->opp_ps == 1) { 800f1d2b4d3SLarry Finger p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW; 801f1d2b4d3SLarry Finger /* Driver should wait LPS entering 802f1d2b4d3SLarry Finger * CTWindow 803f1d2b4d3SLarry Finger */ 804f1d2b4d3SLarry Finger if (rtlpriv->psc.fw_current_inpsmode) 805f1d2b4d3SLarry Finger rtl_p2p_ps_cmd(hw, 806f1d2b4d3SLarry Finger P2P_PS_ENABLE); 807f1d2b4d3SLarry Finger } else if (p2pinfo->noa_num > 0) { 808f1d2b4d3SLarry Finger p2pinfo->p2p_ps_mode = P2P_PS_NOA; 809f1d2b4d3SLarry Finger rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE); 810f1d2b4d3SLarry Finger } else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) { 811f1d2b4d3SLarry Finger rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE); 812f1d2b4d3SLarry Finger } 813f1d2b4d3SLarry Finger } 814f1d2b4d3SLarry Finger break; 815f1d2b4d3SLarry Finger } 816f1d2b4d3SLarry Finger ie += 3 + noa_len; 817f1d2b4d3SLarry Finger } 818f1d2b4d3SLarry Finger 819f1d2b4d3SLarry Finger if (find_p2p_ie == true) { 820f1d2b4d3SLarry Finger if ((p2pinfo->p2p_ps_mode > P2P_PS_NONE) && 821f1d2b4d3SLarry Finger (find_p2p_ps_ie == false)) 822f1d2b4d3SLarry Finger rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE); 823f1d2b4d3SLarry Finger } 824f1d2b4d3SLarry Finger } 825f1d2b4d3SLarry Finger 826f1d2b4d3SLarry Finger static void rtl_p2p_action_ie(struct ieee80211_hw *hw, void *data, 827f1d2b4d3SLarry Finger unsigned int len) 828f1d2b4d3SLarry Finger { 829f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 830f1d2b4d3SLarry Finger struct ieee80211_mgmt *mgmt = data; 831f1d2b4d3SLarry Finger struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info); 832f1d2b4d3SLarry Finger u8 noa_num, index , i , noa_index = 0; 833f1d2b4d3SLarry Finger u8 *pos, *end, *ie; 834f1d2b4d3SLarry Finger u16 noa_len; 835f1d2b4d3SLarry Finger static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09}; 836f1d2b4d3SLarry Finger 837f1d2b4d3SLarry Finger pos = (u8 *)&mgmt->u.action.category; 838f1d2b4d3SLarry Finger end = data + len; 839f1d2b4d3SLarry Finger ie = NULL; 840f1d2b4d3SLarry Finger 841f1d2b4d3SLarry Finger if (pos[0] == 0x7f) { 842f1d2b4d3SLarry Finger if (memcmp(&pos[1], p2p_oui_ie_type, 4) == 0) 843f1d2b4d3SLarry Finger ie = pos + 3+4; 844f1d2b4d3SLarry Finger } 845f1d2b4d3SLarry Finger 846f1d2b4d3SLarry Finger if (ie == NULL) 847f1d2b4d3SLarry Finger return; 848f1d2b4d3SLarry Finger 849f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "action frame find P2P IE.\n"); 850f1d2b4d3SLarry Finger /*to find noa ie*/ 851f1d2b4d3SLarry Finger while (ie + 1 < end) { 852f1d2b4d3SLarry Finger noa_len = READEF2BYTE((__le16 *)&ie[1]); 853f1d2b4d3SLarry Finger if (ie + 3 + ie[1] > end) 854f1d2b4d3SLarry Finger return; 855f1d2b4d3SLarry Finger 856f1d2b4d3SLarry Finger if (ie[0] == 12) { 857f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "find NOA IE.\n"); 858f1d2b4d3SLarry Finger RT_PRINT_DATA(rtlpriv, COMP_FW, DBG_LOUD, "noa ie ", 859f1d2b4d3SLarry Finger ie, noa_len); 860f1d2b4d3SLarry Finger if ((noa_len - 2) % 13 != 0) { 861f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, 862f1d2b4d3SLarry Finger "P2P notice of absence: invalid length.%d\n", 863f1d2b4d3SLarry Finger noa_len); 864f1d2b4d3SLarry Finger return; 865f1d2b4d3SLarry Finger } else { 866f1d2b4d3SLarry Finger noa_num = (noa_len - 2) / 13; 867f1d2b4d3SLarry Finger } 868f1d2b4d3SLarry Finger noa_index = ie[3]; 869f1d2b4d3SLarry Finger if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode == 870f1d2b4d3SLarry Finger P2P_PS_NONE || noa_index != p2pinfo->noa_index) { 871f1d2b4d3SLarry Finger p2pinfo->noa_index = noa_index; 872f1d2b4d3SLarry Finger p2pinfo->opp_ps = (ie[4] >> 7); 873f1d2b4d3SLarry Finger p2pinfo->ctwindow = ie[4] & 0x7F; 874f1d2b4d3SLarry Finger p2pinfo->noa_num = noa_num; 875f1d2b4d3SLarry Finger index = 5; 876f1d2b4d3SLarry Finger for (i = 0; i < noa_num; i++) { 877f1d2b4d3SLarry Finger p2pinfo->noa_count_type[i] = 878f1d2b4d3SLarry Finger READEF1BYTE(ie+index); 879f1d2b4d3SLarry Finger index += 1; 880f1d2b4d3SLarry Finger p2pinfo->noa_duration[i] = 881f1d2b4d3SLarry Finger READEF4BYTE((__le32 *)ie+index); 882f1d2b4d3SLarry Finger index += 4; 883f1d2b4d3SLarry Finger p2pinfo->noa_interval[i] = 884f1d2b4d3SLarry Finger READEF4BYTE((__le32 *)ie+index); 885f1d2b4d3SLarry Finger index += 4; 886f1d2b4d3SLarry Finger p2pinfo->noa_start_time[i] = 887f1d2b4d3SLarry Finger READEF4BYTE((__le32 *)ie+index); 888f1d2b4d3SLarry Finger index += 4; 889f1d2b4d3SLarry Finger } 890f1d2b4d3SLarry Finger 891f1d2b4d3SLarry Finger if (p2pinfo->opp_ps == 1) { 892f1d2b4d3SLarry Finger p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW; 893f1d2b4d3SLarry Finger /* Driver should wait LPS entering 894f1d2b4d3SLarry Finger * CTWindow 895f1d2b4d3SLarry Finger */ 896f1d2b4d3SLarry Finger if (rtlpriv->psc.fw_current_inpsmode) 897f1d2b4d3SLarry Finger rtl_p2p_ps_cmd(hw, 898f1d2b4d3SLarry Finger P2P_PS_ENABLE); 899f1d2b4d3SLarry Finger } else if (p2pinfo->noa_num > 0) { 900f1d2b4d3SLarry Finger p2pinfo->p2p_ps_mode = P2P_PS_NOA; 901f1d2b4d3SLarry Finger rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE); 902f1d2b4d3SLarry Finger } else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) { 903f1d2b4d3SLarry Finger rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE); 904f1d2b4d3SLarry Finger } 905f1d2b4d3SLarry Finger } 906f1d2b4d3SLarry Finger break; 907f1d2b4d3SLarry Finger } 908f1d2b4d3SLarry Finger ie += 3 + noa_len; 909f1d2b4d3SLarry Finger } 910f1d2b4d3SLarry Finger } 911f1d2b4d3SLarry Finger 912f1d2b4d3SLarry Finger void rtl_p2p_ps_cmd(struct ieee80211_hw *hw , u8 p2p_ps_state) 913f1d2b4d3SLarry Finger { 914f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 915f1d2b4d3SLarry Finger struct rtl_ps_ctl *rtlps = rtl_psc(rtl_priv(hw)); 916f1d2b4d3SLarry Finger struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info); 917f1d2b4d3SLarry Finger 918f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, " p2p state %x\n" , p2p_ps_state); 919f1d2b4d3SLarry Finger switch (p2p_ps_state) { 920f1d2b4d3SLarry Finger case P2P_PS_DISABLE: 921f1d2b4d3SLarry Finger p2pinfo->p2p_ps_state = p2p_ps_state; 922f1d2b4d3SLarry Finger rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_P2P_PS_OFFLOAD, 923f1d2b4d3SLarry Finger &p2p_ps_state); 924f1d2b4d3SLarry Finger p2pinfo->noa_index = 0; 925f1d2b4d3SLarry Finger p2pinfo->ctwindow = 0; 926f1d2b4d3SLarry Finger p2pinfo->opp_ps = 0; 927f1d2b4d3SLarry Finger p2pinfo->noa_num = 0; 928f1d2b4d3SLarry Finger p2pinfo->p2p_ps_mode = P2P_PS_NONE; 929f1d2b4d3SLarry Finger if (rtlps->fw_current_inpsmode) { 930f1d2b4d3SLarry Finger if (rtlps->smart_ps == 0) { 931f1d2b4d3SLarry Finger rtlps->smart_ps = 2; 932f1d2b4d3SLarry Finger rtlpriv->cfg->ops->set_hw_reg(hw, 933f1d2b4d3SLarry Finger HW_VAR_H2C_FW_PWRMODE, 934f1d2b4d3SLarry Finger &rtlps->pwr_mode); 935f1d2b4d3SLarry Finger } 936f1d2b4d3SLarry Finger 937f1d2b4d3SLarry Finger } 938f1d2b4d3SLarry Finger break; 939f1d2b4d3SLarry Finger case P2P_PS_ENABLE: 940f1d2b4d3SLarry Finger if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) { 941f1d2b4d3SLarry Finger p2pinfo->p2p_ps_state = p2p_ps_state; 942f1d2b4d3SLarry Finger 943f1d2b4d3SLarry Finger if (p2pinfo->ctwindow > 0) { 944f1d2b4d3SLarry Finger if (rtlps->smart_ps != 0) { 945f1d2b4d3SLarry Finger rtlps->smart_ps = 0; 946f1d2b4d3SLarry Finger rtlpriv->cfg->ops->set_hw_reg(hw, 947f1d2b4d3SLarry Finger HW_VAR_H2C_FW_PWRMODE, 948f1d2b4d3SLarry Finger &rtlps->pwr_mode); 949f1d2b4d3SLarry Finger } 950f1d2b4d3SLarry Finger } 951f1d2b4d3SLarry Finger rtlpriv->cfg->ops->set_hw_reg(hw, 952f1d2b4d3SLarry Finger HW_VAR_H2C_FW_P2P_PS_OFFLOAD, 953f1d2b4d3SLarry Finger &p2p_ps_state); 954f1d2b4d3SLarry Finger 955f1d2b4d3SLarry Finger } 956f1d2b4d3SLarry Finger break; 957f1d2b4d3SLarry Finger case P2P_PS_SCAN: 958f1d2b4d3SLarry Finger case P2P_PS_SCAN_DONE: 959f1d2b4d3SLarry Finger case P2P_PS_ALLSTASLEEP: 960f1d2b4d3SLarry Finger if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) { 961f1d2b4d3SLarry Finger p2pinfo->p2p_ps_state = p2p_ps_state; 962f1d2b4d3SLarry Finger rtlpriv->cfg->ops->set_hw_reg(hw, 963f1d2b4d3SLarry Finger HW_VAR_H2C_FW_P2P_PS_OFFLOAD, 964f1d2b4d3SLarry Finger &p2p_ps_state); 965f1d2b4d3SLarry Finger } 966f1d2b4d3SLarry Finger break; 967f1d2b4d3SLarry Finger default: 968f1d2b4d3SLarry Finger break; 969f1d2b4d3SLarry Finger } 970f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, 971f1d2b4d3SLarry Finger "ctwindow %x oppps %x\n", 972f1d2b4d3SLarry Finger p2pinfo->ctwindow , p2pinfo->opp_ps); 973f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, 974f1d2b4d3SLarry Finger "count %x duration %x index %x interval %x start time %x noa num %x\n", 975f1d2b4d3SLarry Finger p2pinfo->noa_count_type[0], 976f1d2b4d3SLarry Finger p2pinfo->noa_duration[0], 977f1d2b4d3SLarry Finger p2pinfo->noa_index, 978f1d2b4d3SLarry Finger p2pinfo->noa_interval[0], 979f1d2b4d3SLarry Finger p2pinfo->noa_start_time[0], 980f1d2b4d3SLarry Finger p2pinfo->noa_num); 981f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "end\n"); 982f1d2b4d3SLarry Finger } 983f1d2b4d3SLarry Finger 984f1d2b4d3SLarry Finger void rtl_p2p_info(struct ieee80211_hw *hw, void *data, unsigned int len) 985f1d2b4d3SLarry Finger { 986f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 987f1d2b4d3SLarry Finger struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); 988f1d2b4d3SLarry Finger struct ieee80211_hdr *hdr = data; 989f1d2b4d3SLarry Finger 990f1d2b4d3SLarry Finger if (!mac->p2p) 991f1d2b4d3SLarry Finger return; 992f1d2b4d3SLarry Finger if (mac->link_state != MAC80211_LINKED) 993f1d2b4d3SLarry Finger return; 994f1d2b4d3SLarry Finger /* min. beacon length + FCS_LEN */ 995f1d2b4d3SLarry Finger if (len <= 40 + FCS_LEN) 996f1d2b4d3SLarry Finger return; 997f1d2b4d3SLarry Finger 998f1d2b4d3SLarry Finger /* and only beacons from the associated BSSID, please */ 999f1d2b4d3SLarry Finger if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid)) 1000f1d2b4d3SLarry Finger return; 1001f1d2b4d3SLarry Finger 1002f1d2b4d3SLarry Finger /* check if this really is a beacon */ 1003f1d2b4d3SLarry Finger if (!(ieee80211_is_beacon(hdr->frame_control) || 1004f1d2b4d3SLarry Finger ieee80211_is_probe_resp(hdr->frame_control) || 1005f1d2b4d3SLarry Finger ieee80211_is_action(hdr->frame_control))) 1006f1d2b4d3SLarry Finger return; 1007f1d2b4d3SLarry Finger 1008f1d2b4d3SLarry Finger if (ieee80211_is_action(hdr->frame_control)) 1009f1d2b4d3SLarry Finger rtl_p2p_action_ie(hw , data , len - FCS_LEN); 1010f1d2b4d3SLarry Finger else 1011f1d2b4d3SLarry Finger rtl_p2p_noa_ie(hw , data , len - FCS_LEN); 1012f1d2b4d3SLarry Finger } 1013f1d2b4d3SLarry Finger EXPORT_SYMBOL_GPL(rtl_p2p_info); 1014