diff --git a/include/net/mac80211.h b/include/net/mac80211.h index fe1a3a603375..7f256e23c57f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1271,6 +1271,15 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, * dynamic PS feature in stack and will just keep %IEEE80211_CONF_PS * enabled whenever user has enabled powersave. * + * Some hardware need to toggle a single shared antenna between WLAN and + * Bluetooth to facilitate co-existence. These types of hardware set + * limitations on the use of host controlled dynamic powersave whenever there + * is simultaneous WLAN and Bluetooth traffic. For these types of hardware, the + * driver may request temporarily going into full power save, in order to + * enable toggling the antenna between BT and WLAN. If the driver requests + * disabling dynamic powersave, the @dynamic_ps_timeout value will be + * temporarily set to zero until the driver re-enables dynamic powersave. + * * Driver informs U-APSD client support by enabling * %IEEE80211_HW_SUPPORTS_UAPSD flag. The mode is configured through the * uapsd paramater in conf_tx() operation. Hardware needs to send the QoS @@ -2446,6 +2455,36 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif); */ void ieee80211_connection_loss(struct ieee80211_vif *vif); +/** + * ieee80211_disable_dyn_ps - force mac80211 to temporarily disable dynamic psm + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * Some hardware require full power save to manage simultaneous BT traffic + * on the WLAN frequency. Full PSM is required periodically, whenever there are + * burst of BT traffic. The hardware gets information of BT traffic via + * hardware co-existence lines, and consequentially requests mac80211 to + * (temporarily) enter full psm. + * This function will only temporarily disable dynamic PS, not enable PSM if + * it was not already enabled. + * The driver must make sure to re-enable dynamic PS using + * ieee80211_enable_dyn_ps() if the driver has disabled it. + * + */ +void ieee80211_disable_dyn_ps(struct ieee80211_vif *vif); + +/** + * ieee80211_enable_dyn_ps - restore dynamic psm after being disabled + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * This function restores dynamic PS after being temporarily disabled via + * ieee80211_disable_dyn_ps(). Each ieee80211_disable_dyn_ps() call must + * be coupled with an eventual call to this function. + * + */ +void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif); + /** * ieee80211_cqm_rssi_notify - inform a configured connection quality monitoring * rssi threshold triggered diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index fb5430188e87..f9251d50192c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -855,6 +855,8 @@ struct ieee80211_local { * this will override whatever chosen by mac80211 internally. */ int dynamic_ps_forced_timeout; + int dynamic_ps_user_timeout; + bool disable_dynamic_ps; int user_power_level; /* in dBm */ int power_constr_level; /* in dBm */ diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 85c3ca33333e..d1962650b254 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -478,6 +478,39 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, } } +void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_local *local = sdata->local; + struct ieee80211_conf *conf = &local->hw.conf; + + WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION || + !(local->hw.flags & IEEE80211_HW_SUPPORTS_PS) || + (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)); + + local->disable_dynamic_ps = false; + conf->dynamic_ps_timeout = local->dynamic_ps_user_timeout; +} +EXPORT_SYMBOL(ieee80211_enable_dyn_ps); + +void ieee80211_disable_dyn_ps(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_local *local = sdata->local; + struct ieee80211_conf *conf = &local->hw.conf; + + WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION || + !(local->hw.flags & IEEE80211_HW_SUPPORTS_PS) || + (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)); + + local->disable_dynamic_ps = true; + conf->dynamic_ps_timeout = 0; + del_timer_sync(&local->dynamic_ps_timer); + ieee80211_queue_work(&local->hw, + &local->dynamic_ps_enable_work); +} +EXPORT_SYMBOL(ieee80211_disable_dyn_ps); + /* powersave */ static void ieee80211_enable_ps(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) @@ -553,6 +586,7 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) found->u.mgd.associated->beacon_ies && !(found->u.mgd.flags & (IEEE80211_STA_BEACON_POLL | IEEE80211_STA_CONNECTION_POLL))) { + struct ieee80211_conf *conf = &local->hw.conf; s32 beaconint_us; if (latency < 0) @@ -575,7 +609,10 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) else timeout = 100; } - local->hw.conf.dynamic_ps_timeout = timeout; + local->dynamic_ps_user_timeout = timeout; + if (!local->disable_dynamic_ps) + conf->dynamic_ps_timeout = + local->dynamic_ps_user_timeout; if (beaconint_us > latency) { local->ps_sdata = NULL;