diff options
Diffstat (limited to 'net/mac80211/scan.c')
-rw-r--r-- | net/mac80211/scan.c | 230 |
1 files changed, 58 insertions, 172 deletions
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index f934c9620b73..9afe2f9885dc 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -12,7 +12,6 @@ | |||
12 | * published by the Free Software Foundation. | 12 | * published by the Free Software Foundation. |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/wireless.h> | ||
16 | #include <linux/if_arp.h> | 15 | #include <linux/if_arp.h> |
17 | #include <linux/rtnetlink.h> | 16 | #include <linux/rtnetlink.h> |
18 | #include <net/mac80211.h> | 17 | #include <net/mac80211.h> |
@@ -29,16 +28,19 @@ struct ieee80211_bss * | |||
29 | ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq, | 28 | ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq, |
30 | u8 *ssid, u8 ssid_len) | 29 | u8 *ssid, u8 ssid_len) |
31 | { | 30 | { |
32 | return (void *)cfg80211_get_bss(local->hw.wiphy, | 31 | struct cfg80211_bss *cbss; |
33 | ieee80211_get_channel(local->hw.wiphy, | 32 | |
34 | freq), | 33 | cbss = cfg80211_get_bss(local->hw.wiphy, |
35 | bssid, ssid, ssid_len, | 34 | ieee80211_get_channel(local->hw.wiphy, freq), |
36 | 0, 0); | 35 | bssid, ssid, ssid_len, 0, 0); |
36 | if (!cbss) | ||
37 | return NULL; | ||
38 | return (void *)cbss->priv; | ||
37 | } | 39 | } |
38 | 40 | ||
39 | static void ieee80211_rx_bss_free(struct cfg80211_bss *cbss) | 41 | static void ieee80211_rx_bss_free(struct cfg80211_bss *cbss) |
40 | { | 42 | { |
41 | struct ieee80211_bss *bss = (void *)cbss; | 43 | struct ieee80211_bss *bss = (void *)cbss->priv; |
42 | 44 | ||
43 | kfree(bss_mesh_id(bss)); | 45 | kfree(bss_mesh_id(bss)); |
44 | kfree(bss_mesh_cfg(bss)); | 46 | kfree(bss_mesh_cfg(bss)); |
@@ -47,7 +49,26 @@ static void ieee80211_rx_bss_free(struct cfg80211_bss *cbss) | |||
47 | void ieee80211_rx_bss_put(struct ieee80211_local *local, | 49 | void ieee80211_rx_bss_put(struct ieee80211_local *local, |
48 | struct ieee80211_bss *bss) | 50 | struct ieee80211_bss *bss) |
49 | { | 51 | { |
50 | cfg80211_put_bss((struct cfg80211_bss *)bss); | 52 | if (!bss) |
53 | return; | ||
54 | cfg80211_put_bss(container_of((void *)bss, struct cfg80211_bss, priv)); | ||
55 | } | ||
56 | |||
57 | static bool is_uapsd_supported(struct ieee802_11_elems *elems) | ||
58 | { | ||
59 | u8 qos_info; | ||
60 | |||
61 | if (elems->wmm_info && elems->wmm_info_len == 7 | ||
62 | && elems->wmm_info[5] == 1) | ||
63 | qos_info = elems->wmm_info[6]; | ||
64 | else if (elems->wmm_param && elems->wmm_param_len == 24 | ||
65 | && elems->wmm_param[5] == 1) | ||
66 | qos_info = elems->wmm_param[6]; | ||
67 | else | ||
68 | /* no valid wmm information or parameter element found */ | ||
69 | return false; | ||
70 | |||
71 | return qos_info & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD; | ||
51 | } | 72 | } |
52 | 73 | ||
53 | struct ieee80211_bss * | 74 | struct ieee80211_bss * |
@@ -59,6 +80,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local, | |||
59 | struct ieee80211_channel *channel, | 80 | struct ieee80211_channel *channel, |
60 | bool beacon) | 81 | bool beacon) |
61 | { | 82 | { |
83 | struct cfg80211_bss *cbss; | ||
62 | struct ieee80211_bss *bss; | 84 | struct ieee80211_bss *bss; |
63 | int clen; | 85 | int clen; |
64 | s32 signal = 0; | 86 | s32 signal = 0; |
@@ -68,13 +90,14 @@ ieee80211_bss_info_update(struct ieee80211_local *local, | |||
68 | else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) | 90 | else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) |
69 | signal = (rx_status->signal * 100) / local->hw.max_signal; | 91 | signal = (rx_status->signal * 100) / local->hw.max_signal; |
70 | 92 | ||
71 | bss = (void *)cfg80211_inform_bss_frame(local->hw.wiphy, channel, | 93 | cbss = cfg80211_inform_bss_frame(local->hw.wiphy, channel, |
72 | mgmt, len, signal, GFP_ATOMIC); | 94 | mgmt, len, signal, GFP_ATOMIC); |
73 | 95 | ||
74 | if (!bss) | 96 | if (!cbss) |
75 | return NULL; | 97 | return NULL; |
76 | 98 | ||
77 | bss->cbss.free_priv = ieee80211_rx_bss_free; | 99 | cbss->free_priv = ieee80211_rx_bss_free; |
100 | bss = (void *)cbss->priv; | ||
78 | 101 | ||
79 | /* save the ERP value so that it is available at association time */ | 102 | /* save the ERP value so that it is available at association time */ |
80 | if (elems->erp_info && elems->erp_info_len >= 1) { | 103 | if (elems->erp_info && elems->erp_info_len >= 1) { |
@@ -111,6 +134,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local, | |||
111 | } | 134 | } |
112 | 135 | ||
113 | bss->wmm_used = elems->wmm_param || elems->wmm_info; | 136 | bss->wmm_used = elems->wmm_param || elems->wmm_info; |
137 | bss->uapsd_supported = is_uapsd_supported(elems); | ||
114 | 138 | ||
115 | if (!beacon) | 139 | if (!beacon) |
116 | bss->last_probe_resp = jiffies; | 140 | bss->last_probe_resp = jiffies; |
@@ -147,7 +171,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) | |||
147 | presp = ieee80211_is_probe_resp(fc); | 171 | presp = ieee80211_is_probe_resp(fc); |
148 | if (presp) { | 172 | if (presp) { |
149 | /* ignore ProbeResp to foreign address */ | 173 | /* ignore ProbeResp to foreign address */ |
150 | if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN)) | 174 | if (memcmp(mgmt->da, sdata->vif.addr, ETH_ALEN)) |
151 | return RX_DROP_MONITOR; | 175 | return RX_DROP_MONITOR; |
152 | 176 | ||
153 | presp = true; | 177 | presp = true; |
@@ -220,82 +244,9 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) | |||
220 | return true; | 244 | return true; |
221 | } | 245 | } |
222 | 246 | ||
223 | /* | ||
224 | * inform AP that we will go to sleep so that it will buffer the frames | ||
225 | * while we scan | ||
226 | */ | ||
227 | static void ieee80211_scan_ps_enable(struct ieee80211_sub_if_data *sdata) | ||
228 | { | ||
229 | struct ieee80211_local *local = sdata->local; | ||
230 | |||
231 | local->scan_ps_enabled = false; | ||
232 | |||
233 | /* FIXME: what to do when local->pspolling is true? */ | ||
234 | |||
235 | del_timer_sync(&local->dynamic_ps_timer); | ||
236 | cancel_work_sync(&local->dynamic_ps_enable_work); | ||
237 | |||
238 | if (local->hw.conf.flags & IEEE80211_CONF_PS) { | ||
239 | local->scan_ps_enabled = true; | ||
240 | local->hw.conf.flags &= ~IEEE80211_CONF_PS; | ||
241 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); | ||
242 | } | ||
243 | |||
244 | if (!(local->scan_ps_enabled) || | ||
245 | !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) | ||
246 | /* | ||
247 | * If power save was enabled, no need to send a nullfunc | ||
248 | * frame because AP knows that we are sleeping. But if the | ||
249 | * hardware is creating the nullfunc frame for power save | ||
250 | * status (ie. IEEE80211_HW_PS_NULLFUNC_STACK is not | ||
251 | * enabled) and power save was enabled, the firmware just | ||
252 | * sent a null frame with power save disabled. So we need | ||
253 | * to send a new nullfunc frame to inform the AP that we | ||
254 | * are again sleeping. | ||
255 | */ | ||
256 | ieee80211_send_nullfunc(local, sdata, 1); | ||
257 | } | ||
258 | |||
259 | /* inform AP that we are awake again, unless power save is enabled */ | ||
260 | static void ieee80211_scan_ps_disable(struct ieee80211_sub_if_data *sdata) | ||
261 | { | ||
262 | struct ieee80211_local *local = sdata->local; | ||
263 | |||
264 | if (!local->ps_sdata) | ||
265 | ieee80211_send_nullfunc(local, sdata, 0); | ||
266 | else if (local->scan_ps_enabled) { | ||
267 | /* | ||
268 | * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware | ||
269 | * will send a nullfunc frame with the powersave bit set | ||
270 | * even though the AP already knows that we are sleeping. | ||
271 | * This could be avoided by sending a null frame with power | ||
272 | * save bit disabled before enabling the power save, but | ||
273 | * this doesn't gain anything. | ||
274 | * | ||
275 | * When IEEE80211_HW_PS_NULLFUNC_STACK is enabled, no need | ||
276 | * to send a nullfunc frame because AP already knows that | ||
277 | * we are sleeping, let's just enable power save mode in | ||
278 | * hardware. | ||
279 | */ | ||
280 | local->hw.conf.flags |= IEEE80211_CONF_PS; | ||
281 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); | ||
282 | } else if (local->hw.conf.dynamic_ps_timeout > 0) { | ||
283 | /* | ||
284 | * If IEEE80211_CONF_PS was not set and the dynamic_ps_timer | ||
285 | * had been running before leaving the operating channel, | ||
286 | * restart the timer now and send a nullfunc frame to inform | ||
287 | * the AP that we are awake. | ||
288 | */ | ||
289 | ieee80211_send_nullfunc(local, sdata, 0); | ||
290 | mod_timer(&local->dynamic_ps_timer, jiffies + | ||
291 | msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout)); | ||
292 | } | ||
293 | } | ||
294 | |||
295 | void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | 247 | void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) |
296 | { | 248 | { |
297 | struct ieee80211_local *local = hw_to_local(hw); | 249 | struct ieee80211_local *local = hw_to_local(hw); |
298 | struct ieee80211_sub_if_data *sdata; | ||
299 | bool was_hw_scan; | 250 | bool was_hw_scan; |
300 | 251 | ||
301 | mutex_lock(&local->scan_mtx); | 252 | mutex_lock(&local->scan_mtx); |
@@ -344,41 +295,19 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | |||
344 | 295 | ||
345 | drv_sw_scan_complete(local); | 296 | drv_sw_scan_complete(local); |
346 | 297 | ||
347 | mutex_lock(&local->iflist_mtx); | 298 | ieee80211_offchannel_return(local, true); |
348 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
349 | if (!netif_running(sdata->dev)) | ||
350 | continue; | ||
351 | |||
352 | /* Tell AP we're back */ | ||
353 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | ||
354 | if (sdata->u.mgd.associated) { | ||
355 | ieee80211_scan_ps_disable(sdata); | ||
356 | netif_tx_wake_all_queues(sdata->dev); | ||
357 | } | ||
358 | } else | ||
359 | netif_tx_wake_all_queues(sdata->dev); | ||
360 | |||
361 | /* re-enable beaconing */ | ||
362 | if (sdata->vif.type == NL80211_IFTYPE_AP || | ||
363 | sdata->vif.type == NL80211_IFTYPE_ADHOC || | ||
364 | sdata->vif.type == NL80211_IFTYPE_MESH_POINT) | ||
365 | ieee80211_bss_info_change_notify( | ||
366 | sdata, BSS_CHANGED_BEACON_ENABLED); | ||
367 | } | ||
368 | mutex_unlock(&local->iflist_mtx); | ||
369 | 299 | ||
370 | done: | 300 | done: |
371 | ieee80211_recalc_idle(local); | 301 | ieee80211_recalc_idle(local); |
372 | ieee80211_mlme_notify_scan_completed(local); | 302 | ieee80211_mlme_notify_scan_completed(local); |
373 | ieee80211_ibss_notify_scan_completed(local); | 303 | ieee80211_ibss_notify_scan_completed(local); |
374 | ieee80211_mesh_notify_scan_completed(local); | 304 | ieee80211_mesh_notify_scan_completed(local); |
305 | ieee80211_queue_work(&local->hw, &local->work_work); | ||
375 | } | 306 | } |
376 | EXPORT_SYMBOL(ieee80211_scan_completed); | 307 | EXPORT_SYMBOL(ieee80211_scan_completed); |
377 | 308 | ||
378 | static int ieee80211_start_sw_scan(struct ieee80211_local *local) | 309 | static int ieee80211_start_sw_scan(struct ieee80211_local *local) |
379 | { | 310 | { |
380 | struct ieee80211_sub_if_data *sdata; | ||
381 | |||
382 | /* | 311 | /* |
383 | * Hardware/driver doesn't support hw_scan, so use software | 312 | * Hardware/driver doesn't support hw_scan, so use software |
384 | * scanning instead. First send a nullfunc frame with power save | 313 | * scanning instead. First send a nullfunc frame with power save |
@@ -394,33 +323,15 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) | |||
394 | */ | 323 | */ |
395 | drv_sw_scan_start(local); | 324 | drv_sw_scan_start(local); |
396 | 325 | ||
397 | mutex_lock(&local->iflist_mtx); | 326 | ieee80211_offchannel_stop_beaconing(local); |
398 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
399 | if (!netif_running(sdata->dev)) | ||
400 | continue; | ||
401 | |||
402 | /* disable beaconing */ | ||
403 | if (sdata->vif.type == NL80211_IFTYPE_AP || | ||
404 | sdata->vif.type == NL80211_IFTYPE_ADHOC || | ||
405 | sdata->vif.type == NL80211_IFTYPE_MESH_POINT) | ||
406 | ieee80211_bss_info_change_notify( | ||
407 | sdata, BSS_CHANGED_BEACON_ENABLED); | ||
408 | |||
409 | /* | ||
410 | * only handle non-STA interfaces here, STA interfaces | ||
411 | * are handled in the scan state machine | ||
412 | */ | ||
413 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | ||
414 | netif_tx_stop_all_queues(sdata->dev); | ||
415 | } | ||
416 | mutex_unlock(&local->iflist_mtx); | ||
417 | 327 | ||
418 | local->next_scan_state = SCAN_DECISION; | 328 | local->next_scan_state = SCAN_DECISION; |
419 | local->scan_channel_idx = 0; | 329 | local->scan_channel_idx = 0; |
420 | 330 | ||
331 | drv_flush(local, false); | ||
332 | |||
421 | ieee80211_configure_filter(local); | 333 | ieee80211_configure_filter(local); |
422 | 334 | ||
423 | /* TODO: start scan as soon as all nullfunc frames are ACKed */ | ||
424 | ieee80211_queue_delayed_work(&local->hw, | 335 | ieee80211_queue_delayed_work(&local->hw, |
425 | &local->scan_work, | 336 | &local->scan_work, |
426 | IEEE80211_CHANNEL_TIME); | 337 | IEEE80211_CHANNEL_TIME); |
@@ -433,7 +344,6 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, | |||
433 | struct cfg80211_scan_request *req) | 344 | struct cfg80211_scan_request *req) |
434 | { | 345 | { |
435 | struct ieee80211_local *local = sdata->local; | 346 | struct ieee80211_local *local = sdata->local; |
436 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | ||
437 | int rc; | 347 | int rc; |
438 | 348 | ||
439 | if (local->scan_req) | 349 | if (local->scan_req) |
@@ -463,11 +373,8 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, | |||
463 | local->scan_req = req; | 373 | local->scan_req = req; |
464 | local->scan_sdata = sdata; | 374 | local->scan_sdata = sdata; |
465 | 375 | ||
466 | if (req != local->int_scan_req && | 376 | if (!list_empty(&local->work_list)) { |
467 | sdata->vif.type == NL80211_IFTYPE_STATION && | 377 | /* wait for the work to finish/time out */ |
468 | !list_empty(&ifmgd->work_list)) { | ||
469 | /* actually wait for the work it's doing to finish/time out */ | ||
470 | set_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request); | ||
471 | return 0; | 378 | return 0; |
472 | } | 379 | } |
473 | 380 | ||
@@ -526,7 +433,7 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
526 | /* check if at least one STA interface is associated */ | 433 | /* check if at least one STA interface is associated */ |
527 | mutex_lock(&local->iflist_mtx); | 434 | mutex_lock(&local->iflist_mtx); |
528 | list_for_each_entry(sdata, &local->interfaces, list) { | 435 | list_for_each_entry(sdata, &local->interfaces, list) { |
529 | if (!netif_running(sdata->dev)) | 436 | if (!ieee80211_sdata_running(sdata)) |
530 | continue; | 437 | continue; |
531 | 438 | ||
532 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | 439 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
@@ -564,56 +471,35 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
564 | static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, | 471 | static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, |
565 | unsigned long *next_delay) | 472 | unsigned long *next_delay) |
566 | { | 473 | { |
567 | struct ieee80211_sub_if_data *sdata; | 474 | ieee80211_offchannel_stop_station(local); |
475 | |||
476 | __set_bit(SCAN_OFF_CHANNEL, &local->scanning); | ||
568 | 477 | ||
569 | /* | 478 | /* |
570 | * notify the AP about us leaving the channel and stop all STA interfaces | 479 | * What if the nullfunc frames didn't arrive? |
571 | */ | 480 | */ |
572 | mutex_lock(&local->iflist_mtx); | 481 | drv_flush(local, false); |
573 | list_for_each_entry(sdata, &local->interfaces, list) { | 482 | if (local->ops->flush) |
574 | if (!netif_running(sdata->dev)) | 483 | *next_delay = 0; |
575 | continue; | 484 | else |
576 | 485 | *next_delay = HZ / 10; | |
577 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | ||
578 | netif_tx_stop_all_queues(sdata->dev); | ||
579 | if (sdata->u.mgd.associated) | ||
580 | ieee80211_scan_ps_enable(sdata); | ||
581 | } | ||
582 | } | ||
583 | mutex_unlock(&local->iflist_mtx); | ||
584 | |||
585 | __set_bit(SCAN_OFF_CHANNEL, &local->scanning); | ||
586 | 486 | ||
587 | /* advance to the next channel to be scanned */ | 487 | /* advance to the next channel to be scanned */ |
588 | *next_delay = HZ / 10; | ||
589 | local->next_scan_state = SCAN_SET_CHANNEL; | 488 | local->next_scan_state = SCAN_SET_CHANNEL; |
590 | } | 489 | } |
591 | 490 | ||
592 | static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *local, | 491 | static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *local, |
593 | unsigned long *next_delay) | 492 | unsigned long *next_delay) |
594 | { | 493 | { |
595 | struct ieee80211_sub_if_data *sdata = local->scan_sdata; | ||
596 | |||
597 | /* switch back to the operating channel */ | 494 | /* switch back to the operating channel */ |
598 | local->scan_channel = NULL; | 495 | local->scan_channel = NULL; |
599 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | 496 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); |
600 | 497 | ||
601 | /* | 498 | /* |
602 | * notify the AP about us being back and restart all STA interfaces | 499 | * Only re-enable station mode interface now; beaconing will be |
500 | * re-enabled once the full scan has been completed. | ||
603 | */ | 501 | */ |
604 | mutex_lock(&local->iflist_mtx); | 502 | ieee80211_offchannel_return(local, false); |
605 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
606 | if (!netif_running(sdata->dev)) | ||
607 | continue; | ||
608 | |||
609 | /* Tell AP we're back */ | ||
610 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | ||
611 | if (sdata->u.mgd.associated) | ||
612 | ieee80211_scan_ps_disable(sdata); | ||
613 | netif_tx_wake_all_queues(sdata->dev); | ||
614 | } | ||
615 | } | ||
616 | mutex_unlock(&local->iflist_mtx); | ||
617 | 503 | ||
618 | __clear_bit(SCAN_OFF_CHANNEL, &local->scanning); | 504 | __clear_bit(SCAN_OFF_CHANNEL, &local->scanning); |
619 | 505 | ||
@@ -727,7 +613,7 @@ void ieee80211_scan_work(struct work_struct *work) | |||
727 | /* | 613 | /* |
728 | * Avoid re-scheduling when the sdata is going away. | 614 | * Avoid re-scheduling when the sdata is going away. |
729 | */ | 615 | */ |
730 | if (!netif_running(sdata->dev)) { | 616 | if (!ieee80211_sdata_running(sdata)) { |
731 | ieee80211_scan_completed(&local->hw, true); | 617 | ieee80211_scan_completed(&local->hw, true); |
732 | return; | 618 | return; |
733 | } | 619 | } |