diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-12-23 07:15:35 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-12-28 16:54:55 -0500 |
commit | af6b63741cc4e4dfd575d06beb333b11a8a6e0c0 (patch) | |
tree | a2c1a27b6b6b0fb171606f3653b5c280537b32a3 /net/mac80211 | |
parent | f679f65d417c3ea3f91b4bbfb68e3951c9eb8f04 (diff) |
mac80211: generalise work handling
In order to use auth/assoc for different purposes
other than MLME, it needs to be split up. For other
purposes, a generic work handling (potentially on
another channel) will be useful.
To achieve that, this patch moves much of the MLME
work handling out of mlme into a new work API. The
API can currently handle probing a specific AP,
authentication and association. The MLME previously
handled probe/authentication as one step and will
continue to do so, but they are separate in the new
work handling.
Work items are RCU-managed to be able to check for
existence of an item for a specific frame in the RX
path, but they can be re-used which the MLME right
now will do for its combined probe/auth step.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/Makefile | 2 | ||||
-rw-r--r-- | net/mac80211/debugfs_netdev.c | 2 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 59 | ||||
-rw-r--r-- | net/mac80211/iface.c | 11 | ||||
-rw-r--r-- | net/mac80211/main.c | 2 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 956 | ||||
-rw-r--r-- | net/mac80211/rx.c | 5 | ||||
-rw-r--r-- | net/mac80211/scan.c | 8 | ||||
-rw-r--r-- | net/mac80211/work.c | 902 |
9 files changed, 1112 insertions, 835 deletions
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 298cfcc1bf8d..5a1f57df7cd6 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile | |||
@@ -9,7 +9,7 @@ mac80211-y := \ | |||
9 | scan.o \ | 9 | scan.o \ |
10 | ht.o agg-tx.o agg-rx.o \ | 10 | ht.o agg-tx.o agg-rx.o \ |
11 | ibss.o \ | 11 | ibss.o \ |
12 | mlme.o \ | 12 | mlme.o work.o \ |
13 | iface.o \ | 13 | iface.o \ |
14 | rate.o \ | 14 | rate.o \ |
15 | michael.o \ | 15 | michael.o \ |
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 355983503885..59f6e3bcbd09 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c | |||
@@ -133,7 +133,6 @@ IEEE80211_IF_FILE(max_ratectrl_rateidx, max_ratectrl_rateidx, DEC); | |||
133 | /* STA attributes */ | 133 | /* STA attributes */ |
134 | IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC); | 134 | IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC); |
135 | IEEE80211_IF_FILE(aid, u.mgd.aid, DEC); | 135 | IEEE80211_IF_FILE(aid, u.mgd.aid, DEC); |
136 | IEEE80211_IF_FILE(capab, u.mgd.capab, HEX); | ||
137 | 136 | ||
138 | static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata, | 137 | static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata, |
139 | enum ieee80211_smps_mode smps_mode) | 138 | enum ieee80211_smps_mode smps_mode) |
@@ -270,7 +269,6 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata) | |||
270 | 269 | ||
271 | DEBUGFS_ADD(bssid, sta); | 270 | DEBUGFS_ADD(bssid, sta); |
272 | DEBUGFS_ADD(aid, sta); | 271 | DEBUGFS_ADD(aid, sta); |
273 | DEBUGFS_ADD(capab, sta); | ||
274 | DEBUGFS_ADD_MODE(smps, 0600); | 272 | DEBUGFS_ADD_MODE(smps, 0600); |
275 | } | 273 | } |
276 | 274 | ||
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 0339e909e0c4..97b6076b492e 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -228,41 +228,63 @@ struct mesh_preq_queue { | |||
228 | }; | 228 | }; |
229 | 229 | ||
230 | enum ieee80211_work_type { | 230 | enum ieee80211_work_type { |
231 | IEEE80211_WORK_AUTH_PROBE, | 231 | IEEE80211_WORK_DIRECT_PROBE, |
232 | IEEE80211_WORK_AUTH, | 232 | IEEE80211_WORK_AUTH, |
233 | IEEE80211_WORK_ASSOC, | 233 | IEEE80211_WORK_ASSOC, |
234 | }; | 234 | }; |
235 | 235 | ||
236 | /** | ||
237 | * enum work_done_result - indicates what to do after work was done | ||
238 | * | ||
239 | * @WORK_DONE_DESTROY: This work item is no longer needed, destroy. | ||
240 | * @WORK_DONE_REQUEUE: This work item was reset to be reused, and | ||
241 | * should be requeued. | ||
242 | */ | ||
243 | enum work_done_result { | ||
244 | WORK_DONE_DESTROY, | ||
245 | WORK_DONE_REQUEUE, | ||
246 | }; | ||
247 | |||
236 | struct ieee80211_work { | 248 | struct ieee80211_work { |
237 | struct list_head list; | 249 | struct list_head list; |
238 | 250 | ||
251 | struct rcu_head rcu_head; | ||
252 | |||
253 | struct ieee80211_sub_if_data *sdata; | ||
254 | |||
255 | enum work_done_result (*done)(struct ieee80211_work *wk, | ||
256 | struct sk_buff *skb); | ||
257 | |||
239 | struct ieee80211_channel *chan; | 258 | struct ieee80211_channel *chan; |
240 | /* XXX: chan type? -- right now not really needed */ | 259 | /* XXX: chan type? -- right now not really needed */ |
260 | |||
241 | unsigned long timeout; | 261 | unsigned long timeout; |
242 | enum ieee80211_work_type type; | 262 | enum ieee80211_work_type type; |
243 | 263 | ||
264 | u8 filter_ta[ETH_ALEN]; | ||
265 | |||
244 | union { | 266 | union { |
245 | struct { | 267 | struct { |
246 | int tries; | 268 | int tries; |
247 | u16 algorithm, transaction; | 269 | u16 algorithm, transaction; |
248 | u8 ssid[IEEE80211_MAX_SSID_LEN]; | 270 | u8 ssid[IEEE80211_MAX_SSID_LEN]; |
249 | u8 ssid_len; | 271 | u8 ssid_len; |
250 | u8 bssid[ETH_ALEN]; | ||
251 | u8 key[WLAN_KEY_LEN_WEP104]; | 272 | u8 key[WLAN_KEY_LEN_WEP104]; |
252 | u8 key_len, key_idx; | 273 | u8 key_len, key_idx; |
253 | bool privacy; | 274 | bool privacy; |
254 | } auth; | 275 | } probe_auth; |
255 | struct { | 276 | struct { |
256 | struct ieee80211_bss *bss; | 277 | struct ieee80211_bss *bss; |
257 | const u8 *supp_rates; | 278 | const u8 *supp_rates; |
258 | const u8 *ht_information_ie; | 279 | const u8 *ht_information_ie; |
280 | enum ieee80211_smps_mode smps; | ||
259 | int tries; | 281 | int tries; |
260 | u16 capability; | 282 | u16 capability; |
261 | u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; | 283 | u8 prev_bssid[ETH_ALEN]; |
262 | u8 ssid[IEEE80211_MAX_SSID_LEN]; | 284 | u8 ssid[IEEE80211_MAX_SSID_LEN]; |
263 | u8 ssid_len; | 285 | u8 ssid_len; |
264 | u8 supp_rates_len; | 286 | u8 supp_rates_len; |
265 | bool wmm_used; | 287 | bool wmm_used, use_11n; |
266 | } assoc; | 288 | } assoc; |
267 | }; | 289 | }; |
268 | 290 | ||
@@ -276,17 +298,11 @@ enum ieee80211_sta_flags { | |||
276 | IEEE80211_STA_BEACON_POLL = BIT(0), | 298 | IEEE80211_STA_BEACON_POLL = BIT(0), |
277 | IEEE80211_STA_CONNECTION_POLL = BIT(1), | 299 | IEEE80211_STA_CONNECTION_POLL = BIT(1), |
278 | IEEE80211_STA_CONTROL_PORT = BIT(2), | 300 | IEEE80211_STA_CONTROL_PORT = BIT(2), |
279 | IEEE80211_STA_WMM_ENABLED = BIT(3), | ||
280 | IEEE80211_STA_DISABLE_11N = BIT(4), | 301 | IEEE80211_STA_DISABLE_11N = BIT(4), |
281 | IEEE80211_STA_CSA_RECEIVED = BIT(5), | 302 | IEEE80211_STA_CSA_RECEIVED = BIT(5), |
282 | IEEE80211_STA_MFP_ENABLED = BIT(6), | 303 | IEEE80211_STA_MFP_ENABLED = BIT(6), |
283 | }; | 304 | }; |
284 | 305 | ||
285 | /* flags for MLME request */ | ||
286 | enum ieee80211_sta_request { | ||
287 | IEEE80211_STA_REQ_SCAN, | ||
288 | }; | ||
289 | |||
290 | struct ieee80211_if_managed { | 306 | struct ieee80211_if_managed { |
291 | struct timer_list timer; | 307 | struct timer_list timer; |
292 | struct timer_list conn_mon_timer; | 308 | struct timer_list conn_mon_timer; |
@@ -302,12 +318,10 @@ struct ieee80211_if_managed { | |||
302 | 318 | ||
303 | struct mutex mtx; | 319 | struct mutex mtx; |
304 | struct ieee80211_bss *associated; | 320 | struct ieee80211_bss *associated; |
305 | struct list_head work_list; | ||
306 | 321 | ||
307 | u8 bssid[ETH_ALEN]; | 322 | u8 bssid[ETH_ALEN]; |
308 | 323 | ||
309 | u16 aid; | 324 | u16 aid; |
310 | u16 capab; | ||
311 | 325 | ||
312 | struct sk_buff_head skb_queue; | 326 | struct sk_buff_head skb_queue; |
313 | 327 | ||
@@ -316,8 +330,6 @@ struct ieee80211_if_managed { | |||
316 | enum ieee80211_smps_mode req_smps, /* requested smps mode */ | 330 | enum ieee80211_smps_mode req_smps, /* requested smps mode */ |
317 | ap_smps; /* smps mode AP thinks we're in */ | 331 | ap_smps; /* smps mode AP thinks we're in */ |
318 | 332 | ||
319 | unsigned long request; | ||
320 | |||
321 | unsigned int flags; | 333 | unsigned int flags; |
322 | 334 | ||
323 | u32 beacon_crc; | 335 | u32 beacon_crc; |
@@ -584,6 +596,15 @@ struct ieee80211_local { | |||
584 | const struct ieee80211_ops *ops; | 596 | const struct ieee80211_ops *ops; |
585 | 597 | ||
586 | /* | 598 | /* |
599 | * work stuff, potentially off-channel (in the future) | ||
600 | */ | ||
601 | struct mutex work_mtx; | ||
602 | struct list_head work_list; | ||
603 | struct timer_list work_timer; | ||
604 | struct work_struct work_work; | ||
605 | struct sk_buff_head work_skb_queue; | ||
606 | |||
607 | /* | ||
587 | * private workqueue to mac80211. mac80211 makes this accessible | 608 | * private workqueue to mac80211. mac80211 makes this accessible |
588 | * via ieee80211_queue_work() | 609 | * via ieee80211_queue_work() |
589 | */ | 610 | */ |
@@ -1127,6 +1148,14 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, | |||
1127 | void ieee80211_recalc_smps(struct ieee80211_local *local, | 1148 | void ieee80211_recalc_smps(struct ieee80211_local *local, |
1128 | struct ieee80211_sub_if_data *forsdata); | 1149 | struct ieee80211_sub_if_data *forsdata); |
1129 | 1150 | ||
1151 | /* internal work items */ | ||
1152 | void ieee80211_work_init(struct ieee80211_local *local); | ||
1153 | void ieee80211_add_work(struct ieee80211_work *wk); | ||
1154 | void free_work(struct ieee80211_work *wk); | ||
1155 | void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata); | ||
1156 | ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata, | ||
1157 | struct sk_buff *skb); | ||
1158 | |||
1130 | #ifdef CONFIG_MAC80211_NOINLINE | 1159 | #ifdef CONFIG_MAC80211_NOINLINE |
1131 | #define debug_noinline noinline | 1160 | #define debug_noinline noinline |
1132 | #else | 1161 | #else |
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 389dc8d880f3..7d410f15281a 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -362,6 +362,11 @@ static int ieee80211_stop(struct net_device *dev) | |||
362 | netif_stop_queue(dev); | 362 | netif_stop_queue(dev); |
363 | 363 | ||
364 | /* | 364 | /* |
365 | * Purge work for this interface. | ||
366 | */ | ||
367 | ieee80211_work_purge(sdata); | ||
368 | |||
369 | /* | ||
365 | * Now delete all active aggregation sessions. | 370 | * Now delete all active aggregation sessions. |
366 | */ | 371 | */ |
367 | rcu_read_lock(); | 372 | rcu_read_lock(); |
@@ -928,6 +933,9 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) | |||
928 | struct ieee80211_sub_if_data *sdata; | 933 | struct ieee80211_sub_if_data *sdata; |
929 | int count = 0; | 934 | int count = 0; |
930 | 935 | ||
936 | if (!list_empty(&local->work_list)) | ||
937 | return ieee80211_idle_off(local, "working"); | ||
938 | |||
931 | if (local->scanning) | 939 | if (local->scanning) |
932 | return ieee80211_idle_off(local, "scanning"); | 940 | return ieee80211_idle_off(local, "scanning"); |
933 | 941 | ||
@@ -936,8 +944,7 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) | |||
936 | continue; | 944 | continue; |
937 | /* do not count disabled managed interfaces */ | 945 | /* do not count disabled managed interfaces */ |
938 | if (sdata->vif.type == NL80211_IFTYPE_STATION && | 946 | if (sdata->vif.type == NL80211_IFTYPE_STATION && |
939 | !sdata->u.mgd.associated && | 947 | !sdata->u.mgd.associated) |
940 | list_empty(&sdata->u.mgd.work_list)) | ||
941 | continue; | 948 | continue; |
942 | /* do not count unused IBSS interfaces */ | 949 | /* do not count unused IBSS interfaces */ |
943 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC && | 950 | if (sdata->vif.type == NL80211_IFTYPE_ADHOC && |
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index e93bc558d785..d35023ce7fa1 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -395,6 +395,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
395 | 395 | ||
396 | INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work); | 396 | INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work); |
397 | 397 | ||
398 | ieee80211_work_init(local); | ||
399 | |||
398 | INIT_WORK(&local->restart_work, ieee80211_restart_work); | 400 | INIT_WORK(&local->restart_work, ieee80211_restart_work); |
399 | 401 | ||
400 | INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); | 402 | INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index c65225f29bb6..7c1f91bcc834 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -77,12 +77,6 @@ enum rx_mgmt_action { | |||
77 | 77 | ||
78 | /* caller must tell cfg80211 about internal error */ | 78 | /* caller must tell cfg80211 about internal error */ |
79 | RX_MGMT_CFG80211_ASSOC_ERROR, | 79 | RX_MGMT_CFG80211_ASSOC_ERROR, |
80 | |||
81 | /* caller must call cfg80211_auth_timeout() & free work */ | ||
82 | RX_MGMT_CFG80211_AUTH_TO, | ||
83 | |||
84 | /* caller must call cfg80211_assoc_timeout() & free work */ | ||
85 | RX_MGMT_CFG80211_ASSOC_TO, | ||
86 | }; | 80 | }; |
87 | 81 | ||
88 | /* utils */ | 82 | /* utils */ |
@@ -125,27 +119,6 @@ static int ecw2cw(int ecw) | |||
125 | return (1 << ecw) - 1; | 119 | return (1 << ecw) - 1; |
126 | } | 120 | } |
127 | 121 | ||
128 | static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len, | ||
129 | struct ieee80211_supported_band *sband, | ||
130 | u32 *rates) | ||
131 | { | ||
132 | int i, j, count; | ||
133 | *rates = 0; | ||
134 | count = 0; | ||
135 | for (i = 0; i < supp_rates_len; i++) { | ||
136 | int rate = (supp_rates[i] & 0x7F) * 5; | ||
137 | |||
138 | for (j = 0; j < sband->n_bitrates; j++) | ||
139 | if (sband->bitrates[j].bitrate == rate) { | ||
140 | *rates |= BIT(j); | ||
141 | count++; | ||
142 | break; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | return count; | ||
147 | } | ||
148 | |||
149 | /* | 122 | /* |
150 | * ieee80211_enable_ht should be called only after the operating band | 123 | * ieee80211_enable_ht should be called only after the operating band |
151 | * has been determined as ht configuration depends on the hw's | 124 | * has been determined as ht configuration depends on the hw's |
@@ -231,266 +204,6 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, | |||
231 | 204 | ||
232 | /* frame sending functions */ | 205 | /* frame sending functions */ |
233 | 206 | ||
234 | static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, | ||
235 | struct ieee80211_work *wk) | ||
236 | { | ||
237 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | ||
238 | struct ieee80211_local *local = sdata->local; | ||
239 | struct sk_buff *skb; | ||
240 | struct ieee80211_mgmt *mgmt; | ||
241 | u8 *pos; | ||
242 | const u8 *ies, *ht_ie; | ||
243 | int i, len, count, rates_len, supp_rates_len; | ||
244 | u16 capab; | ||
245 | int wmm = 0; | ||
246 | struct ieee80211_supported_band *sband; | ||
247 | u32 rates = 0; | ||
248 | |||
249 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + | ||
250 | sizeof(*mgmt) + 200 + wk->ie_len + | ||
251 | wk->assoc.ssid_len); | ||
252 | if (!skb) { | ||
253 | printk(KERN_DEBUG "%s: failed to allocate buffer for assoc " | ||
254 | "frame\n", sdata->name); | ||
255 | return; | ||
256 | } | ||
257 | skb_reserve(skb, local->hw.extra_tx_headroom); | ||
258 | |||
259 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | ||
260 | |||
261 | capab = ifmgd->capab; | ||
262 | |||
263 | if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ) { | ||
264 | if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) | ||
265 | capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; | ||
266 | if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)) | ||
267 | capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; | ||
268 | } | ||
269 | |||
270 | if (wk->assoc.capability & WLAN_CAPABILITY_PRIVACY) | ||
271 | capab |= WLAN_CAPABILITY_PRIVACY; | ||
272 | if (wk->assoc.wmm_used) | ||
273 | wmm = 1; | ||
274 | |||
275 | /* get all rates supported by the device and the AP as | ||
276 | * some APs don't like getting a superset of their rates | ||
277 | * in the association request (e.g. D-Link DAP 1353 in | ||
278 | * b-only mode) */ | ||
279 | rates_len = ieee80211_compatible_rates(wk->assoc.supp_rates, | ||
280 | wk->assoc.supp_rates_len, | ||
281 | sband, &rates); | ||
282 | |||
283 | if ((wk->assoc.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) && | ||
284 | (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)) | ||
285 | capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; | ||
286 | |||
287 | mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); | ||
288 | memset(mgmt, 0, 24); | ||
289 | memcpy(mgmt->da, wk->assoc.bssid, ETH_ALEN); | ||
290 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); | ||
291 | memcpy(mgmt->bssid, wk->assoc.bssid, ETH_ALEN); | ||
292 | |||
293 | if (!is_zero_ether_addr(wk->assoc.prev_bssid)) { | ||
294 | skb_put(skb, 10); | ||
295 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | ||
296 | IEEE80211_STYPE_REASSOC_REQ); | ||
297 | mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab); | ||
298 | mgmt->u.reassoc_req.listen_interval = | ||
299 | cpu_to_le16(local->hw.conf.listen_interval); | ||
300 | memcpy(mgmt->u.reassoc_req.current_ap, wk->assoc.prev_bssid, | ||
301 | ETH_ALEN); | ||
302 | } else { | ||
303 | skb_put(skb, 4); | ||
304 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | ||
305 | IEEE80211_STYPE_ASSOC_REQ); | ||
306 | mgmt->u.assoc_req.capab_info = cpu_to_le16(capab); | ||
307 | mgmt->u.assoc_req.listen_interval = | ||
308 | cpu_to_le16(local->hw.conf.listen_interval); | ||
309 | } | ||
310 | |||
311 | /* SSID */ | ||
312 | ies = pos = skb_put(skb, 2 + wk->assoc.ssid_len); | ||
313 | *pos++ = WLAN_EID_SSID; | ||
314 | *pos++ = wk->assoc.ssid_len; | ||
315 | memcpy(pos, wk->assoc.ssid, wk->assoc.ssid_len); | ||
316 | |||
317 | /* add all rates which were marked to be used above */ | ||
318 | supp_rates_len = rates_len; | ||
319 | if (supp_rates_len > 8) | ||
320 | supp_rates_len = 8; | ||
321 | |||
322 | len = sband->n_bitrates; | ||
323 | pos = skb_put(skb, supp_rates_len + 2); | ||
324 | *pos++ = WLAN_EID_SUPP_RATES; | ||
325 | *pos++ = supp_rates_len; | ||
326 | |||
327 | count = 0; | ||
328 | for (i = 0; i < sband->n_bitrates; i++) { | ||
329 | if (BIT(i) & rates) { | ||
330 | int rate = sband->bitrates[i].bitrate; | ||
331 | *pos++ = (u8) (rate / 5); | ||
332 | if (++count == 8) | ||
333 | break; | ||
334 | } | ||
335 | } | ||
336 | |||
337 | if (rates_len > count) { | ||
338 | pos = skb_put(skb, rates_len - count + 2); | ||
339 | *pos++ = WLAN_EID_EXT_SUPP_RATES; | ||
340 | *pos++ = rates_len - count; | ||
341 | |||
342 | for (i++; i < sband->n_bitrates; i++) { | ||
343 | if (BIT(i) & rates) { | ||
344 | int rate = sband->bitrates[i].bitrate; | ||
345 | *pos++ = (u8) (rate / 5); | ||
346 | } | ||
347 | } | ||
348 | } | ||
349 | |||
350 | if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) { | ||
351 | /* 1. power capabilities */ | ||
352 | pos = skb_put(skb, 4); | ||
353 | *pos++ = WLAN_EID_PWR_CAPABILITY; | ||
354 | *pos++ = 2; | ||
355 | *pos++ = 0; /* min tx power */ | ||
356 | *pos++ = local->hw.conf.channel->max_power; /* max tx power */ | ||
357 | |||
358 | /* 2. supported channels */ | ||
359 | /* TODO: get this in reg domain format */ | ||
360 | pos = skb_put(skb, 2 * sband->n_channels + 2); | ||
361 | *pos++ = WLAN_EID_SUPPORTED_CHANNELS; | ||
362 | *pos++ = 2 * sband->n_channels; | ||
363 | for (i = 0; i < sband->n_channels; i++) { | ||
364 | *pos++ = ieee80211_frequency_to_channel( | ||
365 | sband->channels[i].center_freq); | ||
366 | *pos++ = 1; /* one channel in the subband*/ | ||
367 | } | ||
368 | } | ||
369 | |||
370 | if (wk->ie_len && wk->ie) { | ||
371 | pos = skb_put(skb, wk->ie_len); | ||
372 | memcpy(pos, wk->ie, wk->ie_len); | ||
373 | } | ||
374 | |||
375 | if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED)) { | ||
376 | pos = skb_put(skb, 9); | ||
377 | *pos++ = WLAN_EID_VENDOR_SPECIFIC; | ||
378 | *pos++ = 7; /* len */ | ||
379 | *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ | ||
380 | *pos++ = 0x50; | ||
381 | *pos++ = 0xf2; | ||
382 | *pos++ = 2; /* WME */ | ||
383 | *pos++ = 0; /* WME info */ | ||
384 | *pos++ = 1; /* WME ver */ | ||
385 | *pos++ = 0; | ||
386 | } | ||
387 | |||
388 | /* wmm support is a must to HT */ | ||
389 | /* | ||
390 | * IEEE802.11n does not allow TKIP/WEP as pairwise | ||
391 | * ciphers in HT mode. We still associate in non-ht | ||
392 | * mode (11a/b/g) if any one of these ciphers is | ||
393 | * configured as pairwise. | ||
394 | */ | ||
395 | if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) && | ||
396 | sband->ht_cap.ht_supported && | ||
397 | (ht_ie = wk->assoc.ht_information_ie) && | ||
398 | ht_ie[1] >= sizeof(struct ieee80211_ht_info) && | ||
399 | (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))) { | ||
400 | struct ieee80211_ht_info *ht_info = | ||
401 | (struct ieee80211_ht_info *)(ht_ie + 2); | ||
402 | u16 cap = sband->ht_cap.cap; | ||
403 | __le16 tmp; | ||
404 | u32 flags = local->hw.conf.channel->flags; | ||
405 | |||
406 | /* determine capability flags */ | ||
407 | |||
408 | if (ieee80211_disable_40mhz_24ghz && | ||
409 | sband->band == IEEE80211_BAND_2GHZ) { | ||
410 | cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; | ||
411 | cap &= ~IEEE80211_HT_CAP_SGI_40; | ||
412 | } | ||
413 | |||
414 | switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { | ||
415 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: | ||
416 | if (flags & IEEE80211_CHAN_NO_HT40PLUS) { | ||
417 | cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; | ||
418 | cap &= ~IEEE80211_HT_CAP_SGI_40; | ||
419 | } | ||
420 | break; | ||
421 | case IEEE80211_HT_PARAM_CHA_SEC_BELOW: | ||
422 | if (flags & IEEE80211_CHAN_NO_HT40MINUS) { | ||
423 | cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; | ||
424 | cap &= ~IEEE80211_HT_CAP_SGI_40; | ||
425 | } | ||
426 | break; | ||
427 | } | ||
428 | |||
429 | /* set SM PS mode properly */ | ||
430 | cap &= ~IEEE80211_HT_CAP_SM_PS; | ||
431 | /* new association always uses requested smps mode */ | ||
432 | if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) { | ||
433 | if (ifmgd->powersave) | ||
434 | ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC; | ||
435 | else | ||
436 | ifmgd->ap_smps = IEEE80211_SMPS_OFF; | ||
437 | } else | ||
438 | ifmgd->ap_smps = ifmgd->req_smps; | ||
439 | |||
440 | switch (ifmgd->ap_smps) { | ||
441 | case IEEE80211_SMPS_AUTOMATIC: | ||
442 | case IEEE80211_SMPS_NUM_MODES: | ||
443 | WARN_ON(1); | ||
444 | case IEEE80211_SMPS_OFF: | ||
445 | cap |= WLAN_HT_CAP_SM_PS_DISABLED << | ||
446 | IEEE80211_HT_CAP_SM_PS_SHIFT; | ||
447 | break; | ||
448 | case IEEE80211_SMPS_STATIC: | ||
449 | cap |= WLAN_HT_CAP_SM_PS_STATIC << | ||
450 | IEEE80211_HT_CAP_SM_PS_SHIFT; | ||
451 | break; | ||
452 | case IEEE80211_SMPS_DYNAMIC: | ||
453 | cap |= WLAN_HT_CAP_SM_PS_DYNAMIC << | ||
454 | IEEE80211_HT_CAP_SM_PS_SHIFT; | ||
455 | break; | ||
456 | } | ||
457 | |||
458 | /* reserve and fill IE */ | ||
459 | |||
460 | pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); | ||
461 | *pos++ = WLAN_EID_HT_CAPABILITY; | ||
462 | *pos++ = sizeof(struct ieee80211_ht_cap); | ||
463 | memset(pos, 0, sizeof(struct ieee80211_ht_cap)); | ||
464 | |||
465 | /* capability flags */ | ||
466 | tmp = cpu_to_le16(cap); | ||
467 | memcpy(pos, &tmp, sizeof(u16)); | ||
468 | pos += sizeof(u16); | ||
469 | |||
470 | /* AMPDU parameters */ | ||
471 | *pos++ = sband->ht_cap.ampdu_factor | | ||
472 | (sband->ht_cap.ampdu_density << | ||
473 | IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); | ||
474 | |||
475 | /* MCS set */ | ||
476 | memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); | ||
477 | pos += sizeof(sband->ht_cap.mcs); | ||
478 | |||
479 | /* extended capabilities */ | ||
480 | pos += sizeof(__le16); | ||
481 | |||
482 | /* BF capabilities */ | ||
483 | pos += sizeof(__le32); | ||
484 | |||
485 | /* antenna selection */ | ||
486 | pos += sizeof(u8); | ||
487 | } | ||
488 | |||
489 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; | ||
490 | ieee80211_tx_skb(sdata, skb); | ||
491 | } | ||
492 | |||
493 | |||
494 | static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, | 207 | static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, |
495 | const u8 *bssid, u16 stype, u16 reason, | 208 | const u8 *bssid, u16 stype, u16 reason, |
496 | void *cookie) | 209 | void *cookie) |
@@ -751,6 +464,11 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) | |||
751 | return; | 464 | return; |
752 | } | 465 | } |
753 | 466 | ||
467 | if (!list_empty(&local->work_list)) { | ||
468 | local->ps_sdata = NULL; | ||
469 | goto change; | ||
470 | } | ||
471 | |||
754 | list_for_each_entry(sdata, &local->interfaces, list) { | 472 | list_for_each_entry(sdata, &local->interfaces, list) { |
755 | if (!ieee80211_sdata_running(sdata)) | 473 | if (!ieee80211_sdata_running(sdata)) |
756 | continue; | 474 | continue; |
@@ -761,7 +479,7 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) | |||
761 | } | 479 | } |
762 | 480 | ||
763 | if (count == 1 && found->u.mgd.powersave && | 481 | if (count == 1 && found->u.mgd.powersave && |
764 | found->u.mgd.associated && list_empty(&found->u.mgd.work_list) && | 482 | found->u.mgd.associated && |
765 | !(found->u.mgd.flags & (IEEE80211_STA_BEACON_POLL | | 483 | !(found->u.mgd.flags & (IEEE80211_STA_BEACON_POLL | |
766 | IEEE80211_STA_CONNECTION_POLL))) { | 484 | IEEE80211_STA_CONNECTION_POLL))) { |
767 | s32 beaconint_us; | 485 | s32 beaconint_us; |
@@ -789,6 +507,7 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) | |||
789 | local->ps_sdata = NULL; | 507 | local->ps_sdata = NULL; |
790 | } | 508 | } |
791 | 509 | ||
510 | change: | ||
792 | ieee80211_change_ps(local); | 511 | ieee80211_change_ps(local); |
793 | } | 512 | } |
794 | 513 | ||
@@ -848,7 +567,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, | |||
848 | int count; | 567 | int count; |
849 | u8 *pos; | 568 | u8 *pos; |
850 | 569 | ||
851 | if (!(ifmgd->flags & IEEE80211_STA_WMM_ENABLED)) | 570 | if (local->hw.queues < 4) |
852 | return; | 571 | return; |
853 | 572 | ||
854 | if (!wmm_param) | 573 | if (!wmm_param) |
@@ -1005,110 +724,6 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, | |||
1005 | netif_carrier_on(sdata->dev); | 724 | netif_carrier_on(sdata->dev); |
1006 | } | 725 | } |
1007 | 726 | ||
1008 | static void ieee80211_remove_auth_bss(struct ieee80211_local *local, | ||
1009 | struct ieee80211_work *wk) | ||
1010 | { | ||
1011 | struct cfg80211_bss *cbss; | ||
1012 | u16 capa_val = WLAN_CAPABILITY_ESS; | ||
1013 | |||
1014 | if (wk->auth.privacy) | ||
1015 | capa_val |= WLAN_CAPABILITY_PRIVACY; | ||
1016 | |||
1017 | cbss = cfg80211_get_bss(local->hw.wiphy, wk->chan, wk->auth.bssid, | ||
1018 | wk->auth.ssid, wk->auth.ssid_len, | ||
1019 | WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, | ||
1020 | capa_val); | ||
1021 | if (!cbss) | ||
1022 | return; | ||
1023 | |||
1024 | cfg80211_unlink_bss(local->hw.wiphy, cbss); | ||
1025 | cfg80211_put_bss(cbss); | ||
1026 | } | ||
1027 | |||
1028 | static enum rx_mgmt_action __must_check | ||
1029 | ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata, | ||
1030 | struct ieee80211_work *wk) | ||
1031 | { | ||
1032 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | ||
1033 | struct ieee80211_local *local = sdata->local; | ||
1034 | |||
1035 | wk->auth.tries++; | ||
1036 | if (wk->auth.tries > IEEE80211_AUTH_MAX_TRIES) { | ||
1037 | printk(KERN_DEBUG "%s: direct probe to AP %pM timed out\n", | ||
1038 | sdata->name, wk->auth.bssid); | ||
1039 | |||
1040 | /* | ||
1041 | * Most likely AP is not in the range so remove the | ||
1042 | * bss struct for that AP. | ||
1043 | */ | ||
1044 | ieee80211_remove_auth_bss(local, wk); | ||
1045 | |||
1046 | /* | ||
1047 | * We might have a pending scan which had no chance to run yet | ||
1048 | * due to work needing to be done. Hence, queue the STAs work | ||
1049 | * again for that. | ||
1050 | */ | ||
1051 | ieee80211_queue_work(&local->hw, &ifmgd->work); | ||
1052 | return RX_MGMT_CFG80211_AUTH_TO; | ||
1053 | } | ||
1054 | |||
1055 | printk(KERN_DEBUG "%s: direct probe to AP %pM (try %d)\n", | ||
1056 | sdata->name, wk->auth.bssid, wk->auth.tries); | ||
1057 | |||
1058 | /* | ||
1059 | * Direct probe is sent to broadcast address as some APs | ||
1060 | * will not answer to direct packet in unassociated state. | ||
1061 | */ | ||
1062 | ieee80211_send_probe_req(sdata, NULL, wk->auth.ssid, wk->auth.ssid_len, | ||
1063 | NULL, 0); | ||
1064 | |||
1065 | wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; | ||
1066 | run_again(ifmgd, wk->timeout); | ||
1067 | |||
1068 | return RX_MGMT_NONE; | ||
1069 | } | ||
1070 | |||
1071 | |||
1072 | static enum rx_mgmt_action __must_check | ||
1073 | ieee80211_authenticate(struct ieee80211_sub_if_data *sdata, | ||
1074 | struct ieee80211_work *wk) | ||
1075 | { | ||
1076 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | ||
1077 | struct ieee80211_local *local = sdata->local; | ||
1078 | |||
1079 | wk->auth.tries++; | ||
1080 | if (wk->auth.tries > IEEE80211_AUTH_MAX_TRIES) { | ||
1081 | printk(KERN_DEBUG "%s: authentication with AP %pM" | ||
1082 | " timed out\n", sdata->name, wk->auth.bssid); | ||
1083 | |||
1084 | /* | ||
1085 | * Most likely AP is not in the range so remove the | ||
1086 | * bss struct for that AP. | ||
1087 | */ | ||
1088 | ieee80211_remove_auth_bss(local, wk); | ||
1089 | |||
1090 | /* | ||
1091 | * We might have a pending scan which had no chance to run yet | ||
1092 | * due to work needing to be done. Hence, queue the STAs work | ||
1093 | * again for that. | ||
1094 | */ | ||
1095 | ieee80211_queue_work(&local->hw, &ifmgd->work); | ||
1096 | return RX_MGMT_CFG80211_AUTH_TO; | ||
1097 | } | ||
1098 | |||
1099 | printk(KERN_DEBUG "%s: authenticate with AP %pM (try %d)\n", | ||
1100 | sdata->name, wk->auth.bssid, wk->auth.tries); | ||
1101 | |||
1102 | ieee80211_send_auth(sdata, 1, wk->auth.algorithm, wk->ie, wk->ie_len, | ||
1103 | wk->auth.bssid, NULL, 0, 0); | ||
1104 | wk->auth.transaction = 2; | ||
1105 | |||
1106 | wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; | ||
1107 | run_again(ifmgd, wk->timeout); | ||
1108 | |||
1109 | return RX_MGMT_NONE; | ||
1110 | } | ||
1111 | |||
1112 | static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata) | 727 | static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata) |
1113 | { | 728 | { |
1114 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 729 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
@@ -1195,44 +810,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata) | |||
1195 | sta_info_destroy(sta); | 810 | sta_info_destroy(sta); |
1196 | } | 811 | } |
1197 | 812 | ||
1198 | static enum rx_mgmt_action __must_check | ||
1199 | ieee80211_associate(struct ieee80211_sub_if_data *sdata, | ||
1200 | struct ieee80211_work *wk) | ||
1201 | { | ||
1202 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | ||
1203 | struct ieee80211_local *local = sdata->local; | ||
1204 | |||
1205 | wk->assoc.tries++; | ||
1206 | if (wk->assoc.tries > IEEE80211_ASSOC_MAX_TRIES) { | ||
1207 | printk(KERN_DEBUG "%s: association with AP %pM" | ||
1208 | " timed out\n", | ||
1209 | sdata->name, wk->assoc.bssid); | ||
1210 | |||
1211 | /* | ||
1212 | * Most likely AP is not in the range so remove the | ||
1213 | * bss struct for that AP. | ||
1214 | */ | ||
1215 | cfg80211_unlink_bss(local->hw.wiphy, &wk->assoc.bss->cbss); | ||
1216 | |||
1217 | /* | ||
1218 | * We might have a pending scan which had no chance to run yet | ||
1219 | * due to work needing to be done. Hence, queue the STAs work | ||
1220 | * again for that. | ||
1221 | */ | ||
1222 | ieee80211_queue_work(&local->hw, &ifmgd->work); | ||
1223 | return RX_MGMT_CFG80211_ASSOC_TO; | ||
1224 | } | ||
1225 | |||
1226 | printk(KERN_DEBUG "%s: associate with AP %pM (try %d)\n", | ||
1227 | sdata->name, wk->assoc.bssid, wk->assoc.tries); | ||
1228 | ieee80211_send_assoc(sdata, wk); | ||
1229 | |||
1230 | wk->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT; | ||
1231 | run_again(ifmgd, wk->timeout); | ||
1232 | |||
1233 | return RX_MGMT_NONE; | ||
1234 | } | ||
1235 | |||
1236 | void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, | 813 | void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, |
1237 | struct ieee80211_hdr *hdr) | 814 | struct ieee80211_hdr *hdr) |
1238 | { | 815 | { |
@@ -1338,86 +915,6 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif) | |||
1338 | } | 915 | } |
1339 | EXPORT_SYMBOL(ieee80211_beacon_loss); | 916 | EXPORT_SYMBOL(ieee80211_beacon_loss); |
1340 | 917 | ||
1341 | static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata, | ||
1342 | struct ieee80211_work *wk) | ||
1343 | { | ||
1344 | list_del(&wk->list); | ||
1345 | kfree(wk); | ||
1346 | printk(KERN_DEBUG "%s: authenticated\n", sdata->name); | ||
1347 | } | ||
1348 | |||
1349 | |||
1350 | static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, | ||
1351 | struct ieee80211_work *wk, | ||
1352 | struct ieee80211_mgmt *mgmt, | ||
1353 | size_t len) | ||
1354 | { | ||
1355 | u8 *pos; | ||
1356 | struct ieee802_11_elems elems; | ||
1357 | |||
1358 | pos = mgmt->u.auth.variable; | ||
1359 | ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); | ||
1360 | if (!elems.challenge) | ||
1361 | return; | ||
1362 | ieee80211_send_auth(sdata, 3, wk->auth.algorithm, | ||
1363 | elems.challenge - 2, elems.challenge_len + 2, | ||
1364 | wk->auth.bssid, wk->auth.key, wk->auth.key_len, | ||
1365 | wk->auth.key_idx); | ||
1366 | wk->auth.transaction = 4; | ||
1367 | } | ||
1368 | |||
1369 | static enum rx_mgmt_action __must_check | ||
1370 | ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, | ||
1371 | struct ieee80211_work *wk, | ||
1372 | struct ieee80211_mgmt *mgmt, size_t len) | ||
1373 | { | ||
1374 | u16 auth_alg, auth_transaction, status_code; | ||
1375 | |||
1376 | if (wk->type != IEEE80211_WORK_AUTH) | ||
1377 | return RX_MGMT_NONE; | ||
1378 | |||
1379 | if (len < 24 + 6) | ||
1380 | return RX_MGMT_NONE; | ||
1381 | |||
1382 | if (memcmp(wk->auth.bssid, mgmt->sa, ETH_ALEN) != 0) | ||
1383 | return RX_MGMT_NONE; | ||
1384 | |||
1385 | if (memcmp(wk->auth.bssid, mgmt->bssid, ETH_ALEN) != 0) | ||
1386 | return RX_MGMT_NONE; | ||
1387 | |||
1388 | auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); | ||
1389 | auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); | ||
1390 | status_code = le16_to_cpu(mgmt->u.auth.status_code); | ||
1391 | |||
1392 | if (auth_alg != wk->auth.algorithm || | ||
1393 | auth_transaction != wk->auth.transaction) | ||
1394 | return RX_MGMT_NONE; | ||
1395 | |||
1396 | if (status_code != WLAN_STATUS_SUCCESS) { | ||
1397 | list_del(&wk->list); | ||
1398 | kfree(wk); | ||
1399 | return RX_MGMT_CFG80211_AUTH; | ||
1400 | } | ||
1401 | |||
1402 | switch (wk->auth.algorithm) { | ||
1403 | case WLAN_AUTH_OPEN: | ||
1404 | case WLAN_AUTH_LEAP: | ||
1405 | case WLAN_AUTH_FT: | ||
1406 | ieee80211_auth_completed(sdata, wk); | ||
1407 | return RX_MGMT_CFG80211_AUTH; | ||
1408 | case WLAN_AUTH_SHARED_KEY: | ||
1409 | if (wk->auth.transaction == 4) { | ||
1410 | ieee80211_auth_completed(sdata, wk); | ||
1411 | return RX_MGMT_CFG80211_AUTH; | ||
1412 | } else | ||
1413 | ieee80211_auth_challenge(sdata, wk, mgmt, len); | ||
1414 | break; | ||
1415 | } | ||
1416 | |||
1417 | return RX_MGMT_NONE; | ||
1418 | } | ||
1419 | |||
1420 | |||
1421 | static enum rx_mgmt_action __must_check | 918 | static enum rx_mgmt_action __must_check |
1422 | ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, | 919 | ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, |
1423 | struct ieee80211_mgmt *mgmt, size_t len) | 920 | struct ieee80211_mgmt *mgmt, size_t len) |
@@ -1474,98 +971,51 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, | |||
1474 | } | 971 | } |
1475 | 972 | ||
1476 | 973 | ||
1477 | static enum rx_mgmt_action __must_check | 974 | static bool ieee80211_assoc_success(struct ieee80211_work *wk, |
1478 | ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, | 975 | struct ieee80211_mgmt *mgmt, size_t len) |
1479 | struct ieee80211_work *wk, | ||
1480 | struct ieee80211_mgmt *mgmt, size_t len, | ||
1481 | bool reassoc) | ||
1482 | { | 976 | { |
977 | struct ieee80211_sub_if_data *sdata = wk->sdata; | ||
1483 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 978 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
1484 | struct ieee80211_local *local = sdata->local; | 979 | struct ieee80211_local *local = sdata->local; |
1485 | struct ieee80211_supported_band *sband; | 980 | struct ieee80211_supported_band *sband; |
1486 | struct sta_info *sta; | 981 | struct sta_info *sta; |
1487 | struct ieee80211_bss *bss = wk->assoc.bss; | 982 | struct ieee80211_bss *bss = wk->assoc.bss; |
983 | u8 *pos; | ||
1488 | u32 rates, basic_rates; | 984 | u32 rates, basic_rates; |
1489 | u16 capab_info, status_code, aid; | 985 | u16 capab_info, aid; |
1490 | struct ieee802_11_elems elems; | 986 | struct ieee802_11_elems elems; |
1491 | struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; | 987 | struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; |
1492 | u8 *pos; | ||
1493 | u32 changed = 0; | 988 | u32 changed = 0; |
1494 | int i, j, err; | 989 | int i, j, err; |
1495 | bool have_higher_than_11mbit = false; | 990 | bool have_higher_than_11mbit = false; |
1496 | u16 ap_ht_cap_flags; | 991 | u16 ap_ht_cap_flags; |
1497 | 992 | ||
1498 | /* | 993 | /* AssocResp and ReassocResp have identical structure */ |
1499 | * AssocResp and ReassocResp have identical structure, so process both | ||
1500 | * of them in this function. | ||
1501 | */ | ||
1502 | |||
1503 | if (len < 24 + 6) | ||
1504 | return RX_MGMT_NONE; | ||
1505 | 994 | ||
1506 | if (memcmp(bss->cbss.bssid, mgmt->sa, ETH_ALEN) != 0) | ||
1507 | return RX_MGMT_NONE; | ||
1508 | |||
1509 | capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); | ||
1510 | status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); | ||
1511 | aid = le16_to_cpu(mgmt->u.assoc_resp.aid); | 995 | aid = le16_to_cpu(mgmt->u.assoc_resp.aid); |
1512 | 996 | capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); | |
1513 | printk(KERN_DEBUG "%s: RX %sssocResp from %pM (capab=0x%x " | ||
1514 | "status=%d aid=%d)\n", | ||
1515 | sdata->name, reassoc ? "Rea" : "A", mgmt->sa, | ||
1516 | capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14)))); | ||
1517 | |||
1518 | pos = mgmt->u.assoc_resp.variable; | ||
1519 | ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); | ||
1520 | |||
1521 | if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && | ||
1522 | elems.timeout_int && elems.timeout_int_len == 5 && | ||
1523 | elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) { | ||
1524 | u32 tu, ms; | ||
1525 | tu = get_unaligned_le32(elems.timeout_int + 1); | ||
1526 | ms = tu * 1024 / 1000; | ||
1527 | printk(KERN_DEBUG "%s: AP rejected association temporarily; " | ||
1528 | "comeback duration %u TU (%u ms)\n", | ||
1529 | sdata->name, tu, ms); | ||
1530 | wk->timeout = jiffies + msecs_to_jiffies(ms); | ||
1531 | if (ms > IEEE80211_ASSOC_TIMEOUT) | ||
1532 | run_again(ifmgd, jiffies + msecs_to_jiffies(ms)); | ||
1533 | return RX_MGMT_NONE; | ||
1534 | } | ||
1535 | |||
1536 | /* | ||
1537 | * Here the association was either successful or not. | ||
1538 | */ | ||
1539 | |||
1540 | /* delete work item -- must be before set_associated for PS */ | ||
1541 | list_del(&wk->list); | ||
1542 | kfree(wk); | ||
1543 | |||
1544 | if (status_code != WLAN_STATUS_SUCCESS) { | ||
1545 | printk(KERN_DEBUG "%s: AP denied association (code=%d)\n", | ||
1546 | sdata->name, status_code); | ||
1547 | return RX_MGMT_CFG80211_ASSOC; | ||
1548 | } | ||
1549 | 997 | ||
1550 | if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) | 998 | if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) |
1551 | printk(KERN_DEBUG "%s: invalid aid value %d; bits 15:14 not " | 999 | printk(KERN_DEBUG "%s: invalid aid value %d; bits 15:14 not " |
1552 | "set\n", sdata->name, aid); | 1000 | "set\n", sdata->name, aid); |
1553 | aid &= ~(BIT(15) | BIT(14)); | 1001 | aid &= ~(BIT(15) | BIT(14)); |
1554 | 1002 | ||
1003 | pos = mgmt->u.assoc_resp.variable; | ||
1004 | ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); | ||
1005 | |||
1555 | if (!elems.supp_rates) { | 1006 | if (!elems.supp_rates) { |
1556 | printk(KERN_DEBUG "%s: no SuppRates element in AssocResp\n", | 1007 | printk(KERN_DEBUG "%s: no SuppRates element in AssocResp\n", |
1557 | sdata->name); | 1008 | sdata->name); |
1558 | return RX_MGMT_NONE; | 1009 | return false; |
1559 | } | 1010 | } |
1560 | 1011 | ||
1561 | printk(KERN_DEBUG "%s: associated\n", sdata->name); | ||
1562 | ifmgd->aid = aid; | 1012 | ifmgd->aid = aid; |
1563 | 1013 | ||
1564 | sta = sta_info_alloc(sdata, bss->cbss.bssid, GFP_KERNEL); | 1014 | sta = sta_info_alloc(sdata, bss->cbss.bssid, GFP_KERNEL); |
1565 | if (!sta) { | 1015 | if (!sta) { |
1566 | printk(KERN_DEBUG "%s: failed to alloc STA entry for" | 1016 | printk(KERN_DEBUG "%s: failed to alloc STA entry for" |
1567 | " the AP\n", sdata->name); | 1017 | " the AP\n", sdata->name); |
1568 | return RX_MGMT_CFG80211_ASSOC_ERROR; | 1018 | return false; |
1569 | } | 1019 | } |
1570 | 1020 | ||
1571 | set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC | | 1021 | set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC | |
@@ -1650,7 +1100,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, | |||
1650 | ieee80211_set_wmm_default(sdata); | 1100 | ieee80211_set_wmm_default(sdata); |
1651 | 1101 | ||
1652 | if (elems.ht_info_elem && elems.wmm_param && | 1102 | if (elems.ht_info_elem && elems.wmm_param && |
1653 | (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) && | 1103 | (sdata->local->hw.queues >= 4) && |
1654 | !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) | 1104 | !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) |
1655 | changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem, | 1105 | changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem, |
1656 | bss->cbss.bssid, | 1106 | bss->cbss.bssid, |
@@ -1669,7 +1119,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, | |||
1669 | ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt); | 1119 | ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt); |
1670 | mod_beacon_timer(sdata); | 1120 | mod_beacon_timer(sdata); |
1671 | 1121 | ||
1672 | return RX_MGMT_CFG80211_ASSOC; | 1122 | return true; |
1673 | } | 1123 | } |
1674 | 1124 | ||
1675 | 1125 | ||
@@ -1714,12 +1164,12 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, | |||
1714 | 1164 | ||
1715 | 1165 | ||
1716 | static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, | 1166 | static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, |
1717 | struct ieee80211_work *wk, | 1167 | struct sk_buff *skb) |
1718 | struct ieee80211_mgmt *mgmt, size_t len, | ||
1719 | struct ieee80211_rx_status *rx_status) | ||
1720 | { | 1168 | { |
1169 | struct ieee80211_mgmt *mgmt = (void *)skb->data; | ||
1721 | struct ieee80211_if_managed *ifmgd; | 1170 | struct ieee80211_if_managed *ifmgd; |
1722 | size_t baselen; | 1171 | struct ieee80211_rx_status *rx_status = (void *) skb->cb; |
1172 | size_t baselen, len = skb->len; | ||
1723 | struct ieee802_11_elems elems; | 1173 | struct ieee802_11_elems elems; |
1724 | 1174 | ||
1725 | ifmgd = &sdata->u.mgd; | 1175 | ifmgd = &sdata->u.mgd; |
@@ -1738,15 +1188,6 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, | |||
1738 | 1188 | ||
1739 | ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false); | 1189 | ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false); |
1740 | 1190 | ||
1741 | /* direct probe may be part of the association flow */ | ||
1742 | if (wk && wk->type == IEEE80211_WORK_AUTH_PROBE) { | ||
1743 | printk(KERN_DEBUG "%s: direct probe responded\n", | ||
1744 | sdata->name); | ||
1745 | wk->auth.tries = 0; | ||
1746 | wk->type = IEEE80211_WORK_AUTH; | ||
1747 | WARN_ON(ieee80211_authenticate(sdata, wk) != RX_MGMT_NONE); | ||
1748 | } | ||
1749 | |||
1750 | if (ifmgd->associated && | 1191 | if (ifmgd->associated && |
1751 | memcmp(mgmt->bssid, ifmgd->associated->cbss.bssid, ETH_ALEN) == 0 && | 1192 | memcmp(mgmt->bssid, ifmgd->associated->cbss.bssid, ETH_ALEN) == 0 && |
1752 | ifmgd->flags & (IEEE80211_STA_BEACON_POLL | | 1193 | ifmgd->flags & (IEEE80211_STA_BEACON_POLL | |
@@ -1960,9 +1401,6 @@ ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, | |||
1960 | switch (fc & IEEE80211_FCTL_STYPE) { | 1401 | switch (fc & IEEE80211_FCTL_STYPE) { |
1961 | case IEEE80211_STYPE_PROBE_RESP: | 1402 | case IEEE80211_STYPE_PROBE_RESP: |
1962 | case IEEE80211_STYPE_BEACON: | 1403 | case IEEE80211_STYPE_BEACON: |
1963 | case IEEE80211_STYPE_AUTH: | ||
1964 | case IEEE80211_STYPE_ASSOC_RESP: | ||
1965 | case IEEE80211_STYPE_REASSOC_RESP: | ||
1966 | case IEEE80211_STYPE_DEAUTH: | 1404 | case IEEE80211_STYPE_DEAUTH: |
1967 | case IEEE80211_STYPE_DISASSOC: | 1405 | case IEEE80211_STYPE_DISASSOC: |
1968 | case IEEE80211_STYPE_ACTION: | 1406 | case IEEE80211_STYPE_ACTION: |
@@ -1980,7 +1418,6 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, | |||
1980 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 1418 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
1981 | struct ieee80211_rx_status *rx_status; | 1419 | struct ieee80211_rx_status *rx_status; |
1982 | struct ieee80211_mgmt *mgmt; | 1420 | struct ieee80211_mgmt *mgmt; |
1983 | struct ieee80211_work *wk; | ||
1984 | enum rx_mgmt_action rma = RX_MGMT_NONE; | 1421 | enum rx_mgmt_action rma = RX_MGMT_NONE; |
1985 | u16 fc; | 1422 | u16 fc; |
1986 | 1423 | ||
@@ -1999,8 +1436,7 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, | |||
1999 | rx_status); | 1436 | rx_status); |
2000 | break; | 1437 | break; |
2001 | case IEEE80211_STYPE_PROBE_RESP: | 1438 | case IEEE80211_STYPE_PROBE_RESP: |
2002 | ieee80211_rx_mgmt_probe_resp(sdata, NULL, mgmt, | 1439 | ieee80211_rx_mgmt_probe_resp(sdata, skb); |
2003 | skb->len, rx_status); | ||
2004 | break; | 1440 | break; |
2005 | case IEEE80211_STYPE_DEAUTH: | 1441 | case IEEE80211_STYPE_DEAUTH: |
2006 | rma = ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len); | 1442 | rma = ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len); |
@@ -2033,88 +1469,11 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, | |||
2033 | goto out; | 1469 | goto out; |
2034 | } | 1470 | } |
2035 | 1471 | ||
2036 | list_for_each_entry(wk, &ifmgd->work_list, list) { | ||
2037 | const u8 *bssid = NULL; | ||
2038 | |||
2039 | switch (wk->type) { | ||
2040 | case IEEE80211_WORK_AUTH_PROBE: | ||
2041 | case IEEE80211_WORK_AUTH: | ||
2042 | bssid = wk->auth.bssid; | ||
2043 | break; | ||
2044 | case IEEE80211_WORK_ASSOC: | ||
2045 | bssid = wk->assoc.bssid; | ||
2046 | break; | ||
2047 | default: | ||
2048 | continue; | ||
2049 | } | ||
2050 | if (memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0) | ||
2051 | continue; | ||
2052 | |||
2053 | switch (fc & IEEE80211_FCTL_STYPE) { | ||
2054 | case IEEE80211_STYPE_PROBE_RESP: | ||
2055 | ieee80211_rx_mgmt_probe_resp(sdata, wk, mgmt, skb->len, | ||
2056 | rx_status); | ||
2057 | break; | ||
2058 | case IEEE80211_STYPE_AUTH: | ||
2059 | rma = ieee80211_rx_mgmt_auth(sdata, wk, mgmt, skb->len); | ||
2060 | break; | ||
2061 | case IEEE80211_STYPE_ASSOC_RESP: | ||
2062 | rma = ieee80211_rx_mgmt_assoc_resp(sdata, wk, mgmt, | ||
2063 | skb->len, false); | ||
2064 | break; | ||
2065 | case IEEE80211_STYPE_REASSOC_RESP: | ||
2066 | rma = ieee80211_rx_mgmt_assoc_resp(sdata, wk, mgmt, | ||
2067 | skb->len, true); | ||
2068 | break; | ||
2069 | case IEEE80211_STYPE_DEAUTH: | ||
2070 | if (skb->len >= 24 + 2 /* mgmt + deauth reason */) { | ||
2071 | /* | ||
2072 | * We get here if we get deauth while | ||
2073 | * trying to auth/assoc. Telling cfg80211 | ||
2074 | * is handled below, unconditionally. | ||
2075 | */ | ||
2076 | list_del(&wk->list); | ||
2077 | kfree(wk); | ||
2078 | } | ||
2079 | break; | ||
2080 | } | ||
2081 | /* | ||
2082 | * We've processed this frame for that work, so it can't | ||
2083 | * belong to another work struct. | ||
2084 | * NB: this is also required for correctness because the | ||
2085 | * called functions can free 'wk', and for 'rma'! | ||
2086 | */ | ||
2087 | break; | ||
2088 | } | ||
2089 | |||
2090 | mutex_unlock(&ifmgd->mtx); | 1472 | mutex_unlock(&ifmgd->mtx); |
2091 | 1473 | ||
2092 | if (skb->len >= 24 + 2 /* mgmt + deauth reason */ && | 1474 | if (skb->len >= 24 + 2 /* mgmt + deauth reason */ && |
2093 | (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_DEAUTH) { | 1475 | (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_DEAUTH) |
2094 | WARN_ON(rma != RX_MGMT_NONE); | ||
2095 | rma = RX_MGMT_CFG80211_DEAUTH; | ||
2096 | } | ||
2097 | |||
2098 | switch (rma) { | ||
2099 | case RX_MGMT_NONE: | ||
2100 | /* no action */ | ||
2101 | break; | ||
2102 | case RX_MGMT_CFG80211_AUTH: | ||
2103 | cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, skb->len); | ||
2104 | break; | ||
2105 | case RX_MGMT_CFG80211_ASSOC: | ||
2106 | cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, skb->len); | ||
2107 | break; | ||
2108 | case RX_MGMT_CFG80211_DEAUTH: | ||
2109 | cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len); | 1476 | cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len); |
2110 | break; | ||
2111 | case RX_MGMT_CFG80211_ASSOC_ERROR: | ||
2112 | /* an internal error -- pretend timeout for now */ | ||
2113 | cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid); | ||
2114 | break; | ||
2115 | default: | ||
2116 | WARN(1, "unexpected: %d", rma); | ||
2117 | } | ||
2118 | 1477 | ||
2119 | out: | 1478 | out: |
2120 | kfree_skb(skb); | 1479 | kfree_skb(skb); |
@@ -2142,9 +1501,6 @@ static void ieee80211_sta_work(struct work_struct *work) | |||
2142 | struct ieee80211_local *local = sdata->local; | 1501 | struct ieee80211_local *local = sdata->local; |
2143 | struct ieee80211_if_managed *ifmgd; | 1502 | struct ieee80211_if_managed *ifmgd; |
2144 | struct sk_buff *skb; | 1503 | struct sk_buff *skb; |
2145 | struct ieee80211_work *wk, *tmp; | ||
2146 | LIST_HEAD(free_work); | ||
2147 | enum rx_mgmt_action rma; | ||
2148 | 1504 | ||
2149 | if (!ieee80211_sdata_running(sdata)) | 1505 | if (!ieee80211_sdata_running(sdata)) |
2150 | return; | 1506 | return; |
@@ -2214,84 +1570,7 @@ static void ieee80211_sta_work(struct work_struct *work) | |||
2214 | } | 1570 | } |
2215 | } | 1571 | } |
2216 | 1572 | ||
2217 | |||
2218 | ieee80211_recalc_idle(local); | ||
2219 | |||
2220 | list_for_each_entry_safe(wk, tmp, &ifmgd->work_list, list) { | ||
2221 | if (time_is_after_jiffies(wk->timeout)) { | ||
2222 | /* | ||
2223 | * This work item isn't supposed to be worked on | ||
2224 | * right now, but take care to adjust the timer | ||
2225 | * properly. | ||
2226 | */ | ||
2227 | run_again(ifmgd, wk->timeout); | ||
2228 | continue; | ||
2229 | } | ||
2230 | |||
2231 | switch (wk->type) { | ||
2232 | default: | ||
2233 | WARN_ON(1); | ||
2234 | /* nothing */ | ||
2235 | rma = RX_MGMT_NONE; | ||
2236 | break; | ||
2237 | case IEEE80211_WORK_AUTH_PROBE: | ||
2238 | rma = ieee80211_direct_probe(sdata, wk); | ||
2239 | break; | ||
2240 | case IEEE80211_WORK_AUTH: | ||
2241 | rma = ieee80211_authenticate(sdata, wk); | ||
2242 | break; | ||
2243 | case IEEE80211_WORK_ASSOC: | ||
2244 | rma = ieee80211_associate(sdata, wk); | ||
2245 | break; | ||
2246 | } | ||
2247 | |||
2248 | switch (rma) { | ||
2249 | case RX_MGMT_NONE: | ||
2250 | /* no action required */ | ||
2251 | break; | ||
2252 | case RX_MGMT_CFG80211_AUTH_TO: | ||
2253 | case RX_MGMT_CFG80211_ASSOC_TO: | ||
2254 | list_del(&wk->list); | ||
2255 | list_add(&wk->list, &free_work); | ||
2256 | /* | ||
2257 | * small abuse but only local -- keep the | ||
2258 | * action type in wk->timeout while the item | ||
2259 | * is on the cleanup list | ||
2260 | */ | ||
2261 | wk->timeout = rma; | ||
2262 | break; | ||
2263 | default: | ||
2264 | WARN(1, "unexpected: %d", rma); | ||
2265 | } | ||
2266 | } | ||
2267 | |||
2268 | if (list_empty(&ifmgd->work_list) && | ||
2269 | test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request)) | ||
2270 | ieee80211_queue_delayed_work(&local->hw, | ||
2271 | &local->scan_work, | ||
2272 | round_jiffies_relative(0)); | ||
2273 | |||
2274 | mutex_unlock(&ifmgd->mtx); | 1573 | mutex_unlock(&ifmgd->mtx); |
2275 | |||
2276 | list_for_each_entry_safe(wk, tmp, &free_work, list) { | ||
2277 | /* see above how we're using wk->timeout */ | ||
2278 | switch (wk->timeout) { | ||
2279 | case RX_MGMT_CFG80211_AUTH_TO: | ||
2280 | cfg80211_send_auth_timeout(sdata->dev, wk->auth.bssid); | ||
2281 | break; | ||
2282 | case RX_MGMT_CFG80211_ASSOC_TO: | ||
2283 | cfg80211_send_assoc_timeout(sdata->dev, | ||
2284 | wk->assoc.bssid); | ||
2285 | break; | ||
2286 | default: | ||
2287 | WARN(1, "unexpected: %lu", wk->timeout); | ||
2288 | } | ||
2289 | |||
2290 | list_del(&wk->list); | ||
2291 | kfree(wk); | ||
2292 | } | ||
2293 | |||
2294 | ieee80211_recalc_idle(local); | ||
2295 | } | 1574 | } |
2296 | 1575 | ||
2297 | static void ieee80211_sta_bcn_mon_timer(unsigned long data) | 1576 | static void ieee80211_sta_bcn_mon_timer(unsigned long data) |
@@ -2400,12 +1679,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) | |||
2400 | (unsigned long) sdata); | 1679 | (unsigned long) sdata); |
2401 | skb_queue_head_init(&ifmgd->skb_queue); | 1680 | skb_queue_head_init(&ifmgd->skb_queue); |
2402 | 1681 | ||
2403 | INIT_LIST_HEAD(&ifmgd->work_list); | ||
2404 | |||
2405 | ifmgd->capab = WLAN_CAPABILITY_ESS; | ||
2406 | ifmgd->flags = 0; | 1682 | ifmgd->flags = 0; |
2407 | if (sdata->local->hw.queues >= 4) | ||
2408 | ifmgd->flags |= IEEE80211_STA_WMM_ENABLED; | ||
2409 | 1683 | ||
2410 | mutex_init(&ifmgd->mtx); | 1684 | mutex_init(&ifmgd->mtx); |
2411 | 1685 | ||
@@ -2443,10 +1717,32 @@ int ieee80211_max_network_latency(struct notifier_block *nb, | |||
2443 | } | 1717 | } |
2444 | 1718 | ||
2445 | /* config hooks */ | 1719 | /* config hooks */ |
1720 | static enum work_done_result | ||
1721 | ieee80211_probe_auth_done(struct ieee80211_work *wk, | ||
1722 | struct sk_buff *skb) | ||
1723 | { | ||
1724 | if (!skb) { | ||
1725 | cfg80211_send_auth_timeout(wk->sdata->dev, wk->filter_ta); | ||
1726 | return WORK_DONE_DESTROY; | ||
1727 | } | ||
1728 | |||
1729 | if (wk->type == IEEE80211_WORK_AUTH) { | ||
1730 | cfg80211_send_rx_auth(wk->sdata->dev, skb->data, skb->len); | ||
1731 | return WORK_DONE_DESTROY; | ||
1732 | } | ||
1733 | |||
1734 | mutex_lock(&wk->sdata->u.mgd.mtx); | ||
1735 | ieee80211_rx_mgmt_probe_resp(wk->sdata, skb); | ||
1736 | mutex_unlock(&wk->sdata->u.mgd.mtx); | ||
1737 | |||
1738 | wk->type = IEEE80211_WORK_AUTH; | ||
1739 | wk->probe_auth.tries = 0; | ||
1740 | return WORK_DONE_REQUEUE; | ||
1741 | } | ||
1742 | |||
2446 | int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, | 1743 | int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, |
2447 | struct cfg80211_auth_request *req) | 1744 | struct cfg80211_auth_request *req) |
2448 | { | 1745 | { |
2449 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | ||
2450 | const u8 *ssid; | 1746 | const u8 *ssid; |
2451 | struct ieee80211_work *wk; | 1747 | struct ieee80211_work *wk; |
2452 | u16 auth_alg; | 1748 | u16 auth_alg; |
@@ -2472,7 +1768,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, | |||
2472 | if (!wk) | 1768 | if (!wk) |
2473 | return -ENOMEM; | 1769 | return -ENOMEM; |
2474 | 1770 | ||
2475 | memcpy(wk->auth.bssid, req->bss->bssid, ETH_ALEN);; | 1771 | memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN);; |
2476 | 1772 | ||
2477 | if (req->ie && req->ie_len) { | 1773 | if (req->ie && req->ie_len) { |
2478 | memcpy(wk->ie, req->ie, req->ie_len); | 1774 | memcpy(wk->ie, req->ie, req->ie_len); |
@@ -2480,21 +1776,22 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, | |||
2480 | } | 1776 | } |
2481 | 1777 | ||
2482 | if (req->key && req->key_len) { | 1778 | if (req->key && req->key_len) { |
2483 | wk->auth.key_len = req->key_len; | 1779 | wk->probe_auth.key_len = req->key_len; |
2484 | wk->auth.key_idx = req->key_idx; | 1780 | wk->probe_auth.key_idx = req->key_idx; |
2485 | memcpy(wk->auth.key, req->key, req->key_len); | 1781 | memcpy(wk->probe_auth.key, req->key, req->key_len); |
2486 | } | 1782 | } |
2487 | 1783 | ||
2488 | ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); | 1784 | ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); |
2489 | memcpy(wk->auth.ssid, ssid + 2, ssid[1]); | 1785 | memcpy(wk->probe_auth.ssid, ssid + 2, ssid[1]); |
2490 | wk->auth.ssid_len = ssid[1]; | 1786 | wk->probe_auth.ssid_len = ssid[1]; |
2491 | 1787 | ||
2492 | wk->auth.algorithm = auth_alg; | 1788 | wk->probe_auth.algorithm = auth_alg; |
2493 | wk->auth.privacy = req->bss->capability & WLAN_CAPABILITY_PRIVACY; | 1789 | wk->probe_auth.privacy = req->bss->capability & WLAN_CAPABILITY_PRIVACY; |
2494 | 1790 | ||
2495 | wk->type = IEEE80211_WORK_AUTH_PROBE; | 1791 | wk->type = IEEE80211_WORK_DIRECT_PROBE; |
2496 | wk->timeout = jiffies; /* run right away */ | ||
2497 | wk->chan = req->bss->channel; | 1792 | wk->chan = req->bss->channel; |
1793 | wk->sdata = sdata; | ||
1794 | wk->done = ieee80211_probe_auth_done; | ||
2498 | 1795 | ||
2499 | /* | 1796 | /* |
2500 | * XXX: if still associated need to tell AP that we're going | 1797 | * XXX: if still associated need to tell AP that we're going |
@@ -2505,29 +1802,58 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, | |||
2505 | sdata->local->oper_channel = req->bss->channel; | 1802 | sdata->local->oper_channel = req->bss->channel; |
2506 | ieee80211_hw_config(sdata->local, 0); | 1803 | ieee80211_hw_config(sdata->local, 0); |
2507 | 1804 | ||
2508 | mutex_lock(&ifmgd->mtx); | 1805 | ieee80211_add_work(wk); |
2509 | list_add(&wk->list, &sdata->u.mgd.work_list); | ||
2510 | mutex_unlock(&ifmgd->mtx); | ||
2511 | |||
2512 | ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.work); | ||
2513 | return 0; | 1806 | return 0; |
2514 | } | 1807 | } |
2515 | 1808 | ||
1809 | static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk, | ||
1810 | struct sk_buff *skb) | ||
1811 | { | ||
1812 | struct ieee80211_mgmt *mgmt; | ||
1813 | u16 status; | ||
1814 | |||
1815 | if (!skb) { | ||
1816 | cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta); | ||
1817 | return WORK_DONE_DESTROY; | ||
1818 | } | ||
1819 | |||
1820 | mgmt = (void *)skb->data; | ||
1821 | status = le16_to_cpu(mgmt->u.assoc_resp.status_code); | ||
1822 | |||
1823 | if (status == WLAN_STATUS_SUCCESS) { | ||
1824 | mutex_lock(&wk->sdata->u.mgd.mtx); | ||
1825 | if (!ieee80211_assoc_success(wk, mgmt, skb->len)) { | ||
1826 | mutex_unlock(&wk->sdata->u.mgd.mtx); | ||
1827 | /* oops -- internal error -- send timeout for now */ | ||
1828 | cfg80211_send_assoc_timeout(wk->sdata->dev, | ||
1829 | wk->filter_ta); | ||
1830 | return WORK_DONE_DESTROY; | ||
1831 | } | ||
1832 | mutex_unlock(&wk->sdata->u.mgd.mtx); | ||
1833 | } | ||
1834 | |||
1835 | cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len); | ||
1836 | return WORK_DONE_DESTROY; | ||
1837 | } | ||
1838 | |||
2516 | int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | 1839 | int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, |
2517 | struct cfg80211_assoc_request *req) | 1840 | struct cfg80211_assoc_request *req) |
2518 | { | 1841 | { |
2519 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 1842 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
2520 | struct ieee80211_work *wk; | 1843 | struct ieee80211_work *wk; |
2521 | const u8 *ssid; | 1844 | const u8 *ssid; |
2522 | int i, err; | 1845 | int i; |
2523 | 1846 | ||
2524 | mutex_lock(&ifmgd->mtx); | 1847 | mutex_lock(&ifmgd->mtx); |
1848 | if (ifmgd->associated) { | ||
1849 | mutex_unlock(&ifmgd->mtx); | ||
1850 | return -EALREADY; | ||
1851 | } | ||
1852 | mutex_unlock(&ifmgd->mtx); | ||
2525 | 1853 | ||
2526 | wk = kzalloc(sizeof(*wk) + req->ie_len, GFP_KERNEL); | 1854 | wk = kzalloc(sizeof(*wk) + req->ie_len, GFP_KERNEL); |
2527 | if (!wk) { | 1855 | if (!wk) |
2528 | err = -ENOMEM; | 1856 | return -ENOMEM; |
2529 | goto out; | ||
2530 | } | ||
2531 | 1857 | ||
2532 | ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N; | 1858 | ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N; |
2533 | 1859 | ||
@@ -2546,8 +1872,19 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
2546 | 1872 | ||
2547 | wk->assoc.bss = (void *)req->bss; | 1873 | wk->assoc.bss = (void *)req->bss; |
2548 | 1874 | ||
2549 | memcpy(wk->assoc.bssid, req->bss->bssid, ETH_ALEN); | 1875 | memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN); |
2550 | 1876 | ||
1877 | /* new association always uses requested smps mode */ | ||
1878 | if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) { | ||
1879 | if (ifmgd->powersave) | ||
1880 | ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC; | ||
1881 | else | ||
1882 | ifmgd->ap_smps = IEEE80211_SMPS_OFF; | ||
1883 | } else | ||
1884 | ifmgd->ap_smps = ifmgd->req_smps; | ||
1885 | |||
1886 | wk->assoc.smps = ifmgd->ap_smps; | ||
1887 | wk->assoc.use_11n = !(ifmgd->flags & IEEE80211_STA_DISABLE_11N); | ||
2551 | wk->assoc.capability = req->bss->capability; | 1888 | wk->assoc.capability = req->bss->capability; |
2552 | wk->assoc.wmm_used = wk->assoc.bss->wmm_used; | 1889 | wk->assoc.wmm_used = wk->assoc.bss->wmm_used; |
2553 | wk->assoc.supp_rates = wk->assoc.bss->supp_rates; | 1890 | wk->assoc.supp_rates = wk->assoc.bss->supp_rates; |
@@ -2563,8 +1900,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
2563 | memcpy(wk->assoc.prev_bssid, req->prev_bssid, ETH_ALEN); | 1900 | memcpy(wk->assoc.prev_bssid, req->prev_bssid, ETH_ALEN); |
2564 | 1901 | ||
2565 | wk->type = IEEE80211_WORK_ASSOC; | 1902 | wk->type = IEEE80211_WORK_ASSOC; |
2566 | wk->timeout = jiffies; /* run right away */ | ||
2567 | wk->chan = req->bss->channel; | 1903 | wk->chan = req->bss->channel; |
1904 | wk->sdata = sdata; | ||
1905 | wk->done = ieee80211_assoc_done; | ||
2568 | 1906 | ||
2569 | if (req->use_mfp) { | 1907 | if (req->use_mfp) { |
2570 | ifmgd->mfp = IEEE80211_MFP_REQUIRED; | 1908 | ifmgd->mfp = IEEE80211_MFP_REQUIRED; |
@@ -2582,56 +1920,56 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
2582 | sdata->local->oper_channel = req->bss->channel; | 1920 | sdata->local->oper_channel = req->bss->channel; |
2583 | ieee80211_hw_config(sdata->local, 0); | 1921 | ieee80211_hw_config(sdata->local, 0); |
2584 | 1922 | ||
2585 | list_add(&wk->list, &ifmgd->work_list); | 1923 | ieee80211_add_work(wk); |
2586 | ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.work); | 1924 | return 0; |
2587 | |||
2588 | err = 0; | ||
2589 | |||
2590 | out: | ||
2591 | mutex_unlock(&ifmgd->mtx); | ||
2592 | return err; | ||
2593 | } | 1925 | } |
2594 | 1926 | ||
2595 | int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, | 1927 | int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, |
2596 | struct cfg80211_deauth_request *req, | 1928 | struct cfg80211_deauth_request *req, |
2597 | void *cookie) | 1929 | void *cookie) |
2598 | { | 1930 | { |
1931 | struct ieee80211_local *local = sdata->local; | ||
2599 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 1932 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
2600 | struct ieee80211_work *wk; | 1933 | struct ieee80211_work *wk; |
2601 | const u8 *bssid = req->bss->bssid; | 1934 | const u8 *bssid = req->bss->bssid; |
2602 | bool not_auth_yet = false; | ||
2603 | 1935 | ||
2604 | mutex_lock(&ifmgd->mtx); | 1936 | mutex_lock(&ifmgd->mtx); |
2605 | 1937 | ||
2606 | if (ifmgd->associated && &ifmgd->associated->cbss == req->bss) { | 1938 | if (ifmgd->associated && &ifmgd->associated->cbss == req->bss) { |
2607 | bssid = req->bss->bssid; | 1939 | bssid = req->bss->bssid; |
2608 | ieee80211_set_disassoc(sdata); | 1940 | ieee80211_set_disassoc(sdata); |
2609 | } else list_for_each_entry(wk, &ifmgd->work_list, list) { | 1941 | mutex_unlock(&ifmgd->mtx); |
2610 | if (wk->type != IEEE80211_WORK_AUTH_PROBE) | 1942 | } else { |
2611 | continue; | 1943 | bool not_auth_yet = false; |
2612 | if (memcmp(req->bss->bssid, wk->auth.bssid, ETH_ALEN)) | ||
2613 | continue; | ||
2614 | not_auth_yet = true; | ||
2615 | list_del(&wk->list); | ||
2616 | kfree(wk); | ||
2617 | break; | ||
2618 | } | ||
2619 | 1944 | ||
2620 | /* | ||
2621 | * If somebody requests authentication and we haven't | ||
2622 | * sent out an auth frame yet there's no need to send | ||
2623 | * out a deauth frame either. If the state was PROBE, | ||
2624 | * then this is the case. If it's AUTH we have sent a | ||
2625 | * frame, and if it's IDLE we have completed the auth | ||
2626 | * process already. | ||
2627 | */ | ||
2628 | if (not_auth_yet) { | ||
2629 | mutex_unlock(&ifmgd->mtx); | 1945 | mutex_unlock(&ifmgd->mtx); |
2630 | __cfg80211_auth_canceled(sdata->dev, bssid); | ||
2631 | return 0; | ||
2632 | } | ||
2633 | 1946 | ||
2634 | mutex_unlock(&ifmgd->mtx); | 1947 | mutex_lock(&local->work_mtx); |
1948 | list_for_each_entry(wk, &local->work_list, list) { | ||
1949 | if (wk->type != IEEE80211_WORK_DIRECT_PROBE) | ||
1950 | continue; | ||
1951 | if (memcmp(req->bss->bssid, wk->filter_ta, ETH_ALEN)) | ||
1952 | continue; | ||
1953 | not_auth_yet = true; | ||
1954 | list_del(&wk->list); | ||
1955 | free_work(wk); | ||
1956 | break; | ||
1957 | } | ||
1958 | mutex_unlock(&local->work_mtx); | ||
1959 | |||
1960 | /* | ||
1961 | * If somebody requests authentication and we haven't | ||
1962 | * sent out an auth frame yet there's no need to send | ||
1963 | * out a deauth frame either. If the state was PROBE, | ||
1964 | * then this is the case. If it's AUTH we have sent a | ||
1965 | * frame, and if it's IDLE we have completed the auth | ||
1966 | * process already. | ||
1967 | */ | ||
1968 | if (not_auth_yet) { | ||
1969 | __cfg80211_auth_canceled(sdata->dev, bssid); | ||
1970 | return 0; | ||
1971 | } | ||
1972 | } | ||
2635 | 1973 | ||
2636 | printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n", | 1974 | printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n", |
2637 | sdata->name, bssid, req->reason_code); | 1975 | sdata->name, bssid, req->reason_code); |
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index f60dfca52196..bfcf09eb64b4 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c | |||
@@ -1945,6 +1945,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) | |||
1945 | { | 1945 | { |
1946 | struct ieee80211_sub_if_data *sdata = rx->sdata; | 1946 | struct ieee80211_sub_if_data *sdata = rx->sdata; |
1947 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data; | 1947 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data; |
1948 | ieee80211_rx_result rxs; | ||
1948 | 1949 | ||
1949 | if (!(rx->flags & IEEE80211_RX_RA_MATCH)) | 1950 | if (!(rx->flags & IEEE80211_RX_RA_MATCH)) |
1950 | return RX_DROP_MONITOR; | 1951 | return RX_DROP_MONITOR; |
@@ -1952,6 +1953,10 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) | |||
1952 | if (ieee80211_drop_unencrypted(rx, mgmt->frame_control)) | 1953 | if (ieee80211_drop_unencrypted(rx, mgmt->frame_control)) |
1953 | return RX_DROP_MONITOR; | 1954 | return RX_DROP_MONITOR; |
1954 | 1955 | ||
1956 | rxs = ieee80211_work_rx_mgmt(rx->sdata, rx->skb); | ||
1957 | if (rxs != RX_CONTINUE) | ||
1958 | return rxs; | ||
1959 | |||
1955 | if (ieee80211_vif_is_mesh(&sdata->vif)) | 1960 | if (ieee80211_vif_is_mesh(&sdata->vif)) |
1956 | return ieee80211_mesh_rx_mgmt(sdata, rx->skb); | 1961 | return ieee80211_mesh_rx_mgmt(sdata, rx->skb); |
1957 | 1962 | ||
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index d98c45e5528b..fb89e4c0fbfd 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -434,7 +434,6 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, | |||
434 | struct cfg80211_scan_request *req) | 434 | struct cfg80211_scan_request *req) |
435 | { | 435 | { |
436 | struct ieee80211_local *local = sdata->local; | 436 | struct ieee80211_local *local = sdata->local; |
437 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | ||
438 | int rc; | 437 | int rc; |
439 | 438 | ||
440 | if (local->scan_req) | 439 | if (local->scan_req) |
@@ -464,11 +463,8 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, | |||
464 | local->scan_req = req; | 463 | local->scan_req = req; |
465 | local->scan_sdata = sdata; | 464 | local->scan_sdata = sdata; |
466 | 465 | ||
467 | if (req != local->int_scan_req && | 466 | if (!list_empty(&local->work_list)) { |
468 | sdata->vif.type == NL80211_IFTYPE_STATION && | 467 | /* wait for the work to finish/time out */ |
469 | !list_empty(&ifmgd->work_list)) { | ||
470 | /* actually wait for the work it's doing to finish/time out */ | ||
471 | set_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request); | ||
472 | return 0; | 468 | return 0; |
473 | } | 469 | } |
474 | 470 | ||
diff --git a/net/mac80211/work.c b/net/mac80211/work.c new file mode 100644 index 000000000000..8b8961d806ab --- /dev/null +++ b/net/mac80211/work.c | |||
@@ -0,0 +1,902 @@ | |||
1 | /* | ||
2 | * mac80211 work implementation | ||
3 | * | ||
4 | * Copyright 2003-2008, Jouni Malinen <j@w1.fi> | ||
5 | * Copyright 2004, Instant802 Networks, Inc. | ||
6 | * Copyright 2005, Devicescape Software, Inc. | ||
7 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> | ||
8 | * Copyright 2007, Michael Wu <flamingice@sourmilk.net> | ||
9 | * Copyright 2009, Johannes Berg <johannes@sipsolutions.net> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | */ | ||
15 | |||
16 | #include <linux/delay.h> | ||
17 | #include <linux/if_ether.h> | ||
18 | #include <linux/skbuff.h> | ||
19 | #include <linux/if_arp.h> | ||
20 | #include <linux/etherdevice.h> | ||
21 | #include <linux/crc32.h> | ||
22 | #include <net/mac80211.h> | ||
23 | #include <asm/unaligned.h> | ||
24 | |||
25 | #include "ieee80211_i.h" | ||
26 | #include "rate.h" | ||
27 | |||
28 | #define IEEE80211_AUTH_TIMEOUT (HZ / 5) | ||
29 | #define IEEE80211_AUTH_MAX_TRIES 3 | ||
30 | #define IEEE80211_ASSOC_TIMEOUT (HZ / 5) | ||
31 | #define IEEE80211_ASSOC_MAX_TRIES 3 | ||
32 | #define IEEE80211_MAX_PROBE_TRIES 5 | ||
33 | |||
34 | enum work_action { | ||
35 | WORK_ACT_NONE, | ||
36 | WORK_ACT_TIMEOUT, | ||
37 | WORK_ACT_DONE, | ||
38 | }; | ||
39 | |||
40 | |||
41 | /* utils */ | ||
42 | static inline void ASSERT_WORK_MTX(struct ieee80211_local *local) | ||
43 | { | ||
44 | WARN_ON(!mutex_is_locked(&local->work_mtx)); | ||
45 | } | ||
46 | |||
47 | /* | ||
48 | * We can have multiple work items (and connection probing) | ||
49 | * scheduling this timer, but we need to take care to only | ||
50 | * reschedule it when it should fire _earlier_ than it was | ||
51 | * asked for before, or if it's not pending right now. This | ||
52 | * function ensures that. Note that it then is required to | ||
53 | * run this function for all timeouts after the first one | ||
54 | * has happened -- the work that runs from this timer will | ||
55 | * do that. | ||
56 | */ | ||
57 | static void run_again(struct ieee80211_local *local, | ||
58 | unsigned long timeout) | ||
59 | { | ||
60 | ASSERT_WORK_MTX(local); | ||
61 | |||
62 | if (!timer_pending(&local->work_timer) || | ||
63 | time_before(timeout, local->work_timer.expires)) | ||
64 | mod_timer(&local->work_timer, timeout); | ||
65 | } | ||
66 | |||
67 | static void work_free_rcu(struct rcu_head *head) | ||
68 | { | ||
69 | struct ieee80211_work *wk = | ||
70 | container_of(head, struct ieee80211_work, rcu_head); | ||
71 | |||
72 | kfree(wk); | ||
73 | } | ||
74 | |||
75 | void free_work(struct ieee80211_work *wk) | ||
76 | { | ||
77 | call_rcu(&wk->rcu_head, work_free_rcu); | ||
78 | } | ||
79 | |||
80 | static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len, | ||
81 | struct ieee80211_supported_band *sband, | ||
82 | u32 *rates) | ||
83 | { | ||
84 | int i, j, count; | ||
85 | *rates = 0; | ||
86 | count = 0; | ||
87 | for (i = 0; i < supp_rates_len; i++) { | ||
88 | int rate = (supp_rates[i] & 0x7F) * 5; | ||
89 | |||
90 | for (j = 0; j < sband->n_bitrates; j++) | ||
91 | if (sband->bitrates[j].bitrate == rate) { | ||
92 | *rates |= BIT(j); | ||
93 | count++; | ||
94 | break; | ||
95 | } | ||
96 | } | ||
97 | |||
98 | return count; | ||
99 | } | ||
100 | |||
101 | /* frame sending functions */ | ||
102 | |||
103 | static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, | ||
104 | struct ieee80211_work *wk) | ||
105 | { | ||
106 | struct ieee80211_local *local = sdata->local; | ||
107 | struct sk_buff *skb; | ||
108 | struct ieee80211_mgmt *mgmt; | ||
109 | u8 *pos; | ||
110 | const u8 *ies, *ht_ie; | ||
111 | int i, len, count, rates_len, supp_rates_len; | ||
112 | u16 capab; | ||
113 | struct ieee80211_supported_band *sband; | ||
114 | u32 rates = 0; | ||
115 | |||
116 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + | ||
117 | sizeof(*mgmt) + 200 + wk->ie_len + | ||
118 | wk->assoc.ssid_len); | ||
119 | if (!skb) { | ||
120 | printk(KERN_DEBUG "%s: failed to allocate buffer for assoc " | ||
121 | "frame\n", sdata->name); | ||
122 | return; | ||
123 | } | ||
124 | skb_reserve(skb, local->hw.extra_tx_headroom); | ||
125 | |||
126 | sband = local->hw.wiphy->bands[wk->chan->band]; | ||
127 | |||
128 | capab = WLAN_CAPABILITY_ESS; | ||
129 | |||
130 | if (sband->band == IEEE80211_BAND_2GHZ) { | ||
131 | if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) | ||
132 | capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; | ||
133 | if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)) | ||
134 | capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; | ||
135 | } | ||
136 | |||
137 | if (wk->assoc.capability & WLAN_CAPABILITY_PRIVACY) | ||
138 | capab |= WLAN_CAPABILITY_PRIVACY; | ||
139 | |||
140 | /* | ||
141 | * Get all rates supported by the device and the AP as | ||
142 | * some APs don't like getting a superset of their rates | ||
143 | * in the association request (e.g. D-Link DAP 1353 in | ||
144 | * b-only mode)... | ||
145 | */ | ||
146 | rates_len = ieee80211_compatible_rates(wk->assoc.supp_rates, | ||
147 | wk->assoc.supp_rates_len, | ||
148 | sband, &rates); | ||
149 | |||
150 | if ((wk->assoc.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) && | ||
151 | (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)) | ||
152 | capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; | ||
153 | |||
154 | mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); | ||
155 | memset(mgmt, 0, 24); | ||
156 | memcpy(mgmt->da, wk->filter_ta, ETH_ALEN); | ||
157 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); | ||
158 | memcpy(mgmt->bssid, wk->filter_ta, ETH_ALEN); | ||
159 | |||
160 | if (!is_zero_ether_addr(wk->assoc.prev_bssid)) { | ||
161 | skb_put(skb, 10); | ||
162 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | ||
163 | IEEE80211_STYPE_REASSOC_REQ); | ||
164 | mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab); | ||
165 | mgmt->u.reassoc_req.listen_interval = | ||
166 | cpu_to_le16(local->hw.conf.listen_interval); | ||
167 | memcpy(mgmt->u.reassoc_req.current_ap, wk->assoc.prev_bssid, | ||
168 | ETH_ALEN); | ||
169 | } else { | ||
170 | skb_put(skb, 4); | ||
171 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | ||
172 | IEEE80211_STYPE_ASSOC_REQ); | ||
173 | mgmt->u.assoc_req.capab_info = cpu_to_le16(capab); | ||
174 | mgmt->u.assoc_req.listen_interval = | ||
175 | cpu_to_le16(local->hw.conf.listen_interval); | ||
176 | } | ||
177 | |||
178 | /* SSID */ | ||
179 | ies = pos = skb_put(skb, 2 + wk->assoc.ssid_len); | ||
180 | *pos++ = WLAN_EID_SSID; | ||
181 | *pos++ = wk->assoc.ssid_len; | ||
182 | memcpy(pos, wk->assoc.ssid, wk->assoc.ssid_len); | ||
183 | |||
184 | /* add all rates which were marked to be used above */ | ||
185 | supp_rates_len = rates_len; | ||
186 | if (supp_rates_len > 8) | ||
187 | supp_rates_len = 8; | ||
188 | |||
189 | len = sband->n_bitrates; | ||
190 | pos = skb_put(skb, supp_rates_len + 2); | ||
191 | *pos++ = WLAN_EID_SUPP_RATES; | ||
192 | *pos++ = supp_rates_len; | ||
193 | |||
194 | count = 0; | ||
195 | for (i = 0; i < sband->n_bitrates; i++) { | ||
196 | if (BIT(i) & rates) { | ||
197 | int rate = sband->bitrates[i].bitrate; | ||
198 | *pos++ = (u8) (rate / 5); | ||
199 | if (++count == 8) | ||
200 | break; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | if (rates_len > count) { | ||
205 | pos = skb_put(skb, rates_len - count + 2); | ||
206 | *pos++ = WLAN_EID_EXT_SUPP_RATES; | ||
207 | *pos++ = rates_len - count; | ||
208 | |||
209 | for (i++; i < sband->n_bitrates; i++) { | ||
210 | if (BIT(i) & rates) { | ||
211 | int rate = sband->bitrates[i].bitrate; | ||
212 | *pos++ = (u8) (rate / 5); | ||
213 | } | ||
214 | } | ||
215 | } | ||
216 | |||
217 | if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) { | ||
218 | /* 1. power capabilities */ | ||
219 | pos = skb_put(skb, 4); | ||
220 | *pos++ = WLAN_EID_PWR_CAPABILITY; | ||
221 | *pos++ = 2; | ||
222 | *pos++ = 0; /* min tx power */ | ||
223 | *pos++ = local->hw.conf.channel->max_power; /* max tx power */ | ||
224 | |||
225 | /* 2. supported channels */ | ||
226 | /* TODO: get this in reg domain format */ | ||
227 | pos = skb_put(skb, 2 * sband->n_channels + 2); | ||
228 | *pos++ = WLAN_EID_SUPPORTED_CHANNELS; | ||
229 | *pos++ = 2 * sband->n_channels; | ||
230 | for (i = 0; i < sband->n_channels; i++) { | ||
231 | *pos++ = ieee80211_frequency_to_channel( | ||
232 | sband->channels[i].center_freq); | ||
233 | *pos++ = 1; /* one channel in the subband*/ | ||
234 | } | ||
235 | } | ||
236 | |||
237 | if (wk->ie_len && wk->ie) { | ||
238 | pos = skb_put(skb, wk->ie_len); | ||
239 | memcpy(pos, wk->ie, wk->ie_len); | ||
240 | } | ||
241 | |||
242 | if (wk->assoc.wmm_used && local->hw.queues >= 4) { | ||
243 | pos = skb_put(skb, 9); | ||
244 | *pos++ = WLAN_EID_VENDOR_SPECIFIC; | ||
245 | *pos++ = 7; /* len */ | ||
246 | *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ | ||
247 | *pos++ = 0x50; | ||
248 | *pos++ = 0xf2; | ||
249 | *pos++ = 2; /* WME */ | ||
250 | *pos++ = 0; /* WME info */ | ||
251 | *pos++ = 1; /* WME ver */ | ||
252 | *pos++ = 0; | ||
253 | } | ||
254 | |||
255 | /* wmm support is a must to HT */ | ||
256 | /* | ||
257 | * IEEE802.11n does not allow TKIP/WEP as pairwise | ||
258 | * ciphers in HT mode. We still associate in non-ht | ||
259 | * mode (11a/b/g) if any one of these ciphers is | ||
260 | * configured as pairwise. | ||
261 | */ | ||
262 | if (wk->assoc.use_11n && wk->assoc.wmm_used && | ||
263 | (local->hw.queues >= 4) && | ||
264 | sband->ht_cap.ht_supported && | ||
265 | (ht_ie = wk->assoc.ht_information_ie) && | ||
266 | ht_ie[1] >= sizeof(struct ieee80211_ht_info)) { | ||
267 | struct ieee80211_ht_info *ht_info = | ||
268 | (struct ieee80211_ht_info *)(ht_ie + 2); | ||
269 | u16 cap = sband->ht_cap.cap; | ||
270 | __le16 tmp; | ||
271 | u32 flags = local->hw.conf.channel->flags; | ||
272 | |||
273 | /* determine capability flags */ | ||
274 | |||
275 | if (ieee80211_disable_40mhz_24ghz && | ||
276 | sband->band == IEEE80211_BAND_2GHZ) { | ||
277 | cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; | ||
278 | cap &= ~IEEE80211_HT_CAP_SGI_40; | ||
279 | } | ||
280 | |||
281 | switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { | ||
282 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: | ||
283 | if (flags & IEEE80211_CHAN_NO_HT40PLUS) { | ||
284 | cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; | ||
285 | cap &= ~IEEE80211_HT_CAP_SGI_40; | ||
286 | } | ||
287 | break; | ||
288 | case IEEE80211_HT_PARAM_CHA_SEC_BELOW: | ||
289 | if (flags & IEEE80211_CHAN_NO_HT40MINUS) { | ||
290 | cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; | ||
291 | cap &= ~IEEE80211_HT_CAP_SGI_40; | ||
292 | } | ||
293 | break; | ||
294 | } | ||
295 | |||
296 | /* set SM PS mode properly */ | ||
297 | cap &= ~IEEE80211_HT_CAP_SM_PS; | ||
298 | switch (wk->assoc.smps) { | ||
299 | case IEEE80211_SMPS_AUTOMATIC: | ||
300 | case IEEE80211_SMPS_NUM_MODES: | ||
301 | WARN_ON(1); | ||
302 | case IEEE80211_SMPS_OFF: | ||
303 | cap |= WLAN_HT_CAP_SM_PS_DISABLED << | ||
304 | IEEE80211_HT_CAP_SM_PS_SHIFT; | ||
305 | break; | ||
306 | case IEEE80211_SMPS_STATIC: | ||
307 | cap |= WLAN_HT_CAP_SM_PS_STATIC << | ||
308 | IEEE80211_HT_CAP_SM_PS_SHIFT; | ||
309 | break; | ||
310 | case IEEE80211_SMPS_DYNAMIC: | ||
311 | cap |= WLAN_HT_CAP_SM_PS_DYNAMIC << | ||
312 | IEEE80211_HT_CAP_SM_PS_SHIFT; | ||
313 | break; | ||
314 | } | ||
315 | |||
316 | /* reserve and fill IE */ | ||
317 | |||
318 | pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); | ||
319 | *pos++ = WLAN_EID_HT_CAPABILITY; | ||
320 | *pos++ = sizeof(struct ieee80211_ht_cap); | ||
321 | memset(pos, 0, sizeof(struct ieee80211_ht_cap)); | ||
322 | |||
323 | /* capability flags */ | ||
324 | tmp = cpu_to_le16(cap); | ||
325 | memcpy(pos, &tmp, sizeof(u16)); | ||
326 | pos += sizeof(u16); | ||
327 | |||
328 | /* AMPDU parameters */ | ||
329 | *pos++ = sband->ht_cap.ampdu_factor | | ||
330 | (sband->ht_cap.ampdu_density << | ||
331 | IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); | ||
332 | |||
333 | /* MCS set */ | ||
334 | memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); | ||
335 | pos += sizeof(sband->ht_cap.mcs); | ||
336 | |||
337 | /* extended capabilities */ | ||
338 | pos += sizeof(__le16); | ||
339 | |||
340 | /* BF capabilities */ | ||
341 | pos += sizeof(__le32); | ||
342 | |||
343 | /* antenna selection */ | ||
344 | pos += sizeof(u8); | ||
345 | } | ||
346 | |||
347 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; | ||
348 | ieee80211_tx_skb(sdata, skb); | ||
349 | } | ||
350 | |||
351 | static void ieee80211_remove_auth_bss(struct ieee80211_local *local, | ||
352 | struct ieee80211_work *wk) | ||
353 | { | ||
354 | struct cfg80211_bss *cbss; | ||
355 | u16 capa_val = WLAN_CAPABILITY_ESS; | ||
356 | |||
357 | if (wk->probe_auth.privacy) | ||
358 | capa_val |= WLAN_CAPABILITY_PRIVACY; | ||
359 | |||
360 | cbss = cfg80211_get_bss(local->hw.wiphy, wk->chan, wk->filter_ta, | ||
361 | wk->probe_auth.ssid, wk->probe_auth.ssid_len, | ||
362 | WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, | ||
363 | capa_val); | ||
364 | if (!cbss) | ||
365 | return; | ||
366 | |||
367 | cfg80211_unlink_bss(local->hw.wiphy, cbss); | ||
368 | cfg80211_put_bss(cbss); | ||
369 | } | ||
370 | |||
371 | static enum work_action __must_check | ||
372 | ieee80211_direct_probe(struct ieee80211_work *wk) | ||
373 | { | ||
374 | struct ieee80211_sub_if_data *sdata = wk->sdata; | ||
375 | struct ieee80211_local *local = sdata->local; | ||
376 | |||
377 | wk->probe_auth.tries++; | ||
378 | if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) { | ||
379 | printk(KERN_DEBUG "%s: direct probe to AP %pM timed out\n", | ||
380 | sdata->name, wk->filter_ta); | ||
381 | |||
382 | /* | ||
383 | * Most likely AP is not in the range so remove the | ||
384 | * bss struct for that AP. | ||
385 | */ | ||
386 | ieee80211_remove_auth_bss(local, wk); | ||
387 | |||
388 | /* | ||
389 | * We might have a pending scan which had no chance to run yet | ||
390 | * due to work needing to be done. Hence, queue the STAs work | ||
391 | * again for that. | ||
392 | */ | ||
393 | ieee80211_queue_work(&local->hw, &local->work_work); | ||
394 | return WORK_ACT_TIMEOUT; | ||
395 | } | ||
396 | |||
397 | printk(KERN_DEBUG "%s: direct probe to AP %pM (try %d)\n", | ||
398 | sdata->name, wk->filter_ta, wk->probe_auth.tries); | ||
399 | |||
400 | /* | ||
401 | * Direct probe is sent to broadcast address as some APs | ||
402 | * will not answer to direct packet in unassociated state. | ||
403 | */ | ||
404 | ieee80211_send_probe_req(sdata, NULL, wk->probe_auth.ssid, | ||
405 | wk->probe_auth.ssid_len, NULL, 0); | ||
406 | |||
407 | wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; | ||
408 | run_again(local, wk->timeout); | ||
409 | |||
410 | return WORK_ACT_NONE; | ||
411 | } | ||
412 | |||
413 | |||
414 | static enum work_action __must_check | ||
415 | ieee80211_authenticate(struct ieee80211_work *wk) | ||
416 | { | ||
417 | struct ieee80211_sub_if_data *sdata = wk->sdata; | ||
418 | struct ieee80211_local *local = sdata->local; | ||
419 | |||
420 | wk->probe_auth.tries++; | ||
421 | if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) { | ||
422 | printk(KERN_DEBUG "%s: authentication with AP %pM" | ||
423 | " timed out\n", sdata->name, wk->filter_ta); | ||
424 | |||
425 | /* | ||
426 | * Most likely AP is not in the range so remove the | ||
427 | * bss struct for that AP. | ||
428 | */ | ||
429 | ieee80211_remove_auth_bss(local, wk); | ||
430 | |||
431 | /* | ||
432 | * We might have a pending scan which had no chance to run yet | ||
433 | * due to work needing to be done. Hence, queue the STAs work | ||
434 | * again for that. | ||
435 | */ | ||
436 | ieee80211_queue_work(&local->hw, &local->work_work); | ||
437 | return WORK_ACT_TIMEOUT; | ||
438 | } | ||
439 | |||
440 | printk(KERN_DEBUG "%s: authenticate with AP %pM (try %d)\n", | ||
441 | sdata->name, wk->filter_ta, wk->probe_auth.tries); | ||
442 | |||
443 | ieee80211_send_auth(sdata, 1, wk->probe_auth.algorithm, wk->ie, | ||
444 | wk->ie_len, wk->filter_ta, NULL, 0, 0); | ||
445 | wk->probe_auth.transaction = 2; | ||
446 | |||
447 | wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; | ||
448 | run_again(local, wk->timeout); | ||
449 | |||
450 | return WORK_ACT_NONE; | ||
451 | } | ||
452 | |||
453 | static enum work_action __must_check | ||
454 | ieee80211_associate(struct ieee80211_work *wk) | ||
455 | { | ||
456 | struct ieee80211_sub_if_data *sdata = wk->sdata; | ||
457 | struct ieee80211_local *local = sdata->local; | ||
458 | |||
459 | wk->assoc.tries++; | ||
460 | if (wk->assoc.tries > IEEE80211_ASSOC_MAX_TRIES) { | ||
461 | printk(KERN_DEBUG "%s: association with AP %pM" | ||
462 | " timed out\n", | ||
463 | sdata->name, wk->filter_ta); | ||
464 | |||
465 | /* | ||
466 | * Most likely AP is not in the range so remove the | ||
467 | * bss struct for that AP. | ||
468 | */ | ||
469 | if (wk->assoc.bss) | ||
470 | cfg80211_unlink_bss(local->hw.wiphy, | ||
471 | &wk->assoc.bss->cbss); | ||
472 | |||
473 | /* | ||
474 | * We might have a pending scan which had no chance to run yet | ||
475 | * due to work needing to be done. Hence, queue the STAs work | ||
476 | * again for that. | ||
477 | */ | ||
478 | ieee80211_queue_work(&local->hw, &local->work_work); | ||
479 | return WORK_ACT_TIMEOUT; | ||
480 | } | ||
481 | |||
482 | printk(KERN_DEBUG "%s: associate with AP %pM (try %d)\n", | ||
483 | sdata->name, wk->filter_ta, wk->assoc.tries); | ||
484 | ieee80211_send_assoc(sdata, wk); | ||
485 | |||
486 | wk->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT; | ||
487 | run_again(local, wk->timeout); | ||
488 | |||
489 | return WORK_ACT_NONE; | ||
490 | } | ||
491 | |||
492 | static void ieee80211_auth_challenge(struct ieee80211_work *wk, | ||
493 | struct ieee80211_mgmt *mgmt, | ||
494 | size_t len) | ||
495 | { | ||
496 | struct ieee80211_sub_if_data *sdata = wk->sdata; | ||
497 | u8 *pos; | ||
498 | struct ieee802_11_elems elems; | ||
499 | |||
500 | pos = mgmt->u.auth.variable; | ||
501 | ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); | ||
502 | if (!elems.challenge) | ||
503 | return; | ||
504 | ieee80211_send_auth(sdata, 3, wk->probe_auth.algorithm, | ||
505 | elems.challenge - 2, elems.challenge_len + 2, | ||
506 | wk->filter_ta, wk->probe_auth.key, | ||
507 | wk->probe_auth.key_len, wk->probe_auth.key_idx); | ||
508 | wk->probe_auth.transaction = 4; | ||
509 | } | ||
510 | |||
511 | static enum work_action __must_check | ||
512 | ieee80211_rx_mgmt_auth(struct ieee80211_work *wk, | ||
513 | struct ieee80211_mgmt *mgmt, size_t len) | ||
514 | { | ||
515 | u16 auth_alg, auth_transaction, status_code; | ||
516 | |||
517 | if (wk->type != IEEE80211_WORK_AUTH) | ||
518 | return WORK_ACT_NONE; | ||
519 | |||
520 | if (len < 24 + 6) | ||
521 | return WORK_ACT_NONE; | ||
522 | |||
523 | auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); | ||
524 | auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); | ||
525 | status_code = le16_to_cpu(mgmt->u.auth.status_code); | ||
526 | |||
527 | if (auth_alg != wk->probe_auth.algorithm || | ||
528 | auth_transaction != wk->probe_auth.transaction) | ||
529 | return WORK_ACT_NONE; | ||
530 | |||
531 | if (status_code != WLAN_STATUS_SUCCESS) { | ||
532 | printk(KERN_DEBUG "%s: %pM denied authentication (status %d)\n", | ||
533 | wk->sdata->name, mgmt->sa, status_code); | ||
534 | return WORK_ACT_DONE; | ||
535 | } | ||
536 | |||
537 | switch (wk->probe_auth.algorithm) { | ||
538 | case WLAN_AUTH_OPEN: | ||
539 | case WLAN_AUTH_LEAP: | ||
540 | case WLAN_AUTH_FT: | ||
541 | break; | ||
542 | case WLAN_AUTH_SHARED_KEY: | ||
543 | if (wk->probe_auth.transaction != 4) { | ||
544 | ieee80211_auth_challenge(wk, mgmt, len); | ||
545 | /* need another frame */ | ||
546 | return WORK_ACT_NONE; | ||
547 | } | ||
548 | break; | ||
549 | default: | ||
550 | WARN_ON(1); | ||
551 | return WORK_ACT_NONE; | ||
552 | } | ||
553 | |||
554 | printk(KERN_DEBUG "%s: authenticated\n", wk->sdata->name); | ||
555 | return WORK_ACT_DONE; | ||
556 | } | ||
557 | |||
558 | static enum work_action __must_check | ||
559 | ieee80211_rx_mgmt_assoc_resp(struct ieee80211_work *wk, | ||
560 | struct ieee80211_mgmt *mgmt, size_t len, | ||
561 | bool reassoc) | ||
562 | { | ||
563 | struct ieee80211_sub_if_data *sdata = wk->sdata; | ||
564 | struct ieee80211_local *local = sdata->local; | ||
565 | u16 capab_info, status_code, aid; | ||
566 | struct ieee802_11_elems elems; | ||
567 | u8 *pos; | ||
568 | |||
569 | /* | ||
570 | * AssocResp and ReassocResp have identical structure, so process both | ||
571 | * of them in this function. | ||
572 | */ | ||
573 | |||
574 | if (len < 24 + 6) | ||
575 | return WORK_ACT_NONE; | ||
576 | |||
577 | capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); | ||
578 | status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); | ||
579 | aid = le16_to_cpu(mgmt->u.assoc_resp.aid); | ||
580 | |||
581 | printk(KERN_DEBUG "%s: RX %sssocResp from %pM (capab=0x%x " | ||
582 | "status=%d aid=%d)\n", | ||
583 | sdata->name, reassoc ? "Rea" : "A", mgmt->sa, | ||
584 | capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14)))); | ||
585 | |||
586 | pos = mgmt->u.assoc_resp.variable; | ||
587 | ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); | ||
588 | |||
589 | if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && | ||
590 | elems.timeout_int && elems.timeout_int_len == 5 && | ||
591 | elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) { | ||
592 | u32 tu, ms; | ||
593 | tu = get_unaligned_le32(elems.timeout_int + 1); | ||
594 | ms = tu * 1024 / 1000; | ||
595 | printk(KERN_DEBUG "%s: AP rejected association temporarily; " | ||
596 | "comeback duration %u TU (%u ms)\n", | ||
597 | sdata->name, tu, ms); | ||
598 | wk->timeout = jiffies + msecs_to_jiffies(ms); | ||
599 | if (ms > IEEE80211_ASSOC_TIMEOUT) | ||
600 | run_again(local, wk->timeout); | ||
601 | return WORK_ACT_NONE; | ||
602 | } | ||
603 | |||
604 | if (status_code != WLAN_STATUS_SUCCESS) | ||
605 | printk(KERN_DEBUG "%s: AP denied association (code=%d)\n", | ||
606 | sdata->name, status_code); | ||
607 | else | ||
608 | printk(KERN_DEBUG "%s: associated\n", sdata->name); | ||
609 | |||
610 | return WORK_ACT_DONE; | ||
611 | } | ||
612 | |||
613 | static enum work_action __must_check | ||
614 | ieee80211_rx_mgmt_probe_resp(struct ieee80211_work *wk, | ||
615 | struct ieee80211_mgmt *mgmt, size_t len, | ||
616 | struct ieee80211_rx_status *rx_status) | ||
617 | { | ||
618 | struct ieee80211_sub_if_data *sdata = wk->sdata; | ||
619 | struct ieee80211_local *local = sdata->local; | ||
620 | size_t baselen; | ||
621 | |||
622 | ASSERT_WORK_MTX(local); | ||
623 | |||
624 | baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt; | ||
625 | if (baselen > len) | ||
626 | return WORK_ACT_NONE; | ||
627 | |||
628 | printk(KERN_DEBUG "%s: direct probe responded\n", sdata->name); | ||
629 | return WORK_ACT_DONE; | ||
630 | } | ||
631 | |||
632 | static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local, | ||
633 | struct sk_buff *skb) | ||
634 | { | ||
635 | struct ieee80211_rx_status *rx_status; | ||
636 | struct ieee80211_mgmt *mgmt; | ||
637 | struct ieee80211_work *wk; | ||
638 | enum work_action rma = WORK_ACT_NONE; | ||
639 | u16 fc; | ||
640 | |||
641 | rx_status = (struct ieee80211_rx_status *) skb->cb; | ||
642 | mgmt = (struct ieee80211_mgmt *) skb->data; | ||
643 | fc = le16_to_cpu(mgmt->frame_control); | ||
644 | |||
645 | mutex_lock(&local->work_mtx); | ||
646 | |||
647 | list_for_each_entry(wk, &local->work_list, list) { | ||
648 | const u8 *bssid = NULL; | ||
649 | |||
650 | switch (wk->type) { | ||
651 | case IEEE80211_WORK_DIRECT_PROBE: | ||
652 | case IEEE80211_WORK_AUTH: | ||
653 | case IEEE80211_WORK_ASSOC: | ||
654 | bssid = wk->filter_ta; | ||
655 | break; | ||
656 | default: | ||
657 | continue; | ||
658 | } | ||
659 | |||
660 | /* | ||
661 | * Before queuing, we already verified mgmt->sa, | ||
662 | * so this is needed just for matching. | ||
663 | */ | ||
664 | if (compare_ether_addr(bssid, mgmt->bssid)) | ||
665 | continue; | ||
666 | |||
667 | switch (fc & IEEE80211_FCTL_STYPE) { | ||
668 | case IEEE80211_STYPE_PROBE_RESP: | ||
669 | rma = ieee80211_rx_mgmt_probe_resp(wk, mgmt, skb->len, | ||
670 | rx_status); | ||
671 | break; | ||
672 | case IEEE80211_STYPE_AUTH: | ||
673 | rma = ieee80211_rx_mgmt_auth(wk, mgmt, skb->len); | ||
674 | break; | ||
675 | case IEEE80211_STYPE_ASSOC_RESP: | ||
676 | rma = ieee80211_rx_mgmt_assoc_resp(wk, mgmt, | ||
677 | skb->len, false); | ||
678 | break; | ||
679 | case IEEE80211_STYPE_REASSOC_RESP: | ||
680 | rma = ieee80211_rx_mgmt_assoc_resp(wk, mgmt, | ||
681 | skb->len, true); | ||
682 | break; | ||
683 | default: | ||
684 | WARN_ON(1); | ||
685 | } | ||
686 | /* | ||
687 | * We've processed this frame for that work, so it can't | ||
688 | * belong to another work struct. | ||
689 | * NB: this is also required for correctness for 'rma'! | ||
690 | */ | ||
691 | break; | ||
692 | } | ||
693 | |||
694 | switch (rma) { | ||
695 | case WORK_ACT_NONE: | ||
696 | break; | ||
697 | case WORK_ACT_DONE: | ||
698 | list_del_rcu(&wk->list); | ||
699 | break; | ||
700 | default: | ||
701 | WARN(1, "unexpected: %d", rma); | ||
702 | } | ||
703 | |||
704 | mutex_unlock(&local->work_mtx); | ||
705 | |||
706 | if (rma != WORK_ACT_DONE) | ||
707 | goto out; | ||
708 | |||
709 | switch (wk->done(wk, skb)) { | ||
710 | case WORK_DONE_DESTROY: | ||
711 | free_work(wk); | ||
712 | break; | ||
713 | case WORK_DONE_REQUEUE: | ||
714 | synchronize_rcu(); | ||
715 | wk->timeout = jiffies; /* run again directly */ | ||
716 | mutex_lock(&local->work_mtx); | ||
717 | list_add_tail(&wk->list, &local->work_list); | ||
718 | mutex_unlock(&local->work_mtx); | ||
719 | } | ||
720 | |||
721 | out: | ||
722 | kfree_skb(skb); | ||
723 | } | ||
724 | |||
725 | static void ieee80211_work_timer(unsigned long data) | ||
726 | { | ||
727 | struct ieee80211_local *local = (void *) data; | ||
728 | |||
729 | if (local->quiescing) | ||
730 | return; | ||
731 | |||
732 | ieee80211_queue_work(&local->hw, &local->work_work); | ||
733 | } | ||
734 | |||
735 | static void ieee80211_work_work(struct work_struct *work) | ||
736 | { | ||
737 | struct ieee80211_local *local = | ||
738 | container_of(work, struct ieee80211_local, work_work); | ||
739 | struct sk_buff *skb; | ||
740 | struct ieee80211_work *wk, *tmp; | ||
741 | LIST_HEAD(free_work); | ||
742 | enum work_action rma; | ||
743 | |||
744 | if (local->scanning) | ||
745 | return; | ||
746 | |||
747 | /* | ||
748 | * ieee80211_queue_work() should have picked up most cases, | ||
749 | * here we'll pick the the rest. | ||
750 | */ | ||
751 | if (WARN(local->suspended, "work scheduled while going to suspend\n")) | ||
752 | return; | ||
753 | |||
754 | /* first process frames to avoid timing out while a frame is pending */ | ||
755 | while ((skb = skb_dequeue(&local->work_skb_queue))) | ||
756 | ieee80211_work_rx_queued_mgmt(local, skb); | ||
757 | |||
758 | ieee80211_recalc_idle(local); | ||
759 | |||
760 | mutex_lock(&local->work_mtx); | ||
761 | |||
762 | list_for_each_entry_safe(wk, tmp, &local->work_list, list) { | ||
763 | if (time_is_after_jiffies(wk->timeout)) { | ||
764 | /* | ||
765 | * This work item isn't supposed to be worked on | ||
766 | * right now, but take care to adjust the timer | ||
767 | * properly. | ||
768 | */ | ||
769 | run_again(local, wk->timeout); | ||
770 | continue; | ||
771 | } | ||
772 | |||
773 | switch (wk->type) { | ||
774 | default: | ||
775 | WARN_ON(1); | ||
776 | /* nothing */ | ||
777 | rma = WORK_ACT_NONE; | ||
778 | break; | ||
779 | case IEEE80211_WORK_DIRECT_PROBE: | ||
780 | rma = ieee80211_direct_probe(wk); | ||
781 | break; | ||
782 | case IEEE80211_WORK_AUTH: | ||
783 | rma = ieee80211_authenticate(wk); | ||
784 | break; | ||
785 | case IEEE80211_WORK_ASSOC: | ||
786 | rma = ieee80211_associate(wk); | ||
787 | break; | ||
788 | } | ||
789 | |||
790 | switch (rma) { | ||
791 | case WORK_ACT_NONE: | ||
792 | /* no action required */ | ||
793 | break; | ||
794 | case WORK_ACT_TIMEOUT: | ||
795 | list_del_rcu(&wk->list); | ||
796 | synchronize_rcu(); | ||
797 | list_add(&wk->list, &free_work); | ||
798 | break; | ||
799 | default: | ||
800 | WARN(1, "unexpected: %d", rma); | ||
801 | } | ||
802 | } | ||
803 | |||
804 | if (list_empty(&local->work_list) && local->scan_req) | ||
805 | ieee80211_queue_delayed_work(&local->hw, | ||
806 | &local->scan_work, | ||
807 | round_jiffies_relative(0)); | ||
808 | |||
809 | mutex_unlock(&local->work_mtx); | ||
810 | |||
811 | list_for_each_entry_safe(wk, tmp, &free_work, list) { | ||
812 | wk->done(wk, NULL); | ||
813 | list_del(&wk->list); | ||
814 | kfree(wk); | ||
815 | } | ||
816 | } | ||
817 | |||
818 | void ieee80211_add_work(struct ieee80211_work *wk) | ||
819 | { | ||
820 | struct ieee80211_local *local; | ||
821 | |||
822 | if (WARN_ON(!wk->chan)) | ||
823 | return; | ||
824 | |||
825 | if (WARN_ON(!wk->sdata)) | ||
826 | return; | ||
827 | |||
828 | if (WARN_ON(!wk->done)) | ||
829 | return; | ||
830 | |||
831 | wk->timeout = jiffies; | ||
832 | |||
833 | local = wk->sdata->local; | ||
834 | mutex_lock(&local->work_mtx); | ||
835 | list_add_tail(&wk->list, &local->work_list); | ||
836 | mutex_unlock(&local->work_mtx); | ||
837 | |||
838 | ieee80211_queue_work(&local->hw, &local->work_work); | ||
839 | } | ||
840 | |||
841 | void ieee80211_work_init(struct ieee80211_local *local) | ||
842 | { | ||
843 | mutex_init(&local->work_mtx); | ||
844 | INIT_LIST_HEAD(&local->work_list); | ||
845 | setup_timer(&local->work_timer, ieee80211_work_timer, | ||
846 | (unsigned long)local); | ||
847 | INIT_WORK(&local->work_work, ieee80211_work_work); | ||
848 | skb_queue_head_init(&local->work_skb_queue); | ||
849 | } | ||
850 | |||
851 | void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata) | ||
852 | { | ||
853 | struct ieee80211_local *local = sdata->local; | ||
854 | struct ieee80211_work *wk, *tmp; | ||
855 | |||
856 | mutex_lock(&local->work_mtx); | ||
857 | list_for_each_entry_safe(wk, tmp, &local->work_list, list) { | ||
858 | if (wk->sdata != sdata) | ||
859 | continue; | ||
860 | list_del(&wk->list); | ||
861 | free_work(wk); | ||
862 | } | ||
863 | mutex_unlock(&local->work_mtx); | ||
864 | } | ||
865 | |||
866 | ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata, | ||
867 | struct sk_buff *skb) | ||
868 | { | ||
869 | struct ieee80211_local *local = sdata->local; | ||
870 | struct ieee80211_mgmt *mgmt; | ||
871 | struct ieee80211_work *wk; | ||
872 | u16 fc; | ||
873 | |||
874 | if (skb->len < 24) | ||
875 | return RX_DROP_MONITOR; | ||
876 | |||
877 | mgmt = (struct ieee80211_mgmt *) skb->data; | ||
878 | fc = le16_to_cpu(mgmt->frame_control); | ||
879 | |||
880 | list_for_each_entry_rcu(wk, &local->work_list, list) { | ||
881 | if (sdata != wk->sdata) | ||
882 | continue; | ||
883 | if (compare_ether_addr(wk->filter_ta, mgmt->sa)) | ||
884 | continue; | ||
885 | if (compare_ether_addr(wk->filter_ta, mgmt->bssid)) | ||
886 | continue; | ||
887 | |||
888 | switch (fc & IEEE80211_FCTL_STYPE) { | ||
889 | case IEEE80211_STYPE_AUTH: | ||
890 | case IEEE80211_STYPE_PROBE_RESP: | ||
891 | case IEEE80211_STYPE_ASSOC_RESP: | ||
892 | case IEEE80211_STYPE_REASSOC_RESP: | ||
893 | case IEEE80211_STYPE_DEAUTH: | ||
894 | case IEEE80211_STYPE_DISASSOC: | ||
895 | skb_queue_tail(&local->work_skb_queue, skb); | ||
896 | ieee80211_queue_work(&local->hw, &local->work_work); | ||
897 | return RX_QUEUED; | ||
898 | } | ||
899 | } | ||
900 | |||
901 | return RX_CONTINUE; | ||
902 | } | ||