diff options
author | John W. Linville <linville@tuxdriver.com> | 2012-09-24 14:34:40 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-09-24 14:34:40 -0400 |
commit | 9b4e9e756541fd5d1223b323ed5a8a8545dd11cd (patch) | |
tree | 3eed3a2582532bd1fd890b58eadd472fcd2b1dc7 /net | |
parent | 7a5f799becc51c842ec1a3aeb8dd82651aea7036 (diff) | |
parent | 9385d04f2872057a2029901190391fe192b18693 (diff) |
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/cfg.c | 34 | ||||
-rw-r--r-- | net/mac80211/chan.c | 67 | ||||
-rw-r--r-- | net/mac80211/ibss.c | 33 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 23 | ||||
-rw-r--r-- | net/mac80211/iface.c | 15 | ||||
-rw-r--r-- | net/mac80211/key.c | 2 | ||||
-rw-r--r-- | net/mac80211/main.c | 10 | ||||
-rw-r--r-- | net/mac80211/mesh_plink.c | 3 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 205 | ||||
-rw-r--r-- | net/mac80211/offchannel.c | 3 | ||||
-rw-r--r-- | net/mac80211/scan.c | 41 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 121 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 2 | ||||
-rw-r--r-- | net/mac80211/status.c | 48 | ||||
-rw-r--r-- | net/mac80211/tx.c | 2 | ||||
-rw-r--r-- | net/mac80211/util.c | 51 |
16 files changed, 415 insertions, 245 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 03fe6d1cff42..9bd56a744982 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -170,6 +170,38 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, | |||
170 | } | 170 | } |
171 | } | 171 | } |
172 | 172 | ||
173 | switch (sdata->vif.type) { | ||
174 | case NL80211_IFTYPE_STATION: | ||
175 | if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED) | ||
176 | key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT; | ||
177 | break; | ||
178 | case NL80211_IFTYPE_AP: | ||
179 | case NL80211_IFTYPE_AP_VLAN: | ||
180 | /* Keys without a station are used for TX only */ | ||
181 | if (key->sta && test_sta_flag(key->sta, WLAN_STA_MFP)) | ||
182 | key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT; | ||
183 | break; | ||
184 | case NL80211_IFTYPE_ADHOC: | ||
185 | /* no MFP (yet) */ | ||
186 | break; | ||
187 | case NL80211_IFTYPE_MESH_POINT: | ||
188 | #ifdef CONFIG_MAC80211_MESH | ||
189 | if (sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE) | ||
190 | key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT; | ||
191 | break; | ||
192 | #endif | ||
193 | case NL80211_IFTYPE_WDS: | ||
194 | case NL80211_IFTYPE_MONITOR: | ||
195 | case NL80211_IFTYPE_P2P_DEVICE: | ||
196 | case NL80211_IFTYPE_UNSPECIFIED: | ||
197 | case NUM_NL80211_IFTYPES: | ||
198 | case NL80211_IFTYPE_P2P_CLIENT: | ||
199 | case NL80211_IFTYPE_P2P_GO: | ||
200 | /* shouldn't happen */ | ||
201 | WARN_ON_ONCE(1); | ||
202 | break; | ||
203 | } | ||
204 | |||
173 | err = ieee80211_key_link(key, sdata, sta); | 205 | err = ieee80211_key_link(key, sdata, sta); |
174 | if (err) | 206 | if (err) |
175 | ieee80211_key_free(sdata->local, key); | 207 | ieee80211_key_free(sdata->local, key); |
@@ -2038,9 +2070,7 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, | |||
2038 | */ | 2070 | */ |
2039 | if (!sdata->u.mgd.associated || | 2071 | if (!sdata->u.mgd.associated || |
2040 | sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) { | 2072 | sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) { |
2041 | mutex_lock(&sdata->local->iflist_mtx); | ||
2042 | ieee80211_recalc_smps(sdata->local); | 2073 | ieee80211_recalc_smps(sdata->local); |
2043 | mutex_unlock(&sdata->local->iflist_mtx); | ||
2044 | return 0; | 2074 | return 0; |
2045 | } | 2075 | } |
2046 | 2076 | ||
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index f0f87e5a1d35..0bfc914ddd15 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c | |||
@@ -68,16 +68,14 @@ ieee80211_get_channel_mode(struct ieee80211_local *local, | |||
68 | return mode; | 68 | return mode; |
69 | } | 69 | } |
70 | 70 | ||
71 | bool ieee80211_set_channel_type(struct ieee80211_local *local, | 71 | static enum nl80211_channel_type |
72 | struct ieee80211_sub_if_data *sdata, | 72 | ieee80211_get_superchan(struct ieee80211_local *local, |
73 | enum nl80211_channel_type chantype) | 73 | struct ieee80211_sub_if_data *sdata) |
74 | { | 74 | { |
75 | struct ieee80211_sub_if_data *tmp; | ||
76 | enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT; | 75 | enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT; |
77 | bool result; | 76 | struct ieee80211_sub_if_data *tmp; |
78 | 77 | ||
79 | mutex_lock(&local->iflist_mtx); | 78 | mutex_lock(&local->iflist_mtx); |
80 | |||
81 | list_for_each_entry(tmp, &local->interfaces, list) { | 79 | list_for_each_entry(tmp, &local->interfaces, list) { |
82 | if (tmp == sdata) | 80 | if (tmp == sdata) |
83 | continue; | 81 | continue; |
@@ -103,39 +101,70 @@ bool ieee80211_set_channel_type(struct ieee80211_local *local, | |||
103 | break; | 101 | break; |
104 | } | 102 | } |
105 | } | 103 | } |
104 | mutex_unlock(&local->iflist_mtx); | ||
106 | 105 | ||
107 | switch (superchan) { | 106 | return superchan; |
107 | } | ||
108 | |||
109 | static bool | ||
110 | ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1, | ||
111 | enum nl80211_channel_type chantype2, | ||
112 | enum nl80211_channel_type *compat) | ||
113 | { | ||
114 | /* | ||
115 | * start out with chantype1 being the result, | ||
116 | * overwriting later if needed | ||
117 | */ | ||
118 | if (compat) | ||
119 | *compat = chantype1; | ||
120 | |||
121 | switch (chantype1) { | ||
108 | case NL80211_CHAN_NO_HT: | 122 | case NL80211_CHAN_NO_HT: |
123 | if (compat) | ||
124 | *compat = chantype2; | ||
125 | break; | ||
109 | case NL80211_CHAN_HT20: | 126 | case NL80211_CHAN_HT20: |
110 | /* | 127 | /* |
111 | * allow any change that doesn't go to no-HT | 128 | * allow any change that doesn't go to no-HT |
112 | * (if it already is no-HT no change is needed) | 129 | * (if it already is no-HT no change is needed) |
113 | */ | 130 | */ |
114 | if (chantype == NL80211_CHAN_NO_HT) | 131 | if (chantype2 == NL80211_CHAN_NO_HT) |
115 | break; | 132 | break; |
116 | superchan = chantype; | 133 | if (compat) |
134 | *compat = chantype2; | ||
117 | break; | 135 | break; |
118 | case NL80211_CHAN_HT40PLUS: | 136 | case NL80211_CHAN_HT40PLUS: |
119 | case NL80211_CHAN_HT40MINUS: | 137 | case NL80211_CHAN_HT40MINUS: |
120 | /* allow smaller bandwidth and same */ | 138 | /* allow smaller bandwidth and same */ |
121 | if (chantype == NL80211_CHAN_NO_HT) | 139 | if (chantype2 == NL80211_CHAN_NO_HT) |
122 | break; | 140 | break; |
123 | if (chantype == NL80211_CHAN_HT20) | 141 | if (chantype2 == NL80211_CHAN_HT20) |
124 | break; | 142 | break; |
125 | if (superchan == chantype) | 143 | if (chantype2 == chantype1) |
126 | break; | 144 | break; |
127 | result = false; | 145 | return false; |
128 | goto out; | ||
129 | } | 146 | } |
130 | 147 | ||
131 | local->_oper_channel_type = superchan; | 148 | return true; |
149 | } | ||
150 | |||
151 | bool ieee80211_set_channel_type(struct ieee80211_local *local, | ||
152 | struct ieee80211_sub_if_data *sdata, | ||
153 | enum nl80211_channel_type chantype) | ||
154 | { | ||
155 | enum nl80211_channel_type superchan; | ||
156 | enum nl80211_channel_type compatchan; | ||
157 | |||
158 | superchan = ieee80211_get_superchan(local, sdata); | ||
159 | if (!ieee80211_channel_types_are_compatible(superchan, chantype, | ||
160 | &compatchan)) | ||
161 | return false; | ||
162 | |||
163 | local->_oper_channel_type = compatchan; | ||
132 | 164 | ||
133 | if (sdata) | 165 | if (sdata) |
134 | sdata->vif.bss_conf.channel_type = chantype; | 166 | sdata->vif.bss_conf.channel_type = chantype; |
135 | 167 | ||
136 | result = true; | 168 | return true; |
137 | out: | ||
138 | mutex_unlock(&local->iflist_mtx); | ||
139 | 169 | ||
140 | return result; | ||
141 | } | 170 | } |
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index a9d93285dba7..7c082517f0c7 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c | |||
@@ -332,11 +332,27 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, | |||
332 | return ieee80211_ibss_finish_sta(sta, auth); | 332 | return ieee80211_ibss_finish_sta(sta, auth); |
333 | } | 333 | } |
334 | 334 | ||
335 | static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata, | ||
336 | struct ieee80211_mgmt *mgmt, | ||
337 | size_t len) | ||
338 | { | ||
339 | u16 reason = le16_to_cpu(mgmt->u.deauth.reason_code); | ||
340 | |||
341 | if (len < IEEE80211_DEAUTH_FRAME_LEN) | ||
342 | return; | ||
343 | |||
344 | ibss_dbg(sdata, "RX DeAuth SA=%pM DA=%pM BSSID=%pM (reason: %d)\n", | ||
345 | mgmt->sa, mgmt->da, mgmt->bssid, reason); | ||
346 | sta_info_destroy_addr(sdata, mgmt->sa); | ||
347 | } | ||
348 | |||
335 | static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, | 349 | static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, |
336 | struct ieee80211_mgmt *mgmt, | 350 | struct ieee80211_mgmt *mgmt, |
337 | size_t len) | 351 | size_t len) |
338 | { | 352 | { |
339 | u16 auth_alg, auth_transaction; | 353 | u16 auth_alg, auth_transaction; |
354 | struct sta_info *sta; | ||
355 | u8 deauth_frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; | ||
340 | 356 | ||
341 | lockdep_assert_held(&sdata->u.ibss.mtx); | 357 | lockdep_assert_held(&sdata->u.ibss.mtx); |
342 | 358 | ||
@@ -352,10 +368,22 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, | |||
352 | "RX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=%d)\n", | 368 | "RX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=%d)\n", |
353 | mgmt->sa, mgmt->da, mgmt->bssid, auth_transaction); | 369 | mgmt->sa, mgmt->da, mgmt->bssid, auth_transaction); |
354 | sta_info_destroy_addr(sdata, mgmt->sa); | 370 | sta_info_destroy_addr(sdata, mgmt->sa); |
355 | ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false); | 371 | sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false); |
356 | rcu_read_unlock(); | 372 | rcu_read_unlock(); |
357 | 373 | ||
358 | /* | 374 | /* |
375 | * if we have any problem in allocating the new station, we reply with a | ||
376 | * DEAUTH frame to tell the other end that we had a problem | ||
377 | */ | ||
378 | if (!sta) { | ||
379 | ieee80211_send_deauth_disassoc(sdata, sdata->u.ibss.bssid, | ||
380 | IEEE80211_STYPE_DEAUTH, | ||
381 | WLAN_REASON_UNSPECIFIED, true, | ||
382 | deauth_frame_buf); | ||
383 | return; | ||
384 | } | ||
385 | |||
386 | /* | ||
359 | * IEEE 802.11 standard does not require authentication in IBSS | 387 | * IEEE 802.11 standard does not require authentication in IBSS |
360 | * networks and most implementations do not seem to use it. | 388 | * networks and most implementations do not seem to use it. |
361 | * However, try to reply to authentication attempts if someone | 389 | * However, try to reply to authentication attempts if someone |
@@ -902,6 +930,9 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, | |||
902 | case IEEE80211_STYPE_AUTH: | 930 | case IEEE80211_STYPE_AUTH: |
903 | ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len); | 931 | ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len); |
904 | break; | 932 | break; |
933 | case IEEE80211_STYPE_DEAUTH: | ||
934 | ieee80211_rx_mgmt_deauth_ibss(sdata, mgmt, skb->len); | ||
935 | break; | ||
905 | } | 936 | } |
906 | 937 | ||
907 | mgmt_out: | 938 | mgmt_out: |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 204bfedba306..8c804550465b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -68,6 +68,8 @@ struct ieee80211_local; | |||
68 | #define IEEE80211_DEFAULT_MAX_SP_LEN \ | 68 | #define IEEE80211_DEFAULT_MAX_SP_LEN \ |
69 | IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL | 69 | IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL |
70 | 70 | ||
71 | #define IEEE80211_DEAUTH_FRAME_LEN (24 /* hdr */ + 2 /* reason */) | ||
72 | |||
71 | struct ieee80211_fragment_entry { | 73 | struct ieee80211_fragment_entry { |
72 | unsigned long first_frag_time; | 74 | unsigned long first_frag_time; |
73 | unsigned int seq; | 75 | unsigned int seq; |
@@ -411,6 +413,7 @@ struct ieee80211_if_managed { | |||
411 | struct work_struct monitor_work; | 413 | struct work_struct monitor_work; |
412 | struct work_struct chswitch_work; | 414 | struct work_struct chswitch_work; |
413 | struct work_struct beacon_connection_loss_work; | 415 | struct work_struct beacon_connection_loss_work; |
416 | struct work_struct csa_connection_drop_work; | ||
414 | 417 | ||
415 | unsigned long beacon_timeout; | 418 | unsigned long beacon_timeout; |
416 | unsigned long probe_timeout; | 419 | unsigned long probe_timeout; |
@@ -970,7 +973,6 @@ struct ieee80211_local { | |||
970 | int scan_channel_idx; | 973 | int scan_channel_idx; |
971 | int scan_ies_len; | 974 | int scan_ies_len; |
972 | 975 | ||
973 | struct ieee80211_sched_scan_ies sched_scan_ies; | ||
974 | struct work_struct sched_scan_stopped_work; | 976 | struct work_struct sched_scan_stopped_work; |
975 | struct ieee80211_sub_if_data __rcu *sched_scan_sdata; | 977 | struct ieee80211_sub_if_data __rcu *sched_scan_sdata; |
976 | 978 | ||
@@ -1057,7 +1059,7 @@ struct ieee80211_local { | |||
1057 | bool disable_dynamic_ps; | 1059 | bool disable_dynamic_ps; |
1058 | 1060 | ||
1059 | int user_power_level; /* in dBm */ | 1061 | int user_power_level; /* in dBm */ |
1060 | int power_constr_level; /* in dBm */ | 1062 | int ap_power_level; /* in dBm */ |
1061 | 1063 | ||
1062 | enum ieee80211_smps_mode smps_mode; | 1064 | enum ieee80211_smps_mode smps_mode; |
1063 | 1065 | ||
@@ -1165,7 +1167,6 @@ struct ieee802_11_elems { | |||
1165 | u8 prep_len; | 1167 | u8 prep_len; |
1166 | u8 perr_len; | 1168 | u8 perr_len; |
1167 | u8 country_elem_len; | 1169 | u8 country_elem_len; |
1168 | u8 pwr_constr_elem_len; | ||
1169 | u8 quiet_elem_len; | 1170 | u8 quiet_elem_len; |
1170 | u8 num_of_quiet_elem; /* can be more the one */ | 1171 | u8 num_of_quiet_elem; /* can be more the one */ |
1171 | u8 timeout_int_len; | 1172 | u8 timeout_int_len; |
@@ -1367,7 +1368,6 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, | |||
1367 | int ieee80211_reconfig(struct ieee80211_local *local); | 1368 | int ieee80211_reconfig(struct ieee80211_local *local); |
1368 | void ieee80211_stop_device(struct ieee80211_local *local); | 1369 | void ieee80211_stop_device(struct ieee80211_local *local); |
1369 | 1370 | ||
1370 | #ifdef CONFIG_PM | ||
1371 | int __ieee80211_suspend(struct ieee80211_hw *hw, | 1371 | int __ieee80211_suspend(struct ieee80211_hw *hw, |
1372 | struct cfg80211_wowlan *wowlan); | 1372 | struct cfg80211_wowlan *wowlan); |
1373 | 1373 | ||
@@ -1381,18 +1381,6 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw) | |||
1381 | 1381 | ||
1382 | return ieee80211_reconfig(hw_to_local(hw)); | 1382 | return ieee80211_reconfig(hw_to_local(hw)); |
1383 | } | 1383 | } |
1384 | #else | ||
1385 | static inline int __ieee80211_suspend(struct ieee80211_hw *hw, | ||
1386 | struct cfg80211_wowlan *wowlan) | ||
1387 | { | ||
1388 | return 0; | ||
1389 | } | ||
1390 | |||
1391 | static inline int __ieee80211_resume(struct ieee80211_hw *hw) | ||
1392 | { | ||
1393 | return 0; | ||
1394 | } | ||
1395 | #endif | ||
1396 | 1384 | ||
1397 | /* utility functions/constants */ | 1385 | /* utility functions/constants */ |
1398 | extern void *mac80211_wiphy_privid; /* for wiphy privid */ | 1386 | extern void *mac80211_wiphy_privid; /* for wiphy privid */ |
@@ -1459,6 +1447,9 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, | |||
1459 | u16 transaction, u16 auth_alg, | 1447 | u16 transaction, u16 auth_alg, |
1460 | u8 *extra, size_t extra_len, const u8 *bssid, | 1448 | u8 *extra, size_t extra_len, const u8 *bssid, |
1461 | const u8 *da, const u8 *key, u8 key_len, u8 key_idx); | 1449 | const u8 *da, const u8 *key, u8 key_len, u8 key_idx); |
1450 | void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, | ||
1451 | const u8 *bssid, u16 stype, u16 reason, | ||
1452 | bool send_frame, u8 *frame_buf); | ||
1462 | int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, | 1453 | int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, |
1463 | const u8 *ie, size_t ie_len, | 1454 | const u8 *ie, size_t ie_len, |
1464 | enum ieee80211_band band, u32 rate_mask, | 1455 | enum ieee80211_band band, u32 rate_mask, |
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index d747da541747..6f8a73c64fb3 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -793,11 +793,20 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, | |||
793 | flush_work(&sdata->work); | 793 | flush_work(&sdata->work); |
794 | /* | 794 | /* |
795 | * When we get here, the interface is marked down. | 795 | * When we get here, the interface is marked down. |
796 | * Call synchronize_rcu() to wait for the RX path | 796 | * Call rcu_barrier() to wait both for the RX path |
797 | * should it be using the interface and enqueuing | 797 | * should it be using the interface and enqueuing |
798 | * frames at this very time on another CPU. | 798 | * frames at this very time on another CPU, and |
799 | * for the sta free call_rcu callbacks. | ||
799 | */ | 800 | */ |
800 | synchronize_rcu(); | 801 | rcu_barrier(); |
802 | |||
803 | /* | ||
804 | * free_sta_rcu() enqueues a work for the actual | ||
805 | * sta cleanup, so we need to flush it while | ||
806 | * sdata is still valid. | ||
807 | */ | ||
808 | flush_workqueue(local->workqueue); | ||
809 | |||
801 | skb_queue_purge(&sdata->skb_queue); | 810 | skb_queue_purge(&sdata->skb_queue); |
802 | 811 | ||
803 | /* | 812 | /* |
diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 7ae678ba5d67..d27e61aaa71b 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c | |||
@@ -402,7 +402,7 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key) | |||
402 | * Synchronize so the TX path can no longer be using | 402 | * Synchronize so the TX path can no longer be using |
403 | * this key before we free/remove it. | 403 | * this key before we free/remove it. |
404 | */ | 404 | */ |
405 | synchronize_rcu(); | 405 | synchronize_net(); |
406 | 406 | ||
407 | if (key->local) | 407 | if (key->local) |
408 | ieee80211_key_disable_hw_accel(key); | 408 | ieee80211_key_disable_hw_accel(key); |
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index bd7529363193..c80c4490351c 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -150,13 +150,11 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) | |||
150 | 150 | ||
151 | if (test_bit(SCAN_SW_SCANNING, &local->scanning) || | 151 | if (test_bit(SCAN_SW_SCANNING, &local->scanning) || |
152 | test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) || | 152 | test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) || |
153 | test_bit(SCAN_HW_SCANNING, &local->scanning)) | 153 | test_bit(SCAN_HW_SCANNING, &local->scanning) || |
154 | !local->ap_power_level) | ||
154 | power = chan->max_power; | 155 | power = chan->max_power; |
155 | else | 156 | else |
156 | power = local->power_constr_level ? | 157 | power = min(chan->max_power, local->ap_power_level); |
157 | min(chan->max_power, | ||
158 | (chan->max_reg_power - local->power_constr_level)) : | ||
159 | chan->max_power; | ||
160 | 158 | ||
161 | if (local->user_power_level >= 0) | 159 | if (local->user_power_level >= 0) |
162 | power = min(power, local->user_power_level); | 160 | power = min(power, local->user_power_level); |
@@ -366,9 +364,7 @@ static void ieee80211_recalc_smps_work(struct work_struct *work) | |||
366 | struct ieee80211_local *local = | 364 | struct ieee80211_local *local = |
367 | container_of(work, struct ieee80211_local, recalc_smps); | 365 | container_of(work, struct ieee80211_local, recalc_smps); |
368 | 366 | ||
369 | mutex_lock(&local->iflist_mtx); | ||
370 | ieee80211_recalc_smps(local); | 367 | ieee80211_recalc_smps(local); |
371 | mutex_unlock(&local->iflist_mtx); | ||
372 | } | 368 | } |
373 | 369 | ||
374 | #ifdef CONFIG_INET | 370 | #ifdef CONFIG_INET |
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 9d7ad366ef09..3ab34d816897 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c | |||
@@ -537,7 +537,8 @@ int mesh_plink_open(struct sta_info *sta) | |||
537 | spin_lock_bh(&sta->lock); | 537 | spin_lock_bh(&sta->lock); |
538 | get_random_bytes(&llid, 2); | 538 | get_random_bytes(&llid, 2); |
539 | sta->llid = llid; | 539 | sta->llid = llid; |
540 | if (sta->plink_state != NL80211_PLINK_LISTEN) { | 540 | if (sta->plink_state != NL80211_PLINK_LISTEN && |
541 | sta->plink_state != NL80211_PLINK_BLOCKED) { | ||
541 | spin_unlock_bh(&sta->lock); | 542 | spin_unlock_bh(&sta->lock); |
542 | return -EBUSY; | 543 | return -EBUSY; |
543 | } | 544 | } |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 5d77650d4363..2dbd9e1e3583 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -88,8 +88,6 @@ MODULE_PARM_DESC(probe_wait_ms, | |||
88 | #define TMR_RUNNING_TIMER 0 | 88 | #define TMR_RUNNING_TIMER 0 |
89 | #define TMR_RUNNING_CHANSW 1 | 89 | #define TMR_RUNNING_CHANSW 1 |
90 | 90 | ||
91 | #define DEAUTH_DISASSOC_LEN (24 /* hdr */ + 2 /* reason */) | ||
92 | |||
93 | /* | 91 | /* |
94 | * All cfg80211 functions have to be called outside a locked | 92 | * All cfg80211 functions have to be called outside a locked |
95 | * section so that they can acquire a lock themselves... This | 93 | * section so that they can acquire a lock themselves... This |
@@ -574,46 +572,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) | |||
574 | ieee80211_tx_skb(sdata, skb); | 572 | ieee80211_tx_skb(sdata, skb); |
575 | } | 573 | } |
576 | 574 | ||
577 | static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, | ||
578 | const u8 *bssid, u16 stype, | ||
579 | u16 reason, bool send_frame, | ||
580 | u8 *frame_buf) | ||
581 | { | ||
582 | struct ieee80211_local *local = sdata->local; | ||
583 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | ||
584 | struct sk_buff *skb; | ||
585 | struct ieee80211_mgmt *mgmt = (void *)frame_buf; | ||
586 | |||
587 | /* build frame */ | ||
588 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); | ||
589 | mgmt->duration = 0; /* initialize only */ | ||
590 | mgmt->seq_ctrl = 0; /* initialize only */ | ||
591 | memcpy(mgmt->da, bssid, ETH_ALEN); | ||
592 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); | ||
593 | memcpy(mgmt->bssid, bssid, ETH_ALEN); | ||
594 | /* u.deauth.reason_code == u.disassoc.reason_code */ | ||
595 | mgmt->u.deauth.reason_code = cpu_to_le16(reason); | ||
596 | |||
597 | if (send_frame) { | ||
598 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + | ||
599 | DEAUTH_DISASSOC_LEN); | ||
600 | if (!skb) | ||
601 | return; | ||
602 | |||
603 | skb_reserve(skb, local->hw.extra_tx_headroom); | ||
604 | |||
605 | /* copy in frame */ | ||
606 | memcpy(skb_put(skb, DEAUTH_DISASSOC_LEN), | ||
607 | mgmt, DEAUTH_DISASSOC_LEN); | ||
608 | |||
609 | if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED)) | ||
610 | IEEE80211_SKB_CB(skb)->flags |= | ||
611 | IEEE80211_TX_INTFL_DONT_ENCRYPT; | ||
612 | |||
613 | ieee80211_tx_skb(sdata, skb); | ||
614 | } | ||
615 | } | ||
616 | |||
617 | void ieee80211_send_pspoll(struct ieee80211_local *local, | 575 | void ieee80211_send_pspoll(struct ieee80211_local *local, |
618 | struct ieee80211_sub_if_data *sdata) | 576 | struct ieee80211_sub_if_data *sdata) |
619 | { | 577 | { |
@@ -730,16 +688,13 @@ void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success) | |||
730 | 688 | ||
731 | trace_api_chswitch_done(sdata, success); | 689 | trace_api_chswitch_done(sdata, success); |
732 | if (!success) { | 690 | if (!success) { |
733 | /* | 691 | sdata_info(sdata, |
734 | * If the channel switch was not successful, stay | 692 | "driver channel switch failed, disconnecting\n"); |
735 | * around on the old channel. We currently lack | 693 | ieee80211_queue_work(&sdata->local->hw, |
736 | * good handling of this situation, possibly we | 694 | &ifmgd->csa_connection_drop_work); |
737 | * should just drop the association. | 695 | } else { |
738 | */ | 696 | ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); |
739 | sdata->local->csa_channel = sdata->local->oper_channel; | ||
740 | } | 697 | } |
741 | |||
742 | ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); | ||
743 | } | 698 | } |
744 | EXPORT_SYMBOL(ieee80211_chswitch_done); | 699 | EXPORT_SYMBOL(ieee80211_chswitch_done); |
745 | 700 | ||
@@ -784,8 +739,14 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
784 | return; | 739 | return; |
785 | 740 | ||
786 | new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); | 741 | new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); |
787 | if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) | 742 | if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) { |
743 | sdata_info(sdata, | ||
744 | "AP %pM switches to unsupported channel (%d MHz), disconnecting\n", | ||
745 | ifmgd->associated->bssid, new_freq); | ||
746 | ieee80211_queue_work(&sdata->local->hw, | ||
747 | &ifmgd->csa_connection_drop_work); | ||
788 | return; | 748 | return; |
749 | } | ||
789 | 750 | ||
790 | sdata->local->csa_channel = new_ch; | 751 | sdata->local->csa_channel = new_ch; |
791 | 752 | ||
@@ -818,23 +779,71 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
818 | } | 779 | } |
819 | 780 | ||
820 | static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, | 781 | static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, |
821 | u16 capab_info, u8 *pwr_constr_elem, | 782 | struct ieee80211_channel *channel, |
822 | u8 pwr_constr_elem_len) | 783 | const u8 *country_ie, u8 country_ie_len, |
784 | const u8 *pwr_constr_elem) | ||
823 | { | 785 | { |
824 | struct ieee80211_conf *conf = &sdata->local->hw.conf; | 786 | struct ieee80211_country_ie_triplet *triplet; |
787 | int chan = ieee80211_frequency_to_channel(channel->center_freq); | ||
788 | int i, chan_pwr, chan_increment, new_ap_level; | ||
789 | bool have_chan_pwr = false; | ||
825 | 790 | ||
826 | if (!(capab_info & WLAN_CAPABILITY_SPECTRUM_MGMT)) | 791 | /* Invalid IE */ |
792 | if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) | ||
827 | return; | 793 | return; |
828 | 794 | ||
829 | /* Power constraint IE length should be 1 octet */ | 795 | triplet = (void *)(country_ie + 3); |
830 | if (pwr_constr_elem_len != 1) | 796 | country_ie_len -= 3; |
831 | return; | ||
832 | 797 | ||
833 | if ((*pwr_constr_elem <= conf->channel->max_reg_power) && | 798 | switch (channel->band) { |
834 | (*pwr_constr_elem != sdata->local->power_constr_level)) { | 799 | default: |
835 | sdata->local->power_constr_level = *pwr_constr_elem; | 800 | WARN_ON_ONCE(1); |
836 | ieee80211_hw_config(sdata->local, 0); | 801 | /* fall through */ |
802 | case IEEE80211_BAND_2GHZ: | ||
803 | case IEEE80211_BAND_60GHZ: | ||
804 | chan_increment = 1; | ||
805 | break; | ||
806 | case IEEE80211_BAND_5GHZ: | ||
807 | chan_increment = 4; | ||
808 | break; | ||
837 | } | 809 | } |
810 | |||
811 | /* find channel */ | ||
812 | while (country_ie_len >= 3) { | ||
813 | u8 first_channel = triplet->chans.first_channel; | ||
814 | |||
815 | if (first_channel >= IEEE80211_COUNTRY_EXTENSION_ID) | ||
816 | goto next; | ||
817 | |||
818 | for (i = 0; i < triplet->chans.num_channels; i++) { | ||
819 | if (first_channel + i * chan_increment == chan) { | ||
820 | have_chan_pwr = true; | ||
821 | chan_pwr = triplet->chans.max_power; | ||
822 | break; | ||
823 | } | ||
824 | } | ||
825 | if (have_chan_pwr) | ||
826 | break; | ||
827 | |||
828 | next: | ||
829 | triplet++; | ||
830 | country_ie_len -= 3; | ||
831 | } | ||
832 | |||
833 | if (!have_chan_pwr) | ||
834 | return; | ||
835 | |||
836 | new_ap_level = max_t(int, 0, chan_pwr - *pwr_constr_elem); | ||
837 | |||
838 | if (sdata->local->ap_power_level == new_ap_level) | ||
839 | return; | ||
840 | |||
841 | sdata_info(sdata, | ||
842 | "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n", | ||
843 | new_ap_level, chan_pwr, *pwr_constr_elem, | ||
844 | sdata->u.mgd.bssid); | ||
845 | sdata->local->ap_power_level = new_ap_level; | ||
846 | ieee80211_hw_config(sdata->local, 0); | ||
838 | } | 847 | } |
839 | 848 | ||
840 | void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif) | 849 | void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif) |
@@ -1339,9 +1348,9 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, | |||
1339 | 1348 | ||
1340 | mutex_lock(&local->iflist_mtx); | 1349 | mutex_lock(&local->iflist_mtx); |
1341 | ieee80211_recalc_ps(local, -1); | 1350 | ieee80211_recalc_ps(local, -1); |
1342 | ieee80211_recalc_smps(local); | ||
1343 | mutex_unlock(&local->iflist_mtx); | 1351 | mutex_unlock(&local->iflist_mtx); |
1344 | 1352 | ||
1353 | ieee80211_recalc_smps(local); | ||
1345 | ieee80211_recalc_ps_vif(sdata); | 1354 | ieee80211_recalc_ps_vif(sdata); |
1346 | 1355 | ||
1347 | netif_tx_start_all_queues(sdata->dev); | 1356 | netif_tx_start_all_queues(sdata->dev); |
@@ -1438,7 +1447,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, | |||
1438 | memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa)); | 1447 | memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa)); |
1439 | memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask)); | 1448 | memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask)); |
1440 | 1449 | ||
1441 | local->power_constr_level = 0; | 1450 | local->ap_power_level = 0; |
1442 | 1451 | ||
1443 | del_timer_sync(&local->dynamic_ps_timer); | 1452 | del_timer_sync(&local->dynamic_ps_timer); |
1444 | cancel_work_sync(&local->dynamic_ps_enable_work); | 1453 | cancel_work_sync(&local->dynamic_ps_enable_work); |
@@ -1692,11 +1701,12 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, | |||
1692 | } | 1701 | } |
1693 | EXPORT_SYMBOL(ieee80211_ap_probereq_get); | 1702 | EXPORT_SYMBOL(ieee80211_ap_probereq_get); |
1694 | 1703 | ||
1695 | static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) | 1704 | static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata, |
1705 | bool transmit_frame) | ||
1696 | { | 1706 | { |
1697 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 1707 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
1698 | struct ieee80211_local *local = sdata->local; | 1708 | struct ieee80211_local *local = sdata->local; |
1699 | u8 frame_buf[DEAUTH_DISASSOC_LEN]; | 1709 | u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; |
1700 | 1710 | ||
1701 | mutex_lock(&ifmgd->mtx); | 1711 | mutex_lock(&ifmgd->mtx); |
1702 | if (!ifmgd->associated) { | 1712 | if (!ifmgd->associated) { |
@@ -1704,19 +1714,17 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) | |||
1704 | return; | 1714 | return; |
1705 | } | 1715 | } |
1706 | 1716 | ||
1707 | sdata_info(sdata, "Connection to AP %pM lost\n", | ||
1708 | ifmgd->associated->bssid); | ||
1709 | |||
1710 | ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, | 1717 | ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, |
1711 | WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, | 1718 | WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, |
1712 | false, frame_buf); | 1719 | transmit_frame, frame_buf); |
1720 | ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; | ||
1713 | mutex_unlock(&ifmgd->mtx); | 1721 | mutex_unlock(&ifmgd->mtx); |
1714 | 1722 | ||
1715 | /* | 1723 | /* |
1716 | * must be outside lock due to cfg80211, | 1724 | * must be outside lock due to cfg80211, |
1717 | * but that's not a problem. | 1725 | * but that's not a problem. |
1718 | */ | 1726 | */ |
1719 | cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN); | 1727 | cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN); |
1720 | 1728 | ||
1721 | mutex_lock(&local->mtx); | 1729 | mutex_lock(&local->mtx); |
1722 | ieee80211_recalc_idle(local); | 1730 | ieee80211_recalc_idle(local); |
@@ -1739,10 +1747,24 @@ static void ieee80211_beacon_connection_loss_work(struct work_struct *work) | |||
1739 | rcu_read_unlock(); | 1747 | rcu_read_unlock(); |
1740 | } | 1748 | } |
1741 | 1749 | ||
1742 | if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) | 1750 | if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) { |
1743 | __ieee80211_connection_loss(sdata); | 1751 | sdata_info(sdata, "Connection to AP %pM lost\n", |
1744 | else | 1752 | ifmgd->bssid); |
1753 | __ieee80211_disconnect(sdata, false); | ||
1754 | } else { | ||
1745 | ieee80211_mgd_probe_ap(sdata, true); | 1755 | ieee80211_mgd_probe_ap(sdata, true); |
1756 | } | ||
1757 | } | ||
1758 | |||
1759 | static void ieee80211_csa_connection_drop_work(struct work_struct *work) | ||
1760 | { | ||
1761 | struct ieee80211_sub_if_data *sdata = | ||
1762 | container_of(work, struct ieee80211_sub_if_data, | ||
1763 | u.mgd.csa_connection_drop_work); | ||
1764 | |||
1765 | ieee80211_wake_queues_by_reason(&sdata->local->hw, | ||
1766 | IEEE80211_QUEUE_STOP_REASON_CSA); | ||
1767 | __ieee80211_disconnect(sdata, true); | ||
1746 | } | 1768 | } |
1747 | 1769 | ||
1748 | void ieee80211_beacon_loss(struct ieee80211_vif *vif) | 1770 | void ieee80211_beacon_loss(struct ieee80211_vif *vif) |
@@ -2530,15 +2552,13 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | |||
2530 | bssid, true); | 2552 | bssid, true); |
2531 | } | 2553 | } |
2532 | 2554 | ||
2533 | /* Note: country IE parsing is done for us by cfg80211 */ | 2555 | if (elems.country_elem && elems.pwr_constr_elem && |
2534 | if (elems.country_elem) { | 2556 | mgmt->u.probe_resp.capab_info & |
2535 | /* TODO: IBSS also needs this */ | 2557 | cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT)) |
2536 | if (elems.pwr_constr_elem) | 2558 | ieee80211_handle_pwr_constr(sdata, local->oper_channel, |
2537 | ieee80211_handle_pwr_constr(sdata, | 2559 | elems.country_elem, |
2538 | le16_to_cpu(mgmt->u.probe_resp.capab_info), | 2560 | elems.country_elem_len, |
2539 | elems.pwr_constr_elem, | 2561 | elems.pwr_constr_elem); |
2540 | elems.pwr_constr_elem_len); | ||
2541 | } | ||
2542 | 2562 | ||
2543 | ieee80211_bss_info_change_notify(sdata, changed); | 2563 | ieee80211_bss_info_change_notify(sdata, changed); |
2544 | } | 2564 | } |
@@ -2635,7 +2655,7 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, | |||
2635 | { | 2655 | { |
2636 | struct ieee80211_local *local = sdata->local; | 2656 | struct ieee80211_local *local = sdata->local; |
2637 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 2657 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
2638 | u8 frame_buf[DEAUTH_DISASSOC_LEN]; | 2658 | u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; |
2639 | 2659 | ||
2640 | ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason, | 2660 | ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason, |
2641 | false, frame_buf); | 2661 | false, frame_buf); |
@@ -2645,7 +2665,7 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, | |||
2645 | * must be outside lock due to cfg80211, | 2665 | * must be outside lock due to cfg80211, |
2646 | * but that's not a problem. | 2666 | * but that's not a problem. |
2647 | */ | 2667 | */ |
2648 | cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN); | 2668 | cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN); |
2649 | 2669 | ||
2650 | mutex_lock(&local->mtx); | 2670 | mutex_lock(&local->mtx); |
2651 | ieee80211_recalc_idle(local); | 2671 | ieee80211_recalc_idle(local); |
@@ -2929,6 +2949,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata) | |||
2929 | 2949 | ||
2930 | cancel_work_sync(&ifmgd->monitor_work); | 2950 | cancel_work_sync(&ifmgd->monitor_work); |
2931 | cancel_work_sync(&ifmgd->beacon_connection_loss_work); | 2951 | cancel_work_sync(&ifmgd->beacon_connection_loss_work); |
2952 | cancel_work_sync(&ifmgd->csa_connection_drop_work); | ||
2932 | if (del_timer_sync(&ifmgd->timer)) | 2953 | if (del_timer_sync(&ifmgd->timer)) |
2933 | set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); | 2954 | set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); |
2934 | 2955 | ||
@@ -2985,6 +3006,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) | |||
2985 | INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work); | 3006 | INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work); |
2986 | INIT_WORK(&ifmgd->beacon_connection_loss_work, | 3007 | INIT_WORK(&ifmgd->beacon_connection_loss_work, |
2987 | ieee80211_beacon_connection_loss_work); | 3008 | ieee80211_beacon_connection_loss_work); |
3009 | INIT_WORK(&ifmgd->csa_connection_drop_work, | ||
3010 | ieee80211_csa_connection_drop_work); | ||
2988 | INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work); | 3011 | INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work); |
2989 | setup_timer(&ifmgd->timer, ieee80211_sta_timer, | 3012 | setup_timer(&ifmgd->timer, ieee80211_sta_timer, |
2990 | (unsigned long) sdata); | 3013 | (unsigned long) sdata); |
@@ -3525,7 +3548,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, | |||
3525 | struct cfg80211_deauth_request *req) | 3548 | struct cfg80211_deauth_request *req) |
3526 | { | 3549 | { |
3527 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 3550 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
3528 | u8 frame_buf[DEAUTH_DISASSOC_LEN]; | 3551 | u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; |
3529 | 3552 | ||
3530 | mutex_lock(&ifmgd->mtx); | 3553 | mutex_lock(&ifmgd->mtx); |
3531 | 3554 | ||
@@ -3553,7 +3576,8 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, | |||
3553 | 3576 | ||
3554 | mutex_unlock(&ifmgd->mtx); | 3577 | mutex_unlock(&ifmgd->mtx); |
3555 | 3578 | ||
3556 | __cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN); | 3579 | __cfg80211_send_deauth(sdata->dev, frame_buf, |
3580 | IEEE80211_DEAUTH_FRAME_LEN); | ||
3557 | 3581 | ||
3558 | mutex_lock(&sdata->local->mtx); | 3582 | mutex_lock(&sdata->local->mtx); |
3559 | ieee80211_recalc_idle(sdata->local); | 3583 | ieee80211_recalc_idle(sdata->local); |
@@ -3567,7 +3591,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, | |||
3567 | { | 3591 | { |
3568 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 3592 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
3569 | u8 bssid[ETH_ALEN]; | 3593 | u8 bssid[ETH_ALEN]; |
3570 | u8 frame_buf[DEAUTH_DISASSOC_LEN]; | 3594 | u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; |
3571 | 3595 | ||
3572 | mutex_lock(&ifmgd->mtx); | 3596 | mutex_lock(&ifmgd->mtx); |
3573 | 3597 | ||
@@ -3592,7 +3616,8 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, | |||
3592 | frame_buf); | 3616 | frame_buf); |
3593 | mutex_unlock(&ifmgd->mtx); | 3617 | mutex_unlock(&ifmgd->mtx); |
3594 | 3618 | ||
3595 | __cfg80211_send_disassoc(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN); | 3619 | __cfg80211_send_disassoc(sdata->dev, frame_buf, |
3620 | IEEE80211_DEAUTH_FRAME_LEN); | ||
3596 | 3621 | ||
3597 | mutex_lock(&sdata->local->mtx); | 3622 | mutex_lock(&sdata->local->mtx); |
3598 | ieee80211_recalc_idle(sdata->local); | 3623 | ieee80211_recalc_idle(sdata->local); |
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 507121dad082..83608ac16780 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c | |||
@@ -233,8 +233,7 @@ static void ieee80211_hw_roc_start(struct work_struct *work) | |||
233 | u32 dur = dep->duration; | 233 | u32 dur = dep->duration; |
234 | dep->duration = dur - roc->duration; | 234 | dep->duration = dur - roc->duration; |
235 | roc->duration = dur; | 235 | roc->duration = dur; |
236 | list_del(&dep->list); | 236 | list_move(&dep->list, &roc->list); |
237 | list_add(&dep->list, &roc->list); | ||
238 | } | 237 | } |
239 | } | 238 | } |
240 | out_unlock: | 239 | out_unlock: |
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 740e414d44f4..c4cdbde24fd3 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -407,7 +407,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, | |||
407 | enum ieee80211_band band = local->hw.conf.channel->band; | 407 | enum ieee80211_band band = local->hw.conf.channel->band; |
408 | 408 | ||
409 | sdata = rcu_dereference_protected(local->scan_sdata, | 409 | sdata = rcu_dereference_protected(local->scan_sdata, |
410 | lockdep_is_held(&local->mtx));; | 410 | lockdep_is_held(&local->mtx)); |
411 | 411 | ||
412 | for (i = 0; i < local->scan_req->n_ssids; i++) | 412 | for (i = 0; i < local->scan_req->n_ssids; i++) |
413 | ieee80211_send_probe_req( | 413 | ieee80211_send_probe_req( |
@@ -917,6 +917,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, | |||
917 | struct cfg80211_sched_scan_request *req) | 917 | struct cfg80211_sched_scan_request *req) |
918 | { | 918 | { |
919 | struct ieee80211_local *local = sdata->local; | 919 | struct ieee80211_local *local = sdata->local; |
920 | struct ieee80211_sched_scan_ies sched_scan_ies; | ||
920 | int ret, i; | 921 | int ret, i; |
921 | 922 | ||
922 | mutex_lock(&local->mtx); | 923 | mutex_lock(&local->mtx); |
@@ -935,33 +936,28 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, | |||
935 | if (!local->hw.wiphy->bands[i]) | 936 | if (!local->hw.wiphy->bands[i]) |
936 | continue; | 937 | continue; |
937 | 938 | ||
938 | local->sched_scan_ies.ie[i] = kzalloc(2 + | 939 | sched_scan_ies.ie[i] = kzalloc(2 + IEEE80211_MAX_SSID_LEN + |
939 | IEEE80211_MAX_SSID_LEN + | 940 | local->scan_ies_len + |
940 | local->scan_ies_len + | 941 | req->ie_len, |
941 | req->ie_len, | 942 | GFP_KERNEL); |
942 | GFP_KERNEL); | 943 | if (!sched_scan_ies.ie[i]) { |
943 | if (!local->sched_scan_ies.ie[i]) { | ||
944 | ret = -ENOMEM; | 944 | ret = -ENOMEM; |
945 | goto out_free; | 945 | goto out_free; |
946 | } | 946 | } |
947 | 947 | ||
948 | local->sched_scan_ies.len[i] = | 948 | sched_scan_ies.len[i] = |
949 | ieee80211_build_preq_ies(local, | 949 | ieee80211_build_preq_ies(local, sched_scan_ies.ie[i], |
950 | local->sched_scan_ies.ie[i], | ||
951 | req->ie, req->ie_len, i, | 950 | req->ie, req->ie_len, i, |
952 | (u32) -1, 0); | 951 | (u32) -1, 0); |
953 | } | 952 | } |
954 | 953 | ||
955 | ret = drv_sched_scan_start(local, sdata, req, | 954 | ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies); |
956 | &local->sched_scan_ies); | 955 | if (ret == 0) |
957 | if (ret == 0) { | ||
958 | rcu_assign_pointer(local->sched_scan_sdata, sdata); | 956 | rcu_assign_pointer(local->sched_scan_sdata, sdata); |
959 | goto out; | ||
960 | } | ||
961 | 957 | ||
962 | out_free: | 958 | out_free: |
963 | while (i > 0) | 959 | while (i > 0) |
964 | kfree(local->sched_scan_ies.ie[--i]); | 960 | kfree(sched_scan_ies.ie[--i]); |
965 | out: | 961 | out: |
966 | mutex_unlock(&local->mtx); | 962 | mutex_unlock(&local->mtx); |
967 | return ret; | 963 | return ret; |
@@ -970,7 +966,7 @@ out: | |||
970 | int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata) | 966 | int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata) |
971 | { | 967 | { |
972 | struct ieee80211_local *local = sdata->local; | 968 | struct ieee80211_local *local = sdata->local; |
973 | int ret = 0, i; | 969 | int ret = 0; |
974 | 970 | ||
975 | mutex_lock(&local->mtx); | 971 | mutex_lock(&local->mtx); |
976 | 972 | ||
@@ -979,12 +975,9 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata) | |||
979 | goto out; | 975 | goto out; |
980 | } | 976 | } |
981 | 977 | ||
982 | if (rcu_access_pointer(local->sched_scan_sdata)) { | 978 | if (rcu_access_pointer(local->sched_scan_sdata)) |
983 | for (i = 0; i < IEEE80211_NUM_BANDS; i++) | ||
984 | kfree(local->sched_scan_ies.ie[i]); | ||
985 | |||
986 | drv_sched_scan_stop(local, sdata); | 979 | drv_sched_scan_stop(local, sdata); |
987 | } | 980 | |
988 | out: | 981 | out: |
989 | mutex_unlock(&local->mtx); | 982 | mutex_unlock(&local->mtx); |
990 | 983 | ||
@@ -1006,7 +999,6 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work) | |||
1006 | struct ieee80211_local *local = | 999 | struct ieee80211_local *local = |
1007 | container_of(work, struct ieee80211_local, | 1000 | container_of(work, struct ieee80211_local, |
1008 | sched_scan_stopped_work); | 1001 | sched_scan_stopped_work); |
1009 | int i; | ||
1010 | 1002 | ||
1011 | mutex_lock(&local->mtx); | 1003 | mutex_lock(&local->mtx); |
1012 | 1004 | ||
@@ -1015,9 +1007,6 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work) | |||
1015 | return; | 1007 | return; |
1016 | } | 1008 | } |
1017 | 1009 | ||
1018 | for (i = 0; i < IEEE80211_NUM_BANDS; i++) | ||
1019 | kfree(local->sched_scan_ies.ie[i]); | ||
1020 | |||
1021 | rcu_assign_pointer(local->sched_scan_sdata, NULL); | 1010 | rcu_assign_pointer(local->sched_scan_sdata, NULL); |
1022 | 1011 | ||
1023 | mutex_unlock(&local->mtx); | 1012 | mutex_unlock(&local->mtx); |
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 06fa75ceb025..9c8cd8b8f753 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
@@ -91,6 +91,70 @@ static int sta_info_hash_del(struct ieee80211_local *local, | |||
91 | return -ENOENT; | 91 | return -ENOENT; |
92 | } | 92 | } |
93 | 93 | ||
94 | static void free_sta_work(struct work_struct *wk) | ||
95 | { | ||
96 | struct sta_info *sta = container_of(wk, struct sta_info, free_sta_wk); | ||
97 | int ac, i; | ||
98 | struct tid_ampdu_tx *tid_tx; | ||
99 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
100 | struct ieee80211_local *local = sdata->local; | ||
101 | |||
102 | /* | ||
103 | * At this point, when being called as call_rcu callback, | ||
104 | * neither mac80211 nor the driver can reference this | ||
105 | * sta struct any more except by still existing timers | ||
106 | * associated with this station that we clean up below. | ||
107 | */ | ||
108 | |||
109 | if (test_sta_flag(sta, WLAN_STA_PS_STA)) { | ||
110 | BUG_ON(!sdata->bss); | ||
111 | |||
112 | clear_sta_flag(sta, WLAN_STA_PS_STA); | ||
113 | |||
114 | atomic_dec(&sdata->bss->num_sta_ps); | ||
115 | sta_info_recalc_tim(sta); | ||
116 | } | ||
117 | |||
118 | for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { | ||
119 | local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]); | ||
120 | __skb_queue_purge(&sta->ps_tx_buf[ac]); | ||
121 | __skb_queue_purge(&sta->tx_filtered[ac]); | ||
122 | } | ||
123 | |||
124 | #ifdef CONFIG_MAC80211_MESH | ||
125 | if (ieee80211_vif_is_mesh(&sdata->vif)) { | ||
126 | mesh_accept_plinks_update(sdata); | ||
127 | mesh_plink_deactivate(sta); | ||
128 | del_timer_sync(&sta->plink_timer); | ||
129 | } | ||
130 | #endif | ||
131 | |||
132 | cancel_work_sync(&sta->drv_unblock_wk); | ||
133 | |||
134 | /* | ||
135 | * Destroy aggregation state here. It would be nice to wait for the | ||
136 | * driver to finish aggregation stop and then clean up, but for now | ||
137 | * drivers have to handle aggregation stop being requested, followed | ||
138 | * directly by station destruction. | ||
139 | */ | ||
140 | for (i = 0; i < STA_TID_NUM; i++) { | ||
141 | tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]); | ||
142 | if (!tid_tx) | ||
143 | continue; | ||
144 | __skb_queue_purge(&tid_tx->pending); | ||
145 | kfree(tid_tx); | ||
146 | } | ||
147 | |||
148 | sta_info_free(local, sta); | ||
149 | } | ||
150 | |||
151 | static void free_sta_rcu(struct rcu_head *h) | ||
152 | { | ||
153 | struct sta_info *sta = container_of(h, struct sta_info, rcu_head); | ||
154 | |||
155 | ieee80211_queue_work(&sta->local->hw, &sta->free_sta_wk); | ||
156 | } | ||
157 | |||
94 | /* protected by RCU */ | 158 | /* protected by RCU */ |
95 | struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, | 159 | struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, |
96 | const u8 *addr) | 160 | const u8 *addr) |
@@ -241,6 +305,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, | |||
241 | 305 | ||
242 | spin_lock_init(&sta->lock); | 306 | spin_lock_init(&sta->lock); |
243 | INIT_WORK(&sta->drv_unblock_wk, sta_unblock); | 307 | INIT_WORK(&sta->drv_unblock_wk, sta_unblock); |
308 | INIT_WORK(&sta->free_sta_wk, free_sta_work); | ||
244 | INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work); | 309 | INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work); |
245 | mutex_init(&sta->ampdu_mlme.mtx); | 310 | mutex_init(&sta->ampdu_mlme.mtx); |
246 | 311 | ||
@@ -654,8 +719,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta) | |||
654 | { | 719 | { |
655 | struct ieee80211_local *local; | 720 | struct ieee80211_local *local; |
656 | struct ieee80211_sub_if_data *sdata; | 721 | struct ieee80211_sub_if_data *sdata; |
657 | int ret, i, ac; | 722 | int ret, i; |
658 | struct tid_ampdu_tx *tid_tx; | ||
659 | 723 | ||
660 | might_sleep(); | 724 | might_sleep(); |
661 | 725 | ||
@@ -711,65 +775,14 @@ int __must_check __sta_info_destroy(struct sta_info *sta) | |||
711 | WARN_ON_ONCE(ret != 0); | 775 | WARN_ON_ONCE(ret != 0); |
712 | } | 776 | } |
713 | 777 | ||
714 | /* | ||
715 | * At this point, after we wait for an RCU grace period, | ||
716 | * neither mac80211 nor the driver can reference this | ||
717 | * sta struct any more except by still existing timers | ||
718 | * associated with this station that we clean up below. | ||
719 | */ | ||
720 | synchronize_rcu(); | ||
721 | |||
722 | if (test_sta_flag(sta, WLAN_STA_PS_STA)) { | ||
723 | BUG_ON(!sdata->bss); | ||
724 | |||
725 | clear_sta_flag(sta, WLAN_STA_PS_STA); | ||
726 | |||
727 | atomic_dec(&sdata->bss->num_sta_ps); | ||
728 | sta_info_recalc_tim(sta); | ||
729 | } | ||
730 | |||
731 | for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { | ||
732 | local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]); | ||
733 | __skb_queue_purge(&sta->ps_tx_buf[ac]); | ||
734 | __skb_queue_purge(&sta->tx_filtered[ac]); | ||
735 | } | ||
736 | |||
737 | #ifdef CONFIG_MAC80211_MESH | ||
738 | if (ieee80211_vif_is_mesh(&sdata->vif)) | ||
739 | mesh_accept_plinks_update(sdata); | ||
740 | #endif | ||
741 | |||
742 | sta_dbg(sdata, "Removed STA %pM\n", sta->sta.addr); | 778 | sta_dbg(sdata, "Removed STA %pM\n", sta->sta.addr); |
743 | 779 | ||
744 | cancel_work_sync(&sta->drv_unblock_wk); | ||
745 | |||
746 | cfg80211_del_sta(sdata->dev, sta->sta.addr, GFP_KERNEL); | 780 | cfg80211_del_sta(sdata->dev, sta->sta.addr, GFP_KERNEL); |
747 | 781 | ||
748 | rate_control_remove_sta_debugfs(sta); | 782 | rate_control_remove_sta_debugfs(sta); |
749 | ieee80211_sta_debugfs_remove(sta); | 783 | ieee80211_sta_debugfs_remove(sta); |
750 | 784 | ||
751 | #ifdef CONFIG_MAC80211_MESH | 785 | call_rcu(&sta->rcu_head, free_sta_rcu); |
752 | if (ieee80211_vif_is_mesh(&sta->sdata->vif)) { | ||
753 | mesh_plink_deactivate(sta); | ||
754 | del_timer_sync(&sta->plink_timer); | ||
755 | } | ||
756 | #endif | ||
757 | |||
758 | /* | ||
759 | * Destroy aggregation state here. It would be nice to wait for the | ||
760 | * driver to finish aggregation stop and then clean up, but for now | ||
761 | * drivers have to handle aggregation stop being requested, followed | ||
762 | * directly by station destruction. | ||
763 | */ | ||
764 | for (i = 0; i < STA_TID_NUM; i++) { | ||
765 | tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]); | ||
766 | if (!tid_tx) | ||
767 | continue; | ||
768 | __skb_queue_purge(&tid_tx->pending); | ||
769 | kfree(tid_tx); | ||
770 | } | ||
771 | |||
772 | sta_info_free(local, sta); | ||
773 | 786 | ||
774 | return 0; | 787 | return 0; |
775 | } | 788 | } |
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index a470e1123a55..c88f161f8118 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h | |||
@@ -287,6 +287,7 @@ struct sta_ampdu_mlme { | |||
287 | struct sta_info { | 287 | struct sta_info { |
288 | /* General information, mostly static */ | 288 | /* General information, mostly static */ |
289 | struct list_head list; | 289 | struct list_head list; |
290 | struct rcu_head rcu_head; | ||
290 | struct sta_info __rcu *hnext; | 291 | struct sta_info __rcu *hnext; |
291 | struct ieee80211_local *local; | 292 | struct ieee80211_local *local; |
292 | struct ieee80211_sub_if_data *sdata; | 293 | struct ieee80211_sub_if_data *sdata; |
@@ -297,6 +298,7 @@ struct sta_info { | |||
297 | spinlock_t lock; | 298 | spinlock_t lock; |
298 | 299 | ||
299 | struct work_struct drv_unblock_wk; | 300 | struct work_struct drv_unblock_wk; |
301 | struct work_struct free_sta_wk; | ||
300 | 302 | ||
301 | u16 listen_interval; | 303 | u16 listen_interval; |
302 | 304 | ||
diff --git a/net/mac80211/status.c b/net/mac80211/status.c index b0801b7d572d..2ce89732d0f2 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c | |||
@@ -517,29 +517,41 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
517 | 517 | ||
518 | if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) { | 518 | if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) { |
519 | u64 cookie = (unsigned long)skb; | 519 | u64 cookie = (unsigned long)skb; |
520 | bool found = false; | ||
521 | |||
520 | acked = info->flags & IEEE80211_TX_STAT_ACK; | 522 | acked = info->flags & IEEE80211_TX_STAT_ACK; |
521 | 523 | ||
522 | if (ieee80211_is_nullfunc(hdr->frame_control) || | 524 | rcu_read_lock(); |
523 | ieee80211_is_qos_nullfunc(hdr->frame_control)) { | 525 | |
524 | cfg80211_probe_status(skb->dev, hdr->addr1, | 526 | list_for_each_entry_rcu(sdata, &local->interfaces, list) { |
525 | cookie, acked, GFP_ATOMIC); | 527 | if (!sdata->dev) |
526 | } else if (skb->dev) { | 528 | continue; |
527 | cfg80211_mgmt_tx_status( | ||
528 | skb->dev->ieee80211_ptr, cookie, skb->data, | ||
529 | skb->len, acked, GFP_ATOMIC); | ||
530 | } else { | ||
531 | struct ieee80211_sub_if_data *p2p_sdata; | ||
532 | 529 | ||
533 | rcu_read_lock(); | 530 | if (skb->dev != sdata->dev) |
531 | continue; | ||
534 | 532 | ||
535 | p2p_sdata = rcu_dereference(local->p2p_sdata); | 533 | found = true; |
536 | if (p2p_sdata) { | 534 | break; |
537 | cfg80211_mgmt_tx_status( | 535 | } |
538 | &p2p_sdata->wdev, cookie, skb->data, | 536 | |
539 | skb->len, acked, GFP_ATOMIC); | 537 | if (!skb->dev) { |
540 | } | 538 | sdata = rcu_dereference(local->p2p_sdata); |
541 | rcu_read_unlock(); | 539 | if (sdata) |
540 | found = true; | ||
541 | } | ||
542 | |||
543 | if (!found) | ||
544 | skb->dev = NULL; | ||
545 | else if (ieee80211_is_nullfunc(hdr->frame_control) || | ||
546 | ieee80211_is_qos_nullfunc(hdr->frame_control)) { | ||
547 | cfg80211_probe_status(sdata->dev, hdr->addr1, | ||
548 | cookie, acked, GFP_ATOMIC); | ||
549 | } else { | ||
550 | cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data, | ||
551 | skb->len, acked, GFP_ATOMIC); | ||
542 | } | 552 | } |
553 | |||
554 | rcu_read_unlock(); | ||
543 | } | 555 | } |
544 | 556 | ||
545 | if (unlikely(info->ack_frame_id)) { | 557 | if (unlikely(info->ack_frame_id)) { |
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 29eb4e678235..e0e0d1d0e830 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -580,7 +580,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) | |||
580 | tx->key = NULL; | 580 | tx->key = NULL; |
581 | else | 581 | else |
582 | skip_hw = (tx->key->conf.flags & | 582 | skip_hw = (tx->key->conf.flags & |
583 | IEEE80211_KEY_FLAG_SW_MGMT) && | 583 | IEEE80211_KEY_FLAG_SW_MGMT_TX) && |
584 | ieee80211_is_mgmt(hdr->frame_control); | 584 | ieee80211_is_mgmt(hdr->frame_control); |
585 | break; | 585 | break; |
586 | case WLAN_CIPHER_SUITE_AES_CMAC: | 586 | case WLAN_CIPHER_SUITE_AES_CMAC: |
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 471fb0516c99..22ca35054dd0 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -792,8 +792,11 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, | |||
792 | elems->country_elem_len = elen; | 792 | elems->country_elem_len = elen; |
793 | break; | 793 | break; |
794 | case WLAN_EID_PWR_CONSTRAINT: | 794 | case WLAN_EID_PWR_CONSTRAINT: |
795 | if (elen != 1) { | ||
796 | elem_parse_failed = true; | ||
797 | break; | ||
798 | } | ||
795 | elems->pwr_constr_elem = pos; | 799 | elems->pwr_constr_elem = pos; |
796 | elems->pwr_constr_elem_len = elen; | ||
797 | break; | 800 | break; |
798 | case WLAN_EID_TIMEOUT_INTERVAL: | 801 | case WLAN_EID_TIMEOUT_INTERVAL: |
799 | elems->timeout_int = pos; | 802 | elems->timeout_int = pos; |
@@ -1004,6 +1007,45 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, | |||
1004 | ieee80211_tx_skb(sdata, skb); | 1007 | ieee80211_tx_skb(sdata, skb); |
1005 | } | 1008 | } |
1006 | 1009 | ||
1010 | void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, | ||
1011 | const u8 *bssid, u16 stype, u16 reason, | ||
1012 | bool send_frame, u8 *frame_buf) | ||
1013 | { | ||
1014 | struct ieee80211_local *local = sdata->local; | ||
1015 | struct sk_buff *skb; | ||
1016 | struct ieee80211_mgmt *mgmt = (void *)frame_buf; | ||
1017 | |||
1018 | /* build frame */ | ||
1019 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); | ||
1020 | mgmt->duration = 0; /* initialize only */ | ||
1021 | mgmt->seq_ctrl = 0; /* initialize only */ | ||
1022 | memcpy(mgmt->da, bssid, ETH_ALEN); | ||
1023 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); | ||
1024 | memcpy(mgmt->bssid, bssid, ETH_ALEN); | ||
1025 | /* u.deauth.reason_code == u.disassoc.reason_code */ | ||
1026 | mgmt->u.deauth.reason_code = cpu_to_le16(reason); | ||
1027 | |||
1028 | if (send_frame) { | ||
1029 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + | ||
1030 | IEEE80211_DEAUTH_FRAME_LEN); | ||
1031 | if (!skb) | ||
1032 | return; | ||
1033 | |||
1034 | skb_reserve(skb, local->hw.extra_tx_headroom); | ||
1035 | |||
1036 | /* copy in frame */ | ||
1037 | memcpy(skb_put(skb, IEEE80211_DEAUTH_FRAME_LEN), | ||
1038 | mgmt, IEEE80211_DEAUTH_FRAME_LEN); | ||
1039 | |||
1040 | if (sdata->vif.type != NL80211_IFTYPE_STATION || | ||
1041 | !(sdata->u.mgd.flags & IEEE80211_STA_MFP_ENABLED)) | ||
1042 | IEEE80211_SKB_CB(skb)->flags |= | ||
1043 | IEEE80211_TX_INTFL_DONT_ENCRYPT; | ||
1044 | |||
1045 | ieee80211_tx_skb(sdata, skb); | ||
1046 | } | ||
1047 | } | ||
1048 | |||
1007 | int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, | 1049 | int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, |
1008 | const u8 *ie, size_t ie_len, | 1050 | const u8 *ie, size_t ie_len, |
1009 | enum ieee80211_band band, u32 rate_mask, | 1051 | enum ieee80211_band band, u32 rate_mask, |
@@ -1564,14 +1606,13 @@ static int check_mgd_smps(struct ieee80211_if_managed *ifmgd, | |||
1564 | return 0; | 1606 | return 0; |
1565 | } | 1607 | } |
1566 | 1608 | ||
1567 | /* must hold iflist_mtx */ | ||
1568 | void ieee80211_recalc_smps(struct ieee80211_local *local) | 1609 | void ieee80211_recalc_smps(struct ieee80211_local *local) |
1569 | { | 1610 | { |
1570 | struct ieee80211_sub_if_data *sdata; | 1611 | struct ieee80211_sub_if_data *sdata; |
1571 | enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF; | 1612 | enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF; |
1572 | int count = 0; | 1613 | int count = 0; |
1573 | 1614 | ||
1574 | lockdep_assert_held(&local->iflist_mtx); | 1615 | mutex_lock(&local->iflist_mtx); |
1575 | 1616 | ||
1576 | /* | 1617 | /* |
1577 | * This function could be improved to handle multiple | 1618 | * This function could be improved to handle multiple |
@@ -1600,12 +1641,14 @@ void ieee80211_recalc_smps(struct ieee80211_local *local) | |||
1600 | } | 1641 | } |
1601 | 1642 | ||
1602 | if (smps_mode == local->smps_mode) | 1643 | if (smps_mode == local->smps_mode) |
1603 | return; | 1644 | goto unlock; |
1604 | 1645 | ||
1605 | set: | 1646 | set: |
1606 | local->smps_mode = smps_mode; | 1647 | local->smps_mode = smps_mode; |
1607 | /* changed flag is auto-detected for this */ | 1648 | /* changed flag is auto-detected for this */ |
1608 | ieee80211_hw_config(local, 0); | 1649 | ieee80211_hw_config(local, 0); |
1650 | unlock: | ||
1651 | mutex_unlock(&local->iflist_mtx); | ||
1609 | } | 1652 | } |
1610 | 1653 | ||
1611 | static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) | 1654 | static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) |