diff options
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 85507bd9e341..e1b0be7a57b9 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 | ||
