Jesse Keating 2f82dda
Back-port of the following upstream commit...
Jesse Keating 2f82dda
Jesse Keating 2f82dda
commit afbdd69af0e6a0c40676d4d4b94a0a4414708eaa
Jesse Keating 2f82dda
Author: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Jesse Keating 2f82dda
Date:   Fri Jan 22 14:22:43 2010 -0800
Jesse Keating 2f82dda
Jesse Keating 2f82dda
    iwlwifi: add function to reset/tune radio if needed
Jesse Keating 2f82dda
    
Jesse Keating 2f82dda
    Adding "radio reset" function to help reset and stabilize the radio.
Jesse Keating 2f82dda
    
Jesse Keating 2f82dda
    During normal operation, sometime for unknown reason, radio encounter
Jesse Keating 2f82dda
    problem and can not recover by itself; the best way to
Jesse Keating 2f82dda
    recover from it is to reset and re-tune the radio. Currently, there is
Jesse Keating 2f82dda
    no RF reset command available, but since radio will get reset when
Jesse Keating 2f82dda
    switching channel, use internal hw scan request to force radio
Jesse Keating 2f82dda
    reset and get back to normal operation state.
Jesse Keating 2f82dda
    
Jesse Keating 2f82dda
    The internal hw scan will only perform passive scan on the first
Jesse Keating 2f82dda
    available channel (not the channel being used) in associated state. The
Jesse Keating 2f82dda
    request should be ignored if already performing scan operation or STA is
Jesse Keating 2f82dda
    not in associated state.
Jesse Keating 2f82dda
    
Jesse Keating 2f82dda
    Also include an "internal_scan" debugfs file to help trigger the
Jesse Keating 2f82dda
    internal scan from user mode.
Jesse Keating 2f82dda
    
Jesse Keating 2f82dda
    Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Jesse Keating 2f82dda
    Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Jesse Keating 2f82dda
    Signed-off-by: John W. Linville <linville@tuxdriver.com>
Jesse Keating 2f82dda
Jesse Keating 2f82dda
diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c.orig	2010-03-22 10:23:59.000000000 -0400
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c	2010-03-22 11:26:18.000000000 -0400
Jesse Keating 2f82dda
@@ -3035,6 +3035,30 @@ void iwl_update_stats(struct iwl_priv *p
Jesse Keating 2f82dda
 EXPORT_SYMBOL(iwl_update_stats);
Jesse Keating 2f82dda
 #endif
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
+void iwl_force_rf_reset(struct iwl_priv *priv)
Jesse Keating 2f82dda
+{
Jesse Keating 2f82dda
+	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
Jesse Keating 2f82dda
+		return;
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+	if (!iwl_is_associated(priv)) {
Jesse Keating 2f82dda
+		IWL_DEBUG_SCAN(priv, "force reset rejected: not associated\n");
Jesse Keating 2f82dda
+		return;
Jesse Keating 2f82dda
+	}
Jesse Keating 2f82dda
+	/*
Jesse Keating 2f82dda
+	 * There is no easy and better way to force reset the radio,
Jesse Keating 2f82dda
+	 * the only known method is switching channel which will force to
Jesse Keating 2f82dda
+	 * reset and tune the radio.
Jesse Keating 2f82dda
+	 * Use internal short scan (single channel) operation to should
Jesse Keating 2f82dda
+	 * achieve this objective.
Jesse Keating 2f82dda
+	 * Driver should reset the radio when number of consecutive missed
Jesse Keating 2f82dda
+	 * beacon, or any other uCode error condition detected.
Jesse Keating 2f82dda
+	 */
Jesse Keating 2f82dda
+	IWL_DEBUG_INFO(priv, "perform radio reset.\n");
Jesse Keating 2f82dda
+	iwl_internal_short_hw_scan(priv);
Jesse Keating 2f82dda
+	return;
Jesse Keating 2f82dda
+}
Jesse Keating 2f82dda
+EXPORT_SYMBOL(iwl_force_rf_reset);
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
 #ifdef CONFIG_PM
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state)
Jesse Keating 2f82dda
diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig	2010-03-22 10:23:59.000000000 -0400
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h	2010-03-22 11:26:18.000000000 -0400
Jesse Keating 2f82dda
@@ -461,6 +461,8 @@ void iwl_init_scan_params(struct iwl_pri
Jesse Keating 2f82dda
 int iwl_scan_cancel(struct iwl_priv *priv);
Jesse Keating 2f82dda
 int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms);
Jesse Keating 2f82dda
 int iwl_mac_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req);
Jesse Keating 2f82dda
+int iwl_internal_short_hw_scan(struct iwl_priv *priv);
Jesse Keating 2f82dda
+void iwl_force_rf_reset(struct iwl_priv *priv);
Jesse Keating 2f82dda
 u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame,
Jesse Keating 2f82dda
 		       const u8 *ie, int ie_len, int left);
Jesse Keating 2f82dda
 void iwl_setup_rx_scan_handlers(struct iwl_priv *priv);
Jesse Keating 2f82dda
diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debugfs.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debugfs.c
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debugfs.c.orig	2009-12-02 22:51:21.000000000 -0500
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debugfs.c	2010-03-22 11:33:02.000000000 -0400
Jesse Keating 2f82dda
@@ -1614,6 +1614,27 @@ static ssize_t iwl_dbgfs_tx_power_read(s
Jesse Keating 2f82dda
 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
Jesse Keating 2f82dda
 }
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
+static ssize_t iwl_dbgfs_internal_scan_write(struct file *file,
Jesse Keating 2f82dda
+					const char __user *user_buf,
Jesse Keating 2f82dda
+					size_t count, loff_t *ppos)
Jesse Keating 2f82dda
+{
Jesse Keating 2f82dda
+	struct iwl_priv *priv = file->private_data;
Jesse Keating 2f82dda
+	char buf[8];
Jesse Keating 2f82dda
+	int buf_size;
Jesse Keating 2f82dda
+	int scan;
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+	memset(buf, 0, sizeof(buf));
Jesse Keating 2f82dda
+	buf_size = min(count, sizeof(buf) -  1);
Jesse Keating 2f82dda
+	if (copy_from_user(buf, user_buf, buf_size))
Jesse Keating 2f82dda
+		return -EFAULT;
Jesse Keating 2f82dda
+	if (sscanf(buf, "%d", &scan) != 1)
Jesse Keating 2f82dda
+		return -EINVAL;
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+	iwl_internal_short_hw_scan(priv);
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+	return count;
Jesse Keating 2f82dda
+}
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
 DEBUGFS_READ_WRITE_FILE_OPS(rx_statistics);
Jesse Keating 2f82dda
 DEBUGFS_READ_WRITE_FILE_OPS(tx_statistics);
Jesse Keating 2f82dda
 DEBUGFS_READ_WRITE_FILE_OPS(traffic_log);
Jesse Keating 2f82dda
@@ -1625,6 +1646,7 @@ DEBUGFS_READ_FILE_OPS(ucode_general_stat
Jesse Keating 2f82dda
 DEBUGFS_READ_FILE_OPS(sensitivity);
Jesse Keating 2f82dda
 DEBUGFS_READ_FILE_OPS(chain_noise);
Jesse Keating 2f82dda
 DEBUGFS_READ_FILE_OPS(tx_power);
Jesse Keating 2f82dda
+DEBUGFS_WRITE_FILE_OPS(internal_scan);
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 /*
Jesse Keating 2f82dda
  * Create the debugfs files and directories
Jesse Keating 2f82dda
@@ -1674,6 +1696,7 @@ int iwl_dbgfs_register(struct iwl_priv *
Jesse Keating 2f82dda
 	DEBUGFS_ADD_FILE(rx_queue, debug);
Jesse Keating 2f82dda
 	DEBUGFS_ADD_FILE(tx_queue, debug);
Jesse Keating 2f82dda
 	DEBUGFS_ADD_FILE(tx_power, debug);
Jesse Keating 2f82dda
+	DEBUGFS_ADD_FILE(internal_scan, debug);
Jesse Keating 2f82dda
 	if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) {
Jesse Keating 2f82dda
 		DEBUGFS_ADD_FILE(ucode_rx_stats, debug);
Jesse Keating 2f82dda
 		DEBUGFS_ADD_FILE(ucode_tx_stats, debug);
Jesse Keating 2f82dda
@@ -1728,6 +1751,7 @@ void iwl_dbgfs_unregister(struct iwl_pri
Jesse Keating 2f82dda
 	DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_rx_queue);
Jesse Keating 2f82dda
 	DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_queue);
Jesse Keating 2f82dda
 	DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_power);
Jesse Keating 2f82dda
+	DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_internal_scan);
Jesse Keating 2f82dda
 	if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) {
Jesse Keating 2f82dda
 		DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.
Jesse Keating 2f82dda
 			file_ucode_rx_stats);
Jesse Keating 2f82dda
diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debug.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debug.h
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debug.h.orig	2009-12-02 22:51:21.000000000 -0500
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debug.h	2010-03-22 11:27:31.000000000 -0400
Jesse Keating 2f82dda
@@ -108,6 +108,7 @@ struct iwl_debugfs {
Jesse Keating 2f82dda
 		struct dentry *file_sensitivity;
Jesse Keating 2f82dda
 		struct dentry *file_chain_noise;
Jesse Keating 2f82dda
 		struct dentry *file_tx_power;
Jesse Keating 2f82dda
+		struct dentry *file_internal_scan;
Jesse Keating 2f82dda
 	} dbgfs_debug_files;
Jesse Keating 2f82dda
 	u32 sram_offset;
Jesse Keating 2f82dda
 	u32 sram_len;
Jesse Keating 2f82dda
diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig	2010-03-22 10:23:59.000000000 -0400
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h	2010-03-22 11:26:18.000000000 -0400
Jesse Keating 2f82dda
@@ -1016,6 +1016,7 @@ struct iwl_priv {
Jesse Keating 2f82dda
 	void *scan;
Jesse Keating 2f82dda
 	int scan_bands;
Jesse Keating 2f82dda
 	struct cfg80211_scan_request *scan_request;
Jesse Keating 2f82dda
+	bool is_internal_short_scan;
Jesse Keating 2f82dda
 	u8 scan_tx_ant[IEEE80211_NUM_BANDS];
Jesse Keating 2f82dda
 	u8 mgmt_tx_ant;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c.orig	2009-12-02 22:51:21.000000000 -0500
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c	2010-03-22 11:26:18.000000000 -0400
Jesse Keating 2f82dda
@@ -316,6 +316,72 @@ u16 iwl_get_passive_dwell_time(struct iw
Jesse Keating 2f82dda
 }
Jesse Keating 2f82dda
 EXPORT_SYMBOL(iwl_get_passive_dwell_time);
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
+static int iwl_get_single_channel_for_scan(struct iwl_priv *priv,
Jesse Keating 2f82dda
+				     enum ieee80211_band band,
Jesse Keating 2f82dda
+				     struct iwl_scan_channel *scan_ch)
Jesse Keating 2f82dda
+{
Jesse Keating 2f82dda
+	const struct ieee80211_supported_band *sband;
Jesse Keating 2f82dda
+	const struct iwl_channel_info *ch_info;
Jesse Keating 2f82dda
+	u16 passive_dwell = 0;
Jesse Keating 2f82dda
+	u16 active_dwell = 0;
Jesse Keating 2f82dda
+	int i, added = 0;
Jesse Keating 2f82dda
+	u16 channel = 0;
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+	sband = iwl_get_hw_mode(priv, band);
Jesse Keating 2f82dda
+	if (!sband) {
Jesse Keating 2f82dda
+		IWL_ERR(priv, "invalid band\n");
Jesse Keating 2f82dda
+		return added;
Jesse Keating 2f82dda
+	}
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+	active_dwell = iwl_get_active_dwell_time(priv, band, 0);
Jesse Keating 2f82dda
+	passive_dwell = iwl_get_passive_dwell_time(priv, band);
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+	if (passive_dwell <= active_dwell)
Jesse Keating 2f82dda
+		passive_dwell = active_dwell + 1;
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+	/* only scan single channel, good enough to reset the RF */
Jesse Keating 2f82dda
+	/* pick the first valid not in-use channel */
Jesse Keating 2f82dda
+	if (band == IEEE80211_BAND_5GHZ) {
Jesse Keating 2f82dda
+		for (i = 14; i < priv->channel_count; i++) {
Jesse Keating 2f82dda
+			if (priv->channel_info[i].channel !=
Jesse Keating 2f82dda
+			    le16_to_cpu(priv->staging_rxon.channel)) {
Jesse Keating 2f82dda
+				channel = priv->channel_info[i].channel;
Jesse Keating 2f82dda
+				ch_info = iwl_get_channel_info(priv,
Jesse Keating 2f82dda
+					band, channel);
Jesse Keating 2f82dda
+				if (is_channel_valid(ch_info))
Jesse Keating 2f82dda
+					break;
Jesse Keating 2f82dda
+			}
Jesse Keating 2f82dda
+		}
Jesse Keating 2f82dda
+	} else {
Jesse Keating 2f82dda
+		for (i = 0; i < 14; i++) {
Jesse Keating 2f82dda
+			if (priv->channel_info[i].channel !=
Jesse Keating 2f82dda
+			    le16_to_cpu(priv->staging_rxon.channel)) {
Jesse Keating 2f82dda
+					channel =
Jesse Keating 2f82dda
+						priv->channel_info[i].channel;
Jesse Keating 2f82dda
+					ch_info = iwl_get_channel_info(priv,
Jesse Keating 2f82dda
+						band, channel);
Jesse Keating 2f82dda
+					if (is_channel_valid(ch_info))
Jesse Keating 2f82dda
+						break;
Jesse Keating 2f82dda
+			}
Jesse Keating 2f82dda
+		}
Jesse Keating 2f82dda
+	}
Jesse Keating 2f82dda
+	if (channel) {
Jesse Keating 2f82dda
+		scan_ch->channel = cpu_to_le16(channel);
Jesse Keating 2f82dda
+		scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
Jesse Keating 2f82dda
+		scan_ch->active_dwell = cpu_to_le16(active_dwell);
Jesse Keating 2f82dda
+		scan_ch->passive_dwell = cpu_to_le16(passive_dwell);
Jesse Keating 2f82dda
+		/* Set txpower levels to defaults */
Jesse Keating 2f82dda
+		scan_ch->dsp_atten = 110;
Jesse Keating 2f82dda
+		if (band == IEEE80211_BAND_5GHZ)
Jesse Keating 2f82dda
+			scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
Jesse Keating 2f82dda
+		else
Jesse Keating 2f82dda
+			scan_ch->tx_gain = ((1 << 5) | (5 << 3));
Jesse Keating 2f82dda
+		added++;
Jesse Keating 2f82dda
+	} else
Jesse Keating 2f82dda
+		IWL_ERR(priv, "no valid channel found\n");
Jesse Keating 2f82dda
+	return added;
Jesse Keating 2f82dda
+}
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
 static int iwl_get_channels_for_scan(struct iwl_priv *priv,
Jesse Keating 2f82dda
 				     enum ieee80211_band band,
Jesse Keating 2f82dda
 				     u8 is_active, u8 n_probes,
Jesse Keating 2f82dda
@@ -422,6 +488,7 @@ static int iwl_scan_initiate(struct iwl_
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 	IWL_DEBUG_INFO(priv, "Starting scan...\n");
Jesse Keating 2f82dda
 	set_bit(STATUS_SCANNING, &priv->status);
Jesse Keating 2f82dda
+	priv->is_internal_short_scan = false;
Jesse Keating 2f82dda
 	priv->scan_start = jiffies;
Jesse Keating 2f82dda
 	priv->scan_pass_start = priv->scan_start;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
@@ -489,6 +556,45 @@ out_unlock:
Jesse Keating 2f82dda
 }
Jesse Keating 2f82dda
 EXPORT_SYMBOL(iwl_mac_hw_scan);
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
+/*
Jesse Keating 2f82dda
+ * internal short scan, this function should only been called while associated.
Jesse Keating 2f82dda
+ * It will reset and tune the radio to prevent possible RF related problem
Jesse Keating 2f82dda
+ */
Jesse Keating 2f82dda
+int iwl_internal_short_hw_scan(struct iwl_priv *priv)
Jesse Keating 2f82dda
+{
Jesse Keating 2f82dda
+	int ret = 0;
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+	if (!iwl_is_ready_rf(priv)) {
Jesse Keating 2f82dda
+		ret = -EIO;
Jesse Keating 2f82dda
+		IWL_DEBUG_SCAN(priv, "not ready or exit pending\n");
Jesse Keating 2f82dda
+		goto out;
Jesse Keating 2f82dda
+	}
Jesse Keating 2f82dda
+	if (test_bit(STATUS_SCANNING, &priv->status)) {
Jesse Keating 2f82dda
+		IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
Jesse Keating 2f82dda
+		ret = -EAGAIN;
Jesse Keating 2f82dda
+		goto out;
Jesse Keating 2f82dda
+	}
Jesse Keating 2f82dda
+	if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
Jesse Keating 2f82dda
+		IWL_DEBUG_SCAN(priv, "Scan request while abort pending\n");
Jesse Keating 2f82dda
+		ret = -EAGAIN;
Jesse Keating 2f82dda
+		goto out;
Jesse Keating 2f82dda
+	}
Jesse Keating 2f82dda
+	priv->scan_bands = 0;
Jesse Keating 2f82dda
+	if (priv->band == IEEE80211_BAND_5GHZ)
Jesse Keating 2f82dda
+		priv->scan_bands |= BIT(IEEE80211_BAND_5GHZ);
Jesse Keating 2f82dda
+	else
Jesse Keating 2f82dda
+		priv->scan_bands |= BIT(IEEE80211_BAND_2GHZ);
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+	IWL_DEBUG_SCAN(priv, "Start internal short scan...\n");
Jesse Keating 2f82dda
+	set_bit(STATUS_SCANNING, &priv->status);
Jesse Keating 2f82dda
+	priv->is_internal_short_scan = true;
Jesse Keating 2f82dda
+	queue_work(priv->workqueue, &priv->request_scan);
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+out:
Jesse Keating 2f82dda
+	return ret;
Jesse Keating 2f82dda
+}
Jesse Keating 2f82dda
+EXPORT_SYMBOL(iwl_internal_short_hw_scan);
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
 #define IWL_SCAN_CHECK_WATCHDOG (7 * HZ)
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 void iwl_bg_scan_check(struct work_struct *data)
Jesse Keating 2f82dda
@@ -552,7 +658,8 @@ u16 iwl_fill_probe_req(struct iwl_priv *
Jesse Keating 2f82dda
 	if (WARN_ON(left < ie_len))
Jesse Keating 2f82dda
 		return len;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
-	memcpy(pos, ies, ie_len);
Jesse Keating 2f82dda
+	if (ies)
Jesse Keating 2f82dda
+		memcpy(pos, ies, ie_len);
Jesse Keating 2f82dda
 	len += ie_len;
Jesse Keating 2f82dda
 	left -= ie_len;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
@@ -654,7 +761,6 @@ static void iwl_bg_request_scan(struct w
Jesse Keating 2f82dda
 		unsigned long flags;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 		IWL_DEBUG_INFO(priv, "Scanning while associated...\n");
Jesse Keating 2f82dda
-
Jesse Keating 2f82dda
 		spin_lock_irqsave(&priv->lock, flags);
Jesse Keating 2f82dda
 		interval = priv->beacon_int;
Jesse Keating 2f82dda
 		spin_unlock_irqrestore(&priv->lock, flags);
Jesse Keating 2f82dda
@@ -672,7 +778,9 @@ static void iwl_bg_request_scan(struct w
Jesse Keating 2f82dda
 			       scan_suspend_time, interval);
Jesse Keating 2f82dda
 	}
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
-	if (priv->scan_request->n_ssids) {
Jesse Keating 2f82dda
+	if (priv->is_internal_short_scan) {
Jesse Keating 2f82dda
+		IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n");
Jesse Keating 2f82dda
+	} else if (priv->scan_request->n_ssids) {
Jesse Keating 2f82dda
 		int i, p = 0;
Jesse Keating 2f82dda
 		IWL_DEBUG_SCAN(priv, "Kicking off active scan\n");
Jesse Keating 2f82dda
 		for (i = 0; i < priv->scan_request->n_ssids; i++) {
Jesse Keating 2f82dda
@@ -740,24 +848,38 @@ static void iwl_bg_request_scan(struct w
Jesse Keating 2f82dda
 	rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS;
Jesse Keating 2f82dda
 	rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS;
Jesse Keating 2f82dda
 	scan->rx_chain = cpu_to_le16(rx_chain);
Jesse Keating 2f82dda
-	cmd_len = iwl_fill_probe_req(priv,
Jesse Keating 2f82dda
-				(struct ieee80211_mgmt *)scan->data,
Jesse Keating 2f82dda
-				priv->scan_request->ie,
Jesse Keating 2f82dda
-				priv->scan_request->ie_len,
Jesse Keating 2f82dda
-				IWL_MAX_SCAN_SIZE - sizeof(*scan));
Jesse Keating 2f82dda
+	if (!priv->is_internal_short_scan) {
Jesse Keating 2f82dda
+		cmd_len = iwl_fill_probe_req(priv,
Jesse Keating 2f82dda
+					(struct ieee80211_mgmt *)scan->data,
Jesse Keating 2f82dda
+					priv->scan_request->ie,
Jesse Keating 2f82dda
+					priv->scan_request->ie_len,
Jesse Keating 2f82dda
+					IWL_MAX_SCAN_SIZE - sizeof(*scan));
Jesse Keating 2f82dda
+	} else {
Jesse Keating 2f82dda
+		cmd_len = iwl_fill_probe_req(priv,
Jesse Keating 2f82dda
+					(struct ieee80211_mgmt *)scan->data,
Jesse Keating 2f82dda
+					NULL, 0,
Jesse Keating 2f82dda
+					IWL_MAX_SCAN_SIZE - sizeof(*scan));
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
+	}
Jesse Keating 2f82dda
 	scan->tx_cmd.len = cpu_to_le16(cmd_len);
Jesse Keating 2f82dda
-
Jesse Keating 2f82dda
 	if (iwl_is_monitor_mode(priv))
Jesse Keating 2f82dda
 		scan->filter_flags = RXON_FILTER_PROMISC_MSK;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 	scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK |
Jesse Keating 2f82dda
 			       RXON_FILTER_BCON_AWARE_MSK);
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
-	scan->channel_count =
Jesse Keating 2f82dda
-		iwl_get_channels_for_scan(priv, band, is_active, n_probes,
Jesse Keating 2f82dda
-			(void *)&scan->data[le16_to_cpu(scan->tx_cmd.len)]);
Jesse Keating 2f82dda
-
Jesse Keating 2f82dda
+	if (priv->is_internal_short_scan) {
Jesse Keating 2f82dda
+		scan->channel_count =
Jesse Keating 2f82dda
+			iwl_get_single_channel_for_scan(priv, band,
Jesse Keating 2f82dda
+				(void *)&scan->data[le16_to_cpu(
Jesse Keating 2f82dda
+				scan->tx_cmd.len)]);
Jesse Keating 2f82dda
+	} else {
Jesse Keating 2f82dda
+		scan->channel_count =
Jesse Keating 2f82dda
+			iwl_get_channels_for_scan(priv, band,
Jesse Keating 2f82dda
+				is_active, n_probes,
Jesse Keating 2f82dda
+				(void *)&scan->data[le16_to_cpu(
Jesse Keating 2f82dda
+				scan->tx_cmd.len)]);
Jesse Keating 2f82dda
+	}
Jesse Keating 2f82dda
 	if (scan->channel_count == 0) {
Jesse Keating 2f82dda
 		IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count);
Jesse Keating 2f82dda
 		goto done;
Jesse Keating 2f82dda
@@ -818,7 +940,12 @@ void iwl_bg_scan_completed(struct work_s
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 	cancel_delayed_work(&priv->scan_check);
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
-	ieee80211_scan_completed(priv->hw, false);
Jesse Keating 2f82dda
+	if (!priv->is_internal_short_scan)
Jesse Keating 2f82dda
+		ieee80211_scan_completed(priv->hw, false);
Jesse Keating 2f82dda
+	else {
Jesse Keating 2f82dda
+		priv->is_internal_short_scan = false;
Jesse Keating 2f82dda
+		IWL_DEBUG_SCAN(priv, "internal short scan completed\n");
Jesse Keating 2f82dda
+	}
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
Jesse Keating 2f82dda
 		return;