iwlwifi: remove unnecessary parameter in scan function
[safe/jmp/linux-2.6] / drivers / net / wireless / iwlwifi / iwl-scan.c
index c4c916d..1544d75 100644 (file)
@@ -2,7 +2,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2009 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -27,7 +27,6 @@
  *****************************************************************************/
 #include <linux/types.h>
 #include <linux/etherdevice.h>
-#include <net/lib80211.h>
 #include <net/mac80211.h>
 
 #include "iwl-eeprom.h"
@@ -112,7 +111,7 @@ EXPORT_SYMBOL(iwl_scan_cancel_timeout);
 static int iwl_send_scan_abort(struct iwl_priv *priv)
 {
        int ret = 0;
-       struct iwl_rx_packet *res;
+       struct iwl_rx_packet *pkt;
        struct iwl_host_cmd cmd = {
                .id = REPLY_SCAN_ABORT_CMD,
                .flags = CMD_WANT_SKB,
@@ -132,21 +131,20 @@ static int iwl_send_scan_abort(struct iwl_priv *priv)
                return ret;
        }
 
-       res = (struct iwl_rx_packet *)cmd.reply_skb->data;
-       if (res->u.status != CAN_ABORT_STATUS) {
+       pkt = (struct iwl_rx_packet *)cmd.reply_page;
+       if (pkt->u.status != CAN_ABORT_STATUS) {
                /* The scan abort will return 1 for success or
                 * 2 for "failure".  A failure condition can be
                 * due to simply not being in an active scan which
                 * can occur if we send the scan abort before we
                 * the microcode has notified us that a scan is
                 * completed. */
-               IWL_DEBUG_INFO(priv, "SCAN_ABORT returned %d.\n", res->u.status);
+               IWL_DEBUG_INFO(priv, "SCAN_ABORT returned %d.\n", pkt->u.status);
                clear_bit(STATUS_SCAN_ABORTING, &priv->status);
                clear_bit(STATUS_SCAN_HW, &priv->status);
        }
 
-       priv->alloc_rxb_skb--;
-       dev_kfree_skb_any(cmd.reply_skb);
+       iwl_free_pages(priv, cmd.reply_page);
 
        return ret;
 }
@@ -156,7 +154,7 @@ static void iwl_rx_reply_scan(struct iwl_priv *priv,
                              struct iwl_rx_mem_buffer *rxb)
 {
 #ifdef CONFIG_IWLWIFI_DEBUG
-       struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
        struct iwl_scanreq_notification *notif =
            (struct iwl_scanreq_notification *)pkt->u.raw;
 
@@ -168,7 +166,7 @@ static void iwl_rx_reply_scan(struct iwl_priv *priv,
 static void iwl_rx_scan_start_notif(struct iwl_priv *priv,
                                    struct iwl_rx_mem_buffer *rxb)
 {
-       struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
        struct iwl_scanstart_notification *notif =
            (struct iwl_scanstart_notification *)pkt->u.raw;
        priv->scan_start_tsf = le32_to_cpu(notif->tsf_low);
@@ -187,26 +185,24 @@ static void iwl_rx_scan_results_notif(struct iwl_priv *priv,
                                      struct iwl_rx_mem_buffer *rxb)
 {
 #ifdef CONFIG_IWLWIFI_DEBUG
-       struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
        struct iwl_scanresults_notification *notif =
            (struct iwl_scanresults_notification *)pkt->u.raw;
 
        IWL_DEBUG_SCAN(priv, "Scan ch.res: "
                       "%d [802.11%s] "
                       "(TSF: 0x%08X:%08X) - %d "
-                      "elapsed=%lu usec (%dms since last)\n",
+                      "elapsed=%lu usec\n",
                       notif->channel,
                       notif->band ? "bg" : "a",
                       le32_to_cpu(notif->tsf_high),
                       le32_to_cpu(notif->tsf_low),
                       le32_to_cpu(notif->statistics[0]),
-                      le32_to_cpu(notif->tsf_low) - priv->scan_start_tsf,
-                      jiffies_to_msecs(elapsed_jiffies
-                                       (priv->last_scan_jiffies, jiffies)));
+                      le32_to_cpu(notif->tsf_low) - priv->scan_start_tsf);
 #endif
 
-       priv->last_scan_jiffies = jiffies;
-       priv->next_scan_jiffies = 0;
+       if (!priv->is_internal_short_scan)
+               priv->next_scan_jiffies = 0;
 }
 
 /* Service SCAN_COMPLETE_NOTIFICATION (0x84) */
@@ -214,7 +210,7 @@ static void iwl_rx_scan_complete_notif(struct iwl_priv *priv,
                                       struct iwl_rx_mem_buffer *rxb)
 {
 #ifdef CONFIG_IWLWIFI_DEBUG
-       struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
        struct iwl_scancomplete_notification *scan_notif = (void *)pkt->u.raw;
 
        IWL_DEBUG_SCAN(priv, "Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n",
@@ -252,8 +248,9 @@ static void iwl_rx_scan_complete_notif(struct iwl_priv *priv,
                        goto reschedule;
        }
 
-       priv->last_scan_jiffies = jiffies;
-       priv->next_scan_jiffies = 0;
+       if (!priv->is_internal_short_scan)
+               priv->next_scan_jiffies = 0;
+
        IWL_DEBUG_INFO(priv, "Setting scan to off\n");
 
        clear_bit(STATUS_SCANNING, &priv->status);
@@ -316,6 +313,72 @@ u16 iwl_get_passive_dwell_time(struct iwl_priv *priv,
 }
 EXPORT_SYMBOL(iwl_get_passive_dwell_time);
 
+static int iwl_get_single_channel_for_scan(struct iwl_priv *priv,
+                                    enum ieee80211_band band,
+                                    struct iwl_scan_channel *scan_ch)
+{
+       const struct ieee80211_supported_band *sband;
+       const struct iwl_channel_info *ch_info;
+       u16 passive_dwell = 0;
+       u16 active_dwell = 0;
+       int i, added = 0;
+       u16 channel = 0;
+
+       sband = iwl_get_hw_mode(priv, band);
+       if (!sband) {
+               IWL_ERR(priv, "invalid band\n");
+               return added;
+       }
+
+       active_dwell = iwl_get_active_dwell_time(priv, band, 0);
+       passive_dwell = iwl_get_passive_dwell_time(priv, band);
+
+       if (passive_dwell <= active_dwell)
+               passive_dwell = active_dwell + 1;
+
+       /* only scan single channel, good enough to reset the RF */
+       /* pick the first valid not in-use channel */
+       if (band == IEEE80211_BAND_5GHZ) {
+               for (i = 14; i < priv->channel_count; i++) {
+                       if (priv->channel_info[i].channel !=
+                           le16_to_cpu(priv->staging_rxon.channel)) {
+                               channel = priv->channel_info[i].channel;
+                               ch_info = iwl_get_channel_info(priv,
+                                       band, channel);
+                               if (is_channel_valid(ch_info))
+                                       break;
+                       }
+               }
+       } else {
+               for (i = 0; i < 14; i++) {
+                       if (priv->channel_info[i].channel !=
+                           le16_to_cpu(priv->staging_rxon.channel)) {
+                                       channel =
+                                               priv->channel_info[i].channel;
+                                       ch_info = iwl_get_channel_info(priv,
+                                               band, channel);
+                                       if (is_channel_valid(ch_info))
+                                               break;
+                       }
+               }
+       }
+       if (channel) {
+               scan_ch->channel = cpu_to_le16(channel);
+               scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
+               scan_ch->active_dwell = cpu_to_le16(active_dwell);
+               scan_ch->passive_dwell = cpu_to_le16(passive_dwell);
+               /* Set txpower levels to defaults */
+               scan_ch->dsp_atten = 110;
+               if (band == IEEE80211_BAND_5GHZ)
+                       scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
+               else
+                       scan_ch->tx_gain = ((1 << 5) | (5 << 3));
+               added++;
+       } else
+               IWL_ERR(priv, "no valid channel found\n");
+       return added;
+}
+
 static int iwl_get_channels_for_scan(struct iwl_priv *priv,
                                     enum ieee80211_band band,
                                     u8 is_active, u8 n_probes,
@@ -402,26 +465,13 @@ void iwl_init_scan_params(struct iwl_priv *priv)
        if (!priv->scan_tx_ant[IEEE80211_BAND_2GHZ])
                priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx;
 }
+EXPORT_SYMBOL(iwl_init_scan_params);
 
 static int iwl_scan_initiate(struct iwl_priv *priv)
 {
-       if (!iwl_is_ready_rf(priv)) {
-               IWL_DEBUG_SCAN(priv, "Aborting scan due to not ready.\n");
-               return -EIO;
-       }
-
-       if (test_bit(STATUS_SCANNING, &priv->status)) {
-               IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
-               return -EAGAIN;
-       }
-
-       if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
-               IWL_DEBUG_SCAN(priv, "Scan request while abort pending\n");
-               return -EAGAIN;
-       }
-
        IWL_DEBUG_INFO(priv, "Starting scan...\n");
        set_bit(STATUS_SCANNING, &priv->status);
+       priv->is_internal_short_scan = false;
        priv->scan_start = jiffies;
        priv->scan_pass_start = priv->scan_start;
 
@@ -450,6 +500,18 @@ int iwl_mac_hw_scan(struct ieee80211_hw *hw,
                goto out_unlock;
        }
 
+       if (test_bit(STATUS_SCANNING, &priv->status)) {
+               IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
+               ret = -EAGAIN;
+               goto out_unlock;
+       }
+
+       if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
+               IWL_DEBUG_SCAN(priv, "Scan request while abort pending\n");
+               ret = -EAGAIN;
+               goto out_unlock;
+       }
+
        /* We don't schedule scan within next_scan_jiffies period.
         * Avoid scanning during possible EAPOL exchange, return
         * success immediately.
@@ -462,15 +524,6 @@ int iwl_mac_hw_scan(struct ieee80211_hw *hw,
                goto out_unlock;
        }
 
-       /* if we just finished scan ask for delay */
-       if (iwl_is_associated(priv) && priv->last_scan_jiffies &&
-           time_after(priv->last_scan_jiffies + IWL_DELAY_NEXT_SCAN, jiffies)) {
-               IWL_DEBUG_SCAN(priv, "scan rejected: within previous scan period\n");
-               queue_work(priv->workqueue, &priv->scan_completed);
-               ret = 0;
-               goto out_unlock;
-       }
-
        priv->scan_bands = 0;
        for (i = 0; i < req->n_channels; i++)
                priv->scan_bands |= BIT(req->channels[i]->band);
@@ -489,6 +542,45 @@ out_unlock:
 }
 EXPORT_SYMBOL(iwl_mac_hw_scan);
 
+/*
+ * internal short scan, this function should only been called while associated.
+ * It will reset and tune the radio to prevent possible RF related problem
+ */
+int iwl_internal_short_hw_scan(struct iwl_priv *priv)
+{
+       int ret = 0;
+
+       if (!iwl_is_ready_rf(priv)) {
+               ret = -EIO;
+               IWL_DEBUG_SCAN(priv, "not ready or exit pending\n");
+               goto out;
+       }
+       if (test_bit(STATUS_SCANNING, &priv->status)) {
+               IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
+               ret = -EAGAIN;
+               goto out;
+       }
+       if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
+               IWL_DEBUG_SCAN(priv, "Scan request while abort pending\n");
+               ret = -EAGAIN;
+               goto out;
+       }
+
+       priv->scan_bands = 0;
+       if (priv->band == IEEE80211_BAND_5GHZ)
+               priv->scan_bands |= BIT(IEEE80211_BAND_5GHZ);
+       else
+               priv->scan_bands |= BIT(IEEE80211_BAND_2GHZ);
+
+       IWL_DEBUG_SCAN(priv, "Start internal short scan...\n");
+       set_bit(STATUS_SCANNING, &priv->status);
+       priv->is_internal_short_scan = true;
+       queue_work(priv->workqueue, &priv->request_scan);
+
+out:
+       return ret;
+}
+
 #define IWL_SCAN_CHECK_WATCHDOG (7 * HZ)
 
 void iwl_bg_scan_check(struct work_struct *data)
@@ -545,14 +637,26 @@ u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame,
        if (left < 0)
                return 0;
        *pos++ = WLAN_EID_SSID;
-       *pos++ = 0;
-
-       len += 2;
+       if (!priv->is_internal_short_scan &&
+           priv->scan_request->n_ssids) {
+               struct cfg80211_ssid *ssid =
+                       priv->scan_request->ssids;
+
+               /* Broadcast if ssid_len is 0 */
+               *pos++ = ssid->ssid_len;
+               memcpy(pos, ssid->ssid, ssid->ssid_len);
+               pos += ssid->ssid_len;
+               len += 2 + ssid->ssid_len;
+       } else {
+               *pos++ = 0;
+               len += 2;
+       }
 
        if (WARN_ON(left < ie_len))
                return len;
 
-       memcpy(pos, ies, ie_len);
+       if (ies)
+               memcpy(pos, ies, ie_len);
        len += ie_len;
        left -= ie_len;
 
@@ -571,7 +675,6 @@ static void iwl_bg_request_scan(struct work_struct *data)
        };
        struct iwl_scan_cmd *scan;
        struct ieee80211_conf *conf = NULL;
-       int ret = 0;
        u32 rate_flags = 0;
        u16 cmd_len;
        u16 rx_chain = 0;
@@ -581,6 +684,7 @@ static void iwl_bg_request_scan(struct work_struct *data)
        u8 rate;
        bool is_active = false;
        int  chan_mod;
+       u8 active_chains;
 
        conf = ieee80211_get_hw_conf(priv->hw);
 
@@ -603,7 +707,6 @@ static void iwl_bg_request_scan(struct work_struct *data)
        if (test_bit(STATUS_SCAN_HW, &priv->status)) {
                IWL_DEBUG_INFO(priv, "Multiple concurrent scan requests in parallel. "
                               "Ignoring second request.\n");
-               ret = -EIO;
                goto done;
        }
 
@@ -636,7 +739,8 @@ static void iwl_bg_request_scan(struct work_struct *data)
                priv->scan = kmalloc(sizeof(struct iwl_scan_cmd) +
                                     IWL_MAX_SCAN_SIZE, GFP_KERNEL);
                if (!priv->scan) {
-                       ret = -ENOMEM;
+                       IWL_DEBUG_SCAN(priv,
+                                      "fail to allocate memory for scan\n");
                        goto done;
                }
        }
@@ -654,7 +758,6 @@ static void iwl_bg_request_scan(struct work_struct *data)
                unsigned long flags;
 
                IWL_DEBUG_INFO(priv, "Scanning while associated...\n");
-
                spin_lock_irqsave(&priv->lock, flags);
                interval = priv->beacon_int;
                spin_unlock_irqrestore(&priv->lock, flags);
@@ -672,21 +775,29 @@ static void iwl_bg_request_scan(struct work_struct *data)
                               scan_suspend_time, interval);
        }
 
-       if (priv->scan_request->n_ssids) {
-               int i, p = 0;
+       if (priv->is_internal_short_scan) {
+               IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n");
+       } else if (priv->scan_request->n_ssids) {
                IWL_DEBUG_SCAN(priv, "Kicking off active scan\n");
-               for (i = 0; i < priv->scan_request->n_ssids; i++) {
-                       /* always does wildcard anyway */
-                       if (!priv->scan_request->ssids[i].ssid_len)
-                               continue;
-                       scan->direct_scan[p].id = WLAN_EID_SSID;
-                       scan->direct_scan[p].len =
-                               priv->scan_request->ssids[i].ssid_len;
-                       memcpy(scan->direct_scan[p].ssid,
-                              priv->scan_request->ssids[i].ssid,
-                              priv->scan_request->ssids[i].ssid_len);
-                       n_probes++;
-                       p++;
+               /*
+                * The first SSID to scan is stuffed into the probe request
+                * template and the remaining ones are handled through the
+                * direct_scan array.
+                */
+               if (priv->scan_request->n_ssids > 1) {
+                       int i, p = 0;
+                       for (i = 1; i < priv->scan_request->n_ssids; i++) {
+                               if (!priv->scan_request->ssids[i].ssid_len)
+                                       continue;
+                               scan->direct_scan[p].id = WLAN_EID_SSID;
+                               scan->direct_scan[p].len =
+                                       priv->scan_request->ssids[i].ssid_len;
+                               memcpy(scan->direct_scan[p].ssid,
+                                      priv->scan_request->ssids[i].ssid,
+                                      priv->scan_request->ssids[i].ssid_len);
+                               n_probes++;
+                               p++;
+                       }
                }
                is_active = true;
        } else
@@ -734,30 +845,57 @@ static void iwl_bg_request_scan(struct work_struct *data)
        rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]);
        scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags);
 
+       /* In power save mode use one chain, otherwise use all chains */
+       if (test_bit(STATUS_POWER_PMI, &priv->status)) {
+               /* rx_ant has been set to all valid chains previously */
+               active_chains = rx_ant &
+                               ((u8)(priv->chain_noise_data.active_chains));
+               if (!active_chains)
+                       active_chains = rx_ant;
+
+               IWL_DEBUG_SCAN(priv, "chain_noise_data.active_chains: %u\n",
+                               priv->chain_noise_data.active_chains);
+
+               rx_ant = first_antenna(active_chains);
+       }
        /* MIMO is not used here, but value is required */
-       rx_chain |= ANT_ABC << RXON_RX_CHAIN_VALID_POS;
-       rx_chain |= ANT_ABC << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS;
+       rx_chain |= priv->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS;
+       rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS;
        rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS;
        rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS;
        scan->rx_chain = cpu_to_le16(rx_chain);
-       cmd_len = iwl_fill_probe_req(priv,
-                               (struct ieee80211_mgmt *)scan->data,
-                               priv->scan_request->ie,
-                               priv->scan_request->ie_len,
-                               IWL_MAX_SCAN_SIZE - sizeof(*scan));
+       if (!priv->is_internal_short_scan) {
+               cmd_len = iwl_fill_probe_req(priv,
+                                       (struct ieee80211_mgmt *)scan->data,
+                                       priv->scan_request->ie,
+                                       priv->scan_request->ie_len,
+                                       IWL_MAX_SCAN_SIZE - sizeof(*scan));
+       } else {
+               cmd_len = iwl_fill_probe_req(priv,
+                                       (struct ieee80211_mgmt *)scan->data,
+                                       NULL, 0,
+                                       IWL_MAX_SCAN_SIZE - sizeof(*scan));
 
+       }
        scan->tx_cmd.len = cpu_to_le16(cmd_len);
-
        if (iwl_is_monitor_mode(priv))
                scan->filter_flags = RXON_FILTER_PROMISC_MSK;
 
        scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK |
                               RXON_FILTER_BCON_AWARE_MSK);
 
-       scan->channel_count =
-               iwl_get_channels_for_scan(priv, band, is_active, n_probes,
-                       (void *)&scan->data[le16_to_cpu(scan->tx_cmd.len)]);
-
+       if (priv->is_internal_short_scan) {
+               scan->channel_count =
+                       iwl_get_single_channel_for_scan(priv, band,
+                               (void *)&scan->data[le16_to_cpu(
+                               scan->tx_cmd.len)]);
+       } else {
+               scan->channel_count =
+                       iwl_get_channels_for_scan(priv, band,
+                               is_active, n_probes,
+                               (void *)&scan->data[le16_to_cpu(
+                               scan->tx_cmd.len)]);
+       }
        if (scan->channel_count == 0) {
                IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count);
                goto done;
@@ -769,8 +907,7 @@ static void iwl_bg_request_scan(struct work_struct *data)
        scan->len = cpu_to_le16(cmd.len);
 
        set_bit(STATUS_SCAN_HW, &priv->status);
-       ret = iwl_send_cmd_sync(priv, &cmd);
-       if (ret)
+       if (iwl_send_cmd_sync(priv, &cmd))
                goto done;
 
        queue_delayed_work(priv->workqueue, &priv->scan_check,
@@ -796,7 +933,8 @@ void iwl_bg_abort_scan(struct work_struct *work)
 {
        struct iwl_priv *priv = container_of(work, struct iwl_priv, abort_scan);
 
-       if (!iwl_is_ready(priv))
+       if (!test_bit(STATUS_READY, &priv->status) ||
+           !test_bit(STATUS_GEO_CONFIGURED, &priv->status))
                return;
 
        mutex_lock(&priv->mutex);
@@ -817,7 +955,12 @@ void iwl_bg_scan_completed(struct work_struct *work)
 
        cancel_delayed_work(&priv->scan_check);
 
-       ieee80211_scan_completed(priv->hw, false);
+       if (!priv->is_internal_short_scan)
+               ieee80211_scan_completed(priv->hw, false);
+       else {
+               priv->is_internal_short_scan = false;
+               IWL_DEBUG_SCAN(priv, "internal short scan completed\n");
+       }
 
        if (test_bit(STATUS_EXIT_PENDING, &priv->status))
                return;