diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-21 00:04:44 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-21 00:04:44 -0400 |
commit | f8965467f366fd18f01feafb5db10512d7b4422c (patch) | |
tree | 3706a9cd779859271ca61b85c63a1bc3f82d626e /net/mac80211/scan.c | |
parent | a26272e5200765691e67d6780e52b32498fdb659 (diff) | |
parent | 2ec8c6bb5d8f3a62a79f463525054bae1e3d4487 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6: (1674 commits)
qlcnic: adding co maintainer
ixgbe: add support for active DA cables
ixgbe: dcb, do not tag tc_prio_control frames
ixgbe: fix ixgbe_tx_is_paused logic
ixgbe: always enable vlan strip/insert when DCB is enabled
ixgbe: remove some redundant code in setting FCoE FIP filter
ixgbe: fix wrong offset to fc_frame_header in ixgbe_fcoe_ddp
ixgbe: fix header len when unsplit packet overflows to data buffer
ipv6: Never schedule DAD timer on dead address
ipv6: Use POSTDAD state
ipv6: Use state_lock to protect ifa state
ipv6: Replace inet6_ifaddr->dead with state
cxgb4: notify upper drivers if the device is already up when they load
cxgb4: keep interrupts available when the ports are brought down
cxgb4: fix initial addition of MAC address
cnic: Return SPQ credit to bnx2x after ring setup and shutdown.
cnic: Convert cnic_local_flags to atomic ops.
can: Fix SJA1000 command register writes on SMP systems
bridge: fix build for CONFIG_SYSFS disabled
ARCNET: Limit com20020 PCI ID matches for SOHARD cards
...
Fix up various conflicts with pcmcia tree drivers/net/
{pcmcia/3c589_cs.c, wireless/orinoco/orinoco_cs.c and
wireless/orinoco/spectrum_cs.c} and feature removal
(Documentation/feature-removal-schedule.txt).
Also fix a non-content conflict due to pm_qos_requirement getting
renamed in the PM tree (now pm_qos_request) in net/mac80211/scan.c
Diffstat (limited to 'net/mac80211/scan.c')
-rw-r--r-- | net/mac80211/scan.c | 126 |
1 files changed, 107 insertions, 19 deletions
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 85507bd9e34..e1b0be7a57b 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -14,6 +14,8 @@ | |||
14 | 14 | ||
15 | #include <linux/if_arp.h> | 15 | #include <linux/if_arp.h> |
16 | #include <linux/rtnetlink.h> | 16 | #include <linux/rtnetlink.h> |
17 | #include <linux/pm_qos_params.h> | ||
18 | #include <net/sch_generic.h> | ||
17 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
18 | #include <net/mac80211.h> | 20 | #include <net/mac80211.h> |
19 | 21 | ||
@@ -83,7 +85,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local, | |||
83 | { | 85 | { |
84 | struct cfg80211_bss *cbss; | 86 | struct cfg80211_bss *cbss; |
85 | struct ieee80211_bss *bss; | 87 | struct ieee80211_bss *bss; |
86 | int clen; | 88 | int clen, srlen; |
87 | s32 signal = 0; | 89 | s32 signal = 0; |
88 | 90 | ||
89 | if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) | 91 | if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) |
@@ -112,23 +114,24 @@ ieee80211_bss_info_update(struct ieee80211_local *local, | |||
112 | bss->dtim_period = tim_ie->dtim_period; | 114 | bss->dtim_period = tim_ie->dtim_period; |
113 | } | 115 | } |
114 | 116 | ||
115 | bss->supp_rates_len = 0; | 117 | /* replace old supported rates if we get new values */ |
118 | srlen = 0; | ||
116 | if (elems->supp_rates) { | 119 | if (elems->supp_rates) { |
117 | clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; | 120 | clen = IEEE80211_MAX_SUPP_RATES; |
118 | if (clen > elems->supp_rates_len) | 121 | if (clen > elems->supp_rates_len) |
119 | clen = elems->supp_rates_len; | 122 | clen = elems->supp_rates_len; |
120 | memcpy(&bss->supp_rates[bss->supp_rates_len], elems->supp_rates, | 123 | memcpy(bss->supp_rates, elems->supp_rates, clen); |
121 | clen); | 124 | srlen += clen; |
122 | bss->supp_rates_len += clen; | ||
123 | } | 125 | } |
124 | if (elems->ext_supp_rates) { | 126 | if (elems->ext_supp_rates) { |
125 | clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; | 127 | clen = IEEE80211_MAX_SUPP_RATES - srlen; |
126 | if (clen > elems->ext_supp_rates_len) | 128 | if (clen > elems->ext_supp_rates_len) |
127 | clen = elems->ext_supp_rates_len; | 129 | clen = elems->ext_supp_rates_len; |
128 | memcpy(&bss->supp_rates[bss->supp_rates_len], | 130 | memcpy(bss->supp_rates + srlen, elems->ext_supp_rates, clen); |
129 | elems->ext_supp_rates, clen); | 131 | srlen += clen; |
130 | bss->supp_rates_len += clen; | ||
131 | } | 132 | } |
133 | if (srlen) | ||
134 | bss->supp_rates_len = srlen; | ||
132 | 135 | ||
133 | bss->wmm_used = elems->wmm_param || elems->wmm_info; | 136 | bss->wmm_used = elems->wmm_param || elems->wmm_info; |
134 | bss->uapsd_supported = is_uapsd_supported(elems); | 137 | bss->uapsd_supported = is_uapsd_supported(elems); |
@@ -246,6 +249,8 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | |||
246 | struct ieee80211_local *local = hw_to_local(hw); | 249 | struct ieee80211_local *local = hw_to_local(hw); |
247 | bool was_hw_scan; | 250 | bool was_hw_scan; |
248 | 251 | ||
252 | trace_api_scan_completed(local, aborted); | ||
253 | |||
249 | mutex_lock(&local->scan_mtx); | 254 | mutex_lock(&local->scan_mtx); |
250 | 255 | ||
251 | /* | 256 | /* |
@@ -322,6 +327,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) | |||
322 | 327 | ||
323 | ieee80211_offchannel_stop_beaconing(local); | 328 | ieee80211_offchannel_stop_beaconing(local); |
324 | 329 | ||
330 | local->leave_oper_channel_time = 0; | ||
325 | local->next_scan_state = SCAN_DECISION; | 331 | local->next_scan_state = SCAN_DECISION; |
326 | local->scan_channel_idx = 0; | 332 | local->scan_channel_idx = 0; |
327 | 333 | ||
@@ -406,7 +412,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, | |||
406 | 412 | ||
407 | if (local->ops->hw_scan) { | 413 | if (local->ops->hw_scan) { |
408 | WARN_ON(!ieee80211_prep_hw_scan(local)); | 414 | WARN_ON(!ieee80211_prep_hw_scan(local)); |
409 | rc = drv_hw_scan(local, local->hw_scan_req); | 415 | rc = drv_hw_scan(local, sdata, local->hw_scan_req); |
410 | } else | 416 | } else |
411 | rc = ieee80211_start_sw_scan(local); | 417 | rc = ieee80211_start_sw_scan(local); |
412 | 418 | ||
@@ -426,11 +432,28 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, | |||
426 | return rc; | 432 | return rc; |
427 | } | 433 | } |
428 | 434 | ||
435 | static unsigned long | ||
436 | ieee80211_scan_get_channel_time(struct ieee80211_channel *chan) | ||
437 | { | ||
438 | /* | ||
439 | * TODO: channel switching also consumes quite some time, | ||
440 | * add that delay as well to get a better estimation | ||
441 | */ | ||
442 | if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) | ||
443 | return IEEE80211_PASSIVE_CHANNEL_TIME; | ||
444 | return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME; | ||
445 | } | ||
446 | |||
429 | static int ieee80211_scan_state_decision(struct ieee80211_local *local, | 447 | static int ieee80211_scan_state_decision(struct ieee80211_local *local, |
430 | unsigned long *next_delay) | 448 | unsigned long *next_delay) |
431 | { | 449 | { |
432 | bool associated = false; | 450 | bool associated = false; |
451 | bool tx_empty = true; | ||
452 | bool bad_latency; | ||
453 | bool listen_int_exceeded; | ||
454 | unsigned long min_beacon_int = 0; | ||
433 | struct ieee80211_sub_if_data *sdata; | 455 | struct ieee80211_sub_if_data *sdata; |
456 | struct ieee80211_channel *next_chan; | ||
434 | 457 | ||
435 | /* if no more bands/channels left, complete scan and advance to the idle state */ | 458 | /* if no more bands/channels left, complete scan and advance to the idle state */ |
436 | if (local->scan_channel_idx >= local->scan_req->n_channels) { | 459 | if (local->scan_channel_idx >= local->scan_req->n_channels) { |
@@ -438,7 +461,11 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
438 | return 1; | 461 | return 1; |
439 | } | 462 | } |
440 | 463 | ||
441 | /* check if at least one STA interface is associated */ | 464 | /* |
465 | * check if at least one STA interface is associated, | ||
466 | * check if at least one STA interface has pending tx frames | ||
467 | * and grab the lowest used beacon interval | ||
468 | */ | ||
442 | mutex_lock(&local->iflist_mtx); | 469 | mutex_lock(&local->iflist_mtx); |
443 | list_for_each_entry(sdata, &local->interfaces, list) { | 470 | list_for_each_entry(sdata, &local->interfaces, list) { |
444 | if (!ieee80211_sdata_running(sdata)) | 471 | if (!ieee80211_sdata_running(sdata)) |
@@ -447,7 +474,16 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
447 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | 474 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
448 | if (sdata->u.mgd.associated) { | 475 | if (sdata->u.mgd.associated) { |
449 | associated = true; | 476 | associated = true; |
450 | break; | 477 | |
478 | if (sdata->vif.bss_conf.beacon_int < | ||
479 | min_beacon_int || min_beacon_int == 0) | ||
480 | min_beacon_int = | ||
481 | sdata->vif.bss_conf.beacon_int; | ||
482 | |||
483 | if (!qdisc_all_tx_empty(sdata->dev)) { | ||
484 | tx_empty = false; | ||
485 | break; | ||
486 | } | ||
451 | } | 487 | } |
452 | } | 488 | } |
453 | } | 489 | } |
@@ -456,11 +492,34 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
456 | if (local->scan_channel) { | 492 | if (local->scan_channel) { |
457 | /* | 493 | /* |
458 | * we're currently scanning a different channel, let's | 494 | * we're currently scanning a different channel, let's |
459 | * switch back to the operating channel now if at least | 495 | * see if we can scan another channel without interfering |
460 | * one interface is associated. Otherwise just scan the | 496 | * with the current traffic situation. |
461 | * next channel | 497 | * |
498 | * Since we don't know if the AP has pending frames for us | ||
499 | * we can only check for our tx queues and use the current | ||
500 | * pm_qos requirements for rx. Hence, if no tx traffic occurs | ||
501 | * at all we will scan as many channels in a row as the pm_qos | ||
502 | * latency allows us to. Additionally we also check for the | ||
503 | * currently negotiated listen interval to prevent losing | ||
504 | * frames unnecessarily. | ||
505 | * | ||
506 | * Otherwise switch back to the operating channel. | ||
462 | */ | 507 | */ |
463 | if (associated) | 508 | next_chan = local->scan_req->channels[local->scan_channel_idx]; |
509 | |||
510 | bad_latency = time_after(jiffies + | ||
511 | ieee80211_scan_get_channel_time(next_chan), | ||
512 | local->leave_oper_channel_time + | ||
513 | usecs_to_jiffies(pm_qos_request(PM_QOS_NETWORK_LATENCY))); | ||
514 | |||
515 | listen_int_exceeded = time_after(jiffies + | ||
516 | ieee80211_scan_get_channel_time(next_chan), | ||
517 | local->leave_oper_channel_time + | ||
518 | usecs_to_jiffies(min_beacon_int * 1024) * | ||
519 | local->hw.conf.listen_interval); | ||
520 | |||
521 | if (associated && ( !tx_empty || bad_latency || | ||
522 | listen_int_exceeded)) | ||
464 | local->next_scan_state = SCAN_ENTER_OPER_CHANNEL; | 523 | local->next_scan_state = SCAN_ENTER_OPER_CHANNEL; |
465 | else | 524 | else |
466 | local->next_scan_state = SCAN_SET_CHANNEL; | 525 | local->next_scan_state = SCAN_SET_CHANNEL; |
@@ -492,6 +551,9 @@ static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *loca | |||
492 | else | 551 | else |
493 | *next_delay = HZ / 10; | 552 | *next_delay = HZ / 10; |
494 | 553 | ||
554 | /* remember when we left the operating channel */ | ||
555 | local->leave_oper_channel_time = jiffies; | ||
556 | |||
495 | /* advance to the next channel to be scanned */ | 557 | /* advance to the next channel to be scanned */ |
496 | local->next_scan_state = SCAN_SET_CHANNEL; | 558 | local->next_scan_state = SCAN_SET_CHANNEL; |
497 | } | 559 | } |
@@ -594,7 +656,7 @@ void ieee80211_scan_work(struct work_struct *work) | |||
594 | } | 656 | } |
595 | 657 | ||
596 | if (local->hw_scan_req) { | 658 | if (local->hw_scan_req) { |
597 | int rc = drv_hw_scan(local, local->hw_scan_req); | 659 | int rc = drv_hw_scan(local, sdata, local->hw_scan_req); |
598 | mutex_unlock(&local->scan_mtx); | 660 | mutex_unlock(&local->scan_mtx); |
599 | if (rc) | 661 | if (rc) |
600 | ieee80211_scan_completed(&local->hw, true); | 662 | ieee80211_scan_completed(&local->hw, true); |
@@ -667,10 +729,12 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, | |||
667 | } | 729 | } |
668 | 730 | ||
669 | int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, | 731 | int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, |
670 | const u8 *ssid, u8 ssid_len) | 732 | const u8 *ssid, u8 ssid_len, |
733 | struct ieee80211_channel *chan) | ||
671 | { | 734 | { |
672 | struct ieee80211_local *local = sdata->local; | 735 | struct ieee80211_local *local = sdata->local; |
673 | int ret = -EBUSY; | 736 | int ret = -EBUSY; |
737 | enum nl80211_band band; | ||
674 | 738 | ||
675 | mutex_lock(&local->scan_mtx); | 739 | mutex_lock(&local->scan_mtx); |
676 | 740 | ||
@@ -678,6 +742,30 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, | |||
678 | if (local->scan_req) | 742 | if (local->scan_req) |
679 | goto unlock; | 743 | goto unlock; |
680 | 744 | ||
745 | /* fill internal scan request */ | ||
746 | if (!chan) { | ||
747 | int i, nchan = 0; | ||
748 | |||
749 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | ||
750 | if (!local->hw.wiphy->bands[band]) | ||
751 | continue; | ||
752 | for (i = 0; | ||
753 | i < local->hw.wiphy->bands[band]->n_channels; | ||
754 | i++) { | ||
755 | local->int_scan_req->channels[nchan] = | ||
756 | &local->hw.wiphy->bands[band]->channels[i]; | ||
757 | nchan++; | ||
758 | } | ||
759 | } | ||
760 | |||
761 | local->int_scan_req->n_channels = nchan; | ||
762 | } else { | ||
763 | local->int_scan_req->channels[0] = chan; | ||
764 | local->int_scan_req->n_channels = 1; | ||
765 | } | ||
766 | |||
767 | local->int_scan_req->ssids = &local->scan_ssid; | ||
768 | local->int_scan_req->n_ssids = 1; | ||
681 | memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN); | 769 | memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN); |
682 | local->int_scan_req->ssids[0].ssid_len = ssid_len; | 770 | local->int_scan_req->ssids[0].ssid_len = ssid_len; |
683 | 771 | ||