diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-scan.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-scan.c | 155 |
1 files changed, 141 insertions, 14 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index ceb91f969e45..fd6bafbddfca 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c | |||
@@ -314,6 +314,72 @@ u16 iwl_get_passive_dwell_time(struct iwl_priv *priv, | |||
314 | } | 314 | } |
315 | EXPORT_SYMBOL(iwl_get_passive_dwell_time); | 315 | EXPORT_SYMBOL(iwl_get_passive_dwell_time); |
316 | 316 | ||
317 | static int iwl_get_single_channel_for_scan(struct iwl_priv *priv, | ||
318 | enum ieee80211_band band, | ||
319 | struct iwl_scan_channel *scan_ch) | ||
320 | { | ||
321 | const struct ieee80211_supported_band *sband; | ||
322 | const struct iwl_channel_info *ch_info; | ||
323 | u16 passive_dwell = 0; | ||
324 | u16 active_dwell = 0; | ||
325 | int i, added = 0; | ||
326 | u16 channel = 0; | ||
327 | |||
328 | sband = iwl_get_hw_mode(priv, band); | ||
329 | if (!sband) { | ||
330 | IWL_ERR(priv, "invalid band\n"); | ||
331 | return added; | ||
332 | } | ||
333 | |||
334 | active_dwell = iwl_get_active_dwell_time(priv, band, 0); | ||
335 | passive_dwell = iwl_get_passive_dwell_time(priv, band); | ||
336 | |||
337 | if (passive_dwell <= active_dwell) | ||
338 | passive_dwell = active_dwell + 1; | ||
339 | |||
340 | /* only scan single channel, good enough to reset the RF */ | ||
341 | /* pick the first valid not in-use channel */ | ||
342 | if (band == IEEE80211_BAND_5GHZ) { | ||
343 | for (i = 14; i < priv->channel_count; i++) { | ||
344 | if (priv->channel_info[i].channel != | ||
345 | le16_to_cpu(priv->staging_rxon.channel)) { | ||
346 | channel = priv->channel_info[i].channel; | ||
347 | ch_info = iwl_get_channel_info(priv, | ||
348 | band, channel); | ||
349 | if (is_channel_valid(ch_info)) | ||
350 | break; | ||
351 | } | ||
352 | } | ||
353 | } else { | ||
354 | for (i = 0; i < 14; i++) { | ||
355 | if (priv->channel_info[i].channel != | ||
356 | le16_to_cpu(priv->staging_rxon.channel)) { | ||
357 | channel = | ||
358 | priv->channel_info[i].channel; | ||
359 | ch_info = iwl_get_channel_info(priv, | ||
360 | band, channel); | ||
361 | if (is_channel_valid(ch_info)) | ||
362 | break; | ||
363 | } | ||
364 | } | ||
365 | } | ||
366 | if (channel) { | ||
367 | scan_ch->channel = cpu_to_le16(channel); | ||
368 | scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; | ||
369 | scan_ch->active_dwell = cpu_to_le16(active_dwell); | ||
370 | scan_ch->passive_dwell = cpu_to_le16(passive_dwell); | ||
371 | /* Set txpower levels to defaults */ | ||
372 | scan_ch->dsp_atten = 110; | ||
373 | if (band == IEEE80211_BAND_5GHZ) | ||
374 | scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; | ||
375 | else | ||
376 | scan_ch->tx_gain = ((1 << 5) | (5 << 3)); | ||
377 | added++; | ||
378 | } else | ||
379 | IWL_ERR(priv, "no valid channel found\n"); | ||
380 | return added; | ||
381 | } | ||
382 | |||
317 | static int iwl_get_channels_for_scan(struct iwl_priv *priv, | 383 | static int iwl_get_channels_for_scan(struct iwl_priv *priv, |
318 | enum ieee80211_band band, | 384 | enum ieee80211_band band, |
319 | u8 is_active, u8 n_probes, | 385 | u8 is_active, u8 n_probes, |
@@ -421,6 +487,7 @@ static int iwl_scan_initiate(struct iwl_priv *priv) | |||
421 | 487 | ||
422 | IWL_DEBUG_INFO(priv, "Starting scan...\n"); | 488 | IWL_DEBUG_INFO(priv, "Starting scan...\n"); |
423 | set_bit(STATUS_SCANNING, &priv->status); | 489 | set_bit(STATUS_SCANNING, &priv->status); |
490 | priv->is_internal_short_scan = false; | ||
424 | priv->scan_start = jiffies; | 491 | priv->scan_start = jiffies; |
425 | priv->scan_pass_start = priv->scan_start; | 492 | priv->scan_pass_start = priv->scan_start; |
426 | 493 | ||
@@ -488,6 +555,45 @@ out_unlock: | |||
488 | } | 555 | } |
489 | EXPORT_SYMBOL(iwl_mac_hw_scan); | 556 | EXPORT_SYMBOL(iwl_mac_hw_scan); |
490 | 557 | ||
558 | /* | ||
559 | * internal short scan, this function should only been called while associated. | ||
560 | * It will reset and tune the radio to prevent possible RF related problem | ||
561 | */ | ||
562 | int iwl_internal_short_hw_scan(struct iwl_priv *priv) | ||
563 | { | ||
564 | int ret = 0; | ||
565 | |||
566 | if (!iwl_is_ready_rf(priv)) { | ||
567 | ret = -EIO; | ||
568 | IWL_DEBUG_SCAN(priv, "not ready or exit pending\n"); | ||
569 | goto out; | ||
570 | } | ||
571 | if (test_bit(STATUS_SCANNING, &priv->status)) { | ||
572 | IWL_DEBUG_SCAN(priv, "Scan already in progress.\n"); | ||
573 | ret = -EAGAIN; | ||
574 | goto out; | ||
575 | } | ||
576 | if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) { | ||
577 | IWL_DEBUG_SCAN(priv, "Scan request while abort pending\n"); | ||
578 | ret = -EAGAIN; | ||
579 | goto out; | ||
580 | } | ||
581 | priv->scan_bands = 0; | ||
582 | if (priv->band == IEEE80211_BAND_5GHZ) | ||
583 | priv->scan_bands |= BIT(IEEE80211_BAND_5GHZ); | ||
584 | else | ||
585 | priv->scan_bands |= BIT(IEEE80211_BAND_2GHZ); | ||
586 | |||
587 | IWL_DEBUG_SCAN(priv, "Start internal short scan...\n"); | ||
588 | set_bit(STATUS_SCANNING, &priv->status); | ||
589 | priv->is_internal_short_scan = true; | ||
590 | queue_work(priv->workqueue, &priv->request_scan); | ||
591 | |||
592 | out: | ||
593 | return ret; | ||
594 | } | ||
595 | EXPORT_SYMBOL(iwl_internal_short_hw_scan); | ||
596 | |||
491 | #define IWL_SCAN_CHECK_WATCHDOG (7 * HZ) | 597 | #define IWL_SCAN_CHECK_WATCHDOG (7 * HZ) |
492 | 598 | ||
493 | void iwl_bg_scan_check(struct work_struct *data) | 599 | void iwl_bg_scan_check(struct work_struct *data) |
@@ -551,7 +657,8 @@ u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame, | |||
551 | if (WARN_ON(left < ie_len)) | 657 | if (WARN_ON(left < ie_len)) |
552 | return len; | 658 | return len; |
553 | 659 | ||
554 | memcpy(pos, ies, ie_len); | 660 | if (ies) |
661 | memcpy(pos, ies, ie_len); | ||
555 | len += ie_len; | 662 | len += ie_len; |
556 | left -= ie_len; | 663 | left -= ie_len; |
557 | 664 | ||
@@ -654,7 +761,6 @@ static void iwl_bg_request_scan(struct work_struct *data) | |||
654 | unsigned long flags; | 761 | unsigned long flags; |
655 | 762 | ||
656 | IWL_DEBUG_INFO(priv, "Scanning while associated...\n"); | 763 | IWL_DEBUG_INFO(priv, "Scanning while associated...\n"); |
657 | |||
658 | spin_lock_irqsave(&priv->lock, flags); | 764 | spin_lock_irqsave(&priv->lock, flags); |
659 | interval = priv->beacon_int; | 765 | interval = priv->beacon_int; |
660 | spin_unlock_irqrestore(&priv->lock, flags); | 766 | spin_unlock_irqrestore(&priv->lock, flags); |
@@ -672,7 +778,9 @@ static void iwl_bg_request_scan(struct work_struct *data) | |||
672 | scan_suspend_time, interval); | 778 | scan_suspend_time, interval); |
673 | } | 779 | } |
674 | 780 | ||
675 | if (priv->scan_request->n_ssids) { | 781 | if (priv->is_internal_short_scan) { |
782 | IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n"); | ||
783 | } else if (priv->scan_request->n_ssids) { | ||
676 | int i, p = 0; | 784 | int i, p = 0; |
677 | IWL_DEBUG_SCAN(priv, "Kicking off active scan\n"); | 785 | IWL_DEBUG_SCAN(priv, "Kicking off active scan\n"); |
678 | for (i = 0; i < priv->scan_request->n_ssids; i++) { | 786 | for (i = 0; i < priv->scan_request->n_ssids; i++) { |
@@ -753,24 +861,38 @@ static void iwl_bg_request_scan(struct work_struct *data) | |||
753 | rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; | 861 | rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; |
754 | rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; | 862 | rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; |
755 | scan->rx_chain = cpu_to_le16(rx_chain); | 863 | scan->rx_chain = cpu_to_le16(rx_chain); |
756 | cmd_len = iwl_fill_probe_req(priv, | 864 | if (!priv->is_internal_short_scan) { |
757 | (struct ieee80211_mgmt *)scan->data, | 865 | cmd_len = iwl_fill_probe_req(priv, |
758 | priv->scan_request->ie, | 866 | (struct ieee80211_mgmt *)scan->data, |
759 | priv->scan_request->ie_len, | 867 | priv->scan_request->ie, |
760 | IWL_MAX_SCAN_SIZE - sizeof(*scan)); | 868 | priv->scan_request->ie_len, |
869 | IWL_MAX_SCAN_SIZE - sizeof(*scan)); | ||
870 | } else { | ||
871 | cmd_len = iwl_fill_probe_req(priv, | ||
872 | (struct ieee80211_mgmt *)scan->data, | ||
873 | NULL, 0, | ||
874 | IWL_MAX_SCAN_SIZE - sizeof(*scan)); | ||
761 | 875 | ||
876 | } | ||
762 | scan->tx_cmd.len = cpu_to_le16(cmd_len); | 877 | scan->tx_cmd.len = cpu_to_le16(cmd_len); |
763 | |||
764 | if (iwl_is_monitor_mode(priv)) | 878 | if (iwl_is_monitor_mode(priv)) |
765 | scan->filter_flags = RXON_FILTER_PROMISC_MSK; | 879 | scan->filter_flags = RXON_FILTER_PROMISC_MSK; |
766 | 880 | ||
767 | scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK | | 881 | scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK | |
768 | RXON_FILTER_BCON_AWARE_MSK); | 882 | RXON_FILTER_BCON_AWARE_MSK); |
769 | 883 | ||
770 | scan->channel_count = | 884 | if (priv->is_internal_short_scan) { |
771 | iwl_get_channels_for_scan(priv, band, is_active, n_probes, | 885 | scan->channel_count = |
772 | (void *)&scan->data[le16_to_cpu(scan->tx_cmd.len)]); | 886 | iwl_get_single_channel_for_scan(priv, band, |
773 | 887 | (void *)&scan->data[le16_to_cpu( | |
888 | scan->tx_cmd.len)]); | ||
889 | } else { | ||
890 | scan->channel_count = | ||
891 | iwl_get_channels_for_scan(priv, band, | ||
892 | is_active, n_probes, | ||
893 | (void *)&scan->data[le16_to_cpu( | ||
894 | scan->tx_cmd.len)]); | ||
895 | } | ||
774 | if (scan->channel_count == 0) { | 896 | if (scan->channel_count == 0) { |
775 | IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count); | 897 | IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count); |
776 | goto done; | 898 | goto done; |
@@ -831,7 +953,12 @@ void iwl_bg_scan_completed(struct work_struct *work) | |||
831 | 953 | ||
832 | cancel_delayed_work(&priv->scan_check); | 954 | cancel_delayed_work(&priv->scan_check); |
833 | 955 | ||
834 | ieee80211_scan_completed(priv->hw, false); | 956 | if (!priv->is_internal_short_scan) |
957 | ieee80211_scan_completed(priv->hw, false); | ||
958 | else { | ||
959 | priv->is_internal_short_scan = false; | ||
960 | IWL_DEBUG_SCAN(priv, "internal short scan completed\n"); | ||
961 | } | ||
835 | 962 | ||
836 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) | 963 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) |
837 | return; | 964 | return; |