Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include <linux/export.h>
00016 #include <net/mac80211.h>
00017 #include "ieee80211_i.h"
00018 #include "driver-trace.h"
00019
00020
00021
00022
00023
00024
00025
00026
00027 static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata,
00028 bool tell_ap)
00029 {
00030 struct ieee80211_local *local = sdata->local;
00031 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
00032
00033 local->offchannel_ps_enabled = false;
00034
00035
00036
00037 del_timer_sync(&local->dynamic_ps_timer);
00038 del_timer_sync(&ifmgd->bcn_mon_timer);
00039 del_timer_sync(&ifmgd->conn_mon_timer);
00040
00041 cancel_work_sync(&local->dynamic_ps_enable_work);
00042
00043 if (local->hw.conf.flags & IEEE80211_CONF_PS) {
00044 local->offchannel_ps_enabled = true;
00045 local->hw.conf.flags &= ~IEEE80211_CONF_PS;
00046 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
00047 }
00048
00049 if (tell_ap && (!local->offchannel_ps_enabled ||
00050 !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)))
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061 ieee80211_send_nullfunc(local, sdata, 1);
00062 }
00063
00064
00065 static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
00066 {
00067 struct ieee80211_local *local = sdata->local;
00068
00069 if (!local->ps_sdata)
00070 ieee80211_send_nullfunc(local, sdata, 0);
00071 else if (local->offchannel_ps_enabled) {
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088 local->hw.conf.flags |= IEEE80211_CONF_PS;
00089 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
00090 } else if (local->hw.conf.dynamic_ps_timeout > 0) {
00091
00092
00093
00094
00095
00096
00097 ieee80211_send_nullfunc(local, sdata, 0);
00098 mod_timer(&local->dynamic_ps_timer, jiffies +
00099 msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
00100 }
00101
00102 ieee80211_sta_reset_beacon_monitor(sdata);
00103 ieee80211_sta_reset_conn_monitor(sdata);
00104 }
00105
00106 void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
00107 bool offchannel_ps_enable)
00108 {
00109 struct ieee80211_sub_if_data *sdata;
00110
00111
00112
00113
00114
00115 mutex_lock(&local->iflist_mtx);
00116 list_for_each_entry(sdata, &local->interfaces, list) {
00117 if (!ieee80211_sdata_running(sdata))
00118 continue;
00119
00120 if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
00121 set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
00122
00123
00124 if (sdata->vif.type == NL80211_IFTYPE_AP ||
00125 sdata->vif.type == NL80211_IFTYPE_ADHOC ||
00126 sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
00127 ieee80211_bss_info_change_notify(
00128 sdata, BSS_CHANGED_BEACON_ENABLED);
00129
00130 if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
00131 netif_tx_stop_all_queues(sdata->dev);
00132 if (offchannel_ps_enable &&
00133 (sdata->vif.type == NL80211_IFTYPE_STATION) &&
00134 sdata->u.mgd.associated)
00135 ieee80211_offchannel_ps_enable(sdata, true);
00136 }
00137 }
00138 mutex_unlock(&local->iflist_mtx);
00139 }
00140
00141 void ieee80211_offchannel_return(struct ieee80211_local *local,
00142 bool offchannel_ps_disable)
00143 {
00144 struct ieee80211_sub_if_data *sdata;
00145
00146 mutex_lock(&local->iflist_mtx);
00147 list_for_each_entry(sdata, &local->interfaces, list) {
00148 if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
00149 clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
00150
00151 if (!ieee80211_sdata_running(sdata))
00152 continue;
00153
00154
00155 if (offchannel_ps_disable &&
00156 sdata->vif.type == NL80211_IFTYPE_STATION) {
00157 if (sdata->u.mgd.associated)
00158 ieee80211_offchannel_ps_disable(sdata);
00159 }
00160
00161 if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172 netif_tx_wake_all_queues(sdata->dev);
00173 }
00174
00175 if (sdata->vif.type == NL80211_IFTYPE_AP ||
00176 sdata->vif.type == NL80211_IFTYPE_ADHOC ||
00177 sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
00178 ieee80211_bss_info_change_notify(
00179 sdata, BSS_CHANGED_BEACON_ENABLED);
00180 }
00181 mutex_unlock(&local->iflist_mtx);
00182 }
00183
00184 static void ieee80211_hw_roc_start(struct work_struct *work)
00185 {
00186 struct ieee80211_local *local =
00187 container_of(work, struct ieee80211_local, hw_roc_start);
00188 struct ieee80211_sub_if_data *sdata;
00189
00190 mutex_lock(&local->mtx);
00191
00192 if (!local->hw_roc_channel) {
00193 mutex_unlock(&local->mtx);
00194 return;
00195 }
00196
00197 if (local->hw_roc_skb) {
00198 sdata = IEEE80211_DEV_TO_SUB_IF(local->hw_roc_dev);
00199 ieee80211_tx_skb(sdata, local->hw_roc_skb);
00200 local->hw_roc_skb = NULL;
00201 } else {
00202 cfg80211_ready_on_channel(local->hw_roc_dev,
00203 local->hw_roc_cookie,
00204 local->hw_roc_channel,
00205 local->hw_roc_channel_type,
00206 local->hw_roc_duration,
00207 GFP_KERNEL);
00208 }
00209
00210 ieee80211_recalc_idle(local);
00211
00212 mutex_unlock(&local->mtx);
00213 }
00214
00215 void ieee80211_ready_on_channel(struct ieee80211_hw *hw)
00216 {
00217 struct ieee80211_local *local = hw_to_local(hw);
00218
00219 trace_api_ready_on_channel(local);
00220
00221 ieee80211_queue_work(hw, &local->hw_roc_start);
00222 }
00223 EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel);
00224
00225 static void ieee80211_hw_roc_done(struct work_struct *work)
00226 {
00227 struct ieee80211_local *local =
00228 container_of(work, struct ieee80211_local, hw_roc_done);
00229
00230 mutex_lock(&local->mtx);
00231
00232 if (!local->hw_roc_channel) {
00233 mutex_unlock(&local->mtx);
00234 return;
00235 }
00236
00237
00238 if (local->hw_roc_skb) {
00239 u64 cookie;
00240
00241 cookie = local->hw_roc_cookie ^ 2;
00242
00243 cfg80211_mgmt_tx_status(local->hw_roc_dev, cookie,
00244 local->hw_roc_skb->data,
00245 local->hw_roc_skb->len, false,
00246 GFP_KERNEL);
00247
00248 kfree_skb(local->hw_roc_skb);
00249 local->hw_roc_skb = NULL;
00250 local->hw_roc_skb_for_status = NULL;
00251 }
00252
00253 if (!local->hw_roc_for_tx)
00254 cfg80211_remain_on_channel_expired(local->hw_roc_dev,
00255 local->hw_roc_cookie,
00256 local->hw_roc_channel,
00257 local->hw_roc_channel_type,
00258 GFP_KERNEL);
00259
00260 local->hw_roc_channel = NULL;
00261 local->hw_roc_cookie = 0;
00262
00263 ieee80211_recalc_idle(local);
00264
00265 mutex_unlock(&local->mtx);
00266 }
00267
00268 void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw)
00269 {
00270 struct ieee80211_local *local = hw_to_local(hw);
00271
00272 trace_api_remain_on_channel_expired(local);
00273
00274 ieee80211_queue_work(hw, &local->hw_roc_done);
00275 }
00276 EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired);
00277
00278 void ieee80211_hw_roc_setup(struct ieee80211_local *local)
00279 {
00280 INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start);
00281 INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done);
00282 }