iwl-scan.c revision 1b63ba8a86c85524a8d7e5953b314ce71ebcb9c9
12a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler/******************************************************************************
22a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler *
32a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * GPL LICENSE SUMMARY
42a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler *
52a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * Copyright(c) 2008 Intel Corporation. All rights reserved.
62a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler *
72a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * This program is free software; you can redistribute it and/or modify
82a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * it under the terms of version 2 of the GNU General Public License as
92a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * published by the Free Software Foundation.
102a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler *
112a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * This program is distributed in the hope that it will be useful, but
122a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * WITHOUT ANY WARRANTY; without even the implied warranty of
132a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
142a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * General Public License for more details.
152a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler *
162a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * You should have received a copy of the GNU General Public License
172a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * along with this program; if not, write to the Free Software
182a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
192a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * USA
202a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler *
212a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * The full GNU General Public License is included in this distribution
222a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * in the file called LICENSE.GPL.
232a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler *
242a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * Contact Information:
252a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * Tomas Winkler <tomas.winkler@intel.com>
262a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
272a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler *****************************************************************************/
282a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#include <net/mac80211.h>
292a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#include <linux/etherdevice.h>
302a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
312a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#include "iwl-eeprom.h"
322a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#include "iwl-dev.h"
332a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#include "iwl-core.h"
342a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#include "iwl-sta.h"
352a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#include "iwl-io.h"
362a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#include "iwl-helpers.h"
372a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
382a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after
392a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * sending probe req.  This should be set long enough to hear probe responses
402a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * from more than one AP.  */
412a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#define IWL_ACTIVE_DWELL_TIME_24    (20)       /* all times in msec */
422a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#define IWL_ACTIVE_DWELL_TIME_52    (10)
432a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
442a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler/* For faster active scanning, scan will move to the next channel if fewer than
452a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * PLCP_QUIET_THRESH packets are heard on this channel within
462a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * ACTIVE_QUIET_TIME after sending probe request.  This shortens the dwell
472a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * time if it's a quiet channel (nothing responded to our probe, and there's
482a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * no other traffic).
492a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * Disable "quiet" feature by setting PLCP_QUIET_THRESH to 0. */
502a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#define IWL_PLCP_QUIET_THRESH       __constant_cpu_to_le16(1)  /* packets */
512a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#define IWL_ACTIVE_QUIET_TIME       __constant_cpu_to_le16(5)  /* msec */
522a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
532a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel.
542a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * Must be set longer than active dwell time.
552a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * For the most reliable scan, set > AP beacon interval (typically 100msec). */
562a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#define IWL_PASSIVE_DWELL_TIME_24   (20)       /* all times in msec */
572a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#define IWL_PASSIVE_DWELL_TIME_52   (10)
582a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#define IWL_PASSIVE_DWELL_BASE      (100)
592a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#define IWL_CHANNEL_TUNE_TIME       5
602a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
61f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winklerstatic int scan_tx_ant[3] = {
62f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	RATE_MCS_ANT_A_MSK, RATE_MCS_ANT_B_MSK, RATE_MCS_ANT_C_MSK
63f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler};
642a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
652a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerstatic int iwl_is_empty_essid(const char *essid, int essid_len)
662a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
672a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	/* Single white space is for Linksys APs */
682a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (essid_len == 1 && essid[0] == ' ')
692a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return 1;
702a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
712a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	/* Otherwise, if the entire essid is 0, we assume it is hidden */
722a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	while (essid_len) {
732a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		essid_len--;
742a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		if (essid[essid_len] != '\0')
752a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			return 0;
762a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
772a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
782a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	return 1;
792a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
802a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
812a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
822a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
832a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerconst char *iwl_escape_essid(const char *essid, u8 essid_len)
842a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
852a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	static char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
862a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	const char *s = essid;
872a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	char *d = escaped;
882a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
892a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (iwl_is_empty_essid(essid, essid_len)) {
902a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		memcpy(escaped, "<hidden>", sizeof("<hidden>"));
912a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return escaped;
922a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
932a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
942a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	essid_len = min(essid_len, (u8) IW_ESSID_MAX_SIZE);
952a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	while (essid_len--) {
962a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		if (*s == '\0') {
972a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			*d++ = '\\';
982a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			*d++ = '0';
992a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			s++;
1002a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		} else
1012a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			*d++ = *s++;
1022a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
1032a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	*d = '\0';
1042a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	return escaped;
1052a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
1062a421b91d6fe89e27ded7544a25449c0b050098fTomas WinklerEXPORT_SYMBOL(iwl_escape_essid);
1072a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
1082a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler/**
1092a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * iwl_scan_cancel - Cancel any currently executing HW scan
1102a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler *
1112a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * NOTE: priv->mutex is not required before calling this function
1122a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler */
1132a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerint iwl_scan_cancel(struct iwl_priv *priv)
1142a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
1152a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (!test_bit(STATUS_SCAN_HW, &priv->status)) {
1162a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		clear_bit(STATUS_SCANNING, &priv->status);
1172a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return 0;
1182a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
1192a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
1202a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (test_bit(STATUS_SCANNING, &priv->status)) {
1212a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		if (!test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
1222a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			IWL_DEBUG_SCAN("Queuing scan abort.\n");
1232a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			set_bit(STATUS_SCAN_ABORTING, &priv->status);
1242a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			queue_work(priv->workqueue, &priv->abort_scan);
1252a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
1262a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		} else
1272a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			IWL_DEBUG_SCAN("Scan abort already in progress.\n");
1282a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
1292a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return test_bit(STATUS_SCANNING, &priv->status);
1302a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
1312a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
1322a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	return 0;
1332a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
1342a421b91d6fe89e27ded7544a25449c0b050098fTomas WinklerEXPORT_SYMBOL(iwl_scan_cancel);
1352a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler/**
1362a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * iwl_scan_cancel_timeout - Cancel any currently executing HW scan
1372a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * @ms: amount of time to wait (in milliseconds) for scan to abort
1382a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler *
1392a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * NOTE: priv->mutex must be held before calling this function
1402a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler */
1412a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerint iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms)
1422a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
1432a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	unsigned long now = jiffies;
1442a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	int ret;
1452a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
1462a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	ret = iwl_scan_cancel(priv);
1472a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (ret && ms) {
1482a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		mutex_unlock(&priv->mutex);
1492a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		while (!time_after(jiffies, now + msecs_to_jiffies(ms)) &&
1502a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler				test_bit(STATUS_SCANNING, &priv->status))
1512a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			msleep(1);
1522a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		mutex_lock(&priv->mutex);
1532a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
1542a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return test_bit(STATUS_SCANNING, &priv->status);
1552a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
1562a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
1572a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	return ret;
1582a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
1592a421b91d6fe89e27ded7544a25449c0b050098fTomas WinklerEXPORT_SYMBOL(iwl_scan_cancel_timeout);
1602a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
1612a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerstatic int iwl_send_scan_abort(struct iwl_priv *priv)
1622a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
1632a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	int ret = 0;
1642a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	struct iwl_rx_packet *res;
1652a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	struct iwl_host_cmd cmd = {
1662a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		.id = REPLY_SCAN_ABORT_CMD,
1672a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		.meta.flags = CMD_WANT_SKB,
1682a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	};
1692a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
1702a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	/* If there isn't a scan actively going on in the hardware
1712a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	 * then we are in between scan bands and not actually
1722a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	 * actively scanning, so don't send the abort command */
1732a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (!test_bit(STATUS_SCAN_HW, &priv->status)) {
1742a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		clear_bit(STATUS_SCAN_ABORTING, &priv->status);
1752a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return 0;
1762a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
1772a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
1782a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	ret = iwl_send_cmd_sync(priv, &cmd);
1792a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (ret) {
1802a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		clear_bit(STATUS_SCAN_ABORTING, &priv->status);
1812a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return ret;
1822a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
1832a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
1842a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	res = (struct iwl_rx_packet *)cmd.meta.u.skb->data;
1852a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (res->u.status != CAN_ABORT_STATUS) {
1862a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		/* The scan abort will return 1 for success or
1872a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		 * 2 for "failure".  A failure condition can be
1882a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		 * due to simply not being in an active scan which
1892a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		 * can occur if we send the scan abort before we
1902a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		 * the microcode has notified us that a scan is
1912a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		 * completed. */
1922a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		IWL_DEBUG_INFO("SCAN_ABORT returned %d.\n", res->u.status);
1932a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		clear_bit(STATUS_SCAN_ABORTING, &priv->status);
1942a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		clear_bit(STATUS_SCAN_HW, &priv->status);
1952a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
1962a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
1972a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	dev_kfree_skb_any(cmd.meta.u.skb);
1982a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
1992a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	return ret;
2002a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
2012a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
2022a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
2032a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler/* Service response to REPLY_SCAN_CMD (0x80) */
2042a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerstatic void iwl_rx_reply_scan(struct iwl_priv *priv,
2052a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			      struct iwl_rx_mem_buffer *rxb)
2062a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
2072a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#ifdef CONFIG_IWLWIFI_DEBUG
2082a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
2092a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	struct iwl_scanreq_notification *notif =
2102a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	    (struct iwl_scanreq_notification *)pkt->u.raw;
2112a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
2122a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	IWL_DEBUG_RX("Scan request status = 0x%x\n", notif->status);
2132a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#endif
2142a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
2152a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
2162a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler/* Service SCAN_START_NOTIFICATION (0x82) */
2172a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerstatic void iwl_rx_scan_start_notif(struct iwl_priv *priv,
2182a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler				    struct iwl_rx_mem_buffer *rxb)
2192a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
2202a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
2212a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	struct iwl_scanstart_notification *notif =
2222a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	    (struct iwl_scanstart_notification *)pkt->u.raw;
2232a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	priv->scan_start_tsf = le32_to_cpu(notif->tsf_low);
2242a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	IWL_DEBUG_SCAN("Scan start: "
2252a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       "%d [802.11%s] "
2262a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n",
2272a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       notif->channel,
2282a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       notif->band ? "bg" : "a",
2292a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       notif->tsf_high,
2302a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       notif->tsf_low, notif->status, notif->beacon_timer);
2312a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
2322a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
2332a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler/* Service SCAN_RESULTS_NOTIFICATION (0x83) */
2342a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerstatic void iwl_rx_scan_results_notif(struct iwl_priv *priv,
2352a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler				      struct iwl_rx_mem_buffer *rxb)
2362a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
2372a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#ifdef CONFIG_IWLWIFI_DEBUG
2382a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
2392a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	struct iwl_scanresults_notification *notif =
2402a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	    (struct iwl_scanresults_notification *)pkt->u.raw;
2412a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
2422a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	IWL_DEBUG_SCAN("Scan ch.res: "
2432a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       "%d [802.11%s] "
2442a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       "(TSF: 0x%08X:%08X) - %d "
2452a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       "elapsed=%lu usec (%dms since last)\n",
2462a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       notif->channel,
2472a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       notif->band ? "bg" : "a",
2482a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       le32_to_cpu(notif->tsf_high),
2492a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       le32_to_cpu(notif->tsf_low),
2502a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       le32_to_cpu(notif->statistics[0]),
2512a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       le32_to_cpu(notif->tsf_low) - priv->scan_start_tsf,
2522a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       jiffies_to_msecs(elapsed_jiffies
2532a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler					(priv->last_scan_jiffies, jiffies)));
2542a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#endif
2552a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
2562a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	priv->last_scan_jiffies = jiffies;
2572a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	priv->next_scan_jiffies = 0;
2582a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
2592a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
2602a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler/* Service SCAN_COMPLETE_NOTIFICATION (0x84) */
2612a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerstatic void iwl_rx_scan_complete_notif(struct iwl_priv *priv,
2622a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler				       struct iwl_rx_mem_buffer *rxb)
2632a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
2642a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
2652a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	struct iwl_scancomplete_notification *scan_notif = (void *)pkt->u.raw;
2662a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
2672a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	IWL_DEBUG_SCAN("Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n",
2682a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       scan_notif->scanned_channels,
2692a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       scan_notif->tsf_low,
2702a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       scan_notif->tsf_high, scan_notif->status);
2712a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
2722a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	/* The HW is no longer scanning */
2732a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	clear_bit(STATUS_SCAN_HW, &priv->status);
2742a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
2752a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	/* The scan completion notification came in, so kill that timer... */
2762a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	cancel_delayed_work(&priv->scan_check);
2772a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
2782a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	IWL_DEBUG_INFO("Scan pass on %sGHz took %dms\n",
2791b63ba8a86c85524a8d7e5953b314ce71ebcb9c9David S. Miller		       (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) ?
2801b63ba8a86c85524a8d7e5953b314ce71ebcb9c9David S. Miller						"2.4" : "5.2",
2812a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       jiffies_to_msecs(elapsed_jiffies
2822a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler					(priv->scan_pass_start, jiffies)));
2832a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
2841b63ba8a86c85524a8d7e5953b314ce71ebcb9c9David S. Miller	/* Remove this scanned band from the list of pending
2851b63ba8a86c85524a8d7e5953b314ce71ebcb9c9David S. Miller	 * bands to scan, band G precedes A in order of scanning
2861b63ba8a86c85524a8d7e5953b314ce71ebcb9c9David S. Miller	 * as seen in iwl_bg_request_scan */
2871b63ba8a86c85524a8d7e5953b314ce71ebcb9c9David S. Miller	if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ))
2881b63ba8a86c85524a8d7e5953b314ce71ebcb9c9David S. Miller		priv->scan_bands &= ~BIT(IEEE80211_BAND_2GHZ);
2891b63ba8a86c85524a8d7e5953b314ce71ebcb9c9David S. Miller	else if (priv->scan_bands &  BIT(IEEE80211_BAND_5GHZ))
2901b63ba8a86c85524a8d7e5953b314ce71ebcb9c9David S. Miller		priv->scan_bands &= ~BIT(IEEE80211_BAND_5GHZ);
2912a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
2922a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	/* If a request to abort was given, or the scan did not succeed
2932a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	 * then we reset the scan state machine and terminate,
2942a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	 * re-queuing another scan if one has been requested */
2952a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
2962a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		IWL_DEBUG_INFO("Aborted scan completed.\n");
2972a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		clear_bit(STATUS_SCAN_ABORTING, &priv->status);
2982a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	} else {
2992a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		/* If there are more bands on this scan pass reschedule */
3001b63ba8a86c85524a8d7e5953b314ce71ebcb9c9David S. Miller		if (priv->scan_bands)
3012a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			goto reschedule;
3022a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
3032a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
3042a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	priv->last_scan_jiffies = jiffies;
3052a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	priv->next_scan_jiffies = 0;
3062a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	IWL_DEBUG_INFO("Setting scan to off\n");
3072a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
3082a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	clear_bit(STATUS_SCANNING, &priv->status);
3092a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
3102a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	IWL_DEBUG_INFO("Scan took %dms\n",
3112a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		jiffies_to_msecs(elapsed_jiffies(priv->scan_start, jiffies)));
3122a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
3132a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	queue_work(priv->workqueue, &priv->scan_completed);
3142a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
3152a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	return;
3162a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
3172a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerreschedule:
3182a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	priv->scan_pass_start = jiffies;
3192a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	queue_work(priv->workqueue, &priv->request_scan);
3202a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
3212a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
3222a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklervoid iwl_setup_rx_scan_handlers(struct iwl_priv *priv)
3232a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
3242a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	/* scan handlers */
3252a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	priv->rx_handlers[REPLY_SCAN_CMD] = iwl_rx_reply_scan;
3262a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	priv->rx_handlers[SCAN_START_NOTIFICATION] = iwl_rx_scan_start_notif;
3272a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	priv->rx_handlers[SCAN_RESULTS_NOTIFICATION] =
3282a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler					iwl_rx_scan_results_notif;
3292a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	priv->rx_handlers[SCAN_COMPLETE_NOTIFICATION] =
3302a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler					iwl_rx_scan_complete_notif;
3312a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
3322a421b91d6fe89e27ded7544a25449c0b050098fTomas WinklerEXPORT_SYMBOL(iwl_setup_rx_scan_handlers);
3332a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
3342a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerstatic inline u16 iwl_get_active_dwell_time(struct iwl_priv *priv,
3352a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler						enum ieee80211_band band)
3362a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
3372a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (band == IEEE80211_BAND_5GHZ)
3382a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return IWL_ACTIVE_DWELL_TIME_52;
3392a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	else
3402a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return IWL_ACTIVE_DWELL_TIME_24;
3412a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
3422a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
3432a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerstatic u16 iwl_get_passive_dwell_time(struct iwl_priv *priv,
3442a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler					  enum ieee80211_band band)
3452a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
3462a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	u16 active = iwl_get_active_dwell_time(priv, band);
3472a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	u16 passive = (band != IEEE80211_BAND_5GHZ) ?
3482a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	    IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_24 :
3492a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	    IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_52;
3502a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
3512a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (iwl_is_associated(priv)) {
3522a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		/* If we're associated, we clamp the maximum passive
3532a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		 * dwell time to be 98% of the beacon interval (minus
3542a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		 * 2 * channel tune time) */
3552a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		passive = priv->beacon_int;
3562a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		if ((passive > IWL_PASSIVE_DWELL_BASE) || !passive)
3572a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			passive = IWL_PASSIVE_DWELL_BASE;
3582a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		passive = (passive * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2;
3592a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
3602a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
3612a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (passive <= active)
3622a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		passive = active + 1;
3632a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
3642a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	return passive;
3652a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
3662a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
3672a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerstatic int iwl_get_channels_for_scan(struct iwl_priv *priv,
3682a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler				     enum ieee80211_band band,
3692a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler				     u8 is_active, u8 direct_mask,
3702a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler				     struct iwl_scan_channel *scan_ch)
3712a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
3722a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	const struct ieee80211_channel *channels = NULL;
3732a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	const struct ieee80211_supported_band *sband;
3742a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	const struct iwl_channel_info *ch_info;
3752a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	u16 passive_dwell = 0;
3762a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	u16 active_dwell = 0;
3772a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	int added, i;
3782a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
3792a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	sband = iwl_get_hw_mode(priv, band);
3802a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (!sband)
3812a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return 0;
3822a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
3832a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	channels = sband->channels;
3842a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
3852a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	active_dwell = iwl_get_active_dwell_time(priv, band);
3862a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	passive_dwell = iwl_get_passive_dwell_time(priv, band);
3872a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
3882a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	for (i = 0, added = 0; i < sband->n_channels; i++) {
3892a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		if (channels[i].flags & IEEE80211_CHAN_DISABLED)
3902a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			continue;
3912a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
3922a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		scan_ch->channel =
3932a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			ieee80211_frequency_to_channel(channels[i].center_freq);
3942a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
395f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		ch_info = iwl_get_channel_info(priv, band, scan_ch->channel);
3962a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		if (!is_channel_valid(ch_info)) {
3971b63ba8a86c85524a8d7e5953b314ce71ebcb9c9David S. Miller			IWL_DEBUG_SCAN("Channel %d is INVALID for this band.\n",
3982a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler				       scan_ch->channel);
3992a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			continue;
4002a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		}
4012a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
4022a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		if (!is_active || is_channel_passive(ch_info) ||
4032a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		    (channels[i].flags & IEEE80211_CHAN_PASSIVE_SCAN))
404f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler			scan_ch->type = 0;
4052a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		else
406f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler			scan_ch->type = 1;
4072a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
4082a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		if (scan_ch->type & 1)
4092a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			scan_ch->type |= (direct_mask << 1);
4102a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
4112a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		scan_ch->active_dwell = cpu_to_le16(active_dwell);
4122a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		scan_ch->passive_dwell = cpu_to_le16(passive_dwell);
4132a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
4142a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		/* Set txpower levels to defaults */
415f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		scan_ch->dsp_atten = 110;
4162a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
4172a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		if (band == IEEE80211_BAND_5GHZ)
418f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler			scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
4192a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		else {
420f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler			scan_ch->tx_gain = ((1 << 5) | (5 << 3));
4212a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			/* NOTE: if we were doing 6Mb OFDM for scans we'd use
4222a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			 * power level:
423f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler			 * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3;
4242a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			 */
4252a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		}
4262a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
4272a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		IWL_DEBUG_SCAN("Scanning %d [%s %d]\n",
4282a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			       scan_ch->channel,
4292a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			       (scan_ch->type & 1) ? "ACTIVE" : "PASSIVE",
4302a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			       (scan_ch->type & 1) ?
4312a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			       active_dwell : passive_dwell);
4322a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
4332a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		scan_ch++;
4342a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		added++;
4352a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
4362a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
4372a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	IWL_DEBUG_SCAN("total channels to scan %d \n", added);
4382a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	return added;
4392a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
4402a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
441f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winklervoid iwl_init_scan_params(struct iwl_priv *priv)
442f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler{
443f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	if (!priv->scan_tx_ant[IEEE80211_BAND_5GHZ])
444f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		priv->scan_tx_ant[IEEE80211_BAND_5GHZ] = RATE_MCS_ANT_INIT_IND;
445f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	if (!priv->scan_tx_ant[IEEE80211_BAND_2GHZ])
446f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = RATE_MCS_ANT_INIT_IND;
447f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler}
448f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler
4492a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerint iwl_scan_initiate(struct iwl_priv *priv)
4502a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
4512a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (priv->iw_mode == IEEE80211_IF_TYPE_AP) {
4522a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		IWL_ERROR("APs don't scan.\n");
4532a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return 0;
4542a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
4552a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
4562a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (!iwl_is_ready_rf(priv)) {
4572a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		IWL_DEBUG_SCAN("Aborting scan due to not ready.\n");
4582a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return -EIO;
4592a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
4602a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
4612a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (test_bit(STATUS_SCANNING, &priv->status)) {
4622a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		IWL_DEBUG_SCAN("Scan already in progress.\n");
4632a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return -EAGAIN;
4642a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
4652a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
4662a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
4672a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		IWL_DEBUG_SCAN("Scan request while abort pending.  "
4682a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			       "Queuing.\n");
4692a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return -EAGAIN;
4702a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
4712a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
4722a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	IWL_DEBUG_INFO("Starting scan...\n");
4731b63ba8a86c85524a8d7e5953b314ce71ebcb9c9David S. Miller	if (priv->cfg->sku & IWL_SKU_G)
4741b63ba8a86c85524a8d7e5953b314ce71ebcb9c9David S. Miller		priv->scan_bands |= BIT(IEEE80211_BAND_2GHZ);
4751b63ba8a86c85524a8d7e5953b314ce71ebcb9c9David S. Miller	if (priv->cfg->sku & IWL_SKU_A)
4761b63ba8a86c85524a8d7e5953b314ce71ebcb9c9David S. Miller		priv->scan_bands |= BIT(IEEE80211_BAND_5GHZ);
4772a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	set_bit(STATUS_SCANNING, &priv->status);
4782a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	priv->scan_start = jiffies;
4792a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	priv->scan_pass_start = priv->scan_start;
4802a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
4812a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	queue_work(priv->workqueue, &priv->request_scan);
4822a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
4832a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	return 0;
4842a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
4852a421b91d6fe89e27ded7544a25449c0b050098fTomas WinklerEXPORT_SYMBOL(iwl_scan_initiate);
4862a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
4872a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler#define IWL_SCAN_CHECK_WATCHDOG (7 * HZ)
4882a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
4892a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerstatic void iwl_bg_scan_check(struct work_struct *data)
4902a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
4912a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	struct iwl_priv *priv =
4922a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	    container_of(data, struct iwl_priv, scan_check.work);
4932a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
4942a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
4952a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return;
4962a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
4972a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	mutex_lock(&priv->mutex);
4982a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (test_bit(STATUS_SCANNING, &priv->status) ||
4992a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	    test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
5002a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		IWL_DEBUG(IWL_DL_SCAN, "Scan completion watchdog resetting "
5012a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			"adapter (%dms)\n",
5022a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			jiffies_to_msecs(IWL_SCAN_CHECK_WATCHDOG));
5032a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
5042a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		if (!test_bit(STATUS_EXIT_PENDING, &priv->status))
5052a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			iwl_send_scan_abort(priv);
5062a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
5072a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	mutex_unlock(&priv->mutex);
5082a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
5092a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler/**
5102a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * iwl_supported_rate_to_ie - fill in the supported rate in IE field
5112a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler *
5122a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * return : set the bit for each supported rate insert in ie
5132a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler */
5142a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerstatic u16 iwl_supported_rate_to_ie(u8 *ie, u16 supported_rate,
5152a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler				    u16 basic_rate, int *left)
5162a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
5172a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	u16 ret_rates = 0, bit;
5182a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	int i;
5192a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	u8 *cnt = ie;
5202a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	u8 *rates = ie + 1;
5212a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
5222a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	for (bit = 1, i = 0; i < IWL_RATE_COUNT; i++, bit <<= 1) {
5232a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		if (bit & supported_rate) {
5242a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			ret_rates |= bit;
5252a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			rates[*cnt] = iwl_rates[i].ieee |
5262a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler				((bit & basic_rate) ? 0x80 : 0x00);
5272a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			(*cnt)++;
5282a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			(*left)--;
5292a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			if ((*left <= 0) ||
5302a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			    (*cnt >= IWL_SUPPORTED_RATES_IE_LEN))
5312a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler				break;
5322a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		}
5332a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
5342a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
5352a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	return ret_rates;
5362a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
5372a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
5382a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
5392a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerstatic void iwl_ht_cap_to_ie(const struct ieee80211_supported_band *sband,
540f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler			     u8 *pos, int *left)
5412a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
5422a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	struct ieee80211_ht_cap *ht_cap;
5432a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
5442a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (!sband || !sband->ht_info.ht_supported)
5452a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return;
5462a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
5472a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (*left < sizeof(struct ieee80211_ht_cap))
5482a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return;
5492a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
5502a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	*pos++ = sizeof(struct ieee80211_ht_cap);
5512a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	ht_cap = (struct ieee80211_ht_cap *) pos;
5522a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
5532a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	ht_cap->cap_info = cpu_to_le16(sband->ht_info.cap);
5542a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	memcpy(ht_cap->supp_mcs_set, sband->ht_info.supp_mcs_set, 16);
5552a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	ht_cap->ampdu_params_info =
5562a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		(sband->ht_info.ampdu_factor & IEEE80211_HT_CAP_AMPDU_FACTOR) |
5572a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		((sband->ht_info.ampdu_density << 2) &
5582a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			IEEE80211_HT_CAP_AMPDU_DENSITY);
5592a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	*left -= sizeof(struct ieee80211_ht_cap);
5602a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
5612a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
5622a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler/**
5632a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler * iwl_fill_probe_req - fill in all required fields and IE for probe request
5642a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler */
565f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler
5662a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerstatic u16 iwl_fill_probe_req(struct iwl_priv *priv,
5672a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler				  enum ieee80211_band band,
5682a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler				  struct ieee80211_mgmt *frame,
569f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler				  int left)
5702a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
5712a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	int len = 0;
5722a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	u8 *pos = NULL;
5732a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	u16 active_rates, ret_rates, cck_rates, active_rate_basic;
5742a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	const struct ieee80211_supported_band *sband =
5752a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler						iwl_get_hw_mode(priv, band);
5762a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
577f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler
5782a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	/* Make sure there is enough space for the probe request,
5792a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	 * two mandatory IEs and the data */
5802a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	left -= 24;
5812a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (left < 0)
5822a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return 0;
5832a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
5842a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
5852a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	memcpy(frame->da, iwl_bcast_addr, ETH_ALEN);
5862a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	memcpy(frame->sa, priv->mac_addr, ETH_ALEN);
5872a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	memcpy(frame->bssid, iwl_bcast_addr, ETH_ALEN);
5882a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	frame->seq_ctrl = 0;
5892a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
590f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	len += 24;
591f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler
5922a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	/* ...next IE... */
593f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	pos = &frame->u.probe_req.variable[0];
5942a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
595f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	/* fill in our indirect SSID IE */
5962a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	left -= 2;
5972a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (left < 0)
5982a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return 0;
5992a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	*pos++ = WLAN_EID_SSID;
6002a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	*pos++ = 0;
6012a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
602f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	len += 2;
6032a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
6042a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	/* fill in supported rate */
6052a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	left -= 2;
6062a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (left < 0)
6072a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return 0;
6082a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
6092a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	*pos++ = WLAN_EID_SUPP_RATES;
6102a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	*pos = 0;
6112a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
6122a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	/* exclude 60M rate */
6132a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	active_rates = priv->rates_mask;
6142a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	active_rates &= ~IWL_RATE_60M_MASK;
6152a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
6162a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	active_rate_basic = active_rates & IWL_BASIC_RATES_MASK;
6172a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
6182a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	cck_rates = IWL_CCK_RATES_MASK & active_rates;
6192a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	ret_rates = iwl_supported_rate_to_ie(pos, cck_rates,
620f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler					     active_rate_basic, &left);
6212a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	active_rates &= ~ret_rates;
6222a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
6232a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	ret_rates = iwl_supported_rate_to_ie(pos, active_rates,
624f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler					     active_rate_basic, &left);
6252a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	active_rates &= ~ret_rates;
6262a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
6272a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	len += 2 + *pos;
6282a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	pos += (*pos) + 1;
629f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler
6302a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (active_rates == 0)
6312a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		goto fill_end;
6322a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
6332a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	/* fill in supported extended rate */
6342a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	/* ...next IE... */
6352a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	left -= 2;
6362a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (left < 0)
6372a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return 0;
6382a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	/* ... fill it in... */
6392a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	*pos++ = WLAN_EID_EXT_SUPP_RATES;
6402a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	*pos = 0;
641f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	iwl_supported_rate_to_ie(pos, active_rates, active_rate_basic, &left);
642f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	if (*pos > 0) {
6432a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		len += 2 + *pos;
644f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		pos += (*pos) + 1;
645f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	} else {
646f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		pos--;
647f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	}
6482a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
6492a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler fill_end:
650f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler
6512a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	left -= 2;
6522a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (left < 0)
6532a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return 0;
6542a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
6552a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	*pos++ = WLAN_EID_HT_CAPABILITY;
6562a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	*pos = 0;
6572a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	iwl_ht_cap_to_ie(sband, pos, &left);
6582a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (*pos > 0)
6592a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		len += 2 + *pos;
660f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler
6612a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	return (u16)len;
6622a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
6632a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
664f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winklerstatic u32 iwl_scan_tx_ant(struct iwl_priv *priv, enum ieee80211_band band)
665f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler{
666f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	int i, ind;
667f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler
668f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	ind = priv->scan_tx_ant[band];
669f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	for (i = 0; i < priv->hw_params.tx_chains_num; i++) {
670f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		ind = (ind+1) >= priv->hw_params.tx_chains_num ? 0 : ind+1;
671f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		if (priv->hw_params.valid_tx_ant & (1 << ind)) {
672f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler			priv->scan_tx_ant[band] = ind;
673f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler			break;
674f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		}
675f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	}
676f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler
677f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	return scan_tx_ant[ind];
678f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler}
679f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler
680f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler
6812a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerstatic void iwl_bg_request_scan(struct work_struct *data)
6822a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
6832a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	struct iwl_priv *priv =
6842a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	    container_of(data, struct iwl_priv, request_scan);
6852a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	struct iwl_host_cmd cmd = {
6862a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		.id = REPLY_SCAN_CMD,
6872a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		.len = sizeof(struct iwl_scan_cmd),
6882a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		.meta.flags = CMD_SIZE_HUGE,
6892a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	};
6902a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	struct iwl_scan_cmd *scan;
6912a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	struct ieee80211_conf *conf = NULL;
692f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	int ret = 0;
693f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	u32 tx_ant;
6942a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	u16 cmd_len;
6952a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	enum ieee80211_band band;
6962a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	u8 direct_mask;
697f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	u8 rx_chain = 0x7; /* bitmap: ABC chains */
6982a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
6992a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	conf = ieee80211_get_hw_conf(priv->hw);
7002a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
7012a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	mutex_lock(&priv->mutex);
7022a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
7032a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (!iwl_is_ready(priv)) {
7042a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		IWL_WARNING("request scan called when driver not ready.\n");
7052a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		goto done;
7062a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
7072a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
7082a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	/* Make sure the scan wasn't cancelled before this queued work
7092a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	 * was given the chance to run... */
7102a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (!test_bit(STATUS_SCANNING, &priv->status))
7112a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		goto done;
7122a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
7132a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	/* This should never be called or scheduled if there is currently
7142a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	 * a scan active in the hardware. */
7152a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (test_bit(STATUS_SCAN_HW, &priv->status)) {
7162a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		IWL_DEBUG_INFO("Multiple concurrent scan requests in parallel. "
7172a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			       "Ignoring second request.\n");
7182a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		ret = -EIO;
7192a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		goto done;
7202a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
7212a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
7222a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
7232a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		IWL_DEBUG_SCAN("Aborting scan due to device shutdown\n");
7242a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		goto done;
7252a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
7262a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
7272a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
7282a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		IWL_DEBUG_HC("Scan request while abort pending.  Queuing.\n");
7292a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		goto done;
7302a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
7312a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
7322a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (iwl_is_rfkill(priv)) {
7332a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		IWL_DEBUG_HC("Aborting scan due to RF Kill activation\n");
7342a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		goto done;
7352a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
7362a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
7372a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (!test_bit(STATUS_READY, &priv->status)) {
7382a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		IWL_DEBUG_HC("Scan request while uninitialized.  Queuing.\n");
7392a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		goto done;
7402a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
7412a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
7422a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (!priv->scan_bands) {
7432a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		IWL_DEBUG_HC("Aborting scan due to no requested bands\n");
7442a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		goto done;
7452a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
7462a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
7472a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (!priv->scan) {
7482a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		priv->scan = kmalloc(sizeof(struct iwl_scan_cmd) +
7492a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler				     IWL_MAX_SCAN_SIZE, GFP_KERNEL);
7502a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		if (!priv->scan) {
7512a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			ret = -ENOMEM;
7522a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			goto done;
7532a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		}
7542a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
7552a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	scan = priv->scan;
7562a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	memset(scan, 0, sizeof(struct iwl_scan_cmd) + IWL_MAX_SCAN_SIZE);
7572a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
7582a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH;
7592a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	scan->quiet_time = IWL_ACTIVE_QUIET_TIME;
7602a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
7612a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (iwl_is_associated(priv)) {
7622a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		u16 interval = 0;
7632a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		u32 extra;
7642a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		u32 suspend_time = 100;
7652a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		u32 scan_suspend_time = 100;
7662a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		unsigned long flags;
7672a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
7682a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		IWL_DEBUG_INFO("Scanning while associated...\n");
7692a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
7702a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		spin_lock_irqsave(&priv->lock, flags);
7712a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		interval = priv->beacon_int;
7722a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		spin_unlock_irqrestore(&priv->lock, flags);
7732a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
7742a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		scan->suspend_time = 0;
7752a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		scan->max_out_time = cpu_to_le32(200 * 1024);
7762a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		if (!interval)
7772a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			interval = suspend_time;
7782a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
7792a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		extra = (suspend_time / interval) << 22;
7802a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		scan_suspend_time = (extra |
7812a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		    ((suspend_time % interval) * 1024));
7822a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		scan->suspend_time = cpu_to_le32(scan_suspend_time);
7832a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		IWL_DEBUG_SCAN("suspend_time 0x%X beacon interval %d\n",
7842a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			       scan_suspend_time, interval);
7852a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
7862a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
7872a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	/* We should add the ability for user to lock to PASSIVE ONLY */
7882a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (priv->one_direct_scan) {
789f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		IWL_DEBUG_SCAN("Start direct scan for '%s'\n",
790f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler				iwl_escape_essid(priv->direct_ssid,
791f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler				priv->direct_ssid_len));
7922a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		scan->direct_scan[0].id = WLAN_EID_SSID;
7932a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		scan->direct_scan[0].len = priv->direct_ssid_len;
7942a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		memcpy(scan->direct_scan[0].ssid,
7952a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		       priv->direct_ssid, priv->direct_ssid_len);
7962a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		direct_mask = 1;
7972a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	} else if (!iwl_is_associated(priv) && priv->essid_len) {
798f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		IWL_DEBUG_SCAN("Start direct scan for '%s' (not associated)\n",
799f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler				iwl_escape_essid(priv->essid, priv->essid_len));
8002a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		scan->direct_scan[0].id = WLAN_EID_SSID;
8012a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		scan->direct_scan[0].len = priv->essid_len;
8022a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		memcpy(scan->direct_scan[0].ssid, priv->essid, priv->essid_len);
8032a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		direct_mask = 1;
8042a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	} else {
805f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		IWL_DEBUG_SCAN("Start indirect scan.\n");
8062a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		direct_mask = 0;
8072a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
8082a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
8092a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
8102a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	scan->tx_cmd.sta_id = priv->hw_params.bcast_sta_id;
8112a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
8122a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
8132a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
8141b63ba8a86c85524a8d7e5953b314ce71ebcb9c9David S. Miller	if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) {
815f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		band = IEEE80211_BAND_2GHZ;
8162a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
817f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		tx_ant = iwl_scan_tx_ant(priv, band);
818f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		if (priv->active_rxon.flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK)
819f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler			scan->tx_cmd.rate_n_flags =
820f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler				iwl_hw_set_rate_n_flags(IWL_RATE_6M_PLCP,
821f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler							tx_ant);
822f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		else
823f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler			scan->tx_cmd.rate_n_flags =
824e7d326ac437e9e9425dcd79382f4e5f6ca31fb16Tomas Winkler				iwl_hw_set_rate_n_flags(IWL_RATE_1M_PLCP,
825f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler							tx_ant |
826e7d326ac437e9e9425dcd79382f4e5f6ca31fb16Tomas Winkler							RATE_MCS_CCK_MSK);
8272a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		scan->good_CRC_th = 0;
8281b63ba8a86c85524a8d7e5953b314ce71ebcb9c9David S. Miller	} else if (priv->scan_bands & BIT(IEEE80211_BAND_5GHZ)) {
829f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		band = IEEE80211_BAND_5GHZ;
830f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		tx_ant = iwl_scan_tx_ant(priv, band);
8312a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		scan->tx_cmd.rate_n_flags =
832e7d326ac437e9e9425dcd79382f4e5f6ca31fb16Tomas Winkler				iwl_hw_set_rate_n_flags(IWL_RATE_6M_PLCP,
833f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler							tx_ant);
8342a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		scan->good_CRC_th = IWL_GOOD_CRC_TH;
8352a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
836f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		/* Force use of chains B and C (0x6) for scan Rx for 4965
837f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		 * Avoid A (0x1) because of its off-channel reception on A-band.
838f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		 * MIMO is not used here, but value is required */
839f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965)
840f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler			rx_chain = 0x6;
8411b63ba8a86c85524a8d7e5953b314ce71ebcb9c9David S. Miller	} else {
8422a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		IWL_WARNING("Invalid scan band count\n");
8432a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		goto done;
8442a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	}
8452a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
846f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	scan->rx_chain = RXON_RX_CHAIN_DRIVER_FORCE_MSK |
847f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler				cpu_to_le16((0x7 << RXON_RX_CHAIN_VALID_POS) |
848f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler				(rx_chain << RXON_RX_CHAIN_FORCE_SEL_POS) |
849f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler				(0x7 << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS));
850f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler
8512a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	cmd_len = iwl_fill_probe_req(priv, band,
852f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler				     (struct ieee80211_mgmt *)scan->data,
853f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler				     IWL_MAX_SCAN_SIZE - sizeof(*scan));
8542a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
8552a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	scan->tx_cmd.len = cpu_to_le16(cmd_len);
8562a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
8572a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR)
8582a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		scan->filter_flags = RXON_FILTER_PROMISC_MSK;
8592a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
860f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK |
861f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler			       RXON_FILTER_BCON_AWARE_MSK);
862f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler
8632a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (direct_mask)
8642a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		scan->channel_count =
865f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler			iwl_get_channels_for_scan(priv, band, 1, /* active */
866f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler						  direct_mask,
8672a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler				(void *)&scan->data[le16_to_cpu(scan->tx_cmd.len)]);
8682a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	else
8692a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		scan->channel_count =
870f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler			iwl_get_channels_for_scan(priv, band, 0, /* passive */
871f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler						  direct_mask,
8722a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler				(void *)&scan->data[le16_to_cpu(scan->tx_cmd.len)]);
873f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	if (scan->channel_count == 0) {
874f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		IWL_DEBUG_SCAN("channel count %d\n", scan->channel_count);
875f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler		goto done;
876f53696de6722a4aac00b76e25a5321c01e88a55fTomas Winkler	}
8772a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
8782a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	cmd.len += le16_to_cpu(scan->tx_cmd.len) +
8792a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	    scan->channel_count * sizeof(struct iwl_scan_channel);
8802a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	cmd.data = scan;
8812a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	scan->len = cpu_to_le16(cmd.len);
8822a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
8832a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	set_bit(STATUS_SCAN_HW, &priv->status);
8842a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	ret = iwl_send_cmd_sync(priv, &cmd);
8852a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (ret)
8862a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		goto done;
8872a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
8882a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	queue_delayed_work(priv->workqueue, &priv->scan_check,
8892a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler			   IWL_SCAN_CHECK_WATCHDOG);
8902a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
8912a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	mutex_unlock(&priv->mutex);
8922a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	return;
8932a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
8942a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler done:
8952a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	/* inform mac80211 scan aborted */
8962a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	queue_work(priv->workqueue, &priv->scan_completed);
8972a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	mutex_unlock(&priv->mutex);
8982a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
8992a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
9002a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklerstatic void iwl_bg_abort_scan(struct work_struct *work)
9012a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
9022a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	struct iwl_priv *priv = container_of(work, struct iwl_priv, abort_scan);
9032a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
9042a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	if (!iwl_is_ready(priv))
9052a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler		return;
9062a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
9072a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	mutex_lock(&priv->mutex);
9082a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
9092a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	set_bit(STATUS_SCAN_ABORTING, &priv->status);
9102a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	iwl_send_scan_abort(priv);
9112a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
9122a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	mutex_unlock(&priv->mutex);
9132a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
9142a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
9152a421b91d6fe89e27ded7544a25449c0b050098fTomas Winklervoid iwl_setup_scan_deferred_work(struct iwl_priv *priv)
9162a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler{
9172a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	/*  FIXME: move here when resolved PENDING
9182a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	 *  INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed); */
9192a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	INIT_WORK(&priv->request_scan, iwl_bg_request_scan);
9202a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	INIT_WORK(&priv->abort_scan, iwl_bg_abort_scan);
9212a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler	INIT_DELAYED_WORK(&priv->scan_check, iwl_bg_scan_check);
9222a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler}
9232a421b91d6fe89e27ded7544a25449c0b050098fTomas WinklerEXPORT_SYMBOL(iwl_setup_scan_deferred_work);
9242a421b91d6fe89e27ded7544a25449c0b050098fTomas Winkler
925