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> */ 58*7c51d17cSKees 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 292f1d2b4d3SLarry Finger spin_lock(&rtlpriv->locks.ips_lock); 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 } 309f1d2b4d3SLarry Finger spin_unlock(&rtlpriv->locks.ips_lock); 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 unsigned long flag; 419f1d2b4d3SLarry Finger 420f1d2b4d3SLarry Finger if (!ppsc->fwctrl_lps) 421f1d2b4d3SLarry Finger return; 422f1d2b4d3SLarry Finger 423f1d2b4d3SLarry Finger if (rtlpriv->sec.being_setkey) 424f1d2b4d3SLarry Finger return; 425f1d2b4d3SLarry Finger 426f1d2b4d3SLarry Finger if (rtlpriv->link_info.busytraffic) 427f1d2b4d3SLarry Finger return; 428f1d2b4d3SLarry Finger 429f1d2b4d3SLarry Finger /*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */ 430f1d2b4d3SLarry Finger if (mac->cnt_after_linked < 5) 431f1d2b4d3SLarry Finger return; 432f1d2b4d3SLarry Finger 433f1d2b4d3SLarry Finger if (mac->opmode == NL80211_IFTYPE_ADHOC) 434f1d2b4d3SLarry Finger return; 435f1d2b4d3SLarry Finger 436f1d2b4d3SLarry Finger if (mac->link_state != MAC80211_LINKED) 437f1d2b4d3SLarry Finger return; 438f1d2b4d3SLarry Finger 439f1d2b4d3SLarry Finger spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag); 440f1d2b4d3SLarry Finger 441135f4fbdSPing-Ke Shih /* Don't need to check (ppsc->dot11_psmode == EACTIVE), because 442135f4fbdSPing-Ke Shih * bt_ccoexist may ask to enter lps. 443135f4fbdSPing-Ke Shih * In normal case, this constraint move to rtl_lps_set_psmode(). 444135f4fbdSPing-Ke Shih */ 445f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, 446f1d2b4d3SLarry Finger "Enter 802.11 power save mode...\n"); 447f1d2b4d3SLarry Finger rtl_lps_set_psmode(hw, EAUTOPS); 448f1d2b4d3SLarry Finger 449f1d2b4d3SLarry Finger spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); 450f1d2b4d3SLarry Finger } 451f1d2b4d3SLarry Finger 452ba9f93f8SLarry Finger /* Interrupt safe routine to leave the leisure power save mode.*/ 453ba9f93f8SLarry Finger static void rtl_lps_leave_core(struct ieee80211_hw *hw) 454f1d2b4d3SLarry Finger { 455f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 456f1d2b4d3SLarry Finger struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); 457f1d2b4d3SLarry Finger struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); 458f1d2b4d3SLarry Finger unsigned long flag; 459f1d2b4d3SLarry Finger 460f1d2b4d3SLarry Finger spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag); 461f1d2b4d3SLarry Finger 462f1d2b4d3SLarry Finger if (ppsc->fwctrl_lps) { 463f1d2b4d3SLarry Finger if (ppsc->dot11_psmode != EACTIVE) { 464f1d2b4d3SLarry Finger 465f1d2b4d3SLarry Finger /*FIX ME */ 466f1d2b4d3SLarry Finger /*rtlpriv->cfg->ops->enable_interrupt(hw); */ 467f1d2b4d3SLarry Finger 468f1d2b4d3SLarry Finger if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM && 469f1d2b4d3SLarry Finger RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) && 470f1d2b4d3SLarry Finger rtlhal->interface == INTF_PCI) { 471f1d2b4d3SLarry Finger rtlpriv->intf_ops->disable_aspm(hw); 472f1d2b4d3SLarry Finger RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); 473f1d2b4d3SLarry Finger } 474f1d2b4d3SLarry Finger 475f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, 476f1d2b4d3SLarry Finger "Busy Traffic,Leave 802.11 power save..\n"); 477f1d2b4d3SLarry Finger 478f1d2b4d3SLarry Finger rtl_lps_set_psmode(hw, EACTIVE); 479f1d2b4d3SLarry Finger } 480f1d2b4d3SLarry Finger } 481f1d2b4d3SLarry Finger spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); 482f1d2b4d3SLarry Finger } 483f1d2b4d3SLarry Finger 484f1d2b4d3SLarry Finger /* For sw LPS*/ 485f1d2b4d3SLarry Finger void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len) 486f1d2b4d3SLarry Finger { 487f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 488f1d2b4d3SLarry Finger struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); 489f1d2b4d3SLarry Finger struct ieee80211_hdr *hdr = data; 490f1d2b4d3SLarry Finger struct ieee80211_tim_ie *tim_ie; 491f1d2b4d3SLarry Finger u8 *tim; 492f1d2b4d3SLarry Finger u8 tim_len; 493f1d2b4d3SLarry Finger bool u_buffed; 494f1d2b4d3SLarry Finger bool m_buffed; 495f1d2b4d3SLarry Finger 496f1d2b4d3SLarry Finger if (mac->opmode != NL80211_IFTYPE_STATION) 497f1d2b4d3SLarry Finger return; 498f1d2b4d3SLarry Finger 499f1d2b4d3SLarry Finger if (!rtlpriv->psc.swctrl_lps) 500f1d2b4d3SLarry Finger return; 501f1d2b4d3SLarry Finger 502f1d2b4d3SLarry Finger if (rtlpriv->mac80211.link_state != MAC80211_LINKED) 503f1d2b4d3SLarry Finger return; 504f1d2b4d3SLarry Finger 505f1d2b4d3SLarry Finger if (!rtlpriv->psc.sw_ps_enabled) 506f1d2b4d3SLarry Finger return; 507f1d2b4d3SLarry Finger 508f1d2b4d3SLarry Finger if (rtlpriv->psc.fwctrl_lps) 509f1d2b4d3SLarry Finger return; 510f1d2b4d3SLarry Finger 511f1d2b4d3SLarry Finger if (likely(!(hw->conf.flags & IEEE80211_CONF_PS))) 512f1d2b4d3SLarry Finger return; 513f1d2b4d3SLarry Finger 514f1d2b4d3SLarry Finger /* check if this really is a beacon */ 515f1d2b4d3SLarry Finger if (!ieee80211_is_beacon(hdr->frame_control)) 516f1d2b4d3SLarry Finger return; 517f1d2b4d3SLarry Finger 518f1d2b4d3SLarry Finger /* min. beacon length + FCS_LEN */ 519f1d2b4d3SLarry Finger if (len <= 40 + FCS_LEN) 520f1d2b4d3SLarry Finger return; 521f1d2b4d3SLarry Finger 522f1d2b4d3SLarry Finger /* and only beacons from the associated BSSID, please */ 523f1d2b4d3SLarry Finger if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid)) 524f1d2b4d3SLarry Finger return; 525f1d2b4d3SLarry Finger 526f1d2b4d3SLarry Finger rtlpriv->psc.last_beacon = jiffies; 527f1d2b4d3SLarry Finger 528f1d2b4d3SLarry Finger tim = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_TIM); 529f1d2b4d3SLarry Finger if (!tim) 530f1d2b4d3SLarry Finger return; 531f1d2b4d3SLarry Finger 532f1d2b4d3SLarry Finger if (tim[1] < sizeof(*tim_ie)) 533f1d2b4d3SLarry Finger return; 534f1d2b4d3SLarry Finger 535f1d2b4d3SLarry Finger tim_len = tim[1]; 536f1d2b4d3SLarry Finger tim_ie = (struct ieee80211_tim_ie *) &tim[2]; 537f1d2b4d3SLarry Finger 538f1d2b4d3SLarry Finger if (!WARN_ON_ONCE(!hw->conf.ps_dtim_period)) 539f1d2b4d3SLarry Finger rtlpriv->psc.dtim_counter = tim_ie->dtim_count; 540f1d2b4d3SLarry Finger 541f1d2b4d3SLarry Finger /* Check whenever the PHY can be turned off again. */ 542f1d2b4d3SLarry Finger 543f1d2b4d3SLarry Finger /* 1. What about buffered unicast traffic for our AID? */ 544f1d2b4d3SLarry Finger u_buffed = ieee80211_check_tim(tim_ie, tim_len, 545f1d2b4d3SLarry Finger rtlpriv->mac80211.assoc_id); 546f1d2b4d3SLarry Finger 547f1d2b4d3SLarry Finger /* 2. Maybe the AP wants to send multicast/broadcast data? */ 548f1d2b4d3SLarry Finger m_buffed = tim_ie->bitmap_ctrl & 0x01; 549f1d2b4d3SLarry Finger rtlpriv->psc.multi_buffered = m_buffed; 550f1d2b4d3SLarry Finger 551f1d2b4d3SLarry Finger /* unicast will process by mac80211 through 552f1d2b4d3SLarry Finger * set ~IEEE80211_CONF_PS, So we just check 553f1d2b4d3SLarry Finger * multicast frames here */ 554f1d2b4d3SLarry Finger if (!m_buffed) { 555f1d2b4d3SLarry Finger /* back to low-power land. and delay is 556f1d2b4d3SLarry Finger * prevent null power save frame tx fail */ 557f1d2b4d3SLarry Finger queue_delayed_work(rtlpriv->works.rtl_wq, 558f1d2b4d3SLarry Finger &rtlpriv->works.ps_work, MSECS(5)); 559f1d2b4d3SLarry Finger } else { 560f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG, 561f1d2b4d3SLarry Finger "u_bufferd: %x, m_buffered: %x\n", u_buffed, m_buffed); 562f1d2b4d3SLarry Finger } 563f1d2b4d3SLarry Finger } 564f1d2b4d3SLarry Finger EXPORT_SYMBOL_GPL(rtl_swlps_beacon); 565f1d2b4d3SLarry Finger 566f1d2b4d3SLarry Finger void rtl_swlps_rf_awake(struct ieee80211_hw *hw) 567f1d2b4d3SLarry Finger { 568f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 569f1d2b4d3SLarry Finger struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); 570f1d2b4d3SLarry Finger struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); 571f1d2b4d3SLarry Finger unsigned long flag; 572f1d2b4d3SLarry Finger 573f1d2b4d3SLarry Finger if (!rtlpriv->psc.swctrl_lps) 574f1d2b4d3SLarry Finger return; 575f1d2b4d3SLarry Finger if (mac->link_state != MAC80211_LINKED) 576f1d2b4d3SLarry Finger return; 577f1d2b4d3SLarry Finger 578f1d2b4d3SLarry Finger if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM && 579f1d2b4d3SLarry Finger RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) { 580f1d2b4d3SLarry Finger rtlpriv->intf_ops->disable_aspm(hw); 581f1d2b4d3SLarry Finger RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); 582f1d2b4d3SLarry Finger } 583f1d2b4d3SLarry Finger 584f1d2b4d3SLarry Finger spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag); 58530462b51SLarry Finger rtl_ps_set_rf_state(hw, ERFON, RF_CHANGE_BY_PS); 586f1d2b4d3SLarry Finger spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); 587f1d2b4d3SLarry Finger } 588f1d2b4d3SLarry Finger 589f1d2b4d3SLarry Finger void rtl_swlps_rfon_wq_callback(void *data) 590f1d2b4d3SLarry Finger { 591f1d2b4d3SLarry Finger struct rtl_works *rtlworks = 592f1d2b4d3SLarry Finger container_of_dwork_rtl(data, struct rtl_works, ps_rfon_wq); 593f1d2b4d3SLarry Finger struct ieee80211_hw *hw = rtlworks->hw; 594f1d2b4d3SLarry Finger 595f1d2b4d3SLarry Finger rtl_swlps_rf_awake(hw); 596f1d2b4d3SLarry Finger } 597f1d2b4d3SLarry Finger 598f1d2b4d3SLarry Finger void rtl_swlps_rf_sleep(struct ieee80211_hw *hw) 599f1d2b4d3SLarry Finger { 600f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 601f1d2b4d3SLarry Finger struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); 602f1d2b4d3SLarry Finger struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); 603f1d2b4d3SLarry Finger unsigned long flag; 604f1d2b4d3SLarry Finger u8 sleep_intv; 605f1d2b4d3SLarry Finger 606f1d2b4d3SLarry Finger if (!rtlpriv->psc.sw_ps_enabled) 607f1d2b4d3SLarry Finger return; 608f1d2b4d3SLarry Finger 609f1d2b4d3SLarry Finger if ((rtlpriv->sec.being_setkey) || 610f1d2b4d3SLarry Finger (mac->opmode == NL80211_IFTYPE_ADHOC)) 611f1d2b4d3SLarry Finger return; 612f1d2b4d3SLarry Finger 613f1d2b4d3SLarry Finger /*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */ 614f1d2b4d3SLarry Finger if ((mac->link_state != MAC80211_LINKED) || (mac->cnt_after_linked < 5)) 615f1d2b4d3SLarry Finger return; 616f1d2b4d3SLarry Finger 617f1d2b4d3SLarry Finger if (rtlpriv->link_info.busytraffic) 618f1d2b4d3SLarry Finger return; 619f1d2b4d3SLarry Finger 620f1d2b4d3SLarry Finger spin_lock(&rtlpriv->locks.rf_ps_lock); 621f1d2b4d3SLarry Finger if (rtlpriv->psc.rfchange_inprogress) { 622f1d2b4d3SLarry Finger spin_unlock(&rtlpriv->locks.rf_ps_lock); 623f1d2b4d3SLarry Finger return; 624f1d2b4d3SLarry Finger } 625f1d2b4d3SLarry Finger spin_unlock(&rtlpriv->locks.rf_ps_lock); 626f1d2b4d3SLarry Finger 627f1d2b4d3SLarry Finger spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag); 62830462b51SLarry Finger rtl_ps_set_rf_state(hw, ERFSLEEP, RF_CHANGE_BY_PS); 629f1d2b4d3SLarry Finger spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); 630f1d2b4d3SLarry Finger 631f1d2b4d3SLarry Finger if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM && 632f1d2b4d3SLarry Finger !RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) { 633f1d2b4d3SLarry Finger rtlpriv->intf_ops->enable_aspm(hw); 634f1d2b4d3SLarry Finger RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); 635f1d2b4d3SLarry Finger } 636f1d2b4d3SLarry Finger 637f1d2b4d3SLarry Finger /* here is power save alg, when this beacon is DTIM 638f1d2b4d3SLarry Finger * we will set sleep time to dtim_period * n; 639f1d2b4d3SLarry Finger * when this beacon is not DTIM, we will set sleep 640f1d2b4d3SLarry Finger * time to sleep_intv = rtlpriv->psc.dtim_counter or 641f1d2b4d3SLarry Finger * MAX_SW_LPS_SLEEP_INTV(default set to 5) */ 642f1d2b4d3SLarry Finger 643f1d2b4d3SLarry Finger if (rtlpriv->psc.dtim_counter == 0) { 644f1d2b4d3SLarry Finger if (hw->conf.ps_dtim_period == 1) 645f1d2b4d3SLarry Finger sleep_intv = hw->conf.ps_dtim_period * 2; 646f1d2b4d3SLarry Finger else 647f1d2b4d3SLarry Finger sleep_intv = hw->conf.ps_dtim_period; 648f1d2b4d3SLarry Finger } else { 649f1d2b4d3SLarry Finger sleep_intv = rtlpriv->psc.dtim_counter; 650f1d2b4d3SLarry Finger } 651f1d2b4d3SLarry Finger 652f1d2b4d3SLarry Finger if (sleep_intv > MAX_SW_LPS_SLEEP_INTV) 653f1d2b4d3SLarry Finger sleep_intv = MAX_SW_LPS_SLEEP_INTV; 654f1d2b4d3SLarry Finger 655f1d2b4d3SLarry Finger /* this print should always be dtim_conter = 0 & 656f1d2b4d3SLarry Finger * sleep = dtim_period, that meaons, we should 657f1d2b4d3SLarry Finger * awake before every dtim */ 658f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG, 659f1d2b4d3SLarry Finger "dtim_counter:%x will sleep :%d beacon_intv\n", 660f1d2b4d3SLarry Finger rtlpriv->psc.dtim_counter, sleep_intv); 661f1d2b4d3SLarry Finger 662f1d2b4d3SLarry Finger /* we tested that 40ms is enough for sw & hw sw delay */ 663f1d2b4d3SLarry Finger queue_delayed_work(rtlpriv->works.rtl_wq, &rtlpriv->works.ps_rfon_wq, 664f1d2b4d3SLarry Finger MSECS(sleep_intv * mac->vif->bss_conf.beacon_int - 40)); 665f1d2b4d3SLarry Finger } 666f1d2b4d3SLarry Finger 667f1d2b4d3SLarry Finger void rtl_lps_change_work_callback(struct work_struct *work) 668f1d2b4d3SLarry Finger { 669f1d2b4d3SLarry Finger struct rtl_works *rtlworks = 670f1d2b4d3SLarry Finger container_of(work, struct rtl_works, lps_change_work); 671f1d2b4d3SLarry Finger struct ieee80211_hw *hw = rtlworks->hw; 672f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 673f1d2b4d3SLarry Finger 674f1d2b4d3SLarry Finger if (rtlpriv->enter_ps) 675ba9f93f8SLarry Finger rtl_lps_enter_core(hw); 676f1d2b4d3SLarry Finger else 677ba9f93f8SLarry Finger rtl_lps_leave_core(hw); 678f1d2b4d3SLarry Finger } 679f1d2b4d3SLarry Finger EXPORT_SYMBOL_GPL(rtl_lps_change_work_callback); 680f1d2b4d3SLarry Finger 681ba9f93f8SLarry Finger void rtl_lps_enter(struct ieee80211_hw *hw) 682ba9f93f8SLarry Finger { 683ba9f93f8SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 684ba9f93f8SLarry Finger 685ba9f93f8SLarry Finger if (!in_interrupt()) 686ba9f93f8SLarry Finger return rtl_lps_enter_core(hw); 687ba9f93f8SLarry Finger rtlpriv->enter_ps = true; 688ba9f93f8SLarry Finger schedule_work(&rtlpriv->works.lps_change_work); 689ba9f93f8SLarry Finger } 690ba9f93f8SLarry Finger EXPORT_SYMBOL_GPL(rtl_lps_enter); 691ba9f93f8SLarry Finger 692ba9f93f8SLarry Finger void rtl_lps_leave(struct ieee80211_hw *hw) 693ba9f93f8SLarry Finger { 694ba9f93f8SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 695ba9f93f8SLarry Finger 696ba9f93f8SLarry Finger if (!in_interrupt()) 697ba9f93f8SLarry Finger return rtl_lps_leave_core(hw); 698ba9f93f8SLarry Finger rtlpriv->enter_ps = false; 699ba9f93f8SLarry Finger schedule_work(&rtlpriv->works.lps_change_work); 700ba9f93f8SLarry Finger } 701ba9f93f8SLarry Finger EXPORT_SYMBOL_GPL(rtl_lps_leave); 702ba9f93f8SLarry Finger 703f1d2b4d3SLarry Finger void rtl_swlps_wq_callback(void *data) 704f1d2b4d3SLarry Finger { 705f1d2b4d3SLarry Finger struct rtl_works *rtlworks = container_of_dwork_rtl(data, 706f1d2b4d3SLarry Finger struct rtl_works, 707f1d2b4d3SLarry Finger ps_work); 708f1d2b4d3SLarry Finger struct ieee80211_hw *hw = rtlworks->hw; 709f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 710f1d2b4d3SLarry Finger bool ps = false; 711f1d2b4d3SLarry Finger 712f1d2b4d3SLarry Finger ps = (hw->conf.flags & IEEE80211_CONF_PS); 713f1d2b4d3SLarry Finger 714f1d2b4d3SLarry Finger /* we can sleep after ps null send ok */ 715f1d2b4d3SLarry Finger if (rtlpriv->psc.state_inap) { 716f1d2b4d3SLarry Finger rtl_swlps_rf_sleep(hw); 717f1d2b4d3SLarry Finger 718f1d2b4d3SLarry Finger if (rtlpriv->psc.state && !ps) { 719f1d2b4d3SLarry Finger rtlpriv->psc.sleep_ms = jiffies_to_msecs(jiffies - 720f1d2b4d3SLarry Finger rtlpriv->psc.last_action); 721f1d2b4d3SLarry Finger } 722f1d2b4d3SLarry Finger 723f1d2b4d3SLarry Finger if (ps) 724f1d2b4d3SLarry Finger rtlpriv->psc.last_slept = jiffies; 725f1d2b4d3SLarry Finger 726f1d2b4d3SLarry Finger rtlpriv->psc.last_action = jiffies; 727f1d2b4d3SLarry Finger rtlpriv->psc.state = ps; 728f1d2b4d3SLarry Finger } 729f1d2b4d3SLarry Finger } 730f1d2b4d3SLarry Finger 731f1d2b4d3SLarry Finger static void rtl_p2p_noa_ie(struct ieee80211_hw *hw, void *data, 732f1d2b4d3SLarry Finger unsigned int len) 733f1d2b4d3SLarry Finger { 734f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 735f1d2b4d3SLarry Finger struct ieee80211_mgmt *mgmt = data; 736f1d2b4d3SLarry Finger struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info); 737f1d2b4d3SLarry Finger u8 *pos, *end, *ie; 738f1d2b4d3SLarry Finger u16 noa_len; 739f1d2b4d3SLarry Finger static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09}; 740f1d2b4d3SLarry Finger u8 noa_num, index , i, noa_index = 0; 741f1d2b4d3SLarry Finger bool find_p2p_ie = false , find_p2p_ps_ie = false; 742f1d2b4d3SLarry Finger pos = (u8 *)mgmt->u.beacon.variable; 743f1d2b4d3SLarry Finger end = data + len; 744f1d2b4d3SLarry Finger ie = NULL; 745f1d2b4d3SLarry Finger 746f1d2b4d3SLarry Finger while (pos + 1 < end) { 747f1d2b4d3SLarry Finger if (pos + 2 + pos[1] > end) 748f1d2b4d3SLarry Finger return; 749f1d2b4d3SLarry Finger 750f1d2b4d3SLarry Finger if (pos[0] == 221 && pos[1] > 4) { 751f1d2b4d3SLarry Finger if (memcmp(&pos[2], p2p_oui_ie_type, 4) == 0) { 752f1d2b4d3SLarry Finger ie = pos + 2+4; 753f1d2b4d3SLarry Finger break; 754f1d2b4d3SLarry Finger } 755f1d2b4d3SLarry Finger } 756f1d2b4d3SLarry Finger pos += 2 + pos[1]; 757f1d2b4d3SLarry Finger } 758f1d2b4d3SLarry Finger 759f1d2b4d3SLarry Finger if (ie == NULL) 760f1d2b4d3SLarry Finger return; 761f1d2b4d3SLarry Finger find_p2p_ie = true; 762f1d2b4d3SLarry Finger /*to find noa ie*/ 763f1d2b4d3SLarry Finger while (ie + 1 < end) { 764f1d2b4d3SLarry Finger noa_len = READEF2BYTE((__le16 *)&ie[1]); 765f1d2b4d3SLarry Finger if (ie + 3 + ie[1] > end) 766f1d2b4d3SLarry Finger return; 767f1d2b4d3SLarry Finger 768f1d2b4d3SLarry Finger if (ie[0] == 12) { 769f1d2b4d3SLarry Finger find_p2p_ps_ie = true; 770f1d2b4d3SLarry Finger if ((noa_len - 2) % 13 != 0) { 771f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, 772f1d2b4d3SLarry Finger "P2P notice of absence: invalid length.%d\n", 773f1d2b4d3SLarry Finger noa_len); 774f1d2b4d3SLarry Finger return; 775f1d2b4d3SLarry Finger } else { 776f1d2b4d3SLarry Finger noa_num = (noa_len - 2) / 13; 777f1d2b4d3SLarry Finger } 778f1d2b4d3SLarry Finger noa_index = ie[3]; 779f1d2b4d3SLarry Finger if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode == 780f1d2b4d3SLarry Finger P2P_PS_NONE || noa_index != p2pinfo->noa_index) { 781f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, 782f1d2b4d3SLarry Finger "update NOA ie.\n"); 783f1d2b4d3SLarry Finger p2pinfo->noa_index = noa_index; 784f1d2b4d3SLarry Finger p2pinfo->opp_ps = (ie[4] >> 7); 785f1d2b4d3SLarry Finger p2pinfo->ctwindow = ie[4] & 0x7F; 786f1d2b4d3SLarry Finger p2pinfo->noa_num = noa_num; 787f1d2b4d3SLarry Finger index = 5; 788f1d2b4d3SLarry Finger for (i = 0; i < noa_num; i++) { 789f1d2b4d3SLarry Finger p2pinfo->noa_count_type[i] = 790f1d2b4d3SLarry Finger READEF1BYTE(ie+index); 791f1d2b4d3SLarry Finger index += 1; 792f1d2b4d3SLarry Finger p2pinfo->noa_duration[i] = 793f1d2b4d3SLarry Finger READEF4BYTE((__le32 *)ie+index); 794f1d2b4d3SLarry Finger index += 4; 795f1d2b4d3SLarry Finger p2pinfo->noa_interval[i] = 796f1d2b4d3SLarry Finger READEF4BYTE((__le32 *)ie+index); 797f1d2b4d3SLarry Finger index += 4; 798f1d2b4d3SLarry Finger p2pinfo->noa_start_time[i] = 799f1d2b4d3SLarry Finger READEF4BYTE((__le32 *)ie+index); 800f1d2b4d3SLarry Finger index += 4; 801f1d2b4d3SLarry Finger } 802f1d2b4d3SLarry Finger 803f1d2b4d3SLarry Finger if (p2pinfo->opp_ps == 1) { 804f1d2b4d3SLarry Finger p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW; 805f1d2b4d3SLarry Finger /* Driver should wait LPS entering 806f1d2b4d3SLarry Finger * CTWindow 807f1d2b4d3SLarry Finger */ 808f1d2b4d3SLarry Finger if (rtlpriv->psc.fw_current_inpsmode) 809f1d2b4d3SLarry Finger rtl_p2p_ps_cmd(hw, 810f1d2b4d3SLarry Finger P2P_PS_ENABLE); 811f1d2b4d3SLarry Finger } else if (p2pinfo->noa_num > 0) { 812f1d2b4d3SLarry Finger p2pinfo->p2p_ps_mode = P2P_PS_NOA; 813f1d2b4d3SLarry Finger rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE); 814f1d2b4d3SLarry Finger } else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) { 815f1d2b4d3SLarry Finger rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE); 816f1d2b4d3SLarry Finger } 817f1d2b4d3SLarry Finger } 818f1d2b4d3SLarry Finger break; 819f1d2b4d3SLarry Finger } 820f1d2b4d3SLarry Finger ie += 3 + noa_len; 821f1d2b4d3SLarry Finger } 822f1d2b4d3SLarry Finger 823f1d2b4d3SLarry Finger if (find_p2p_ie == true) { 824f1d2b4d3SLarry Finger if ((p2pinfo->p2p_ps_mode > P2P_PS_NONE) && 825f1d2b4d3SLarry Finger (find_p2p_ps_ie == false)) 826f1d2b4d3SLarry Finger rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE); 827f1d2b4d3SLarry Finger } 828f1d2b4d3SLarry Finger } 829f1d2b4d3SLarry Finger 830f1d2b4d3SLarry Finger static void rtl_p2p_action_ie(struct ieee80211_hw *hw, void *data, 831f1d2b4d3SLarry Finger unsigned int len) 832f1d2b4d3SLarry Finger { 833f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 834f1d2b4d3SLarry Finger struct ieee80211_mgmt *mgmt = data; 835f1d2b4d3SLarry Finger struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info); 836f1d2b4d3SLarry Finger u8 noa_num, index , i , noa_index = 0; 837f1d2b4d3SLarry Finger u8 *pos, *end, *ie; 838f1d2b4d3SLarry Finger u16 noa_len; 839f1d2b4d3SLarry Finger static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09}; 840f1d2b4d3SLarry Finger 841f1d2b4d3SLarry Finger pos = (u8 *)&mgmt->u.action.category; 842f1d2b4d3SLarry Finger end = data + len; 843f1d2b4d3SLarry Finger ie = NULL; 844f1d2b4d3SLarry Finger 845f1d2b4d3SLarry Finger if (pos[0] == 0x7f) { 846f1d2b4d3SLarry Finger if (memcmp(&pos[1], p2p_oui_ie_type, 4) == 0) 847f1d2b4d3SLarry Finger ie = pos + 3+4; 848f1d2b4d3SLarry Finger } 849f1d2b4d3SLarry Finger 850f1d2b4d3SLarry Finger if (ie == NULL) 851f1d2b4d3SLarry Finger return; 852f1d2b4d3SLarry Finger 853f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "action frame find P2P IE.\n"); 854f1d2b4d3SLarry Finger /*to find noa ie*/ 855f1d2b4d3SLarry Finger while (ie + 1 < end) { 856f1d2b4d3SLarry Finger noa_len = READEF2BYTE((__le16 *)&ie[1]); 857f1d2b4d3SLarry Finger if (ie + 3 + ie[1] > end) 858f1d2b4d3SLarry Finger return; 859f1d2b4d3SLarry Finger 860f1d2b4d3SLarry Finger if (ie[0] == 12) { 861f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "find NOA IE.\n"); 862f1d2b4d3SLarry Finger RT_PRINT_DATA(rtlpriv, COMP_FW, DBG_LOUD, "noa ie ", 863f1d2b4d3SLarry Finger ie, noa_len); 864f1d2b4d3SLarry Finger if ((noa_len - 2) % 13 != 0) { 865f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, 866f1d2b4d3SLarry Finger "P2P notice of absence: invalid length.%d\n", 867f1d2b4d3SLarry Finger noa_len); 868f1d2b4d3SLarry Finger return; 869f1d2b4d3SLarry Finger } else { 870f1d2b4d3SLarry Finger noa_num = (noa_len - 2) / 13; 871f1d2b4d3SLarry Finger } 872f1d2b4d3SLarry Finger noa_index = ie[3]; 873f1d2b4d3SLarry Finger if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode == 874f1d2b4d3SLarry Finger P2P_PS_NONE || noa_index != p2pinfo->noa_index) { 875f1d2b4d3SLarry Finger p2pinfo->noa_index = noa_index; 876f1d2b4d3SLarry Finger p2pinfo->opp_ps = (ie[4] >> 7); 877f1d2b4d3SLarry Finger p2pinfo->ctwindow = ie[4] & 0x7F; 878f1d2b4d3SLarry Finger p2pinfo->noa_num = noa_num; 879f1d2b4d3SLarry Finger index = 5; 880f1d2b4d3SLarry Finger for (i = 0; i < noa_num; i++) { 881f1d2b4d3SLarry Finger p2pinfo->noa_count_type[i] = 882f1d2b4d3SLarry Finger READEF1BYTE(ie+index); 883f1d2b4d3SLarry Finger index += 1; 884f1d2b4d3SLarry Finger p2pinfo->noa_duration[i] = 885f1d2b4d3SLarry Finger READEF4BYTE((__le32 *)ie+index); 886f1d2b4d3SLarry Finger index += 4; 887f1d2b4d3SLarry Finger p2pinfo->noa_interval[i] = 888f1d2b4d3SLarry Finger READEF4BYTE((__le32 *)ie+index); 889f1d2b4d3SLarry Finger index += 4; 890f1d2b4d3SLarry Finger p2pinfo->noa_start_time[i] = 891f1d2b4d3SLarry Finger READEF4BYTE((__le32 *)ie+index); 892f1d2b4d3SLarry Finger index += 4; 893f1d2b4d3SLarry Finger } 894f1d2b4d3SLarry Finger 895f1d2b4d3SLarry Finger if (p2pinfo->opp_ps == 1) { 896f1d2b4d3SLarry Finger p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW; 897f1d2b4d3SLarry Finger /* Driver should wait LPS entering 898f1d2b4d3SLarry Finger * CTWindow 899f1d2b4d3SLarry Finger */ 900f1d2b4d3SLarry Finger if (rtlpriv->psc.fw_current_inpsmode) 901f1d2b4d3SLarry Finger rtl_p2p_ps_cmd(hw, 902f1d2b4d3SLarry Finger P2P_PS_ENABLE); 903f1d2b4d3SLarry Finger } else if (p2pinfo->noa_num > 0) { 904f1d2b4d3SLarry Finger p2pinfo->p2p_ps_mode = P2P_PS_NOA; 905f1d2b4d3SLarry Finger rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE); 906f1d2b4d3SLarry Finger } else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) { 907f1d2b4d3SLarry Finger rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE); 908f1d2b4d3SLarry Finger } 909f1d2b4d3SLarry Finger } 910f1d2b4d3SLarry Finger break; 911f1d2b4d3SLarry Finger } 912f1d2b4d3SLarry Finger ie += 3 + noa_len; 913f1d2b4d3SLarry Finger } 914f1d2b4d3SLarry Finger } 915f1d2b4d3SLarry Finger 916f1d2b4d3SLarry Finger void rtl_p2p_ps_cmd(struct ieee80211_hw *hw , u8 p2p_ps_state) 917f1d2b4d3SLarry Finger { 918f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 919f1d2b4d3SLarry Finger struct rtl_ps_ctl *rtlps = rtl_psc(rtl_priv(hw)); 920f1d2b4d3SLarry Finger struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info); 921f1d2b4d3SLarry Finger 922f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, " p2p state %x\n" , p2p_ps_state); 923f1d2b4d3SLarry Finger switch (p2p_ps_state) { 924f1d2b4d3SLarry Finger case P2P_PS_DISABLE: 925f1d2b4d3SLarry Finger p2pinfo->p2p_ps_state = p2p_ps_state; 926f1d2b4d3SLarry Finger rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_P2P_PS_OFFLOAD, 927f1d2b4d3SLarry Finger &p2p_ps_state); 928f1d2b4d3SLarry Finger p2pinfo->noa_index = 0; 929f1d2b4d3SLarry Finger p2pinfo->ctwindow = 0; 930f1d2b4d3SLarry Finger p2pinfo->opp_ps = 0; 931f1d2b4d3SLarry Finger p2pinfo->noa_num = 0; 932f1d2b4d3SLarry Finger p2pinfo->p2p_ps_mode = P2P_PS_NONE; 933f1d2b4d3SLarry Finger if (rtlps->fw_current_inpsmode) { 934f1d2b4d3SLarry Finger if (rtlps->smart_ps == 0) { 935f1d2b4d3SLarry Finger rtlps->smart_ps = 2; 936f1d2b4d3SLarry Finger rtlpriv->cfg->ops->set_hw_reg(hw, 937f1d2b4d3SLarry Finger HW_VAR_H2C_FW_PWRMODE, 938f1d2b4d3SLarry Finger &rtlps->pwr_mode); 939f1d2b4d3SLarry Finger } 940f1d2b4d3SLarry Finger 941f1d2b4d3SLarry Finger } 942f1d2b4d3SLarry Finger break; 943f1d2b4d3SLarry Finger case P2P_PS_ENABLE: 944f1d2b4d3SLarry Finger if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) { 945f1d2b4d3SLarry Finger p2pinfo->p2p_ps_state = p2p_ps_state; 946f1d2b4d3SLarry Finger 947f1d2b4d3SLarry Finger if (p2pinfo->ctwindow > 0) { 948f1d2b4d3SLarry Finger if (rtlps->smart_ps != 0) { 949f1d2b4d3SLarry Finger rtlps->smart_ps = 0; 950f1d2b4d3SLarry Finger rtlpriv->cfg->ops->set_hw_reg(hw, 951f1d2b4d3SLarry Finger HW_VAR_H2C_FW_PWRMODE, 952f1d2b4d3SLarry Finger &rtlps->pwr_mode); 953f1d2b4d3SLarry Finger } 954f1d2b4d3SLarry Finger } 955f1d2b4d3SLarry Finger rtlpriv->cfg->ops->set_hw_reg(hw, 956f1d2b4d3SLarry Finger HW_VAR_H2C_FW_P2P_PS_OFFLOAD, 957f1d2b4d3SLarry Finger &p2p_ps_state); 958f1d2b4d3SLarry Finger 959f1d2b4d3SLarry Finger } 960f1d2b4d3SLarry Finger break; 961f1d2b4d3SLarry Finger case P2P_PS_SCAN: 962f1d2b4d3SLarry Finger case P2P_PS_SCAN_DONE: 963f1d2b4d3SLarry Finger case P2P_PS_ALLSTASLEEP: 964f1d2b4d3SLarry Finger if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) { 965f1d2b4d3SLarry Finger p2pinfo->p2p_ps_state = p2p_ps_state; 966f1d2b4d3SLarry Finger rtlpriv->cfg->ops->set_hw_reg(hw, 967f1d2b4d3SLarry Finger HW_VAR_H2C_FW_P2P_PS_OFFLOAD, 968f1d2b4d3SLarry Finger &p2p_ps_state); 969f1d2b4d3SLarry Finger } 970f1d2b4d3SLarry Finger break; 971f1d2b4d3SLarry Finger default: 972f1d2b4d3SLarry Finger break; 973f1d2b4d3SLarry Finger } 974f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, 975f1d2b4d3SLarry Finger "ctwindow %x oppps %x\n", 976f1d2b4d3SLarry Finger p2pinfo->ctwindow , p2pinfo->opp_ps); 977f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, 978f1d2b4d3SLarry Finger "count %x duration %x index %x interval %x start time %x noa num %x\n", 979f1d2b4d3SLarry Finger p2pinfo->noa_count_type[0], 980f1d2b4d3SLarry Finger p2pinfo->noa_duration[0], 981f1d2b4d3SLarry Finger p2pinfo->noa_index, 982f1d2b4d3SLarry Finger p2pinfo->noa_interval[0], 983f1d2b4d3SLarry Finger p2pinfo->noa_start_time[0], 984f1d2b4d3SLarry Finger p2pinfo->noa_num); 985f1d2b4d3SLarry Finger RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "end\n"); 986f1d2b4d3SLarry Finger } 987f1d2b4d3SLarry Finger 988f1d2b4d3SLarry Finger void rtl_p2p_info(struct ieee80211_hw *hw, void *data, unsigned int len) 989f1d2b4d3SLarry Finger { 990f1d2b4d3SLarry Finger struct rtl_priv *rtlpriv = rtl_priv(hw); 991f1d2b4d3SLarry Finger struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); 992f1d2b4d3SLarry Finger struct ieee80211_hdr *hdr = data; 993f1d2b4d3SLarry Finger 994f1d2b4d3SLarry Finger if (!mac->p2p) 995f1d2b4d3SLarry Finger return; 996f1d2b4d3SLarry Finger if (mac->link_state != MAC80211_LINKED) 997f1d2b4d3SLarry Finger return; 998f1d2b4d3SLarry Finger /* min. beacon length + FCS_LEN */ 999f1d2b4d3SLarry Finger if (len <= 40 + FCS_LEN) 1000f1d2b4d3SLarry Finger return; 1001f1d2b4d3SLarry Finger 1002f1d2b4d3SLarry Finger /* and only beacons from the associated BSSID, please */ 1003f1d2b4d3SLarry Finger if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid)) 1004f1d2b4d3SLarry Finger return; 1005f1d2b4d3SLarry Finger 1006f1d2b4d3SLarry Finger /* check if this really is a beacon */ 1007f1d2b4d3SLarry Finger if (!(ieee80211_is_beacon(hdr->frame_control) || 1008f1d2b4d3SLarry Finger ieee80211_is_probe_resp(hdr->frame_control) || 1009f1d2b4d3SLarry Finger ieee80211_is_action(hdr->frame_control))) 1010f1d2b4d3SLarry Finger return; 1011f1d2b4d3SLarry Finger 1012f1d2b4d3SLarry Finger if (ieee80211_is_action(hdr->frame_control)) 1013f1d2b4d3SLarry Finger rtl_p2p_action_ie(hw , data , len - FCS_LEN); 1014f1d2b4d3SLarry Finger else 1015f1d2b4d3SLarry Finger rtl_p2p_noa_ie(hw , data , len - FCS_LEN); 1016f1d2b4d3SLarry Finger } 1017f1d2b4d3SLarry Finger EXPORT_SYMBOL_GPL(rtl_p2p_info); 1018