diff -ruN linux-2.6.22/Documentation/networking/README.iwlwifi linux-2.6.22-iwlwifi/Documentation/networking/README.iwlwifi
--- linux-2.6.22/Documentation/networking/README.iwlwifi	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-iwlwifi/Documentation/networking/README.iwlwifi	2007-07-31 15:29:35.000000000 +0200
@@ -0,0 +1,178 @@
+
+
+Intel Wireless Link Wifi (iwlwifi) drivers for Linux* in support 
+of:
+
+iwl4965.ko - Intel(R) Wireless WiFi Link 4965AGN
+iwl3945.ko - Intel(R) PRO/Wireless 3945ABG/BG Network Connection Adapter
+
+Copyright (C) 2005 - 2007, Intel Corporation
+
+README.iwlwifi
+
+Version: 0.1.6
+Date   : July 30, 2007
+
+
+Index
+-----------------------------------------------
+0.   IMPORTANT INFORMATION BEFORE USING THIS DRIVER
+1.   Introduction
+2.   Loading the Driver
+3.   Feature Details
+4.   Configuring the driver
+5.   Wireless Tools Details
+6.   Support
+
+
+===============================================
+0.   IMPORTANT INFORMATION BEFORE USING THIS DRIVER
+===============================================
+
+Important Notice FOR ALL USERS OR DISTRIBUTORS!!!! 
+
+Intel wireless LAN adapters are engineered, manufactured, tested, and
+quality checked to ensure that they meet all necessary local and
+governmental regulatory agency requirements for the regions that they
+are designated and/or marked to ship into. Since wireless LANs are
+generally unlicensed devices that share spectrum with radars,
+satellites, and other licensed and unlicensed devices, it is sometimes
+necessary to dynamically detect, avoid, and limit usage to avoid
+interference with these devices. In many instances Intel is required to
+provide test data to prove regional and local compliance to regional and
+governmental regulations before certification or approval to use the
+product is granted. Intel's wireless LAN's EEPROM, firmware, and
+software driver are designed to carefully control parameters that affect
+radio operation and to ensure electromagnetic compliance (EMC). These
+parameters include, without limitation, RF power, spectrum usage,
+channel scanning, and human exposure. 
+
+For these reasons Intel cannot permit any manipulation by third parties
+of the software provided in binary format with the wireless WLAN
+adapters (e.g., the EEPROM and firmware). Furthermore, if you use any
+patches, utilities, or code with the Intel wireless LAN adapters that
+have been manipulated by an unauthorized party (i.e., patches,
+utilities, or code (including open source code modifications) which have
+not been validated by Intel), (i) you will be solely responsible for
+ensuring the regulatory compliance of the products, (ii) Intel will bear
+no liability, under any theory of liability for any issues associated
+with the modified products, including without limitation, claims under
+the warranty and/or issues arising from regulatory non-compliance, and
+(iii) Intel will not provide or be required to assist in providing
+support to any third parties for such modified products.  
+
+Note: Many regulatory agencies consider Wireless LAN adapters to be
+modules, and accordingly, condition system-level regulatory approval
+upon receipt and review of test data documenting that the antennas and
+system configuration do not cause the EMC and radio operation to be
+non-compliant.
+
+The drivers available for download from SourceForge are provided as a 
+part of a development project.  Conformance to local regulatory 
+requirements is the responsibility of the individual developer.  As 
+such, if you are interested in deploying or shipping a driver as part of 
+solution intended to be used for purposes other than development, please 
+obtain a tested driver from Intel Customer Support at:
+
+http://support.intel.com/support/notebook/sb/CS-006408.htm
+
+
+===============================================
+1.   Introduction
+===============================================
+The following sections attempt to provide a brief introduction to using 
+the iwlwifi drivers for Linux.
+
+This document is not meant to be a comprehensive manual on 
+understanding or using wireless technologies, but should be sufficient 
+to get you moving without wires on Linux.
+
+For information on building and installing the driver, see the INSTALL
+file.
+
+The iwlwifi project builds two kernel modules:
+
+	iwl3945.ko	iwl4965.ko
+
+The modules are built using as much common code as possible.
+
+===============================================
+2. Loading the Driver
+===============================================
+
+See the INSTALL document for information on installing the driver.
+
+Once installed, a typical method for launching the driver and the 
+is via the load script provided in the source package:
+
+	# ./load debug=0x43fff   <--- You need to be root for this
+
+If you followed the steps of the INSTALL document, you can load the 
+module simply by running:
+
+	# modprobe iwl3945       <--- You need to be root for this
+	(Or modprobe iwl4965 depending on your hardware)
+
+If you want to unload the module you can simply use the unload script:
+
+	# ./unload               <--- You need to be root for this
+
+or:
+
+	# modprobe -r iwl3945       <--- You need to be root for this
+	(Or modprobe -r iwl4965 depending on your hardware)
+
+
+===============================================
+3. Feature Details
+===============================================
+
+TODO: Document features.
+
+===============================================
+4. Configuring the driver
+===============================================
+
+4.1. Command Line Parameters
+-----------------------------------------------
+
+TODO:  Document module parameters
+
+4.3. Sysfs Helper Files:
+-----------------------------------------------
+
+TODO: Document sysfs attributes
+
+===============================================
+5. Wireless Tools Details
+===============================================
+
+TODO: Document wireless tools, mac80211, userspace MLMEs, etc.
+
+short_preamble -- set by PRISM2_PARAM_PREAMBLE in ieee80211_ioctl.c
+IEEE80211_SCAN_EXTRA_INFO -- set by PRISM2_PARAM_SCAN_FLAGS
+
+6. Support
+-----------------------------------------------
+
+For general development information and support, go to:
+	
+    http://intellinuxwireless.org/
+
+
+------------------------------
+Copyright (C) 2005 - 2007, Intel Corporation
+
+INFORMATION IN THIS DOCUMENT IS PROVIDED IN CONNECTION WITH INTEL PRODUCTS.  
+EXCEPT AS PROVIDED IN INTEL'S TERMS AND CONDITIONS OF SALE FOR SUCH PRODUCTS, 
+INTEL ASSUMES NO LIABILITY WHATSOEVER, AND INTEL DISCLAIMS ANY EXPRESS OR 
+IMPLIED WARRANTY RELATING TO SALE AND/OR USE OF INTEL PRODUCTS, INCLUDING 
+LIABILITY OR WARRANTIES RELATING TO FITNESS FOR A PARTICULAR PURPOSE, 
+MERCHANTABILITY, OR INFRINGEMENT OF ANY PATENT, COPYRIGHT, OR OTHER 
+INTELLECTUAL PROPERTY RIGHT. 
+
+This document is subject to change without notice. 
+
+* Other names and brands may be claimed as the property of others.
+
+
diff -ruN linux-2.6.22/drivers/net/wireless/Kconfig linux-2.6.22-iwlwifi/drivers/net/wireless/Kconfig
--- linux-2.6.22/drivers/net/wireless/Kconfig	2007-07-28 13:40:39.000000000 +0200
+++ linux-2.6.22-iwlwifi/drivers/net/wireless/Kconfig	2007-07-31 15:29:35.000000000 +0200
@@ -550,4 +550,5 @@
 source "drivers/net/wireless/bcm43xx/Kconfig"
 source "drivers/net/wireless/zd1211rw/Kconfig"
 
+source "drivers/net/wireless/mac80211/Kconfig"
 endmenu
diff -ruN linux-2.6.22/drivers/net/wireless/Makefile linux-2.6.22-iwlwifi/drivers/net/wireless/Makefile
--- linux-2.6.22/drivers/net/wireless/Makefile	2007-07-28 13:40:39.000000000 +0200
+++ linux-2.6.22-iwlwifi/drivers/net/wireless/Makefile	2007-07-31 15:29:35.000000000 +0200
@@ -44,3 +44,4 @@
 
 obj-$(CONFIG_USB_ZD1201)	+= zd1201.o
 obj-$(CONFIG_LIBERTAS_USB)     += libertas/
+obj-y += mac80211/
diff -ruN linux-2.6.22/drivers/net/wireless/mac80211/Kconfig linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/Kconfig
--- linux-2.6.22/drivers/net/wireless/mac80211/Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/Kconfig	2007-07-31 15:29:35.000000000 +0200
@@ -0,0 +1 @@
+source "drivers/net/wireless/mac80211/iwlwifi/Kconfig"
diff -ruN linux-2.6.22/drivers/net/wireless/mac80211/Makefile linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/Makefile
--- linux-2.6.22/drivers/net/wireless/mac80211/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/Makefile	2007-07-31 15:29:35.000000000 +0200
@@ -0,0 +1 @@
+obj-$(CONFIG_IWLWIFI)	+= iwlwifi/
diff -ruN linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/Kconfig linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/Kconfig
--- linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/Kconfig	2007-07-31 15:29:35.000000000 +0200
@@ -0,0 +1,96 @@
+config IWLWIFI
+	bool "Intel Wireless WiFi Link Drivers"
+	depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL
+	select FW_LOADER
+	default n
+	---help---
+	  Select to enable drivers based on the iwlwifi project.  This
+	  project provides a common foundation for Intel's wireless
+	  drivers designed to use the mac80211 subsystem.
+
+	  See <file:Documentation/networking/README.iwlwifi> for
+	  information on the capabilities currently enabled in this
+	  driver and for tips for debugging issues and problems.
+
+config IWLWIFI_DEBUG
+	bool "Enable full debugging output in iwlwifi drivers"
+	depends on IWLWIFI
+	default y
+	---help---
+	  This option will enable debug tracing output for the iwlwifi
+	  drivers.
+
+	  This will result in the kernel module being ~100k larger.  You can
+	  control which debug output is sent to the kernel log by setting the
+	  value in
+
+	          /sys/bus/pci/drivers/${DRIVER}/debug_level
+
+	  This entry will only exist if this option is enabled.
+
+	  To set a value, simply echo an 8-byte hex value to the same file:
+
+		  % echo 0x43fff > /sys/bus/pci/drivers/${DRIVER}/debug_level
+
+	  You can find the list of debug mask values in:
+		  drivers/net/wireless/mac80211/iwlwifi/iwl-debug.h
+
+	  If this is your first time using this driver, you should say Y here
+	  as the debug information can assist others in helping you resolve
+	  any problems you may encounter.
+
+config IWL4965
+	tristate "Intel Wireless WiFi 4965AGN"
+	depends on m && IWLWIFI && EXPERIMENTAL
+	default m
+	---help---
+	  Select to build the driver supporting the:
+
+	  Intel Wireless WiFi Link 4965AGN
+
+	  This driver uses the kernel's mac80211 subsystem.
+
+	  See <file:Documentation/networking/README.iwlwifi> for
+	  information on the capabilities currently enabled in this
+	  driver and for tips for debugging any issues or problems.
+
+	  In order to use this driver, you will need a microcode (uCode)
+	  image for it. You can obtain the microcode from:
+
+	          <http://intellinuxwireless.org/>.
+
+	  See the above referenced README.iwlwifi for information on where
+	  to install the microcode images.
+
+	  If you want to compile the driver as a module ( = code which can be
+	  inserted in and remvoed from the running kernel whenever you want),
+	  say M here and read <file:Documentation/modules.txt>.  The module
+	  will be called iwl4965.ko.
+
+config IWL3945
+	tristate "Intel PRO/Wireless 3945ABG/BG Network Connection"
+	depends on m && IWLWIFI && EXPERIMENTAL
+	default m
+	---help---
+	  Select to build the driver supporting the:
+
+	  Intel PRO/Wireless 3945ABG/BG Network Connection
+
+	  This driver uses the kernel's mac80211 subsystem.
+
+	  See <file:Documentation/networking/README.iwlwifi> for
+	  information on the capabilities currently enabled in this
+	  driver and for tips for debugging any issues or problems.
+
+	  In order to use this driver, you will need a microcode (uCode)
+	  image for it. You can obtain the microcode from:
+
+	          <http://intellinuxwireless.org/>.
+
+	  See the above referenced README.iwlwifi for information on where
+	  to install the microcode images.
+
+	  If you want to compile the driver as a module ( = code which can be
+	  inserted in and remvoed from the running kernel whenever you want),
+	  say M here and read <file:Documentation/modules.txt>.  The module
+	  will be called iwl3945.ko.
diff -ruN linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/Makefile linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/Makefile
--- linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/Makefile	2007-07-31 15:29:35.000000000 +0200
@@ -0,0 +1,29 @@
+# NOTE:  We use common code from iwl-base.c to build driver
+# specific binaries based on the #define IWL -- the target
+# setup below creates a specific driver target from iwl-base.c
+#
+# NOTE2: base-XXXX.o has -D"KBUILD_MODNAME=KBUILD_STR(...)" in order to
+# prevent the following kbuild error:
+# 	include/linux/pci.h:603: error: `KBUILD_MODNAME' undeclared (first \
+#		use in this function)
+#
+# -jpk
+
+obj-$(CONFIG_IWL3945)	+= iwl3945.o
+iwl3945-objs		= base-3945.o iwl-3945.o iwl-3945-rs.o
+CFLAGS_iwl-3945.o	= -DIWL=3945
+CFLAGS_iwl-3945-rs.o	= -DIWL=3945
+CFLAGS_base-3945.o	= -DIWL=3945 -D"KBUILD_MODNAME=KBUILD_STR(iwl3945)"
+$(obj)/base-3945.o: $(src)/iwl-base.c FORCE
+	$(call cmd,force_checksrc)
+	$(call if_changed_rule,cc_o_c)
+
+obj-$(CONFIG_IWL4965)	+= iwl4965.o
+iwl4965-objs		= base-4965.o iwl-4965.o iwl-4965-rs.o
+CFLAGS_iwl-4965.o	= -DIWL=4965
+CFLAGS_iwl-4965-rs.o	= -DIWL=4965
+CFLAGS_base-4965.o	= -DIWL=4965 -D"KBUILD_MODNAME=KBUILD_STR(iwl4965)"
+$(obj)/base-4965.o: $(src)/iwl-base.c FORCE
+	$(call cmd,force_checksrc)
+	$(call if_changed_rule,cc_o_c)
+
diff -ruN linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-3945-hw.h linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-3945-hw.h
--- linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-3945-hw.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-3945-hw.h	2007-07-31 15:29:35.000000000 +0200
@@ -0,0 +1,90 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2005 - 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU Geeral Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2007 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __iwl_3945_hw__
+#define __iwl_3945_hw__
+
+#define IWL_RX_BUF_SIZE 3000
+#define IWL_MAX_BSM_SIZE ALM_RTC_INST_SIZE
+#define IWL_MAX_INST_SIZE ALM_RTC_INST_SIZE
+#define IWL_MAX_DATA_SIZE ALM_RTC_DATA_SIZE
+
+/* Base physical address of iwl_shared is provided to FH_TSSR_CBB_BASE
+ * and &iwl_shared.rx_read_ptr[0] is provided to FH_RCSR_RPTR_ADDR(0) */
+struct iwl_shared {
+	__le32 tx_base_ptr[8];
+	__le32 rx_read_ptr[3];
+} __attribute__ ((packed));
+
+struct iwl_tfd_frame_data {
+	__le32 addr;
+	__le32 len;
+} __attribute__ ((packed));
+
+struct iwl_tfd_frame {
+	__le32 control_flags;
+	struct iwl_tfd_frame_data pa[4];
+	u8 reserved[28];
+} __attribute__ ((packed));
+
+#endif
diff -ruN linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-3945-rs.c linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-3945-rs.c
--- linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-3945-rs.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-3945-rs.c	2007-07-31 15:29:35.000000000 +0200
@@ -0,0 +1,985 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2005 - 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/wireless.h>
+#include <net/mac80211.h>
+#include <net/ieee80211.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+
+#include <linux/workqueue.h>
+
+#include <net/mac80211.h>
+#include <linux/wireless.h>
+
+#include "../net/mac80211/ieee80211_rate.h"
+
+#include "iwl-3945-rs.h"
+#include "iwlwifi.h"
+#include "iwl-helpers.h"
+
+#define RS_NAME "iwl-3945-rs"
+
+struct iwl_rate_scale_data {
+	u64 data;
+	s32 success_counter;
+	s32 success_ratio;
+	s32 counter;
+	s32 average_tpt;
+	unsigned long stamp;
+};
+
+struct iwl_rate_scale_priv {
+	spinlock_t lock;
+	s32 *expected_tpt;
+	unsigned long last_partial_flush;
+	unsigned long last_flush;
+	u8 flush_pending;
+	u32 flush_time;
+	u32 last_tx_packets;
+	u8 tgg;
+	u32 tx_packets;
+	u8 start_rate;
+	u8 ibss_sta_added;
+	struct timer_list rate_scale_flush;
+	struct iwl_rate_scale_data win[IWL_RATE_COUNT];
+};
+
+static s32 iwl_expected_tpt_g[IWL_RATE_COUNT] = {
+	0, 0, 76, 104, 130, 168, 191, 202, 7, 13, 35, 58
+};
+
+static s32 iwl_expected_tpt_g_prot[IWL_RATE_COUNT] = {
+	0, 0, 0, 80, 93, 113, 123, 125, 7, 13, 35, 58
+};
+
+static s32 iwl_expected_tpt_a[IWL_RATE_COUNT] = {
+	40, 57, 72, 98, 121, 154, 177, 186, 0, 0, 0, 0
+};
+
+static s32 iwl_expected_tpt_b[IWL_RATE_COUNT] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 7, 13, 35, 58
+};
+
+struct iwl_tpt_entry {
+	s8 min_rssi;
+	u8 index;
+};
+
+static struct iwl_tpt_entry iwl_tpt_table_a[] = {
+	{-60, IWL_RATE_54M_INDEX},
+	{-64, IWL_RATE_48M_INDEX},
+	{-72, IWL_RATE_36M_INDEX},
+	{-80, IWL_RATE_24M_INDEX},
+	{-84, IWL_RATE_18M_INDEX},
+	{-85, IWL_RATE_12M_INDEX},
+	{-87, IWL_RATE_9M_INDEX},
+	{-89, IWL_RATE_6M_INDEX}
+};
+
+static struct iwl_tpt_entry iwl_tpt_table_b[] = {
+	{-86, IWL_RATE_11M_INDEX},
+	{-88, IWL_RATE_5M_INDEX},
+	{-90, IWL_RATE_2M_INDEX},
+	{-92, IWL_RATE_1M_INDEX}
+
+};
+
+static struct iwl_tpt_entry iwl_tpt_table_g[] = {
+	{-60, IWL_RATE_54M_INDEX},
+	{-64, IWL_RATE_48M_INDEX},
+	{-68, IWL_RATE_36M_INDEX},
+	{-80, IWL_RATE_24M_INDEX},
+	{-84, IWL_RATE_18M_INDEX},
+	{-85, IWL_RATE_12M_INDEX},
+	{-86, IWL_RATE_11M_INDEX},
+	{-88, IWL_RATE_5M_INDEX},
+	{-90, IWL_RATE_2M_INDEX},
+	{-92, IWL_RATE_1M_INDEX}
+};
+
+#define IWL_RATE_MAX_WINDOW          62
+#define IWL_RATE_FLUSH        (3*HZ/10)
+#define IWL_RATE_WIN_FLUSH       (HZ/2)
+#define IWL_RATE_HIGH_TH          11520
+#define IWL_RATE_MIN_FAILURE_TH       8
+#define IWL_RATE_MIN_SUCCESS_TH       8
+#define IWL_RATE_DECREASE_TH       1920
+
+static u8 iwl_get_rate_index_by_rssi(s32 rssi, u8 mode)
+{
+	u32 index = 0;
+	u32 table_size = 0;
+	struct iwl_tpt_entry *tpt_table = NULL;
+
+	if ((rssi < IWL_MIN_RSSI_VAL) || (rssi > IWL_MAX_RSSI_VAL))
+		rssi = IWL_MIN_RSSI_VAL;
+
+	switch (mode) {
+	case MODE_IEEE80211G:
+		tpt_table = iwl_tpt_table_g;
+		table_size = ARRAY_SIZE(iwl_tpt_table_g);
+		break;
+
+	case MODE_IEEE80211A:
+		tpt_table = iwl_tpt_table_a;
+		table_size = ARRAY_SIZE(iwl_tpt_table_a);
+		break;
+
+	default:
+	case MODE_IEEE80211B:
+		tpt_table = iwl_tpt_table_b;
+		table_size = ARRAY_SIZE(iwl_tpt_table_b);
+		break;
+	}
+
+	while ((index < table_size) && (rssi < tpt_table[index].min_rssi))
+		index++;
+
+	index = min(index, (table_size - 1));
+
+	return tpt_table[index].index;
+}
+
+static void iwl_clear_window(struct iwl_rate_scale_data *window)
+{
+	window->data = 0;
+	window->success_counter = 0;
+	window->success_ratio = IWL_INVALID_VALUE;
+	window->counter = 0;
+	window->average_tpt = IWL_INVALID_VALUE;
+	window->stamp = 0;
+}
+
+/**
+ * iwl_rate_scale_flush_windows - flush out the rate scale windows
+ *
+ * Returns the number of windows that have gathered data but were
+ * not flushed.  If there were any that were not flushed, then
+ * reschedule the rate flushing routine.
+ */
+static int iwl_rate_scale_flush_windows(struct iwl_rate_scale_priv *rs_priv)
+{
+	int unflushed = 0;
+	int i;
+	unsigned long flags;
+
+	/*
+	 * For each rate, if we have collected data on that rate
+	 * and it has been more than IWL_RATE_WIN_FLUSH
+	 * since we flushed, clear out the gathered statistics
+	 */
+	for (i = 0; i < IWL_RATE_COUNT; i++) {
+		if (!rs_priv->win[i].counter)
+			continue;
+
+		spin_lock_irqsave(&rs_priv->lock, flags);
+		if (time_after(jiffies, rs_priv->win[i].stamp +
+			       IWL_RATE_WIN_FLUSH)) {
+			IWL_DEBUG_RATE("flushing %d samples of rate "
+				       "index %d\n",
+				       rs_priv->win[i].counter, i);
+			iwl_clear_window(&rs_priv->win[i]);
+		} else
+			unflushed++;
+		spin_unlock_irqrestore(&rs_priv->lock, flags);
+	}
+
+	return unflushed;
+}
+
+#define IWL_RATE_FLUSH_MAX              5000	/* msec */
+#define IWL_RATE_FLUSH_MIN              50	/* msec */
+
+static void iwl_bg_rate_scale_flush(unsigned long data)
+{
+	struct iwl_rate_scale_priv *rs_priv = (void *)data;
+	int unflushed = 0;
+	unsigned long flags;
+	u32 packet_count, duration, pps;
+
+	IWL_DEBUG_RATE("enter\n");
+
+	unflushed = iwl_rate_scale_flush_windows(rs_priv);
+
+	spin_lock_irqsave(&rs_priv->lock, flags);
+
+	rs_priv->flush_pending = 0;
+
+	/* Number of packets Rx'd since last time this timer ran */
+	packet_count = (rs_priv->tx_packets - rs_priv->last_tx_packets) + 1;
+
+	rs_priv->last_tx_packets = rs_priv->tx_packets + 1;
+
+	if (unflushed) {
+		duration =
+		    jiffies_to_msecs(jiffies - rs_priv->last_partial_flush);
+/*              duration = jiffies_to_msecs(rs_priv->flush_time); */
+
+		IWL_DEBUG_RATE("Tx'd %d packets in %dms\n",
+			       packet_count, duration);
+
+		/* Determine packets per second */
+		if (duration)
+			pps = (packet_count * 1000) / duration;
+		else
+			pps = 0;
+
+		if (pps) {
+			duration = IWL_RATE_FLUSH_MAX / pps;
+			if (duration < IWL_RATE_FLUSH_MIN)
+				duration = IWL_RATE_FLUSH_MIN;
+		} else
+			duration = IWL_RATE_FLUSH_MAX;
+
+		rs_priv->flush_time = msecs_to_jiffies(duration);
+
+		IWL_DEBUG_RATE("new flush period: %d msec ave %d\n",
+			       duration, packet_count);
+
+		mod_timer(&rs_priv->rate_scale_flush, jiffies +
+			  rs_priv->flush_time);
+
+		rs_priv->last_partial_flush = jiffies;
+	}
+
+	/* If there weren't any unflushed entries, we don't schedule the timer
+	 * to run again */
+
+	rs_priv->last_flush = jiffies;
+
+	spin_unlock_irqrestore(&rs_priv->lock, flags);
+
+	IWL_DEBUG_RATE("leave\n");
+}
+
+/**
+ * iwl_collect_tx_data - Update the success/failure sliding window
+ *
+ * We keep a sliding window of the last 64 packets transmitted
+ * at this rate.  window->data contains the bitmask of successful
+ * packets.
+ */
+static void iwl_collect_tx_data(struct iwl_rate_scale_priv *rs_priv,
+				struct iwl_rate_scale_data *window,
+				int success, int retries)
+{
+	unsigned long flags;
+
+	if (!retries) {
+		IWL_DEBUG_RATE("leave: retries == 0 -- should be at "
+			       "least 1\n");
+		return;
+	}
+
+	while (retries--) {
+		spin_lock_irqsave(&rs_priv->lock, flags);
+
+		/* If we have filled up the window then subtract one from the
+		 * success counter if the high-bit is counting toward
+		 * success */
+		if (window->counter == IWL_RATE_MAX_WINDOW) {
+			if (window->data & (1ULL << (IWL_RATE_MAX_WINDOW - 1)))
+				window->success_counter--;
+		} else
+			window->counter++;
+
+		/* Slide the window to the left one bit */
+		window->data = (window->data << 1);
+
+		/* If this packet was a success then set the low bit high */
+		if (success) {
+			window->success_counter++;
+			window->data |= 1;
+		}
+
+		/* window->counter can't be 0 -- it is either >0 or
+		 * IWL_RATE_MAX_WINDOW */
+		window->success_ratio = 12800 * window->success_counter /
+		    window->counter;
+
+		/* Tag this window as having been updated */
+		window->stamp = jiffies;
+
+		spin_unlock_irqrestore(&rs_priv->lock, flags);
+	}
+}
+
+static void rs_rate_init(void *priv_rate, void *priv_sta,
+			 struct ieee80211_local *local, struct sta_info *sta)
+{
+	int i;
+
+	IWL_DEBUG_RATE("enter\n");
+
+	/* TODO: what is a good starting rate for STA? About middle? Maybe not
+	 * the lowest or the highest rate.. Could consider using RSSI from
+	 * previous packets? Need to have IEEE 802.1X auth succeed immediately
+	 * after assoc.. */
+
+	for (i = IWL_RATE_COUNT - 1; i >= 0; i--) {
+		if (sta->supp_rates & (1 << i)) {
+			sta->txrate = i;
+			break;
+		}
+	}
+
+	sta->last_txrate = sta->txrate;
+
+	IWL_DEBUG_RATE("leave\n");
+}
+
+static void *rs_alloc(struct ieee80211_local *local)
+{
+	IWL_DEBUG_RATE("enter\n");
+	IWL_DEBUG_RATE("leave\n");
+	return local->hw.priv;
+}
+
+static void rs_free(void *data)
+{
+	IWL_DEBUG_RATE("enter\n");
+	IWL_DEBUG_RATE("leave\n");
+}
+
+static void *rs_alloc_sta(void *priv, gfp_t gfp)
+{
+	struct iwl_rate_scale_priv *rs_priv;
+	int i;
+
+	IWL_DEBUG_RATE("enter\n");
+
+	rs_priv = kzalloc(sizeof(struct iwl_rate_scale_priv), gfp);
+	if (!rs_priv) {
+		IWL_DEBUG_RATE("leave: ENOMEM\n");
+		return NULL;
+	}
+
+	spin_lock_init(&rs_priv->lock);
+
+	rs_priv->start_rate = IWL_RATE_INVALID;
+
+	/* default to just 802.11b */
+	rs_priv->expected_tpt = iwl_expected_tpt_b;
+
+	rs_priv->last_partial_flush = jiffies;
+	rs_priv->last_flush = jiffies;
+	rs_priv->flush_time = IWL_RATE_FLUSH;
+	rs_priv->last_tx_packets = 0;
+	rs_priv->ibss_sta_added = 0;
+
+	init_timer(&rs_priv->rate_scale_flush);
+	rs_priv->rate_scale_flush.data = (unsigned long)rs_priv;
+	rs_priv->rate_scale_flush.function = &iwl_bg_rate_scale_flush;
+
+	for (i = 0; i < IWL_RATE_COUNT; i++)
+		iwl_clear_window(&rs_priv->win[i]);
+
+	IWL_DEBUG_RATE("leave\n");
+
+	return rs_priv;
+}
+
+static void rs_free_sta(void *priv, void *priv_sta)
+{
+	struct iwl_rate_scale_priv *rs_priv = priv_sta;
+
+	IWL_DEBUG_RATE("enter\n");
+	del_timer_sync(&rs_priv->rate_scale_flush);
+	kfree(rs_priv);
+	IWL_DEBUG_RATE("leave\n");
+}
+
+static void rs_clear(void *priv)
+{
+	IWL_DEBUG_RATE("NOP\n");
+}
+
+/**
+ * rs_tx_status - Update rate control values based on Tx results
+ *
+ * NOTE: Uses iwl_priv->retry_rate for the # of retries attempted by
+ * the hardware for each rate.
+ */
+static void rs_tx_status(void *priv_rate,
+			 struct net_device *dev,
+			 struct sk_buff *skb,
+			 struct ieee80211_tx_status *tx_resp)
+{
+	u8 retries, current_count;
+	int scale_rate_index, first_index, last_index;
+	unsigned long flags;
+	struct sta_info *sta;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	struct iwl_priv *priv = (struct iwl_priv *)priv_rate;
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct iwl_rate_scale_priv *rs_priv;
+
+	IWL_DEBUG_RATE("enter\n");
+
+	retries = tx_resp->retry_count;
+
+	first_index = tx_resp->control.tx_rate;
+	if ((first_index < 0) || (first_index >= IWL_RATE_COUNT)) {
+		IWL_DEBUG_RATE("leave: Rate out of bounds: %0x for %d\n",
+			       tx_resp->control.tx_rate, first_index);
+		return;
+	}
+
+	sta = sta_info_get(local, hdr->addr1);
+	if (!sta || !sta->rate_ctrl_priv) {
+		if (sta)
+			sta_info_put(sta);
+		IWL_DEBUG_RATE("leave: No STA priv data to update!\n");
+		return;
+	}
+
+	rs_priv = (void *)sta->rate_ctrl_priv;
+
+	rs_priv->tx_packets++;
+
+	scale_rate_index = first_index;
+	last_index = first_index;
+
+	/*
+	 * Update the window for each rate.  We determine which rates
+	 * were Tx'd based on the total number of retries vs. the number
+	 * of retries configured for each rate -- currently set to the
+	 * priv value 'retry_rate' vs. rate specific
+	 *
+	 * On exit from this while loop last_index indicates the rate
+	 * at which the frame was finally transmitted (or failed if no
+	 * ACK)
+	 */
+	while (retries > 0) {
+		if (retries < priv->retry_rate) {
+			current_count = retries;
+			last_index = scale_rate_index;
+		} else {
+			current_count = priv->retry_rate;
+			last_index = iwl_get_prev_ieee_rate(scale_rate_index);
+		}
+
+		/* Update this rate accounting for as many retries
+		 * as was used for it (per current_count) */
+		iwl_collect_tx_data(rs_priv,
+				    &rs_priv->win[scale_rate_index],
+				    0, current_count);
+		IWL_DEBUG_RATE("Update rate %d for %d retries.\n",
+			       scale_rate_index, current_count);
+
+		retries -= current_count;
+
+		if (retries)
+			scale_rate_index =
+			    iwl_get_prev_ieee_rate(scale_rate_index);
+	}
+
+	/* Update the last index window with success/failure based on ACK */
+	IWL_DEBUG_RATE("Update rate %d with %s.\n",
+		       last_index,
+		       (tx_resp->flags & IEEE80211_TX_STATUS_ACK) ?
+		       "success" : "failure");
+	iwl_collect_tx_data(rs_priv,
+			    &rs_priv->win[last_index],
+			    tx_resp->flags & IEEE80211_TX_STATUS_ACK, 1);
+
+	/* We updated the rate scale window -- if its been more than
+	 * flush_time since the last run, schedule the flush
+	 * again */
+	spin_lock_irqsave(&rs_priv->lock, flags);
+
+	if (!rs_priv->flush_pending &&
+	    time_after(jiffies, rs_priv->last_partial_flush +
+		       rs_priv->flush_time)) {
+
+		rs_priv->flush_pending = 1;
+		mod_timer(&rs_priv->rate_scale_flush,
+			  jiffies + rs_priv->flush_time);
+	}
+
+	spin_unlock_irqrestore(&rs_priv->lock, flags);
+
+	sta_info_put(sta);
+
+	IWL_DEBUG_RATE("leave\n");
+
+	return;
+}
+
+static struct ieee80211_rate *iwl_get_lowest_rate(struct ieee80211_local
+						  *local)
+{
+	struct ieee80211_hw_mode *mode = local->oper_hw_mode;
+	int i;
+
+	for (i = 0; i < mode->num_rates; i++) {
+		struct ieee80211_rate *rate = &mode->rates[i];
+
+		if (rate->flags & IEEE80211_RATE_SUPPORTED)
+			return rate;
+	}
+
+	return &mode->rates[0];
+}
+
+static u16 iwl_get_adjacent_rate(struct iwl_rate_scale_priv *rs_priv,
+				 u8 index, u16 rate_mask, int phymode)
+{
+	u8 high = IWL_RATE_INVALID;
+	u8 low = IWL_RATE_INVALID;
+
+	/* 802.11A walks to the next literal adjascent rate in
+	 * the rate table */
+	if (unlikely(phymode == MODE_IEEE80211A)) {
+		int i;
+		u32 mask;
+
+		/* Find the previous rate that is in the rate mask */
+		i = index - 1;
+		for (mask = (1 << i); i >= 0; i--, mask >>= 1) {
+			if (rate_mask & mask) {
+				low = i;
+				break;
+			}
+		}
+
+		/* Find the next rate that is in the rate mask */
+		i = index + 1;
+		for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) {
+			if (rate_mask & mask) {
+				high = i;
+				break;
+			}
+		}
+
+		return (high << 8) | low;
+	}
+
+	low = index;
+	while (low != IWL_RATE_INVALID) {
+		if (rs_priv->tgg)
+			low = iwl_rates[low].prev_rs_tgg;
+		else
+			low = iwl_rates[low].prev_rs;
+		if (low == IWL_RATE_INVALID)
+			break;
+		if (rate_mask & (1 << low))
+			break;
+		IWL_DEBUG_RATE("Skipping masked lower rate: %d\n", low);
+	}
+
+	high = index;
+	while (high != IWL_RATE_INVALID) {
+		if (rs_priv->tgg)
+			high = iwl_rates[high].next_rs_tgg;
+		else
+			high = iwl_rates[high].next_rs;
+		if (high == IWL_RATE_INVALID)
+			break;
+		if (rate_mask & (1 << high))
+			break;
+		IWL_DEBUG_RATE("Skipping masked higher rate: %d\n", high);
+	}
+
+	return (high << 8) | low;
+}
+
+/**
+ * rs_get_rate - find the rate for the requested packet
+ *
+ * Returns the ieee80211_rate structure allocated by the driver.
+ *
+ * The rate control algorithm has no internal mapping between hw_mode's
+ * rate ordering and the rate ordering used by the rate control algorithm.
+ *
+ * The rate control algorithm uses a single table of rates that goes across
+ * the entire A/B/G spectrum vs. being limited to just one particular
+ * hw_mode.
+ *
+ * As such, we can't convert the index obtained below into the hw_mode's
+ * rate table and must reference the driver allocated rate table
+ *
+ */
+static struct ieee80211_rate *rs_get_rate(void *priv_rate,
+					  struct net_device *dev,
+					  struct sk_buff *skb,
+					  struct rate_control_extra *extra)
+{
+	u8 low = IWL_RATE_INVALID;
+	u8 high = IWL_RATE_INVALID;
+	u16 high_low;
+	int index;
+	struct iwl_rate_scale_priv *rs_priv;
+	struct iwl_rate_scale_data *window = NULL;
+	int current_tpt = IWL_INVALID_VALUE;
+	int low_tpt = IWL_INVALID_VALUE;
+	int high_tpt = IWL_INVALID_VALUE;
+	u32 fail_count;
+	s8 scale_action = 0;
+	unsigned long flags;
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	struct sta_info *sta;
+	u16 fc, rate_mask;
+	struct iwl_priv *priv = (struct iwl_priv *)priv_rate;
+
+	IWL_DEBUG_RATE("enter\n");
+
+	memset(extra, 0, sizeof(*extra));
+
+	fc = le16_to_cpu(hdr->frame_control);
+	if (((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA) ||
+	    (is_multicast_ether_addr(hdr->addr1))) {
+		/* Send management frames and broadcast/multicast data using
+		 * lowest rate. */
+		/* TODO: this could probably be improved.. */
+		IWL_DEBUG_RATE("leave: lowest rate (not data or is "
+			       "multicast)\n");
+
+		return iwl_get_lowest_rate(local);
+	}
+
+	sta = sta_info_get(local, hdr->addr1);
+	if (!sta || !sta->rate_ctrl_priv) {
+		IWL_DEBUG_RATE("leave: No STA priv data to update!\n");
+		if (sta)
+			sta_info_put(sta);
+		return NULL;
+	}
+
+	rate_mask = sta->supp_rates;
+	index = min(sta->txrate & 0xffff, IWL_RATE_COUNT - 1);
+
+	rs_priv = (void *)sta->rate_ctrl_priv;
+
+	if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) &&
+	    !rs_priv->ibss_sta_added) {
+		u8 sta_id = iwl_hw_find_station(priv, hdr->addr1);
+
+		if (sta_id == IWL_INVALID_STATION) {
+			IWL_DEBUG_RATE("LQ: ADD station " MAC_FMT "\n",
+					MAC_ARG(hdr->addr1));
+			sta_id = iwl_add_station(priv,
+				    hdr->addr1, 0, CMD_ASYNC);
+		}
+		if (sta_id != IWL_INVALID_STATION)
+			rs_priv->ibss_sta_added = 1;
+	}
+
+	spin_lock_irqsave(&rs_priv->lock, flags);
+
+	if (rs_priv->start_rate != IWL_RATE_INVALID) {
+		index = rs_priv->start_rate;
+		rs_priv->start_rate = IWL_RATE_INVALID;
+	}
+
+	window = &(rs_priv->win[index]);
+
+	fail_count = window->counter - window->success_counter;
+
+	if (((fail_count <= IWL_RATE_MIN_FAILURE_TH) &&
+	     (window->success_counter < IWL_RATE_MIN_SUCCESS_TH))) {
+		window->average_tpt = IWL_INVALID_VALUE;
+		spin_unlock_irqrestore(&rs_priv->lock, flags);
+
+		IWL_DEBUG_RATE("Invalid average_tpt on rate %d: "
+			       "counter: %d, success_counter: %d, "
+			       "expected_tpt is %sNULL\n",
+			       index,
+			       window->counter,
+			       window->success_counter,
+			       rs_priv->expected_tpt ? "not " : "");
+		goto out;
+
+	}
+
+	window->average_tpt = ((window->success_ratio *
+				rs_priv->expected_tpt[index] + 64) / 128);
+	current_tpt = window->average_tpt;
+
+	high_low = iwl_get_adjacent_rate(rs_priv, index, rate_mask,
+					 local->hw.conf.phymode);
+	low = high_low & 0xff;
+	high = (high_low >> 8) & 0xff;
+
+	if (low != IWL_RATE_INVALID)
+		low_tpt = rs_priv->win[low].average_tpt;
+
+	if (high != IWL_RATE_INVALID)
+		high_tpt = rs_priv->win[high].average_tpt;
+
+	spin_unlock_irqrestore(&rs_priv->lock, flags);
+
+	scale_action = 1;
+
+	if ((window->success_ratio < IWL_RATE_DECREASE_TH) || !current_tpt) {
+		IWL_DEBUG_RATE("decrease rate because of low success_ratio\n");
+		scale_action = -1;
+	} else if ((low_tpt == IWL_INVALID_VALUE) &&
+		   (high_tpt == IWL_INVALID_VALUE)) {
+		scale_action = 1;
+	} else if ((low_tpt != IWL_INVALID_VALUE) &&
+		   (high_tpt != IWL_INVALID_VALUE)
+		   && (low_tpt < current_tpt)
+		   && (high_tpt < current_tpt)) {
+		IWL_DEBUG_RATE("No action -- low [%d] & high [%d] < "
+			       "current_tpt [%d]\n",
+			       low_tpt, high_tpt, current_tpt);
+		scale_action = 0;
+	} else {
+		if (high_tpt != IWL_INVALID_VALUE) {
+			if (high_tpt > current_tpt)
+				scale_action = 1;
+			else {
+				IWL_DEBUG_RATE
+				    ("decrease rate because of high tpt\n");
+				scale_action = -1;
+			}
+		} else if (low_tpt != IWL_INVALID_VALUE) {
+			if (low_tpt > current_tpt) {
+				IWL_DEBUG_RATE
+				    ("decrease rate because of low tpt\n");
+				scale_action = -1;
+			} else
+				scale_action = 1;
+		}
+	}
+
+	if ((window->success_ratio > IWL_RATE_HIGH_TH) ||
+	    (current_tpt > window->average_tpt)) {
+		IWL_DEBUG_RATE("No action -- success_ratio [%d] > HIGH_TH or "
+			       "current_tpt [%d] > average_tpt [%d]\n",
+			       window->success_ratio,
+			       current_tpt, window->average_tpt);
+		scale_action = 0;
+	}
+
+	switch (scale_action) {
+	case -1:
+		if (low != IWL_RATE_INVALID)
+			index = low;
+		break;
+
+	case 1:
+		if (high != IWL_RATE_INVALID)
+			index = high;
+
+		break;
+
+	case 0:
+	default:
+		break;
+	}
+
+	IWL_DEBUG_RATE("Selected %d (action %d) - low %d high %d\n",
+		       index, scale_action, low, high);
+
+ out:
+
+	sta->last_txrate = index;
+	sta->txrate = sta->last_txrate;
+	sta_info_put(sta);
+
+	IWL_DEBUG_RATE("leave: %d\n", index);
+
+	return &priv->ieee_rates[index];
+}
+
+static struct rate_control_ops rs_ops = {
+	.module = NULL,
+	.name = RS_NAME,
+	.tx_status = rs_tx_status,
+	.get_rate = rs_get_rate,
+	.rate_init = rs_rate_init,
+	.clear = rs_clear,
+	.alloc = rs_alloc,
+	.free = rs_free,
+	.alloc_sta = rs_alloc_sta,
+	.free_sta = rs_free_sta,
+};
+
+int iwl_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+	struct iwl_priv *priv = hw->priv;
+	struct iwl_rate_scale_priv *rs_priv;
+	struct sta_info *sta;
+	unsigned long flags;
+	int count = 0, i;
+	u32 samples = 0, success = 0, good = 0;
+	unsigned long now = jiffies;
+	u32 max_time = 0;
+
+	sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
+	if (!sta || !sta->rate_ctrl_priv) {
+		if (sta) {
+			sta_info_put(sta);
+			IWL_DEBUG_RATE("leave - no private rate data!\n");
+		} else
+			IWL_DEBUG_RATE("leave - no station!\n");
+		return sprintf(buf, "station %d not found\n", sta_id);
+	}
+
+	rs_priv = (void *)sta->rate_ctrl_priv;
+	spin_lock_irqsave(&rs_priv->lock, flags);
+	i = IWL_RATE_54M_INDEX;
+	while (1) {
+		u64 mask;
+		int j;
+
+		count +=
+		    sprintf(&buf[count], " %2dMbs: ", iwl_rates[i].ieee / 2);
+
+		mask = (1ULL << (IWL_RATE_MAX_WINDOW - 1));
+		for (j = 0; j < IWL_RATE_MAX_WINDOW; j++, mask >>= 1)
+			buf[count++] =
+			    (rs_priv->win[i].data & mask) ? '1' : '0';
+
+		samples += rs_priv->win[i].counter;
+		good += rs_priv->win[i].success_counter;
+		success += rs_priv->win[i].success_counter * iwl_rates[i].ieee;
+
+		if (rs_priv->win[i].stamp) {
+			int delta =
+			    jiffies_to_msecs(now - rs_priv->win[i].stamp);
+
+			if (delta > max_time)
+				max_time = delta;
+
+			count += sprintf(&buf[count], "%5dms\n", delta);
+		} else
+			buf[count++] = '\n';
+
+		j = iwl_get_prev_ieee_rate(i);
+		if (j == i)
+			break;
+		i = j;
+	}
+	spin_unlock_irqrestore(&rs_priv->lock, flags);
+	sta_info_put(sta);
+
+	/* Display the average rate of all samples taken.
+	 *
+	 * NOTE:  We multiple # of samples by 2 since the IEEE measurement
+	 * added from iwl_rates is actually 2X the rate */
+	if (samples)
+		count += sprintf(
+			&buf[count],
+			"\nAverage rate is %3d.%02dMbs over last %4dms\n"
+			"%3d%% success (%d good packets over %d tries)\n",
+			success / (2 * samples), (success * 5 / samples) % 10,
+			max_time, good * 100 / samples, good, samples);
+	else
+		count += sprintf(&buf[count], "\nAverage rate: 0Mbs\n");
+
+	return count;
+}
+
+void iwl_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
+{
+	struct iwl_priv *priv = hw->priv;
+	s32 rssi = 0;
+	unsigned long flags;
+	struct ieee80211_local *local = hw_to_local(hw);
+	struct iwl_rate_scale_priv *rs_priv;
+	struct sta_info *sta;
+
+	IWL_DEBUG_RATE("enter\n");
+
+	if (!local->rate_ctrl->ops->name ||
+	    strcmp(local->rate_ctrl->ops->name, RS_NAME)) {
+		IWL_WARNING("iwl-3945-rs not selected as rate control "
+			    "aglo!\n");
+		IWL_DEBUG_RATE("leave - mac80211 picked the wrong RC algo.\n");
+		return;
+	}
+
+	sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
+	if (!sta || !sta->rate_ctrl_priv) {
+		if (sta)
+			sta_info_put(sta);
+		IWL_DEBUG_RATE("leave - no private rate data!\n");
+		return;
+	}
+
+	rs_priv = (void *)sta->rate_ctrl_priv;
+
+	spin_lock_irqsave(&rs_priv->lock, flags);
+
+	rs_priv->tgg = 0;
+	switch (priv->phymode) {
+	case MODE_IEEE80211G:
+		if (priv->active_rxon.flags & RXON_FLG_TGG_PROTECT_MSK) {
+			rs_priv->tgg = 1;
+			rs_priv->expected_tpt = iwl_expected_tpt_g_prot;
+		} else
+			rs_priv->expected_tpt = iwl_expected_tpt_g;
+		break;
+
+	case MODE_IEEE80211A:
+		rs_priv->expected_tpt = iwl_expected_tpt_a;
+		break;
+
+	default:
+		IWL_WARNING("Invalid phymode.  Defaulting to 802.11b\n");
+	case MODE_IEEE80211B:
+		rs_priv->expected_tpt = iwl_expected_tpt_b;
+		break;
+	}
+
+	sta_info_put(sta);
+	spin_unlock_irqrestore(&rs_priv->lock, flags);
+
+	rssi = priv->last_rx_rssi;
+	if (rssi == 0)
+		rssi = IWL_MIN_RSSI_VAL;
+
+	IWL_DEBUG(IWL_DL_INFO | IWL_DL_RATE, "Network RSSI: %d\n", rssi);
+
+	rs_priv->start_rate = iwl_get_rate_index_by_rssi(rssi, priv->phymode);
+
+	IWL_DEBUG_RATE("leave: rssi %d assign rate index: "
+		       "%d (plcp 0x%x)\n", rssi, rs_priv->start_rate,
+		       iwl_rates[rs_priv->start_rate].plcp);
+}
+
+void iwl_rate_control_register(void)
+{
+	ieee80211_rate_control_register(&rs_ops);
+}
+
+void iwl_rate_control_unregister(void)
+{
+	ieee80211_rate_control_unregister(&rs_ops);
+}
+
+
diff -ruN linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-3945-rs.h linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-3945-rs.h
--- linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-3945-rs.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-3945-rs.h	2007-07-31 15:29:35.000000000 +0200
@@ -0,0 +1,221 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2005 - 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#ifndef __iwl_3945_rs_h__
+#define __iwl_3945_rs_h__
+
+struct iwl_rate_info {
+	u8 plcp;
+	u8 ieee;
+	u8 prev_ieee;		/* previous rate in IEEE speeds */
+	u8 next_ieee;		/* next rate in IEEE speeds */
+	u8 prev_rs;		/* previous rate used in rs algo */
+	u8 next_rs;		/* next rate used in rs algo */
+	u8 prev_rs_tgg;		/* previous rate used in TGG rs algo */
+	u8 next_rs_tgg;		/* next rate used in TGG rs algo */
+};
+
+enum {
+	IWL_RATE_6M_INDEX = 0,
+	IWL_RATE_9M_INDEX,
+	IWL_RATE_12M_INDEX,
+	IWL_RATE_18M_INDEX,
+	IWL_RATE_24M_INDEX,
+	IWL_RATE_36M_INDEX,
+	IWL_RATE_48M_INDEX,
+	IWL_RATE_54M_INDEX,
+	IWL_RATE_1M_INDEX,
+	IWL_RATE_2M_INDEX,
+	IWL_RATE_5M_INDEX,
+	IWL_RATE_11M_INDEX,
+	IWL_RATE_COUNT,
+	IWL_RATE_INVM_INDEX,
+	IWL_RATE_INVALID = IWL_RATE_INVM_INDEX
+};
+
+enum {
+	IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX,
+	IWL_LAST_OFDM_RATE = IWL_RATE_54M_INDEX,
+	IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX,
+	IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX,
+};
+
+/* #define vs. enum to keep from defaulting to 'large integer' */
+#define	IWL_RATE_6M_MASK   (1<<IWL_RATE_6M_INDEX)
+#define	IWL_RATE_9M_MASK   (1<<IWL_RATE_9M_INDEX)
+#define	IWL_RATE_12M_MASK  (1<<IWL_RATE_12M_INDEX)
+#define	IWL_RATE_18M_MASK  (1<<IWL_RATE_18M_INDEX)
+#define	IWL_RATE_24M_MASK  (1<<IWL_RATE_24M_INDEX)
+#define	IWL_RATE_36M_MASK  (1<<IWL_RATE_36M_INDEX)
+#define	IWL_RATE_48M_MASK  (1<<IWL_RATE_48M_INDEX)
+#define	IWL_RATE_54M_MASK  (1<<IWL_RATE_54M_INDEX)
+#define	IWL_RATE_1M_MASK   (1<<IWL_RATE_1M_INDEX)
+#define	IWL_RATE_2M_MASK   (1<<IWL_RATE_2M_INDEX)
+#define	IWL_RATE_5M_MASK   (1<<IWL_RATE_5M_INDEX)
+#define	IWL_RATE_11M_MASK  (1<<IWL_RATE_11M_INDEX)
+
+enum {
+	IWL_RATE_6M_PLCP = 13,
+	IWL_RATE_9M_PLCP = 15,
+	IWL_RATE_12M_PLCP = 5,
+	IWL_RATE_18M_PLCP = 7,
+	IWL_RATE_24M_PLCP = 9,
+	IWL_RATE_36M_PLCP = 11,
+	IWL_RATE_48M_PLCP = 1,
+	IWL_RATE_54M_PLCP = 3,
+	IWL_RATE_1M_PLCP = 10,
+	IWL_RATE_2M_PLCP = 20,
+	IWL_RATE_5M_PLCP = 55,
+	IWL_RATE_11M_PLCP = 110,
+};
+
+enum {
+	IWL_RATE_6M_IEEE = 12,
+	IWL_RATE_9M_IEEE = 18,
+	IWL_RATE_12M_IEEE = 24,
+	IWL_RATE_18M_IEEE = 36,
+	IWL_RATE_24M_IEEE = 48,
+	IWL_RATE_36M_IEEE = 72,
+	IWL_RATE_48M_IEEE = 96,
+	IWL_RATE_54M_IEEE = 108,
+	IWL_RATE_1M_IEEE = 2,
+	IWL_RATE_2M_IEEE = 4,
+	IWL_RATE_5M_IEEE = 11,
+	IWL_RATE_11M_IEEE = 22,
+};
+
+#define IWL_CCK_BASIC_RATES_MASK    \
+       (IWL_RATE_1M_MASK          | \
+	IWL_RATE_2M_MASK)
+
+#define IWL_CCK_RATES_MASK          \
+       (IWL_BASIC_RATES_MASK      | \
+	IWL_RATE_5M_MASK          | \
+	IWL_RATE_11M_MASK)
+
+#define IWL_OFDM_BASIC_RATES_MASK   \
+	(IWL_RATE_6M_MASK         | \
+	IWL_RATE_12M_MASK         | \
+	IWL_RATE_24M_MASK)
+
+#define IWL_OFDM_RATES_MASK         \
+       (IWL_OFDM_BASIC_RATES_MASK | \
+	IWL_RATE_9M_MASK          | \
+	IWL_RATE_18M_MASK         | \
+	IWL_RATE_36M_MASK         | \
+	IWL_RATE_48M_MASK         | \
+	IWL_RATE_54M_MASK)
+
+#define IWL_BASIC_RATES_MASK         \
+	(IWL_OFDM_BASIC_RATES_MASK | \
+	 IWL_CCK_BASIC_RATES_MASK)
+
+#define IWL_RATES_MASK ((1<<IWL_RATE_COUNT)-1)
+
+#define IWL_INVALID_VALUE    -1
+
+#define IWL_MIN_RSSI_VAL                 -100
+#define IWL_MAX_RSSI_VAL                    0
+
+extern const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT];
+
+static inline int iwl_rate_index_from_plcp(int plcp)
+{
+	int i = 0;
+	for (i = 0; i < IWL_RATE_COUNT; i++)
+		if (iwl_rates[i].plcp == plcp)
+			return i;
+	return -1;
+}
+
+static inline u8 iwl_rate_get_lowest_plcp(int rate_mask)
+{
+	u8 i;
+
+	for (i = IWL_RATE_1M_INDEX; i != IWL_RATE_INVALID;
+	     i = iwl_rates[i].next_ieee) {
+		if (rate_mask & (1 << i))
+			return iwl_rates[i].plcp;
+	}
+
+	return IWL_RATE_INVALID;
+}
+
+static inline u8 iwl_get_prev_ieee_rate(u8 rate_index)
+{
+	u8 rate = iwl_rates[rate_index].prev_ieee;
+	if (rate == IWL_RATE_INVALID)
+		rate = rate_index;
+	return rate;
+}
+
+#if IWL == 3945
+/**
+ * iwl_fill_rs_info - Fill an output text buffer with the rate representation
+ *
+ * NOTE:  This is provided as a quick mechanism for a user to visualize
+ * the performance of the rate control alogirthm and is not meant to be
+ * parsed software.
+ */
+extern int iwl_fill_rs_info(struct ieee80211_hw *, char *buf, u8 sta_id);
+
+/**
+ * iwl_rate_scale_init - Initialize the rate scale table based on assoc info
+ *
+ * The specific througput table used is based on the type of network
+ * the associated with, including A, B, G, and G w/ TGG protection
+ */
+extern void iwl_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id);
+
+/**
+ * iwl_rate_control_register - Register the rate control algorithm callbacks
+ *
+ * Since the rate control algorithm is hardware specific, there is no need
+ * or reason to place it as a stand alone module.  The driver can call
+ * iwl_rate_control_register in order to register the rate control callbacks
+ * with the mac80211 subsystem.  This should be performed prior to calling
+ * ieee80211_register_hw
+ *
+ */
+extern void iwl_rate_control_register(void);
+
+/**
+ * iwl_rate_control_unregister - Unregister the rate control callbacks
+ *
+ * This should be called after calling ieee80211_unregister_hw, but before
+ * the driver is unloaded.
+ */
+extern void iwl_rate_control_unregister(void);
+#else
+static inline int iwl_fill_rs_info(struct ieee80211_hw *hw, char *buf,
+				   u8 sta_id)
+{ return -ENOTSUPP; }
+static inline void iwl_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) {}
+static inline void iwl_rate_control_register(void) {}
+static inline void iwl_rate_control_unregister(void) {}
+#endif /* IWL == 3945 */
+
+#endif
diff -ruN linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-3945.c linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-3945.c
--- linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-3945.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-3945.c	2007-07-31 15:29:35.000000000 +0200
@@ -0,0 +1,2304 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <linux/firmware.h>
+#include <net/mac80211.h>
+
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+
+#include "iwlwifi.h"
+#include "iwl-helpers.h"
+#include "iwl-3945.h"
+#include "iwl-3945-rs.h"
+
+#define IWL_DECLARE_RATE_INFO(r, ip, in, rp, rn, pp, np)    \
+	[IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP,   \
+				    IWL_RATE_##r##M_IEEE,   \
+				    IWL_RATE_##ip##M_INDEX, \
+				    IWL_RATE_##in##M_INDEX, \
+				    IWL_RATE_##rp##M_INDEX, \
+				    IWL_RATE_##rn##M_INDEX, \
+				    IWL_RATE_##pp##M_INDEX, \
+				    IWL_RATE_##np##M_INDEX }
+
+/*
+ * Parameter order:
+ *   rate, prev rate, next rate, prev tgg rate, next tgg rate
+ *
+ * If there isn't a valid next or previous rate then INV is used which
+ * maps to IWL_RATE_INVALID
+ *
+ */
+const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = {
+	IWL_DECLARE_RATE_INFO(6, 5, 9, 5, 11, 5, 11),        /*  6mbps */
+	IWL_DECLARE_RATE_INFO(9, 6, 11, 5, 11, 5, 11),       /*  9mbps */
+	IWL_DECLARE_RATE_INFO(12, 11, 18, 11, 18, 11, 18),   /* 12mbps */
+	IWL_DECLARE_RATE_INFO(18, 12, 24, 12, 24, 11, 24),   /* 18mbps */
+	IWL_DECLARE_RATE_INFO(24, 18, 36, 18, 36, 18, 36),   /* 24mbps */
+	IWL_DECLARE_RATE_INFO(36, 24, 48, 24, 48, 24, 48),   /* 36mbps */
+	IWL_DECLARE_RATE_INFO(48, 36, 54, 36, 54, 36, 54),   /* 48mbps */
+	IWL_DECLARE_RATE_INFO(54, 48, INV, 48, INV, 48, INV),/* 54mbps */
+	IWL_DECLARE_RATE_INFO(1, INV, 2, INV, 2, INV, 2),    /*  1mbps */
+	IWL_DECLARE_RATE_INFO(2, 1, 5, 1, 5, 1, 5),          /*  2mbps */
+	IWL_DECLARE_RATE_INFO(5, 2, 6, 2, 11, 2, 11),        /*5.5mbps */
+	IWL_DECLARE_RATE_INFO(11, 9, 12, 5, 12, 5, 18),      /* 11mbps */
+};
+
+/* 1 = enable the iwl_disable_events() function */
+#define IWL_EVT_DISABLE (0)
+#define IWL_EVT_DISABLE_SIZE (1532/32)
+
+/**
+ * iwl_disable_events - Disable selected events in uCode event log
+ *
+ * Disable an event by writing "1"s into "disable"
+ *   bitmap in SRAM.  Bit position corresponds to Event # (id/type).
+ *   Default values of 0 enable uCode events to be logged.
+ * Use for only special debugging.  This function is just a placeholder as-is,
+ *   you'll need to provide the special bits! ...
+ *   ... and set IWL_EVT_DISABLE to 1. */
+void iwl_disable_events(struct iwl_priv *priv)
+{
+	int rc;
+	int i;
+	u32 base;		/* SRAM address of event log header */
+	u32 disable_ptr;	/* SRAM address of event-disable bitmap array */
+	u32 array_size;		/* # of u32 entries in array */
+	u32 evt_disable[IWL_EVT_DISABLE_SIZE] = {
+		0x00000000,	/*   31 -    0  Event id numbers */
+		0x00000000,	/*   63 -   32 */
+		0x00000000,	/*   95 -   64 */
+		0x00000000,	/*  127 -   96 */
+		0x00000000,	/*  159 -  128 */
+		0x00000000,	/*  191 -  160 */
+		0x00000000,	/*  223 -  192 */
+		0x00000000,	/*  255 -  224 */
+		0x00000000,	/*  287 -  256 */
+		0x00000000,	/*  319 -  288 */
+		0x00000000,	/*  351 -  320 */
+		0x00000000,	/*  383 -  352 */
+		0x00000000,	/*  415 -  384 */
+		0x00000000,	/*  447 -  416 */
+		0x00000000,	/*  479 -  448 */
+		0x00000000,	/*  511 -  480 */
+		0x00000000,	/*  543 -  512 */
+		0x00000000,	/*  575 -  544 */
+		0x00000000,	/*  607 -  576 */
+		0x00000000,	/*  639 -  608 */
+		0x00000000,	/*  671 -  640 */
+		0x00000000,	/*  703 -  672 */
+		0x00000000,	/*  735 -  704 */
+		0x00000000,	/*  767 -  736 */
+		0x00000000,	/*  799 -  768 */
+		0x00000000,	/*  831 -  800 */
+		0x00000000,	/*  863 -  832 */
+		0x00000000,	/*  895 -  864 */
+		0x00000000,	/*  927 -  896 */
+		0x00000000,	/*  959 -  928 */
+		0x00000000,	/*  991 -  960 */
+		0x00000000,	/* 1023 -  992 */
+		0x00000000,	/* 1055 - 1024 */
+		0x00000000,	/* 1087 - 1056 */
+		0x00000000,	/* 1119 - 1088 */
+		0x00000000,	/* 1151 - 1120 */
+		0x00000000,	/* 1183 - 1152 */
+		0x00000000,	/* 1215 - 1184 */
+		0x00000000,	/* 1247 - 1216 */
+		0x00000000,	/* 1279 - 1248 */
+		0x00000000,	/* 1311 - 1280 */
+		0x00000000,	/* 1343 - 1312 */
+		0x00000000,	/* 1375 - 1344 */
+		0x00000000,	/* 1407 - 1376 */
+		0x00000000,	/* 1439 - 1408 */
+		0x00000000,	/* 1471 - 1440 */
+		0x00000000,	/* 1503 - 1472 */
+	};
+
+	base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
+	if (!VALID_RTC_DATA_ADDR(base)) {
+		IWL_ERROR("Invalid event log pointer 0x%08X\n", base);
+		return;
+	}
+
+	rc = iwl_grab_restricted_access(priv);
+	if (rc) {
+		IWL_WARNING("Can not read from adapter at this time.\n");
+		return;
+	}
+
+	disable_ptr = iwl_read_restricted_mem(priv, base + (4 * sizeof(u32)));
+	array_size = iwl_read_restricted_mem(priv, base + (5 * sizeof(u32)));
+	iwl_release_restricted_access(priv);
+
+	if (IWL_EVT_DISABLE && (array_size == IWL_EVT_DISABLE_SIZE)) {
+		IWL_DEBUG_INFO("Disabling selected uCode log events at 0x%x\n",
+			       disable_ptr);
+		rc = iwl_grab_restricted_access(priv);
+		for (i = 0; i < IWL_EVT_DISABLE_SIZE; i++) {
+			iwl_write_restricted_mem(priv,
+						 disable_ptr +
+						 (i * sizeof(u32)),
+						 evt_disable[i]);
+		}
+		iwl_release_restricted_access(priv);
+	} else {
+		IWL_DEBUG_INFO("Selected uCode log events may be disabled\n");
+		IWL_DEBUG_INFO("  by writing \"1\"s into disable bitmap\n");
+		IWL_DEBUG_INFO("  in SRAM at 0x%x, size %d u32s\n",
+			       disable_ptr, array_size);
+	}
+
+}
+
+/**
+ * iwl3945_get_antenna_flags - Get antenna flags for RXON command
+ * @priv: eeprom and antenna fields are used to determine antenna flags
+ *
+ * priv->eeprom  is used to determine if antenna AUX/MAIN are reversed
+ * priv->antenna specifies the antenna diversity mode:
+ *
+ * IWL_ANTENNA_DIVERISTY - NIC selects best antenna by itself
+ * IWL_ANTENNA_MAIN      - Force MAIN antenna
+ * IWL_ANTENNA_AUX       - Force AUX antenna
+ */
+__le32 iwl3945_get_antenna_flags(const struct iwl_priv *priv)
+{
+	switch (priv->antenna) {
+	case IWL_ANTENNA_DIVERSITY:
+		return 0;
+
+	case IWL_ANTENNA_MAIN:
+		if (priv->eeprom.antenna_switch_type)
+			return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_B_MSK;
+		return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_A_MSK;
+
+	case IWL_ANTENNA_AUX:
+		if (priv->eeprom.antenna_switch_type)
+			return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_A_MSK;
+		return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_B_MSK;
+	}
+
+	/* bad antenna selector value */
+	IWL_ERROR("Bad antenna selector value (0x%x)\n", priv->antenna);
+	return 0;		/* "diversity" is default if error */
+}
+
+/*****************************************************************************
+ *
+ * Intel PRO/Wireless 3945ABG/BG Network Connection
+ *
+ *  RX handler implementations
+ *
+ *  Used by iwl-base.c
+ *
+ *****************************************************************************/
+
+void iwl_hw_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
+{
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	IWL_DEBUG_RX("Statistics notification received (%d vs %d).\n",
+		     (int)sizeof(struct iwl_notif_statistics),
+		     le32_to_cpu(pkt->len));
+
+	memcpy(&priv->statistics, pkt->u.raw, sizeof(priv->statistics));
+
+	priv->last_statistics_time = jiffies;
+}
+
+static void iwl3945_handle_data_packet(struct iwl_priv *priv, int is_data,
+				   struct iwl_rx_mem_buffer *rxb,
+				   struct ieee80211_rx_status *stats,
+				   u16 phy_flags)
+{
+	struct ieee80211_hdr *hdr;
+	struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+	struct iwl_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
+	struct iwl_rx_frame_end *rx_end = IWL_RX_END(pkt);
+	short len = le16_to_cpu(rx_hdr->len);
+
+	/* We received data from the HW, so stop the watchdog */
+	if (unlikely((len + IWL_RX_FRAME_SIZE) > skb_tailroom(rxb->skb))) {
+		IWL_DEBUG_DROP("Corruption detected!\n");
+		return;
+	}
+
+	/* We only process data packets if the interface is open */
+	if (unlikely(!priv->is_open)) {
+		IWL_DEBUG_DROP_LIMIT
+		    ("Dropping packet while interface is not open.\n");
+		return;
+	}
+	if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) {
+		if (iwl_param_hwcrypto)
+			iwl_set_decrypted_flag(priv, rxb->skb,
+					       le32_to_cpu(rx_end->status),
+					       stats);
+		iwl_handle_data_packet_monitor(priv, rxb, IWL_RX_DATA(pkt),
+					       len, stats, phy_flags);
+		return;
+	}
+
+	skb_reserve(rxb->skb, (void *)rx_hdr->payload - (void *)pkt);
+	/* Set the size of the skb to the size of the frame */
+	skb_put(rxb->skb, le16_to_cpu(rx_hdr->len));
+
+	hdr = (void *)rxb->skb->data;
+
+	if (iwl_param_hwcrypto)
+		iwl_set_decrypted_flag(priv, rxb->skb,
+				       le32_to_cpu(rx_end->status), stats);
+
+	ieee80211_rx_irqsafe(priv->hw, rxb->skb, stats);
+	rxb->skb = NULL;
+}
+
+static void iwl3945_rx_reply_rx(struct iwl_priv *priv,
+				struct iwl_rx_mem_buffer *rxb)
+{
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	struct iwl_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt);
+	struct iwl_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
+	struct iwl_rx_frame_end *rx_end = IWL_RX_END(pkt);
+	struct ieee80211_hdr *header;
+	u16 phy_flags = le16_to_cpu(rx_hdr->phy_flags);
+	struct ieee80211_rx_status stats = {
+		.mactime = le32_to_cpu(rx_end->beacon_timestamp),
+		.freq = ieee80211chan2mhz(le16_to_cpu(rx_hdr->channel)),
+		.channel = le16_to_cpu(rx_hdr->channel),
+		.phymode = (rx_hdr->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ?
+		MODE_IEEE80211G : MODE_IEEE80211A,
+		.antenna = 0,
+		.rate = rx_hdr->rate,
+		.flag = 0,
+	};
+
+	u8 network_packet;
+	int snr;
+
+	if ((unlikely(rx_stats->phy_count > 20))) {
+		IWL_DEBUG_DROP
+		    ("dsp size out of range [0,20]: "
+		     "%d/n", rx_stats->phy_count);
+		return;
+	}
+
+	if (!(le32_to_cpu(rx_end->status) & RX_RES_STATUS_NO_CRC32_ERROR)
+	    || !(le32_to_cpu(rx_end->status) &
+		 RX_RES_STATUS_NO_RXE_OVERFLOW)) {
+		IWL_DEBUG_RX("Bad CRC or FIFO: 0x%08X.\n", rx_end->status);
+		return;
+	}
+
+	if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) {
+		iwl3945_handle_data_packet(priv, 1, rxb, &stats, phy_flags);
+		return;
+	}
+
+	/* Convert 3945's rssi indicator to dBm */
+	stats.ssi = rx_stats->rssi - IWL_RSSI_OFFSET;
+
+	/* Set default noise value to -127 ... this works better than 0 when
+	 *   averaging frames with/without noise info; measured values are
+	 *   always negative ... using a negative value as the default value
+	 *   keeps all averages within an s8's range of negative values. */
+	if (priv->last_rx_noise == 0)
+		priv->last_rx_noise = -127;
+
+	/* 3945 provides noise info for OFDM frames only.
+	 * sig_avg and noise_diff are measured by the 3945's digital signal
+	 *   processor (DSP), and indicate linear levels of signal level and
+	 *   distortion/noise within the packet preamble after
+	 *   automatic gain control (AGC).  sig_avg should stay fairly
+	 *   constant if the radio's AGC is working well.
+	 * Since these values are linear (not dB or dBm), linear
+	 *   signal-to-noise ratio (SNR) is (sig_avg / noise_diff).
+	 * Convert linear SNR to dB SNR, then subtract that from rssi dBm
+	 *   to obtain noise level in dBm.
+	 * Calculate stats.signal (quality indicator in %) based on SNR. */
+	if (rx_stats->noise_diff) {
+		snr = rx_stats->sig_avg / rx_stats->noise_diff;
+		stats.noise = stats.ssi - iwl_calc_db_from_ratio(snr);
+		stats.signal = iwl_calc_sig_qual(stats.ssi, stats.noise);
+
+	/* If noise info not available, calculate signal quality indicator (%)
+	 *   using just the dBm signal level. */
+	} else {
+		stats.noise = priv->last_rx_noise;
+		stats.signal = iwl_calc_sig_qual(stats.ssi, 0);
+	}
+
+
+	IWL_DEBUG_STATS("Rssi %d noise %d qual %d sig_avg %d noise_diff %d\n",
+			stats.ssi, stats.noise, stats.signal,
+			rx_stats->sig_avg, rx_stats->noise_diff);
+
+	stats.freq = ieee80211chan2mhz(stats.channel);
+
+	/* can be covered by iwl_report_frame() in most cases */
+/*      IWL_DEBUG_RX("RX status: 0x%08X\n", rx_end->status); */
+
+	header = (struct ieee80211_hdr *)IWL_RX_DATA(pkt);
+
+	network_packet = iwl_is_network_packet(priv, header);
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+	if (iwl_debug_level & IWL_DL_STATS && net_ratelimit())
+		IWL_DEBUG_STATS
+		    ("[%c] %d RSSI: %d Signal: %u, Noise: %u, Rate: %u\n",
+		     network_packet ? '*' : ' ',
+		     stats.channel, stats.ssi, stats.ssi,
+		     stats.ssi, stats.rate);
+
+	if (iwl_debug_level & (IWL_DL_RX))
+		/* Set "1" to report good data frames in groups of 100 */
+		iwl_report_frame(priv, pkt, header, 1);
+#endif
+
+	if (network_packet) {
+		priv->last_beacon_time = le32_to_cpu(rx_end->beacon_timestamp);
+		priv->last_tsf = le64_to_cpu(rx_end->timestamp);
+		priv->last_rx_rssi = stats.ssi;
+		priv->last_rx_noise = stats.noise;
+	}
+
+	switch (WLAN_FC_GET_TYPE(le16_to_cpu(header->frame_control))) {
+	case IEEE80211_FTYPE_MGMT:
+		switch (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_control))) {
+		case IEEE80211_STYPE_PROBE_RESP:
+		case IEEE80211_STYPE_BEACON:{
+				/* If this is a beacon or probe response for
+				 * our network then cache the beacon
+				 * timestamp */
+				if ((((priv->iw_mode == IEEE80211_IF_TYPE_STA)
+				      && !compare_ether_addr(header->addr2,
+							     priv->bssid)) ||
+				     ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS)
+				      && !compare_ether_addr(header->addr3,
+							     priv->bssid)))) {
+					struct ieee80211_mgmt *mgmt =
+					    (struct ieee80211_mgmt *)header;
+					__le32 *pos;
+					pos =
+					    (__le32 *) & mgmt->u.beacon.
+					    timestamp;
+					priv->timestamp0 = le64_to_cpu(pos[0]);
+					priv->timestamp1 = le32_to_cpu(pos[1]);
+					priv->beacon_int = le16_to_cpu(
+					    mgmt->u.beacon.beacon_int);
+					if (priv->call_post_assoc_from_beacon &&
+					    (priv->iw_mode ==
+						IEEE80211_IF_TYPE_STA))
+						queue_work(priv->workqueue,
+						    &priv->post_associate.work);
+
+					priv->call_post_assoc_from_beacon = 0;
+				}
+
+				break;
+			}
+
+		case IEEE80211_STYPE_ACTION:
+			/* TODO: Parse 802.11h frames for CSA... */
+			break;
+
+			/*
+			 * TODO: There is no callback function from upper
+			 * stack to inform us when associated status. this
+			 * work around to sniff assoc_resp management frame
+			 * and finish the association process.
+			 */
+		case IEEE80211_STYPE_ASSOC_RESP:
+		case IEEE80211_STYPE_REASSOC_RESP:{
+				struct ieee80211_mgmt *mgnt =
+				    (struct ieee80211_mgmt *)header;
+				priv->assoc_id = (~((1 << 15) | (1 << 14)) &
+						  le16_to_cpu(mgnt->u.
+							      assoc_resp.aid));
+				priv->assoc_capability =
+				    le16_to_cpu(mgnt->u.assoc_resp.capab_info);
+				if (priv->beacon_int)
+					queue_work(priv->workqueue,
+					    &priv->post_associate.work);
+				else
+					priv->call_post_assoc_from_beacon = 1;
+				break;
+			}
+
+		case IEEE80211_STYPE_PROBE_REQ:{
+				if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS)
+					IWL_DEBUG_DROP
+					    ("Dropping (non network): " MAC_FMT
+					     ", " MAC_FMT ", " MAC_FMT "\n",
+					     MAC_ARG(header->addr1),
+					     MAC_ARG(header->addr2),
+					     MAC_ARG(header->addr3));
+				return;
+			}
+		}
+
+		iwl3945_handle_data_packet(priv, 0, rxb, &stats, phy_flags);
+		break;
+
+	case IEEE80211_FTYPE_CTL:
+		break;
+
+	case IEEE80211_FTYPE_DATA:
+		if (unlikely(is_duplicate_packet(priv, header)))
+			IWL_DEBUG_DROP("Dropping (dup): " MAC_FMT ", "
+				       MAC_FMT ", " MAC_FMT "\n",
+				       MAC_ARG(header->addr1),
+				       MAC_ARG(header->addr2),
+				       MAC_ARG(header->addr3));
+		else
+			iwl3945_handle_data_packet(priv, 1, rxb, &stats,
+						   phy_flags);
+		break;
+	}
+}
+
+int iwl_hw_tx_queue_attach_buffer_to_tfd(struct iwl_priv *priv,
+					 void *ptr, dma_addr_t addr, u16 len)
+{
+	int count;
+	u32 pad;
+	struct iwl_tfd_frame *tfd = (struct iwl_tfd_frame *)ptr;
+
+	count = TFD_CTL_COUNT_GET(le32_to_cpu(tfd->control_flags));
+	pad = TFD_CTL_PAD_GET(le32_to_cpu(tfd->control_flags));
+
+	if ((count >= NUM_TFD_CHUNKS) || (count < 0)) {
+		IWL_ERROR("Error can not send more than %d chunks\n",
+			  NUM_TFD_CHUNKS);
+		return -EINVAL;
+	}
+
+	tfd->pa[count].addr = cpu_to_le32(addr);
+	tfd->pa[count].len = cpu_to_le32(len);
+
+	count++;
+
+	tfd->control_flags = cpu_to_le32(TFD_CTL_COUNT_SET(count) |
+					 TFD_CTL_PAD_SET(pad));
+
+	return 0;
+}
+
+/**
+ * iwl_hw_tx_queue_free_tfd - Free one TFD, those at index [txq->q.last_used]
+ *
+ * Does NOT advance any indexes
+ */
+int iwl_hw_tx_queue_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq)
+{
+	struct iwl_tfd_frame *bd_tmp = (struct iwl_tfd_frame *)&txq->bd[0];
+	struct iwl_tfd_frame *bd = &bd_tmp[txq->q.last_used];
+	struct pci_dev *dev = priv->pci_dev;
+	int i;
+	int counter;
+
+	/* classify bd */
+	if (txq->q.id == IWL_CMD_QUEUE_NUM)
+		/* nothing to cleanup after for host commands */
+		return 0;
+
+	/* sanity check */
+	counter = TFD_CTL_COUNT_GET(le32_to_cpu(bd->control_flags));
+	if (counter > NUM_TFD_CHUNKS) {
+		IWL_ERROR("Too many chunks: %i\n", counter);
+		/* @todo issue fatal error, it is quite serious situation */
+		return 0;
+	}
+
+	/* unmap chunks if any */
+
+	for (i = 1; i < counter; i++) {
+		pci_unmap_single(dev, le32_to_cpu(bd->pa[i].addr),
+				 le32_to_cpu(bd->pa[i].len), PCI_DMA_TODEVICE);
+		if (txq->txb[txq->q.last_used].skb[0]) {
+			struct sk_buff *skb = txq->txb[txq->q.last_used].skb[0];
+			if (txq->txb[txq->q.last_used].skb[0]) {
+				/* Can be called from interrupt context */
+				dev_kfree_skb_any(skb);
+				txq->txb[txq->q.last_used].skb[0] = NULL;
+			}
+		}
+	}
+	return 0;
+}
+
+u8 iwl_hw_find_station(struct iwl_priv *priv, const u8 *bssid)
+{
+	int i;
+	int ret = IWL_INVALID_STATION;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->sta_lock, flags);
+	for (i = IWL_STA_ID; i < (IWL_STA_ID + priv->num_stations); i++)
+		if ((priv->stations[i].used) &&
+		    (!compare_ether_addr
+		     (priv->stations[i].sta.sta.addr, bssid))) {
+			ret = i;
+			goto out;
+		}
+
+	IWL_DEBUG_INFO("can not find STA " MAC_FMT " (total %d)\n",
+		       MAC_ARG(bssid), priv->num_stations);
+ out:
+	spin_unlock_irqrestore(&priv->sta_lock, flags);
+	return ret;
+}
+
+/**
+ * iwl_hw_build_tx_cmd_rate - Add rate portion to TX_CMD
+ *
+*/
+void iwl_hw_build_tx_cmd_rate(struct iwl_priv *priv,
+			      struct iwl_cmd *cmd,
+			      struct ieee80211_tx_control *ctrl,
+			      struct ieee80211_hdr *hdr, int sta_id, int tx_id)
+{
+	unsigned long flags;
+	u16 rate_index = min(ctrl->tx_rate & 0xffff, IWL_RATE_COUNT - 1);
+	u16 rate_mask;
+	int rate;
+	u8 rts_retry_limit;
+	u8 data_retry_limit;
+	u32 tx_flags;
+	u16 fc = le16_to_cpu(hdr->frame_control);
+
+	rate = iwl_rates[rate_index].plcp;
+	tx_flags = cmd->cmd.tx.tx_flags;
+
+	/* We need to figure out how to get the sta->supp_rates while
+	 * in this running context; perhaps encoding into ctrl->tx_rate? */
+	rate_mask = IWL_RATES_MASK;
+
+	spin_lock_irqsave(&priv->sta_lock, flags);
+
+	priv->stations[sta_id].current_rate.rate_n_flags = rate;
+
+	if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) &&
+	    (sta_id != IWL_BROADCAST_ID) && (sta_id != IWL_MULTICAST_ID))
+		priv->stations[IWL_STA_ID].current_rate.rate_n_flags = rate;
+
+	spin_unlock_irqrestore(&priv->sta_lock, flags);
+
+	if (tx_id >= IWL_CMD_QUEUE_NUM)
+		rts_retry_limit = 3;
+	else
+		rts_retry_limit = 7;
+
+	if (ieee80211_is_probe_response(fc)) {
+		data_retry_limit = 3;
+		if (data_retry_limit < rts_retry_limit)
+			rts_retry_limit = data_retry_limit;
+	} else
+		data_retry_limit = IWL_DEFAULT_TX_RETRY;
+
+	if (priv->data_retry_limit != -1)
+		data_retry_limit = priv->data_retry_limit;
+
+	if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT) {
+		switch (WLAN_FC_GET_STYPE(fc)) {
+		case IEEE80211_STYPE_AUTH:
+		case IEEE80211_STYPE_DEAUTH:
+		case IEEE80211_STYPE_ASSOC_REQ:
+		case IEEE80211_STYPE_REASSOC_REQ:
+			if (tx_flags & TX_CMD_FLG_RTS_MSK) {
+				tx_flags &= ~TX_CMD_FLG_RTS_MSK;
+				tx_flags |= TX_CMD_FLG_CTS_MSK;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	cmd->cmd.tx.rts_retry_limit = rts_retry_limit;
+	cmd->cmd.tx.data_retry_limit = data_retry_limit;
+	cmd->cmd.tx.rate = rate;
+	cmd->cmd.tx.tx_flags = tx_flags;
+
+	/* OFDM */
+	cmd->cmd.tx.supp_rates[0] = rate_mask & IWL_OFDM_RATES_MASK;
+
+	/* CCK */
+	cmd->cmd.tx.supp_rates[1] = (rate_mask >> 8) & 0xF;
+
+	IWL_DEBUG_RATE("Tx sta id: %d, rate: %d (plcp), flags: " BIT_FMT16 " "
+		       "cck/ofdm mask: 0x%x/0x%x\n",
+		       sta_id,
+		       cmd->cmd.tx.rate,
+		       BIT_ARG16(cmd->cmd.tx.tx_flags),
+		       cmd->cmd.tx.supp_rates[1], cmd->cmd.tx.supp_rates[0]);
+}
+
+u8 iwl3945_sync_sta(struct iwl_priv *priv, int sta_id, u16 tx_rate, u8 flags)
+{
+	unsigned long flags_spin;
+	struct iwl_station_entry *station;
+
+	if (sta_id == IWL_INVALID_STATION)
+		return IWL_INVALID_STATION;
+
+	spin_lock_irqsave(&priv->sta_lock, flags_spin);
+	station = &priv->stations[sta_id];
+
+	station->sta.sta.modify_mask = STA_MODIFY_TX_RATE_MSK;
+	station->sta.rate_n_flags = cpu_to_le16(tx_rate);
+	station->current_rate.rate_n_flags = tx_rate;
+	station->sta.mode = STA_CONTROL_MODIFY_MSK;
+
+	spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
+
+	iwl_send_add_station(priv, &station->sta, flags);
+	IWL_DEBUG_RATE("SCALE sync station %d to rate %d\n",
+			sta_id, tx_rate);
+	return sta_id;
+}
+
+void iwl_hw_card_show_info(struct iwl_priv *priv)
+{
+	IWL_DEBUG_INFO("3945ABG HW Version %u.%u.%u\n",
+		       ((priv->eeprom.board_revision >> 8) & 0x0F),
+		       ((priv->eeprom.board_revision >> 8) >> 4),
+		       (priv->eeprom.board_revision & 0x00FF));
+
+	IWL_DEBUG_INFO("3945ABG PBA Number %.*s\n",
+		       (int)sizeof(priv->eeprom.board_pba_number),
+		       priv->eeprom.board_pba_number);
+
+	IWL_DEBUG_INFO("EEPROM_ANTENNA_SWITCH_TYPE is 0x%02X\n",
+		       priv->eeprom.antenna_switch_type);
+}
+
+static int iwl3945_nic_set_pwr_src(struct iwl_priv *priv, int pwr_max)
+{
+	int rc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	rc = iwl_grab_restricted_access(priv);
+	if (rc) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return rc;
+	}
+
+	if (!pwr_max) {
+		u32 val;
+		rc = pci_read_config_dword(priv->pci_dev, 0x0C8, &val);
+		if (val & PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT) {
+			iwl_set_bits_mask_restricted_reg(
+				priv, ALM_APMG_PS_CTL,
+				APMG_PS_CTRL_REG_VAL_POWER_SRC_VAUX,
+				~APMG_PS_CTRL_REG_MSK_POWER_SRC);
+			iwl_release_restricted_access(priv);
+
+			iwl_poll_bit(priv, CSR_GPIO_IN,
+				     CSR_GPIO_IN_VAL_VAUX_PWR_SRC,
+				     CSR_GPIO_IN_BIT_AUX_POWER, 5000);
+		} else
+			iwl_release_restricted_access(priv);
+
+	} else {
+		iwl_set_bits_mask_restricted_reg(
+			priv, ALM_APMG_PS_CTL,
+			APMG_PS_CTRL_REG_VAL_POWER_SRC_VMAIN,
+			~APMG_PS_CTRL_REG_MSK_POWER_SRC);
+
+		iwl_release_restricted_access(priv);
+		iwl_poll_bit(priv, CSR_GPIO_IN, CSR_GPIO_IN_VAL_VMAIN_PWR_SRC,
+			     CSR_GPIO_IN_BIT_AUX_POWER, 5000);	/* uS */
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return rc;
+}
+
+static int iwl3945_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
+{
+	int rc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	rc = iwl_grab_restricted_access(priv);
+	if (rc) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return rc;
+	}
+
+	iwl_write_restricted(priv, FH_RCSR_RBD_BASE(0), rxq->dma_addr);
+	iwl_write_restricted(priv, FH_RCSR_RPTR_ADDR(0),
+			     priv->hw_setting.shared_phys +
+			     offsetof(struct iwl_shared, rx_read_ptr[0]));
+	iwl_write_restricted(priv, FH_RCSR_WPTR(0), 0);
+	iwl_write_restricted(
+		priv, FH_RCSR_CONFIG(0),
+		ALM_FH_RCSR_RX_CONFIG_REG_VAL_DMA_CHNL_EN_ENABLE |
+		ALM_FH_RCSR_RX_CONFIG_REG_VAL_RDRBD_EN_ENABLE |
+		ALM_FH_RCSR_RX_CONFIG_REG_BIT_WR_STTS_EN |
+		ALM_FH_RCSR_RX_CONFIG_REG_VAL_MAX_FRAG_SIZE_128 |
+		(RX_QUEUE_SIZE_LOG <<
+		 ALM_FH_RCSR_RX_CONFIG_REG_POS_RBDC_SIZE) |
+		ALM_FH_RCSR_RX_CONFIG_REG_VAL_IRQ_DEST_INT_HOST |
+		(1 << ALM_FH_RCSR_RX_CONFIG_REG_POS_IRQ_RBTH) |
+		ALM_FH_RCSR_RX_CONFIG_REG_VAL_MSG_MODE_FH);
+
+	/* fake read to flush all prev I/O */
+	iwl_read_restricted(priv, FH_RSSR_CTRL);
+
+	iwl_release_restricted_access(priv);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+static int iwl3945_tx_reset(struct iwl_priv *priv)
+{
+	int rc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	rc = iwl_grab_restricted_access(priv);
+	if (rc) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return rc;
+	}
+
+	/* bypass mode */
+	iwl_write_restricted_reg(priv, SCD_MODE_REG, 0x2);
+
+	/* RA 0 is active */
+	iwl_write_restricted_reg(priv, SCD_ARASTAT_REG, 0x01);
+
+	/* all 6 fifo are active */
+	iwl_write_restricted_reg(priv, SCD_TXFACT_REG, 0x3f);
+
+	iwl_write_restricted_reg(priv, SCD_SBYP_MODE_1_REG, 0x010000);
+	iwl_write_restricted_reg(priv, SCD_SBYP_MODE_2_REG, 0x030002);
+	iwl_write_restricted_reg(priv, SCD_TXF4MF_REG, 0x000004);
+	iwl_write_restricted_reg(priv, SCD_TXF5MF_REG, 0x000005);
+
+	iwl_write_restricted(priv, FH_TSSR_CBB_BASE,
+			     priv->hw_setting.shared_phys);
+
+	iwl_write_restricted(
+		priv, FH_TSSR_MSG_CONFIG,
+		ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TXPD_ON |
+		ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_TXPD_ON |
+		ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_MAX_FRAG_SIZE_128B |
+		ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TFD_ON |
+		ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_CBB_ON |
+		ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RSP_WAIT_TH |
+		ALM_FH_TSSR_TX_MSG_CONFIG_REG_VAL_RSP_WAIT_TH);
+
+	iwl_release_restricted_access(priv);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+/**
+ * iwl3945_txq_ctx_reset - Reset TX queue context
+ *
+ * Destroys all DMA structures and initialize them again
+ */
+static int iwl3945_txq_ctx_reset(struct iwl_priv *priv)
+{
+	int rc;
+	int txq_id, slots_num;
+
+	iwl_hw_txq_ctx_free(priv);
+
+	/* Tx CMD queue */
+	rc = iwl3945_tx_reset(priv);
+	if (rc)
+		goto error;
+
+	/* Tx queue(s) */
+	for (txq_id = 0; txq_id < TFD_QUEUE_MAX; txq_id++) {
+		slots_num =
+		    (txq_id == IWL_CMD_QUEUE_NUM) ? TFD_CMD_SLOTS :
+			TFD_TX_CMD_SLOTS;
+		rc = iwl_tx_queue_init(priv, &priv->txq[txq_id], slots_num,
+					txq_id);
+		if (rc) {
+			IWL_ERROR("Tx %d queue init failed\n", txq_id);
+			goto error;
+		}
+	}
+
+	return rc;
+
+ error:
+	iwl_hw_txq_ctx_free(priv);
+	return rc;
+}
+
+int iwl_hw_nic_init(struct iwl_priv *priv)
+{
+	u8 rev_id;
+	int rc;
+	unsigned long flags;
+	struct iwl_rx_queue *rxq = &priv->rxq;
+
+	iwl_power_init_handle(priv);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	iwl_set_bit(priv, CSR_ANA_PLL_CFG, (1 << 24));
+	iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS,
+		    CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);
+
+	iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+	rc = iwl_poll_bit(priv, CSR_GP_CNTRL,
+			  CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+			  CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
+	if (rc < 0) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		IWL_DEBUG_INFO("Failed to init the card\n");
+		return rc;
+	}
+
+	rc = iwl_grab_restricted_access(priv);
+	if (rc) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return rc;
+	}
+	iwl_write_restricted_reg(priv, ALM_APMG_CLK_EN,
+				 APMG_CLK_REG_VAL_DMA_CLK_RQT |
+				 APMG_CLK_REG_VAL_BSM_CLK_RQT);
+	udelay(20);
+	iwl_set_bits_restricted_reg(priv, ALM_APMG_PCIDEV_STT,
+				    APMG_DEV_STATE_REG_VAL_L1_ACTIVE_DISABLE);
+	iwl_release_restricted_access(priv);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* Determine HW type */
+	rc = pci_read_config_byte(priv->pci_dev, PCI_REVISION_ID, &rev_id);
+	if (rc)
+		return rc;
+	IWL_DEBUG_INFO("HW Revision ID = 0x%X\n", rev_id);
+
+	iwl3945_nic_set_pwr_src(priv, 1);
+	spin_lock_irqsave(&priv->lock, flags);
+
+	if (rev_id & PCI_CFG_REV_ID_BIT_RTP)
+		IWL_DEBUG_INFO("RTP type \n");
+	else if (rev_id & PCI_CFG_REV_ID_BIT_BASIC_SKU) {
+		IWL_DEBUG_INFO("ALM-MB type\n");
+		iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+			    CSR_HW_IF_CONFIG_REG_BIT_ALMAGOR_MB);
+	} else {
+		IWL_DEBUG_INFO("ALM-MM type\n");
+		iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+			    CSR_HW_IF_CONFIG_REG_BIT_ALMAGOR_MM);
+	}
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* Initialize the EEPROM */
+	rc = iwl_eeprom_init(priv);
+	if (rc)
+		return rc;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (EEPROM_SKU_CAP_OP_MODE_MRC == priv->eeprom.sku_cap) {
+		IWL_DEBUG_INFO("SKU OP mode is mrc\n");
+		iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+			    CSR_HW_IF_CONFIG_REG_BIT_SKU_MRC);
+	} else {
+		IWL_DEBUG_INFO("SKU OP mode is basic\n");
+	}
+
+	if ((priv->eeprom.board_revision & 0xF0) == 0xD0) {
+		IWL_DEBUG_INFO("3945ABG revision is 0x%X\n",
+			       priv->eeprom.board_revision);
+		iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+			    CSR_HW_IF_CONFIG_REG_BIT_BOARD_TYPE);
+	} else {
+		IWL_DEBUG_INFO("3945ABG revision is 0x%X\n",
+			       priv->eeprom.board_revision);
+		iwl_clear_bit(priv, CSR_HW_IF_CONFIG_REG,
+			      CSR_HW_IF_CONFIG_REG_BIT_BOARD_TYPE);
+	}
+
+	if (priv->eeprom.almgor_m_version <= 1) {
+		iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+			    CSR_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A);
+		IWL_DEBUG_INFO("Card M type A version is 0x%X\n",
+			       priv->eeprom.almgor_m_version);
+	} else {
+		IWL_DEBUG_INFO("Card M type B version is 0x%X\n",
+			       priv->eeprom.almgor_m_version);
+		iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+			    CSR_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B);
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	if (priv->eeprom.sku_cap & EEPROM_SKU_CAP_SW_RF_KILL_ENABLE)
+		IWL_DEBUG_RF_KILL("SW RF KILL supported in EEPROM.\n");
+
+	if (priv->eeprom.sku_cap & EEPROM_SKU_CAP_HW_RF_KILL_ENABLE)
+		IWL_DEBUG_RF_KILL("HW RF KILL supported in EEPROM.\n");
+
+	/* Allocate the RX queue, or reset if it is already allocated */
+	if (!rxq->bd) {
+		rc = iwl_rx_queue_alloc(priv);
+		if (rc) {
+			IWL_ERROR("Unable to initialize Rx queue\n");
+			return -ENOMEM;
+		}
+	} else
+		iwl_rx_queue_reset(priv, rxq);
+
+	iwl_rx_replenish(priv);
+
+	iwl3945_rx_init(priv, rxq);
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+/*
+ * Look at using this instead :::
+	rxq->need_update = 1;
+	iwl_rx_queue_update_write_ptr(priv, rxq);
+*/
+
+	rc = iwl_grab_restricted_access(priv);
+	if (rc) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return rc;
+	}
+	iwl_write_restricted(priv, FH_RCSR_WPTR(0), rxq->write & ~7);
+	iwl_release_restricted_access(priv);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	rc = iwl3945_txq_ctx_reset(priv);
+	if (rc)
+		return rc;
+
+	priv->status |= STATUS_INIT;
+
+	return 0;
+}
+
+/**
+ * iwl_hw_txq_ctx_free - Free TXQ Context
+ *
+ * Destroy all TX DMA queues and structures
+ */
+void iwl_hw_txq_ctx_free(struct iwl_priv *priv)
+{
+	int txq_id;
+
+	/* Tx queues */
+	for (txq_id = 0; txq_id < TFD_QUEUE_MAX; txq_id++)
+		iwl_tx_queue_free(priv, &priv->txq[txq_id]);
+}
+
+void iwl_hw_txq_ctx_stop(struct iwl_priv *priv)
+{
+	int queue;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (iwl_grab_restricted_access(priv)) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		iwl_hw_txq_ctx_free(priv);
+		return;
+	}
+
+	/* stop SCD */
+	iwl_write_restricted_reg(priv, SCD_MODE_REG, 0);
+
+	/* reset TFD queues */
+	for (queue = TFD_QUEUE_MIN; queue < TFD_QUEUE_MAX; queue++) {
+		iwl_write_restricted(priv, FH_TCSR_CONFIG(queue), 0x0);
+		iwl_poll_restricted_bit(priv, FH_TSSR_TX_STATUS,
+					ALM_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE
+					(queue), 1000);
+	}
+
+	iwl_release_restricted_access(priv);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	iwl_hw_txq_ctx_free(priv);
+}
+
+int iwl_hw_nic_stop_master(struct iwl_priv *priv)
+{
+	int rc = 0;
+	u32 reg_val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	/* set stop master bit */
+	iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
+
+	reg_val = iwl_read32(priv, CSR_GP_CNTRL);
+
+	if (CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE ==
+	    (reg_val & CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE)) {
+		IWL_DEBUG_INFO
+		    ("Card in power save, master is already stopped\n");
+	} else {
+		rc = iwl_poll_bit(priv,
+				  CSR_RESET,
+				  CSR_RESET_REG_FLAG_MASTER_DISABLED,
+				  CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
+		if (rc < 0) {
+			spin_unlock_irqrestore(&priv->lock, flags);
+			return rc;
+		}
+	}
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+	IWL_DEBUG_INFO("stop master\n");
+
+	return rc;
+}
+
+int iwl_hw_nic_reset(struct iwl_priv *priv)
+{
+	int rc;
+	unsigned long flags;
+
+	iwl_hw_nic_stop_master(priv);
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+
+	rc = iwl_poll_bit(priv, CSR_GP_CNTRL,
+			  CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+			  CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
+
+	rc = iwl_grab_restricted_access(priv);
+	if (!rc) {
+		iwl_write_restricted_reg(priv, APMG_CLK_CTRL_REG,
+					 APMG_CLK_REG_VAL_BSM_CLK_RQT);
+
+		udelay(10);
+
+		iwl_set_bit(priv, CSR_GP_CNTRL,
+			    CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+
+		iwl_write_restricted_reg(priv, ALM_APMG_LARC_INT_MSK, 0x0);
+		iwl_write_restricted_reg(priv, ALM_APMG_LARC_INT, 0xFFFFFFFF);
+
+		/* enable DMA */
+		iwl_write_restricted_reg(priv, ALM_APMG_CLK_EN,
+					 APMG_CLK_REG_VAL_DMA_CLK_RQT |
+					 APMG_CLK_REG_VAL_BSM_CLK_RQT);
+		udelay(10);
+
+		iwl_set_bits_restricted_reg(
+			priv, ALM_APMG_PS_CTL,
+			APMG_PS_CTRL_REG_VAL_ALM_R_RESET_REQ);
+		udelay(5);
+		iwl_clear_bits_restricted_reg(
+			priv, ALM_APMG_PS_CTL,
+			APMG_PS_CTRL_REG_VAL_ALM_R_RESET_REQ);
+		iwl_release_restricted_access(priv);
+	}
+
+	/* Clear the 'host command active' bit... */
+	priv->status &= ~STATUS_HCMD_ACTIVE;
+
+	wake_up_interruptible(&priv->wait_command_queue);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return rc;
+}
+
+/**
+ * iwl_hw_reg_adjust_power_by_temp - return index delta into power gain settings table
+ */
+static int iwl_hw_reg_adjust_power_by_temp(int new_reading, int old_reading)
+{
+	return (new_reading - old_reading) * (-11) / 100;
+}
+
+/**
+ * iwl_hw_reg_temp_out_of_range - Keep temperature in sane range
+ */
+static inline int iwl_hw_reg_temp_out_of_range(int temperature)
+{
+	return (((temperature < -260) || (temperature > 25)) ? 1 : 0);
+}
+
+int iwl_hw_get_temperature(struct iwl_priv *priv)
+{
+	return iwl_read32(priv, CSR_UCODE_DRV_GP2);
+}
+
+/**
+ * iwl_hw_reg_txpower_get_temperature - get current temperature by reading from NIC
+ */
+static int iwl_hw_reg_txpower_get_temperature(struct iwl_priv *priv)
+{
+	int temperature;
+
+	temperature = iwl_hw_get_temperature(priv);
+
+	/* driver's okay range is -260 to +25.
+	 *   human readable okay range is 0 to +285 */
+	IWL_DEBUG_INFO("Temperature: %d\n", temperature + IWL_TEMP_CONVERT);
+
+	/* handle insane temp reading */
+	if (iwl_hw_reg_temp_out_of_range(temperature)) {
+		IWL_ERROR("Error bad temperature value  %d\n", temperature);
+
+		/* if really really hot(?),
+		 *   substitute the 3rd band/group's temp measured at factory */
+		if (priv->last_temperature > 100)
+			temperature =
+				(s16)le16_to_cpu(priv->eeprom.groups[2].temperature);
+		else /* else use most recent "sane" value from driver */
+			temperature = priv->last_temperature;
+	}
+
+	return temperature;	/* raw, not "human readable" */
+}
+
+/* Adjust Txpower only if temperature variance is greater than threshold.
+ *
+ * Both are lower than older versions' 9 degrees */
+#define IWL_TEMPERATURE_LIMIT_TIMER   6
+
+/**
+ * is_temp_calib_needed - determines if new calibration is needed
+ *
+ * records new temperature in tx_mgr->temperature.
+ * replaces tx_mgr->last_temperature *only* if calib needed
+ *    (assumes caller will actually do the calibration!). */
+static int is_temp_calib_needed(struct iwl_priv *priv)
+{
+	int temp_diff;
+
+	priv->temperature = iwl_hw_reg_txpower_get_temperature(priv);
+	temp_diff = priv->temperature - priv->last_temperature;
+
+	/* get absolute value */
+	if (temp_diff < 0) {
+		IWL_DEBUG_POWER("Getting cooler, delta %d,\n", temp_diff);
+		temp_diff = -temp_diff;
+	} else if (temp_diff == 0)
+		IWL_DEBUG_POWER("Same temp,\n");
+	else
+		IWL_DEBUG_POWER("Getting warmer, delta %d,\n", temp_diff);
+
+	/* if we don't need calibration, *don't* update last_temperature */
+	if (temp_diff < IWL_TEMPERATURE_LIMIT_TIMER) {
+		IWL_DEBUG_POWER("Timed thermal calib not needed\n");
+		return 0;
+	}
+
+	IWL_DEBUG_POWER("Timed thermal calib needed\n");
+
+	/* assume that caller will actually do calib ...
+	 *   update the "last temperature" value */
+	priv->last_temperature = priv->temperature;
+	return 1;
+}
+
+#define IWL_MAX_GAIN_ENTRIES 78
+#define IWL_CCK_FROM_OFDM_POWER_DIFF  -5
+#define IWL_CCK_FROM_OFDM_INDEX_DIFF (10)
+
+/* radio and DSP power table, each step is 1/2 dB.
+ * 1st number is for RF analog gain, 2nd number is for DSP pre-DAC gain. */
+static struct iwl_tx_power power_gain_table[2][IWL_MAX_GAIN_ENTRIES] = {
+	{
+	 {251, 127},		/* 2.4 GHz, highest power */
+	 {251, 127},
+	 {251, 127},
+	 {251, 127},
+	 {251, 125},
+	 {251, 110},
+	 {251, 105},
+	 {251, 98},
+	 {187, 125},
+	 {187, 115},
+	 {187, 108},
+	 {187, 99},
+	 {243, 119},
+	 {243, 111},
+	 {243, 105},
+	 {243, 97},
+	 {243, 92},
+	 {211, 106},
+	 {211, 100},
+	 {179, 120},
+	 {179, 113},
+	 {179, 107},
+	 {147, 125},
+	 {147, 119},
+	 {147, 112},
+	 {147, 106},
+	 {147, 101},
+	 {147, 97},
+	 {147, 91},
+	 {115, 107},
+	 {235, 121},
+	 {235, 115},
+	 {235, 109},
+	 {203, 127},
+	 {203, 121},
+	 {203, 115},
+	 {203, 108},
+	 {203, 102},
+	 {203, 96},
+	 {203, 92},
+	 {171, 110},
+	 {171, 104},
+	 {171, 98},
+	 {139, 116},
+	 {227, 125},
+	 {227, 119},
+	 {227, 113},
+	 {227, 107},
+	 {227, 101},
+	 {227, 96},
+	 {195, 113},
+	 {195, 106},
+	 {195, 102},
+	 {195, 95},
+	 {163, 113},
+	 {163, 106},
+	 {163, 102},
+	 {163, 95},
+	 {131, 113},
+	 {131, 106},
+	 {131, 102},
+	 {131, 95},
+	 {99, 113},
+	 {99, 106},
+	 {99, 102},
+	 {99, 95},
+	 {67, 113},
+	 {67, 106},
+	 {67, 102},
+	 {67, 95},
+	 {35, 113},
+	 {35, 106},
+	 {35, 102},
+	 {35, 95},
+	 {3, 113},
+	 {3, 106},
+	 {3, 102},
+	 {3, 95}},		/* 2.4 GHz, lowest power */
+	{
+	 {251, 127},		/* 5.x GHz, highest power */
+	 {251, 120},
+	 {251, 114},
+	 {219, 119},
+	 {219, 101},
+	 {187, 113},
+	 {187, 102},
+	 {155, 114},
+	 {155, 103},
+	 {123, 117},
+	 {123, 107},
+	 {123, 99},
+	 {123, 92},
+	 {91, 108},
+	 {59, 125},
+	 {59, 118},
+	 {59, 109},
+	 {59, 102},
+	 {59, 96},
+	 {59, 90},
+	 {27, 104},
+	 {27, 98},
+	 {27, 92},
+	 {115, 118},
+	 {115, 111},
+	 {115, 104},
+	 {83, 126},
+	 {83, 121},
+	 {83, 113},
+	 {83, 105},
+	 {83, 99},
+	 {51, 118},
+	 {51, 111},
+	 {51, 104},
+	 {51, 98},
+	 {19, 116},
+	 {19, 109},
+	 {19, 102},
+	 {19, 98},
+	 {19, 93},
+	 {171, 113},
+	 {171, 107},
+	 {171, 99},
+	 {139, 120},
+	 {139, 113},
+	 {139, 107},
+	 {139, 99},
+	 {107, 120},
+	 {107, 113},
+	 {107, 107},
+	 {107, 99},
+	 {75, 120},
+	 {75, 113},
+	 {75, 107},
+	 {75, 99},
+	 {43, 120},
+	 {43, 113},
+	 {43, 107},
+	 {43, 99},
+	 {11, 120},
+	 {11, 113},
+	 {11, 107},
+	 {11, 99},
+	 {131, 107},
+	 {131, 99},
+	 {99, 120},
+	 {99, 113},
+	 {99, 107},
+	 {99, 99},
+	 {67, 120},
+	 {67, 113},
+	 {67, 107},
+	 {67, 99},
+	 {35, 120},
+	 {35, 113},
+	 {35, 107},
+	 {35, 99},
+	 {3, 120}}		/* 5.x GHz, lowest power */
+};
+
+static inline u8 iwl_hw_reg_fix_power_index(int index)
+{
+	if (index < 0)
+		return 0;
+	if (index >= IWL_MAX_GAIN_ENTRIES)
+		return IWL_MAX_GAIN_ENTRIES - 1;
+	return (u8) index;
+}
+
+/* Kick off thermal recalibration check every 60 seconds */
+#define REG_RECALIB_PERIOD (60)
+
+/**
+ * iwl_hw_reg_set_scan_power - Set Tx power for scan probe requests
+ *
+ * Set (in our channel info database) the direct scan Tx power for 1 Mbit (CCK)
+ * or 6 Mbit (OFDM) rates.
+ */
+static void iwl_hw_reg_set_scan_power(struct iwl_priv *priv, u32 scan_tbl_index,
+			       s32 rate_index, const s8 * clip_pwrs,
+			       struct iwl_channel_info *ch_info,
+			       int band_index)
+{
+	struct iwl_scan_power_info *scan_power_info;
+	s8 power;
+	u8 power_index;
+
+	scan_power_info = &ch_info->scan_pwr_info[scan_tbl_index];
+
+	/* use this channel group's 6Mbit clipping/saturation pwr,
+	 *   but cap at regulatory scan power restriction (set during init
+	 *   based on eeprom channel data) for this channel.  */
+	power = min(ch_info->scan_power, clip_pwrs[IWL_RATE_6M_INDEX]);
+
+	/* further limit to user's max power preference.
+	 * FIXME:  Other spectrum management power limitations do not
+	 *   seem to apply?? */
+	power = min(power, priv->user_txpower_limit);
+	scan_power_info->requested_power = power;
+
+	/* find difference between new scan *power* and current "normal"
+	 *   Tx *power* for 6Mb.  Use this difference (x2) to adjust the
+	 *   current "normal" temperature-compensated Tx power *index* for
+	 *   this rate (1Mb or 6Mb) to yield new temp-compensated scan power
+	 *   *index*. */
+	power_index = ch_info->power_info[rate_index].power_table_index
+	    - (power - ch_info->power_info
+	       [IWL_RATE_6M_INDEX].requested_power) * 2;
+
+#if 0
+	IWL_DEBUG_POWER("chnl %d scan power index %d\n",
+			ch_info->channel, power_index);
+#endif
+
+	/* store reference index that we use when adjusting *all* scan
+	 *   powers.  So we can accommodate user (all channel) or spectrum
+	 *   management (single channel) power changes "between" temperature
+	 *   feedback compensation procedures.
+	 * don't force fit this reference index into gain table; it may be a
+	 *   negative number.  This will help avoid errors when we're at
+	 *   the lower bounds (highest gains, for warmest temperatures)
+	 *   of the table. */
+
+	/* don't exceed table bounds for "real" setting */
+	power_index = iwl_hw_reg_fix_power_index(power_index);
+
+	scan_power_info->power_table_index = power_index;
+	scan_power_info->tpc.tx_gain =
+	    power_gain_table[band_index][power_index].tx_gain;
+	scan_power_info->tpc.dsp_atten =
+	    power_gain_table[band_index][power_index].dsp_atten;
+}
+
+/**
+ * iwl_hw_reg_send_txpower - fill in Tx Power command with gain settings
+ *
+ * Configures power settings for all rates for the current channel,
+ * using values from channel info struct, and send to NIC
+ */
+int iwl_hw_reg_send_txpower(struct iwl_priv *priv)
+{
+	int rate_idx;
+	const struct iwl_channel_info *ch_info = NULL;
+	struct iwl_txpowertable_cmd txpower = {
+		.channel = priv->active_rxon.channel,
+	};
+
+	txpower.band = (priv->phymode == MODE_IEEE80211A) ? 0 : 1;
+	ch_info = iwl_get_channel_info(priv,
+				       priv->phymode,
+				       le16_to_cpu(priv->active_rxon.channel));
+	if (!ch_info) {
+		IWL_ERROR
+		    ("Failed to get channel info for channel %d [%d]\n",
+		     le16_to_cpu(priv->active_rxon.channel), priv->phymode);
+		return -EINVAL;
+	}
+
+	if (!is_channel_valid(ch_info)) {
+		IWL_DEBUG_POWER("Not calling TX_PWR_TABLE_CMD on "
+				"non-Tx channel.\n");
+		return 0;
+	}
+
+	/* fill cmd with power settings for all rates for current channel */
+	for (rate_idx = 0; rate_idx < IWL_RATE_COUNT; rate_idx++) {
+		txpower.power[rate_idx].tpc = ch_info->power_info[rate_idx].tpc;
+		txpower.power[rate_idx].rate = iwl_rates[rate_idx].plcp;
+
+		IWL_DEBUG_POWER("ch %d:%d rf %d dsp %3d rate code 0x%02x\n",
+				le16_to_cpu(txpower.channel),
+				txpower.band,
+				txpower.power[rate_idx].tpc.tx_gain,
+				txpower.power[rate_idx].tpc.dsp_atten,
+				txpower.power[rate_idx].rate);
+	}
+
+	return iwl_send_cmd_pdu(priv, REPLY_TX_PWR_TABLE_CMD,
+				sizeof(struct iwl_txpowertable_cmd), &txpower);
+
+}
+
+/**
+ * iwl_hw_reg_set_new_power - Configures power tables at new levels
+ * @ch_info: Channel to update.  Uses power_info.requested_power.
+ *
+ * Replace requested_power and base_power_index ch_info fields for
+ * one channel.
+ *
+ * Called if user or spectrum management changes power preferences.
+ * Takes into account h/w and modulation limitations (clip power).
+ *
+ * This does *not* send anything to NIC, just sets up ch_info for one channel.
+ *
+ * NOTE:reg_compensate_for_temperature_dif() *must* be run after this to
+ *	properly fill out the scan powers, and actual h/w gain settings,
+ *	and send changes to NIC
+ */
+static int iwl_hw_reg_set_new_power(struct iwl_priv *priv,
+			     struct iwl_channel_info *ch_info)
+{
+	struct iwl_channel_power_info *power_info;
+	int power_changed = 0;
+	int i;
+	const s8 *clip_pwrs;
+	int power;
+
+	/* Get this chnlgrp's rate-to-max/clip-powers table */
+	clip_pwrs = priv->clip_groups[ch_info->group_index].clip_powers;
+
+	/* Get this channel's rate-to-current-power settings table */
+	power_info = ch_info->power_info;
+
+	/* update OFDM Txpower settings */
+	for (i = IWL_FIRST_OFDM_RATE; i <= IWL_LAST_OFDM_RATE;
+	     i++, ++power_info) {
+		int delta_idx;
+
+		/* limit new power to be no more than h/w capability */
+		power = min(ch_info->curr_txpow, clip_pwrs[i]);
+		if (power == power_info->requested_power)
+			continue;
+
+		/* find difference between old and new requested powers,
+		 *    update base (non-temp-compensated) power index */
+		delta_idx = (power - power_info->requested_power) * 2;
+		power_info->base_power_index -= delta_idx;
+
+		/* save new requested power value */
+		power_info->requested_power = power;
+
+		power_changed = 1;
+	}
+
+	/* update CCK Txpower settings, based on OFDM 12M setting ...
+	 *    ... all CCK power settings for a given channel are the *same*. */
+	if (power_changed) {
+		power =
+		    ch_info->power_info[IWL_RATE_12M_INDEX].
+		    requested_power + IWL_CCK_FROM_OFDM_POWER_DIFF;
+
+		/* do all CCK rates' iwl_channel_power_info structures */
+		for (i = IWL_FIRST_CCK_RATE; i <= IWL_LAST_CCK_RATE; i++) {
+			power_info->requested_power = power;
+			power_info->base_power_index =
+			    ch_info->power_info[IWL_RATE_12M_INDEX].
+			    base_power_index + IWL_CCK_FROM_OFDM_INDEX_DIFF;
+			++power_info;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * iwl_hw_reg_get_channel_txpower_limit - returns new power limit for channel
+ *
+ * NOTE:  Returned power limit may be less (but not more) than requested,
+ *   based strictly on regulatory (eeprom and spectrum mgt) limitations
+ *   (no consideration for h/w clipping limitations). */
+static int iwl_hw_reg_get_channel_txpower_limit(struct iwl_channel_info *ch_info)
+{
+	s8 max_power;
+
+#if 0
+	/* if we're using TGd limits, use lower of TGd or EEPROM */
+	if (ch_info->tgd_data.max_power != 0)
+		max_power = min(ch_info->tgd_data.max_power,
+				ch_info->eeprom.max_power_avg);
+
+	/* else just use EEPROM limits */
+	else
+#endif
+		max_power = ch_info->eeprom.max_power_avg;
+	return min(max_power, ch_info->max_power_avg);
+}
+
+/**
+ * iwl_hw_reg_comp_txpower_temp - Compensate for temperature
+ *
+ * Compensate txpower settings of *all* channels for temperature.
+ * This only accounts for the difference between current temperature
+ *   and the factory calibration temperatures, and bases the new settings
+ *   on the channel's base_power_index.
+ *
+ * If RxOn is "associated", this sends the new Txpower to NIC!
+ */
+static int iwl_hw_reg_comp_txpower_temp(struct iwl_priv *priv)
+{
+	struct iwl_channel_info *ch_info = NULL;
+	int delta_index;
+	const s8 *clip_pwrs; /* array of h/w max power levels for each rate */
+	u8 a_band;
+	u8 rate_index;
+	u8 scan_tbl_index;
+	u8 i;
+	int ref_temp;
+	int temperature = priv->temperature;
+
+	/* set up new Tx power info for each and every channel, 2.4 and 5.x */
+	for (i = 0; i < priv->channel_count; i++) {
+		ch_info = &priv->channel_info[i];
+		a_band = is_channel_a_band(ch_info);
+
+		/* Get this chnlgrp's factory calibration temperature */
+		ref_temp = (s16)priv->eeprom.groups[ch_info->group_index].
+		    temperature;
+
+		/* get power index adjustment based on curr and factory
+		 * temps */
+		delta_index = iwl_hw_reg_adjust_power_by_temp(temperature, ref_temp);
+
+		/* set tx power value for all rates, OFDM and CCK */
+		for (rate_index = 0; rate_index < IWL_RATE_COUNT;
+		     rate_index++) {
+			int power_idx =
+			    ch_info->power_info[rate_index].base_power_index;
+
+			/* temperature compensate */
+			power_idx += delta_index;
+
+			/* stay within table range */
+			power_idx = iwl_hw_reg_fix_power_index(power_idx);
+			ch_info->power_info[rate_index].
+			    power_table_index = (u8) power_idx;
+			ch_info->power_info[rate_index].tpc =
+			    power_gain_table[a_band][power_idx];
+		}
+
+		/* Get this chnlgrp's rate-to-max/clip-powers table */
+		clip_pwrs = priv->clip_groups[ch_info->group_index].clip_powers;
+
+		/* set scan tx power, 1Mbit for CCK, 6Mbit for OFDM */
+		for (scan_tbl_index = 0;
+		     scan_tbl_index < IWL_NUM_SCAN_RATES; scan_tbl_index++) {
+			s32 actual_index = (scan_tbl_index == 0) ?
+			    IWL_RATE_1M_INDEX : IWL_RATE_6M_INDEX;
+			iwl_hw_reg_set_scan_power(priv, scan_tbl_index,
+					   actual_index, clip_pwrs,
+					   ch_info, a_band);
+		}
+	}
+
+	/* send Txpower command for current channel to ucode */
+	return iwl_hw_reg_send_txpower(priv);
+}
+
+int iwl_hw_reg_set_txpower(struct iwl_priv *priv, s8 power)
+{
+	struct iwl_channel_info *ch_info;
+	s8 max_power;
+	u8 channel;
+	u8 a_band;
+	u8 i;
+
+	if (priv->user_txpower_limit == power) {
+		IWL_DEBUG_POWER("Requested Tx power same as current "
+				"limit: %ddBm.\n", power);
+		return 0;
+	}
+
+	IWL_DEBUG_POWER("Setting upper limit clamp to %ddBm.\n", power);
+	priv->user_txpower_limit = power;
+
+	/* set up new Tx powers for each and every channel, 2.4 and 5.x */
+
+	for (i = 0; i < priv->channel_count; i++) {
+		ch_info = &priv->channel_info[i];
+		a_band = is_channel_a_band(ch_info);
+		channel = ch_info->channel;
+
+		/* find minimum power of all user and regulatory constraints
+		 *    (does not consider h/w clipping limitations) */
+		max_power = iwl_hw_reg_get_channel_txpower_limit(ch_info);
+		max_power = min(power, max_power);
+		if (max_power != ch_info->curr_txpow) {
+			ch_info->curr_txpow = max_power;
+
+			/* this considers the h/w clipping limitations */
+			iwl_hw_reg_set_new_power(priv, ch_info);
+		}
+	}
+
+	/* update txpower settings for all channels,
+	 *   send to NIC if associated. */
+	is_temp_calib_needed(priv);
+	iwl_hw_reg_comp_txpower_temp(priv);
+
+	return 0;
+}
+
+/* will add 3945 channel switch cmd handling later */
+int iwl_hw_channel_switch(struct iwl_priv *priv, u8 channel)
+{
+	return 0;
+}
+
+/**
+ * iwl3945_reg_txpower_periodic -  called when time to check our temperature.
+ *
+ * -- reset periodic timer
+ * -- see if temp has changed enough to warrant re-calibration ... if so:
+ *     -- correct coeffs for temp (can reset temp timer)
+ *     -- save this temp as "last",
+ *     -- send new set of gain settings to NIC
+ * NOTE:  This should continue working, even when we're not associated,
+ *   so we can keep our internal table of scan powers current. */
+void iwl3945_reg_txpower_periodic(struct iwl_priv *priv)
+{
+	/* This will kick in the "brute force"
+	 * iwl_hw_reg_comp_txpower_temp() below */
+	if (!is_temp_calib_needed(priv))
+		goto reschedule;
+
+	/* Set up a new set of temp-adjusted TxPowers, send to NIC.
+	 * This is based *only* on current temperature,
+	 * ignoring any previous power measurements */
+	iwl_hw_reg_comp_txpower_temp(priv);
+
+ reschedule:
+	queue_delayed_work(priv->workqueue,
+			   &priv->thermal_periodic, REG_RECALIB_PERIOD * HZ);
+}
+
+void iwl3945_bg_reg_txpower_periodic(struct work_struct *work)
+{
+	struct iwl_priv *priv = container_of(work, struct iwl_priv,
+					     thermal_periodic.work);
+
+	if (priv->status & STATUS_EXIT_PENDING)
+		return;
+
+	mutex_lock(&priv->mutex);
+	iwl3945_reg_txpower_periodic(priv);
+	mutex_unlock(&priv->mutex);
+}
+
+/* iwl_hw_reg_get_chnl_grp_index - find the channel-group index (0-4) for the channel.
+ *
+ *  ... used when initializing channel-info structs.
+ * NOTE:  These channel groups do *NOT* match the bands above!
+ *   These channel groups are based on factory-tested channels;
+ *   on A-band, EEPROM's "group frequency" entries represent the top channel
+ *   in each group 1-4.  Group 5 All B/G channels are in group 0.  */
+static u16 iwl_hw_reg_get_chnl_grp_index(struct iwl_priv *priv,
+				  const struct iwl_channel_info *ch_info)
+{
+	struct iwl_eeprom_txpower_group *ch_grp = &priv->eeprom.groups[0];
+	u8 group;
+	u16 group_index = 0;	/* based on factory calib frequencies */
+	u8 grp_channel;
+
+	/* Find the group index for the channel ... don't use index 1(?) */
+	if (is_channel_a_band(ch_info)) {
+		for (group = 1; group < 5; group++) {
+			grp_channel = ch_grp[group].group_channel;
+			if (ch_info->channel <= grp_channel) {
+				group_index = group;
+				break;
+			}
+		}
+		/* group 4 has a few channels *above* its factory cal freq */
+		if (group == 5)
+			group_index = 4;
+	} else
+		group_index = 0;	/* 2.4 GHz, group 0 */
+
+	IWL_DEBUG_POWER("Chnl %d mapped to grp %d\n", ch_info->channel,
+			group_index);
+	return group_index;
+}
+
+/**
+ * iwl_hw_reg_get_matched_power_index - Interpolate to get nominal index
+ *
+ * Interpolate to get nominal (i.e. at factory calibration temperature) index
+ *   into radio/DSP gain settings table for requested power.
+ */
+static int iwl_hw_reg_get_matched_power_index(struct iwl_priv *priv,
+				       s8 requested_power,
+				       s32 setting_index, s32 * new_index)
+{
+	const struct iwl_eeprom_txpower_group *chnl_grp = NULL;
+	s32 index0, index1;
+	s32 power = 2 * requested_power;
+	s32 i;
+	const struct iwl_eeprom_txpower_sample *samples;
+	s32 gains0, gains1;
+	s32 res;
+	s32 denominator;
+
+	chnl_grp = &priv->eeprom.groups[setting_index];
+	samples = chnl_grp->samples;
+	for (i = 0; i < 5; i++) {
+		if (power == samples[i].power) {
+			*new_index = samples[i].gain_index;
+			return 0;
+		}
+	}
+
+	if (power > samples[1].power) {
+		index0 = 0;
+		index1 = 1;
+	} else if (power > samples[2].power) {
+		index0 = 1;
+		index1 = 2;
+	} else if (power > samples[3].power) {
+		index0 = 2;
+		index1 = 3;
+	} else {
+		index0 = 3;
+		index1 = 4;
+	}
+
+	denominator = (s32) samples[index1].power - (s32) samples[index0].power;
+	if (denominator == 0)
+		return -EINVAL;
+	gains0 = (s32) samples[index0].gain_index * (1 << 19);
+	gains1 = (s32) samples[index1].gain_index * (1 << 19);
+	res = gains0 + (gains1 - gains0) *
+	    ((s32) power - (s32) samples[index0].power) / denominator +
+	    (1 << 18);
+	*new_index = res >> 19;
+	return 0;
+}
+
+static void iwl_hw_reg_init_channel_groups(struct iwl_priv *priv)
+{
+	u32 i;
+	s32 rate_index;
+	const struct iwl_eeprom_txpower_group *group;
+
+	IWL_DEBUG_POWER("Initializing factory calib info from EEPROM\n");
+
+	for (i = 0; i < IWL_NUM_TX_CALIB_GROUPS; i++) {
+		s8 *clip_pwrs;	/* table of power levels for each rate */
+		s8 satur_pwr;	/* saturation power for each chnl group */
+		group = &priv->eeprom.groups[i];
+
+		/* sanity check on factory saturation power value */
+		if (group->saturation_power < 40) {
+			IWL_WARNING("Error: saturation power is %d, "
+				    "less than minimum expected 40\n",
+				    group->saturation_power);
+			return;
+		}
+
+		/*
+		 * Derive requested power levels for each rate, based on
+		 *   hardware capabilities (saturation power for band).
+		 * Basic value is 3dB down from saturation, with further
+		 *   power reductions for highest 3 data rates.  These
+		 *   backoffs provide headroom for high rate modulation
+		 *   power peaks, without too much distortion (clipping).
+		 */
+		/* we'll fill in this array with h/w max power levels */
+		clip_pwrs = (s8 *) priv->clip_groups[i].clip_powers;
+
+		/* divide factory saturation power by 2 to find -3dB level */
+		satur_pwr = (s8) (group->saturation_power >> 1);
+
+		/* fill in channel group's nominal powers for each rate */
+		for (rate_index = 0;
+		     rate_index < IWL_RATE_COUNT; rate_index++, clip_pwrs++) {
+			switch (rate_index) {
+			case IWL_RATE_36M_INDEX:
+				if (i == 0)	/* B/G */
+					*clip_pwrs = satur_pwr;
+				else	/* A */
+					*clip_pwrs = satur_pwr - 5;
+				break;
+			case IWL_RATE_48M_INDEX:
+				if (i == 0)
+					*clip_pwrs = satur_pwr - 7;
+				else
+					*clip_pwrs = satur_pwr - 10;
+				break;
+			case IWL_RATE_54M_INDEX:
+				if (i == 0)
+					*clip_pwrs = satur_pwr - 9;
+				else
+					*clip_pwrs = satur_pwr - 12;
+				break;
+			default:
+				*clip_pwrs = satur_pwr;
+				break;
+			}
+		}
+	}
+}
+
+/**
+ * iwl3945_txpower_set_from_eeprom - Set channel power info based on EEPROM
+ *
+ * Second pass (during init) to set up priv->channel_info
+ *
+ * Set up Tx-power settings in our channel info database for each VALID
+ * (for this geo/SKU) channel, at all Tx data rates, based on eeprom values
+ * and current temperature.
+ *
+ * Since this is based on current temperature (at init time), these values may
+ * not be valid for very long, but it gives us a starting/default point,
+ * and allows us to active (i.e. using Tx) scan.
+ *
+ * This does *not* write values to NIC, just sets up our internal table.
+ */
+int iwl3945_txpower_set_from_eeprom(struct iwl_priv *priv)
+{
+	struct iwl_channel_info *ch_info = NULL;
+	struct iwl_channel_power_info *pwr_info;
+	int delta_index;
+	u8 rate_index;
+	u8 scan_tbl_index;
+	const s8 *clip_pwrs;	/* array of power levels for each rate */
+	u8 gain, dsp_atten;
+	s8 power;
+	u8 pwr_index, base_pwr_index, a_band;
+	u8 i;
+	int temperature;
+
+	/* save temperature reference,
+	 *   so we can determine next time to calibrate */
+	temperature = iwl_hw_reg_txpower_get_temperature(priv);
+	priv->last_temperature = temperature;
+
+	iwl_hw_reg_init_channel_groups(priv);
+
+	/* initialize Tx power info for each and every channel, 2.4 and 5.x */
+	for (i = 0, ch_info = priv->channel_info; i < priv->channel_count;
+	     i++, ch_info++) {
+		a_band = is_channel_a_band(ch_info);
+		if (!is_channel_valid(ch_info))
+			continue;
+
+		/* find this channel's channel group (*not* "band") index */
+		ch_info->group_index = iwl_hw_reg_get_chnl_grp_index(priv, ch_info);
+
+		/* Get this chnlgrp's rate->max/clip-powers table */
+		clip_pwrs = priv->clip_groups[ch_info->group_index].clip_powers;
+
+		/* calculate power index *adjustment* value according to
+		 *  diff between current temperature and factory temperature */
+		delta_index = iwl_hw_reg_adjust_power_by_temp(
+			temperature,
+			(s16)le16_to_cpu(
+				priv->eeprom.groups[ch_info->group_index].temperature));
+
+		IWL_DEBUG_POWER("Delta index for channel %d: %d [%d]\n",
+				ch_info->channel, delta_index, temperature +
+				IWL_TEMP_CONVERT);
+
+		/* set tx power value for all OFDM rates */
+		for (rate_index = 0; rate_index < IWL_OFDM_RATES;
+		     rate_index++) {
+			s32 power_idx;
+			int rc;
+
+			/* use channel group's clip-power table,
+			 *   but don't exceed channel's max power */
+			s8 pwr = min(ch_info->max_power_avg,
+				     clip_pwrs[rate_index]);
+
+			pwr_info = &ch_info->power_info[rate_index];
+
+			/* get base (i.e. at factory-measured temperature)
+			 *    power table index for this rate's power */
+			rc = iwl_hw_reg_get_matched_power_index(priv, pwr,
+							 ch_info->group_index,
+							 &power_idx);
+			if (rc) {
+				IWL_ERROR("Invalid power index\n");
+				return rc;
+			}
+			pwr_info->base_power_index = (u8) power_idx;
+
+			/* temperature compensate */
+			power_idx += delta_index;
+
+			/* stay within range of gain table */
+			power_idx = iwl_hw_reg_fix_power_index(power_idx);
+
+			/* fill 1 OFDM rate's iwl_channel_power_info struct */
+			pwr_info->requested_power = pwr;
+			pwr_info->power_table_index = (u8) power_idx;
+			pwr_info->tpc.tx_gain =
+			    power_gain_table[a_band][power_idx].tx_gain;
+			pwr_info->tpc.dsp_atten =
+			    power_gain_table[a_band][power_idx].dsp_atten;
+		}
+
+		/* set tx power for CCK rates, based on OFDM 12 Mbit settings*/
+		pwr_info = &ch_info->power_info[IWL_RATE_12M_INDEX];
+		power = pwr_info->requested_power
+		    + IWL_CCK_FROM_OFDM_POWER_DIFF;
+		pwr_index = pwr_info->power_table_index
+		    + IWL_CCK_FROM_OFDM_INDEX_DIFF;
+		base_pwr_index = pwr_info->base_power_index
+		    + IWL_CCK_FROM_OFDM_INDEX_DIFF;
+
+		/* stay within table range */
+		pwr_index = iwl_hw_reg_fix_power_index(pwr_index);
+		gain = power_gain_table[a_band][pwr_index].tx_gain;
+		dsp_atten = power_gain_table[a_band][pwr_index].dsp_atten;
+
+		/* fill each CCK rate's iwl_channel_power_info structure
+		 * NOTE:  All CCK-rate Txpwrs are the same for a given chnl!
+		 * NOTE:  CCK rates start at end of OFDM rates! */
+		for (rate_index = IWL_OFDM_RATES;
+		     rate_index < IWL_RATE_COUNT; rate_index++) {
+			pwr_info = &ch_info->power_info[rate_index];
+			pwr_info->requested_power = power;
+			pwr_info->power_table_index = pwr_index;
+			pwr_info->base_power_index = base_pwr_index;
+			pwr_info->tpc.tx_gain = gain;
+			pwr_info->tpc.dsp_atten = dsp_atten;
+		}
+
+		/* set scan tx power, 1Mbit for CCK, 6Mbit for OFDM */
+		for (scan_tbl_index = 0;
+		     scan_tbl_index < IWL_NUM_SCAN_RATES; scan_tbl_index++) {
+			s32 actual_index = (scan_tbl_index == 0) ?
+			    IWL_RATE_1M_INDEX : IWL_RATE_6M_INDEX;
+			iwl_hw_reg_set_scan_power(priv, scan_tbl_index,
+					   actual_index, clip_pwrs,
+					   ch_info, a_band);
+		}
+	}
+
+	return 0;
+}
+
+int iwl_hw_rxq_stop(struct iwl_priv *priv)
+{
+	int rc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	rc = iwl_grab_restricted_access(priv);
+	if (rc) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return rc;
+	}
+
+	iwl_write_restricted(priv, FH_RCSR_CONFIG(0), 0);
+	rc = iwl_poll_restricted_bit(priv, FH_RSSR_STATUS, (1 << 24), 1000);
+	if (rc < 0)
+		IWL_ERROR("Can't stop Rx DMA.\n");
+
+	iwl_release_restricted_access(priv);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+int iwl_hw_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq)
+{
+	int rc;
+	unsigned long flags;
+	int txq_id = txq->q.id;
+
+	struct iwl_shared *shared_data = priv->hw_setting.shared_virt;
+
+	shared_data->tx_base_ptr[txq_id] = (u32) txq->q.dma_addr;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	rc = iwl_grab_restricted_access(priv);
+	if (rc) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return rc;
+	}
+	iwl_write_restricted(priv, FH_CBCC_CTRL(txq_id), 0);
+	iwl_write_restricted(priv, FH_CBCC_BASE(txq_id), 0);
+
+	iwl_write_restricted(
+		priv, FH_TCSR_CONFIG(txq_id),
+		ALM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT |
+		ALM_FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF |
+		ALM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD |
+		ALM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL |
+		ALM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE);
+	iwl_release_restricted_access(priv);
+
+	/* fake read to flush all prev. writes */
+	iwl_read32(priv, FH_TSSR_CBB_BASE);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+	return 0;
+}
+
+int iwl_hw_get_rx_read(struct iwl_priv *priv)
+{
+	struct iwl_shared *shared_data =
+	    (struct iwl_shared *)priv->hw_setting.shared_virt;
+
+	return shared_data->rx_read_ptr[0];
+}
+
+/**
+ * iwl3945_init_hw_rate_table - Initialize the hardware rate fallback table
+ */
+int iwl3945_init_hw_rate_table(struct iwl_priv *priv)
+{
+	int rc, i;
+	struct iwl_rate_scaling_cmd rate_cmd = {
+		.reserved = {0, 0, 0},
+	};
+	struct iwl_rate_scaling_info *table = rate_cmd.table;
+
+	for (i = 0; i < ARRAY_SIZE(iwl_rates); i++) {
+		table[i].rate_n_flags = iwl_rates[i].plcp;
+		table[i].try_cnt = priv->retry_rate;
+		table[i].next_rate_index = iwl_get_prev_ieee_rate(i);
+	}
+
+	switch (priv->phymode) {
+	case MODE_IEEE80211A:
+		IWL_DEBUG_RATE("Select A mode rate scale\n");
+		/* If one of the following CCK rates is used,
+		 * have it fall back to the 6M OFDM rate */
+		for (i = IWL_FIRST_CCK_RATE; i <= IWL_LAST_CCK_RATE; i++)
+			table[i].next_rate_index = IWL_FIRST_OFDM_RATE;
+
+		/* Don't fall back to CCK rates */
+		table[IWL_RATE_12M_INDEX].next_rate_index = IWL_RATE_9M_INDEX;
+
+		/* Don't drop out of OFDM rates */
+		table[IWL_FIRST_OFDM_RATE].next_rate_index =
+		    IWL_FIRST_OFDM_RATE;
+		break;
+
+	case MODE_IEEE80211B:
+		IWL_DEBUG_RATE("Select B mode rate scale\n");
+		/* If an OFDM rate is used, have it fall back to the
+		 * 1M CCK rates */
+		for (i = IWL_FIRST_OFDM_RATE; i <= IWL_LAST_OFDM_RATE; i++)
+			table[i].next_rate_index = IWL_FIRST_CCK_RATE;
+
+		/* CCK shouldn't fall back to OFDM... */
+		table[IWL_RATE_11M_INDEX].next_rate_index = IWL_RATE_5M_INDEX;
+		break;
+
+	default:
+		IWL_DEBUG_RATE("Select G mode rate scale\n");
+		break;
+	}
+
+	/* Update the rate scaling for control frame Tx */
+	rate_cmd.table_id = 0;
+	rc = iwl_send_cmd_pdu(priv, REPLY_RATE_SCALE, sizeof(rate_cmd),
+			      &rate_cmd);
+	if (rc)
+		return rc;
+
+	/* Update the rate scaling for data frame Tx */
+	rate_cmd.table_id = 1;
+	return iwl_send_cmd_pdu(priv, REPLY_RATE_SCALE, sizeof(rate_cmd),
+				&rate_cmd);
+}
+
+int iwl_hw_set_hw_setting(struct iwl_priv *priv)
+{
+	memset((void *)&priv->hw_setting, 0,
+	       sizeof(struct iwl_driver_hw_info));
+
+	priv->hw_setting.shared_virt =
+	    pci_alloc_consistent(priv->pci_dev,
+				 sizeof(struct iwl_shared),
+				 &priv->hw_setting.shared_phys);
+
+	if (!priv->hw_setting.shared_virt) {
+		IWL_ERROR("failed to allocate pci memory\n");
+		mutex_unlock(&priv->mutex);
+		return -ENOMEM;
+	}
+
+	priv->hw_setting.ac_queue_count = AC_NUM;
+	priv->hw_setting.rx_buffer_size = IWL_RX_BUF_SIZE;
+	priv->hw_setting.tx_cmd_len = sizeof(struct iwl_tx_cmd);
+	priv->hw_setting.max_rxq_size = RX_QUEUE_SIZE;
+	priv->hw_setting.max_rxq_log = RX_QUEUE_SIZE_LOG;
+	priv->hw_setting.cck_flag = 0;
+	return 0;
+}
+
+int iwl_hw_get_beacon_cmd(struct iwl_priv *priv,
+			  struct iwl_frame *frame, u16 rate)
+{
+	struct iwl_tx_beacon_cmd *tx_beacon_cmd;
+	int frame_size;
+
+	tx_beacon_cmd = (struct iwl_tx_beacon_cmd *)&frame->u;
+	memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd));
+
+	tx_beacon_cmd->tx.sta_id = IWL_BROADCAST_ID;
+	tx_beacon_cmd->tx.stop_time.life_time = 0xFFFFFFFF;
+
+	frame_size = iwl_fill_beacon_frame(priv,
+				tx_beacon_cmd->frame,
+				BROADCAST_ADDR,
+				sizeof(frame->u) - sizeof(*tx_beacon_cmd));
+
+	tx_beacon_cmd->tx.len = frame_size;
+
+	tx_beacon_cmd->tx.rate = rate;
+	tx_beacon_cmd->tx.tx_flags = (TX_CMD_FLG_SEQ_CTL_MSK |
+				      TX_CMD_FLG_TSF_MSK);
+
+	/* supp_rates[0] == OFDM  */
+	tx_beacon_cmd->tx.supp_rates[0] = IWL_OFDM_BASIC_RATES_MASK;
+
+	/* supp_rates[1] == CCK
+	 *
+	 * NOTE:  IWL_*_RATES_MASK are not in the order that supp_rates
+	 * expects so we have to shift them around.
+	 *
+	 * supp_rates expects:
+	 * CCK rates are bit0..3
+	 *
+	 * However IWL_*_RATES_MASK has:
+	 * CCK rates are bit8..11
+	 */
+	tx_beacon_cmd->tx.supp_rates[1] =
+		(IWL_CCK_BASIC_RATES_MASK >> 8) & 0xF;
+
+	return (sizeof(struct iwl_tx_beacon_cmd) + frame_size);
+}
+
+void iwl_hw_rx_handler_setup(struct iwl_priv *priv)
+{
+	priv->rx_handlers[REPLY_3945_RX] = iwl3945_rx_reply_rx;
+}
+
+void iwl_hw_setup_deferred_work(struct iwl_priv *priv)
+{
+	INIT_DELAYED_WORK(&priv->thermal_periodic,
+			  iwl3945_bg_reg_txpower_periodic);
+}
+
+void iwl_hw_cancel_deferred_work(struct iwl_priv *priv)
+{
+	cancel_delayed_work(&priv->thermal_periodic);
+}
+
+struct pci_device_id iwl_hw_card_ids[] = {
+	{0x8086, 0x4222, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x8086, 0x4227, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0}
+};
+
+inline int iwl_eeprom_aqcuire_semaphore(struct iwl_priv *priv)
+{
+	_iwl_clear_bit(priv, CSR_EEPROM_GP, CSR_EEPROM_GP_OWNER);
+	return 0;
+}
+
+MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
diff -ruN linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-3945.h linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-3945.h
--- linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-3945.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-3945.h	2007-07-31 15:29:35.000000000 +0200
@@ -0,0 +1,60 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#ifndef __iwl_3945_h__
+#define __iwl_3945_h__
+
+#if IWL != 3945
+/*
+ * In non IWL == 3945 builds, these must build to nothing in order to allow
+ * the common code to not have several #if IWL == XXXX / #endif blocks
+ */
+static inline __le32 iwl3945_get_antenna_flags(const struct iwl_priv *priv)
+{ return 0; }
+static inline int iwl3945_init_hw_rate_table(struct iwl_priv *priv)
+{ return 0; }
+static inline void iwl3945_reg_txpower_periodic(struct iwl_priv *priv) {}
+static inline void iwl3945_bg_reg_txpower_periodic(struct work_struct *work)
+{}
+static inline int iwl3945_txpower_set_from_eeprom(struct iwl_priv *priv)
+{ return 0; }
+static inline u8 iwl3945_sync_sta(struct iwl_priv *priv, int sta_id,
+		 u16 tx_rate, u8 flags) { return 0; }
+#else				/* IWL == 3945 */
+/*
+ * Forward declare iwl-3945.c functions for iwl-base.c
+ */
+extern int iwl_eeprom_aqcuire_semaphore(struct iwl_priv *priv);
+extern __le32 iwl3945_get_antenna_flags(const struct iwl_priv *priv);
+extern int iwl3945_init_hw_rate_table(struct iwl_priv *priv);
+extern void iwl3945_reg_txpower_periodic(struct iwl_priv *priv);
+extern void iwl3945_bg_reg_txpower_periodic(struct work_struct *work);
+extern int iwl3945_txpower_set_from_eeprom(struct iwl_priv *priv);
+extern u8 iwl3945_sync_sta(struct iwl_priv *priv, int sta_id,
+		 u16 tx_rate, u8 flags);
+#endif				/* IWL == 3945 */
+
+#endif
diff -ruN linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-4965-hw.h linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-4965-hw.h
--- linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-4965-hw.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-4965-hw.h	2007-07-31 15:29:35.000000000 +0200
@@ -0,0 +1,927 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2005 - 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU Geeral Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2007 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __iwl_4965_hw_h__
+#define __iwl_4965_hw_h__
+
+#define IWL_RX_BUF_SIZE (4 * 1024)
+#define IWL_MAX_BSM_SIZE BSM_SRAM_SIZE
+#define IWL_MAX_INST_SIZE (96 * 1024)
+#define IWL_MAX_DATA_SIZE (40 * 1024)
+
+/********************* START TXPOWER *****************************************/
+enum {
+	HT_IE_EXT_CHANNEL_NONE = 0,
+	HT_IE_EXT_CHANNEL_ABOVE,
+	HT_IE_EXT_CHANNEL_INVALID,
+	HT_IE_EXT_CHANNEL_BELOW,
+	HT_IE_EXT_CHANNEL_MAX
+};
+
+enum {
+	CALIB_CH_GROUP_1 = 0,
+	CALIB_CH_GROUP_2 = 1,
+	CALIB_CH_GROUP_3 = 2,
+	CALIB_CH_GROUP_4 = 3,
+	CALIB_CH_GROUP_5 = 4,
+	CALIB_CH_GROUP_MAX
+};
+
+#define POWER_TABLE_NUM_HT_OFDM_ENTRIES           (32)
+
+/* Temperature calibration offset is 3% 0C in Kelvin */
+#define TEMPERATURE_CALIB_KELVIN_OFFSET 8
+#define TEMPERATURE_CALIB_A_VAL 259
+
+#define IWL_TX_POWER_TEMPERATURE_MIN  (263)
+#define IWL_TX_POWER_TEMPERATURE_MAX  (410)
+
+#define IWL_TX_POWER_TEMPERATURE_OUT_OF_RANGE(t) \
+	(((t) < IWL_TX_POWER_TEMPERATURE_MIN) || \
+	 ((t) > IWL_TX_POWER_TEMPERATURE_MAX))
+
+#define IWL_TX_POWER_ILLEGAL_TEMPERATURE (300)
+
+#define IWL_TX_POWER_TEMPERATURE_DIFFERENCE (2)
+
+#define IWL_TX_POWER_MIMO_REGULATORY_COMPENSATION (6)
+
+#define IWL_TX_POWER_TARGET_POWER_MIN       (0)	/* 0 dBm = 1 milliwatt */
+#define IWL_TX_POWER_TARGET_POWER_MAX      (16)	/* 16 dBm */
+
+/* timeout equivalent to 3 minutes */
+#define IWL_TX_POWER_TIMELIMIT_NOCALIB 1800000000
+
+#define IWL_TX_POWER_CCK_COMPENSATION (9)
+
+#define MIN_TX_GAIN_INDEX		(0)
+#define MIN_TX_GAIN_INDEX_52GHZ_EXT	(-9)
+#define MAX_TX_GAIN_INDEX_52GHZ		(98)
+#define MIN_TX_GAIN_52GHZ		(98)
+#define MAX_TX_GAIN_INDEX_24GHZ		(98)
+#define MIN_TX_GAIN_24GHZ		(98)
+#define MAX_TX_GAIN			(0)
+#define MAX_TX_GAIN_52GHZ_EXT		(-9)
+
+#define IWL_TX_POWER_DEFAULT_REGULATORY_24   (34)
+#define IWL_TX_POWER_DEFAULT_REGULATORY_52   (34)
+#define IWL_TX_POWER_REGULATORY_MIN          (0)
+#define IWL_TX_POWER_REGULATORY_MAX          (34)
+#define IWL_TX_POWER_DEFAULT_SATURATION_24   (38)
+#define IWL_TX_POWER_DEFAULT_SATURATION_52   (38)
+#define IWL_TX_POWER_SATURATION_MIN          (20)
+#define IWL_TX_POWER_SATURATION_MAX          (50)
+
+/* dv *0.4 = dt; so that 5 degrees temperature diff equals
+ * 12.5 in voltage diff */
+#define IWL_TX_TEMPERATURE_UPDATE_LIMIT 9
+
+#define IWL_INVALID_CHANNEL                 (0xffffffff)
+#define IWL_TX_POWER_REGITRY_BIT            (2)
+
+#define MIN_IWL_TX_POWER_CALIB_DUR          (100)
+#define IWL_CCK_FROM_OFDM_POWER_DIFF        (-5)
+#define IWL_CCK_FROM_OFDM_INDEX_DIFF (9)
+
+/* Number of entries in the gain table */
+#define POWER_GAIN_NUM_ENTRIES 78
+#define TX_POW_MAX_SESSION_NUM 5
+/*  timeout equivalent to 3 minutes */
+#define TX_IWL_TIMELIMIT_NOCALIB 1800000000
+
+/* Kedron TX_CALIB_STATES */
+#define IWL_TX_CALIB_STATE_SEND_TX        0x00000001
+#define IWL_TX_CALIB_WAIT_TX_RESPONSE     0x00000002
+#define IWL_TX_CALIB_ENABLED              0x00000004
+#define IWL_TX_CALIB_XVT_ON               0x00000008
+#define IWL_TX_CALIB_TEMPERATURE_CORRECT  0x00000010
+#define IWL_TX_CALIB_WORKING_WITH_XVT     0x00000020
+#define IWL_TX_CALIB_XVT_PERIODICAL       0x00000040
+
+#define NUM_IWL_TX_CALIB_SETTINS 5	/* Number of tx correction groups */
+
+#define IWL_MIN_POWER_IN_VP_TABLE 1	/* 0.5dBm multiplied by 2 */
+#define IWL_MAX_POWER_IN_VP_TABLE 40	/* 20dBm - multiplied by 2 (because
+					 * entries are for each 0.5dBm) */
+#define IWL_STEP_IN_VP_TABLE 1	/* 0.5dB - multiplied by 2 */
+#define IWL_NUM_POINTS_IN_VPTABLE \
+	(1 + IWL_MAX_POWER_IN_VP_TABLE - IWL_MIN_POWER_IN_VP_TABLE)
+
+#define MIN_TX_GAIN_INDEX         (0)
+#define MAX_TX_GAIN_INDEX_52GHZ   (98)
+#define MIN_TX_GAIN_52GHZ         (98)
+#define MAX_TX_GAIN_INDEX_24GHZ   (98)
+#define MIN_TX_GAIN_24GHZ         (98)
+#define MAX_TX_GAIN               (0)
+
+/* First and last channels of all groups */
+#define CALIB_IWL_TX_ATTEN_GR1_FCH 34
+#define CALIB_IWL_TX_ATTEN_GR1_LCH 43
+#define CALIB_IWL_TX_ATTEN_GR2_FCH 44
+#define CALIB_IWL_TX_ATTEN_GR2_LCH 70
+#define CALIB_IWL_TX_ATTEN_GR3_FCH 71
+#define CALIB_IWL_TX_ATTEN_GR3_LCH 124
+#define CALIB_IWL_TX_ATTEN_GR4_FCH 125
+#define CALIB_IWL_TX_ATTEN_GR4_LCH 200
+#define CALIB_IWL_TX_ATTEN_GR5_FCH 1
+#define CALIB_IWL_TX_ATTEN_GR5_LCH 20
+
+struct tx_power_dual_stream {
+	__le16 ramon_tx_gain;
+	__le16 dsp_predis_atten;
+} __attribute__ ((packed));
+
+union tx_power_dual_stream_u {
+	struct tx_power_dual_stream s;
+	__le32 dw;
+} __attribute__ ((packed));
+
+struct iwl_tx_power_db {
+	union tx_power_dual_stream_u
+	 ht_ofdm_power[POWER_TABLE_NUM_HT_OFDM_ENTRIES];
+	union tx_power_dual_stream_u legacy_cck_power;
+
+} __attribute__ ((packed));
+
+struct iwl_tx_power_table_cmd {
+	u8 band;
+	u8 channel_normal_width;
+	__le16 channel;
+	struct iwl_tx_power_db tx_power;
+} __attribute__ ((packed));
+
+struct iwl_channel_switch_cmd {
+	u8 band;
+	u8 expect_beacon;
+	__le16 channel;
+	__le32 rxon_flags;
+	__le32 rxon_filter_flags;
+	__le32 switch_time;
+	struct iwl_tx_power_db tx_power;
+} __attribute__ ((packed));
+
+struct iwl_channel_switch_notif {
+	__le16 band;
+	__le16 channel;
+	__le32 status;
+} __attribute__ ((packed));
+
+/********************* END TXPOWER *****************************************/
+
+/* HT flags */
+#define RXON_FLG_CONTROL_CHANNEL_LOCATION_MSK	0x400000
+#define RXON_FLG_CONTROL_CHANNEL_LOC_LOW_MSK	0x000000
+#define RXON_FLG_CONTROL_CHANNEL_LOC_HIGH_MSK	0x400000
+
+#define RXON_FLG_HT_OPERATING_MODE_POS		(23)
+/*yshevet - bug fix */
+#define RXON_FLG_HT_PROT_MSK			0x800000
+#define RXON_FLG_FAT_PROT_MSK			0x1000000
+
+#define RXON_FLG_CHANNEL_MODE_POS		(25)
+#define RXON_FLG_CHANNEL_MODE_MSK		0x06000000
+#define RXON_FLG_CHANNEL_MODE_LEGACY_MSK	0x00000000
+#define RXON_FLG_CHANNEL_MODE_PURE_40_MSK	0x02000000
+#define RXON_FLG_CHANNEL_MODE_MIXED_MSK		0x04000000
+
+#define RXON_RX_CHAIN_DRIVER_FORCE_MSK		(0x1<<0)
+#define RXON_RX_CHAIN_VALID_MSK			(0x7<<1)
+#define RXON_RX_CHAIN_VALID_POS			(1)
+#define RXON_RX_CHAIN_FORCE_SEL_MSK		(0x7<<4)
+#define RXON_RX_CHAIN_FORCE_SEL_POS		(4)
+#define RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK	(0x7<<7)
+#define RXON_RX_CHAIN_FORCE_MIMO_SEL_POS	(7)
+#define RXON_RX_CHAIN_CNT_MSK			(0x3<<10)
+#define RXON_RX_CHAIN_CNT_POS			(10)
+#define RXON_RX_CHAIN_MIMO_CNT_MSK		(0x3<<12)
+#define RXON_RX_CHAIN_MIMO_CNT_POS		(12)
+#define RXON_RX_CHAIN_MIMO_FORCE_MSK		(0x1<<14)
+#define RXON_RX_CHAIN_MIMO_FORCE_POS		(14)
+
+
+#define MCS_DUP_6M_PLCP 0x20
+
+/* OFDM HT rate masks */
+/* ***************************************** */
+#define R_MCS_6M_MSK 0x1
+#define R_MCS_12M_MSK 0x2
+#define R_MCS_18M_MSK 0x4
+#define R_MCS_24M_MSK 0x8
+#define R_MCS_36M_MSK 0x10
+#define R_MCS_48M_MSK 0x20
+#define R_MCS_54M_MSK 0x40
+#define R_MCS_60M_MSK 0x80
+#define R_MCS_12M_DUAL_MSK 0x100
+#define R_MCS_24M_DUAL_MSK 0x200
+#define R_MCS_36M_DUAL_MSK 0x400
+#define R_MCS_48M_DUAL_MSK 0x800
+
+#define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A))
+#define is_siso(tbl) (((tbl) == LQ_SISO))
+#define is_mimo(tbl) (((tbl) == LQ_MIMO))
+#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl))
+#define is_a_band(tbl) (((tbl) == LQ_A))
+#define is_g_and(tbl) (((tbl) == LQ_G))
+
+/*RS_NEW_API: only TLC_RTS remains and moved to bit 0 */
+#define  LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK	(1<<0)
+
+#define  LINK_QUAL_AC_NUM AC_NUM
+#define  LINK_QUAL_MAX_RETRY_NUM 16
+
+#define  LINK_QUAL_ANT_A_MSK (1<<0)
+#define  LINK_QUAL_ANT_B_MSK (1<<1)
+#define  LINK_QUAL_ANT_MSK   (LINK_QUAL_ANT_A_MSK|LINK_QUAL_ANT_B_MSK)
+
+struct iwl_link_qual_general_params {
+	u8 flags;
+	u8 mimo_delimiter;
+	u8 single_stream_ant_msk;
+	u8 dual_stream_ant_msk;
+	u8 start_rate_index[LINK_QUAL_AC_NUM];
+} __attribute__ ((packed));
+
+struct iwl_link_qual_agg_params {
+	__le16 agg_time_limit;
+	u8 agg_dis_start_th;
+	u8 agg_frame_cnt_limit;
+	__le32 reserved;
+} __attribute__ ((packed));
+
+struct iwl_link_quality_cmd {
+	u8 sta_id;
+	u8 reserved1;
+	__le16 control;
+	struct iwl_link_qual_general_params general_params;
+	struct iwl_link_qual_agg_params agg_params;
+	struct iwl_rate rate_scale_table[LINK_QUAL_MAX_RETRY_NUM];
+	__le32 reserved2;
+} __attribute__ ((packed));
+
+/* Flow Handler Definitions */
+
+/**********************/
+/*     Addresses      */
+/**********************/
+
+#define FH_MEM_LOWER_BOUND                   (0x1000)
+#define FH_MEM_UPPER_BOUND                   (0x1EF0)
+
+#define IWL_FH_REGS_LOWER_BOUND		     (0x1000)
+#define IWL_FH_REGS_UPPER_BOUND		     (0x2000)
+
+/* TFDB  Area - TFDs buffer table */
+#define FH_MEM_TFDB_LOWER_BOUND              (FH_MEM_LOWER_BOUND + 0x000)
+#define FH_MEM_TFDB_UPPER_BOUND              (FH_MEM_LOWER_BOUND + 0x900)
+/* channels 0 - 8 */
+#define FH_MEM_TFDB_CHNL_BUF0(x) (FH_MEM_TFDB_LOWER_BOUND + (x) * 0x100)
+#define FH_MEM_TFDB_CHNL_BUF1(x) (FH_MEM_TFDB_LOWER_BOUND + 0x80 + (x) * 0x100)
+
+/* TFDIB Area - TFD Immediate Buffer */
+#define FH_MEM_TFDIB_LOWER_BOUND	     (FH_MEM_LOWER_BOUND + 0x900)
+#define FH_MEM_TFDIB_UPPER_BOUND	     (FH_MEM_LOWER_BOUND + 0x958)
+/* channels 0 - 10 */
+#define FH_MEM_TFDIB_CHNL(x)     (FH_MEM_TFDIB_LOWER_BOUND + (x) * 0x8)
+
+/* TFDIB registers used in Service Mode */
+#define FH_MEM_TFDIB_CHNL9_REG0		     (FH_MEM_TFDIB_CHNL(9))
+#define FH_MEM_TFDIB_CHNL9_REG1		     (FH_MEM_TFDIB_CHNL(9) + 4)
+#define FH_MEM_TFDIB_CHNL10_REG0	     (FH_MEM_TFDIB_CHNL(10))
+#define FH_MEM_TFDIB_CHNL10_REG1	     (FH_MEM_TFDIB_CHNL(10) + 4)
+
+/* Tx service channels */
+#define FH_MEM_TFDIB_DRAM_ADDR_LSB_MASK	     (0xFFFFFFFF)
+#define FH_MEM_TFDIB_DRAM_ADDR_MSB_MASK	     (0xF00000000)
+#define FH_MEM_TFDIB_TB_LENGTH_MASK	     (0x0001FFFF)	/* bits 16:0 */
+
+#define FH_MEM_TFDIB_DRAM_ADDR_LSB_BITSHIFT  (0)
+#define FH_MEM_TFDIB_DRAM_ADDR_MSB_BITSHIFT  (32)
+#define FH_MEM_TFDIB_TB_LENGTH_BITSHIFT	     (0)
+
+#define FH_MEM_TFDIB_REG0_ADDR_MASK	     (0xFFFFFFFF)
+#define FH_MEM_TFDIB_REG1_ADDR_MASK	     (0xF0000000)
+#define FH_MEM_TFDIB_REG1_LENGTH_MASK	     (0x0001FFFF)
+
+#define FH_MEM_TFDIB_REG0_ADDR_BITSHIFT	     (0)
+#define FH_MEM_TFDIB_REG1_ADDR_BITSHIFT	     (28)
+#define FH_MEM_TFDIB_REG1_LENGTH_BITSHIFT    (0)
+
+/* TRB Area - Transmit Request Buffers */
+#define FH_MEM_TRB_LOWER_BOUND		     (FH_MEM_LOWER_BOUND + 0x0958)
+#define FH_MEM_TRB_UPPER_BOUND		     (FH_MEM_LOWER_BOUND + 0x0980)
+/* channels 0 - 8 */
+#define FH_MEM_TRB_CHNL(x)	   (FH_MEM_TRB_LOWER_BOUND + (x) * 0x4)
+
+#define IWL_FH_KW_MEM_ADDR_REG		     (FH_MEM_LOWER_BOUND + 0x97C)
+/* STAGB Area - Scheduler TAG Buffer */
+#define FH_MEM_STAGB_LOWER_BOUND	     (FH_MEM_LOWER_BOUND + 0x980)
+#define FH_MEM_STAGB_UPPER_BOUND	     (FH_MEM_LOWER_BOUND + 0x9D0)
+/* channels 0 - 8 */
+#define FH_MEM_STAGB_0(x)     (FH_MEM_STAGB_LOWER_BOUND + (x) * 0x8)
+#define FH_MEM_STAGB_1(x)     (FH_MEM_STAGB_LOWER_BOUND + 0x4 + (x) * 0x8)
+
+/* Tx service channels */
+#define FH_MEM_SRAM_ADDR_9	     (FH_MEM_STAGB_LOWER_BOUND + 0x048)
+#define FH_MEM_SRAM_ADDR_10	     (FH_MEM_STAGB_LOWER_BOUND + 0x04C)
+
+#define FH_MEM_STAGB_SRAM_ADDR_MASK	     (0x00FFFFFF)
+
+/* CBBC Area - Circular buffers base address cache pointers table */
+#define FH_MEM_CBBC_LOWER_BOUND              (FH_MEM_LOWER_BOUND + 0x9D0)
+#define FH_MEM_CBBC_UPPER_BOUND              (FH_MEM_LOWER_BOUND + 0xA10)
+/* queues 0 - 15 */
+#define FH_MEM_CBBC_QUEUE(x)  (FH_MEM_CBBC_LOWER_BOUND + (x) * 0x4)
+
+/* TAGR Area - TAG reconstruct table */
+#define FH_MEM_TAGR_LOWER_BOUND              (FH_MEM_LOWER_BOUND + 0xA10)
+#define FH_MEM_TAGR_UPPER_BOUND              (FH_MEM_LOWER_BOUND + 0xA70)
+
+/* TDBGR Area - Tx Debug Registers */
+#define FH_MEM_TDBGR_LOWER_BOUND             (FH_MEM_LOWER_BOUND + 0x0A70)
+#define FH_MEM_TDBGR_UPPER_BOUND             (FH_MEM_LOWER_BOUND + 0x0B20)
+/* channels 0 - 10 */
+#define FH_MEM_TDBGR_CHNL(x)      (FH_MEM_TDBGR_LOWER_BOUND + (x) * 0x10)
+
+#define FH_MEM_TDBGR_CHNL_REG_0(x)	     (FH_MEM_TDBGR_CHNL(x))
+#define FH_MEM_TDBGR_CHNL_REG_1(x)	     (FH_MEM_TDBGR_CHNL_REG_0(x) + 0x4)
+
+#define FH_MEM_TDBGR_CHNL_BYTES_TO_FIFO_MASK		(0x000FFFFF)
+#define FH_MEM_TDBGR_CHNL_BYTES_TO_FIFO_BITSHIFT	(0)
+
+/* RDBUF Area */
+#define FH_MEM_RDBUF_LOWER_BOUND             (FH_MEM_LOWER_BOUND + 0xB80)
+#define FH_MEM_RDBUF_UPPER_BOUND             (FH_MEM_LOWER_BOUND + 0xBC0)
+#define FH_MEM_RDBUF_CHNL0		     (FH_MEM_RDBUF_LOWER_BOUND)
+
+/* RSCSR Area */
+#define FH_MEM_RSCSR_LOWER_BOUND	(FH_MEM_LOWER_BOUND + 0xBC0)
+#define FH_MEM_RSCSR_UPPER_BOUND	(FH_MEM_LOWER_BOUND + 0xC00)
+#define FH_MEM_RSCSR_CHNL0		(FH_MEM_RSCSR_LOWER_BOUND)
+#define FH_MEM_RSCSR_CHNL1		(FH_MEM_RSCSR_LOWER_BOUND + 0x020)
+
+/* RSCSR registers used in Normal mode*/
+#define FH_RSCSR_CHNL0_STTS_WPTR_REG		(FH_MEM_RSCSR_CHNL0)
+#define FH_RSCSR_CHNL0_RBDCB_BASE_REG		(FH_MEM_RSCSR_CHNL0 + 0x004)
+#define FH_RSCSR_CHNL0_RBDCB_WPTR_REG		(FH_MEM_RSCSR_CHNL0 + 0x008)
+#define FH_RSCSR_CHNL0_RBDCB_RPTR_REG		(FH_MEM_RSCSR_CHNL0 + 0x00c)
+
+#define FH_RSCSR_FRAME_SIZE_MASK	(0x00003FFF)	/* bits 0-13 */
+/* RSCSR registers used in Service mode*/
+#define FH_RSCSR_CHNL1_RB_WPTR_REG		(FH_MEM_RSCSR_CHNL1)
+#define FH_RSCSR_CHNL1_RB_WPTR_OFFSET_REG	(FH_MEM_RSCSR_CHNL1 + 0x004)
+#define FH_RSCSR_CHNL1_RB_CHUNK_NUM_REG		(FH_MEM_RSCSR_CHNL1 + 0x008)
+#define FH_RSCSR_CHNL1_SRAM_ADDR_REG		(FH_MEM_RSCSR_CHNL1 + 0x00C)
+
+/* RCSR Area - Registers address map */
+#define FH_MEM_RCSR_LOWER_BOUND      (FH_MEM_LOWER_BOUND + 0xC00)
+#define FH_MEM_RCSR_UPPER_BOUND      (FH_MEM_LOWER_BOUND + 0xCC0)
+#define FH_MEM_RCSR_CHNL0            (FH_MEM_RCSR_LOWER_BOUND)
+#define FH_MEM_RCSR_CHNL1            (FH_MEM_RCSR_LOWER_BOUND + 0x020)
+
+#define FH_MEM_RCSR_CHNL0_CONFIG_REG	(FH_MEM_RCSR_CHNL0)
+#define FH_MEM_RCSR_CHNL0_CREDIT_REG	(FH_MEM_RCSR_CHNL0 + 0x004)
+#define FH_MEM_RCSR_CHNL0_RBD_STTS_REG	(FH_MEM_RCSR_CHNL0 + 0x008)
+#define FH_MEM_RCSR_CHNL0_RB_STTS_REG	(FH_MEM_RCSR_CHNL0 + 0x00C)
+#define FH_MEM_RCSR_CHNL0_RXPD_STTS_REG	(FH_MEM_RCSR_CHNL0 + 0x010)
+
+#define FH_MEM_RCSR_CHNL0_RBD_STTS_FRAME_RB_CNT_MASK (0x7FFFFFF0) /* bits4:30 */
+
+/* RCSR registers used in Service mode*/
+#define FH_MEM_RCSR_CHNL1_CONFIG_REG		(FH_MEM_RCSR_CHNL1)
+#define FH_MEM_RCSR_CHNL1_RB_STTS_REG         	(FH_MEM_RCSR_CHNL1 + 0x00C)
+#define FH_MEM_RCSR_CHNL1_RX_PD_STTS_REG       	(FH_MEM_RCSR_CHNL1 + 0x010)
+
+/* RSSR Area - Rx shared ctrl & status registers */
+#define FH_MEM_RSSR_LOWER_BOUND                	(FH_MEM_LOWER_BOUND + 0xC40)
+#define FH_MEM_RSSR_UPPER_BOUND               	(FH_MEM_LOWER_BOUND + 0xD00)
+#define FH_MEM_RSSR_SHARED_CTRL_REG           	(FH_MEM_RSSR_LOWER_BOUND)
+#define FH_MEM_RSSR_RX_STATUS_REG	(FH_MEM_RSSR_LOWER_BOUND + 0x004)
+#define FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV  (FH_MEM_RSSR_LOWER_BOUND + 0x008)
+
+/* TCSR */
+#define IWL_FH_TCSR_LOWER_BOUND  (IWL_FH_REGS_LOWER_BOUND + 0xD00)
+#define IWL_FH_TCSR_UPPER_BOUND  (IWL_FH_REGS_LOWER_BOUND + 0xE60)
+
+#define IWL_FH_TCSR_CHNL_NUM                            (7)
+#define IWL_FH_TCSR_CHNL_TX_CONFIG_REG(_chnl) \
+	(IWL_FH_TCSR_LOWER_BOUND + 0x20 * _chnl)
+#define IWL_FH_TCSR_CHNL_TX_CREDIT_REG(_chnl) \
+	  (IWL_FH_TCSR_LOWER_BOUND + 0x20 * _chnl + 0x4)
+#define IWL_FH_TCSR_CHNL_TX_BUF_STS_REG(_chnl) \
+	 (IWL_FH_TCSR_LOWER_BOUND + 0x20 * _chnl + 0x8)
+
+/* TSSR Area - Tx shared status registers */
+/* TSSR */
+#define IWL_FH_TSSR_LOWER_BOUND		(IWL_FH_REGS_LOWER_BOUND + 0xEA0)
+#define IWL_FH_TSSR_UPPER_BOUND		(IWL_FH_REGS_LOWER_BOUND + 0xEC0)
+
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG	(IWL_FH_TSSR_LOWER_BOUND + 0x008)
+#define IWL_FH_TSSR_TX_STATUS_REG	(IWL_FH_TSSR_LOWER_BOUND + 0x010)
+
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TXPD_ON	(0xFF000000)
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_TXPD_ON	(0x00FF0000)
+
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_MAX_FRAG_SIZE_64B	(0x00000000)
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_MAX_FRAG_SIZE_128B	(0x00000400)
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_MAX_FRAG_SIZE_256B	(0x00000800)
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_MAX_FRAG_SIZE_512B	(0x00000C00)
+
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TFD_ON	(0x00000100)
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_CBB_ON	(0x00000080)
+
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RSP_WAIT_TH	(0x00000020)
+#define IWL_FH_TSSR_TX_MSG_CONFIG_REG_VAL_RSP_WAIT_TH		(0x00000005)
+
+#define IWL_FH_TSSR_TX_STATUS_REG_BIT_BUFS_EMPTY(_chnl)	\
+	((1 << (_chnl)) << 24)
+#define IWL_FH_TSSR_TX_STATUS_REG_BIT_NO_PEND_REQ(_chnl) \
+	((1 << (_chnl)) << 16)
+
+#define IWL_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_chnl) \
+	(IWL_FH_TSSR_TX_STATUS_REG_BIT_BUFS_EMPTY(_chnl) | \
+	IWL_FH_TSSR_TX_STATUS_REG_BIT_NO_PEND_REQ(_chnl))
+
+/* SRVC */
+#define IWL_FH_SRVC_LOWER_BOUND          (IWL_FH_REGS_LOWER_BOUND + 0x9C8)
+#define IWL_FH_SRVC_UPPER_BOUND          (IWL_FH_REGS_LOWER_BOUND + 0x9D0)
+
+#define IWL_FH_SRVC_CHNL_SRAM_ADDR_REG(_chnl) \
+	  (IWL_FH_SRVC_LOWER_BOUND + (_chnl - 9) * 0x4)
+
+/* TFDIB */
+#define IWL_FH_TFDIB_LOWER_BOUND         (IWL_FH_REGS_LOWER_BOUND + 0x900)
+#define IWL_FH_TFDIB_UPPER_BOUND         (IWL_FH_REGS_LOWER_BOUND + 0x958)
+
+#define IWL_FH_TFDIB_CTRL0_REG(_chnl)    \
+	(IWL_FH_TFDIB_LOWER_BOUND + 0x8 * _chnl)
+#define IWL_FH_TFDIB_CTRL1_REG(_chnl)    \
+	(IWL_FH_TFDIB_LOWER_BOUND + 0x8 * _chnl + 0x4)
+
+#define IWL_FH_SRVC_CHNL                                (9)
+#define IWL_FH_TFDIB_CTRL1_REG_POS_MSB                  (28)
+
+/* Debug Monitor Area */
+#define FH_MEM_DM_LOWER_BOUND            (FH_MEM_LOWER_BOUND + 0xEE0)
+#define FH_MEM_DM_UPPER_BOUND            (FH_MEM_LOWER_BOUND + 0xEF0)
+#define FH_MEM_DM_CONTROL_MASK_REG       (FH_MEM_DM_LOWER_BOUND)
+#define FH_MEM_DM_CONTROL_START_REG      (FH_MEM_DM_LOWER_BOUND + 0x004)
+#define FH_MEM_DM_CONTROL_STATUS_REG     (FH_MEM_DM_LOWER_BOUND + 0x008)
+#define FH_MEM_DM_MONITOR_REG            (FH_MEM_DM_LOWER_BOUND + 0x00C)
+
+#define FH_TB1_ADDR_LOW_MASK	(0xFFFFFFFF)	/* bits 31:0 */
+#define FH_TB1_ADDR_HIGH_MASK	(0xF00000000)	/* bits 35:32 */
+#define FH_TB2_ADDR_LOW_MASK	(0x0000FFFF)	/* bits 15:0 */
+#define FH_TB2_ADDR_HIGH_MASK	(0xFFFFF0000)	/* bits 35:16 */
+
+#define FH_TB1_ADDR_LOW_BITSHIFT	(0)
+#define FH_TB1_ADDR_HIGH_BITSHIFT	(32)
+#define FH_TB2_ADDR_LOW_BITSHIFT	(0)
+#define FH_TB2_ADDR_HIGH_BITSHIFT	(16)
+
+#define FH_TB1_LENGTH_MASK         (0x00000FFF)	/* bits 11:0 */
+#define FH_TB2_LENGTH_MASK         (0x00000FFF)	/* bits 11:0 */
+
+/* number of FH channels including 2 service mode */
+#define NUM_OF_FH_CHANNELS (10)
+
+/* ctrl field bitology */
+#define FH_TFD_CTRL_PADDING_MASK     (0xC0000000)	/* bits 31:30 */
+#define FH_TFD_CTRL_NUMTB_MASK       (0x1F000000)	/* bits 28:24 */
+
+#define FH_TFD_CTRL_PADDING_BITSHIFT                (30)
+#define FH_TFD_CTRL_NUMTB_BITSHIFT                  (24)
+
+#define FH_TFD_GET_NUM_TBS(ctrl) \
+	((ctrl & FH_TFD_CTRL_NUMTB_MASK) >> FH_TFD_CTRL_NUMTB_BITSHIFT)
+#define FH_TFD_GET_PADDING(ctrl) \
+	((ctrl & FH_TFD_CTRL_PADDING_MASK) >> FH_TFD_CTRL_PADDING_BITSHIFT)
+
+/* TCSR: tx_config register values */
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF              (0x00000000)
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_DRIVER           (0x00000001)
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_ARC              (0x00000002)
+
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE_VAL    (0x00000000)
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL     (0x00000008)
+
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_NOINT           (0x00000000)
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD          (0x00100000)
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD           (0x00200000)
+
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT            (0x00000000)
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_ENDTFD           (0x00400000)
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_IFTFD            (0x00800000)
+
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE            (0x00000000)
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE_EOF        (0x40000000)
+#define IWL_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE           (0x80000000)
+
+#define IWL_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_EMPTY          (0x00000000)
+#define IWL_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_WAIT           (0x00002000)
+#define IWL_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID          (0x00000003)
+
+#define IWL_FH_TCSR_CHNL_TX_BUF_STS_REG_BIT_TFDB_WPTR           (0x00000001)
+
+#define IWL_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM              (20)
+#define IWL_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX              (12)
+
+/* CBB table */
+#define FH_CBB_ADDR_MASK	0x0FFFFFFF	/* bits 27:0 */
+#define FH_CBB_ADDR_BIT_SHIFT		(8)
+
+/* RCSR:  channel 0 rx_config register defines */
+#define FH_RCSR_CHNL0_RX_CONFIG_DMA_CHNL_EN_MASK  (0xC0000000) /* bits 30-31 */
+#define FH_RCSR_CHNL0_RX_CONFIG_RBDBC_SIZE_MASK   (0x00F00000) /* bits 20-23 */
+#define FH_RCSR_CHNL0_RX_CONFIG_RB_SIZE_MASK	  (0x00030000) /* bits 16-17 */
+#define FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MASK (0x00008000) /* bit 15 */
+#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_MASK     (0x00001000) /* bit 12 */
+#define FH_RCSR_CHNL0_RX_CONFIG_RB_TIMEOUT_MASK   (0x00000FF0) /* bit 4-11 */
+
+#define FH_RCSR_RX_CONFIG_RBDCB_SIZE_BITSHIFT       (20)
+#define FH_RCSR_RX_CONFIG_RB_SIZE_BITSHIFT			(16)
+
+#define FH_RCSR_GET_RDBC_SIZE(reg) \
+	 ((reg & FH_RCSR_RX_CONFIG_RDBC_SIZE_MASK) >> \
+	  FH_RCSR_RX_CONFIG_RDBC_SIZE_BITSHIFT)
+
+/* RCSR:  channel 1 rx_config register defines */
+#define FH_RCSR_CHNL1_RX_CONFIG_DMA_CHNL_EN_MASK  (0xC0000000) /* bits 30-31 */
+#define FH_RCSR_CHNL1_RX_CONFIG_IRQ_DEST_MASK	  (0x00003000) /* bits 12-13 */
+
+/* RCSR: rx_config register values */
+#define FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_VAL         (0x00000000)
+#define FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_EOF_VAL     (0x40000000)
+#define FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL        (0x80000000)
+#define FH_RCSR_RX_CONFIG_SINGLE_FRAME_MODE	    (0x00008000)
+
+#define FH_RCSR_RX_CONFIG_RDRBD_DISABLE_VAL         (0x00000000)
+#define FH_RCSR_RX_CONFIG_RDRBD_ENABLE_VAL          (0x20000000)
+
+#define IWL_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K    (0x00000000)
+
+/* RCSR channel 0 config register values */
+#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_NO_INT_VAL       (0x00000000)
+#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL     (0x00001000)
+
+/* RCSR channel 1 config register values */
+#define FH_RCSR_CHNL1_RX_CONFIG_IRQ_DEST_NO_INT_VAL       (0x00000000)
+#define FH_RCSR_CHNL1_RX_CONFIG_IRQ_DEST_INT_HOST_VAL     (0x00001000)
+#define FH_RCSR_CHNL1_RX_CONFIG_IRQ_DEST_INT_RTC_VAL      (0x00002000)
+#define FH_RCSR_CHNL1_RX_CONFIG_IRQ_DEST_INT_HOST_RTC_VAL (0x00003000)
+
+/* RCSR: rb status register defines */
+#define FH_RCSR_RB_BYTE_TO_SEND_MASK		(0x0001FFFF)	/* bits 0-16 */
+
+/* RSCSR: defs used in normal mode */
+#define FH_RSCSR_CHNL0_RBDCB_WPTR_MASK		(0x00000FFF)	/* bits 0-11 */
+
+/* RSCSR: defs used in service mode */
+#define FH_RSCSR_CHNL1_SRAM_ADDR_MASK		(0x00FFFFFF)	/* bits 0-23 */
+#define FH_RSCSR_CHNL1_RB_WPTR_MASK		(0x0FFFFFFF)	/* bits 0-27 */
+#define FH_RSCSR_CHNL1_RB_WPTR_OFFSET_MASK	(0x000000FF)	/* bits 0-7 */
+
+/* RSSR: RX Enable Error IRQ to Driver register defines */
+#define FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV_NO_RBD (0x00400000)	/* bit 22 */
+
+#define FH_DRAM2SRAM_DRAM_ADDR_HIGH_MASK	(0xFFFFFFF00)	/* bits 8-35 */
+#define FH_DRAM2SRAM_DRAM_ADDR_LOW_MASK		(0x000000FF)	/* bits 0-7 */
+
+#define FH_DRAM2SRAM_DRAM_ADDR_HIGH_BITSHIFT	(8)	/* bits 8-35 */
+
+/* RX DRAM status regs definitions  */
+#define FH_RX_RB_NUM_MASK			(0x00000FFF)	/* bits 0-11 */
+#define FH_RX_FRAME_NUM_MASK			(0x0FFF0000) /* bits 16-27 */
+
+#define FH_RX_RB_NUM_BITSHIFT			(0)
+#define FH_RX_FRAME_NUM_BITSHIFT		(16)
+
+#define SCD_WIN_SIZE				64
+#define SCD_FRAME_LIMIT				10
+
+/* memory mapped registers */
+#define SCD_START_OFFSET		0xa02c00
+
+#define SCD_SRAM_BASE_ADDR           (SCD_START_OFFSET + 0x0)
+#define SCD_EMPTY_BITS               (SCD_START_OFFSET + 0x4)
+#define SCD_DRAM_BASE_ADDR           (SCD_START_OFFSET + 0x10)
+#define SCD_AIT                      (SCD_START_OFFSET + 0x18)
+#define SCD_TXFACT                   (SCD_START_OFFSET + 0x1c)
+#define SCD_QUEUE_WRPTR(x)           (SCD_START_OFFSET + 0x24 + (x) * 4)
+#define SCD_QUEUE_RDPTR(x)           (SCD_START_OFFSET + 0x64 + (x) * 4)
+#define SCD_SETQUEUENUM              (SCD_START_OFFSET + 0xa4)
+#define SCD_SET_TXSTAT_TXED          (SCD_START_OFFSET + 0xa8)
+#define SCD_SET_TXSTAT_DONE          (SCD_START_OFFSET + 0xac)
+#define SCD_SET_TXSTAT_NOT_SCHD      (SCD_START_OFFSET + 0xb0)
+#define SCD_DECREASE_CREDIT          (SCD_START_OFFSET + 0xb4)
+#define SCD_DECREASE_SCREDIT         (SCD_START_OFFSET + 0xb8)
+#define SCD_LOAD_CREDIT              (SCD_START_OFFSET + 0xbc)
+#define SCD_LOAD_SCREDIT             (SCD_START_OFFSET + 0xc0)
+#define SCD_BAR                      (SCD_START_OFFSET + 0xc4)
+#define SCD_BAR_DW0                  (SCD_START_OFFSET + 0xc8)
+#define SCD_BAR_DW1                  (SCD_START_OFFSET + 0xcc)
+#define SCD_QUEUECHAIN_SEL           (SCD_START_OFFSET + 0xd0)
+#define SCD_QUERY_REQ                (SCD_START_OFFSET + 0xd8)
+#define SCD_QUERY_RES                (SCD_START_OFFSET + 0xdc)
+#define SCD_PENDING_FRAMES           (SCD_START_OFFSET + 0xe0)
+#define SCD_INTERRUPT_MASK           (SCD_START_OFFSET + 0xe4)
+#define SCD_INTERRUPT_THRESHOLD      (SCD_START_OFFSET + 0xe8)
+#define SCD_QUERY_MIN_FRAME_SIZE     (SCD_START_OFFSET + 0x100)
+#define SCD_QUEUE_STATUS_BITS(x)     (SCD_START_OFFSET + 0x104 + (x) * 4)
+
+/* SRAM structures */
+#define SCD_CONTEXT_DATA_OFFSET			0x380
+#define SCD_TX_STTS_BITMAP_OFFSET		0x400
+#define SCD_TRANSLATE_TBL_OFFSET		0x500
+#define SCD_CONTEXT_QUEUE_OFFSET(x)	(SCD_CONTEXT_DATA_OFFSET + ((x) * 8))
+#define SCD_TRANSLATE_TBL_OFFSET_QUEUE(x) \
+	((SCD_TRANSLATE_TBL_OFFSET + ((x) * 2)) & 0xfffffffc)
+
+#define SCD_TXFACT_REG_TXFIFO_MASK(lo, hi) \
+       ((1<<(hi))|((1<<(hi))-(1<<(lo))))
+
+
+#define SCD_MODE_REG_BIT_SEARCH_MODE		(1<<0)
+#define SCD_MODE_REG_BIT_SBYP_MODE		(1<<1)
+
+#define SCD_TXFIFO_POS_TID			(0)
+#define SCD_TXFIFO_POS_RA			(4)
+#define SCD_QUEUE_STTS_REG_POS_ACTIVE		(0)
+#define SCD_QUEUE_STTS_REG_POS_TXF		(1)
+#define SCD_QUEUE_STTS_REG_POS_WSL		(5)
+#define SCD_QUEUE_STTS_REG_POS_SCD_ACK		(8)
+#define SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN	(10)
+#define SCD_QUEUE_STTS_REG_MSK			(0x0007FC00)
+
+#define SCD_QUEUE_RA_TID_MAP_RATID_MSK		(0x01FF)
+
+#define SCD_QUEUE_CTX_REG1_WIN_SIZE_POS		(0)
+#define SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK		(0x0000007F)
+#define SCD_QUEUE_CTX_REG1_CREDIT_POS		(8)
+#define SCD_QUEUE_CTX_REG1_CREDIT_MSK		(0x00FFFF00)
+#define SCD_QUEUE_CTX_REG1_SUPER_CREDIT_POS	(24)
+#define SCD_QUEUE_CTX_REG1_SUPER_CREDIT_MSK	(0xFF000000)
+#define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS	(16)
+#define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK	(0x007F0000)
+
+#define CSR_HW_IF_CONFIG_REG_BIT_KEDRON_R	(0x00000010)
+#define CSR_HW_IF_CONFIG_REG_MSK_BOARD_VER	(0x00000C00)
+#define CSR_HW_IF_CONFIG_REG_BIT_MAC_SI		(0x00000100)
+#define CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI	(0x00000200)
+
+
+ /*IWL4965-END */
+
+#define IWL4965_BROADCAST_ID    (31)
+
+#define RX_RES_PHY_CNT 14
+
+#define STATISTICS_FLG_CLEAR                      (0x1)
+#define STATISTICS_FLG_DISABLE_NOTIFICATION       (0x2)
+
+#define STATISTICS_REPLY_FLG_CLEAR                __constant_cpu_to_le32(0x1)
+#define STATISTICS_REPLY_FLG_BAND_24G_MSK         __constant_cpu_to_le32(0x2)
+#define STATISTICS_REPLY_FLG_TGJ_NARROW_BAND_MSK  __constant_cpu_to_le32(0x4)
+#define STATISTICS_REPLY_FLG_FAT_MODE_MSK         __constant_cpu_to_le32(0x8)
+#define RX_PHY_FLAGS_ANTENNAE_OFFSET		(4)
+#define RX_PHY_FLAGS_ANTENNAE_MASK		(0x70)
+
+
+
+struct iwl4965_rx_phy_res {
+	u8 non_cfg_phy_cnt;     /* non configurable DSP phy data byte count */
+	u8 cfg_phy_cnt;		/* configurable DSP phy data byte count */
+	u8 stat_id;		/* configurable DSP phy data set ID */
+	u8 reserved1;
+	__le64 timestamp;	/* TSF at on air rise */
+	__le32 beacon_time_stamp; /* beacon at on-air rise */
+	__le16 phy_flags;	/* general phy flags: band, modulation, ... */
+	__le16 channel;		/* channel number */
+	__le16 non_cfg_phy[RX_RES_PHY_CNT];	/* upto 14 phy entries */
+	__le32 reserved2;
+	__le32 rate_n_flags;
+	__le16 byte_count;		/* frame's byte-count */
+	__le16 reserved3;
+} __attribute__ ((packed));
+
+struct iwl4965_rx_mpdu_res_start {
+	__le16 byte_count;
+	__le16 reserved;
+} __attribute__ ((packed));
+
+static inline u8 iwl4965_get_rate(__le32 rate_n_flags)
+{
+	return le32_to_cpu(rate_n_flags) & 0xFF;
+}
+#if 0
+
+union iwl_dram_scratch_union {
+	struct iwl_dram_scratch s;
+	__le32 dw;
+};
+
+struct iwl4965_beacon_notify {
+	struct iwl_tx_resp beacon_notify_hdr;	/*15:4 */
+	__le32 low_tsf;		/*19:16 */
+	__le32 high_tsf;		/*23:20 */
+	__le32 ibss_mgr_status;	/*27:24 */
+} __attribute__ ((packed));
+
+struct iwl4965_tx_beacon_cmd {
+	struct iwl_tx_cmd tx;	/*byte 55:4 */
+	__le16 tim_idx;		/*byte 57:56 */
+	u8 tim_size;		/*byte 58 */
+	u8 reserved1;		/*byte 59 */
+	struct ieee80211_hdr frame[0];
+	/* Beacon Frame */
+} __attribute__ ((packed));
+
+struct iwl4965_powertable_cmd {
+	__le16 flags;
+	u8 keep_alive_seconds;
+	u8 debug_flags;
+	__le32 rx_data_timeout;
+	__le32 tx_data_timeout;
+	__le32 sleep_interval[PMC_TCMD_SLEEP_INTRVL_TABLE_SIZE];
+	__le32 keep_alive_beacons;
+} __attribute__ ((packed));
+
+#define IWL_NUM_OF_STATIONS  ( 32 )
+#define BYTE_CNT_AREA_OFFSET            0
+
+enum HT_STATUS {
+	BA_STATUS_FAILURE = 0,
+	BA_STATUS_INITIATOR_DELBA,
+	BA_STATUS_RECIPIENT_DELBA,
+	BA_STATUS_RENEW_ADDBA_REQUEST,
+	BA_STATUS_ACTIVE,
+};
+#endif
+
+#define IWL_AGC_DB_MASK 	(0x3f80)	/* MASK(7,13) */
+#define IWL_AGC_DB_POS		(7)
+/* Fixed (non-configurable) rx data from phy */
+struct iwl4965_rx_non_cfg_phy {
+	__le16 ant_selection;	/* ant A bit 4, ant B bit 5, ant C bit 6 */
+	__le16 agc_info;	/* agc code 0:6, agc dB 7:13, reserved 14:15 */
+	u8 rssi_info[6];	/* we use even entries, 0/2/4 for A/B/C rssi */
+	u8 pad[0];
+} __attribute__ ((packed));
+
+struct iwl_tfd_frame_data {
+	__le32 tb1_addr;
+
+	__le32 val1;
+	/* __le32 ptb1_32_35:4; */
+#define IWL_tb1_addr_hi_POS 0
+#define IWL_tb1_addr_hi_LEN 4
+#define IWL_tb1_addr_hi_SYM val1
+	/* __le32 tb_len1:12; */
+#define IWL_tb1_len_POS 4
+#define IWL_tb1_len_LEN 12
+#define IWL_tb1_len_SYM val1
+	/* __le32 ptb2_0_15:16; */
+#define IWL_tb2_addr_lo16_POS 16
+#define IWL_tb2_addr_lo16_LEN 16
+#define IWL_tb2_addr_lo16_SYM val1
+
+	__le32 val2;
+	/* __le32 ptb2_16_35:20; */
+#define IWL_tb2_addr_hi20_POS 0
+#define IWL_tb2_addr_hi20_LEN 20
+#define IWL_tb2_addr_hi20_SYM val2
+	/* __le32 tb_len2:12; */
+#define IWL_tb2_len_POS 20
+#define IWL_tb2_len_LEN 12
+#define IWL_tb2_len_SYM val2
+} __attribute__ ((packed));
+
+struct iwl_tfd_frame {
+	__le32 val0;
+	/* __le32 rsvd1:24; */
+	/* __le32 num_tbs:5; */
+#define IWL_num_tbs_POS 24
+#define IWL_num_tbs_LEN 5
+#define IWL_num_tbs_SYM val0
+	/* __le32 rsvd2:1; */
+	/* __le32 padding:2; */
+	struct iwl_tfd_frame_data pa[10];
+	__le32 reserved;
+} __attribute__ ((packed));
+
+#define IWL4965_MAX_WIN_SIZE              64
+#define IWL4965_QUEUE_SIZE               256
+#define IWL4965_NUM_FIFOS                  7
+#define IWL4965_NUM_QUEUES                16
+
+struct iwl4965_queue_byte_cnt_entry {
+	__le16 val;
+	/* __le16 byte_cnt:12; */
+#define IWL_byte_cnt_POS 0
+#define IWL_byte_cnt_LEN 12
+#define IWL_byte_cnt_SYM val
+	/* __le16 rsvd:4; */
+} __attribute__ ((packed));
+
+struct iwl4965_sched_queue_byte_cnt_tbl {
+	struct iwl4965_queue_byte_cnt_entry tfd_offset[IWL4965_QUEUE_SIZE +
+						       IWL4965_MAX_WIN_SIZE];
+	u8 dont_care[1024 -
+		     (IWL4965_QUEUE_SIZE + IWL4965_MAX_WIN_SIZE) *
+		     sizeof(__le16)];
+} __attribute__ ((packed));
+
+/* Base physical address of iwl_shared is provided to SCD_DRAM_BASE_ADDR
+ * and &iwl_shared.val0 is provided to FH_RSCSR_CHNL0_STTS_WPTR_REG */
+struct iwl_shared {
+	struct iwl4965_sched_queue_byte_cnt_tbl
+	 queues_byte_cnt_tbls[IWL4965_NUM_QUEUES];
+	__le32 val0;
+
+	/* __le32 rb_closed_stts_rb_num:12; */
+#define IWL_rb_closed_stts_rb_num_POS 0
+#define IWL_rb_closed_stts_rb_num_LEN 12
+#define IWL_rb_closed_stts_rb_num_SYM val0
+	/* __le32 rsrv1:4; */
+	/* __le32 rb_closed_stts_rx_frame_num:12; */
+#define IWL_rb_closed_stts_rx_frame_num_POS 16
+#define IWL_rb_closed_stts_rx_frame_num_LEN 12
+#define IWL_rb_closed_stts_rx_frame_num_SYM val0
+	/* __le32 rsrv2:4; */
+
+	__le32 val1;
+	/* __le32 frame_finished_stts_rb_num:12; */
+#define IWL_frame_finished_stts_rb_num_POS 0
+#define IWL_frame_finished_stts_rb_num_LEN 12
+#define IWL_frame_finished_stts_rb_num_SYM val1
+	/* __le32 rsrv3:4; */
+	/* __le32 frame_finished_stts_rx_frame_num:12; */
+#define IWL_frame_finished_stts_rx_frame_num_POS 16
+#define IWL_frame_finished_stts_rx_frame_num_LEN 12
+#define IWL_frame_finished_stts_rx_frame_num_SYM val1
+	/* __le32 rsrv4:4; */
+
+	__le32 padding1;  /* so that allocation will be aligned to 16B */
+	__le32 padding2;
+} __attribute__ ((packed));
+
+#endif /* __iwl_4965_hw_h__ */
diff -ruN linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-4965-rs.c linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-4965-rs.c
--- linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-4965-rs.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-4965-rs.c	2007-07-31 15:29:35.000000000 +0200
@@ -0,0 +1,2150 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2005 - 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/wireless.h>
+#include <net/mac80211.h>
+#include <net/ieee80211.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+
+#include <linux/workqueue.h>
+
+#include <net/mac80211.h>
+#include <linux/wireless.h>
+
+#include "../net/mac80211/ieee80211_rate.h"
+
+#include "iwlwifi.h"
+#include "iwl-4965-rs.h"
+#include "iwl-helpers.h"
+
+#define RS_NAME "iwl-4965-rs"
+
+#define NUM_TRY_BEFORE_ANTENNA_TOGGLE 1
+#define IWL_NUMBER_TRY      1
+#define IWL_HT_NUMBER_TRY   3
+
+#define IWL_RATE_MAX_WINDOW		62
+#define IWL_RATE_HIGH_TH		10880
+#define IWL_RATE_MIN_FAILURE_TH		6
+#define IWL_RATE_MIN_SUCCESS_TH		8
+#define IWL_RATE_DECREASE_TH		1920
+#define IWL_RATE_INCREASE_TH            8960
+#define IWL_RATE_SCALE_FLUSH_INTVL   (2*HZ)        /*2 seconds */
+
+static u8 rs_ht_to_legacy[] = {
+	IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX,
+	IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX,
+	IWL_RATE_6M_INDEX,
+	IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX,
+	IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX,
+	IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX,
+	IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX
+};
+
+struct iwl_rate_scale_data {
+	u64 data;
+	s32 success_counter;
+	s32 success_ratio;
+	s32 counter;
+	s32 average_tpt;
+	unsigned long stamp;
+};
+
+struct iwl_scale_tbl_info {
+	enum iwl_table_type lq_type;
+	enum iwl_antenna_type antenna_type;
+	u8 is_SGI;
+	u8 is_fat;
+	u8 is_dup;
+	s32 *expected_tpt;
+	u8 action;
+	struct iwl_rate current_rate;
+	struct iwl_rate_scale_data win[IWL_RATE_COUNT];
+};
+
+struct iwl_rate_scale_priv {
+	u8 active_tbl;
+	u8 enable_counter;
+	u8 stay_in_tbl;
+	u8 search_better_tbl;
+	s32 last_tpt;
+	u32 table_count_limit;
+	u32 max_failure_limit;
+	u32 max_success_limit;
+	u32 table_count;
+	u32 total_failed;
+	u32 total_success;
+	u8 action_counter;
+	u32 flush_timer;
+	u8 commit_lq;
+	u8 antenna;
+	u8 valid_antenna;
+	u8 is_green;
+	u8 is_dup;
+	u8 phymode;
+	u8 ibss_sta_added;
+	u16 active_rate;
+	u16 active_siso_rate;
+	u16 active_mimo_rate;
+	u16 active_rate_basic;
+	struct iwl_link_quality_cmd lq;
+	struct iwl_scale_tbl_info lq_info[LQ_SIZE];
+};
+
+static void rs_rate_scale_perform(struct iwl_priv *priv,
+				   struct net_device *dev,
+				   struct ieee80211_hdr *hdr,
+				   struct sta_info *sta);
+static int rs_fill_link_cmd(struct iwl_rate_scale_priv *lq_data,
+			     struct iwl_rate *tx_mcs,
+			     struct iwl_link_quality_cmd *tbl,
+			     struct sta_info *sta);
+
+
+static s32 expected_tpt_A[IWL_RATE_COUNT] = {
+	0, 0, 0, 0, 40, 57, 72, 98, 121, 154, 177, 186, 186
+};
+
+static s32 expected_tpt_G[IWL_RATE_COUNT] = {
+	7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 186
+};
+
+static s32 expected_tpt_siso20MHz[IWL_RATE_COUNT] = {
+	0, 0, 0, 0, 42, 42, 76, 102, 124, 159, 183, 193, 202
+};
+
+static s32 expected_tpt_siso20MHzSGI[IWL_RATE_COUNT] = {
+	0, 0, 0, 0, 46, 46, 82, 110, 132, 168, 192, 202, 211
+};
+
+static s32 expected_tpt_mimo20MHz[IWL_RATE_COUNT] = {
+	0, 0, 0, 0, 74, 74, 123, 155, 179, 214, 236, 244, 251
+};
+
+static s32 expected_tpt_mimo20MHzSGI[IWL_RATE_COUNT] = {
+	0, 0, 0, 0, 81, 81, 131, 164, 188, 222, 243, 251, 257
+};
+
+static s32 expected_tpt_siso40MHz[IWL_RATE_COUNT] = {
+	0, 0, 0, 0, 77, 77, 127, 160, 184, 220, 242, 250, 257
+};
+
+static s32 expected_tpt_siso40MHzSGI[IWL_RATE_COUNT] = {
+	0, 0, 0, 0, 83, 83, 135, 169, 193, 229, 250, 257, 264
+};
+
+static s32 expected_tpt_mimo40MHz[IWL_RATE_COUNT] = {
+	0, 0, 0, 0, 123, 123, 182, 214, 235, 264, 279, 285, 289
+};
+
+static s32 expected_tpt_mimo40MHzSGI[IWL_RATE_COUNT] = {
+	0, 0, 0, 0, 131, 131, 191, 222, 242, 270, 284, 289, 293
+};
+
+static int iwl_lq_sync_callback(struct iwl_priv *priv,
+				struct iwl_cmd *cmd, struct sk_buff *skb)
+{
+	/*We didn't cache the SKB; let the caller free it */
+	return 1;
+}
+
+static int rs_send_lq_cmd(struct iwl_priv *priv,
+			  struct iwl_link_quality_cmd *lq, u8 flags)
+{
+#ifdef CONFIG_IWLWIFI_DEBUG
+	int i;
+#endif
+	int rc = -1;
+
+	struct iwl_host_cmd cmd = {
+		.id = REPLY_TX_LINK_QUALITY_CMD,
+		.len = sizeof(struct iwl_link_quality_cmd),
+		.meta.flags = flags,
+		.data = lq,
+	};
+
+	if ((lq->sta_id == 0xFF) &&
+	    (priv->iw_mode == IEEE80211_IF_TYPE_IBSS))
+		return rc;
+
+	if (lq->sta_id == 0xFF)
+		lq->sta_id = IWL_AP_ID;
+
+	IWL_DEBUG_RATE("lq station id 0x%x\n", lq->sta_id);
+	IWL_DEBUG_RATE("lq dta 0x%X 0x%X\n",
+		       lq->general_params.single_stream_ant_msk,
+		       lq->general_params.dual_stream_ant_msk);
+#ifdef CONFIG_IWLWIFI_DEBUG
+	for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
+		IWL_DEBUG_RATE("lq index %d 0x%X\n",
+			       i, lq->rate_scale_table[i].rate_n_flags);
+	}
+#endif
+
+	if (flags & CMD_ASYNC)
+		cmd.meta.u.callback = iwl_lq_sync_callback;
+	if (iwl_is_associated(priv) && priv->lq_mngr.lq_ready)
+		rc = iwl_send_cmd(priv, &cmd);
+
+	return rc;
+}
+
+static int rs_rate_scale_clear_window(struct iwl_rate_scale_data
+				      *window)
+{
+
+	window->data = 0;
+	window->success_counter = 0;
+	window->success_ratio = IWL_INVALID_VALUE;
+	window->counter = 0;
+	window->average_tpt = IWL_INVALID_VALUE;
+	window->stamp = 0;
+	return 0;
+}
+
+static int rs_collect_tx_data(struct iwl_rate_scale_data *windows,
+			      int scale_index, s32 tpt, u32 status)
+{
+	int rc = 0;
+	struct iwl_rate_scale_data *window = NULL;
+	u64 mask;
+	u8 win_size = IWL_RATE_MAX_WINDOW;
+	s32 fail_count;
+
+	if (scale_index < 0)
+		return -1;
+
+	if (scale_index >= IWL_RATE_COUNT)
+		return -1;
+
+	window = &(windows[scale_index]);
+
+	if (window->counter >= win_size) {
+
+		window->counter = win_size - 1;
+		mask = 1;
+		mask = (mask << (win_size - 1));
+		if ((window->data & mask)) {
+			window->data &= ~mask;
+			window->success_counter = window->success_counter - 1;
+		}
+	}
+
+	window->counter = window->counter + 1;
+	mask = window->data;
+	window->data = (mask << 1);
+	if (status != 0) {
+		window->success_counter = window->success_counter + 1;
+		window->data |= 0x1;
+	}
+
+	if (window->counter > 0) {
+		window->success_ratio = 128 * (100 * window->success_counter)
+					/ window->counter;
+	} else
+		window->success_ratio = IWL_INVALID_VALUE;
+
+
+	fail_count = window->counter - window->success_counter;
+	if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) ||
+	    (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH))
+		window->average_tpt = ((window->success_ratio *
+					tpt + 64) / 128);
+	else
+		 window->average_tpt = IWL_INVALID_VALUE;
+
+	window->stamp = jiffies;
+
+	return rc;
+}
+
+int static rs_mcs_from_tbl(struct iwl_rate *mcs_rate,
+			   struct iwl_scale_tbl_info *tbl,
+			   int index, u8 use_green)
+{
+	int rc = 0;
+
+	if (is_legacy(tbl->lq_type)) {
+		mcs_rate->rate_n_flags = iwl_rates[index].plcp;
+		if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE)
+			mcs_rate->rate_n_flags |= RATE_MCS_CCK_MSK;
+
+	} else if (is_siso(tbl->lq_type)) {
+		if (index > IWL_LAST_OFDM_RATE)
+			index = IWL_LAST_OFDM_RATE;
+		 mcs_rate->rate_n_flags = iwl_rates[index].plcp_siso |
+					  RATE_MCS_HT_MSK;
+	} else {
+		if (index > IWL_LAST_OFDM_RATE)
+			index = IWL_LAST_OFDM_RATE;
+		mcs_rate->rate_n_flags = iwl_rates[index].plcp_mimo |
+					 RATE_MCS_HT_MSK;
+	}
+
+	switch (tbl->antenna_type) {
+	case ANT_BOTH:
+		mcs_rate->rate_n_flags |= RATE_MCS_ANT_AB_MSK;
+		break;
+	case ANT_MAIN:
+		mcs_rate->rate_n_flags |= RATE_MCS_ANT_A_MSK;
+		break;
+	case ANT_AUX:
+		mcs_rate->rate_n_flags |= RATE_MCS_ANT_B_MSK;
+		break;
+	case ANT_NONE:
+		break;
+	}
+
+	if (is_legacy(tbl->lq_type))
+		return rc;
+
+	if (tbl->is_fat) {
+		if (tbl->is_dup)
+			mcs_rate->rate_n_flags |= RATE_MCS_DUP_MSK;
+		else
+			mcs_rate->rate_n_flags |= RATE_MCS_FAT_MSK;
+	}
+	if (tbl->is_SGI)
+		mcs_rate->rate_n_flags |= RATE_MCS_SGI_MSK;
+
+	if (use_green) {
+		mcs_rate->rate_n_flags |= RATE_MCS_GF_MSK;
+		if (is_siso(tbl->lq_type))
+			mcs_rate->rate_n_flags &= ~RATE_MCS_SGI_MSK;
+	}
+	return rc;
+}
+
+static int rs_get_tbl_info_from_mcs(struct iwl_rate *mcs_rate, int phymode,
+				    struct iwl_scale_tbl_info *tbl,
+				    int *rate_idx)
+{
+	int index;
+	u32 ant_msk;
+
+	index = iwl_rate_index_from_plcp(mcs_rate->rate_n_flags);
+
+	if (index  == IWL_RATE_INVALID) {
+		*rate_idx = -1;
+		return -1;
+	}
+	tbl->is_SGI = 0;
+	tbl->is_fat = 0;
+	tbl->is_dup = 0;
+	tbl->antenna_type = ANT_BOTH;
+
+	if (!(mcs_rate->rate_n_flags & RATE_MCS_HT_MSK)) {
+		ant_msk = (mcs_rate->rate_n_flags & RATE_MCS_ANT_AB_MSK);
+
+		if (ant_msk == RATE_MCS_ANT_AB_MSK)
+			tbl->lq_type = LQ_NONE;
+		else {
+
+			if ((phymode == MODE_ATHEROS_TURBO) ||
+			    (phymode == MODE_IEEE80211A))
+				tbl->lq_type = LQ_A;
+			else
+				tbl->lq_type = LQ_G;
+
+			if (mcs_rate->rate_n_flags & RATE_MCS_ANT_A_MSK)
+				tbl->antenna_type = ANT_MAIN;
+			else
+				tbl->antenna_type = ANT_AUX;
+		}
+		*rate_idx = index;
+
+	} else if (mcs_rate->s.rate <= IWL_RATE_SISO_60M_PLCP) {
+		tbl->lq_type = LQ_SISO;
+
+		ant_msk = (mcs_rate->rate_n_flags & RATE_MCS_ANT_AB_MSK);
+		if (ant_msk == RATE_MCS_ANT_AB_MSK)
+			tbl->lq_type = LQ_NONE;
+		else {
+			if (mcs_rate->rate_n_flags & RATE_MCS_ANT_A_MSK)
+				tbl->antenna_type = ANT_MAIN;
+			else
+				tbl->antenna_type = ANT_AUX;
+		}
+		if (mcs_rate->rate_n_flags & RATE_MCS_SGI_MSK)
+			tbl->is_SGI = 1;
+
+		if ((mcs_rate->rate_n_flags & RATE_MCS_FAT_MSK) ||
+		    (mcs_rate->rate_n_flags & RATE_MCS_DUP_MSK))
+			tbl->is_fat = 1;
+
+		if (mcs_rate->rate_n_flags & RATE_MCS_DUP_MSK)
+			tbl->is_dup = 1;
+
+		*rate_idx = index;
+	} else {
+		tbl->lq_type = LQ_MIMO;
+		if (mcs_rate->rate_n_flags & RATE_MCS_SGI_MSK)
+			tbl->is_SGI = 1;
+
+		if ((mcs_rate->rate_n_flags & RATE_MCS_FAT_MSK) ||
+		    (mcs_rate->rate_n_flags & RATE_MCS_DUP_MSK))
+			tbl->is_fat = 1;
+
+		if (mcs_rate->rate_n_flags & RATE_MCS_DUP_MSK)
+			tbl->is_dup = 1;
+		*rate_idx = index;
+	}
+	return 0;
+}
+
+static inline void rs_toggle_antenna(struct iwl_rate *new_rate,
+				     struct iwl_scale_tbl_info *tbl)
+{
+	if (tbl->antenna_type == ANT_AUX) {
+		tbl->antenna_type = ANT_MAIN;
+		new_rate->rate_n_flags &= ~RATE_MCS_ANT_B_MSK;
+		new_rate->rate_n_flags |= RATE_MCS_ANT_A_MSK;
+	} else {
+		tbl->antenna_type = ANT_AUX;
+		new_rate->rate_n_flags &= ~RATE_MCS_ANT_A_MSK;
+		new_rate->rate_n_flags |= RATE_MCS_ANT_B_MSK;
+	}
+}
+
+static inline s8 rs_use_green(struct iwl_priv *priv)
+{
+	s8 rc = 0;
+#ifdef CONFIG_IWLWIFI_HT
+	if (!priv->is_ht_enabled || !priv->current_assoc_ht.is_ht)
+		return 0;
+
+	if ((priv->current_assoc_ht.is_green_field) &&
+	    !(priv->current_assoc_ht.operating_mode & 0x4))
+		rc = 1;
+#endif	/*CONFIG_IWLWIFI_HT */
+	return rc;
+}
+
+/**
+ * rs_get_supported_rates - get the available rates
+ *
+ * if management frame or broadcast frame only return
+ * basic available rates.
+ *
+ */
+static void rs_get_supported_rates(struct iwl_rate_scale_priv *lq_data,
+				    struct ieee80211_hdr *hdr,
+				    enum iwl_table_type rate_type,
+				    u16 * data_rate)
+{
+	if (is_legacy(rate_type))
+		*data_rate = lq_data->active_rate;
+	else {
+		if (is_siso(rate_type))
+			*data_rate = lq_data->active_siso_rate;
+		else
+			*data_rate = lq_data->active_mimo_rate;
+	}
+
+	if (hdr && (is_multicast_ether_addr(hdr->addr1))
+	    && (lq_data->active_rate_basic))
+		*data_rate = lq_data->active_rate_basic;
+}
+
+static u16 rs_get_adjacent_rate(u8 index, u16 rate_mask, int rate_type)
+{
+	u8 high = IWL_RATE_INVALID;
+	u8 low = IWL_RATE_INVALID;
+
+	/* 802.11A or ht walks to the next literal adjascent rate in
+	 * the rate table */
+	if (is_a_band(rate_type) || !is_legacy(rate_type)) {
+		int i;
+		u32 mask;
+
+		/* Find the previous rate that is in the rate mask */
+		i = index - 1;
+		for (mask = (1 << i); i >= 0; i--, mask >>= 1) {
+			if (rate_mask & mask) {
+				low = i;
+				break;
+			}
+		}
+
+		/* Find the next rate that is in the rate mask */
+		i = index + 1;
+		for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) {
+			if (rate_mask & mask) {
+				high = i;
+				break;
+			}
+		}
+
+		return (high << 8) | low;
+	}
+
+	low = index;
+	while (low != IWL_RATE_INVALID) {
+		low = iwl_rates[low].prev_rs;
+		if (low == IWL_RATE_INVALID)
+			break;
+		if (rate_mask & (1 << low))
+			break;
+		IWL_DEBUG_RATE("Skipping masked lower rate: %d\n", low);
+	}
+
+	high = index;
+	while (high != IWL_RATE_INVALID) {
+		high = iwl_rates[high].next_rs;
+		if (high == IWL_RATE_INVALID)
+			break;
+		if (rate_mask & (1 << high))
+			break;
+		IWL_DEBUG_RATE("Skipping masked higher rate: %d\n", high);
+	}
+
+	return (high << 8) | low;
+}
+
+static int rs_get_lower_rate(struct iwl_rate_scale_priv *lq_data,
+			     struct iwl_scale_tbl_info *tbl, u8 scale_index,
+			     u8 ht_possible, struct iwl_rate *mcs_rate,
+			     struct sta_info *sta)
+{
+	u8 is_green = lq_data->is_green;
+	s32 low;
+	u16 rate_mask;
+	u16 high_low;
+	u8 switch_to_legacy = 0;
+
+	/* check if we need to switch from HT to legacy rates.
+	 * assumption is that mandatory rates (1Mbps or 6Mbps)
+	 * are always supported (spec demand) */
+	if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_index)) {
+		switch_to_legacy = 1;
+		scale_index = rs_ht_to_legacy[scale_index];
+		if ((lq_data->phymode == MODE_IEEE80211A) ||
+		    (lq_data->phymode == MODE_ATHEROS_TURBO))
+			tbl->lq_type = LQ_A;
+		else
+			tbl->lq_type = LQ_G;
+
+		if ((tbl->antenna_type == ANT_BOTH) ||
+		    (tbl->antenna_type == ANT_NONE))
+			tbl->antenna_type = ANT_MAIN;
+
+		tbl->is_fat = 0;
+		tbl->is_SGI = 0;
+	}
+
+	rs_get_supported_rates(lq_data, NULL, tbl->lq_type, &rate_mask);
+
+	/* mask with station rate restriction */
+	if (is_legacy(tbl->lq_type)) {
+		if ((lq_data->phymode == (u8) MODE_IEEE80211A) ||
+		    (lq_data->phymode == (u8) MODE_ATHEROS_TURBO))
+			rate_mask  = (u16)(rate_mask &
+			   (sta->supp_rates << IWL_FIRST_OFDM_RATE));
+		else
+			rate_mask = (u16)(rate_mask & sta->supp_rates);
+	}
+
+	/* if we did switched from HT to legacy check current rate */
+	if ((switch_to_legacy) &&
+	    (rate_mask & (1 << scale_index))) {
+		rs_mcs_from_tbl(mcs_rate, tbl, scale_index, is_green);
+		return 0;
+	}
+
+	high_low = rs_get_adjacent_rate(scale_index, rate_mask, tbl->lq_type);
+	low = high_low & 0xff;
+
+	if (low != IWL_RATE_INVALID)
+		rs_mcs_from_tbl(mcs_rate, tbl, low, is_green);
+	else
+		rs_mcs_from_tbl(mcs_rate, tbl, scale_index, is_green);
+
+	return 0;
+}
+
+static void rs_tx_status(void *priv_rate,
+			 struct net_device *dev,
+			 struct sk_buff *skb,
+			 struct ieee80211_tx_status *tx_resp)
+{
+	int status;
+	u8 retries;
+	int rs_index, index = 0;
+	struct iwl_rate_scale_priv *lq;
+	struct iwl_link_quality_cmd *table;
+	struct sta_info *sta;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	struct iwl_priv *priv = (struct iwl_priv *)priv_rate;
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct iwl_rate_scale_data *window = NULL;
+	struct iwl_rate_scale_data *search_win = NULL;
+	struct iwl_rate tx_mcs;
+	struct iwl_scale_tbl_info tbl_type;
+	struct iwl_scale_tbl_info *curr_tbl, *search_tbl;
+	u8 active_index = 0;
+	u16 fc = le16_to_cpu(hdr->frame_control);
+	s32 tpt = 0;
+
+	IWL_DEBUG_RATE("getting frame ack response, update rate "
+		       "scale window\n");
+
+	if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1))
+		return;
+
+	retries = tx_resp->retry_count;
+
+	if (retries > 15)
+		retries = 15;
+
+
+	sta = sta_info_get(local, hdr->addr1);
+
+	if (!sta || !sta->rate_ctrl_priv) {
+		if (sta)
+			sta_info_put(sta);
+		return;
+	}
+
+	lq = (struct iwl_rate_scale_priv *)sta->rate_ctrl_priv;
+
+	if (!priv->lq_mngr.lq_ready)
+		return;
+
+	if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) && !lq->ibss_sta_added)
+		return;
+
+	table = &lq->lq;
+	active_index = lq->active_tbl;
+
+	lq->antenna = (lq->valid_antenna & local->hw.conf.antenna_sel_tx);
+	if (!lq->antenna)
+		lq->antenna = lq->valid_antenna;
+
+	lq->antenna = lq->valid_antenna;
+	curr_tbl = &(lq->lq_info[active_index]);
+	search_tbl = &(lq->lq_info[(1 - active_index)]);
+	window = (struct iwl_rate_scale_data *)
+	    &(curr_tbl->win[0]);
+	search_win = (struct iwl_rate_scale_data *)
+	    &(search_tbl->win[0]);
+
+	tx_mcs.rate_n_flags = tx_resp->control.tx_rate;
+
+	rs_get_tbl_info_from_mcs(&tx_mcs, priv->phymode,
+				  &tbl_type, &rs_index);
+	if ((rs_index < 0) || (rs_index >= IWL_RATE_COUNT)) {
+		IWL_DEBUG_RATE("bad rate index at: %d rate 0x%X\n",
+			     rs_index, tx_mcs.rate_n_flags);
+		sta_info_put(sta);
+		return;
+	}
+
+	if (retries &&
+	    (tx_mcs.rate_n_flags != table->rate_scale_table[0].rate_n_flags)) {
+		IWL_DEBUG_RATE("initial rate does not match 0x%x 0x%x\n",
+				tx_mcs.rate_n_flags,
+				table->rate_scale_table[0].rate_n_flags);
+		sta_info_put(sta);
+		return;
+	}
+
+	while (retries) {
+		tx_mcs.rate_n_flags =
+		    table->rate_scale_table[index].rate_n_flags;
+		rs_get_tbl_info_from_mcs(&tx_mcs, priv->phymode,
+					  &tbl_type, &rs_index);
+
+		if ((tbl_type.lq_type == search_tbl->lq_type) &&
+		    (tbl_type.antenna_type == search_tbl->antenna_type) &&
+		    (tbl_type.is_SGI == search_tbl->is_SGI)) {
+			if (search_tbl->expected_tpt)
+				tpt = search_tbl->expected_tpt[rs_index];
+			else
+				tpt = 0;
+			rs_collect_tx_data(search_win,
+					    rs_index, tpt, 0);
+		} else if ((tbl_type.lq_type == curr_tbl->lq_type) &&
+			   (tbl_type.antenna_type == curr_tbl->antenna_type) &&
+			   (tbl_type.is_SGI == curr_tbl->is_SGI)) {
+			if (curr_tbl->expected_tpt)
+				tpt = curr_tbl->expected_tpt[rs_index];
+			else
+				tpt = 0;
+			rs_collect_tx_data(window, rs_index, tpt, 0);
+		}
+		if (lq->stay_in_tbl)
+			lq->total_failed++;
+		--retries;
+		index++;
+
+	}
+
+	if (!tx_resp->retry_count )
+		tx_mcs.rate_n_flags = tx_resp->control.tx_rate;
+	else
+		tx_mcs.rate_n_flags =
+			table->rate_scale_table[index].rate_n_flags;
+
+	rs_get_tbl_info_from_mcs(&tx_mcs, priv->phymode,
+				  &tbl_type, &rs_index);
+
+	if (tx_resp->flags & IEEE80211_TX_STATUS_ACK)
+		status = 1;
+	else
+		status = 0;
+
+	if ((tbl_type.lq_type == search_tbl->lq_type) &&
+	    (tbl_type.antenna_type == search_tbl->antenna_type) &&
+	    (tbl_type.is_SGI == search_tbl->is_SGI)) {
+		if (search_tbl->expected_tpt)
+			tpt = search_tbl->expected_tpt[rs_index];
+		else
+			tpt = 0;
+		rs_collect_tx_data(search_win,
+				    rs_index, tpt, status);
+	} else if ((tbl_type.lq_type == curr_tbl->lq_type) &&
+		   (tbl_type.antenna_type == curr_tbl->antenna_type) &&
+		   (tbl_type.is_SGI == curr_tbl->is_SGI)) {
+		if (curr_tbl->expected_tpt)
+			tpt = curr_tbl->expected_tpt[rs_index];
+		else
+			tpt = 0;
+		rs_collect_tx_data(window, rs_index, tpt, status);
+	}
+
+	if (lq->stay_in_tbl) {
+		if (status)
+			lq->total_success++;
+		else
+			lq->total_failed++;
+	}
+
+	rs_rate_scale_perform(priv, dev, hdr, sta);
+	sta_info_put(sta);
+	return;
+}
+
+static u8 rs_is_ant_connected(u8 valid_antenna,
+			      enum iwl_antenna_type antenna_type)
+{
+	if (antenna_type == ANT_AUX)
+		return ((valid_antenna & 0x2) ? 1:0);
+	else if (antenna_type == ANT_MAIN)
+		return ((valid_antenna & 0x1) ? 1:0);
+	else if (antenna_type == ANT_BOTH) {
+		if ((valid_antenna & 0x3) == 0x3)
+			return 1;
+		else
+			return 0;
+	}
+
+	return 1;
+}
+
+static u8 rs_is_other_ant_connected(u8 valid_antenna,
+				    enum iwl_antenna_type antenna_type)
+{
+	if (antenna_type == ANT_AUX)
+		return (rs_is_ant_connected(valid_antenna, ANT_MAIN));
+	else
+		return (rs_is_ant_connected(valid_antenna, ANT_AUX));
+
+	return 0;
+}
+
+#define IWL_LEGACY_SWITCH_ANTENNA	0
+#define IWL_LECACY_SWITCH_SISO		1
+#define IWL_LEGACY_SWITCH_MIMO	        2
+
+#define IWL_RS_GOOD_RATIO		12800
+
+#define IWL_ACTION_LIMIT		3
+#define IWL_LEGACY_FAILURE_LIMIT	160
+#define IWL_LEGACY_SUCCESS_LIMIT	480
+#define IWL_LEGACY_TABLE_COUNT		160
+
+#define IWL_NONE_LEGACY_FAILURE_LIMIT	400
+#define IWL_NONE_LEGACY_SUCCESS_LIMIT	4500
+#define IWL_NONE_LEGACY_TABLE_COUNT	1500
+
+#define IWL_RATE_SCALE_SWITCH		(10880)
+
+static void rs_set_stay_in_table(u8 is_legacy,
+				 struct iwl_rate_scale_priv *lq_data)
+{
+	IWL_DEBUG_HT("we are staying in the same table\n");
+	lq_data->stay_in_tbl = 1;
+	if (is_legacy) {
+		lq_data->table_count_limit = IWL_LEGACY_TABLE_COUNT;
+		lq_data->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT;
+		lq_data->max_success_limit = IWL_LEGACY_TABLE_COUNT;
+	} else {
+		lq_data->table_count_limit = IWL_NONE_LEGACY_TABLE_COUNT;
+		lq_data->max_failure_limit = IWL_NONE_LEGACY_FAILURE_LIMIT;
+		lq_data->max_success_limit = IWL_NONE_LEGACY_SUCCESS_LIMIT;
+	}
+	lq_data->table_count = 0;
+	lq_data->total_failed = 0;
+	lq_data->total_success = 0;
+}
+
+static void rs_get_expected_tpt_table(struct iwl_rate_scale_priv *lq_data,
+				      struct iwl_scale_tbl_info *tbl)
+{
+	if (is_legacy(tbl->lq_type)) {
+		if (!is_a_band(tbl->lq_type))
+			tbl->expected_tpt = expected_tpt_G;
+		else
+			tbl->expected_tpt = expected_tpt_A;
+	} else if (is_siso(tbl->lq_type)) {
+		if (tbl->is_fat && !lq_data->is_dup)
+			if (tbl->is_SGI)
+				tbl->expected_tpt = expected_tpt_siso40MHzSGI;
+			else
+				tbl->expected_tpt = expected_tpt_siso40MHz;
+		else if (tbl->is_SGI)
+			tbl->expected_tpt = expected_tpt_siso20MHzSGI;
+		else
+			tbl->expected_tpt = expected_tpt_siso20MHz;
+
+	} else if (is_mimo(tbl->lq_type)) {
+		if (tbl->is_fat && !lq_data->is_dup)
+			if (tbl->is_SGI)
+				tbl->expected_tpt = expected_tpt_mimo40MHzSGI;
+			else
+				tbl->expected_tpt = expected_tpt_mimo40MHz;
+		else if (tbl->is_SGI)
+			tbl->expected_tpt = expected_tpt_mimo20MHzSGI;
+		else
+			tbl->expected_tpt = expected_tpt_mimo20MHz;
+	} else
+		tbl->expected_tpt = expected_tpt_G;
+}
+
+#ifdef CONFIG_IWLWIFI_HT
+static s32 rs_get_best_rate(struct iwl_priv *priv,
+			    struct iwl_rate_scale_priv *lq_data,
+			    struct iwl_scale_tbl_info *tbl,
+			    u16 rate_mask, s8 index, s8 rate)
+{
+	struct iwl_scale_tbl_info *active_tbl =
+	    &(lq_data->lq_info[lq_data->active_tbl]);
+	s32 new_rate, high, low;
+	s32 active_sr = active_tbl->win[index].success_ratio;
+	s32 *tpt_tbl = tbl->expected_tpt;
+	s32 active_tpt = active_tbl->expected_tpt[index];
+	u16 high_low;
+
+	new_rate = high = low = IWL_RATE_INVALID;
+
+	for (; ; ) {
+		high_low = rs_get_adjacent_rate(rate,
+						 rate_mask, tbl->lq_type);
+
+		low = high_low & 0xff;
+		high = (high_low >> 8) & 0xff;
+
+		if ((((100 * tpt_tbl[rate]) > lq_data->last_tpt) &&
+		     ((active_sr > IWL_RATE_DECREASE_TH) &&
+		      (active_sr <= IWL_RATE_HIGH_TH) &&
+		      (tpt_tbl[rate] <= active_tpt))) ||
+		    ((active_sr >= IWL_RATE_SCALE_SWITCH) &&
+		     (tpt_tbl[rate] > active_tpt))) {
+			new_rate = rate;
+			if (low != IWL_RATE_INVALID)
+				rate = low;
+			else
+				break;
+		} else {
+			if (new_rate != IWL_RATE_INVALID)
+				break;
+			else if (high != IWL_RATE_INVALID)
+				rate = high;
+			else
+				break;
+		}
+	}
+	if (new_rate == IWL_RATE_INVALID)
+		new_rate = rate;
+
+	return new_rate;
+}
+#endif				/* CONFIG_IWLWIFI_HT */
+
+static inline u8 rs_is_both_ant_supp(u8 valid_antenna)
+{
+	return (rs_is_ant_connected(valid_antenna, ANT_BOTH));
+}
+
+static int rs_switch_to_mimo(struct iwl_priv *priv,
+			     struct iwl_rate_scale_priv *lq_data,
+			     struct iwl_scale_tbl_info *tbl, int index)
+{
+	int rc = -1;
+#ifdef CONFIG_IWLWIFI_HT
+	u16 rate_mask;
+	s32 rate;
+	s8 is_green = lq_data->is_green;
+
+	if (!priv->is_ht_enabled || !priv->current_assoc_ht.is_ht)
+		return -1;
+
+	IWL_DEBUG_HT("LQ: try to switch to MIMO\n");
+	tbl->lq_type = LQ_MIMO;
+	rs_get_supported_rates(lq_data, NULL, tbl->lq_type,
+				&rate_mask);
+
+	if (priv->current_assoc_ht.tx_mimo_ps_mode == IWL_MIMO_PS_STATIC)
+		return -1;
+
+	if (!rs_is_both_ant_supp(lq_data->antenna))
+		return -1;
+
+	rc = 0;
+	tbl->is_dup = lq_data->is_dup;
+	tbl->action = 0;
+	if (priv->current_channel_width == IWL_CHANNEL_WIDTH_40MHZ)
+		tbl->is_fat = 1;
+	else
+		tbl->is_fat = 0;
+
+	if (tbl->is_fat) {
+		if (priv->current_assoc_ht.sgf & HT_SHORT_GI_40MHZ_ONLY)
+			tbl->is_SGI = 1;
+		else
+			tbl->is_SGI = 0;
+	} else if (priv->current_assoc_ht.sgf & HT_SHORT_GI_20MHZ_ONLY)
+		tbl->is_SGI = 1;
+	else
+		tbl->is_SGI = 0;
+
+	rs_get_expected_tpt_table(lq_data, tbl);
+
+	rate = rs_get_best_rate(priv, lq_data, tbl, rate_mask, index, 4);
+
+	if ((rate == IWL_RATE_INVALID) || (rate == 1))
+		rate = IWL_RATE_48M_INDEX;
+
+	IWL_DEBUG_HT("LQ: MIMO best rate %d mask %X\n", rate, rate_mask);
+	if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask))
+		return -1;
+	rs_mcs_from_tbl(&tbl->current_rate, tbl, rate, is_green);
+
+	IWL_DEBUG_HT("LQ: Switch to new mcs %X index is green %X\n",
+		     tbl->current_rate.rate_n_flags, is_green);
+
+#endif				/*CONFIG_IWLWIFI_HT */
+	return rc;
+}
+
+static int rs_switch_to_siso(struct iwl_priv *priv,
+			     struct iwl_rate_scale_priv *lq_data,
+			     struct iwl_scale_tbl_info *tbl, int index)
+{
+	int rc = -1;
+#ifdef CONFIG_IWLWIFI_HT
+	u16 rate_mask;
+	u8 is_green = lq_data->is_green;
+	s32 rate;
+
+	IWL_DEBUG_HT("LQ: try to switch to SISO\n");
+	if (!priv->is_ht_enabled || !priv->current_assoc_ht.is_ht)
+		return -1;
+
+	rc = 0;
+	tbl->is_dup = lq_data->is_dup;
+	tbl->lq_type = LQ_SISO;
+	tbl->action = 0;
+	rs_get_supported_rates(lq_data, NULL, tbl->lq_type,
+				&rate_mask);
+
+	if (priv->current_channel_width == IWL_CHANNEL_WIDTH_40MHZ)
+		tbl->is_fat = 1;
+	else
+		tbl->is_fat = 0;
+
+	if (tbl->is_fat) {
+		if (priv->current_assoc_ht.sgf & HT_SHORT_GI_40MHZ_ONLY)
+			tbl->is_SGI = 1;
+		else
+			tbl->is_SGI = 0;
+	} else if (priv->current_assoc_ht.sgf & HT_SHORT_GI_20MHZ_ONLY)
+		tbl->is_SGI = 1;
+	else
+		tbl->is_SGI = 0;
+
+	if (is_green)
+		tbl->is_SGI = 0;
+
+	rs_get_expected_tpt_table(lq_data, tbl);
+	rate = rs_get_best_rate(priv, lq_data, tbl, rate_mask, index, 4);
+	if ((rate == IWL_RATE_INVALID) || (rate == 1))
+		rate = IWL_RATE_48M_INDEX;
+	IWL_DEBUG_HT("LQ: get best rate %d mask %X\n", rate, rate_mask);
+	if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) {
+		IWL_DEBUG_HT("can not switch with index %d rate mask %x\n",
+			     rate, rate_mask);
+		return -1;
+	}
+	rs_mcs_from_tbl(&tbl->current_rate, tbl, rate, is_green);
+	IWL_DEBUG_HT("LQ: Switch to new mcs %X index is green %X\n",
+		     tbl->current_rate.rate_n_flags, is_green);
+
+#endif				/*CONFIG_IWLWIFI_HT */
+	return rc;
+}
+
+static int rs_move_legacy_other(struct iwl_priv *priv,
+				struct iwl_rate_scale_priv *lq_data,
+				int index)
+{
+	int rc = 0;
+	struct iwl_scale_tbl_info *tbl =
+	    &(lq_data->lq_info[lq_data->active_tbl]);
+	struct iwl_scale_tbl_info *search_tbl =
+	    &(lq_data->lq_info[(1 - lq_data->active_tbl)]);
+	struct iwl_rate_scale_data *window = &(tbl->win[index]);
+	u32 sz = (sizeof(struct iwl_scale_tbl_info) -
+		  (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
+	u8 start_action = tbl->action;
+	for (; ; ) {
+		switch (tbl->action) {
+		case IWL_LEGACY_SWITCH_ANTENNA:
+			IWL_DEBUG_HT("LQ Legacy switch Antenna\n");
+
+			search_tbl->lq_type = LQ_NONE;
+			lq_data->action_counter++;
+			if (window->success_ratio >= IWL_RS_GOOD_RATIO)
+				break;
+			if (!rs_is_other_ant_connected(lq_data->antenna,
+							tbl->antenna_type))
+				break;
+
+			memcpy(search_tbl, tbl, sz);
+
+			rs_toggle_antenna(&(search_tbl->current_rate),
+					   search_tbl);
+			rs_get_expected_tpt_table(lq_data, search_tbl);
+			lq_data->search_better_tbl = 1;
+			goto out;
+
+		case IWL_LECACY_SWITCH_SISO:
+			IWL_DEBUG_HT("LQ: Legacy switch to SISO\n");
+			memcpy(search_tbl, tbl, sz);
+			search_tbl->lq_type = LQ_SISO;
+			search_tbl->is_SGI = 0;
+			search_tbl->is_fat = 0;
+			rc = rs_switch_to_siso(priv, lq_data,
+						 search_tbl, index);
+			if (!rc) {
+				lq_data->search_better_tbl = 1;
+				lq_data->action_counter = 0;
+			}
+			if (!rc)
+				goto out;
+
+			break;
+		case IWL_LEGACY_SWITCH_MIMO:
+			IWL_DEBUG_HT("LQ: Legacy switch MIMO\n");
+			memcpy(search_tbl, tbl, sz);
+			search_tbl->lq_type = LQ_MIMO;
+			search_tbl->is_SGI = 0;
+			search_tbl->is_fat = 0;
+			search_tbl->antenna_type = ANT_BOTH;
+			rc = rs_switch_to_mimo(priv, lq_data,
+						 search_tbl, index);
+			if (!rc) {
+				lq_data->search_better_tbl = 1;
+				lq_data->action_counter = 0;
+			}
+			if (!rc)
+				goto out;
+			break;
+		}
+		tbl->action++;
+		if (tbl->action > IWL_LEGACY_SWITCH_MIMO)
+			tbl->action = IWL_LEGACY_SWITCH_ANTENNA;
+
+		if (tbl->action == start_action)
+			break;
+
+	}
+	return 0;
+
+ out:
+	tbl->action++;
+	if (tbl->action > IWL_LEGACY_SWITCH_MIMO)
+		tbl->action = IWL_LEGACY_SWITCH_ANTENNA;
+	return 0;
+
+}
+
+#define IWL_SISO_SWITCH_ANTENNA  0
+#define IWL_SISO_SWITCH_MIMO     1
+#define IWL_SISO_SWITCH_GI       2
+
+static int rs_move_siso_to_other(struct iwl_priv *priv,
+				 struct iwl_rate_scale_priv *lq_data,
+				 int index)
+{
+	int rc = -1;
+	u8 is_green = lq_data->is_green;
+	struct iwl_scale_tbl_info *tbl =
+	    &(lq_data->lq_info[lq_data->active_tbl]);
+	struct iwl_scale_tbl_info *search_tbl =
+	    &(lq_data->lq_info[(1 - lq_data->active_tbl)]);
+	struct iwl_rate_scale_data *window = &(tbl->win[index]);
+	u32 sz = (sizeof(struct iwl_scale_tbl_info) -
+		  (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
+	u8 start_action = tbl->action;
+
+	for (;;) {
+		lq_data->action_counter++;
+		switch (tbl->action) {
+		case IWL_SISO_SWITCH_ANTENNA:
+			IWL_DEBUG_HT("LQ: SISO SWITCH ANTENNA SISO\n");
+			search_tbl->lq_type = LQ_NONE;
+			if (window->success_ratio >= IWL_RS_GOOD_RATIO)
+				break;
+			if (!rs_is_other_ant_connected(lq_data->antenna,
+						       tbl->antenna_type))
+				break;
+
+			memcpy(search_tbl, tbl, sz);
+			search_tbl->action = IWL_SISO_SWITCH_MIMO;
+			rs_toggle_antenna(&(search_tbl->current_rate),
+					   search_tbl);
+			lq_data->search_better_tbl = 1;
+
+			goto out;
+
+		case IWL_SISO_SWITCH_MIMO:
+			IWL_DEBUG_HT("LQ: SISO SWITCH TO MIMO FROM SISO\n");
+			memcpy(search_tbl, tbl, sz);
+			search_tbl->lq_type = LQ_MIMO;
+			search_tbl->is_SGI = 0;
+			search_tbl->is_fat = 0;
+			search_tbl->antenna_type = ANT_BOTH;
+			rc = rs_switch_to_mimo(priv, lq_data,
+						 search_tbl, index);
+			if (!rc)
+				lq_data->search_better_tbl = 1;
+
+			if (!rc)
+				goto out;
+			break;
+		case IWL_SISO_SWITCH_GI:
+			IWL_DEBUG_HT("LQ: SISO SWITCH TO GI\n");
+			memcpy(search_tbl, tbl, sz);
+			search_tbl->action = 0;
+			if (search_tbl->is_SGI)
+				search_tbl->is_SGI = 0;
+			else if (!is_green)
+				search_tbl->is_SGI = 1;
+			else
+				break;
+			lq_data->search_better_tbl = 1;
+			if ((tbl->lq_type == LQ_SISO) &&
+			    (tbl->is_SGI)) {
+				s32 tpt = lq_data->last_tpt / 100;
+				if (((!tbl->is_fat) &&
+				     (tpt >= expected_tpt_siso20MHz[index])) ||
+				    ((tbl->is_fat) &&
+				     (tpt >= expected_tpt_siso40MHz[index])))
+					lq_data->search_better_tbl = 0;
+			}
+			rs_get_expected_tpt_table(lq_data, search_tbl);
+			rs_mcs_from_tbl(&search_tbl->current_rate,
+					     search_tbl, index, is_green);
+			goto out;
+		}
+		tbl->action++;
+		if (tbl->action > IWL_SISO_SWITCH_GI)
+			tbl->action = IWL_SISO_SWITCH_ANTENNA;
+
+		if (tbl->action == start_action)
+			break;
+	}
+	return 0;
+
+ out:
+	tbl->action++;
+	if (tbl->action > IWL_SISO_SWITCH_GI)
+		tbl->action = IWL_SISO_SWITCH_ANTENNA;
+	return 0;
+}
+
+#define IWL_MIMO_SWITCH_ANTENNA_A	0
+#define IWL_MIMO_SWITCH_ANTENNA_B	1
+#define IWL_MIMO_SWITCH_GI		2
+
+static int rs_move_mimo_to_other(struct iwl_priv *priv,
+				 struct iwl_rate_scale_priv *lq_data,
+				 int index)
+{
+	int rc = -1;
+	s8 is_green = lq_data->is_green;
+	struct iwl_scale_tbl_info *tbl =
+	    &(lq_data->lq_info[lq_data->active_tbl]);
+	struct iwl_scale_tbl_info *search_tbl =
+	    &(lq_data->lq_info[(1 - lq_data->active_tbl)]);
+	u32 sz = (sizeof(struct iwl_scale_tbl_info) -
+		  (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
+	u8 start_action = tbl->action;
+
+	for (;;) {
+		lq_data->action_counter++;
+		switch (tbl->action) {
+		case IWL_MIMO_SWITCH_ANTENNA_A:
+		case IWL_MIMO_SWITCH_ANTENNA_B:
+			IWL_DEBUG_HT("LQ: MIMO SWITCH TO SISO\n");
+			memcpy(search_tbl, tbl, sz);
+			search_tbl->lq_type = LQ_SISO;
+			search_tbl->is_SGI = 0;
+			search_tbl->is_fat = 0;
+			if (tbl->action == IWL_MIMO_SWITCH_ANTENNA_A)
+				search_tbl->antenna_type = ANT_MAIN;
+			else
+				search_tbl->antenna_type = ANT_AUX;
+
+			rc = rs_switch_to_siso(priv, lq_data,
+						 search_tbl, index);
+			if (!rc) {
+				lq_data->search_better_tbl = 1;
+				goto out;
+			}
+			break;
+
+		case IWL_MIMO_SWITCH_GI:
+			IWL_DEBUG_HT("LQ: MIMO SWITCH TO GI\n");
+			memcpy(search_tbl, tbl, sz);
+			search_tbl->lq_type = LQ_MIMO;
+			search_tbl->antenna_type = ANT_BOTH;
+			search_tbl->action = 0;
+			if (search_tbl->is_SGI)
+				search_tbl->is_SGI = 0;
+			else
+				search_tbl->is_SGI = 1;
+			lq_data->search_better_tbl = 1;
+			if ((tbl->lq_type == LQ_MIMO) &&
+			    (tbl->is_SGI)) {
+				s32 tpt = lq_data->last_tpt / 100;
+				if (((!tbl->is_fat) &&
+				     (tpt >= expected_tpt_mimo20MHz[index])) ||
+				    ((tbl->is_fat) &&
+				     (tpt >= expected_tpt_mimo40MHz[index])))
+					lq_data->search_better_tbl = 0;
+			}
+			rs_get_expected_tpt_table(lq_data, search_tbl);
+			rs_mcs_from_tbl(&search_tbl->current_rate,
+					     search_tbl, index, is_green);
+			goto out;
+
+		}
+		tbl->action++;
+		if (tbl->action > IWL_MIMO_SWITCH_GI)
+			tbl->action = IWL_MIMO_SWITCH_ANTENNA_A;
+
+		if (tbl->action == start_action)
+			break;
+	}
+
+	return 0;
+ out:
+	tbl->action++;
+	if (tbl->action > IWL_MIMO_SWITCH_GI)
+		tbl->action = IWL_MIMO_SWITCH_ANTENNA_A;
+	return 0;
+
+}
+
+static void rs_stay_in_table(struct iwl_rate_scale_priv *lq_data)
+{
+	struct iwl_scale_tbl_info *tbl;
+	int i;
+	int active_tbl;
+	int flush_interval_passed = 0;
+
+	active_tbl = lq_data->active_tbl;
+
+	tbl = &(lq_data->lq_info[active_tbl]);
+
+	if (lq_data->stay_in_tbl) {
+
+		if (lq_data->flush_timer)
+			flush_interval_passed =
+			    time_after(jiffies,
+				       (unsigned long)(lq_data->flush_timer +
+					IWL_RATE_SCALE_FLUSH_INTVL));
+
+		flush_interval_passed = 0;
+		if ((lq_data->total_failed > lq_data->max_failure_limit) ||
+		    (lq_data->total_success > lq_data->max_success_limit) ||
+		    ((!lq_data->search_better_tbl) && (lq_data->flush_timer)
+		     && (flush_interval_passed))) {
+			IWL_DEBUG_HT("LQ: stay is expired %d %d %d\n:",
+				     lq_data->total_failed,
+				     lq_data->total_success,
+				     flush_interval_passed);
+			lq_data->stay_in_tbl = 0;
+			lq_data->total_failed = 0;
+			lq_data->total_success = 0;
+			lq_data->flush_timer = 0;
+		} else if (lq_data->table_count > 0) {
+			lq_data->table_count++;
+			if (lq_data->table_count >=
+			    lq_data->table_count_limit) {
+				lq_data->table_count = 0;
+
+				IWL_DEBUG_HT("LQ: stay in table clear win\n");
+				for (i = 0; i < IWL_RATE_COUNT; i++)
+					rs_rate_scale_clear_window(
+						&(tbl->win[i]));
+			}
+		}
+
+		if (!lq_data->stay_in_tbl) {
+			for (i = 0; i < IWL_RATE_COUNT; i++)
+				rs_rate_scale_clear_window(&(tbl->win[i]));
+		}
+	}
+}
+
+static void rs_rate_scale_perform(struct iwl_priv *priv,
+				  struct net_device *dev,
+				  struct ieee80211_hdr *hdr,
+				  struct sta_info *sta)
+{
+	int low = IWL_RATE_INVALID;
+	int high = IWL_RATE_INVALID;
+	int index;
+	int i;
+	struct iwl_rate_scale_data *window = NULL;
+	int current_tpt = IWL_INVALID_VALUE;
+	int low_tpt = IWL_INVALID_VALUE;
+	int high_tpt = IWL_INVALID_VALUE;
+	u32 fail_count;
+	s8 scale_action = 0;
+	u16 fc, rate_mask;
+	u8 update_lq = 0;
+	struct iwl_rate_scale_priv *lq_data;
+	struct iwl_scale_tbl_info *tbl, *tbl1;
+	u16 rate_scale_index_msk = 0;
+	struct iwl_rate mcs_rate;
+	u8 is_green = 0;
+	u8 active_tbl = 0;
+	u8 done_search = 0;
+	u16 high_low;
+
+	IWL_DEBUG_RATE("rate scale calculate new rate for skb\n");
+
+	fc = le16_to_cpu(hdr->frame_control);
+	if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1)) {
+		/* Send management frames and broadcast/multicast data using
+		 * lowest rate. */
+		/* TODO: this could probably be improved.. */
+		return;
+	}
+
+	if (!sta || !sta->rate_ctrl_priv)
+		return;
+
+	if (!priv->lq_mngr.lq_ready) {
+		IWL_DEBUG_RATE("still rate scaling not ready\n");
+		return;
+	}
+	lq_data = (struct iwl_rate_scale_priv *)sta->rate_ctrl_priv;
+
+	if (!lq_data->search_better_tbl)
+		active_tbl = lq_data->active_tbl;
+	else
+		active_tbl = 1 - lq_data->active_tbl;
+
+	tbl = &(lq_data->lq_info[active_tbl]);
+	is_green = lq_data->is_green;
+
+	index = sta->last_txrate;
+
+	IWL_DEBUG_RATE("Rate scale index %d for type %d\n", index,
+		       tbl->lq_type);
+
+	rs_get_supported_rates(lq_data, hdr, tbl->lq_type,
+				&rate_mask);
+
+	IWL_DEBUG_RATE("mask 0x%04X \n", rate_mask);
+
+	/* mask with station rate restriction */
+	if (is_legacy(tbl->lq_type)) {
+		if ((lq_data->phymode == (u8) MODE_IEEE80211A) ||
+		    (lq_data->phymode == (u8) MODE_ATHEROS_TURBO))
+			rate_scale_index_msk = (u16) (rate_mask &
+				(sta->supp_rates << IWL_FIRST_OFDM_RATE));
+		else
+			rate_scale_index_msk = (u16) (rate_mask &
+						      sta->supp_rates);
+
+	} else
+		rate_scale_index_msk = rate_mask;
+
+	if (!rate_scale_index_msk)
+		rate_scale_index_msk = rate_mask;
+
+	if (index < 0 || !((1 << index) & rate_scale_index_msk)) {
+		index = IWL_INVALID_VALUE;
+		update_lq = 1;
+
+		/* get the lowest availabe rate */
+		for (i = 0; i <= IWL_RATE_COUNT; i++) {
+			if ((1 << i) & rate_scale_index_msk)
+				index = i;
+		}
+
+		if (index == IWL_INVALID_VALUE) {
+			IWL_WARNING("Can not find a suitable rate\n");
+			return;
+		}
+	}
+
+	if (!tbl->expected_tpt)
+		rs_get_expected_tpt_table(lq_data, tbl);
+
+	window = &(tbl->win[index]);
+
+	fail_count = window->counter - window->success_counter;
+	if (((fail_count < IWL_RATE_MIN_FAILURE_TH) &&
+	     (window->success_counter < IWL_RATE_MIN_SUCCESS_TH))
+	    || (tbl->expected_tpt == NULL)) {
+		IWL_DEBUG_RATE("LQ: still below TH succ %d total %d\n",
+			       window->success_counter, window->counter);
+		window->average_tpt = IWL_INVALID_VALUE;
+		rs_stay_in_table(lq_data);
+		if (update_lq) {
+			rs_mcs_from_tbl(&mcs_rate, tbl, index, is_green);
+			rs_fill_link_cmd(lq_data, &mcs_rate,
+					   &(lq_data->lq), sta);
+			if (!rs_send_lq_cmd(priv, &lq_data->lq, CMD_ASYNC))
+				lq_data->commit_lq = 0;
+			else
+				lq_data->commit_lq = 1;
+		}
+		goto out;
+
+	} else
+		window->average_tpt = ((window->success_ratio *
+					tbl->expected_tpt[index] + 64) / 128);
+
+	if (lq_data->search_better_tbl) {
+		int success_limit = IWL_RATE_SCALE_SWITCH;
+
+		if ((window->success_ratio > success_limit) ||
+		    (window->average_tpt > lq_data->last_tpt)) {
+			if (!is_legacy(tbl->lq_type)) {
+				IWL_DEBUG_HT("LQ: we are switching to HT"
+					     " rate suc %d current tpt %d"
+					     " old tpt %d\n",
+					     window->success_ratio,
+					     window->average_tpt,
+					     lq_data->last_tpt);
+				lq_data->enable_counter = 1;
+			}
+			lq_data->active_tbl = active_tbl;
+			current_tpt = window->average_tpt;
+		} else {
+			tbl->lq_type = LQ_NONE;
+			active_tbl = lq_data->active_tbl;
+			tbl = &(lq_data->lq_info[active_tbl]);
+
+			index = iwl_rate_index_from_plcp(
+				tbl->current_rate.rate_n_flags);
+
+			update_lq = 1;
+			current_tpt = lq_data->last_tpt;
+			IWL_DEBUG_HT("XXY GO BACK TO OLD TABLE\n");
+		}
+		lq_data->search_better_tbl = 0;
+		done_search = 1;
+		goto lq_update;
+	}
+
+	high_low = rs_get_adjacent_rate(index, rate_scale_index_msk,
+					tbl->lq_type);
+	low = high_low & 0xff;
+	high = (high_low >> 8) & 0xff;
+
+	current_tpt = window->average_tpt;
+
+	if (low != IWL_RATE_INVALID) {
+		low_tpt = tbl->win[low].average_tpt;
+	}
+	if (high != IWL_RATE_INVALID)
+		high_tpt = tbl->win[high].average_tpt;
+
+
+	scale_action = 1;
+
+	if ((window->success_ratio <= IWL_RATE_DECREASE_TH) ||
+	    (current_tpt == 0)) {
+		IWL_DEBUG_RATE("decrease rate because of low success_ratio\n");
+		scale_action = -1;
+	} else if ((low_tpt == IWL_INVALID_VALUE) &&
+		   (high_tpt == IWL_INVALID_VALUE)) {
+		scale_action = 1;
+	} else if ((low_tpt != IWL_INVALID_VALUE) &&
+		   (high_tpt != IWL_INVALID_VALUE)
+		   && (low_tpt < current_tpt)
+		   && (high_tpt < current_tpt)) {
+		scale_action = 0;
+	} else {
+		if (high_tpt != IWL_INVALID_VALUE) {
+			if (high_tpt > current_tpt)
+				scale_action = 1;
+			else {
+				IWL_DEBUG_RATE
+				    ("decrease rate because of high tpt\n");
+				scale_action = -1;
+			}
+		} else if (low_tpt != IWL_INVALID_VALUE) {
+			if (low_tpt > current_tpt) {
+				IWL_DEBUG_RATE
+				    ("decrease rate because of low tpt\n");
+				scale_action = -1;
+			} else
+				scale_action = 1;
+		}
+	}
+
+	if (scale_action == -1) {
+		if ((low != IWL_RATE_INVALID) &&
+		    ((window->success_ratio > IWL_RATE_HIGH_TH) ||
+		     (current_tpt > (100 * tbl->expected_tpt[low]))))
+			scale_action = 0;
+	} else if ((scale_action == 1) &&
+		   (window->success_ratio < IWL_RATE_INCREASE_TH))
+		scale_action = 0;
+
+	switch (scale_action) {
+	case -1:
+		if (low != IWL_RATE_INVALID) {
+			update_lq = 1;
+			index = low;
+		}
+		break;
+
+	case 1:
+		if (high != IWL_RATE_INVALID) {
+			update_lq = 1;
+			index = high;
+		}
+
+		break;
+
+	case 0:
+
+	default:
+		break;
+	}
+
+	IWL_DEBUG_HT
+	    ("choose rate scale index %d action %d low %d high %d\n",
+	     index, scale_action, low, high);
+
+ lq_update:
+	if (update_lq) {
+		rs_mcs_from_tbl(&mcs_rate, tbl, index, is_green);
+		rs_fill_link_cmd(lq_data, &mcs_rate, &(lq_data->lq), sta);
+		if (!rs_send_lq_cmd(priv, &lq_data->lq, CMD_ASYNC))
+			lq_data->commit_lq = 0;
+		else
+			lq_data->commit_lq = 1;
+	}
+	rs_stay_in_table(lq_data);
+	if (!update_lq
+	    && !done_search && !lq_data->stay_in_tbl) {
+		lq_data->last_tpt = current_tpt;
+		if (is_legacy(tbl->lq_type))
+			rs_move_legacy_other(priv, lq_data, index);
+		else if (is_siso(tbl->lq_type))
+			rs_move_siso_to_other(priv, lq_data, index);
+		else
+			rs_move_mimo_to_other(priv, lq_data, index);
+
+		if (lq_data->search_better_tbl) {
+			tbl = &(lq_data->lq_info[(1 - lq_data->active_tbl)]);
+			for (i = 0; i < IWL_RATE_COUNT; i++)
+				rs_rate_scale_clear_window(&(tbl->win[i]));
+
+			index = iwl_rate_index_from_plcp(
+				tbl->current_rate.rate_n_flags);
+
+			IWL_DEBUG_HT("Switch current  mcs: %X index: %d\n",
+				     tbl->current_rate.rate_n_flags, index);
+			rs_fill_link_cmd(lq_data, &tbl->current_rate,
+					   &(lq_data->lq), sta);
+			if (!rs_send_lq_cmd(priv, &lq_data->lq, CMD_ASYNC))
+				lq_data->commit_lq = 0;
+			else
+				lq_data->commit_lq = 1;
+		}
+		tbl1 = &(lq_data->lq_info[lq_data->active_tbl]);
+		if (is_legacy(tbl1->lq_type) &&
+#ifdef CONFIG_IWLWIFI_HT
+		    !priv->current_assoc_ht.is_ht &&
+#endif
+		    (lq_data->action_counter >= 1)) {
+			lq_data->action_counter = 0;
+			IWL_DEBUG_HT("LQ: STAY in legacy table\n");
+			rs_set_stay_in_table(1, lq_data);
+		}
+
+		if (lq_data->enable_counter &&
+		    (lq_data->action_counter >= IWL_ACTION_LIMIT)) {
+#ifdef CONFIG_IWLWIFI_HT_AGG
+			if ((lq_data->last_tpt > TID_AGG_TPT_THREHOLD) &&
+			    (priv->lq_mngr.agg_ctrl.auto_agg)) {
+				priv->lq_mngr.agg_ctrl.tid_retry =
+				    TID_ALL_SPECIFIED;
+				schedule_work(&priv->agg_work);
+			}
+#endif /*CONFIG_IWLWIFI_HT_AGG */
+			lq_data->action_counter = 0;
+			rs_set_stay_in_table(0, lq_data);
+		}
+	} else {
+		if ((!update_lq) && (!done_search) && (!lq_data->flush_timer))
+			lq_data->flush_timer = jiffies;
+	}
+
+ out:
+	rs_mcs_from_tbl(&tbl->current_rate, tbl, index, is_green);
+	i = index;
+	sta->last_txrate = i;
+	/* sta->txrate is an index to A mode rates which start
+	*  at IWL_FIRST_OFDM_RATE  */
+	if ((lq_data->phymode == (u8) MODE_IEEE80211A) ||
+	    (lq_data->phymode == (u8) MODE_ATHEROS_TURBO))
+		sta->txrate = i - IWL_FIRST_OFDM_RATE;
+
+	sta->antenna_sel_tx = tbl->lq_type;
+
+	return;
+}
+
+
+static void rs_initialize_lq(struct iwl_priv *priv,
+			     struct sta_info *sta)
+{
+	int i;
+	struct iwl_rate_scale_priv *lq;
+	struct iwl_scale_tbl_info *tbl;
+	u8 active_tbl = 0;
+	int rate_idx;
+	u8 use_green = rs_use_green(priv);
+	struct iwl_rate mcs_rate;
+
+	if (!sta || !sta->rate_ctrl_priv)
+		goto out;
+
+	lq = (struct iwl_rate_scale_priv *)sta->rate_ctrl_priv;
+	i = sta->last_txrate;
+
+	if ((lq->lq.sta_id == 0xff) &&
+	    (priv->iw_mode == IEEE80211_IF_TYPE_IBSS))
+		goto out;
+
+	if (!lq->search_better_tbl)
+		active_tbl = lq->active_tbl;
+	else
+		active_tbl = 1 - lq->active_tbl;
+
+	tbl = &(lq->lq_info[active_tbl]);
+
+	if ((i < 0) || (i >= IWL_RATE_COUNT))
+		i = 0;
+
+	mcs_rate.rate_n_flags = iwl_rates[i].plcp ;
+	mcs_rate.rate_n_flags |= RATE_MCS_ANT_B_MSK;
+	mcs_rate.rate_n_flags &= ~RATE_MCS_ANT_A_MSK;
+
+	if (i >= IWL_FIRST_CCK_RATE && i <= IWL_LAST_CCK_RATE)
+		mcs_rate.rate_n_flags |= RATE_MCS_CCK_MSK;
+
+	tbl->antenna_type = ANT_AUX;
+	rs_get_tbl_info_from_mcs(&mcs_rate, priv->phymode, tbl, &rate_idx);
+	if (!rs_is_ant_connected(priv->valid_antenna, tbl->antenna_type))
+	    rs_toggle_antenna(&mcs_rate, tbl),
+
+	rs_mcs_from_tbl(&mcs_rate, tbl, rate_idx, use_green);
+	tbl->current_rate.rate_n_flags = mcs_rate.rate_n_flags;
+	rs_get_expected_tpt_table(lq, tbl);
+	rs_fill_link_cmd(lq, &mcs_rate, &(lq->lq), sta);
+	lq->commit_lq = 1;
+ out:
+	return;
+}
+
+static struct ieee80211_rate *rs_get_lowest_rate(struct ieee80211_local
+						 *local)
+{
+	struct ieee80211_hw_mode *mode = local->oper_hw_mode;
+	int i;
+
+	for (i = 0; i < mode->num_rates; i++) {
+		struct ieee80211_rate *rate = &mode->rates[i];
+
+		if (rate->flags & IEEE80211_RATE_SUPPORTED)
+			return rate;
+	}
+
+	return &mode->rates[0];
+}
+
+static struct ieee80211_rate *rs_get_rate(void *priv_rate,
+					       struct net_device *dev,
+					       struct sk_buff *skb,
+					       struct rate_control_extra
+					       *extra)
+{
+
+	int i;
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	struct sta_info *sta;
+	u16 fc;
+	struct iwl_priv *priv = (struct iwl_priv *)priv_rate;
+	struct iwl_rate_scale_priv *lq;
+
+	IWL_DEBUG_RATE("rate scale calculate new rate for skb\n");
+
+	memset(extra, 0, sizeof(*extra));
+
+	fc = le16_to_cpu(hdr->frame_control);
+	if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1)) {
+		/* Send management frames and broadcast/multicast data using
+		 * lowest rate. */
+		/* TODO: this could probably be improved.. */
+		return rs_get_lowest_rate(local);
+	}
+
+	sta = sta_info_get(local, hdr->addr1);
+
+	if (!sta || !sta->rate_ctrl_priv) {
+		if (sta)
+			sta_info_put(sta);
+		return rs_get_lowest_rate(local);
+	}
+
+	lq = (struct iwl_rate_scale_priv *)sta->rate_ctrl_priv;
+	i = sta->last_txrate;
+
+	if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) && !lq->ibss_sta_added) {
+		u8 sta_id = iwl_hw_find_station(priv, hdr->addr1);
+
+		if (sta_id == IWL_INVALID_STATION) {
+			IWL_DEBUG_RATE("LQ: ADD station " MAC_FMT "\n",
+					MAC_ARG(hdr->addr1));
+			sta_id = iwl_add_station(priv,
+						 hdr->addr1, 0, CMD_ASYNC);
+		}
+		if ((sta_id != IWL_INVALID_STATION)) {
+			lq->lq.sta_id = sta_id;
+			lq->lq.rate_scale_table[0].rate_n_flags = 0;
+			lq->ibss_sta_added = 1;
+			lq->commit_lq = 1;
+			rs_initialize_lq(priv, sta);
+		}
+		if (!lq->ibss_sta_added)
+			goto done;
+	}
+
+
+	if (lq->commit_lq) {
+		lq->commit_lq = 0;
+		if (rs_send_lq_cmd(priv, &lq->lq, CMD_ASYNC))
+			lq->commit_lq = 1;
+	}
+
+ done:
+	sta_info_put(sta);
+	if ((i < 0) || (i > IWL_RATE_COUNT)) {
+		return rs_get_lowest_rate(local);
+	}
+
+	return &priv->ieee_rates[i];
+}
+
+static void *rs_alloc_sta(void *priv, gfp_t gfp)
+{
+	struct iwl_rate_scale_priv *crl;
+	int i, j;
+
+	IWL_DEBUG_RATE("create station rate scale window\n");
+
+	crl = kzalloc(sizeof(struct iwl_rate_scale_priv), gfp);
+
+	if (crl == NULL)
+		return NULL;
+
+	memset(crl, 0, sizeof(struct iwl_rate_scale_priv));
+	crl->lq.sta_id = 0xff;
+
+	for (j = 0; j < LQ_SIZE; j++)
+		for (i = 0; i < IWL_RATE_COUNT; i++)
+			rs_rate_scale_clear_window(&(crl->lq_info[j].win[i]));
+
+	return crl;
+}
+
+static void rs_rate_init(void *priv_rate, void *priv_sta,
+			 struct ieee80211_local *local,
+			 struct sta_info *sta)
+{
+	int i, j;
+	struct ieee80211_hw_mode *mode = local->oper_hw_mode;
+	struct iwl_priv *priv = (struct iwl_priv *)priv_rate;
+	struct iwl_rate_scale_priv *crl = priv_sta;
+
+	memset(crl, 0, sizeof(struct iwl_rate_scale_priv));
+
+	crl->lq.sta_id = 0xff;
+	crl->flush_timer = 0;
+	sta->txrate = 3;
+	for (j = 0; j < LQ_SIZE; j++)
+		for (i = 0; i < IWL_RATE_COUNT; i++)
+			rs_rate_scale_clear_window(&(crl->lq_info[j].win[i]));
+
+	IWL_DEBUG_RATE("rate scale global init\n");
+	/* TODO: what is a good starting rate for STA? About middle? Maybe not
+	 * the lowest or the highest rate.. Could consider using RSSI from
+	 * previous packets? Need to have IEEE 802.1X auth succeed immediately
+	 * after assoc.. */
+
+	crl->ibss_sta_added = 0;
+	if (priv->iw_mode == IEEE80211_IF_TYPE_AP) {
+		u8 sta_id = iwl_hw_find_station(priv, sta->addr);
+		/* for IBSS the call are from tasklet */
+		IWL_DEBUG_HT("LQ: ADD station " MAC_FMT " \n",
+			     MAC_ARG(sta->addr));
+
+		if (sta_id == IWL_INVALID_STATION) {
+			IWL_DEBUG_RATE("LQ: ADD station " MAC_FMT "\n",
+					MAC_ARG(sta->addr));
+					sta_id = iwl_add_station(priv,
+						 sta->addr, 0, CMD_ASYNC);
+		}
+		if ((sta_id != IWL_INVALID_STATION)) {
+			crl->lq.sta_id = sta_id;
+			crl->lq.rate_scale_table[0].rate_n_flags = 0;
+		}
+		priv->lq_mngr.lq_ready = 1;
+	}
+
+	for (i = 0; i < mode->num_rates; i++) {
+		if ((sta->supp_rates & BIT(i)) &&
+		    (mode->rates[i].flags & IEEE80211_RATE_SUPPORTED))
+			sta->txrate = i;
+	}
+	sta->last_txrate = sta->txrate;
+	/* For MODE_IEEE80211A mode cck rate are at end
+	 * rate table
+	 */
+	if ((local->hw.conf.phymode == MODE_IEEE80211A) ||
+	    (local->hw.conf.phymode == MODE_ATHEROS_TURBO))
+		sta->last_txrate += IWL_FIRST_OFDM_RATE;
+
+	crl->is_dup = priv->is_dup;
+	crl->valid_antenna = priv->valid_antenna;
+	crl->antenna = priv->antenna;
+	crl->is_green = rs_use_green(priv);
+	crl->active_rate = priv->active_rate;
+	crl->active_rate &= ~(0x1000);
+	crl->active_rate_basic = priv->active_rate_basic;
+	crl->phymode = priv->phymode;
+#ifdef CONFIG_IWLWIFI_HT
+	crl->active_siso_rate = (priv->active_rate_ht[0] << 1);
+	crl->active_siso_rate |= (priv->active_rate_ht[0] & 0x1);
+	crl->active_siso_rate &= ~((u16)0x2);
+	crl->active_siso_rate = crl->active_siso_rate << IWL_FIRST_OFDM_RATE;
+
+	crl->active_mimo_rate = (priv->active_rate_ht[1] << 1);
+	crl->active_mimo_rate |= (priv->active_rate_ht[1] & 0x1);
+	crl->active_mimo_rate &= ~((u16)0x2);
+	crl->active_mimo_rate = crl->active_mimo_rate << IWL_FIRST_OFDM_RATE;
+#endif /*CONFIG_IWLWIFI_HT*/
+
+	if (priv)
+		rs_initialize_lq(priv, sta);
+}
+
+static int rs_fill_link_cmd(struct iwl_rate_scale_priv *lq_data,
+			    struct iwl_rate *tx_mcs,
+			    struct iwl_link_quality_cmd *lq_cmd,
+			    struct sta_info *sta)
+{
+	int index = 0;
+	int rc = 0;
+	int rate_idx;
+	u8 ant_toggle_count = 0;
+	u8 use_ht_possible = 1;
+	u8 repeat_cur_rate = 0;
+	struct iwl_rate new_rate;
+	struct iwl_scale_tbl_info tbl_type = { 0 };
+
+	rs_get_tbl_info_from_mcs(tx_mcs, lq_data->phymode,
+				  &tbl_type, &rate_idx);
+
+	if (is_legacy(tbl_type.lq_type)) {
+		ant_toggle_count = 1;
+		repeat_cur_rate = IWL_NUMBER_TRY;
+	} else
+		repeat_cur_rate = IWL_HT_NUMBER_TRY;
+
+	lq_cmd->rate_scale_table[index].rate_n_flags = tx_mcs->rate_n_flags;
+	lq_cmd->general_params.mimo_delimiter = is_mimo(tbl_type.lq_type) ? 1 : 0;
+	new_rate.rate_n_flags = tx_mcs->rate_n_flags;
+
+	if (is_mimo(tbl_type.lq_type) || (tbl_type.antenna_type == ANT_MAIN))
+		lq_cmd->general_params.single_stream_ant_msk = 1;
+	else
+		lq_cmd->general_params.single_stream_ant_msk = 2;
+
+	index++;
+	repeat_cur_rate--;
+
+	while (index < LINK_QUAL_MAX_RETRY_NUM) {
+		while (repeat_cur_rate && (index < LINK_QUAL_MAX_RETRY_NUM)) {
+			if (is_legacy(tbl_type.lq_type)){
+				if (ant_toggle_count < NUM_TRY_BEFORE_ANTENNA_TOGGLE)
+					ant_toggle_count++;
+				else {
+					rs_toggle_antenna(&new_rate, &tbl_type);
+					ant_toggle_count = 1;
+				}
+			}
+			lq_cmd->rate_scale_table[index].rate_n_flags =
+				    new_rate.rate_n_flags;
+			repeat_cur_rate--;
+			index++;
+		}
+		rs_get_tbl_info_from_mcs(&lq_cmd->rate_scale_table[index - 1],
+					  lq_data->phymode,
+					  &tbl_type, &rate_idx);
+		if (is_mimo(tbl_type.lq_type))
+			lq_cmd->general_params.mimo_delimiter = index;
+
+		rs_get_lower_rate(lq_data, &tbl_type, rate_idx,
+				     use_ht_possible, &new_rate, sta);
+
+		if (is_legacy(tbl_type.lq_type)) {
+			if (ant_toggle_count < NUM_TRY_BEFORE_ANTENNA_TOGGLE)
+				ant_toggle_count++;
+			else {
+				rs_toggle_antenna(&new_rate, &tbl_type);
+				ant_toggle_count = 1;
+			}
+			repeat_cur_rate = IWL_NUMBER_TRY;
+		} else {
+			repeat_cur_rate = IWL_HT_NUMBER_TRY;
+		}
+
+		use_ht_possible = 0;
+
+		lq_cmd->rate_scale_table[index].rate_n_flags =
+		    new_rate.rate_n_flags;
+		/*lq_cmd->rate_scale_table[index].rate_n_flags = 0x800d; */
+
+		index++;
+		repeat_cur_rate--;
+	}
+
+	/*lq_cmd->rate_scale_table[0].rate_n_flags = 0x800d; */
+
+	lq_cmd->general_params.dual_stream_ant_msk = 3;
+	lq_cmd->agg_params.agg_dis_start_th = 3;
+	lq_cmd->agg_params.agg_time_limit = 4000;
+	return rc;
+}
+
+static void *rs_alloc(struct ieee80211_local *local)
+{
+	IWL_DEBUG_RATE("enter\n");
+	IWL_DEBUG_RATE("leave\n");
+	return local->hw.priv;
+}
+
+static void rs_free(void *data)
+{
+	IWL_DEBUG_RATE("enter\n");
+	IWL_DEBUG_RATE("leave\n");
+}
+
+static void rs_clear(void *priv_rate)
+{
+	struct iwl_priv *priv = (struct iwl_priv *) priv_rate;
+
+	IWL_DEBUG_RATE("NOP\n");
+
+	priv->lq_mngr.lq_ready = 0;
+#ifdef CONFIG_IWLWIFI_HT
+#ifdef CONFIG_IWLWIFI_HT_AGG
+	if (priv->lq_mngr.agg_ctrl.granted_ba)
+		iwl4965_turn_off_agg(priv, TID_ALL_SPECIFIED);
+#endif /*CONFIG_IWLWIFI_HT_AGG */
+#endif /* CONFIG_IWLWIFI_HT */
+}
+
+static void rs_free_sta(void *priv, void *priv_sta)
+{
+	struct iwl_rate_scale_priv *rs_priv = priv_sta;
+
+	IWL_DEBUG_RATE("enter\n");
+	kfree(rs_priv);
+	IWL_DEBUG_RATE("leave\n");
+}
+
+
+static struct rate_control_ops rs_ops = {
+	.module = NULL,
+	.name = RS_NAME,
+	.tx_status = rs_tx_status,
+	.get_rate = rs_get_rate,
+	.rate_init = rs_rate_init,
+	.clear = rs_clear,
+	.alloc = rs_alloc,
+	.free = rs_free,
+	.alloc_sta = rs_alloc_sta,
+	.free_sta = rs_free_sta,
+};
+
+int iwl_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+	struct iwl_priv *priv = hw->priv;
+	struct iwl_rate_scale_priv *rs_priv;
+	struct sta_info *sta;
+	int count = 0, i;
+	u32 samples = 0, success = 0, good = 0;
+	unsigned long now = jiffies;
+	u32 max_time = 0;
+	u8 lq_type, antenna;
+
+	sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
+	if (!sta || !sta->rate_ctrl_priv) {
+		if (sta) {
+			sta_info_put(sta);
+			IWL_DEBUG_RATE("leave - no private rate data!\n");
+		} else
+			IWL_DEBUG_RATE("leave - no station!\n");
+		return sprintf(buf, "station %d not found\n", sta_id);
+	}
+
+	rs_priv = (void *)sta->rate_ctrl_priv;
+
+	lq_type = rs_priv->lq_info[rs_priv->active_tbl].lq_type;
+	antenna = rs_priv->lq_info[rs_priv->active_tbl].antenna_type;
+
+	if (is_legacy(lq_type))
+		i = IWL_RATE_54M_INDEX;
+	else
+		i = IWL_RATE_60M_INDEX;
+	while (1) {
+		u64 mask;
+		int j;
+		int active = rs_priv->active_tbl;
+
+		count +=
+		    sprintf(&buf[count], " %2dMbs: ", iwl_rates[i].ieee / 2);
+
+		mask = (1ULL << (IWL_RATE_MAX_WINDOW - 1));
+		for (j = 0; j < IWL_RATE_MAX_WINDOW; j++, mask >>= 1)
+			buf[count++] =
+				(rs_priv->lq_info[active].win[i].data & mask)
+				? '1' : '0';
+
+		samples += rs_priv->lq_info[active].win[i].counter;
+		good += rs_priv->lq_info[active].win[i].success_counter;
+		success += rs_priv->lq_info[active].win[i].success_counter *
+			   iwl_rates[i].ieee;
+
+		if (rs_priv->lq_info[active].win[i].stamp) {
+			int delta =
+				   jiffies_to_msecs(now -
+				   rs_priv->lq_info[active].win[i].stamp);
+
+			if (delta > max_time)
+				max_time = delta;
+
+			count += sprintf(&buf[count], "%5dms\n", delta);
+		} else
+			buf[count++] = '\n';
+
+		j = iwl_get_prev_ieee_rate(i);
+		if (j == i)
+			break;
+		i = j;
+	}
+	sta_info_put(sta);
+
+	/* Display the average rate of all samples taken.
+	 *
+	 * NOTE:  We multiple # of samples by 2 since the IEEE measurement
+	 * added from iwl_rates is actually 2X the rate */
+	if (samples)
+		count += sprintf(
+			 &buf[count],
+			 "\nAverage rate is %3d.%02dMbs over last %4dms\n"
+			 "%3d%% success (%d good packets over %d tries)\n",
+			 success / (2 * samples), (success * 5 / samples) % 10,
+			 max_time, good * 100 / samples, good, samples);
+	else
+		count += sprintf(&buf[count], "\nAverage rate: 0Mbs\n");
+	count += sprintf(&buf[count], "\nrate scale type %d anntena %d \n",
+			 lq_type, antenna);
+
+	return count;
+}
+
+void iwl_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
+{
+	struct iwl_priv *priv = hw->priv;
+
+	priv->lq_mngr.lq_ready = 1;
+}
+
+void iwl_rate_control_register(void)
+{
+	ieee80211_rate_control_register(&rs_ops);
+}
+
+void iwl_rate_control_unregister(void)
+{
+	ieee80211_rate_control_unregister(&rs_ops);
+}
+
diff -ruN linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-4965-rs.h linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-4965-rs.h
--- linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-4965-rs.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-4965-rs.h	2007-07-31 15:29:35.000000000 +0200
@@ -0,0 +1,286 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#ifndef __iwl_4965_rs_h__
+#define __iwl_4965_rs_h__
+
+#include "iwl-hw.h"
+#include "iwl-4965.h"
+
+struct iwl_rate_info {
+	u8 plcp;
+	u8 plcp_siso;
+	u8 plcp_mimo;
+	u8 ieee;
+	u8 prev_ieee;    /* previous rate in IEEE speeds */
+	u8 next_ieee;    /* next rate in IEEE speeds */
+	u8 prev_rs;      /* previous rate used in rs algo */
+	u8 next_rs;      /* next rate used in rs algo */
+	u8 prev_rs_tgg;  /* previous rate used in TGG rs algo */
+	u8 next_rs_tgg;  /* next rate used in TGG rs algo */
+};
+
+enum {
+	IWL_RATE_1M_INDEX = 0,
+	IWL_RATE_2M_INDEX,
+	IWL_RATE_5M_INDEX,
+	IWL_RATE_11M_INDEX,
+	IWL_RATE_6M_INDEX,
+	IWL_RATE_9M_INDEX,
+	IWL_RATE_12M_INDEX,
+	IWL_RATE_18M_INDEX,
+	IWL_RATE_24M_INDEX,
+	IWL_RATE_36M_INDEX,
+	IWL_RATE_48M_INDEX,
+	IWL_RATE_54M_INDEX,
+	IWL_RATE_60M_INDEX,
+	IWL_RATE_COUNT,
+	IWL_RATE_INVM_INDEX = IWL_RATE_COUNT,
+	IWL_RATE_INVALID = IWL_RATE_INVM_INDEX
+};
+
+enum {
+	IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX,
+	IWL_LAST_OFDM_RATE = IWL_RATE_60M_INDEX,
+	IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX,
+	IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX,
+};
+
+/* #define vs. enum to keep from defaulting to 'large integer' */
+#define	IWL_RATE_6M_MASK   (1<<IWL_RATE_6M_INDEX)
+#define	IWL_RATE_9M_MASK   (1<<IWL_RATE_9M_INDEX)
+#define	IWL_RATE_12M_MASK  (1<<IWL_RATE_12M_INDEX)
+#define	IWL_RATE_18M_MASK  (1<<IWL_RATE_18M_INDEX)
+#define	IWL_RATE_24M_MASK  (1<<IWL_RATE_24M_INDEX)
+#define	IWL_RATE_36M_MASK  (1<<IWL_RATE_36M_INDEX)
+#define	IWL_RATE_48M_MASK  (1<<IWL_RATE_48M_INDEX)
+#define	IWL_RATE_54M_MASK  (1<<IWL_RATE_54M_INDEX)
+#define IWL_RATE_60M_MASK  (1<<IWL_RATE_60M_INDEX)
+#define	IWL_RATE_1M_MASK   (1<<IWL_RATE_1M_INDEX)
+#define	IWL_RATE_2M_MASK   (1<<IWL_RATE_2M_INDEX)
+#define	IWL_RATE_5M_MASK   (1<<IWL_RATE_5M_INDEX)
+#define	IWL_RATE_11M_MASK  (1<<IWL_RATE_11M_INDEX)
+
+enum {
+	IWL_RATE_6M_PLCP  = 13,
+	IWL_RATE_9M_PLCP  = 15,
+	IWL_RATE_12M_PLCP = 5,
+	IWL_RATE_18M_PLCP = 7,
+	IWL_RATE_24M_PLCP = 9,
+	IWL_RATE_36M_PLCP = 11,
+	IWL_RATE_48M_PLCP = 1,
+	IWL_RATE_54M_PLCP = 3,
+	IWL_RATE_60M_PLCP = 3,
+	IWL_RATE_1M_PLCP  = 10,
+	IWL_RATE_2M_PLCP  = 20,
+	IWL_RATE_5M_PLCP  = 55,
+	IWL_RATE_11M_PLCP = 110,
+};
+
+/* OFDM HT rate plcp */
+enum {
+	IWL_RATE_SISO_6M_PLCP = 0,
+	IWL_RATE_SISO_12M_PLCP = 1,
+	IWL_RATE_SISO_18M_PLCP = 2,
+	IWL_RATE_SISO_24M_PLCP = 3,
+	IWL_RATE_SISO_36M_PLCP = 4,
+	IWL_RATE_SISO_48M_PLCP = 5,
+	IWL_RATE_SISO_54M_PLCP = 6,
+	IWL_RATE_SISO_60M_PLCP = 7,
+	IWL_RATE_MIMO_6M_PLCP  = 0x8,
+	IWL_RATE_MIMO_12M_PLCP = 0x9,
+	IWL_RATE_MIMO_18M_PLCP = 0xa,
+	IWL_RATE_MIMO_24M_PLCP = 0xb,
+	IWL_RATE_MIMO_36M_PLCP = 0xc,
+	IWL_RATE_MIMO_48M_PLCP = 0xd,
+	IWL_RATE_MIMO_54M_PLCP = 0xe,
+	IWL_RATE_MIMO_60M_PLCP = 0xf,
+	IWL_RATE_SISO_INVM_PLCP,
+	IWL_RATE_MIMO_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP,
+};
+
+enum {
+	IWL_RATE_6M_IEEE  = 12,
+	IWL_RATE_9M_IEEE  = 18,
+	IWL_RATE_12M_IEEE = 24,
+	IWL_RATE_18M_IEEE = 36,
+	IWL_RATE_24M_IEEE = 48,
+	IWL_RATE_36M_IEEE = 72,
+	IWL_RATE_48M_IEEE = 96,
+	IWL_RATE_54M_IEEE = 108,
+	IWL_RATE_60M_IEEE = 120,
+	IWL_RATE_1M_IEEE  = 2,
+	IWL_RATE_2M_IEEE  = 4,
+	IWL_RATE_5M_IEEE  = 11,
+	IWL_RATE_11M_IEEE = 22,
+};
+
+#define IWL_CCK_BASIC_RATES_MASK    \
+       (IWL_RATE_1M_MASK          | \
+	IWL_RATE_2M_MASK)
+
+#define IWL_CCK_RATES_MASK          \
+       (IWL_BASIC_RATES_MASK      | \
+	IWL_RATE_5M_MASK          | \
+	IWL_RATE_11M_MASK)
+
+#define IWL_OFDM_BASIC_RATES_MASK   \
+	(IWL_RATE_6M_MASK         | \
+	IWL_RATE_12M_MASK         | \
+	IWL_RATE_24M_MASK)
+
+#define IWL_OFDM_RATES_MASK         \
+       (IWL_OFDM_BASIC_RATES_MASK | \
+	IWL_RATE_9M_MASK          | \
+	IWL_RATE_18M_MASK         | \
+	IWL_RATE_36M_MASK         | \
+	IWL_RATE_48M_MASK         | \
+	IWL_RATE_54M_MASK)
+
+#define IWL_BASIC_RATES_MASK         \
+	(IWL_OFDM_BASIC_RATES_MASK | \
+	 IWL_CCK_BASIC_RATES_MASK)
+
+#define IWL_RATES_MASK ((1<<IWL_RATE_COUNT)-1)
+
+#define IWL_INVALID_VALUE    -1
+
+#define IWL_MIN_RSSI_VAL                 -100
+#define IWL_MAX_RSSI_VAL                    0
+
+extern const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT];
+
+#define LQ_SIZE		2
+
+enum iwl_table_type {
+	LQ_NONE,
+	LQ_G,
+	LQ_A,
+	LQ_SISO,
+	LQ_MIMO,
+	LQ_MAX,
+};
+
+enum iwl_antenna_type {
+	ANT_NONE,
+	ANT_MAIN,
+	ANT_AUX,
+	ANT_BOTH,
+};
+
+static inline int iwl_rate_index_from_plcp(int plcp)
+{
+	int i = 0;
+
+	if (plcp & RATE_MCS_HT_MSK) {
+		i = (plcp & 0xff);
+
+		if (i >= IWL_RATE_MIMO_6M_PLCP)
+			i = i - IWL_RATE_MIMO_6M_PLCP;
+
+		i += IWL_FIRST_OFDM_RATE;
+		/* skip 9M not supported in ht*/
+		if (i >= IWL_RATE_9M_INDEX)
+			i += 1;
+		if ((i >= IWL_FIRST_OFDM_RATE) &&
+		    (i <= IWL_LAST_OFDM_RATE))
+			return i;
+	} else {
+		for (i = 0; i < ARRAY_SIZE(iwl_rates); i++)
+			if (iwl_rates[i].plcp == (plcp &0xFF))
+				return i;
+	}
+	return -1;
+}
+
+static inline u8 iwl_rate_get_lowest_plcp(int rate_mask)
+{
+	u8 i;
+
+	for (i = IWL_RATE_1M_INDEX; i != IWL_RATE_INVALID;
+	     i = iwl_rates[i].next_ieee) {
+		if (rate_mask & (1 << i))
+			return iwl_rates[i].plcp;
+	}
+
+	return IWL_RATE_INVALID;
+}
+
+static inline u8 iwl_get_prev_ieee_rate(u8 rate_index)
+{
+	u8 rate = iwl_rates[rate_index].prev_ieee;
+	if (rate == IWL_RATE_INVALID)
+		rate = rate_index;
+	return rate;
+}
+
+#if IWL == 4965
+/**
+ * iwl_fill_rs_info - Fill an output text buffer with the rate representation
+ *
+ * NOTE:  This is provided as a quick mechanism for a user to visualize
+ * the performance of the rate control alogirthm and is not meant to be
+ * parsed software.
+ */
+extern int iwl_fill_rs_info(struct ieee80211_hw *, char *buf, u8 sta_id);
+
+/**
+ * iwl_rate_scale_init - Initialize the rate scale table based on assoc info
+ *
+ * The specific througput table used is based on the type of network
+ * the associated with, including A, B, G, and G w/ TGG protection
+ */
+extern void iwl_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id);
+
+/**
+ * iwl_rate_control_register - Register the rate control algorithm callbacks
+ *
+ * Since the rate control algorithm is hardware specific, there is no need
+ * or reason to place it as a stand alone module.  The driver can call
+ * iwl_rate_control_register in order to register the rate control callbacks
+ * with the mac80211 subsystem.  This should be performed prior to calling
+ * ieee80211_register_hw
+ *
+ */
+extern void iwl_rate_control_register(void);
+
+/**
+ * iwl_rate_control_unregister - Unregister the rate control callbacks
+ *
+ * This should be called after calling ieee80211_unregister_hw, but before
+ * the driver is unloaded.
+ */
+extern void iwl_rate_control_unregister(void);
+#else
+static inline int iwl_fill_rs_info(struct ieee80211_hw *hw, char *buf,
+				   u8 sta_id)
+{ return -ENOTSUPP; }
+static inline void iwl_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) {}
+static inline void iwl_rate_control_register(void) {}
+static inline void iwl_rate_control_unregister(void) {}
+#endif /* IWL == 4965 */
+
+#endif
diff -ruN linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-4965.c linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-4965.c
--- linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-4965.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-4965.c	2007-07-31 15:29:35.000000000 +0200
@@ -0,0 +1,4814 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <net/mac80211.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+
+#include "iwlwifi.h"
+#include "iwl-4965.h"
+#include "iwl-helpers.h"
+
+#define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np)    \
+	[IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP,      \
+				    IWL_RATE_SISO_##s##M_PLCP, \
+				    IWL_RATE_MIMO_##s##M_PLCP, \
+				    IWL_RATE_##r##M_IEEE,      \
+				    IWL_RATE_##ip##M_INDEX,    \
+				    IWL_RATE_##in##M_INDEX,    \
+				    IWL_RATE_##rp##M_INDEX,    \
+				    IWL_RATE_##rn##M_INDEX,    \
+				    IWL_RATE_##pp##M_INDEX,    \
+				    IWL_RATE_##np##M_INDEX }
+
+/*
+ * Parameter order:
+ *   rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate
+ *
+ * If there isn't a valid next or previous rate then INV is used which
+ * maps to IWL_RATE_INVALID
+ *
+ */
+const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = {
+	IWL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2),    /*  1mbps */
+	IWL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5),          /*  2mbps */
+	IWL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11),        /*5.5mbps */
+	IWL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18),      /* 11mbps */
+	IWL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11),        /*  6mbps */
+	IWL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11),       /*  9mbps */
+	IWL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18),   /* 12mbps */
+	IWL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24),   /* 18mbps */
+	IWL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36),   /* 24mbps */
+	IWL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48),   /* 36mbps */
+	IWL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54),   /* 48mbps */
+	IWL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */
+	IWL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */
+};
+
+static int is_fat_channel(u32 rxon_flags)
+{
+	return (rxon_flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK) ||
+		(rxon_flags & RXON_FLG_CHANNEL_MODE_MIXED_MSK);
+}
+
+static u8 is_single_stream(struct iwl_priv *priv)
+{
+#ifdef CONFIG_IWLWIFI_HT
+	if (!priv->is_ht_enabled || !priv->current_assoc_ht.is_ht ||
+	    (priv->active_rate_ht[1] == 0) ||
+	    (priv->ps_mode == IWL_MIMO_PS_STATIC))
+		return 1;
+#else
+	return 1;
+#endif	/*CONFIG_IWLWIFI_HT */
+	return 0;
+}
+
+/*
+ * Determine how many receiver/antenna chains to use.
+ * More provides better reception via diversity.  Fewer saves power.
+ * MIMO (dual stream) requires at least 2, but works better with 3.
+ * This does not determine *which* chains to use, just how many.
+ */
+static int iwl4965_get_rx_chain_counter(struct iwl_priv *priv,
+					u8 * idle_state, u8 * rx_state)
+{
+	u8 is_single = is_single_stream(priv);
+	u8 is_cam = (priv->status & STATUS_POWER_PMI) ? 0 : 1;
+
+	/* # of Rx chains to use when expecting MIMO. */
+	if (is_single || (!is_cam && (priv->ps_mode == IWL_MIMO_PS_STATIC)))
+		*rx_state = 2;
+	else
+		*rx_state = 3;
+
+	/* # Rx chains when idling and maybe trying to save power */
+	switch (priv->ps_mode) {
+	case IWL_MIMO_PS_STATIC:
+	case IWL_MIMO_PS_DYNAMIC:
+		*idle_state = (is_cam) ? 2 : 1;
+		break;
+	case IWL_MIMO_PS_NONE:
+		*idle_state = (is_cam) ? *rx_state : 1;
+		break;
+	default:
+		*idle_state = 1;
+		break;
+	}
+
+	return 0;
+}
+
+int iwl_hw_rxq_stop(struct iwl_priv *priv)
+{
+	int rc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	rc = iwl_grab_restricted_access(priv);
+	if (rc) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return rc;
+	}
+
+	/* stop HW */
+	iwl_write_restricted(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);
+	rc = iwl_poll_restricted_bit(priv, FH_MEM_RSSR_RX_STATUS_REG,
+				     (1 << 24), 1000);
+	if (rc < 0)
+		IWL_ERROR("Can't stop Rx DMA.\n");
+
+	iwl_release_restricted_access(priv);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+u8 iwl_hw_find_station(struct iwl_priv *priv, const u8 *bssid)
+{
+	int i;
+	int start = 0;
+	int ret = IWL_INVALID_STATION;
+	unsigned long flags;
+
+	if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) ||
+	    (priv->iw_mode == IEEE80211_IF_TYPE_AP))
+		start = IWL_STA_ID;
+
+	if (is_broadcast_ether_addr(bssid))
+		return IWL_BROADCAST_ID;
+
+	spin_lock_irqsave(&priv->sta_lock, flags);
+	for (i = start; i < (start + priv->num_stations); i++)
+		if ((priv->stations[i].used) &&
+		    (!compare_ether_addr
+		     (priv->stations[i].sta.sta.addr, bssid))) {
+			ret = i;
+			goto out;
+		}
+
+	IWL_DEBUG_ASSOC("can not find STA " MAC_FMT " total %d\n",
+			MAC_ARG(bssid), priv->num_stations);
+
+ out:
+	spin_unlock_irqrestore(&priv->sta_lock, flags);
+	return ret;
+}
+
+static int iwl4965_nic_set_pwr_src(struct iwl_priv *priv, int pwr_max)
+{
+	int rc = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	rc = iwl_grab_restricted_access(priv);
+	if (rc) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return rc;
+	}
+
+	if (!pwr_max) {
+		u32 val;
+		rc = pci_read_config_dword(priv->pci_dev, 0x0C8, &val);
+
+		if (val & PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT) {
+			iwl_set_bits_mask_restricted_reg(
+				priv, ALM_APMG_PS_CTL,
+				APMG_PS_CTRL_REG_VAL_POWER_SRC_VAUX,
+				~APMG_PS_CTRL_REG_MSK_POWER_SRC);
+
+		}
+	} else {
+		iwl_set_bits_mask_restricted_reg(
+			priv, ALM_APMG_PS_CTL,
+			APMG_PS_CTRL_REG_VAL_POWER_SRC_VMAIN,
+			~APMG_PS_CTRL_REG_MSK_POWER_SRC);
+
+	}
+
+	iwl_release_restricted_access(priv);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return rc;
+}
+
+static int iwl4965_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
+{
+	int rc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	rc = iwl_grab_restricted_access(priv);
+	if (rc) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return rc;
+	}
+
+	/* stop HW */
+	iwl_write_restricted(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);
+
+	iwl_write_restricted(priv, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0);
+	iwl_write_restricted(priv, FH_RSCSR_CHNL0_RBDCB_BASE_REG,
+			     rxq->dma_addr >> 8);
+
+	iwl_write_restricted(priv, FH_RSCSR_CHNL0_STTS_WPTR_REG,
+			     (priv->hw_setting.shared_phys +
+			      offsetof(struct iwl_shared, val0)) >> 4);
+
+	iwl_write_restricted(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG,
+			     FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
+			     FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
+			     IWL_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K |
+			     /*0x10 << 4 | */
+			     (RX_QUEUE_SIZE_LOG <<
+			      FH_RCSR_RX_CONFIG_RBDCB_SIZE_BITSHIFT));
+
+	/*
+	 * iwl_write32(priv,CSR_INT_COAL_REG,0);
+	 */
+
+	iwl_release_restricted_access(priv);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+static int iwl4965_kw_init(struct iwl_priv *priv)
+{
+	unsigned long flags;
+	int rc;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	rc = iwl_grab_restricted_access(priv);
+	if (rc)
+		goto out;
+
+	iwl_write_restricted(priv, IWL_FH_KW_MEM_ADDR_REG,
+			     (priv->kw.dma_addr >> 4));
+	iwl_release_restricted_access(priv);
+ out:
+	spin_unlock_irqrestore(&priv->lock, flags);
+	return rc;
+}
+
+static int iwl4965_kw_alloc(struct iwl_priv *priv)
+{
+	struct pci_dev *dev = priv->pci_dev;
+	struct iwl_kw *kw = &priv->kw;
+	kw->size = IWL4965_KW_SIZE;	/* TBW need set somewhere else */
+	kw->v_addr = pci_alloc_consistent(dev, kw->size, &kw->dma_addr);
+	if (!kw->v_addr)
+		return -ENOMEM;
+
+	return 0;
+}
+
+#define CHECK_AND_PRINT(x) ((eeprom_ch->flags & EEPROM_CHANNEL_##x) \
+			    ? # x " " : "")
+
+int iwl4965_set_fat_chan_info(struct iwl_priv *priv, int phymode,
+			      int channel,
+			      const struct iwl_eeprom_channel *eeprom_ch,
+			      u8 fat_extension_channel)
+{
+	struct iwl_channel_info *ch_info;
+
+	ch_info = (struct iwl_channel_info *)iwl_get_channel_info(priv,
+						phymode,
+						channel);
+	if (!is_channel_valid(ch_info))
+		return -1;
+
+		IWL_DEBUG_INFO("FAT Ch. %d [%sGHz] %s%s%s%s%s%s(" BIT_FMT8
+				" %ddBm): Ad-Hoc %ssupported\n",
+				ch_info->channel,
+				is_channel_a_band(ch_info) ?
+				"5.2" : "2.4",
+				CHECK_AND_PRINT(IBSS),
+				CHECK_AND_PRINT(ACTIVE),
+				CHECK_AND_PRINT(RADAR),
+				CHECK_AND_PRINT(WIDE),
+				CHECK_AND_PRINT(NARROW),
+				CHECK_AND_PRINT(DFS),
+				BIT_ARG8(eeprom_ch->flags),
+				eeprom_ch->
+				max_power_avg,
+				((eeprom_ch->
+				flags & EEPROM_CHANNEL_IBSS)
+				&& !(eeprom_ch->
+				flags & EEPROM_CHANNEL_RADAR))
+				? "" : "not ");
+
+	ch_info->fat_eeprom = *eeprom_ch;
+	ch_info->fat_max_power_avg = eeprom_ch->max_power_avg;
+	ch_info->fat_curr_txpow = eeprom_ch->max_power_avg;
+	ch_info->fat_min_power = 0;
+	ch_info->fat_scan_power = eeprom_ch->max_power_avg;
+	ch_info->fat_flags = eeprom_ch->flags;
+	ch_info->fat_extension_channel = fat_extension_channel;
+
+	return 0;
+}
+
+static void iwl4965_kw_free(struct iwl_priv *priv)
+{
+	struct pci_dev *dev = priv->pci_dev;
+	struct iwl_kw *kw = &priv->kw;
+	if (kw->v_addr) {
+		pci_free_consistent(dev, kw->size, kw->v_addr, kw->dma_addr);
+		memset(kw, 0, sizeof(*kw));
+	}
+
+}
+
+/**
+ * iwl4965_txq_ctx_reset - Reset TX queue context
+ * Destroys all DMA structures and initialise them again
+ *
+ * @param priv
+ * @return error code
+ */
+static int iwl4965_txq_ctx_reset(struct iwl_priv *priv)
+{
+	int rc = 0;
+	int txq_id, slots_num;
+	unsigned long flags;
+
+	iwl4965_kw_free(priv);
+
+	iwl_hw_txq_ctx_free(priv);
+
+	/* Tx CMD queue */
+	rc = iwl4965_kw_alloc(priv);
+	if (rc) {
+		IWL_ERROR("Keep Warm allocation failed");
+		goto error_kw;
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	rc = iwl_grab_restricted_access(priv);
+	if (unlikely(rc)) {
+		IWL_ERROR("TX reset failed");
+		spin_unlock_irqrestore(&priv->lock, flags);
+		goto error_reset;
+	}
+
+	iwl_write_restricted_reg(priv, SCD_TXFACT, 0);
+	iwl_release_restricted_access(priv);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	rc = iwl4965_kw_init(priv);
+	if (rc) {
+		IWL_ERROR("kw_init failed\n");
+		goto error_reset;
+	}
+
+	/* Tx queue(s) */
+	for (txq_id = 0; txq_id < priv->hw_setting.max_queue_number; txq_id++) {
+		slots_num = (txq_id == IWL_CMD_QUEUE_NUM) ?
+					TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
+		rc = iwl_tx_queue_init(priv, &priv->txq[txq_id], slots_num,
+				       txq_id);
+		if (rc) {
+			IWL_ERROR("Tx %d queue init failed\n", txq_id);
+			goto error;
+		}
+	}
+
+	return rc;
+
+ error:
+	iwl_hw_txq_ctx_free(priv);
+ error_reset:
+	iwl4965_kw_free(priv);
+ error_kw:
+	return rc;
+}
+
+int iwl_hw_nic_init(struct iwl_priv *priv)
+{
+	int rc;
+	unsigned long flags;
+	struct iwl_rx_queue *rxq = &priv->rxq;
+	u8 rev_id;
+	u32 val;
+	u8 val_link;
+
+	iwl_power_init_handle(priv);
+
+	/* nic_init */
+	spin_lock_irqsave(&priv->lock, flags);
+
+	iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS,
+		    CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
+
+	iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+	rc = iwl_poll_bit(priv, CSR_GP_CNTRL,
+			  CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+			  CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
+	if (rc < 0) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		IWL_DEBUG_INFO("Failed to init the card\n");
+		return rc;
+	}
+
+	rc = iwl_grab_restricted_access(priv);
+	if (rc) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return rc;
+	}
+
+	iwl_read_restricted_reg(priv, APMG_CLK_CTRL_REG);
+
+	iwl_write_restricted_reg(priv, APMG_CLK_CTRL_REG,
+				 APMG_CLK_REG_VAL_DMA_CLK_RQT |
+				 APMG_CLK_REG_VAL_BSM_CLK_RQT);
+	iwl_read_restricted_reg(priv, APMG_CLK_CTRL_REG);
+
+	udelay(20);
+
+	iwl_set_bits_restricted_reg(priv, ALM_APMG_PCIDEV_STT,
+				    APMG_DEV_STATE_REG_VAL_L1_ACTIVE_DISABLE);
+
+	iwl_release_restricted_access(priv);
+	iwl_write32(priv, CSR_INT_COALESCING, 512 / 32);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* Determine HW type */
+	rc = pci_read_config_byte(priv->pci_dev, PCI_REVISION_ID, &rev_id);
+	if (rc)
+		return rc;
+
+	IWL_DEBUG_INFO("HW Revision ID = 0x%X\n", rev_id);
+
+	iwl4965_nic_set_pwr_src(priv, 1);
+	spin_lock_irqsave(&priv->lock, flags);
+
+	if ((rev_id & 0x80) == 0x80 && (rev_id & 0x7f) < 8) {
+		pci_read_config_dword(priv->pci_dev, 0xe8, &val);
+		/* Enable No Snoop field */
+		pci_write_config_dword(priv->pci_dev, 0xe8, val & ~(1 << 11));
+	}
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* Read the EEPROM */
+	rc = iwl_eeprom_init(priv);
+	if (rc)
+		return rc;
+
+	if (priv->eeprom.calib_version < EEPROM_TX_POWER_VERSION_NEW) {
+		IWL_ERROR("Older EEPROM detected!  Aborting.\n");
+		return -EINVAL;
+	}
+
+	pci_read_config_byte(priv->pci_dev, PCI_LINK_CTRL, &val_link);
+
+	/* disable L1 entry -- workaround for pre-B1 */
+	pci_write_config_byte(priv->pci_dev, PCI_LINK_CTRL, val_link & ~0x02);
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	/* set CSR_HW_CONFIG_REG for uCode use */
+
+	iwl_set_bit(priv, CSR_SW_VER, CSR_HW_IF_CONFIG_REG_BIT_KEDRON_R |
+		    CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
+		    CSR_HW_IF_CONFIG_REG_BIT_MAC_SI);
+
+	rc = iwl_grab_restricted_access(priv);
+	if (rc < 0) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		IWL_DEBUG_INFO("Failed to init the card\n");
+		return rc;
+	}
+
+	iwl_read_restricted_reg(priv, ALM_APMG_PS_CTL);
+	iwl_set_bits_restricted_reg(priv, ALM_APMG_PS_CTL,
+				    APMG_PS_CTRL_REG_VAL_ALM_R_RESET_REQ);
+	udelay(5);
+	iwl_clear_bits_restricted_reg(priv, ALM_APMG_PS_CTL,
+				      APMG_PS_CTRL_REG_VAL_ALM_R_RESET_REQ);
+
+	iwl_release_restricted_access(priv);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	iwl_hw_card_show_info(priv);
+
+	/* end nic_init */
+
+	/* Allocate the RX queue, or reset if it is already allocated */
+	if (!rxq->bd) {
+		rc = iwl_rx_queue_alloc(priv);
+		if (rc) {
+			IWL_ERROR("Unable to initialize Rx queue\n");
+			return -ENOMEM;
+		}
+	} else
+		iwl_rx_queue_reset(priv, rxq);
+
+	iwl_rx_replenish(priv);
+
+	iwl4965_rx_init(priv, rxq);
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	rxq->need_update = 1;
+	iwl_rx_queue_update_write_ptr(priv, rxq);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+	rc = iwl4965_txq_ctx_reset(priv);
+	if (rc)
+		return rc;
+
+	if (priv->eeprom.sku_cap & EEPROM_SKU_CAP_SW_RF_KILL_ENABLE)
+		IWL_DEBUG_RF_KILL("SW RF KILL supported in EEPROM.\n");
+
+	if (priv->eeprom.sku_cap & EEPROM_SKU_CAP_HW_RF_KILL_ENABLE)
+		IWL_DEBUG_RF_KILL("HW RF KILL supported in EEPROM.\n");
+
+	priv->status |= STATUS_INIT;
+
+	return 0;
+}
+
+int iwl_hw_nic_stop_master(struct iwl_priv *priv)
+{
+	int rc = 0;
+	u32 reg_val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	/* set stop master bit */
+	iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
+
+	reg_val = iwl_read32(priv, CSR_GP_CNTRL);
+
+	if (CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE ==
+	    (reg_val & CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE)) {
+		IWL_DEBUG_INFO
+		    ("Card in power save, master is already stopped\n");
+	} else {
+		rc = iwl_poll_bit(priv,
+				  CSR_RESET,
+				  CSR_RESET_REG_FLAG_MASTER_DISABLED,
+				  CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
+		if (rc < 0) {
+			spin_unlock_irqrestore(&priv->lock, flags);
+			return rc;
+		}
+	}
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+	IWL_DEBUG_INFO("stop master\n");
+
+	return rc;
+}
+
+void iwl_hw_txq_ctx_stop(struct iwl_priv *priv)
+{
+
+	int txq_id;
+	unsigned long flags;
+
+	/* reset TFD queues */
+	for (txq_id = 0; txq_id < IWL4965_NUM_QUEUES; txq_id++) {
+		spin_lock_irqsave(&priv->lock, flags);
+		if (iwl_grab_restricted_access(priv)) {
+			spin_unlock_irqrestore(&priv->lock, flags);
+			continue;
+		}
+
+		iwl_write_restricted(priv,
+				     IWL_FH_TCSR_CHNL_TX_CONFIG_REG(txq_id),
+				     0x0);
+		iwl_poll_restricted_bit(priv, IWL_FH_TSSR_TX_STATUS_REG,
+					IWL_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE
+					(txq_id), 200);
+		iwl_release_restricted_access(priv);
+		spin_unlock_irqrestore(&priv->lock, flags);
+	}
+
+	iwl_hw_txq_ctx_free(priv);
+}
+
+int iwl_hw_nic_reset(struct iwl_priv *priv)
+{
+	int rc = 0;
+	unsigned long flags;
+
+	iwl_hw_nic_stop_master(priv);
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+
+	udelay(10);
+
+	iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+	rc = iwl_poll_bit(priv, CSR_RESET,
+			  CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+			  CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25);
+
+	udelay(10);
+
+	rc = iwl_grab_restricted_access(priv);
+	if (!rc) {
+		iwl_write_restricted_reg(priv, ALM_APMG_CLK_EN,
+					 APMG_CLK_REG_VAL_DMA_CLK_RQT |
+					 APMG_CLK_REG_VAL_BSM_CLK_RQT);
+
+		udelay(10);
+
+		iwl_set_bits_restricted_reg(
+			priv, ALM_APMG_PCIDEV_STT,
+			APMG_DEV_STATE_REG_VAL_L1_ACTIVE_DISABLE);
+
+		iwl_release_restricted_access(priv);
+	}
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	priv->status &= ~STATUS_HCMD_ACTIVE;
+	wake_up_interruptible(&priv->wait_command_queue);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return rc;
+
+}
+
+#define REG_RECALIB_PERIOD (60)
+
+/**
+ * iwl4965_bg_statistics_periodic - Timer callback to queue statistics
+ *
+ * This callback is provided in order to queue the statistics_work
+ * in work_queue context (v. softirq)
+ *
+ * This timer function is continually reset to execute within
+ * REG_RECALIB_PERIOD seconds since the last STATISTICS_NOTIFICATION
+ * was received.  We need to ensure we receive the statistics in order
+ * to update the temperature used for calibrating the TXPOWER.  However,
+ * we can't send the statistics command from softirq context (which
+ * is the context which timers run at) so we have to queue off the
+ * statistics_work to actually send the command to the hardware.
+ */
+static void iwl4965_bg_statistics_periodic(unsigned long data)
+{
+	struct iwl_priv *priv = (struct iwl_priv *)data;
+
+	queue_work(priv->workqueue, &priv->statistics_work);
+}
+
+/**
+ * iwl4965_bg_statistics_work - Send the statistics request to the hardware.
+ *
+ * This is queued by iwl_bg_statistics_periodic.
+ */
+static void iwl4965_bg_statistics_work(struct work_struct *work)
+{
+	struct iwl_priv *priv = container_of(work, struct iwl_priv,
+					     statistics_work);
+
+	if (priv->status & STATUS_EXIT_PENDING)
+		return;
+
+	mutex_lock(&priv->mutex);
+	iwl_send_statistics_request(priv);
+	mutex_unlock(&priv->mutex);
+}
+
+#define CT_LIMIT_CONST		259
+#define TM_CT_KILL_THRESHOLD	110
+
+void iwl4965_rf_kill_ct_config(struct iwl_priv *priv)
+{
+	struct iwl_ct_kill_config cmd;
+	u32 R1 = 0, R2 = 0, R3 = 0;
+	u32 temp_th = 0;
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
+		    CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	if (priv->statistics.flag & STATISTICS_REPLY_FLG_FAT_MODE_MSK) {
+		R1 = (s32)le32_to_cpu(priv->card_alive_init.therm_r1[1]);
+		R2 = (s32)le32_to_cpu(priv->card_alive_init.therm_r2[1]);
+		R3 = (s32)le32_to_cpu(priv->card_alive_init.therm_r3[1]);
+	} else {
+		R1 = (s32)le32_to_cpu(priv->card_alive_init.therm_r1[0]);
+		R2 = (s32)le32_to_cpu(priv->card_alive_init.therm_r2[0]);
+		R3 = (s32)le32_to_cpu(priv->card_alive_init.therm_r3[0]);
+	}
+
+	temp_th = CELSIUS_TO_KELVIN(TM_CT_KILL_THRESHOLD);
+
+	cmd.critical_temperature_R = ((temp_th * (R3-R1))/CT_LIMIT_CONST) + R2;
+	rc = iwl_send_cmd_pdu(priv,
+			      REPLY_CT_KILL_CONFIG_CMD, sizeof(cmd), &cmd);
+	if (rc)
+		IWL_ERROR("REPLY_CT_KILL_CONFIG_CMD failed\n");
+	else
+		IWL_WARNING("REPLY_CT_KILL_CONFIG_CMD succeeded\n");
+
+	return;
+}
+
+#ifdef CONFIG_IWLWIFI_SENSITIVITY
+
+/* "false alarms" are signals that our DSP tries to lock onto,
+ *   but then determines that they are either noise, or transmissions
+ *   from a distant wireless network (also "noise", really) that get
+ *   "stepped on" by stronger transmissions within our own network.
+ * This algorithm attempts to set a sensitivity level that is high
+ *   enough to receive all of our own network traffic, but not so
+ *   high that our DSP gets too busy trying to lock onto non-network
+ *   activity/noise. */
+static int iwl4965_sens_energy_cck(struct iwl_priv *priv,
+				   u32 norm_fa,
+				   u32 rx_enable_time,
+				   struct statistics_general_data *rx_info)
+{
+	u32 max_nrg_cck = 0;
+	int i = 0;
+	u8 max_silence_rssi = 0;
+	u32 silence_ref = 0;
+	u8 silence_rssi_a = 0;
+	u8 silence_rssi_b = 0;
+	u8 silence_rssi_c = 0;
+	u32 val;
+
+	/* "false_alarms" values below are cross-multiplications to assess the
+	 *   numbers of false alarms within the measured period of actual Rx
+	 *   (Rx is off when we're txing), vs the min/max expected false alarms
+	 *   (some should be expected if rx is sensitive enough) in a
+	 *   hypothetical listening period of 200 time units (TU), 204.8 msec:
+	 *
+	 * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time
+	 *
+	 * */
+	u32 false_alarms = norm_fa * 200 * 1024;
+	u32 max_false_alarms = MAX_FA_CCK * rx_enable_time;
+	u32 min_false_alarms = MIN_FA_CCK * rx_enable_time;
+	struct iwl_sensitivity_data *data = NULL;
+
+	data = &(priv->sensitivity_data);
+
+	data->nrg_auto_corr_silence_diff = 0;
+
+	/* Find max silence rssi among all 3 receivers.
+	 * This is background noise, which may include transmissions from other
+	 *    networks, measured during silence before our network's beacon */
+	silence_rssi_a = (u8)((rx_info->beacon_silence_rssi_a &
+			    ALL_BAND_FILTER)>>8);
+	silence_rssi_b = (u8)((rx_info->beacon_silence_rssi_b &
+			    ALL_BAND_FILTER)>>8);
+	silence_rssi_c = (u8)((rx_info->beacon_silence_rssi_c &
+			    ALL_BAND_FILTER)>>8);
+
+	val = max(silence_rssi_b, silence_rssi_c);
+	max_silence_rssi = max(silence_rssi_a, (u8) val);
+
+	/* Store silence rssi in 20-beacon history table */
+	data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi;
+	data->nrg_silence_idx++;
+	if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L)
+		data->nrg_silence_idx = 0;
+
+	/* Find max silence rssi across 20 beacon history */
+	for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) {
+		val = data->nrg_silence_rssi[i];
+		silence_ref = max(silence_ref, val);
+	}
+	IWL_DEBUG_CALIB("silence a %u, b %u, c %u, 20-bcn max %u\n",
+			silence_rssi_a, silence_rssi_b, silence_rssi_c,
+			silence_ref);
+
+	/* Find max rx energy (min value!) among all 3 receivers,
+	 *   measured during beacon frame.
+	 * Save it in 10-beacon history table. */
+	i = data->nrg_energy_idx;
+	val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c);
+	data->nrg_value[i] = min(rx_info->beacon_energy_a, val);
+
+	data->nrg_energy_idx++;
+	if (data->nrg_energy_idx >= 10)
+		data->nrg_energy_idx = 0;
+
+	/* Find min rx energy (max value) across 10 beacon history.
+	 * This is the minimum signal level that we want to receive well.
+	 * Add backoff (margin so we don't miss slightly lower energy frames).
+	 * This establishes an upper bound (min value) for energy threshold. */
+	max_nrg_cck = data->nrg_value[0];
+	for (i = 1; i < 10; i++)
+		max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i]));
+	max_nrg_cck += 6;
+
+	IWL_DEBUG_CALIB("rx energy a %u, b %u, c %u, 10-bcn max/min %u\n",
+			rx_info->beacon_energy_a, rx_info->beacon_energy_b,
+			rx_info->beacon_energy_c, max_nrg_cck - 6);
+
+	/* Count number of consecutive beacons with fewer-than-desired
+	 *   false alarms. */
+	if (false_alarms < min_false_alarms)
+		data->num_in_cck_no_fa++;
+	else
+		data->num_in_cck_no_fa = 0;
+	IWL_DEBUG_CALIB("consecutive bcns with few false alarms = %u\n",
+			data->num_in_cck_no_fa);
+
+	/* If we got too many false alarms this time, reduce sensitivity */
+	if (false_alarms > max_false_alarms) {
+		IWL_DEBUG_CALIB("norm FA %u > max FA %u\n",
+			     false_alarms, max_false_alarms);
+		IWL_DEBUG_CALIB("... reducing sensitivity\n");
+		data->nrg_curr_state = IWL_FA_TOO_MANY;
+
+		if (data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK) {
+			/* Store for "fewer than desired" on later beacon */
+			data->nrg_silence_ref = silence_ref;
+
+			/* increase energy threshold (reduce nrg value)
+			 *   to decrease sensitivity */
+			if (data->nrg_th_cck > (NRG_MAX_CCK + NRG_STEP_CCK))
+				data->nrg_th_cck = data->nrg_th_cck
+							 - NRG_STEP_CCK;
+		}
+
+		/* increase auto_corr values to decrease sensitivity */
+		if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK)
+			data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1;
+		else {
+			val = data->auto_corr_cck + AUTO_CORR_STEP_CCK;
+			data->auto_corr_cck = min((u32)AUTO_CORR_MAX_CCK, val);
+		}
+		val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK;
+		data->auto_corr_cck_mrc = min((u32)AUTO_CORR_MAX_CCK_MRC, val);
+
+	/* Else if we got fewer than desired, increase sensitivity */
+	} else if (false_alarms < min_false_alarms) {
+		data->nrg_curr_state = IWL_FA_TOO_FEW;
+
+		/* Compare silence level with silence level for most recent
+		 *   healthy number or too many false alarms */
+		data->nrg_auto_corr_silence_diff = (s32)data->nrg_silence_ref -
+						   (s32)silence_ref;
+
+		IWL_DEBUG_CALIB("norm FA %u < min FA %u, silence diff %d\n",
+			 false_alarms, min_false_alarms,
+			 data->nrg_auto_corr_silence_diff);
+
+		/* Increase value to increase sensitivity, but only if:
+		 * 1a) previous beacon did *not* have *too many* false alarms
+		 * 1b) AND there's a significant difference in Rx levels
+		 *      from a previous beacon with too many, or healthy # FAs
+		 * OR 2) We've seen a lot of beacons (100) with too few
+		 *       false alarms */
+		if ((data->nrg_prev_state != IWL_FA_TOO_MANY) &&
+			((data->nrg_auto_corr_silence_diff > NRG_DIFF) ||
+			(data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) {
+
+			IWL_DEBUG_CALIB("... increasing sensitivity\n");
+			/* Increase nrg value to increase sensitivity */
+			val = data->nrg_th_cck + NRG_STEP_CCK;
+			data->nrg_th_cck = min((u32)NRG_MIN_CCK, val);
+
+			/* Decrease auto_corr values to increase sensitivity */
+			val = data->auto_corr_cck - AUTO_CORR_STEP_CCK;
+			data->auto_corr_cck = max((u32)AUTO_CORR_MIN_CCK, val);
+
+			val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK;
+			data->auto_corr_cck_mrc =
+					 max((u32)AUTO_CORR_MIN_CCK_MRC, val);
+
+		} else {
+			IWL_DEBUG_CALIB("... but not changing sensitivity\n");
+		}
+
+
+	/* Else we got a healthy number of false alarms, keep status quo */
+	} else {
+		IWL_DEBUG_CALIB(" FA in safe zone\n");
+		data->nrg_curr_state = IWL_FA_GOOD_RANGE;
+
+		/* Store for use in "fewer than desired" with later beacon */
+		data->nrg_silence_ref = silence_ref;
+
+		/* If previous beacon had too many false alarms,
+		 *   give it some extra margin by reducing sensitivity again
+		 *   (but don't go below measured energy of desired Rx) */
+		if (IWL_FA_TOO_MANY == data->nrg_prev_state) {
+			IWL_DEBUG_CALIB("... increasing margin\n");
+			data->nrg_th_cck -= NRG_MARGIN;
+		}
+	}
+
+	/* Make sure the energy threshold does not go above the measured
+	 *   energy of the desired Rx signals (reduced by backoff margin),
+	 *   or else we might start missing Rx frames.
+	 * Lower value is higher energy, so we use max()! */
+	data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck);
+	IWL_DEBUG_CALIB("new nrg_th_cck %u\n", data->nrg_th_cck);
+
+	data->nrg_prev_state = data->nrg_curr_state;
+
+	return 0;
+}
+
+
+static int iwl4965_sens_auto_corr_ofdm(struct iwl_priv *priv,
+				       u32 norm_fa,
+				       u32 rx_enable_time)
+{
+	u32 val;
+	u32 false_alarms = norm_fa * 200 * 1024;
+	u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time;
+	u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time;
+	struct iwl_sensitivity_data *data = NULL;
+
+	data = &(priv->sensitivity_data);
+
+	/* If we got too many false alarms this time, reduce sensitivity */
+	if (false_alarms > max_false_alarms) {
+
+		IWL_DEBUG_CALIB("norm FA %u > max FA %u)\n",
+			     false_alarms, max_false_alarms);
+
+		val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM;
+		data->auto_corr_ofdm =
+				min((u32)AUTO_CORR_MAX_OFDM, val);
+
+		val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM;
+		data->auto_corr_ofdm_mrc =
+				min((u32)AUTO_CORR_MAX_OFDM_MRC, val);
+
+		val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM;
+		data->auto_corr_ofdm_x1 =
+				min((u32)AUTO_CORR_MAX_OFDM_X1, val);
+
+		val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM;
+		data->auto_corr_ofdm_mrc_x1 =
+				min((u32)AUTO_CORR_MAX_OFDM_MRC_X1, val);
+	}
+
+	/* Else if we got fewer than desired, increase sensitivity */
+	else if (false_alarms < min_false_alarms) {
+
+		IWL_DEBUG_CALIB("norm FA %u < min FA %u\n",
+			     false_alarms, min_false_alarms);
+
+		val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM;
+		data->auto_corr_ofdm =
+				max((u32)AUTO_CORR_MIN_OFDM, val);
+
+		val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM;
+		data->auto_corr_ofdm_mrc =
+				max((u32)AUTO_CORR_MIN_OFDM_MRC, val);
+
+		val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM;
+		data->auto_corr_ofdm_x1 =
+				max((u32)AUTO_CORR_MIN_OFDM_X1, val);
+
+		val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM;
+		data->auto_corr_ofdm_mrc_x1 =
+				max((u32)AUTO_CORR_MIN_OFDM_MRC_X1, val);
+	}
+
+	else {
+		IWL_DEBUG_CALIB("min FA %u < norm FA %u < max FA %u OK\n",
+			 min_false_alarms, false_alarms, max_false_alarms);
+	}
+
+	return 0;
+}
+
+static int iwl_sensitivity_callback(struct iwl_priv *priv,
+				    struct iwl_cmd *cmd, struct sk_buff *skb)
+{
+	/* We didn't cache the SKB; let the caller free it */
+	return 1;
+}
+
+/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */
+static int iwl4965_sensitivity_write(struct iwl_priv *priv, u8 flags)
+{
+	int rc = 0;
+	struct iwl_sensitivity_cmd cmd ;
+	struct iwl_sensitivity_data *data = NULL;
+	struct iwl_host_cmd cmd_out = {
+		.id = SENSITIVITY_CMD,
+		.len = sizeof(struct iwl_sensitivity_cmd),
+		.meta.flags = flags,
+		.data = &cmd,
+	};
+
+	data = &(priv->sensitivity_data);
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.table[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] =
+				cpu_to_le16((u16)data->auto_corr_ofdm);
+	cmd.table[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] =
+				cpu_to_le16((u16)data->auto_corr_ofdm_mrc);
+	cmd.table[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] =
+				cpu_to_le16((u16)data->auto_corr_ofdm_x1);
+	cmd.table[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] =
+				cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1);
+
+	cmd.table[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] =
+				cpu_to_le16((u16)data->auto_corr_cck);
+	cmd.table[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] =
+				cpu_to_le16((u16)data->auto_corr_cck_mrc);
+
+	cmd.table[HD_MIN_ENERGY_CCK_DET_INDEX] =
+				cpu_to_le16((u16)data->nrg_th_cck);
+	cmd.table[HD_MIN_ENERGY_OFDM_DET_INDEX] =
+				cpu_to_le16((u16)data->nrg_th_ofdm);
+
+	cmd.table[HD_BARKER_CORR_TH_ADD_MIN_INDEX] =
+				__constant_cpu_to_le16(190);
+	cmd.table[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] =
+				__constant_cpu_to_le16(390);
+	cmd.table[HD_OFDM_ENERGY_TH_IN_INDEX] =
+				__constant_cpu_to_le16(62);
+
+	IWL_DEBUG_CALIB("ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n",
+			data->auto_corr_ofdm, data->auto_corr_ofdm_mrc,
+			data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1,
+			data->nrg_th_ofdm);
+
+	IWL_DEBUG_CALIB("cck: ac %u mrc %u thresh %u\n",
+			data->auto_corr_cck, data->auto_corr_cck_mrc,
+			data->nrg_th_cck);
+
+	cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE;
+
+	if (flags & CMD_ASYNC)
+		cmd_out.meta.u.callback = iwl_sensitivity_callback;
+
+	/* Don't send command to uCode if nothing has changed */
+	if (!memcmp(&cmd.table[0], &(priv->sensitivity_tbl[0]),
+		    sizeof(u16)*HD_TABLE_SIZE)) {
+		IWL_DEBUG_CALIB("No change in SENSITIVITY_CMD\n");
+		return 0;
+	}
+
+	/* Copy table for comparison next time */
+	memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]),
+	       sizeof(u16)*HD_TABLE_SIZE);
+
+	rc = iwl_send_cmd(priv, &cmd_out);
+	if (!rc) {
+		IWL_DEBUG_CALIB("SENSITIVITY_CMD succeeded\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+void iwl4965_init_sensitivity(struct iwl_priv *priv, u8 flags, u8 force)
+{
+	int rc = 0;
+	int i;
+	struct iwl_sensitivity_data *data = NULL;
+
+	IWL_DEBUG_CALIB("Start iwl4965_init_sensitivity\n");
+
+	if (force)
+		memset(&(priv->sensitivity_tbl[0]), 0,
+			sizeof(u16)*HD_TABLE_SIZE);
+
+	/* Clear driver's sensitivity algo data */
+	data = &(priv->sensitivity_data);
+	memset(data, 0, sizeof(struct iwl_sensitivity_data));
+
+	data->num_in_cck_no_fa = 0;
+	data->nrg_curr_state = IWL_FA_TOO_MANY;
+	data->nrg_prev_state = IWL_FA_TOO_MANY;
+	data->nrg_silence_ref = 0;
+	data->nrg_silence_idx = 0;
+	data->nrg_energy_idx = 0;
+
+	for (i = 0; i < 10; i++)
+		data->nrg_value[i] = 0;
+
+	for (i = 0; i < NRG_NUM_PREV_STAT_L; i++)
+		data->nrg_silence_rssi[i] = 0;
+
+	data->auto_corr_ofdm = 90;
+	data->auto_corr_ofdm_mrc = 170;
+	data->auto_corr_ofdm_x1  = 105;
+	data->auto_corr_ofdm_mrc_x1 = 220;
+	data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF;
+	data->auto_corr_cck_mrc = 200;
+	data->nrg_th_cck = 100;
+	data->nrg_th_ofdm = 100;
+
+	data->last_bad_plcp_cnt_ofdm = 0;
+	data->last_fa_cnt_ofdm = 0;
+	data->last_bad_plcp_cnt_cck = 0;
+	data->last_fa_cnt_cck = 0;
+
+	/* Clear prior Sensitivity command data to force send to uCode */
+	if (force)
+		memset(&(priv->sensitivity_tbl[0]), 0,
+		    sizeof(u16)*HD_TABLE_SIZE);
+
+	rc |= iwl4965_sensitivity_write(priv, flags);
+	IWL_DEBUG_CALIB("<<return 0x%X\n", rc);
+
+	return;
+}
+
+
+/* Reset differential Rx gains in NIC to prepare for chain noise calibration.
+ * Called after every association, but this runs only once!
+ *  ... once chain noise is calibrated the first time, it's good forever.  */
+void iwl4965_chain_noise_reset(struct iwl_priv *priv)
+{
+	struct iwl_chain_noise_data *data = NULL;
+	int rc = 0;
+
+	data = &(priv->chain_noise_data);
+	if ((data->state == IWL_CHAIN_NOISE_ALIVE) && iwl_is_associated(priv)) {
+		struct iwl_calibration_cmd cmd;
+
+		memset(&cmd, 0, sizeof(cmd));
+		cmd.opCode = PHY_CALIBRATE_DIFF_GAIN_CMD;
+		cmd.diff_gain_a = 0;
+		cmd.diff_gain_b = 0;
+		cmd.diff_gain_c = 0;
+		rc = iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
+				 sizeof(cmd), &cmd);
+		msleep(4);
+		data->state = IWL_CHAIN_NOISE_ACCUMULATE;
+		IWL_DEBUG_CALIB("Run chain_noise_calibrate\n");
+	}
+	return;
+}
+
+/*
+ * Accumulate 20 beacons of signal and noise statistics for each of
+ *   3 receivers/antennas/rx-chains, then figure out:
+ * 1)  Which antennas are connected.
+ * 2)  Differential rx gain settings to balance the 3 receivers.
+ */
+static void iwl4965_noise_calibration(struct iwl_priv *priv,
+				      struct iwl_notif_statistics *stat_resp)
+{
+	struct iwl_chain_noise_data *data = NULL;
+	int rc = 0;
+
+	u32 chain_noise_a;
+	u32 chain_noise_b;
+	u32 chain_noise_c;
+	u32 chain_sig_a;
+	u32 chain_sig_b;
+	u32 chain_sig_c;
+	u32 average_sig[NUM_RX_CHAINS] = {INITIALIZATION_VALUE};
+	u32 average_noise[NUM_RX_CHAINS] = {INITIALIZATION_VALUE};
+	u32 max_average_sig;
+	u16 max_average_sig_antenna_i;
+	u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE;
+	u16 min_average_noise_antenna_i = INITIALIZATION_VALUE;
+	u16 i = 0;
+	u16 chan_num = INITIALIZATION_VALUE;
+	u32 band = INITIALIZATION_VALUE;
+	u32 active_chains = 0;
+	unsigned long flags;
+	struct statistics_rx_non_phy *rx_info = &(stat_resp->rx.general);
+
+	data = &(priv->chain_noise_data);
+
+	/* Accumulate just the first 20 beacons after the first association,
+	 *   then we're done forever. */
+	if (data->state != IWL_CHAIN_NOISE_ACCUMULATE ) {
+		if (data->state == IWL_CHAIN_NOISE_ALIVE)
+			IWL_DEBUG_CALIB("Wait for noise calib reset\n");
+		return;
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
+		IWL_DEBUG_CALIB(" << Interference data unavailable\n");
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return;
+	}
+
+	band = (priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK) ? 0 : 1;
+	chan_num = le16_to_cpu(priv->staging_rxon.channel);
+
+	/* Make sure we accumulate data for just the associated channel
+	 *   (even if scanning). */
+	if ((chan_num != (le32_to_cpu(stat_resp->flag) >> 16)) ||
+	   ((STATISTICS_REPLY_FLG_BAND_24G_MSK ==
+	     (stat_resp->flag & STATISTICS_REPLY_FLG_BAND_24G_MSK)) &&
+	    band)) {
+		IWL_DEBUG_CALIB("Stats not from chan=%d, band=%d\n",
+				 chan_num, band);
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return;
+	}
+
+	/* Accumulate beacon statistics values across 20 beacons */
+	chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) &
+				IN_BAND_FILTER;
+	chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) &
+				IN_BAND_FILTER;
+	chain_noise_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) &
+				IN_BAND_FILTER;
+
+	chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER;
+	chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER;
+	chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER;
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	data->beacon_count++;
+
+	data->chain_noise_a = (chain_noise_a + data->chain_noise_a);
+	data->chain_noise_b = (chain_noise_b + data->chain_noise_b);
+	data->chain_noise_c = (chain_noise_c + data->chain_noise_c);
+
+	data->chain_signal_a = (chain_sig_a + data->chain_signal_a);
+	data->chain_signal_b = (chain_sig_b + data->chain_signal_b);
+	data->chain_signal_c = (chain_sig_c + data->chain_signal_c);
+
+	IWL_DEBUG_CALIB("chan=%d, band=%d, beacon=%d\n", chan_num, band,
+			data->beacon_count);
+	IWL_DEBUG_CALIB("chain_sig: a %d b %d c %d\n",
+			chain_sig_a, chain_sig_b, chain_sig_c);
+	IWL_DEBUG_CALIB("chain_noise: a %d b %d c %d\n",
+			chain_noise_a, chain_noise_b, chain_noise_c);
+
+	/* If this is the 20th beacon, determine:
+	 * 1)  Disconnected antennas (using signal strengths)
+	 * 2)  Differential gain (using silence noise) to balance receivers */
+	if (data->beacon_count == CAL_NUM_OF_BEACONS) {
+
+		/* Analyze signal for disconnected antenna */
+		average_sig[0] = (data->chain_signal_a) /
+					CAL_NUM_OF_BEACONS;
+		average_sig[1] = (data->chain_signal_b) /
+					CAL_NUM_OF_BEACONS;
+		average_sig[2] = (data->chain_signal_c) /
+					CAL_NUM_OF_BEACONS;
+
+		if (average_sig[0] >= average_sig[1]) {
+			max_average_sig = average_sig[0];
+			max_average_sig_antenna_i = 0;
+			active_chains = (1 << max_average_sig_antenna_i);
+		} else {
+			max_average_sig = average_sig[1];
+			max_average_sig_antenna_i = 1;
+			active_chains = (1 << max_average_sig_antenna_i);
+		}
+
+		if (average_sig[2] >= max_average_sig) {
+			max_average_sig = average_sig[2];
+			max_average_sig_antenna_i = 2;
+			active_chains = (1 << max_average_sig_antenna_i);
+		}
+
+		IWL_DEBUG_CALIB("average_sig: a %d b %d c %d\n",
+			     average_sig[0], average_sig[1], average_sig[2]);
+		IWL_DEBUG_CALIB("max_average_sig = %d, antenna %d\n",
+			     max_average_sig, max_average_sig_antenna_i);
+
+		/* Compare signal strengths for all 3 receivers. */
+		for (i = 0; i < NUM_RX_CHAINS; i++) {
+			if (i != max_average_sig_antenna_i) {
+				s32 rssi_delta = (max_average_sig -
+						 average_sig[i]);
+
+				/* If signal is very weak, compared with
+				 * strongest, mark it as disconnected. */
+				if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS)
+					data->disconn_array[i] = 1;
+				else
+					active_chains |= (1 << i);
+			IWL_DEBUG_CALIB("i = %d  rssiDelta = %d  "
+				     "disconn_array[i] = %d\n",
+				     i, rssi_delta,
+				     data->disconn_array[i]);
+			}
+		}
+
+		/*If both chains A & B are disconnected -
+		 * connect B and leave A as is */
+		if (data->disconn_array[CHAIN_A] &&
+		   data->disconn_array[CHAIN_B]) {
+			data->disconn_array[CHAIN_B] = 0;
+			active_chains |= (1 << CHAIN_B);
+			IWL_DEBUG_CALIB("both A & B chains are disconnected! "
+				     "W/A - declare B as connected\n");
+		}
+
+		IWL_DEBUG_CALIB("active_chains (bitwise) = 0x%x\n",
+				active_chains);
+
+		/* Save for use within RXON, TX, SCAN commands, etc. */
+		priv->valid_antenna = active_chains;
+
+		/* Analyze noise for rx balance */
+		average_noise[0] = ((data->chain_noise_a)/CAL_NUM_OF_BEACONS);
+		average_noise[1] = ((data->chain_noise_b)/CAL_NUM_OF_BEACONS);
+		average_noise[2] = ((data->chain_noise_c)/CAL_NUM_OF_BEACONS);
+
+		for (i = 0; i < NUM_RX_CHAINS; i++) {
+			if (!(data->disconn_array[i]) &&
+			   (average_noise[i] <= min_average_noise)) {
+				/* This means that chain i is active and has
+				 * lower noise values so far: */
+				min_average_noise = average_noise[i];
+				min_average_noise_antenna_i = i;
+			}
+		}
+
+		data->delta_gain_code[min_average_noise_antenna_i] = 0;
+
+		IWL_DEBUG_CALIB("average_noise: a %d b %d c %d\n",
+				average_noise[0], average_noise[1],
+				average_noise[2]);
+
+		IWL_DEBUG_CALIB("min_average_noise = %d, antenna %d\n",
+				min_average_noise, min_average_noise_antenna_i);
+
+		for (i = 0; i < NUM_RX_CHAINS; i++) {
+			s32 delta_g = 0;
+
+			if (!(data->disconn_array[i]) &&
+			    (data->delta_gain_code[i] ==
+			     CHAIN_NOISE_DELTA_GAIN_INIT_VAL)) {
+				delta_g = average_noise[i] - min_average_noise;
+				data->delta_gain_code[i] = (u8)((delta_g *
+								    10) / 15);
+				if (CHAIN_NOISE_MAX_DELTA_GAIN_CODE <
+				   data->delta_gain_code[i])
+					data->delta_gain_code[i] =
+					  CHAIN_NOISE_MAX_DELTA_GAIN_CODE;
+
+				data->delta_gain_code[i] =
+					(data->delta_gain_code[i] | (1 << 2));
+			} else {
+				data->delta_gain_code[i] = 0;
+			}
+		}
+		IWL_DEBUG_CALIB("delta_gain_codes: a %d b %d c %d\n",
+			     data->delta_gain_code[0],
+			     data->delta_gain_code[1],
+			     data->delta_gain_code[2]);
+
+		/* Differential gain gets sent to uCode only once */
+		if (!data->radio_write) {
+			struct iwl_calibration_cmd cmd;
+			data->radio_write = 1;
+
+			memset(&cmd, 0, sizeof(cmd));
+			cmd.opCode = PHY_CALIBRATE_DIFF_GAIN_CMD;
+			cmd.diff_gain_a = data->delta_gain_code[0];
+			cmd.diff_gain_b = data->delta_gain_code[1];
+			cmd.diff_gain_c = data->delta_gain_code[2];
+			rc = iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
+					      sizeof(cmd), &cmd);
+			if (rc)
+				IWL_DEBUG_CALIB("fail sending cmd "
+					     "REPLY_PHY_CALIBRATION_CMD \n");
+
+			/* TODO we might want recalculate
+			 * rx_chain in rxon cmd */
+
+			/* Mark so we run this algo only once! */
+			data->state = IWL_CHAIN_NOISE_CALIBRATED;
+		}
+		data->chain_noise_a = 0;
+		data->chain_noise_b = 0;
+		data->chain_noise_c = 0;
+		data->chain_signal_a = 0;
+		data->chain_signal_b = 0;
+		data->chain_signal_c = 0;
+		data->beacon_count = 0;
+	}
+	return;
+}
+
+static void iwl4965_sensitivity_calibration(struct iwl_priv *priv,
+					    struct iwl_notif_statistics *resp)
+{
+	int rc = 0;
+	u32 rx_enable_time;
+	u32 fa_cck;
+	u32 fa_ofdm;
+	u32 bad_plcp_cck;
+	u32 bad_plcp_ofdm;
+	u32 norm_fa_ofdm;
+	u32 norm_fa_cck;
+	struct iwl_sensitivity_data *data = NULL;
+	struct statistics_rx_non_phy *rx_info = &(resp->rx.general);
+	struct statistics_rx *statistics = &(resp->rx);
+	unsigned long flags;
+	struct statistics_general_data statis;
+
+	data = &(priv->sensitivity_data);
+
+	if (!iwl_is_associated(priv)) {
+		IWL_DEBUG_CALIB("<< - not associated\n");
+		return;
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
+		IWL_DEBUG_CALIB("<< invalid data.\n");
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return;
+	}
+
+	/* Extract Statistics: */
+	rx_enable_time = le32_to_cpu(rx_info->channel_load);
+	fa_cck = le32_to_cpu(statistics->cck.false_alarm_cnt);
+	fa_ofdm = le32_to_cpu(statistics->ofdm.false_alarm_cnt);
+	bad_plcp_cck = le32_to_cpu(statistics->cck.plcp_err);
+	bad_plcp_ofdm = le32_to_cpu(statistics->ofdm.plcp_err);
+
+	statis.beacon_silence_rssi_a =
+			le32_to_cpu(statistics->general.beacon_silence_rssi_a);
+	statis.beacon_silence_rssi_b =
+			le32_to_cpu(statistics->general.beacon_silence_rssi_b);
+	statis.beacon_silence_rssi_c =
+			le32_to_cpu(statistics->general.beacon_silence_rssi_c);
+	statis.beacon_energy_a =
+			le32_to_cpu(statistics->general.beacon_energy_a);
+	statis.beacon_energy_b =
+			le32_to_cpu(statistics->general.beacon_energy_b);
+	statis.beacon_energy_c =
+			le32_to_cpu(statistics->general.beacon_energy_c);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	IWL_DEBUG_CALIB("rx_enable_time = %u usecs\n", rx_enable_time);
+
+	if (!rx_enable_time) {
+		IWL_DEBUG_CALIB("<< RX Enable Time == 0! \n");
+		return;
+	}
+
+	/* These statistics increase monotonically, and do not reset
+	 *   at each beacon.  Calculate difference from last value, or just
+	 *   use the new statistics value if it has reset or wrapped around. */
+	if (data->last_bad_plcp_cnt_cck > bad_plcp_cck)
+		data->last_bad_plcp_cnt_cck = bad_plcp_cck;
+	else {
+		bad_plcp_cck -= data->last_bad_plcp_cnt_cck;
+		data->last_bad_plcp_cnt_cck += bad_plcp_cck;
+	}
+
+	if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm)
+		data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm;
+	else {
+		bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm;
+		data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm;
+	}
+
+	if (data->last_fa_cnt_ofdm > fa_ofdm)
+		data->last_fa_cnt_ofdm = fa_ofdm;
+	else{
+		fa_ofdm -= data->last_fa_cnt_ofdm;
+		data->last_fa_cnt_ofdm += fa_ofdm;
+	}
+
+	if (data->last_fa_cnt_cck > fa_cck)
+		data->last_fa_cnt_cck = fa_cck;
+	else {
+		fa_cck -= data->last_fa_cnt_cck;
+		data->last_fa_cnt_cck += fa_cck;
+	}
+
+	/* Total aborted signal locks */
+	norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm;
+	norm_fa_cck = fa_cck + bad_plcp_cck;
+
+	IWL_DEBUG_CALIB("cck: fa %u badp %u  ofdm: fa %u badp %u\n", fa_cck,
+			bad_plcp_cck, fa_ofdm, bad_plcp_ofdm);
+
+	iwl4965_sens_auto_corr_ofdm(priv, norm_fa_ofdm, rx_enable_time);
+
+	iwl4965_sens_energy_cck(priv, norm_fa_cck,
+					rx_enable_time, &statis);
+
+	rc |= iwl4965_sensitivity_write(priv, CMD_ASYNC);
+	return;
+}
+
+static void iwl4965_bg_sensitivity_work(struct work_struct *work)
+{
+	struct iwl_priv *priv = container_of(work, struct iwl_priv,
+							sensitivity_work);
+
+	mutex_lock(&priv->mutex);
+
+	if ((priv->status & STATUS_EXIT_PENDING) ||
+	    (priv->status & STATUS_SCANNING)) {
+		mutex_unlock(&priv->mutex);
+		return;
+	}
+
+	if (priv->start_calib) {
+		iwl4965_noise_calibration(priv, &priv->statistics);
+
+		if (priv->sensitivity_data.state ==
+					IWL_SENS_CALIB_NEED_REINIT) {
+			iwl4965_init_sensitivity(priv, CMD_ASYNC, 0);
+			priv->sensitivity_data.state = IWL_SENS_CALIB_ALLOWED;
+		} else {
+			iwl4965_sensitivity_calibration(priv,
+							&priv->statistics);
+		}
+	}
+
+	mutex_unlock(&priv->mutex);
+	return;
+}
+#endif /*CONFIG_IWLWIFI_SENSITIVITY*/
+
+static void iwl4965_bg_txpower_work(struct work_struct *work)
+{
+	struct iwl_priv *priv = container_of(work, struct iwl_priv,
+					     txpower_work);
+
+	mutex_lock(&priv->mutex);
+
+	/* If a scan happened to start before we got here
+	 * then just return; the statistics notification will
+	 * kick off another scheduled work to compensate for
+	 * any temperature delta we missed here. */
+	if ((priv->status & STATUS_EXIT_PENDING) ||
+	    priv->status & STATUS_SCANNING) {
+		mutex_unlock(&priv->mutex);
+		return;
+	}
+
+	/* Regardless of if we are assocaited, we must reconfigure the
+	 * TX power since frames can be sent on non-radar channels while
+	 * not associated */
+	iwl_hw_reg_send_txpower(priv);
+
+	/* Update last_temperature to keep is_calib_needed from running
+	 * when it isn't needed... */
+	priv->last_temperature = priv->temperature;
+
+	mutex_unlock(&priv->mutex);
+}
+
+/*
+ * Acquire priv->lock before calling this function !
+ */
+static void iwl4965_set_wr_ptrs(struct iwl_priv *priv, int txq_id, u32 index)
+{
+	iwl_write_restricted(priv, HBUS_TARG_WRPTR,
+			     (index & 0xff) | (txq_id << 8));
+	iwl_write_restricted_reg(priv, SCD_QUEUE_RDPTR(txq_id), index);
+}
+
+/*
+ * Acquire priv->lock before calling this function !
+ */
+static void iwl4965_tx_queue_set_status(struct iwl_priv *priv, struct iwl_tx_queue *txq,
+			    int tx_fifo_id, int scd_retry)
+{
+	int txq_id = txq->q.id;
+	int active = test_bit(txq_id, &priv->txq_ctx_active_msk)?1:0;
+
+	iwl_write_restricted_reg(priv,
+				 SCD_QUEUE_STATUS_BITS(txq_id),
+				 (active << SCD_QUEUE_STTS_REG_POS_ACTIVE)|
+				 (tx_fifo_id << SCD_QUEUE_STTS_REG_POS_TXF)|
+				 (scd_retry << SCD_QUEUE_STTS_REG_POS_WSL)|
+				 (scd_retry << SCD_QUEUE_STTS_REG_POS_SCD_ACK)|
+				 SCD_QUEUE_STTS_REG_MSK);
+
+	txq->sched_retry = scd_retry;
+
+	IWL_DEBUG_INFO("%s %s Queue %d on AC %d\n",
+		       (active ? "Activete" : "Deactivate"),
+		       scd_retry ? "BA" : "AC", txq_id, tx_fifo_id);
+}
+
+static const u16 default_ac_to_tx_fifo[] = {
+	IWL_TX_QUEUE_AC1, IWL_TX_QUEUE_AC0,
+	IWL_TX_QUEUE_AC2, IWL_TX_QUEUE_AC3,
+	IWL_TX_QUEUE_HCCA_1, IWL_TX_QUEUE_HCCA_2
+};
+
+
+static inline void iwl4965_txq_ctx_activate(struct iwl_priv *priv, int txq_id)
+{
+	set_bit(txq_id, &priv->txq_ctx_active_msk);
+}
+
+static inline void iwl4965_txq_ctx_deactivate(struct iwl_priv *priv, int txq_id)
+{
+	clear_bit(txq_id, &priv->txq_ctx_active_msk);
+}
+
+int iwl4965_alive_notify(struct iwl_priv *priv)
+{
+	u32 a;
+	int i = 0;
+	unsigned long flags;
+	int rc;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+#ifdef CONFIG_IWLWIFI_SENSITIVITY
+	memset(&(priv->sensitivity_data), 0,
+	       sizeof(struct iwl_sensitivity_data));
+	memset(&(priv->chain_noise_data), 0,
+	       sizeof(struct iwl_chain_noise_data));
+	for (i = 0; i < NUM_RX_CHAINS; i++)
+		priv->chain_noise_data.delta_gain_code[i] =
+				CHAIN_NOISE_DELTA_GAIN_INIT_VAL;
+#endif /* CONFIG_IWLWIFI_SENSITIVITY*/
+	rc = iwl_grab_restricted_access(priv);
+	if (rc) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return rc;
+	}
+
+	priv->scd_base_addr = iwl_read_restricted_reg(priv, SCD_SRAM_BASE_ADDR);
+	a = priv->scd_base_addr + SCD_CONTEXT_DATA_OFFSET;
+	for (; a < priv->scd_base_addr + SCD_TX_STTS_BITMAP_OFFSET; a += 4)
+		iwl_write_restricted_mem(priv, a, 0);
+	for (; a < priv->scd_base_addr + SCD_TRANSLATE_TBL_OFFSET; a += 4)
+		iwl_write_restricted_mem(priv, a, 0);
+	for (;
+	     a < sizeof(u16) * IWL4965_NUM_QUEUES;
+	     a += 4)
+		iwl_write_restricted_mem(priv, a, 0);
+
+	iwl_write_restricted_reg(priv, SCD_DRAM_BASE_ADDR,
+				 (priv->hw_setting.shared_phys +
+				  offsetof(struct iwl_shared,
+					   queues_byte_cnt_tbls))
+				 >> 10);
+	iwl_write_restricted_reg(priv, SCD_QUEUECHAIN_SEL, 0);
+
+	/* initiate the queues */
+	for (i = 0; i < IWL4965_NUM_QUEUES; i++) {
+		iwl_write_restricted_reg(priv, SCD_QUEUE_RDPTR(i), 0);
+		iwl_write_restricted(priv, HBUS_TARG_WRPTR, 0 | (i << 8));
+		iwl_write_restricted_mem(priv, priv->scd_base_addr +
+					SCD_CONTEXT_QUEUE_OFFSET(i),
+					(SCD_WIN_SIZE <<
+					SCD_QUEUE_CTX_REG1_WIN_SIZE_POS) &
+					SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK);
+		iwl_write_restricted_mem(priv, priv->scd_base_addr +
+					SCD_CONTEXT_QUEUE_OFFSET(i) +
+					sizeof(u32),
+					(SCD_FRAME_LIMIT <<
+					SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
+					SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK);
+
+	}
+	iwl_write_restricted_reg(priv, SCD_INTERRUPT_MASK,
+				 (1 << IWL4965_NUM_QUEUES) - 1);
+
+	iwl_write_restricted_reg(priv, SCD_TXFACT,
+				 SCD_TXFACT_REG_TXFIFO_MASK(0, 7));
+
+	iwl4965_set_wr_ptrs(priv, IWL_CMD_QUEUE_NUM, 0);
+	iwl4965_txq_ctx_activate(priv,IWL_CMD_QUEUE_NUM);
+	iwl4965_tx_queue_set_status(priv, &priv->txq[IWL_CMD_QUEUE_NUM],
+				    IWL_CMD_FIFO_NUM, 0);
+	/* map qos queues to fifos one-to-one */
+	for (i = 0; i < ARRAY_SIZE(default_ac_to_tx_fifo); i++) {
+		int ac = default_ac_to_tx_fifo[i];
+		iwl4965_txq_ctx_activate(priv, ac);
+		iwl4965_tx_queue_set_status(priv, &priv->txq[ac], ac, 0);
+	}
+
+	iwl_release_restricted_access(priv);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+int iwl_hw_set_hw_setting(struct iwl_priv *priv)
+{
+	priv->hw_setting.shared_virt =
+	    pci_alloc_consistent(priv->pci_dev,
+				 sizeof(struct iwl_shared),
+				 &priv->hw_setting.shared_phys);
+
+	if (!priv->hw_setting.shared_virt)
+		return -1;
+
+	memset(priv->hw_setting.shared_virt, 0, sizeof(struct iwl_shared));
+
+	priv->hw_setting.max_queue_number = IWL4965_NUM_QUEUES;
+	priv->hw_setting.ac_queue_count = AC_NUM;
+
+	priv->hw_setting.cck_flag = RATE_MCS_CCK_MSK;
+	priv->hw_setting.tx_cmd_len = sizeof(struct iwl_tx_cmd);
+	priv->hw_setting.max_rxq_size = RX_QUEUE_SIZE;
+	priv->hw_setting.max_rxq_log = RX_QUEUE_SIZE_LOG;
+	return 0;
+}
+
+/**
+ * iwl_hw_txq_ctx_free - Free TXQ Context
+ *
+ * Destroy all TX DMA queues and structures
+ */
+void iwl_hw_txq_ctx_free(struct iwl_priv *priv)
+{
+	int txq_id;
+
+	/* Tx queues */
+	for (txq_id = 0; txq_id < priv->hw_setting.max_queue_number; txq_id++)
+		iwl_tx_queue_free(priv, &priv->txq[txq_id]);
+
+	iwl4965_kw_free(priv);
+}
+
+/**
+ * iwl_hw_tx_queue_free_tfd -  Free one TFD, those at index [txq->q.last_used]
+ *
+ * Does NOT advance any indexes
+ */
+int iwl_hw_tx_queue_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq)
+{
+	struct iwl_tfd_frame *bd_tmp = (struct iwl_tfd_frame *)&txq->bd[0];
+	struct iwl_tfd_frame *bd = &bd_tmp[txq->q.last_used];
+	struct pci_dev *dev = priv->pci_dev;
+	int i;
+	int counter = 0;
+	int index, is_odd;
+
+	/* classify bd */
+	if (txq->q.id == IWL_CMD_QUEUE_NUM)
+		/* nothing to cleanup after for host commands */
+		return 0;
+
+	/* sanity check */
+	counter = IWL_GET_BITS(*bd, num_tbs);
+	if (counter > MAX_NUM_OF_TBS) {
+		IWL_ERROR("Too many chunks: %i\n", counter);
+		/* @todo issue fatal error, it is quite serious situation */
+		return 0;
+	}
+
+	/* unmap chunks if any */
+
+	for (i = 0; i < counter; i++) {
+		index = i / 2;
+		is_odd = i & 0x1;
+
+		if (is_odd)
+			pci_unmap_single(
+				dev,
+				IWL_GET_BITS(bd->pa[index], tb2_addr_lo16) |
+				(IWL_GET_BITS(bd->pa[index],
+					      tb2_addr_hi20) << 16),
+				IWL_GET_BITS(bd->pa[index], tb2_len),
+				PCI_DMA_TODEVICE);
+
+		else if (i > 0)
+			pci_unmap_single(dev,
+					 le32_to_cpu(bd->pa[index].tb1_addr),
+					 IWL_GET_BITS(bd->pa[index], tb1_len),
+					 PCI_DMA_TODEVICE);
+
+		if (txq->txb[txq->q.last_used].skb[i]) {
+			struct sk_buff *skb = txq->txb[txq->q.last_used].skb[i];
+
+			dev_kfree_skb(skb);
+			txq->txb[txq->q.last_used].skb[i] = NULL;
+		}
+	}
+	return 0;
+}
+
+int iwl_hw_reg_set_txpower(struct iwl_priv *priv, s8 power)
+{
+	IWL_ERROR("TODO: Implement iwl_hw_reg_set_txpower!\n");
+	return -EINVAL;
+}
+
+#define TX_POWER_IWL_ILLEGAL_VDET    -100000
+#define TX_POWER_IWL_ILLEGAL_VOLTAGE -10000
+#define TX_POWER_IWL_CLOSED_LOOP_MIN_POWER 18
+#define TX_POWER_IWL_CLOSED_LOOP_MAX_POWER 34
+#define TX_POWER_IWL_VDET_SLOPE_BELOW_NOMINAL 17
+#define TX_POWER_IWL_VDET_SLOPE_ABOVE_NOMINAL 20
+#define TX_POWER_IWL_NOMINAL_POWER            26
+#define TX_POWER_IWL_CLOSED_LOOP_ITERATION_LIMIT 1
+#define TX_POWER_IWL_VOLTAGE_CODES_PER_03V       7
+#define TX_POWER_IWL_DEGREES_PER_VDET_CODE       11
+#define IWL_TX_POWER_MAX_NUM_PA_MEASUREMENTS 1
+#define IWL_TX_POWER_CCK_COMPENSATION_B_STEP (9)
+#define IWL_TX_POWER_CCK_COMPENSATION_C_STEP (5)
+
+static s32 iwl4965_math_div_round(s32 num, s32 denom, s32 * res)
+{
+	s32 sign = 1;
+
+	if (num < 0) {
+		sign = -sign;
+		num = -num;
+	}
+	if (denom < 0) {
+		sign = -sign;
+		denom = -denom;
+	}
+	*res = 1;
+	*res = ((num * 2 + denom) / (denom * 2)) * sign;
+
+	return 1;
+}
+
+static s32 iwl4965_get_voltage_compensation(s32 eeprom_voltage,
+					    s32 current_voltage)
+{
+	s32 comp = 0;
+
+	if ((TX_POWER_IWL_ILLEGAL_VOLTAGE == eeprom_voltage) ||
+	    (TX_POWER_IWL_ILLEGAL_VOLTAGE == current_voltage))
+		return 0;
+
+	iwl4965_math_div_round(current_voltage - eeprom_voltage,
+			       TX_POWER_IWL_VOLTAGE_CODES_PER_03V, &comp);
+
+	if (current_voltage > eeprom_voltage)
+		comp *= 2;
+	if ((comp < -2) || (comp > 2))
+		comp = 0;
+
+	return comp;
+}
+
+static const struct iwl_channel_info *
+iwl4965_get_channel_txpower_info(struct iwl_priv *priv, u8 phymode, u8 channel)
+{
+	const struct iwl_channel_info *ch_info;
+
+	ch_info = iwl_get_channel_info(priv, phymode, channel);
+
+	if (!is_channel_valid(ch_info))
+		return NULL;
+
+	return ch_info;
+}
+
+static s32 iwl4965_get_txatten_group_from_channel(u32 channel)
+{
+	if (channel >= CALIB_IWL_TX_ATTEN_GR5_FCH &&
+	    channel <= CALIB_IWL_TX_ATTEN_GR5_LCH)
+		return CALIB_CH_GROUP_5;
+
+	if (channel >= CALIB_IWL_TX_ATTEN_GR1_FCH &&
+	    channel <= CALIB_IWL_TX_ATTEN_GR1_LCH)
+		return CALIB_CH_GROUP_1;
+
+	if (channel >= CALIB_IWL_TX_ATTEN_GR2_FCH &&
+	    channel <= CALIB_IWL_TX_ATTEN_GR2_LCH)
+		return CALIB_CH_GROUP_2;
+
+	if (channel >= CALIB_IWL_TX_ATTEN_GR3_FCH &&
+	    channel <= CALIB_IWL_TX_ATTEN_GR3_LCH)
+		return CALIB_CH_GROUP_3;
+
+	if (channel >= CALIB_IWL_TX_ATTEN_GR4_FCH &&
+	    channel <= CALIB_IWL_TX_ATTEN_GR4_LCH)
+		return CALIB_CH_GROUP_4;
+
+	IWL_ERROR("Can't find txatten group for channel %d.\n", channel);
+	return -1;
+}
+
+static u32 iwl4965_get_sub_band(const struct iwl_priv *priv, u32 channel)
+{
+	s32 sub_band = -1;
+
+	for (sub_band = 0; sub_band < EEPROM_TX_POWER_BANDS; sub_band++) {
+
+		if (priv->eeprom.calib_info.band_info_tbl[sub_band].ch_from ==
+		    0)
+			continue;
+
+		if ((channel >=
+		     priv->eeprom.calib_info.band_info_tbl[sub_band].ch_from)
+		    && (channel <=
+			priv->eeprom.calib_info.band_info_tbl[sub_band].ch_to))
+			break;
+	}
+
+	return sub_band;
+}
+
+static s32 iwl4965_interpolate_value(s32 x, s32 x1, s32 y1, s32 x2, s32 y2)
+{
+	s32 val;
+
+	if (x2 == x1)
+		return y1;
+	else {
+		iwl4965_math_div_round((x2 - x) * (y1 - y2), (x2 - x1), &val);
+		return val + y2;
+	}
+}
+
+static int iwl4965_interpolate_chan(
+	struct iwl_priv *priv, u32 channel,
+	struct iwl_eeprom_calib_channel_info *chan_info)
+{
+	s32 s = -1;
+	u32 c;
+	u32 m;
+	const struct iwl_eeprom_calib_measurement *m1;
+	const struct iwl_eeprom_calib_measurement *m2;
+	struct iwl_eeprom_calib_measurement *omeas;
+	u32 ch_i1;
+	u32 ch_i2;
+
+	s = iwl4965_get_sub_band(priv, channel);
+	if (s >= EEPROM_TX_POWER_BANDS) {
+		IWL_ERROR("Tx Power can not find channel %d ", channel);
+		return -1;
+	}
+
+	ch_i1 = priv->eeprom.calib_info.band_info_tbl[s].ch1.ch_num;
+	ch_i2 = priv->eeprom.calib_info.band_info_tbl[s].ch2.ch_num;
+	chan_info->ch_num = (u8) channel;
+
+	IWL_DEBUG_TXPOWER("channel %d subband %d factory cal ch %d & %d\n",
+			  channel, s, ch_i1, ch_i2);
+
+	for (c = 0; c < EEPROM_TX_POWER_TX_CHAINS; c++) {
+		for (m = 0; m < EEPROM_TX_POWER_MEASUREMENTS; m++) {
+			m1 = &(priv->eeprom.calib_info.band_info_tbl[s].ch1.
+			       measurements[c][m]);
+			m2 = &(priv->eeprom.calib_info.band_info_tbl[s].ch2.
+			       measurements[c][m]);
+			omeas = &(chan_info->measurements[c][m]);
+
+			omeas->actual_pow =
+			    (u8) iwl4965_interpolate_value(channel, ch_i1,
+							   m1->actual_pow,
+							   ch_i2,
+							   m2->actual_pow);
+			omeas->gain_idx =
+			    (u8) iwl4965_interpolate_value(channel, ch_i1,
+							   m1->gain_idx, ch_i2,
+							   m2->gain_idx);
+			omeas->temperature =
+			    (u8) iwl4965_interpolate_value(channel, ch_i1,
+							   m1->temperature,
+							   ch_i2,
+							   m2->temperature);
+			omeas->pa_det =
+			    (s8) iwl4965_interpolate_value(channel, ch_i1,
+							   m1->pa_det, ch_i2,
+							   m2->pa_det);
+
+			IWL_DEBUG_TXPOWER
+			    ("chain %d meas %d AP1=%d AP2=%d AP=%d\n", c, m,
+			     m1->actual_pow, m2->actual_pow, omeas->actual_pow);
+			IWL_DEBUG_TXPOWER
+			    ("chain %d meas %d NI1=%d NI2=%d NI=%d\n", c, m,
+			     m1->gain_idx, m2->gain_idx, omeas->gain_idx);
+			IWL_DEBUG_TXPOWER
+			    ("chain %d meas %d PA1=%d PA2=%d PA=%d\n", c, m,
+			     m1->pa_det, m2->pa_det, omeas->pa_det);
+			IWL_DEBUG_TXPOWER
+			    ("chain %d meas %d  T1=%d  T2=%d  T=%d\n", c, m,
+			     m1->temperature, m2->temperature,
+			     omeas->temperature);
+		}
+	}
+
+	return 0;
+}
+
+/* bit-rate-dependent table to prevent Tx distortion, in half-dB units,
+ * for OFDM 6, 12, 18, 24, 36, 48, 54, 60 MBit, and CCK all rates. */
+static s32 back_off_table[] = {
+	10, 10, 10, 10, 10, 15, 17, 20,	/* OFDM SISO 20 MHz */
+	10, 10, 10, 10, 10, 15, 17, 20,	/* OFDM MIMO 20 MHz */
+	10, 10, 10, 10, 10, 15, 17, 20,	/* OFDM SISO 40 MHz */
+	10, 10, 10, 10, 10, 15, 17, 20,	/* OFDM MIMO 40 MHz */
+	10			/* CCK */
+};
+
+/* Thermal compensation values for txpower for various frequency ranges ...
+ *   ratios from 3:1 to 4.5:1 of degrees (Celsius) per half-dB gain adjust */
+static struct iwl_txpower_comp_entry {
+	s32 degrees_per_05db_a;
+	s32 degrees_per_05db_a_denom;
+} tx_power_cmp_tble[CALIB_CH_GROUP_MAX] = {
+	{9, 2},			/* group 0 5.2, ch  34-43 */
+	{4, 1},			/* group 1 5.2, ch  44-70 */
+	{4, 1},			/* group 2 5.2, ch  71-124 */
+	{4, 1},			/* group 3 5.2, ch 125-200 */
+	{3, 1}			/* group 4 2.4, ch   all */
+};
+
+static s32 get_min_power_index(s32 rate_power_index, u32 band)
+{
+	if (!band) {
+		if ((rate_power_index % 8) <= 4)
+			return MIN_TX_GAIN_INDEX_52GHZ_EXT;
+	}
+	return MIN_TX_GAIN_INDEX;
+}
+
+struct gain_entry {
+	u8 dsp;
+	u8 radio;
+};
+
+static const struct gain_entry gain_table[2][108] = {
+	/* 5.2GHz power gain index table */
+	{
+	 {123, 0x3F},		/* highest txpower */
+	 {117, 0x3F},
+	 {110, 0x3F},
+	 {104, 0x3F},
+	 {98, 0x3F},
+	 {110, 0x3E},
+	 {104, 0x3E},
+	 {98, 0x3E},
+	 {110, 0x3D},
+	 {104, 0x3D},
+	 {98, 0x3D},
+	 {110, 0x3C},
+	 {104, 0x3C},
+	 {98, 0x3C},
+	 {110, 0x3B},
+	 {104, 0x3B},
+	 {98, 0x3B},
+	 {110, 0x3A},
+	 {104, 0x3A},
+	 {98, 0x3A},
+	 {110, 0x39},
+	 {104, 0x39},
+	 {98, 0x39},
+	 {110, 0x38},
+	 {104, 0x38},
+	 {98, 0x38},
+	 {110, 0x37},
+	 {104, 0x37},
+	 {98, 0x37},
+	 {110, 0x36},
+	 {104, 0x36},
+	 {98, 0x36},
+	 {110, 0x35},
+	 {104, 0x35},
+	 {98, 0x35},
+	 {110, 0x34},
+	 {104, 0x34},
+	 {98, 0x34},
+	 {110, 0x33},
+	 {104, 0x33},
+	 {98, 0x33},
+	 {110, 0x32},
+	 {104, 0x32},
+	 {98, 0x32},
+	 {110, 0x31},
+	 {104, 0x31},
+	 {98, 0x31},
+	 {110, 0x30},
+	 {104, 0x30},
+	 {98, 0x30},
+	 {110, 0x25},
+	 {104, 0x25},
+	 {98, 0x25},
+	 {110, 0x24},
+	 {104, 0x24},
+	 {98, 0x24},
+	 {110, 0x23},
+	 {104, 0x23},
+	 {98, 0x23},
+	 {110, 0x22},
+	 {104, 0x18},
+	 {98, 0x18},
+	 {110, 0x17},
+	 {104, 0x17},
+	 {98, 0x17},
+	 {110, 0x16},
+	 {104, 0x16},
+	 {98, 0x16},
+	 {110, 0x15},
+	 {104, 0x15},
+	 {98, 0x15},
+	 {110, 0x14},
+	 {104, 0x14},
+	 {98, 0x14},
+	 {110, 0x13},
+	 {104, 0x13},
+	 {98, 0x13},
+	 {110, 0x12},
+	 {104, 0x08},
+	 {98, 0x08},
+	 {110, 0x07},
+	 {104, 0x07},
+	 {98, 0x07},
+	 {110, 0x06},
+	 {104, 0x06},
+	 {98, 0x06},
+	 {110, 0x05},
+	 {104, 0x05},
+	 {98, 0x05},
+	 {110, 0x04},
+	 {104, 0x04},
+	 {98, 0x04},
+	 {110, 0x03},
+	 {104, 0x03},
+	 {98, 0x03},
+	 {110, 0x02},
+	 {104, 0x02},
+	 {98, 0x02},
+	 {110, 0x01},
+	 {104, 0x01},
+	 {98, 0x01},
+	 {110, 0x00},
+	 {104, 0x00},
+	 {98, 0x00},
+	 {93, 0x00},
+	 {88, 0x00},
+	 {83, 0x00},
+	 {78, 0x00},
+	 },
+	/* 2.4GHz power gain index table */
+	{
+	 {110, 0x3f},		/* highest txpower */
+	 {104, 0x3f},
+	 {98, 0x3f},
+	 {110, 0x3e},
+	 {104, 0x3e},
+	 {98, 0x3e},
+	 {110, 0x3d},
+	 {104, 0x3d},
+	 {98, 0x3d},
+	 {110, 0x3c},
+	 {104, 0x3c},
+	 {98, 0x3c},
+	 {110, 0x3b},
+	 {104, 0x3b},
+	 {98, 0x3b},
+	 {110, 0x3a},
+	 {104, 0x3a},
+	 {98, 0x3a},
+	 {110, 0x39},
+	 {104, 0x39},
+	 {98, 0x39},
+	 {110, 0x38},
+	 {104, 0x38},
+	 {98, 0x38},
+	 {110, 0x37},
+	 {104, 0x37},
+	 {98, 0x37},
+	 {110, 0x36},
+	 {104, 0x36},
+	 {98, 0x36},
+	 {110, 0x35},
+	 {104, 0x35},
+	 {98, 0x35},
+	 {110, 0x34},
+	 {104, 0x34},
+	 {98, 0x34},
+	 {110, 0x33},
+	 {104, 0x33},
+	 {98, 0x33},
+	 {110, 0x32},
+	 {104, 0x32},
+	 {98, 0x32},
+	 {110, 0x31},
+	 {104, 0x31},
+	 {98, 0x31},
+	 {110, 0x30},
+	 {104, 0x30},
+	 {98, 0x30},
+	 {110, 0x6},
+	 {104, 0x6},
+	 {98, 0x6},
+	 {110, 0x5},
+	 {104, 0x5},
+	 {98, 0x5},
+	 {110, 0x4},
+	 {104, 0x4},
+	 {98, 0x4},
+	 {110, 0x3},
+	 {104, 0x3},
+	 {98, 0x3},
+	 {110, 0x2},
+	 {104, 0x2},
+	 {98, 0x2},
+	 {110, 0x1},
+	 {104, 0x1},
+	 {98, 0x1},
+	 {110, 0x0},
+	 {104, 0x0},
+	 {98, 0x0},
+	 {97, 0},
+	 {96, 0},
+	 {95, 0},
+	 {94, 0},
+	 {93, 0},
+	 {92, 0},
+	 {91, 0},
+	 {90, 0},
+	 {89, 0},
+	 {88, 0},
+	 {87, 0},
+	 {86, 0},
+	 {85, 0},
+	 {84, 0},
+	 {83, 0},
+	 {82, 0},
+	 {81, 0},
+	 {80, 0},
+	 {79, 0},
+	 {78, 0},
+	 {77, 0},
+	 {76, 0},
+	 {75, 0},
+	 {74, 0},
+	 {73, 0},
+	 {72, 0},
+	 {71, 0},
+	 {70, 0},
+	 {69, 0},
+	 {68, 0},
+	 {67, 0},
+	 {66, 0},
+	 {65, 0},
+	 {64, 0},
+	 {63, 0},
+	 {62, 0},
+	 {61, 0},
+	 {60, 0},
+	 {59, 0},
+	 }
+};
+
+static int iwl4965_fill_txpower_tbl(struct iwl_priv *priv, u8 band, u8 channel,
+				    u8 is_fat, u8 ctrl_chan_high,
+				    struct iwl_tx_power_db *tx_power_tbl)
+{
+	u16 radio_gain;
+	u16 dsp_atten;
+	u8 saturation_power;
+	s32 target_power;
+	s32 user_target_power;
+	s32 power_limit;
+	s32 current_temp;
+	s32 reg_limit;
+	s32 current_regulatory;
+	s32 txatten_group = CALIB_CH_GROUP_MAX;
+	int i = 0;
+	int c;
+	const struct iwl_channel_info *ch_info = NULL;
+	struct iwl_eeprom_calib_channel_info ch_eeprom_info;
+	const struct iwl_eeprom_calib_measurement *measurement;
+	s16 voltage;
+	s32 init_voltage;
+	s32 voltage_compensation;
+	s32 degrees_per_05db_num;
+	s32 degrees_per_05db_denom;
+	s32 factory_temp;
+	s32 temperature_comp[2];
+	s32 factory_gain_index[2];
+	s32 factory_actual_pwr[2];
+	s32 power_index;
+
+	/* Sanity check requested level (dBm) */
+	if (priv->user_txpower_limit < IWL_TX_POWER_TARGET_POWER_MIN) {
+		IWL_WARNING("Requested user TXPOWER %d below limit.\n",
+			    priv->user_txpower_limit);
+		return -EINVAL;
+	}
+	if (priv->user_txpower_limit > IWL_TX_POWER_TARGET_POWER_MAX) {
+		IWL_WARNING("Requested user TXPOWER %d above limit.\n",
+			    priv->user_txpower_limit);
+		return -EINVAL;
+	}
+
+	/* user_txpower_limit is in dBm, convert to half-dBm (half-dB units
+	 *   are used for indexing into txpower table) */
+	user_target_power = 2 * priv->user_txpower_limit;
+
+	/* Get current (RXON) channel, band, width */
+	ch_info =
+		iwl4965_get_channel_txpower_info(priv, priv->phymode, channel);
+
+	IWL_DEBUG_TXPOWER("chan %d band %d is_fat %d\n", channel, band,
+			  is_fat);
+
+	if (!ch_info)
+		return -EINVAL;
+
+	/* get txatten group, used to select 1) thermal txpower adjustment
+	 *   and 2) mimo txpower balance between Tx chains. */
+	txatten_group = iwl4965_get_txatten_group_from_channel(channel);
+	if (txatten_group < 0)
+		return -EINVAL;
+
+	IWL_DEBUG_TXPOWER("channel %d belongs to txatten group %d\n",
+			  channel, txatten_group);
+
+	if (is_fat) {
+		if (ctrl_chan_high)
+			channel -= 2;
+		else
+			channel += 2;
+	}
+
+	/* hardware txpower limits ...
+	 * saturation (clipping distortion) txpowers are in half-dBm */
+	if (band)
+		saturation_power = priv->eeprom.calib_info.saturation_power24;
+	else
+		saturation_power = priv->eeprom.calib_info.saturation_power52;
+
+	if (saturation_power < IWL_TX_POWER_SATURATION_MIN ||
+	    saturation_power > IWL_TX_POWER_SATURATION_MAX) {
+		if (band)
+			saturation_power = IWL_TX_POWER_DEFAULT_SATURATION_24;
+		else
+			saturation_power = IWL_TX_POWER_DEFAULT_SATURATION_52;
+	}
+
+	/* regulatory txpower limits ... reg_limit values are in half-dBm,
+	 *   max_power_avg values are in dBm, convert * 2 */
+	if (is_fat)
+		reg_limit = ch_info->fat_max_power_avg * 2;
+	else
+		reg_limit = ch_info->max_power_avg * 2;
+
+	if ((reg_limit < IWL_TX_POWER_REGULATORY_MIN) ||
+	    (reg_limit > IWL_TX_POWER_REGULATORY_MAX)) {
+		if (band)
+			reg_limit = IWL_TX_POWER_DEFAULT_REGULATORY_24;
+		else
+			reg_limit = IWL_TX_POWER_DEFAULT_REGULATORY_52;
+	}
+
+	/* Interpolate txpower calibration values for this channel,
+	 *   based on factory calibration tests on spaced channels. */
+	iwl4965_interpolate_chan(priv, channel, &ch_eeprom_info);
+
+	/* calculate tx gain adjustment based on power supply voltage */
+	voltage = (s16)le16_to_cpu(priv->eeprom.calib_info.voltage);
+	init_voltage = (s32)le32_to_cpu(priv->card_alive_init.voltage);
+	voltage_compensation =
+	    iwl4965_get_voltage_compensation(voltage, init_voltage);
+
+	IWL_DEBUG_TXPOWER("curr volt %d eeprom volt %d volt comp %d\n",
+			  priv->card_alive_init.voltage,
+			  voltage, voltage_compensation);
+
+	/* get current temperature (Celsius) */
+	current_temp = max(priv->temperature, IWL_TX_POWER_TEMPERATURE_MIN);
+	current_temp = min(priv->temperature, IWL_TX_POWER_TEMPERATURE_MAX);
+	current_temp = KELVIN_TO_CELSIUS(current_temp);
+
+	if (-40 > current_temp) {
+		IWL_WARNING("Invalid temperature %d, can't calculate "
+			    "txpower\n", current_temp);
+		return -EINVAL;
+	}
+
+	/* select thermal txpower adjustment params, based on channel group
+	 *   (same frequency group used for mimo txatten adjustment) */
+	degrees_per_05db_num =
+	    tx_power_cmp_tble[txatten_group].degrees_per_05db_a;
+	degrees_per_05db_denom =
+	    tx_power_cmp_tble[txatten_group].degrees_per_05db_a_denom;
+
+	/* get per-chain txpower values from factory measurements */
+	for (c = 0; c < 2; c++) {
+		measurement = &ch_eeprom_info.measurements[c][1];
+
+		/* txgain adjustment (in half-dB steps) based on difference
+		 *   between factory and current temperature */
+		factory_temp = measurement->temperature;
+		iwl4965_math_div_round((current_temp - factory_temp) *
+				       degrees_per_05db_denom,
+				       degrees_per_05db_num,
+				       &temperature_comp[c]);
+
+		factory_gain_index[c] = measurement->gain_idx;
+		factory_actual_pwr[c] = measurement->actual_pow;
+
+		IWL_DEBUG_TXPOWER("chain = %d\n", c);
+		IWL_DEBUG_TXPOWER("fctry tmp %d, "
+				  "curr tmp %d, comp %d steps\n",
+				  factory_temp, current_temp,
+				  temperature_comp[c]);
+
+		IWL_DEBUG_TXPOWER("fctry idx %d, fctry pwr %d\n",
+				  factory_gain_index[c],
+				  factory_actual_pwr[c]);
+	}
+
+	/* for each of 33 bit-rates (including 1 for CCK) */
+	for (i = 0; i <= POWER_TABLE_NUM_HT_OFDM_ENTRIES; i++) {
+		u8 is_mimo_rate;
+		union tx_power_dual_stream_u *tx_power;
+
+		/* for mimo, reduce each chain's txpower by half
+		 * (3dB, 6 steps), so total output power is regulatory
+		 * compliant. */
+		if (i & 0x8) {
+			current_regulatory = reg_limit -
+			    IWL_TX_POWER_MIMO_REGULATORY_COMPENSATION;
+			is_mimo_rate = 1;
+		} else {
+			current_regulatory = reg_limit;
+			is_mimo_rate = 0;
+		}
+
+		/* find txpower limit, either hardware or regulatory */
+		power_limit = saturation_power - back_off_table[i];
+		if (power_limit > current_regulatory)
+			power_limit = current_regulatory;
+
+		/* reduce user's txpower request if necessary
+		 * for this rate on this channel */
+		target_power = user_target_power;
+		if (target_power > power_limit)
+			target_power = power_limit;
+
+		IWL_DEBUG_TXPOWER("rate %d sat %d reg %d usr %d tgt %d\n",
+				  i, saturation_power - back_off_table[i],
+				  current_regulatory, user_target_power,
+				  target_power);
+
+		/* for each of 2 Tx chains (radio transmitters) */
+		for (c = 0; c < 2; c++) {
+			s32 atten_value;
+
+			if (is_mimo_rate)
+				atten_value =
+				    priv->card_alive_init.
+				    tx_atten[txatten_group][c];
+			else
+				atten_value = 0;
+
+			/* calculate index; higher index means lower txpower */
+			power_index = (u8) (factory_gain_index[c] -
+					    (target_power -
+					     factory_actual_pwr[c]) -
+					    temperature_comp[c] -
+					    voltage_compensation +
+					    atten_value);
+
+/*                      IWL_DEBUG_TXPOWER("calculated txpower index %d\n", */
+/*                               power_index); */
+
+			if (power_index < get_min_power_index(i, band))
+				power_index = get_min_power_index(i, band);
+
+			/* adjust 5 GHz index to support negative indexes */
+			if (!band)
+				power_index += 9;
+
+			/* CCK, rate 32, reduce txpower for CCK */
+			if (POWER_TABLE_NUM_HT_OFDM_ENTRIES == i) {
+				power_index +=
+				    IWL_TX_POWER_CCK_COMPENSATION_C_STEP;
+				tx_power = &(tx_power_tbl->legacy_cck_power);
+			} else
+				/* OFDM, rates 0-31 */
+				tx_power = &(tx_power_tbl->ht_ofdm_power[i]);
+
+			/* stay within the table! */
+			if (power_index > 107) {
+				IWL_WARNING("txpower index %d > 107\n",
+					    power_index);
+				power_index = 107;
+			}
+			if (power_index < 0) {
+				IWL_WARNING("txpower index %d < 0\n",
+					    power_index);
+				power_index = 0;
+			}
+
+			/* fill txpower command for this rate/chain */
+			radio_gain = gain_table[band][power_index].radio;
+			dsp_atten = gain_table[band][power_index].dsp;
+
+			if (c == 0) {
+				tx_power->s.ramon_tx_gain = radio_gain;
+				tx_power->s.dsp_predis_atten = dsp_atten;
+			} else {
+				tx_power->s.ramon_tx_gain |= (radio_gain << 8);
+				tx_power->s.dsp_predis_atten |=
+				    (dsp_atten << 8);
+			}
+
+			IWL_DEBUG_TXPOWER("chain %d mimo %d index %d "
+					  "gain 0x%02x dsp %d\n",
+					  c, atten_value, power_index,
+					  radio_gain, dsp_atten);
+
+		}/* for each chain */
+	}/* for each rate */
+
+	return 0;
+}
+
+/**
+ * iwl_hw_reg_send_txpower - Configure the TXPOWER level user limit
+ *
+ * Uses the active RXON for channel, band, and characteristics (fat, high)
+ * The power limit is taken from priv->user_txpower_limit.
+ */
+int iwl_hw_reg_send_txpower(struct iwl_priv *priv)
+{
+	struct iwl_tx_power_table_cmd cmd = { 0 };
+	int rc = 0;
+	u8 band = 0;
+	u8 channel = 0;
+	u8 is_fat = 0;
+	u8 ctrl_chan_high = 0;
+
+	if (priv->status & STATUS_SCANNING) {
+		/* If this gets hit a lot, switch it to a BUG() and catch
+		 * the stack trace to find out who is calling this during
+		 * a scan. */
+		IWL_WARNING("TX Power requested while scanning!\n");
+		return -EAGAIN;
+	}
+
+	band = ((priv->phymode == MODE_IEEE80211B) ||
+		(priv->phymode == MODE_IEEE80211G) ||
+		(priv->phymode == MODE_ATHEROS_TURBOG)) ? 1 : 0;
+
+	channel = priv->active_rxon.channel;
+
+	is_fat =  is_fat_channel(priv->active_rxon.flags);
+
+	if (is_fat &&
+	    (priv->active_rxon.flags & RXON_FLG_CONTROL_CHANNEL_LOC_HIGH_MSK))
+		ctrl_chan_high = 1;
+
+	cmd.band = band;
+	cmd.channel = channel;
+	cmd.channel_normal_width = 0;
+
+	rc = iwl4965_fill_txpower_tbl(priv, band, channel, is_fat,
+				      ctrl_chan_high, &cmd.tx_power);
+	if (rc)
+		return rc;
+
+	rc = iwl_send_cmd_pdu(priv, REPLY_TX_PWR_TABLE_CMD, sizeof(cmd), &cmd);
+	return rc;
+}
+
+int iwl_hw_channel_switch(struct iwl_priv *priv, u8 channel)
+{
+	int rc;
+	u8 band = 0;
+	u8 is_fat = 0;
+	u8 ctrl_chan_high = 0;
+	struct iwl_channel_switch_cmd cmd = { 0 };
+	const struct iwl_channel_info *ch_info;
+
+	band = ((priv->phymode == MODE_IEEE80211B) ||
+		(priv->phymode == MODE_IEEE80211G) ||
+		(priv->phymode == MODE_ATHEROS_TURBOG)) ? 1 : 0;
+
+	ch_info = iwl_get_channel_info(priv, priv->phymode, channel);
+
+	is_fat = is_fat_channel(priv->staging_rxon.flags);
+
+	if (is_fat &&
+	    (priv->active_rxon.flags & RXON_FLG_CONTROL_CHANNEL_LOC_HIGH_MSK))
+		ctrl_chan_high = 1;
+
+	cmd.band = band;
+	cmd.expect_beacon = 0;
+	cmd.channel = channel;
+	cmd.rxon_flags = priv->active_rxon.flags;
+	cmd.rxon_filter_flags = priv->active_rxon.filter_flags;
+	cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time);
+	if (ch_info)
+		cmd.expect_beacon = is_channel_radar(ch_info);
+	else
+		cmd.expect_beacon = 1;
+
+	rc = iwl4965_fill_txpower_tbl(priv, band, channel, is_fat,
+				      ctrl_chan_high, &cmd.tx_power);
+	if (rc) {
+		IWL_DEBUG_11H("error:%d  fill txpower_tbl\n", rc);
+		return rc;
+	}
+
+	rc = iwl_send_cmd_pdu(priv, REPLY_CHANNEL_SWITCH, sizeof(cmd), &cmd);
+	return rc;
+}
+
+#define RTS_HCCA_RETRY_LIMIT		3
+#define RTS_DFAULT_RETRY_LIMIT		60
+
+void iwl_hw_build_tx_cmd_rate(struct iwl_priv *priv,
+			      struct iwl_cmd *cmd,
+			      struct ieee80211_tx_control *ctrl,
+			      struct ieee80211_hdr *hdr, int sta_id,
+			      int is_hcca)
+{
+	int rate = ctrl->tx_rate;
+	u8 rts_retry_limit = 0;
+	u8 data_retry_limit = 0;
+	u32 tx_flags;
+
+	tx_flags = cmd->cmd.tx.tx_flags;
+
+	rate = iwl_rates[ctrl->tx_rate].plcp;
+
+	rts_retry_limit = (is_hcca) ?
+	    RTS_HCCA_RETRY_LIMIT : RTS_DFAULT_RETRY_LIMIT;
+
+	if (ieee80211_is_probe_response(hdr->frame_control)) {
+		data_retry_limit = 3;
+		if (data_retry_limit < rts_retry_limit)
+			rts_retry_limit = data_retry_limit;
+	} else
+		data_retry_limit = IWL_DEFAULT_TX_RETRY;
+
+	if (priv->data_retry_limit != -1)
+		data_retry_limit = priv->data_retry_limit;
+
+	if (WLAN_FC_GET_TYPE(hdr->frame_control) == IEEE80211_FTYPE_MGMT) {
+		switch (WLAN_FC_GET_STYPE(hdr->frame_control)) {
+		case IEEE80211_STYPE_AUTH:
+		case IEEE80211_STYPE_DEAUTH:
+		case IEEE80211_STYPE_ASSOC_REQ:
+		case IEEE80211_STYPE_REASSOC_REQ:
+			if (tx_flags & TX_CMD_FLG_RTS_MSK) {
+				tx_flags &= ~TX_CMD_FLG_RTS_MSK;
+				tx_flags |= TX_CMD_FLG_CTS_MSK;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	cmd->cmd.tx.rts_retry_limit = rts_retry_limit;
+	cmd->cmd.tx.data_retry_limit = data_retry_limit;
+	cmd->cmd.tx.rate.s.rate = rate;
+	cmd->cmd.tx.tx_flags = tx_flags;
+}
+
+int iwl_hw_get_rx_read(struct iwl_priv *priv)
+{
+	struct iwl_shared *shared_data =
+	    (struct iwl_shared *)priv->hw_setting.shared_virt;
+
+	return IWL_GET_BITS(*shared_data, rb_closed_stts_rb_num);
+}
+
+int iwl_hw_get_temperature(struct iwl_priv *priv)
+{
+	return priv->temperature;
+}
+
+int iwl_hw_get_beacon_cmd(struct iwl_priv *priv,
+			  struct iwl_frame *frame, u16 rate)
+{
+	struct iwl_tx_beacon_cmd *tx_beacon_cmd;
+	int frame_size;
+
+	if ((rate == IWL_RATE_1M_PLCP) || (rate >= IWL_RATE_2M_PLCP))
+		rate |= RATE_MCS_CCK_MSK;
+
+	tx_beacon_cmd = &frame->u.beacon;
+	memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd));
+
+	tx_beacon_cmd->tx.sta_id = IWL_BROADCAST_ID;
+	tx_beacon_cmd->tx.stop_time.life_time = 0xFFFFFFFF;
+
+	frame_size = iwl_fill_beacon_frame(priv,
+				tx_beacon_cmd->frame,
+				BROADCAST_ADDR,
+				sizeof(frame->u) - sizeof(*tx_beacon_cmd));
+
+	tx_beacon_cmd->tx.len = frame_size;
+
+	tx_beacon_cmd->tx.rate.rate_n_flags = rate;
+	tx_beacon_cmd->tx.tx_flags = (TX_CMD_FLG_SEQ_CTL_MSK |
+				TX_CMD_FLG_TSF_MSK | TX_CMD_FLG_STA_RATE_MSK);
+	return (sizeof(*tx_beacon_cmd) + frame_size);
+}
+
+int iwl_hw_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq)
+{
+	int rc;
+	unsigned long flags;
+	int txq_id = txq->q.id;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	rc = iwl_grab_restricted_access(priv);
+	if (rc) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return rc;
+	}
+
+	iwl_write_restricted(priv, FH_MEM_CBBC_QUEUE(txq_id),
+			     txq->q.dma_addr >> 8);
+	iwl_write_restricted(
+		priv, IWL_FH_TCSR_CHNL_TX_CONFIG_REG(txq_id),
+		IWL_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
+		IWL_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL);
+	iwl_release_restricted_access(priv);
+	spin_unlock_irqrestore(&priv->lock, flags);
+	return 0;
+}
+
+static inline u32 iwl4965_get_dma_lo_address(dma_addr_t addr)
+{
+	return (u32) (addr & 0xffffffff);
+}
+
+static inline u8 iwl4965_get_dma_hi_address(dma_addr_t addr)
+{
+	return sizeof(addr) > sizeof(u32) ? (addr >> 16) >> 16 : 0;
+}
+
+int iwl_hw_tx_queue_attach_buffer_to_tfd(struct iwl_priv *priv,
+					 void *ptr, dma_addr_t addr, u16 len)
+{
+	int index, is_odd;
+	struct iwl_tfd_frame *tfd = ptr;
+	u32 num_tbs = IWL_GET_BITS(*tfd, num_tbs);
+
+	if ((num_tbs >= MAX_NUM_OF_TBS) || (num_tbs < 0)) {
+		IWL_ERROR("Error can not send more than %d chunks\n",
+			  MAX_NUM_OF_TBS);
+		return -EINVAL;
+	}
+
+	index = num_tbs / 2;
+	is_odd = num_tbs & 0x1;
+
+	if (!is_odd) {
+		tfd->pa[index].tb1_addr = cpu_to_le32(
+			     iwl4965_get_dma_lo_address(addr));
+		IWL_SET_BITS(tfd->pa[index], tb1_addr_hi,
+			     iwl4965_get_dma_hi_address(addr));
+		IWL_SET_BITS(tfd->pa[index], tb1_len, len);
+	} else {
+		IWL_SET_BITS(tfd->pa[index], tb2_addr_lo16,
+			     (u32) (addr & 0xffff));
+		IWL_SET_BITS(tfd->pa[index], tb2_addr_hi20, addr >> 16);
+		IWL_SET_BITS(tfd->pa[index], tb2_len, len);
+	}
+
+	IWL_SET_BITS(*tfd, num_tbs, num_tbs + 1);
+
+	return 0;
+}
+
+void iwl_hw_card_show_info(struct iwl_priv *priv)
+{
+	u16 hw_version = priv->eeprom.board_revision_4965;
+
+	IWL_DEBUG_INFO("4965ABGN HW Version %u.%u.%u\n",
+		       ((hw_version >> 8) & 0x0F),
+		       ((hw_version >> 8) >> 4), (hw_version & 0x00FF));
+
+	IWL_DEBUG_INFO("4965ABGN PBA Number %.16s\n",
+		       priv->eeprom.board_pba_number_4965);
+}
+
+#define IWL_TX_CRC_SIZE		4
+#define IWL_TX_DELIMITER_SIZE	4
+
+int iwl4965_tx_queue_update_wr_ptr(struct iwl_priv *priv,
+				   struct iwl_tx_queue *txq, u16 byte_cnt)
+{
+	int len;
+	int txq_id = txq->q.id;
+	struct iwl_shared *shared_data =
+	    (struct iwl_shared *)priv->hw_setting.shared_virt;
+
+	if (txq->need_update == 0)
+		return 0;
+
+	len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE;
+
+	IWL_SET_BITS(shared_data->queues_byte_cnt_tbls[txq_id].
+		     tfd_offset[txq->q.first_empty], byte_cnt, len);
+
+	if (txq->q.first_empty < IWL4965_MAX_WIN_SIZE)
+		IWL_SET_BITS(shared_data->queues_byte_cnt_tbls[txq_id].
+			tfd_offset[IWL4965_QUEUE_SIZE + txq->q.first_empty],
+			byte_cnt, len);
+
+	return 0;
+}
+
+#define IWL4965_LEGACY_SWITCH_ANTENNA	0
+#define IWL4965_LECACY_SWITCH_SISO		1
+#define IWL4965_LEGACY_SWITCH_MIMO	        2
+
+#define IWL4965_GOOD_RATIO			12800
+
+#define IWL_ACTION_LIMIT		3
+#define IWL4965_LEGACY_FAILURE_LIMIT	160
+#define IWL4965_LEGACY_SUCCESS_LIMIT	480
+#define IWL4965_LEGACY_TABLE_COUNT		160
+
+#define IWL4965_NONE_LEGACY_FAILURE_LIMIT	400
+#define IWL4965_NONE_LEGACY_SUCCESS_LIMIT	4500
+#define IWL4965_NONE_LEGACY_TABLE_COUNT	1500
+
+#define IWL4965_RATE_SCALE_SWITCH  (10880)
+
+/* Set up Rx receiver/antenna/chain usage in "staging" RXON image.
+ * This should not be used for scan command ... it puts data in wrong place.  */
+void iwl4965_set_rxon_chain(struct iwl_priv *priv)
+{
+	u8 is_single = is_single_stream(priv);
+	u8 idle_state, rx_state;
+
+	priv->staging_rxon.rx_chain = 0;
+	rx_state = idle_state = 3;
+
+	/* Tell uCode which antennas are actually connected.
+	 * Before first association, we assume all antennas are connected.
+	 * Just after first association, iwl4965_noise_calibration()
+	 *    checks which antennas actually *are* connected. */
+	priv->staging_rxon.rx_chain |=
+	    (priv->valid_antenna << RXON_RX_CHAIN_VALID_POS);
+
+	/* How many receivers should we use? */
+	iwl4965_get_rx_chain_counter(priv, &idle_state, &rx_state);
+	priv->staging_rxon.rx_chain |= (rx_state << RXON_RX_CHAIN_MIMO_CNT_POS);
+	priv->staging_rxon.rx_chain |= (idle_state << RXON_RX_CHAIN_CNT_POS);
+
+	if (!is_single && !(priv->status & STATUS_POWER_PMI) && (rx_state >= 2))
+		priv->staging_rxon.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK;
+	else
+		priv->staging_rxon.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK;
+
+	IWL_DEBUG_ASSOC("rx chain %X\n", priv->staging_rxon.rx_chain);
+}
+
+#ifdef CONFIG_IWLWIFI_HT
+#ifdef CONFIG_IWLWIFI_HT_AGG
+/*
+	get the traffic load value for tid
+*/
+static u32 iwl4965_tl_get_load(struct iwl_priv *priv, u8 tid)
+{
+	u32 load = 0;
+	u32 current_time = jiffies_to_msecs(jiffies);
+	u32 time_diff;
+	s32 index;
+	unsigned long flags;
+	struct iwl_traffic_load *tid_ptr = NULL;
+
+	if (tid >= TID_MAX_LOAD_COUNT)
+		return 0;
+
+	tid_ptr = &(priv->lq_mngr.agg_ctrl.traffic_load[tid]);
+
+	current_time -= current_time % TID_ROUND_VALUE;
+
+	spin_lock_irqsave(&priv->lq_mngr.lock, flags);
+	if (!(tid_ptr->queue_count))
+		goto out;
+
+	time_diff = TIME_WRAP_AROUND(tid_ptr->time_stamp, current_time);
+	index = time_diff / TID_QUEUE_CELL_SPACING;
+
+	if (index >= TID_QUEUE_MAX_SIZE) {
+		u32 oldest_time = current_time - TID_MAX_TIME_DIFF;
+
+		while (tid_ptr->queue_count &&
+		       (tid_ptr->time_stamp < oldest_time)) {
+			tid_ptr->total -= tid_ptr->packet_count[tid_ptr->head];
+			tid_ptr->packet_count[tid_ptr->head] = 0;
+			tid_ptr->time_stamp += TID_QUEUE_CELL_SPACING;
+			tid_ptr->queue_count--;
+			tid_ptr->head++;
+			if (tid_ptr->head >= TID_QUEUE_MAX_SIZE)
+				tid_ptr->head = 0;
+		}
+	}
+	load = tid_ptr->total;
+
+ out:
+	spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
+	return load;
+}
+
+/*
+	increment traffic load value for tid and also remove
+	any old values if passed the certian time period
+*/
+static void iwl4965_tl_add_packet(struct iwl_priv *priv, u8 tid)
+{
+	u32 current_time = jiffies_to_msecs(jiffies);
+	u32 time_diff;
+	s32 index;
+	unsigned long flags;
+	struct iwl_traffic_load *tid_ptr = NULL;
+
+	if (tid >= TID_MAX_LOAD_COUNT)
+		return;
+
+	tid_ptr = &(priv->lq_mngr.agg_ctrl.traffic_load[tid]);
+
+	current_time -= current_time % TID_ROUND_VALUE;
+
+	spin_lock_irqsave(&priv->lq_mngr.lock, flags);
+	if (!(tid_ptr->queue_count)) {
+		tid_ptr->total = 1;
+		tid_ptr->time_stamp = current_time;
+		tid_ptr->queue_count = 1;
+		tid_ptr->head = 0;
+		tid_ptr->packet_count[0] = 1;
+		goto out;
+	}
+
+	time_diff = TIME_WRAP_AROUND(tid_ptr->time_stamp, current_time);
+	index = time_diff / TID_QUEUE_CELL_SPACING;
+
+	if (index >= TID_QUEUE_MAX_SIZE) {
+		u32 oldest_time = current_time - TID_MAX_TIME_DIFF;
+
+		while (tid_ptr->queue_count &&
+		       (tid_ptr->time_stamp < oldest_time)) {
+			tid_ptr->total -= tid_ptr->packet_count[tid_ptr->head];
+			tid_ptr->packet_count[tid_ptr->head] = 0;
+			tid_ptr->time_stamp += TID_QUEUE_CELL_SPACING;
+			tid_ptr->queue_count--;
+			tid_ptr->head++;
+			if (tid_ptr->head >= TID_QUEUE_MAX_SIZE)
+				tid_ptr->head = 0;
+		}
+	}
+
+	index = (tid_ptr->head + index) % TID_QUEUE_MAX_SIZE;
+	tid_ptr->packet_count[index] = tid_ptr->packet_count[index] + 1;
+	tid_ptr->total = tid_ptr->total + 1;
+
+	if ((index + 1) > tid_ptr->queue_count)
+		tid_ptr->queue_count = index + 1;
+ out:
+	spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
+
+}
+
+#define MMAC_SCHED_MAX_NUMBER_OF_HT_BACK_FLOWS   7
+enum HT_STATUS {
+	BA_STATUS_FAILURE = 0,
+	BA_STATUS_INITIATOR_DELBA,
+	BA_STATUS_RECIPIENT_DELBA,
+	BA_STATUS_RENEW_ADDBA_REQUEST,
+	BA_STATUS_ACTIVE,
+};
+
+static u8 iwl4964_tl_ba_avail(struct iwl_priv *priv)
+{
+	int i;
+	struct iwl_lq_mngr *lq;
+	u8 count = 0;
+	u16 msk;
+
+	lq = (struct iwl_lq_mngr *)&(priv->lq_mngr);
+	for (i = 0; i < TID_MAX_LOAD_COUNT ; i++) {
+		msk = 1 << i;
+		if ((lq->agg_ctrl.granted_ba & msk) ||
+		    (lq->agg_ctrl.wait_for_agg_status & msk))
+			count++;
+	}
+
+	if (count < MMAC_SCHED_MAX_NUMBER_OF_HT_BACK_FLOWS)
+		return 1;
+
+	return 0;
+}
+
+static void iwl4965_ba_status(struct iwl_priv *priv,
+			      u8 tid, enum HT_STATUS status);
+
+static int iwl4965_perform_addba(struct iwl_priv *priv, u8 tid,
+			      u32 length, u32 ba_timeout)
+{
+	int rc = 0;
+
+	IWL_WARNING("ZZZY we are staring Tx agg\n");
+	rc = ieee80211_start_BA_session(priv->hw, priv->bssid, tid);
+	if (rc)
+		iwl4965_ba_status(priv, tid, BA_STATUS_FAILURE);
+
+	return rc;
+}
+
+static int iwl4965_perform_delba(struct iwl_priv *priv, u8 tid)
+{
+	int rc = 0;
+
+	rc = ieee80211_stop_BA_session(priv->hw, priv->bssid, tid);
+
+	if (rc)
+		iwl4965_ba_status(priv, tid, BA_STATUS_FAILURE);
+	return rc;
+}
+
+static void iwl4965_turn_on_agg_for_tid(struct iwl_priv *priv,
+				     struct iwl_lq_mngr *lq,
+				     u8 auto_agg, u8 tid)
+{
+	u32 tid_msk = (1 << tid);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lq_mngr.lock, flags);
+/*
+	if ((auto_agg) && (!lq->enable_counter)){
+		lq->agg_ctrl.next_retry = 0;
+		lq->agg_ctrl.tid_retry = 0;
+		spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
+		return;
+	}
+*/
+	if (!(lq->agg_ctrl.granted_ba & tid_msk) &&
+	    (lq->agg_ctrl.requested_ba & tid_msk)) {
+		u8 available_queues;
+		u32 load;
+
+		spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
+		available_queues = iwl4964_tl_ba_avail(priv);
+		load = iwl4965_tl_get_load(priv, tid);
+
+		spin_lock_irqsave(&priv->lq_mngr.lock, flags);
+		if (!available_queues) {
+			if (auto_agg) {
+				lq->agg_ctrl.tid_retry |= tid_msk;
+			} else {
+				lq->agg_ctrl.requested_ba &= ~tid_msk;
+				lq->agg_ctrl.wait_for_agg_status &= ~tid_msk;
+			}
+		} else if ((auto_agg) &&
+			   ((load <= lq->agg_ctrl.tid_traffic_load_threshold)
+			    || ((lq->agg_ctrl.wait_for_agg_status & tid_msk)))) {
+			lq->agg_ctrl.tid_retry |= tid_msk;
+		} else {
+			lq->agg_ctrl.wait_for_agg_status |= tid_msk;
+			spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
+			iwl4965_perform_addba(priv, tid, 0x40,
+					   lq->agg_ctrl.ba_timeout);
+			spin_lock_irqsave(&priv->lq_mngr.lock, flags);
+		}
+	}
+	spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
+}
+
+static void iwl4965_turn_on_agg(struct iwl_priv *priv, u8 tid)
+{
+	struct iwl_lq_mngr *lq;
+	unsigned long flags;
+
+	lq = (struct iwl_lq_mngr *)&(priv->lq_mngr);
+
+	if ((tid < TID_MAX_LOAD_COUNT))
+		iwl4965_turn_on_agg_for_tid(priv, lq, lq->agg_ctrl.auto_agg, tid);
+	else if (tid == TID_ALL_SPECIFIED) {
+		if (lq->agg_ctrl.requested_ba) {
+			for (tid = 0; tid < TID_MAX_LOAD_COUNT; tid++)
+				iwl4965_turn_on_agg_for_tid(priv, lq,
+							 lq->agg_ctrl.auto_agg,
+							 tid);
+		} else {
+			spin_lock_irqsave(&priv->lq_mngr.lock, flags);
+			lq->agg_ctrl.tid_retry = 0;
+			lq->agg_ctrl.next_retry = 0;
+			spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
+		}
+	}
+
+}
+void iwl4965_turn_off_agg(struct iwl_priv *priv, u8 tid)
+{
+	u32 tid_msk;
+	struct iwl_lq_mngr *lq;
+	unsigned long flags;
+
+	lq = (struct iwl_lq_mngr *)&(priv->lq_mngr);
+
+	if ((tid < TID_MAX_LOAD_COUNT)) {
+		tid_msk = 1 << tid;
+		spin_lock_irqsave(&priv->lq_mngr.lock, flags);
+		lq->agg_ctrl.wait_for_agg_status |= tid_msk;
+		lq->agg_ctrl.requested_ba &= ~tid_msk;
+		spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
+		iwl4965_perform_delba(priv, tid);
+	} else if (tid == TID_ALL_SPECIFIED) {
+		spin_lock_irqsave(&priv->lq_mngr.lock, flags);
+		for (tid = 0; tid < TID_MAX_LOAD_COUNT; tid++) {
+			tid_msk = 1 << tid;
+			lq->agg_ctrl.wait_for_agg_status |= tid_msk;
+			spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
+			iwl4965_perform_delba(priv, tid);
+			spin_lock_irqsave(&priv->lq_mngr.lock, flags);
+		}
+		lq->agg_ctrl.requested_ba = 0;
+		spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
+	}
+}
+
+static void iwl4965_ba_status(struct iwl_priv *priv,
+				u8 tid, enum HT_STATUS status)
+{
+	struct iwl_lq_mngr *lq;
+	u32 tid_msk = (1 << tid);
+	unsigned long flags;
+
+	lq = (struct iwl_lq_mngr *)&(priv->lq_mngr);
+
+	if ((tid >= TID_MAX_LOAD_COUNT))
+		goto out;
+
+	spin_lock_irqsave(&priv->lq_mngr.lock, flags);
+	switch (status) {
+	case BA_STATUS_ACTIVE:
+		if (!(lq->agg_ctrl.granted_ba & tid_msk))
+			lq->agg_ctrl.granted_ba |= tid_msk;
+		break;
+	default:
+		if ((lq->agg_ctrl.granted_ba & tid_msk))
+			lq->agg_ctrl.granted_ba &= ~tid_msk;
+		break;
+	}
+
+	lq->agg_ctrl.wait_for_agg_status &= ~tid_msk;
+	if (status != BA_STATUS_ACTIVE) {
+		if (lq->agg_ctrl.auto_agg) {
+			lq->agg_ctrl.tid_retry |= tid_msk;
+			lq->agg_ctrl.next_retry =
+			    jiffies + msecs_to_jiffies(500);
+		} else
+			lq->agg_ctrl.requested_ba &= ~tid_msk;
+	}
+	spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
+ out:
+	return;
+}
+
+static void iwl4965_bg_agg_work(struct work_struct *work)
+{
+	struct iwl_priv *priv = container_of(work, struct iwl_priv,
+					agg_work);
+
+	u32 tid;
+	u32 retry_tid;
+	u32 tid_msk;
+	unsigned long flags;
+	struct iwl_lq_mngr *lq = (struct iwl_lq_mngr *)&(priv->lq_mngr);
+
+	spin_lock_irqsave(&priv->lq_mngr.lock, flags);
+	retry_tid = lq->agg_ctrl.tid_retry;
+	lq->agg_ctrl.tid_retry = 0;
+	spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
+
+	if (retry_tid == TID_ALL_SPECIFIED)
+		iwl4965_turn_on_agg(priv, TID_ALL_SPECIFIED);
+	else {
+		for (tid = 0; tid < TID_MAX_LOAD_COUNT; tid++) {
+			tid_msk = (1 << tid);
+			if (retry_tid & tid_msk)
+				iwl4965_turn_on_agg(priv, tid);
+		}
+	}
+
+	spin_lock_irqsave(&priv->lq_mngr.lock, flags);
+	if (lq->agg_ctrl.tid_retry)
+		lq->agg_ctrl.next_retry = jiffies + msecs_to_jiffies(500);
+	spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
+	return;
+}
+#endif /*CONFIG_IWLWIFI_HT_AGG */
+#endif /* CONFIG_IWLWIFI_HT */
+
+int iwl4965_tx_cmd(struct iwl_priv *priv, struct iwl_cmd *out_cmd,
+		   u8 sta_id, dma_addr_t txcmd_phys,
+		   struct ieee80211_hdr *hdr, u8 hdr_len,
+		   struct ieee80211_tx_control *ctrl, void *sta_in)
+{
+	struct iwl_tx_cmd cmd;
+	struct iwl_tx_cmd *tx = (struct iwl_tx_cmd *)&out_cmd->cmd.payload[0];
+	dma_addr_t scratch_phys;
+	u8 unicast = 0;
+	u8 is_data = 1;
+	u16 fc;
+	int rate_index = min(ctrl->tx_rate & 0xffff, IWL_RATE_COUNT - 1);
+#ifdef CONFIG_IWLWIFI_HT
+#ifdef CONFIG_IWLWIFI_HT_AGG
+	__le16 *qc;
+#endif /*CONFIG_IWLWIFI_HT_AGG */
+#endif /* CONFIG_IWLWIFI_HT */
+
+	unicast = !is_multicast_ether_addr(hdr->addr1);
+
+	fc = le16_to_cpu(hdr->frame_control);
+	if (WLAN_FC_GET_TYPE(fc) != IEEE80211_FTYPE_DATA)
+		is_data = 0;
+
+	memcpy(&cmd, &(out_cmd->cmd.tx), sizeof(struct iwl_tx_cmd));
+	memset(tx, 0, sizeof(struct iwl_tx_cmd));
+	memcpy(tx->hdr, hdr, hdr_len);
+
+	tx->len = cmd.len;
+	tx->driver_txop = cmd.driver_txop;
+	tx->stop_time.life_time = cmd.stop_time.life_time;
+	tx->tx_flags = cmd.tx_flags;
+	tx->sta_id = cmd.sta_id;
+	tx->tid_tspec = cmd.tid_tspec;
+	tx->timeout.pm_frame_timeout = cmd.timeout.pm_frame_timeout;
+	tx->next_frame_len = cmd.next_frame_len;
+
+	tx->sec_ctl = cmd.sec_ctl;
+	memcpy(&(tx->key[0]), &(cmd.key[0]), 16);
+	tx->tx_flags = cmd.tx_flags;
+
+	tx->rts_retry_limit = cmd.rts_retry_limit;
+	tx->data_retry_limit = cmd.data_retry_limit;
+
+	scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) +
+	    offsetof(struct iwl_tx_cmd, scratch);
+	tx->dram_lsb_ptr = iwl4965_get_dma_lo_address(scratch_phys);
+	tx->dram_msb_ptr = iwl4965_get_dma_hi_address(scratch_phys);
+
+	/* Hard coded to start at the highest retry fallback position
+	 * until the 4965 specific rate control algorithm is tied in */
+	tx->initial_rate_index = LINK_QUAL_MAX_RETRY_NUM - 1;
+	tx->rate.s.rate = iwl_rates[rate_index].plcp;
+
+	/* Alternate between antenna A and B for successive frames */
+	if (priv->use_ant_b_for_management_frame) {
+		priv->use_ant_b_for_management_frame = 0;
+		tx->rate.rate_n_flags |= RATE_MCS_ANT_B_MSK;
+		tx->rate.rate_n_flags &= ~RATE_MCS_ANT_A_MSK;
+	} else {
+		priv->use_ant_b_for_management_frame = 1;
+		tx->rate.rate_n_flags |= RATE_MCS_ANT_A_MSK;
+		tx->rate.rate_n_flags &= ~RATE_MCS_ANT_B_MSK;
+	}
+
+	if (!unicast || !is_data ) {
+		if ((rate_index >= IWL_FIRST_CCK_RATE) &&
+		    (rate_index <= IWL_LAST_CCK_RATE))
+			tx->rate.rate_n_flags |= RATE_MCS_CCK_MSK;
+
+	} else {
+		tx->initial_rate_index = 0;
+		tx->tx_flags |= TX_CMD_FLG_STA_RATE_MSK;
+	}
+
+	if (ieee80211_is_probe_request(fc))
+		tx->tx_flags |= TX_CMD_FLG_TSF_MSK;
+	else if (ieee80211_is_back_request(fc))
+		tx->tx_flags |= TX_CMD_FLG_ACK_MSK |
+			TX_CMD_FLG_IMM_BA_RSP_MASK;
+#ifdef CONFIG_IWLWIFI_HT
+#ifdef CONFIG_IWLWIFI_HT_AGG
+	qc = ieee80211_get_qos_ctrl(hdr);
+	if (qc &&
+	    (priv->iw_mode != IEEE80211_IF_TYPE_IBSS)) {
+		u8 tid = 0;
+		tid = (u8) (le16_to_cpu(*qc) & 0xF);
+		if (tid < TID_MAX_LOAD_COUNT)
+			iwl4965_tl_add_packet(priv, tid);
+	}
+
+	if (priv->lq_mngr.agg_ctrl.next_retry &&
+	    (time_after(priv->lq_mngr.agg_ctrl.next_retry, jiffies))) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&priv->lq_mngr.lock, flags);
+		priv->lq_mngr.agg_ctrl.next_retry = 0;
+		spin_unlock_irqrestore(&priv->lq_mngr.lock, flags);
+		schedule_work(&priv->agg_work);
+	}
+#endif
+#endif
+	return 0;
+}
+
+/**
+ * iwl4965_sign_extend - Sign extend a value using specified bit as sign-bit
+ *
+ * Example: sign_extend(9, 3) would return -7 as bit3 of 1001b is 1
+ * and bit0..2 is 001b which when sign extended to 1111111111111001b is -7.
+ *
+ * @param oper value to sign extend
+ * @param index 0 based bit index (0<=index<32) to sign bit
+ */
+static s32 iwl4965_sign_extend(u32 oper, u8 index)
+{
+	u32 bit;
+	u32 mask;
+
+	/* If the index is the MSB or higher then just return the
+	 * operand cast to a signed value */
+	if (index > 30)
+		return oper;
+
+	bit = 1 << index;
+	mask = ~(bit - 1);
+
+	/* negative -- sign extend */
+	if (oper & bit)
+		return oper |= mask;
+
+	/* positive -- sign clear */
+	return oper &= ~mask;
+}
+
+/**
+ * iwl4965_get_temperature - return the calibrated temperature (in Kelvin)
+ * @statistics: Provides the temperature reading from the uCode
+ *
+ * A return of <0 indicates bogus data in the statistics
+ */
+int iwl4965_get_temperature(const struct iwl_priv *priv)
+{
+	s32 temperature;
+	s32 vt;
+	s32 R1, R2, R3, R4;
+
+	if ((priv->status & STATUS_TEMPERATURE) &&
+		(priv->statistics.flag & STATISTICS_REPLY_FLG_FAT_MODE_MSK)) {
+		IWL_DEBUG_TEMP("Running FAT temperature calibration\n");
+		R1 = (s32)le32_to_cpu(priv->card_alive_init.therm_r1[1]);
+		R2 = (s32)le32_to_cpu(priv->card_alive_init.therm_r2[1]);
+		R3 = (s32)le32_to_cpu(priv->card_alive_init.therm_r3[1]);
+		R4 = (s32)le32_to_cpu(priv->card_alive_init.therm_r4[1]);
+	} else {
+		IWL_DEBUG_TEMP("Running temperature calibration\n");
+		R1 = (s32)le32_to_cpu(priv->card_alive_init.therm_r1[0]);
+		R2 = (s32)le32_to_cpu(priv->card_alive_init.therm_r2[0]);
+		R3 = (s32)le32_to_cpu(priv->card_alive_init.therm_r3[0]);
+		R4 = (s32)le32_to_cpu(priv->card_alive_init.therm_r4[0]);
+	}
+
+	/*
+	 * Temperature is only 23 bits so sign extend out to 32
+	 *
+	 * NOTE If we haven't received a statistics notification yet
+	 * with an updated temperature, use R4 provided to us in the
+	 * ALIVE response. */
+	if (!(priv->status & STATUS_TEMPERATURE))
+		vt = iwl4965_sign_extend(R4, 23);
+	else
+		vt = iwl4965_sign_extend(priv->statistics.general.temperature,
+					 23);
+
+	IWL_DEBUG_TEMP("Calib values R[1-3]: %d %d %d R4: %d\n",
+		       R1, R2, R3, vt);
+
+	if (R3 == R1) {
+		IWL_ERROR("Calibration conflict R1 == R3\n");
+		return -1;
+	}
+
+	/* Calculate temperature in degrees Kelvin, adjust by 97%.
+	 * Add offset to center the adjustment around 0 degrees Centigrade. */
+	temperature = TEMPERATURE_CALIB_A_VAL * (vt - R2);
+	temperature /= (R3 - R1);
+	temperature = (temperature * 97) / 100 +
+	    TEMPERATURE_CALIB_KELVIN_OFFSET;
+
+	IWL_DEBUG_TEMP("Calibrated temperature: %dK, %dC\n", temperature,
+	    KELVIN_TO_CELSIUS(temperature));
+
+	return temperature;
+}
+
+/* Adjust Txpower only if temperature variance is greater than threshold. */
+#define IWL_TEMPERATURE_THRESHOLD   3
+
+/**
+ * iwl4965_is_temp_calib_needed - determines if new calibration is needed
+ *
+ * If the temperature changed has changed sufficiently, then a recalibration
+ * is needed.
+ *
+ * Assumes caller will replace priv->last_temperature once calibration
+ * executed.
+ */
+static int iwl4965_is_temp_calib_needed(struct iwl_priv *priv)
+{
+	int temp_diff;
+
+	if (!(priv->status & STATUS_STATISTICS)) {
+		IWL_DEBUG_TEMP("Temperature not updated -- no statistics.\n");
+		return 0;
+	}
+
+	temp_diff = priv->temperature - priv->last_temperature;
+
+	/* get absolute value */
+	if (temp_diff < 0) {
+		IWL_DEBUG_POWER("Getting cooler, delta %d, \n", temp_diff);
+		temp_diff = -temp_diff;
+	} else if (temp_diff == 0)
+		IWL_DEBUG_POWER("Same temp, \n");
+	else
+		IWL_DEBUG_POWER("Getting warmer, delta %d, \n", temp_diff);
+
+	if (temp_diff < IWL_TEMPERATURE_THRESHOLD) {
+		IWL_DEBUG_POWER("Thermal txpower calib not needed\n");
+		return 0;
+	}
+
+	IWL_DEBUG_POWER("Thermal txpower calib needed\n");
+
+	return 1;
+}
+
+/* Calculate noise level, based on measurements during network silence just
+ *   before arriving beacon.  This measurement can be done only if we know
+ *   exactly when to expect beacons, therefore only when we're associated. */
+static void iwl4965_rx_calc_noise(struct iwl_priv *priv)
+{
+	struct statistics_rx_non_phy *rx_info
+				= &(priv->statistics.rx.general);
+	int num_active_rx = 0;
+	int total_silence = 0;
+	int bcn_silence_a = rx_info->beacon_silence_rssi_a & IN_BAND_FILTER;
+	int bcn_silence_b = rx_info->beacon_silence_rssi_b & IN_BAND_FILTER;
+	int bcn_silence_c = rx_info->beacon_silence_rssi_c & IN_BAND_FILTER;
+
+	if (bcn_silence_a) {
+		total_silence += bcn_silence_a;
+		num_active_rx++;
+	}
+	if (bcn_silence_b) {
+		total_silence += bcn_silence_b;
+		num_active_rx++;
+	}
+	if (bcn_silence_c) {
+		total_silence += bcn_silence_c;
+		num_active_rx++;
+	}
+
+	/* Average among active antennas */
+	if (num_active_rx)
+		priv->last_rx_noise = (total_silence / num_active_rx) - 107;
+	else
+		priv->last_rx_noise = -127;
+
+	IWL_DEBUG_CALIB("inband silence a %u, b %u, c %u, dBm %d\n",
+			bcn_silence_a, bcn_silence_b, bcn_silence_c,
+			priv->last_rx_noise);
+}
+
+void iwl_hw_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
+{
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	int change;
+	s32 temp;
+
+	IWL_DEBUG_RX("Statistics notification received (%d vs %d).\n",
+		     (int)sizeof(priv->statistics), pkt->len);
+
+	change = ((priv->statistics.general.temperature !=
+		   pkt->u.stats.general.temperature) ||
+		  ((priv->statistics.flag &
+		    STATISTICS_REPLY_FLG_FAT_MODE_MSK) !=
+		   (pkt->u.stats.flag & STATISTICS_REPLY_FLG_FAT_MODE_MSK)));
+
+	memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics));
+
+	priv->status |= STATUS_STATISTICS;
+
+	/* Reschedule the statistics timer to occur in
+	 * REG_RECALIB_PERIOD seconds to ensure we get a
+	 * thermal update even if the uCode doesn't give
+	 * us one */
+	mod_timer(&priv->statistics_periodic, jiffies +
+		  msecs_to_jiffies(REG_RECALIB_PERIOD * 1000));
+
+	if (unlikely(!(priv->status & STATUS_SCANNING)) &&
+	    (pkt->hdr.cmd == STATISTICS_NOTIFICATION)) {
+		iwl4965_rx_calc_noise(priv);
+#ifdef CONFIG_IWLWIFI_SENSITIVITY
+		queue_work(priv->workqueue, &priv->sensitivity_work);
+#endif
+	}
+
+	/* If the hardware hasn't reported a change in
+	 * temperature then don't bother computing a
+	 * calibrated temperature value */
+	if (!change)
+		return;
+
+	temp = iwl4965_get_temperature(priv);
+	if (temp < 0)
+		return;
+
+	if (priv->temperature != temp) {
+		if (priv->temperature)
+			IWL_DEBUG_TEMP("Temperature changed "
+				       "from %dC to %dC\n",
+				       KELVIN_TO_CELSIUS(priv->temperature),
+				       KELVIN_TO_CELSIUS(temp));
+		else
+			IWL_DEBUG_TEMP("Temperature "
+				       "initialized to %dC\n",
+				       KELVIN_TO_CELSIUS(temp));
+	}
+
+	priv->temperature = temp;
+	priv->status |= STATUS_TEMPERATURE;
+
+	if (unlikely(!(priv->status & STATUS_SCANNING) &&
+		     iwl4965_is_temp_calib_needed(priv)))
+		queue_work(priv->workqueue, &priv->txpower_work);
+}
+
+static void iwl4965_handle_data_packet(struct iwl_priv *priv, int is_data,
+				       int include_phy,
+				       struct iwl_rx_mem_buffer *rxb,
+				       struct ieee80211_rx_status *stats)
+{
+	struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+	struct iwl4965_rx_phy_res *rx_start = (include_phy) ?
+	    (struct iwl4965_rx_phy_res *)&(pkt->u.raw[0]) : NULL;
+	struct ieee80211_hdr *hdr;
+	unsigned int len;
+	u32 *rx_end;
+	unsigned int skblen;
+	u32 ampd_status;
+
+	if (!include_phy && priv->last_phy_res[0])
+		rx_start = (struct iwl4965_rx_phy_res *)&priv->last_phy_res[1];
+
+	if (!rx_start) {
+		IWL_ERROR("MPDU frame without a PHY data\n");
+		return;
+	}
+	if (include_phy) {
+		hdr = (struct ieee80211_hdr *)((u8 *) & rx_start[1] +
+					       rx_start->cfg_phy_cnt);
+
+		len = rx_start->byte_count;
+
+		rx_end = (u32 *) ((u8 *) & pkt->u.raw[0] +
+				  sizeof(struct iwl4965_rx_phy_res) +
+				  rx_start->cfg_phy_cnt + len);
+
+	} else {
+		struct iwl4965_rx_mpdu_res_start *amsdu =
+		    (struct iwl4965_rx_mpdu_res_start *)pkt->u.raw;
+
+		hdr = (void *)(pkt->u.raw +
+			       sizeof(struct iwl4965_rx_mpdu_res_start));
+		rx_end = (u32 *) (((u8 *) hdr) + amsdu->byte_count);
+		len = amsdu->byte_count;
+		rx_start->byte_count = len;
+	}
+	if (len > 2342 || len < 16) {
+		IWL_DEBUG_DROP("byte count out of range [16,2342]"
+			       " : %d\n", len);
+		return;
+
+	}
+
+	ampd_status = *rx_end;
+	skblen = ((u8 *) rx_end - (u8 *) & pkt->u.raw[0]) + sizeof(u32);
+
+	/* start from MAC */
+	skb_reserve(rxb->skb, (void *)hdr - (void *)pkt);
+	skb_put(rxb->skb, len);	/* end where data ends */
+
+	/* We only process data packets if the interface is open */
+	if (unlikely(!priv->is_open)) {
+		IWL_DEBUG_DROP_LIMIT
+		    ("Dropping packet while interface is not open.\n");
+		return;
+	}
+
+	if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) {
+		if (iwl_param_hwcrypto)
+			iwl_set_decrypted_flag(priv, rxb->skb,
+					       ampd_status, stats);
+		iwl_handle_data_packet_monitor(priv, rxb, hdr, len, stats, 0);
+		return;
+	}
+
+	stats->flag = 0;
+	hdr = (struct ieee80211_hdr *)rxb->skb->data;
+
+	if (iwl_param_hwcrypto)
+		iwl_set_decrypted_flag(priv, rxb->skb, ampd_status, stats);
+
+	ieee80211_rx_irqsafe(priv->hw, rxb->skb, stats);
+	priv->alloc_rxb_skb--;
+	rxb->skb = NULL;
+#ifdef LED
+	priv->led_packets += len;
+	iwl_setup_activity_timer(priv);
+#endif
+}
+
+/* Calc max signal level (dBm) among 3 possible receivers */
+static int iwl4965_calc_rssi(struct iwl4965_rx_phy_res *rx_resp)
+{
+	/* data from PHY/DSP regarding signal strength, etc.,
+	 *   contents are always there, not configurable by host.  */
+	struct iwl4965_rx_non_cfg_phy *ncphy =
+	    (struct iwl4965_rx_non_cfg_phy *)rx_resp->non_cfg_phy;
+	u32 agc = (ncphy->agc_info & IWL_AGC_DB_MASK) >> IWL_AGC_DB_POS;
+
+	u32 valid_antennae =
+	    (rx_resp->phy_flags & RX_PHY_FLAGS_ANTENNAE_MASK) >>
+	    RX_PHY_FLAGS_ANTENNAE_OFFSET;
+	u8 max_rssi = 0;
+	u32 i;
+
+	/* Find max rssi among 3 possible receivers.
+	 * These values are measured by the digital signal processor (DSP).
+	 * They should stay fairly constant even as the signal strength varies,
+	 *   if the radio's automatic gain control (AGC) is working right.
+	 * AGC value (see below) will provide the "interesting" info. */
+	for (i = 0; i < 3; i++)
+		if (valid_antennae & (1 << i))
+			max_rssi = max(ncphy->rssi_info[i << 1], max_rssi);
+
+	IWL_DEBUG_STATS("Rssi In A %d B %d C %d Max %d AGC dB %d\n",
+		ncphy->rssi_info[0], ncphy->rssi_info[2], ncphy->rssi_info[4],
+		max_rssi, agc);
+
+	/* dBm = max_rssi dB - agc dB - constant.
+	 * Higher AGC (higher radio gain) means lower signal. */
+	return (max_rssi - agc - IWL_RSSI_OFFSET);
+}
+
+#ifdef CONFIG_IWLWIFI_HT
+
+/* Parsed Information Elements */
+struct ieee802_11_elems {
+	u8 *ds_params;
+	u8 ds_params_len;
+	u8 *tim;
+	u8 tim_len;
+	u8 *ibss_params;
+	u8 ibss_params_len;
+	u8 *erp_info;
+	u8 erp_info_len;
+	u8 *ht_cap_param;
+	u8 ht_cap_param_len;
+	u8 *ht_extra_param;
+	u8 ht_extra_param_len;
+};
+
+static int parse_elems(u8 * start, size_t len, struct ieee802_11_elems *elems)
+{
+	size_t left = len;
+	u8 *pos = start;
+	int unknown = 0;
+
+	memset(elems, 0, sizeof(*elems));
+
+	while (left >= 2) {
+		u8 id, elen;
+
+		id = *pos++;
+		elen = *pos++;
+		left -= 2;
+
+		if (elen > left)
+			return -1;
+
+		switch (id) {
+		case WLAN_EID_DS_PARAMS:
+			elems->ds_params = pos;
+			elems->ds_params_len = elen;
+			break;
+		case WLAN_EID_TIM:
+			elems->tim = pos;
+			elems->tim_len = elen;
+			break;
+		case WLAN_EID_IBSS_PARAMS:
+			elems->ibss_params = pos;
+			elems->ibss_params_len = elen;
+			break;
+		case WLAN_EID_ERP_INFO:
+			elems->erp_info = pos;
+			elems->erp_info_len = elen;
+			break;
+		case WLAN_EID_HT_CAPABILITY:
+			elems->ht_cap_param = pos;
+			elems->ht_cap_param_len = elen;
+			break;
+		case WLAN_EID_HT_EXTRA_INFO:
+			elems->ht_extra_param = pos;
+			elems->ht_extra_param_len = elen;
+			break;
+		default:
+			unknown++;
+			break;
+		}
+
+		left -= elen;
+		pos += elen;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_IWLWIFI_HT */
+
+static void iwl4965_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id)
+{
+	unsigned long lock_flags;
+	spin_lock_irqsave(&priv->sta_lock, lock_flags);
+	priv->stations[sta_id].sta.station_flags &= ~STA_FLG_PWR_SAVE_MSK;
+	priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK;
+	priv->stations[sta_id].sta.sta.modify_mask = 0;
+	priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+	spin_unlock_irqrestore(&priv->sta_lock, lock_flags);
+	/* assuming we are in rx flow and the lock is already locked */
+	iwl_send_add_station(priv, &priv->stations[sta_id].sta,
+			     CMD_ASYNC | CMD_NO_LOCK);
+}
+
+static void iwl4965_update_ps_mode(struct iwl_priv *priv, u16 ps_bit, u8 *addr)
+{
+	/* FIXME: need locking over ps_status ??? */
+	u8 sta_id = iwl_hw_find_station(priv, addr);
+	if (sta_id != IWL_INVALID_STATION) {
+		u8 sta_awake = priv->stations[sta_id].
+				ps_status == STA_PS_STATUS_WAKE;
+		if (sta_awake && ps_bit) {
+			priv->stations[sta_id].ps_status = STA_PS_STATUS_SLEEP;
+		} else if (!sta_awake && !ps_bit) {
+			iwl4965_sta_modify_ps_wake(priv, sta_id);
+			priv->stations[sta_id].ps_status = STA_PS_STATUS_WAKE;
+		}
+	}
+
+}
+
+/* Called for REPLY_4965_RX (legacy ABG frames), or
+ * REPLY_RX_MPDU_CMD (HT high-throughput N frames). */
+static void iwl4965_rx_reply_rx(struct iwl_priv *priv,
+				struct iwl_rx_mem_buffer *rxb)
+{
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+
+	/* Use phy data (Rx signal strength, etc.) contained within
+	 *   this rx packet for legacy frames,
+	 *   or phy data cached from REPLY_RX_PHY_CMD for HT frames. */
+	int include_phy = (pkt->hdr.cmd == REPLY_4965_RX);
+	struct iwl4965_rx_phy_res *rx_start = (include_phy) ?
+		(struct iwl4965_rx_phy_res *)&(pkt->u.raw[0]) :
+		(struct iwl4965_rx_phy_res *)&priv->last_phy_res[1];
+
+	__le32 *rx_end;
+	unsigned int len = 0;
+	struct ieee80211_hdr *header;
+	u16 fc;
+	struct ieee80211_rx_status stats = {
+		.mactime = le32_to_cpu(rx_start->beacon_time_stamp),
+		.channel = le16_to_cpu(rx_start->channel),
+		.phymode =
+			(rx_start->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ?
+			MODE_IEEE80211G : MODE_IEEE80211A,
+		.antenna = 0,
+		.rate = iwl4965_get_rate(rx_start->rate_n_flags),
+		.flag = le16_to_cpu(rx_start->phy_flags),
+#ifdef CONFIG_IWLWIFI_HT_AGG
+		.ordered = 0
+#endif /* CONFIG_IWLWIFI_HT_AGG */
+	};
+	u8 network_packet;
+
+	if ((unlikely(rx_start->cfg_phy_cnt > 20))) {
+		IWL_DEBUG_DROP
+			("dsp size out of range [0,20]: "
+			 "%d/n", rx_start->cfg_phy_cnt);
+		return;
+	}
+	if (!include_phy) {
+		if (priv->last_phy_res[0])
+			rx_start = (struct iwl4965_rx_phy_res *)
+				&priv->last_phy_res[1];
+		else
+			rx_start = NULL;
+	}
+
+	if (!rx_start) {
+		IWL_ERROR("MPDU frame without a PHY data\n");
+		return;
+	}
+
+	if (include_phy) {
+		header = (struct ieee80211_hdr *)((u8 *) & rx_start[1]
+						  + rx_start->cfg_phy_cnt);
+
+		len = le16_to_cpu(rx_start->byte_count);
+		rx_end = (__le32 *) (pkt->u.raw + rx_start->cfg_phy_cnt +
+				  sizeof(struct iwl4965_rx_phy_res) + len);
+	} else {
+		struct iwl4965_rx_mpdu_res_start *amsdu =
+			(struct iwl4965_rx_mpdu_res_start *)pkt->u.raw;
+
+		header = (void *)(pkt->u.raw +
+			sizeof(struct iwl4965_rx_mpdu_res_start));
+		len = le16_to_cpu(amsdu->byte_count);
+		rx_end = (__le32 *) (pkt->u.raw +
+			sizeof(struct iwl4965_rx_mpdu_res_start) + len);
+	}
+
+	if (!(*rx_end & RX_RES_STATUS_NO_CRC32_ERROR) ||
+	    !(*rx_end & RX_RES_STATUS_NO_RXE_OVERFLOW)) {
+		IWL_DEBUG_RX("Bad CRC or FIFO: 0x%08X.\n",
+				le32_to_cpu(*rx_end));
+		return;
+	}
+
+	priv->ucode_beacon_time = le32_to_cpu(rx_start->beacon_time_stamp);
+
+	stats.freq = ieee80211chan2mhz(stats.channel);
+
+	/* Find max signal strength (dBm) among 3 antenna/receiver chains */
+	stats.ssi = iwl4965_calc_rssi(rx_start);
+
+	/* Meaningful noise values are available only from beacon statistics,
+	 *   which are gathered only when associated, and indicate noise
+	 *   only for the associated network channel ...
+	 * Ignore these noise values while scanning (other channels) */
+	if (iwl_is_associated(priv) && !(priv->status & STATUS_SCANNING)) {
+		stats.noise = priv->last_rx_noise;
+		stats.signal = iwl_calc_sig_qual(stats.ssi, stats.noise);
+	} else {
+		stats.noise = -127;
+		stats.signal = iwl_calc_sig_qual(stats.ssi, 0);
+	}
+
+	/* Reset beacon noise level if not associated.
+	 * Use default noise value of -127 ... this works better than
+	 *   0 when averaging frames with/without noise info;
+	 *   measured dBm values are always negative ... using a
+	 *   negative value as the default keeps all averages within
+	 *   an s8's (used in some apps) range of negative values. */
+	if (!iwl_is_associated(priv))
+		priv->last_rx_noise = -127;
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+	/* TODO:  Parts of iwl_report_frame are broken for 4965 */
+	if (iwl_debug_level & (IWL_DL_RX))
+		/* Set "1" to report good data frames in groups of 100 */
+		iwl_report_frame(priv, pkt, header, 1);
+
+	if (iwl_debug_level & (IWL_DL_RX | IWL_DL_STATS))
+	IWL_DEBUG_RX("Rssi %d, noise %d, qual %d, TSF %lu\n",
+		stats.ssi, stats.noise, stats.signal,
+		 (long unsigned int)le64_to_cpu(rx_start->timestamp));
+#endif
+
+	network_packet = iwl_is_network_packet(priv, header);
+	if (network_packet) {
+		priv->last_rx_rssi = stats.ssi;
+		priv->last_beacon_time =  priv->ucode_beacon_time;
+		priv->last_tsf = le64_to_cpu(rx_start->timestamp);
+	}
+
+	fc = le16_to_cpu(header->frame_control);
+	switch (WLAN_FC_GET_TYPE(fc)) {
+	case IEEE80211_FTYPE_MGMT:
+
+		if (priv->iw_mode == IEEE80211_IF_TYPE_AP)
+			iwl4965_update_ps_mode(priv, fc  & IEEE80211_FCTL_PM,
+						header->addr2);
+		switch (WLAN_FC_GET_STYPE(fc)) {
+		case IEEE80211_STYPE_PROBE_RESP:
+		case IEEE80211_STYPE_BEACON:
+			if ((priv->iw_mode == IEEE80211_IF_TYPE_STA &&
+			     !compare_ether_addr(header->addr2, priv->bssid)) ||
+			    (priv->iw_mode == IEEE80211_IF_TYPE_IBSS &&
+			     !compare_ether_addr(header->addr3, priv->bssid))) {
+				struct ieee80211_mgmt *mgmt =
+					(struct ieee80211_mgmt *)header;
+				u32 *pos;
+
+				pos = (u32 *) & mgmt->u.beacon.timestamp;
+				priv->timestamp0 = le32_to_cpu(pos[0]);
+				priv->timestamp1 = le32_to_cpu(pos[1]);
+				priv->beacon_int = le16_to_cpu(
+				    mgmt->u.beacon.beacon_int);
+				if (priv->call_post_assoc_from_beacon &&
+				    (priv->iw_mode == IEEE80211_IF_TYPE_STA)) {
+					priv->call_post_assoc_from_beacon = 0;
+					queue_work(priv->workqueue,
+					    &priv->post_associate.work);
+				}
+			}
+			break;
+
+		case IEEE80211_STYPE_ACTION:
+			break;
+
+			/*
+			 * TODO: There is no callback function from upper
+			 * stack to inform us when associated status. this
+			 * work around to sniff assoc_resp management frame
+			 * and finish the association process.
+			 */
+		case IEEE80211_STYPE_ASSOC_RESP:
+		case IEEE80211_STYPE_REASSOC_RESP:
+			if (network_packet && iwl_is_associated(priv)) {
+#ifdef CONFIG_IWLWIFI_HT
+				u8 *pos = NULL;
+				struct ieee802_11_elems elems;
+#endif				/*CONFIG_IWLWIFI_HT */
+				struct ieee80211_mgmt *mgnt =
+					(struct ieee80211_mgmt *)header;
+
+				priv->assoc_id = (~((1 << 15) | (1 << 14))
+						  & mgnt->u.assoc_resp.aid);
+				priv->assoc_capability =
+					le16_to_cpu(
+						mgnt->u.assoc_resp.capab_info);
+#ifdef CONFIG_IWLWIFI_HT
+				pos = mgnt->u.assoc_resp.variable;
+				if (!parse_elems(pos,
+						 len - (pos - (u8 *) mgnt),
+						 &elems)) {
+					if (elems.ht_extra_param &&
+					    elems.ht_cap_param)
+						break;
+				}
+#endif				/*CONFIG_IWLWIFI_HT */
+				/* assoc_id is 0 no association */
+				if (!priv->assoc_id)
+					break;
+				if (priv->beacon_int)
+					queue_work(priv->workqueue,
+					    &priv->post_associate.work);
+				else
+					priv->call_post_assoc_from_beacon = 1;
+			}
+
+			break;
+
+		case IEEE80211_STYPE_PROBE_REQ:
+			if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) &&
+			    !iwl_is_associated(priv)) {
+				IWL_DEBUG_DROP("Dropping (non network): "
+					       MAC_FMT ", " MAC_FMT ", "
+					       MAC_FMT "\n",
+					       MAC_ARG(header->addr1),
+					       MAC_ARG(header->addr2),
+					       MAC_ARG(header->addr3));
+				return;
+			}
+		}
+		iwl4965_handle_data_packet(priv, 0, include_phy, rxb, &stats);
+		break;
+
+	case IEEE80211_FTYPE_CTL:
+#ifdef CONFIG_IWLWIFI_HT_AGG
+		switch (WLAN_FC_GET_STYPE(fc)) {
+		case IEEE80211_STYPE_BACK_REQ:
+			IWL_DEBUG_HT("IEEE80211_STYPE_BACK_REQ arrived\n");
+			iwl4965_handle_data_packet(priv, 0, include_phy,
+						rxb, &stats);
+			break;
+		default:
+			break;
+		}
+#endif
+
+		break;
+
+	case IEEE80211_FTYPE_DATA:
+		if (priv->iw_mode == IEEE80211_IF_TYPE_AP)
+			iwl4965_update_ps_mode(priv, fc  & IEEE80211_FCTL_PM,
+						header->addr2);
+
+		if (unlikely(!network_packet))
+			IWL_DEBUG_DROP("Dropping (non network): "
+				       MAC_FMT ", " MAC_FMT ", "
+				       MAC_FMT "\n",
+				       MAC_ARG(header->addr1),
+				       MAC_ARG(header->addr2),
+				       MAC_ARG(header->addr3));
+		else if (unlikely(is_duplicate_packet(priv, header)))
+			IWL_DEBUG_DROP("Dropping (dup): " MAC_FMT ", "
+				       MAC_FMT ", " MAC_FMT "\n",
+				       MAC_ARG(header->addr1),
+				       MAC_ARG(header->addr2),
+				       MAC_ARG(header->addr3));
+		else
+			iwl4965_handle_data_packet(priv, 1, include_phy, rxb,
+						   &stats);
+		break;
+	default:
+		break;
+
+	}
+}
+
+/* Cache phy data (Rx signal strength, etc) for HT frame (REPLY_RX_PHY_CMD).
+ * This will be used later in iwl4965_rx_reply_rx() for REPLY_RX_MPDU_CMD. */
+static void iwl4965_rx_reply_rx_phy(struct iwl_priv *priv,
+				    struct iwl_rx_mem_buffer *rxb)
+{
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	priv->last_phy_res[0] = 1;
+	memcpy(&priv->last_phy_res[1], &(pkt->u.raw[0]),
+	       sizeof(struct iwl4965_rx_phy_res));
+}
+
+static void iwl4965_rx_missed_beacon_notif (struct iwl_priv *priv,
+					   struct iwl_rx_mem_buffer *rxb)
+
+{
+#ifdef CONFIG_IWLWIFI_SENSITIVITY
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	struct iwl_missed_beacon_notif *missed_beacon;
+
+	missed_beacon = &pkt->u.missed_beacon;
+	if (missed_beacon->consequtive_missed_beacons > 5) {
+		IWL_DEBUG_CALIB("missed bcn cnsq %d totl %d rcd %d expctd %d\n",
+			    missed_beacon->consequtive_missed_beacons,
+			    missed_beacon->total_missed_becons,
+			    missed_beacon->num_recvd_beacons,
+			    missed_beacon->num_expected_beacons);
+		priv->sensitivity_data.state = IWL_SENS_CALIB_NEED_REINIT;
+		if (unlikely(!(priv->status & STATUS_SCANNING)))
+			queue_work(priv->workqueue, &priv->sensitivity_work);
+	}
+#endif /*CONFIG_IWLWIFI_SENSITIVITY*/
+}
+
+#ifdef CONFIG_IWLWIFI_HT
+#ifdef CONFIG_IWLWIFI_HT_AGG
+
+static void iwl4965_set_tx_status(struct iwl_priv *priv,int txq_id, int idx,
+				  u32 status,u32 retry_count, u32 rate)
+{
+	struct ieee80211_tx_status *tx_status =
+		&(priv->txq[txq_id].txb[idx].status);
+	tx_status->flags = status?IEEE80211_TX_STATUS_ACK:0;
+	tx_status->retry_count += retry_count;
+	tx_status->control.tx_rate = rate;
+}
+
+
+static void iwl_sta_modify_enable_tid_tx(struct iwl_priv *priv, int sta_id,
+						int tid)
+{
+	unsigned long lock_flags;
+
+	spin_lock_irqsave(&priv->sta_lock, lock_flags);
+	priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX;
+	priv->stations[sta_id].sta.tid_disable_tx &= cpu_to_le32(~(1 << tid));
+	priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+	spin_unlock_irqrestore(&priv->sta_lock, lock_flags);
+	iwl_send_add_station(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
+}
+
+
+static int iwl4965_tx_status_reply_compressed_ba(struct iwl_priv *priv,
+						 struct iwl_ht_agg *agg,
+						 struct iwl_compressed_ba_resp*
+						 ba_resp)
+
+{
+	int i, sh, ack;
+	u32 bitmap0, bitmap1;
+	if (unlikely(!agg->wait_for_ba))  {
+		IWL_ERROR("Received BA when not expected\n");
+		return -EINVAL;
+	}
+	agg->wait_for_ba = 0;
+	IWL_DEBUG_TX_REPLY("BA %d %d\n", agg->start_idx, ba_resp->ba_seq_ctl);
+	sh = agg->start_idx - SEQ_TO_INDEX(ba_resp->ba_seq_ctl>>4);
+	if (sh < 0) /* tbw something is wrong with indeces */
+		sh += 0x100;
+
+	/* don't use 64 bits for now */
+	bitmap0 = ba_resp->ba_bitmap0 >> sh;
+	bitmap1 = ba_resp->ba_bitmap1 >> sh;
+	bitmap0 |= (ba_resp->ba_bitmap1 & ((1<<sh)|((1<<sh)-1))) << (32 - sh);
+
+	if (agg->frame_count > (64 - sh)) {
+		IWL_DEBUG_TX_REPLY("more frames than bitmap size");
+		return -1;
+	}
+
+	/* check for success or failure according to the
+	 * transmitted bitmap and back bitmap */
+	bitmap0 &= agg->bitmap0;
+	bitmap1 &= agg->bitmap1;
+
+	for (i = 0; i < agg->frame_count ; i++) {
+		int idx = (agg->start_idx + i) & 0xff;
+		ack = bitmap0 & (1 << i);
+		IWL_DEBUG_TX_REPLY("%s ON i=%d idx=%d raw=%d\n",
+			ack? "ACK":"NACK", i, idx, agg->start_idx + i);
+		iwl4965_set_tx_status(priv, ba_resp->tid, idx, ack, 1,
+			agg->rate.rate_n_flags);
+
+	}
+
+	IWL_DEBUG_TX_REPLY("Bitmap %x%x\n", bitmap0, bitmap1);
+
+	return 0;
+}
+
+static inline int iwl_queue_dec_wrap(int index, int n_bd)
+{
+	return (index == 0) ? n_bd - 1 : index - 1;
+}
+
+static void iwl4965_rx_reply_compressed_ba(struct iwl_priv *priv,
+					   struct iwl_rx_mem_buffer *rxb)
+{
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	struct iwl_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba;
+	int index;
+	struct iwl_tx_queue *txq = NULL;
+	struct iwl_ht_agg *agg;
+
+	if (ba_resp->scd_flow >= ARRAY_SIZE(priv->txq)) {
+		IWL_ERROR("BUG_ON scd_flow is bigger than number of queues");
+		return;
+	}
+
+	txq = &priv->txq[ba_resp->scd_flow];
+	agg = &priv->stations[ba_resp->sta_id].tid[ba_resp->tid].agg;
+	index = iwl_queue_dec_wrap(ba_resp->scd_ssn & 0xff, txq->q.n_bd);
+
+	/* TODO: Need to get this copy more sefely - now good for debug */
+/*
+	IWL_DEBUG_TX_REPLY("REPLY_COMPRESSED_BA [%d]Received from " MAC_FMT ",
+			   sta_id = %d\n",
+			   agg->wait_for_ba,
+			   MAC_ARG((u8*) &ba_resp->sta_addr_lo32),
+			   ba_resp->sta_id);
+	IWL_DEBUG_TX_REPLY("TID = %d, SeqCtl = %d, bitmap = 0x%X%X, scd_flow = "
+			   "%d, scd_ssn = %d\n",
+			   ba_resp->tid,
+			   ba_resp->ba_seq_ctl,
+			   ba_resp->ba_bitmap1,
+			   ba_resp->ba_bitmap0,
+			   ba_resp->scd_flow,
+			   ba_resp->scd_ssn);
+	IWL_DEBUG_TX_REPLY("DAT start_idx = %d, bitmap = 0x%X%X \n",
+			   agg->start_idx,
+			   agg->bitmap1,
+			   agg->bitmap0);
+*/
+	iwl4965_tx_status_reply_compressed_ba(priv, agg, ba_resp);
+	/* releases all the TFDs until the SSN */
+	if (txq->q.last_used != (ba_resp->scd_ssn & 0xff))
+		iwl_tx_queue_reclaim(priv, ba_resp ->scd_flow, index);
+
+}
+
+
+#define STA_MODIFY_ADDBA_TID_MSK   0x08
+#define STA_MODIFY_DELBA_TID_MSK   0x10
+#define BUILD_RAxTID(sta_id, tid) ( ((sta_id) << 4) + (tid) )
+
+static void iwl4965_tx_queue_stop_scheduler(struct iwl_priv *priv, u16 txq_id)
+{
+	iwl_write_restricted_reg(priv,
+		SCD_QUEUE_STATUS_BITS(txq_id),
+		(0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)|
+		(1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
+}
+
+static int iwl4965_tx_queue_set_q2ratid(struct iwl_priv *priv, u16 ra_tid,
+						u16 txq_id)
+{
+	u32 tbl_dw_addr;
+	u32 tbl_dw;
+	u16 scd_q2ratid;
+
+	scd_q2ratid = ra_tid & SCD_QUEUE_RA_TID_MAP_RATID_MSK;
+
+	tbl_dw_addr = priv->scd_base_addr +
+			SCD_TRANSLATE_TBL_OFFSET_QUEUE(txq_id);
+
+	tbl_dw = iwl_read_restricted_mem(priv, tbl_dw_addr);
+	((u16* )&tbl_dw)[txq_id & 0x1] = scd_q2ratid;
+
+	iwl_write_restricted_mem(priv, tbl_dw_addr, tbl_dw);
+
+	return 0;
+}
+
+/**
+ * txq_id must be greater than IWL_BACK_QUEUE_FIRST_ID
+ */
+int iwl4965_tx_queue_agg_enable(struct iwl_priv *priv, int txq_id, int tx_fifo,
+				int sta_id, int tid, u16 ssn_idx)
+{
+	unsigned long flags;
+	int rc;
+	u16 ra_tid;
+
+	if (IWL_BACK_QUEUE_FIRST_ID > txq_id)
+		IWL_WARNING("queue number too small: %d, must be > %d\n",
+			txq_id, IWL_BACK_QUEUE_FIRST_ID);
+
+	ra_tid = BUILD_RAxTID(sta_id, tid);
+
+	iwl_sta_modify_enable_tid_tx(priv, sta_id, tid);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	rc = iwl_grab_restricted_access(priv);
+	if (rc) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return rc;
+	}
+
+	iwl4965_tx_queue_stop_scheduler(priv, txq_id);
+
+	iwl4965_tx_queue_set_q2ratid(priv, ra_tid, txq_id);
+
+
+	iwl_set_bits_restricted_reg(priv, SCD_QUEUECHAIN_SEL, (1<<txq_id));
+
+	priv->txq[txq_id].q.last_used = (ssn_idx & 0xff);
+	priv->txq[txq_id].q.first_empty = (ssn_idx & 0xff);
+
+	/* supposes that ssn_idx is valid (!= 0xFFF) */
+	iwl4965_set_wr_ptrs(priv, txq_id, ssn_idx);
+
+	iwl_write_restricted_mem(priv, priv->scd_base_addr +
+				SCD_CONTEXT_QUEUE_OFFSET(txq_id),
+				(SCD_WIN_SIZE << SCD_QUEUE_CTX_REG1_WIN_SIZE_POS) &
+				SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK);
+
+	iwl_write_restricted_mem(priv, priv->scd_base_addr +
+				SCD_CONTEXT_QUEUE_OFFSET(txq_id) +
+				sizeof(u32),
+				(SCD_FRAME_LIMIT << SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
+				SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK);
+
+	iwl_set_bits_restricted_reg(priv, SCD_INTERRUPT_MASK, (1 << txq_id));
+
+	iwl4965_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1);
+
+	iwl_release_restricted_access(priv);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+/**
+ * txq_id must be greater than IWL_BACK_QUEUE_FIRST_ID
+ */
+int iwl4965_tx_queue_agg_disable(struct iwl_priv *priv, u16 txq_id,
+				 u16 ssn_idx, u8 tx_fifo)
+{
+	unsigned long flags;
+	int rc;
+
+	if (IWL_BACK_QUEUE_FIRST_ID > txq_id) {
+		IWL_WARNING("queue number too small: %d, must be > %d\n",
+				txq_id, IWL_BACK_QUEUE_FIRST_ID);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+	rc = iwl_grab_restricted_access(priv);
+	if (rc) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		return rc;
+	}
+
+	iwl4965_tx_queue_stop_scheduler(priv, txq_id);
+
+	iwl_clear_bits_restricted_reg(priv, SCD_QUEUECHAIN_SEL, (1 << txq_id));
+
+	priv->txq[txq_id].q.last_used = (ssn_idx & 0xff);
+	priv->txq[txq_id].q.first_empty = (ssn_idx & 0xff);
+	/* supposes that ssn_idx is valid (!= 0xFFF) */
+	iwl4965_set_wr_ptrs(priv, txq_id, ssn_idx);
+
+	iwl_clear_bits_restricted_reg(priv, SCD_INTERRUPT_MASK, (1 << txq_id));
+	iwl4965_txq_ctx_deactivate(priv,txq_id);
+	iwl4965_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0);
+
+	iwl_release_restricted_access(priv);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+#endif/* CONFIG_IWLWIFI_HT_AGG */
+#endif /* CONFIG_IWLWIFI_HT */
+/*
+ * RATE SCALE CODE
+ */
+int iwl4965_init_hw_rates(struct iwl_priv *priv, struct ieee80211_rate *rates)
+{
+	return 0;
+}
+
+
+/**
+ * iwl4965_add_station - Initialize a station's hardware rate table
+ *
+ * The uCode contains a table of fallback rates and retries per rate
+ * for automatic fallback during transmission.
+ *
+ * NOTE: This initializes the table for a single retry per data rate
+ * which is not optimal.  Setting up an intelligent retry per rate
+ * requires feedback from transmission, which isn't exposed through
+ * rc80211_simple which is what this driver is currently using.
+ *
+ */
+void iwl4965_add_station(struct iwl_priv *priv, const u8 * addr, int is_ap)
+{
+	int i, r;
+	struct iwl_link_quality_cmd link_cmd = {
+		.reserved1 = 0,
+	};
+	struct iwl_rate *table = link_cmd.rate_scale_table;
+
+	/* Set up the rate scaling to start at 54M and fallback
+	 * all the way to 1M in IEEE order and then spin on IEEE */
+	i = 0;
+	if (is_ap)
+		r = IWL_RATE_54M_INDEX;
+	else if ((priv->phymode == MODE_IEEE80211A) ||
+		 (priv->phymode == MODE_ATHEROS_TURBO))
+		r = IWL_RATE_6M_INDEX;
+	else
+		r = IWL_RATE_1M_INDEX;
+
+	while (i < LINK_QUAL_MAX_RETRY_NUM) {
+		if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE) {
+			table[i].rate_n_flags |= RATE_MCS_CCK_MSK;
+		}
+		table[i].s.rate = iwl_rates[r].plcp;
+		table[i].rate_n_flags |= RATE_MCS_ANT_B_MSK;
+		table[i].rate_n_flags &= ~RATE_MCS_ANT_A_MSK;
+		r = iwl_get_prev_ieee_rate(r);
+		i++;
+	}
+
+	link_cmd.general_params.single_stream_ant_msk = 2;
+	link_cmd.general_params.dual_stream_ant_msk = 3;
+	link_cmd.agg_params.agg_dis_start_th = 3;
+	link_cmd.agg_params.agg_time_limit = 4000;
+
+	/* Update the rate scaling for control frame Tx to AP */
+	link_cmd.sta_id = is_ap ? IWL_AP_ID : IWL_BROADCAST_ID;
+
+	iwl_send_cmd_pdu(priv, REPLY_TX_LINK_QUALITY_CMD, sizeof(link_cmd),
+			 &link_cmd);
+}
+
+#ifdef CONFIG_IWLWIFI_HT
+
+static u8 iwl_is_channel_extension(struct iwl_priv *priv, int phymode,
+				   int channel, u8 extension_chan_offset)
+{
+	const struct iwl_channel_info *ch_info;
+
+	ch_info = iwl_get_channel_info(priv, phymode, channel);
+	if (!is_channel_valid(ch_info))
+		return 0;
+
+	if (extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_AUTO)
+		return 0;
+
+	if ((ch_info->fat_extension_channel == extension_chan_offset) ||
+	    (ch_info->fat_extension_channel == HT_IE_EXT_CHANNEL_MAX))
+		return 1;
+
+	return 0;
+}
+
+static u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv,
+				const struct sta_ht_info *ht_info)
+{
+
+	if (priv->channel_width != IWL_CHANNEL_WIDTH_40MHZ)
+		return 0;
+
+	if (ht_info->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ)
+		return 0;
+
+	if (ht_info->extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_AUTO)
+		return 0;
+
+	/* no fat tx allowed on 2.4GHZ */
+	if ((priv->phymode != MODE_IEEE80211A) &&
+	    (priv->phymode != MODE_ATHEROS_TURBO))
+		return 0;
+	return (iwl_is_channel_extension(priv, priv->phymode,
+					 ht_info->control_chan,
+					 ht_info->extension_chan_offset));
+}
+
+void iwl4965_set_rxon_ht(struct iwl_priv *priv,
+			 struct sta_ht_info *ht_info)
+{
+	struct iwl_rxon_cmd *rxon = &priv->staging_rxon;
+	u32 val;
+
+	if (!ht_info->is_ht)
+		return;
+
+	if (iwl_is_fat_tx_allowed(priv, ht_info))
+		rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED_MSK;
+	else
+		rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY_MSK;
+
+	if (rxon->channel != ht_info->control_chan) {
+		IWL_DEBUG_ASSOC("control diff than current %d %d\n",
+				rxon->channel, ht_info->control_chan);
+		rxon->channel = ht_info->control_chan;
+		return;
+	}
+
+	rxon->flags &= ~RXON_FLG_CONTROL_CHANNEL_LOCATION_MSK;
+
+	switch (ht_info->extension_chan_offset) {
+	case IWL_EXT_CHANNEL_OFFSET_ABOVE:
+		rxon->flags |= RXON_FLG_CONTROL_CHANNEL_LOC_LOW_MSK;
+		break;
+	case IWL_EXT_CHANNEL_OFFSET_BELOW:
+		rxon->flags |= RXON_FLG_CONTROL_CHANNEL_LOC_HIGH_MSK;
+		break;
+	case IWL_EXT_CHANNEL_OFFSET_AUTO:
+		rxon->flags &= ~RXON_FLG_CHANNEL_MODE_MIXED_MSK;
+		break;
+	default:
+		rxon->flags &= ~RXON_FLG_CHANNEL_MODE_MIXED_MSK;
+		break;
+	}
+
+	val = ht_info->operating_mode;
+
+	rxon->flags |= val << RXON_FLG_HT_OPERATING_MODE_POS;
+
+	priv->active_rate_ht[0] = ht_info->supp_rates[0];
+	priv->active_rate_ht[1] = ht_info->supp_rates[1];
+	iwl4965_set_rxon_chain(priv);
+
+	IWL_DEBUG_ASSOC("supported HT rate 0x%X %X "
+			"rxon flags 0x%X operation mode :0x%X "
+			"extension channel offset 0x%x "
+			"control chan %d\n",
+			priv->active_rate_ht[0], priv->active_rate_ht[1],
+			rxon->flags, ht_info->operating_mode,
+			ht_info->extension_chan_offset, ht_info->control_chan);
+	return;
+}
+void iwl4965_set_ht_add_station(struct iwl_priv *priv,
+				u8 index, u8 need_to_lock)
+{
+	__le32 sta_flags;
+	unsigned long flags = 0;
+	u8 i = index;
+	struct sta_ht_info *ht_info = &priv->current_assoc_ht;
+
+	if (need_to_lock)
+		spin_lock_irqsave(&priv->sta_lock, flags);
+
+	priv->current_channel_width = IWL_CHANNEL_WIDTH_20MHZ;
+	if (!ht_info->is_ht)
+		goto done;
+
+	sta_flags = priv->stations[i].sta.station_flags;
+
+	if (ht_info->tx_mimo_ps_mode == IWL_MIMO_PS_DYNAMIC)
+		sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK;
+	else
+		sta_flags &= ~STA_FLG_RTS_MIMO_PROT_MSK;
+
+	sta_flags |= cpu_to_le32(
+		(u32)ht_info->ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS);
+
+	sta_flags |= cpu_to_le32(
+		(u32)ht_info->mpdu_density << STA_FLG_AGG_MPDU_DENSITY_POS);
+
+	sta_flags &= (~STA_FLG_FAT_EN_MSK);
+	ht_info->tx_chan_width = IWL_CHANNEL_WIDTH_20MHZ;
+	ht_info->chan_width_cap = IWL_CHANNEL_WIDTH_20MHZ;
+
+	if (iwl_is_fat_tx_allowed(priv, ht_info)) {
+		sta_flags |= STA_FLG_FAT_EN_MSK;
+		ht_info->chan_width_cap = IWL_CHANNEL_WIDTH_40MHZ;
+		if (ht_info->supported_chan_width == IWL_CHANNEL_WIDTH_40MHZ)
+			ht_info->tx_chan_width = IWL_CHANNEL_WIDTH_40MHZ;
+	}
+	priv->current_channel_width = ht_info->tx_chan_width;
+
+	priv->stations[i].sta.station_flags = sta_flags;
+
+ done:
+	if (need_to_lock)
+		spin_unlock_irqrestore(&priv->sta_lock, flags);
+
+	return;
+}
+
+#ifdef CONFIG_IWLWIFI_HT_AGG
+
+static void iwl4965_sta_modify_add_ba_tid(struct iwl_priv *priv,
+					  int sta_id, int tid, u16 ssn)
+{
+	unsigned long lock_flags;
+
+	spin_lock_irqsave(&priv->sta_lock, lock_flags);
+	priv->stations[sta_id].sta.station_flags_msk = 0;
+	priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK;
+	priv->stations[sta_id].sta.add_immediate_ba_tid = (u8)tid;
+	priv->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn);
+	priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+	spin_unlock_irqrestore(&priv->sta_lock, lock_flags);
+	iwl_send_add_station(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
+}
+
+static void iwl4965_sta_modify_del_ba_tid(struct iwl_priv *priv,
+					  int sta_id, int tid)
+{
+	unsigned long lock_flags;
+
+	spin_lock_irqsave(&priv->sta_lock, lock_flags);
+	priv->stations[sta_id].sta.station_flags_msk = 0;
+	priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK;
+	priv->stations[sta_id].sta.remove_immediate_ba_tid = (u8)tid;
+	priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+	spin_unlock_irqrestore(&priv->sta_lock, lock_flags);
+	iwl_send_add_station(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
+}
+
+static const u16 default_tid_to_ac[] = {
+	IWL_TX_QUEUE_AC0,
+	IWL_TX_QUEUE_AC1,
+	IWL_TX_QUEUE_AC1,
+	IWL_TX_QUEUE_AC0,
+	IWL_TX_QUEUE_AC2,
+	IWL_TX_QUEUE_AC2,
+	IWL_TX_QUEUE_AC3,
+	IWL_TX_QUEUE_AC3,
+	IWL_TX_QUEUE_NONE,
+	IWL_TX_QUEUE_NONE,
+	IWL_TX_QUEUE_NONE,
+	IWL_TX_QUEUE_NONE,
+	IWL_TX_QUEUE_NONE,
+	IWL_TX_QUEUE_NONE,
+	IWL_TX_QUEUE_NONE,
+	IWL_TX_QUEUE_NONE,
+	IWL_TX_QUEUE_AC3
+};
+
+static int iwl_txq_ctx_activate_free(struct iwl_priv *priv)
+{
+	int txq_id;
+	for (txq_id = 0; txq_id < IWL4965_NUM_QUEUES; txq_id++)
+		if (!test_and_set_bit(txq_id, &priv->txq_ctx_active_msk))
+			return txq_id;
+	return -1;
+}
+
+int iwl_mac_ht_tx_agg_start(struct ieee80211_hw *hw, u8 *da, u16 tid,
+			    u16 *start_seq_num)
+{
+
+	struct iwl_priv *priv = hw->priv;
+	int sta_id;
+	int tx_fifo;
+	int txq_id;
+	int ssn = -1;
+	unsigned long flags;
+	struct iwl_tid_data *tid_data;
+
+	if (likely(tid < ARRAY_SIZE(default_tid_to_ac)))
+		tx_fifo = default_tid_to_ac[tid];
+	else
+		return -EINVAL;
+
+	IWL_WARNING("iwl-AGG iwl_mac_ht_tx_agg_start on da=" MAC_FMT " tid=%d\n",
+		    MAC_ARG(da), tid);
+
+	sta_id = iwl_hw_find_station(priv, da);
+	if (sta_id == IWL_INVALID_STATION)
+		return -ENXIO;
+
+	txq_id = iwl_txq_ctx_activate_free(priv);
+	if (txq_id == -1)
+		return -ENXIO;
+
+	spin_lock_irqsave(&priv->sta_lock, flags);
+	tid_data = &priv->stations[sta_id].tid[tid];
+	ssn = SEQ_TO_SN(tid_data->seq_number);
+	tid_data->agg.txq_id = txq_id;
+	spin_unlock_irqrestore(&priv->sta_lock, flags);
+
+	*start_seq_num = ssn;
+	iwl4965_ba_status(priv, tid, BA_STATUS_ACTIVE);
+	return iwl4965_tx_queue_agg_enable(priv, txq_id, tx_fifo,
+					   sta_id, tid, ssn);
+}
+
+
+int iwl_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, u8 *da, u16 tid,
+			   int generator)
+{
+
+	struct iwl_priv *priv = hw->priv;
+	int tx_fifo_id, txq_id, sta_id, ssn = -1;
+	struct iwl_tid_data *tid_data;
+	int rc;
+	if (!da) {
+		IWL_ERROR("%s: da = NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (likely(tid < ARRAY_SIZE(default_tid_to_ac)))
+		tx_fifo_id = default_tid_to_ac[tid];
+	else
+		return -EINVAL;
+
+	sta_id = iwl_hw_find_station(priv, da);
+
+	if (sta_id == IWL_INVALID_STATION)
+		return -ENXIO;
+
+	tid_data = &priv->stations[sta_id].tid[tid];
+	ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4;
+	txq_id = tid_data->agg.txq_id;
+
+	rc = iwl4965_tx_queue_agg_disable(priv, txq_id, ssn, tx_fifo_id);
+	/* FIXME: need more safe way to handle error condition */
+	if (rc)
+		return rc;
+
+	iwl4965_ba_status(priv, tid, BA_STATUS_INITIATOR_DELBA);
+	IWL_DEBUG_INFO("iwl_mac_ht_tx_agg_stop on da=" MAC_FMT " tid=%d\n",
+		    MAC_ARG(da), tid);
+
+	return 0;
+}
+
+int iwl_mac_ht_rx_agg_start(struct ieee80211_hw *hw, u8 *da,
+			    u16 tid, u16 start_seq_num)
+{
+	struct iwl_priv *priv = hw->priv;
+	int sta_id;
+
+	IWL_WARNING("iwl-AGG iwl_mac_ht_rx_agg_start on da=" MAC_FMT
+		    " tid=%d\n", MAC_ARG(da), tid);
+	sta_id = iwl_hw_find_station(priv, da);
+	iwl4965_sta_modify_add_ba_tid(priv, sta_id, tid, start_seq_num);
+	return 0;
+}
+
+int iwl_mac_ht_rx_agg_stop(struct ieee80211_hw *hw, u8 *da,
+			   u16 tid, int generator)
+{
+	struct iwl_priv *priv = hw->priv;
+	int sta_id;
+
+	IWL_WARNING("iwl-AGG iwl_mac_ht_rx_agg_stop on da=" MAC_FMT " tid=%d\n",
+		     MAC_ARG(da), tid);
+	sta_id = iwl_hw_find_station(priv, da);
+	iwl4965_sta_modify_del_ba_tid(priv, sta_id, tid);
+	return 0;
+}
+
+#endif /* CONFIG_IWLWIFI_HT_AGG */
+#endif /* CONFIG_IWLWIFI_HT */
+
+/* Set up 4965-specific Rx frame reply handlers */
+void iwl_hw_rx_handler_setup(struct iwl_priv *priv)
+{
+	/* Legacy Rx frames */
+	priv->rx_handlers[REPLY_4965_RX] = iwl4965_rx_reply_rx;
+
+	/* High-throughput (HT) Rx frames */
+	priv->rx_handlers[REPLY_RX_PHY_CMD] = iwl4965_rx_reply_rx_phy;
+	priv->rx_handlers[REPLY_RX_MPDU_CMD] = iwl4965_rx_reply_rx;
+
+	priv->rx_handlers[MISSED_BEACONS_NOTIFICATION] =
+	    iwl4965_rx_missed_beacon_notif;
+
+#ifdef CONFIG_IWLWIFI_HT
+#ifdef CONFIG_IWLWIFI_HT_AGG
+	priv->rx_handlers[REPLY_COMPRESSED_BA] = iwl4965_rx_reply_compressed_ba;
+#endif /* CONFIG_IWLWIFI_AGG */
+#endif /* CONFIG_IWLWIFI */
+}
+
+void iwl_hw_setup_deferred_work(struct iwl_priv *priv)
+{
+	INIT_WORK(&priv->txpower_work, iwl4965_bg_txpower_work);
+	INIT_WORK(&priv->statistics_work, iwl4965_bg_statistics_work);
+#ifdef CONFIG_IWLWIFI_SENSITIVITY
+	INIT_WORK(&priv->sensitivity_work, iwl4965_bg_sensitivity_work);
+#endif
+#ifdef CONFIG_IWLWIFI_HT
+#ifdef CONFIG_IWLWIFI_HT_AGG
+	INIT_WORK(&priv->agg_work, iwl4965_bg_agg_work);
+#endif /* CONFIG_IWLWIFI_AGG */
+#endif /* CONFIG_IWLWIFI_HT */
+	init_timer(&priv->statistics_periodic);
+	priv->statistics_periodic.data = (unsigned long)priv;
+	priv->statistics_periodic.function = iwl4965_bg_statistics_periodic;
+}
+
+void iwl_hw_cancel_deferred_work(struct iwl_priv *priv)
+{
+	del_timer_sync(&priv->statistics_periodic);
+
+	cancel_delayed_work(&priv->init_alive_start);
+}
+
+struct pci_device_id iwl_hw_card_ids[] = {
+	{0x8086, 0x4229, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x8086, 0x4230, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0}
+};
+
+int iwl_eeprom_aqcuire_semaphore(struct iwl_priv *priv)
+{
+	u16 count;
+	int rc;
+
+	for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) {
+		iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+			CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
+		rc = iwl_poll_bit(priv, CSR_HW_IF_CONFIG_REG,
+					CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
+					CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
+					EEPROM_SEM_TIMEOUT);
+		if (rc >= 0) {
+			IWL_DEBUG_IO("Aqcuired semaphore after %d tries.\n",
+				count+1);
+			return rc;
+		}
+	}
+
+	return rc;
+}
+
+inline void iwl_eeprom_release_semaphore(struct iwl_priv *priv)
+{
+	iwl_clear_bit(priv, CSR_HW_IF_CONFIG_REG,
+		CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
+}
+
+
+MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
diff -ruN linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-4965.h linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-4965.h
--- linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-4965.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-4965.h	2007-07-31 15:29:35.000000000 +0200
@@ -0,0 +1,369 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+#ifndef __iwl_4965_h__
+#define __iwl_4965_h__
+
+struct iwl_priv;
+struct sta_ht_info;
+
+#if IWL != 4965
+/*
+ * In non IWL == 4965 builds, these must build to nothing in order to allow
+ * the common code to not have several #if IWL == XXXX / #endif blocks
+ */
+static inline void iwl_eeprom_release_semaphore(struct iwl_priv *priv) {}
+
+static inline void iwl4965_add_station(struct iwl_priv *priv, const u8 * addr,
+				       int is_ap) {}
+static inline void iwl4965_set_rxon_ht(struct iwl_priv *priv,
+				       struct sta_ht_info *ht_info) {}
+
+static inline void iwl4965_set_rxon_chain(struct iwl_priv *priv) {}
+static inline int iwl4965_tx_cmd(struct iwl_priv *priv,
+				 struct iwl_cmd *out_cmd,
+				 u8 sta_id, dma_addr_t txcmd_phys,
+				 struct ieee80211_hdr *hdr, u8 hdr_len,
+				 struct ieee80211_tx_control *ctrl,
+				 void *sta_in) { return 0; }
+static inline int iwl4965_tx_queue_update_wr_ptr(struct iwl_priv *priv,
+						 struct iwl_tx_queue *txq,
+						 u16 len) { return 0; }
+static inline int iwl4965_init_hw_rates(struct iwl_priv *priv,
+					struct ieee80211_rate *rates)
+{ return 0; }
+static inline int iwl4965_alive_notify(struct iwl_priv *priv) { return 0; }
+static inline void iwl4965_update_rate_scaling(struct iwl_priv *priv,
+					       u8 mode) {}
+static inline void iwl4965_set_ht_add_station(struct iwl_priv *priv, u8 index,
+					      u8 need_to_lock) {}
+static inline void iwl4965_chain_noise_reset(struct iwl_priv *priv) {}
+static inline void iwl4965_init_sensitivity(struct iwl_priv *priv, u8 flags,
+					    u8 force) {}
+static inline int iwl4965_set_fat_chan_info(struct iwl_priv *priv, int phymode,
+				int channel,
+				const struct iwl_eeprom_channel *eeprom_ch,
+				u8 fat_extension_channel) { return 0; }
+static inline void iwl4965_rf_kill_ct_config(struct iwl_priv *priv) {}
+#else				/* IWL == 4965 */
+/*
+ * Forward declare iwl-4965.c functions for iwl-base.c
+ */
+extern int iwl_eeprom_aqcuire_semaphore(struct iwl_priv *priv);
+extern void iwl_eeprom_release_semaphore(struct iwl_priv *priv);
+
+extern int iwl4965_tx_queue_update_wr_ptr(struct iwl_priv *priv,
+					  struct iwl_tx_queue *txq,
+					  u16 byte_cnt);
+extern void iwl4965_add_station(struct iwl_priv *priv, const u8 * addr,
+				int is_ap);
+extern void iwl4965_set_rxon_ht(struct iwl_priv *priv,
+				struct sta_ht_info *ht_info);
+
+extern void iwl4965_set_rxon_chain(struct iwl_priv *priv);
+extern int iwl4965_tx_cmd(struct iwl_priv *priv, struct iwl_cmd *out_cmd,
+			  u8 sta_id, dma_addr_t txcmd_phys,
+			  struct ieee80211_hdr *hdr, u8 hdr_len,
+			  struct ieee80211_tx_control *ctrl, void *sta_in);
+extern int iwl4965_init_hw_rates(struct iwl_priv *priv,
+				 struct ieee80211_rate *rates);
+extern int iwl4965_alive_notify(struct iwl_priv *priv);
+extern void iwl4965_update_rate_scaling(struct iwl_priv *priv, u8 mode);
+extern void iwl4965_set_ht_add_station(struct iwl_priv *priv,
+				       u8 index, u8 need_to_lock);
+
+extern void iwl4965_chain_noise_reset(struct iwl_priv *priv);
+extern void iwl4965_init_sensitivity(struct iwl_priv *priv, u8 flags,
+				     u8 force);
+extern int iwl4965_set_fat_chan_info(struct iwl_priv *priv, int phymode,
+				int channel,
+				const struct iwl_eeprom_channel *eeprom_ch,
+				u8 fat_extension_channel);
+extern void iwl4965_rf_kill_ct_config(struct iwl_priv *priv);
+
+#ifdef CONFIG_IWLWIFI_HT
+#ifdef CONFIG_IWLWIFI_HT_AGG
+extern int iwl_mac_ht_tx_agg_start(struct ieee80211_hw *hw, u8 *da,
+				   u16 tid, u16 *start_seq_num);
+extern int iwl_mac_ht_rx_agg_start(struct ieee80211_hw *hw, u8 *da,
+				   u16 tid, u16 start_seq_num);
+extern int iwl_mac_ht_rx_agg_stop(struct ieee80211_hw *hw, u8 *da,
+				  u16 tid, int generator);
+extern int iwl_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, u8 *da,
+				  u16 tid, int generator);
+extern void iwl4965_turn_off_agg(struct iwl_priv *priv, u8 tid);
+#endif /* CONFIG_IWLWIFI_HT_AGG */
+#endif /*CONFIG_IWLWIFI_HT */
+/* Structures, enum, and defines specific to the 4965 */
+
+#define IWL4965_KW_SIZE 0x1000	/*4k */
+
+struct iwl_kw {
+	dma_addr_t dma_addr;
+	void *v_addr;
+	size_t size;
+};
+
+#define TID_QUEUE_CELL_SPACING 50	/*mS */
+#define TID_QUEUE_MAX_SIZE     20
+#define TID_ROUND_VALUE        5	/* mS */
+#define TID_MAX_LOAD_COUNT     8
+
+#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING)
+#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y))
+
+#define TID_ALL_ENABLED		0x7f
+#define TID_ALL_SPECIFIED       0xff
+#define TID_AGG_TPT_THREHOLD    0x0
+
+#define IWL_CHANNEL_WIDTH_20MHZ   0
+#define IWL_CHANNEL_WIDTH_40MHZ   1
+
+#define IWL_MIMO_PS_STATIC        0
+#define IWL_MIMO_PS_NONE          3
+#define IWL_MIMO_PS_DYNAMIC       1
+#define IWL_MIMO_PS_INVALID       2
+
+#define IWL_OPERATION_MODE_AUTO     0
+#define IWL_OPERATION_MODE_HT_ONLY  1
+#define IWL_OPERATION_MODE_MIXED    2
+#define IWL_OPERATION_MODE_20MHZ    3
+
+#define IWL_EXT_CHANNEL_OFFSET_AUTO   0
+#define IWL_EXT_CHANNEL_OFFSET_ABOVE  1
+#define IWL_EXT_CHANNEL_OFFSET_       2
+#define IWL_EXT_CHANNEL_OFFSET_BELOW  3
+#define IWL_EXT_CHANNEL_OFFSET_MAX    4
+
+#define NRG_NUM_PREV_STAT_L     20
+#define NUM_RX_CHAINS           (3)
+
+struct iwl_traffic_load {
+	unsigned long time_stamp;
+	u32 packet_count[TID_QUEUE_MAX_SIZE];
+	u8 queue_count;
+	u8 head;
+	u32 total;
+};
+
+#ifdef CONFIG_IWLWIFI_HT_AGG
+struct iwl_agg_control {
+	unsigned long next_retry;
+	u32 wait_for_agg_status;
+	u32 tid_retry;
+	u32 requested_ba;
+	u32 granted_ba;
+	u8 auto_agg;
+	u32 tid_traffic_load_threshold;
+	u32 ba_timeout;
+	struct iwl_traffic_load traffic_load[TID_MAX_LOAD_COUNT];
+};
+#endif				/*CONFIG_IWLWIFI_HT_AGG */
+
+struct iwl_lq_mngr {
+#ifdef CONFIG_IWLWIFI_HT_AGG
+	struct iwl_agg_control agg_ctrl;
+#endif
+	spinlock_t lock;
+	s32 max_window_size;
+	struct iwl_rate_scaling_cmd scale_rate_cmd;
+	s32 *expected_tpt;
+	u8 *next_higher_rate;
+	u8 *next_lower_rate;
+	unsigned long stamp;
+	unsigned long stamp_last;
+	u32 flush_time;
+	u32 tx_packets;
+	u8 lq_ready;
+};
+
+
+/* Sensitivity and chain noise calibration */
+#define INTERFERENCE_DATA_AVAILABLE	__constant_cpu_to_le32(1)
+#define INITIALIZATION_VALUE		0xFFFF
+#define CAL_NUM_OF_BEACONS		20
+#define MAXIMUM_ALLOWED_PATHLOSS	15
+
+/* Param table within SENSITIVITY_CMD */
+#define HD_MIN_ENERGY_CCK_DET_INDEX                 (0)
+#define HD_MIN_ENERGY_OFDM_DET_INDEX                (1)
+#define HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX          (2)
+#define HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX      (3)
+#define HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX      (4)
+#define HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX          (5)
+#define HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX      (6)
+#define HD_BARKER_CORR_TH_ADD_MIN_INDEX             (7)
+#define HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX         (8)
+#define HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX          (9)
+#define HD_OFDM_ENERGY_TH_IN_INDEX                  (10)
+
+#define SENSITIVITY_CMD_CONTROL_DEFAULT_TABLE	__constant_cpu_to_le16(0)
+#define SENSITIVITY_CMD_CONTROL_WORK_TABLE	__constant_cpu_to_le16(1)
+
+#define CHAIN_NOISE_MAX_DELTA_GAIN_CODE 3
+
+#define MAX_FA_OFDM  50
+#define MIN_FA_OFDM  5
+#define MAX_FA_CCK   50
+#define MIN_FA_CCK   5
+
+#define NRG_MIN_CCK  97
+#define NRG_MAX_CCK  0
+
+#define AUTO_CORR_MIN_OFDM        85
+#define AUTO_CORR_MIN_OFDM_MRC    170
+#define AUTO_CORR_MIN_OFDM_X1     105
+#define AUTO_CORR_MIN_OFDM_MRC_X1 220
+#define AUTO_CORR_MAX_OFDM        120
+#define AUTO_CORR_MAX_OFDM_MRC    210
+#define AUTO_CORR_MAX_OFDM_X1     140
+#define AUTO_CORR_MAX_OFDM_MRC_X1 270
+#define AUTO_CORR_STEP_OFDM       1
+
+#define AUTO_CORR_MIN_CCK      (125)
+#define AUTO_CORR_MAX_CCK      (200)
+#define AUTO_CORR_MIN_CCK_MRC  200
+#define AUTO_CORR_MAX_CCK_MRC  400
+#define AUTO_CORR_STEP_CCK     3
+#define AUTO_CORR_MAX_TH_CCK   160
+
+#define NRG_ALG                0
+#define AUTO_CORR_ALG          1
+#define NRG_DIFF               2
+#define NRG_STEP_CCK           2
+#define NRG_MARGIN             8
+#define MAX_NUMBER_CCK_NO_FA 100
+
+#define AUTO_CORR_CCK_MIN_VAL_DEF    (125)
+
+#define CHAIN_A             0
+#define CHAIN_B             1
+#define CHAIN_C             2
+#define CHAIN_NOISE_DELTA_GAIN_INIT_VAL 4
+#define ALL_BAND_FILTER			0xFF00
+#define IN_BAND_FILTER			0xFF
+#define MIN_AVERAGE_NOISE_MAX_VALUE	0xFFFFFFFF
+
+enum iwl_false_alarm_state {
+	IWL_FA_TOO_MANY = 0,
+	IWL_FA_TOO_FEW = 1,
+	IWL_FA_GOOD_RANGE = 2,
+};
+
+enum iwl_chain_noise_state {
+	IWL_CHAIN_NOISE_ALIVE = 0,  /* must be 0 */
+	IWL_CHAIN_NOISE_ACCUMULATE = 1,
+	IWL_CHAIN_NOISE_CALIBRATED = 2,
+};
+
+enum iwl_sensitivity_state {
+	IWL_SENS_CALIB_ALLOWED = 0,
+	IWL_SENS_CALIB_NEED_REINIT = 1,
+};
+
+enum iwl_calib_enabled_state {
+	IWL_CALIB_DISABLED = 0,  /* must be 0 */
+	IWL_CALIB_ENABLED = 1,
+};
+
+struct statistics_general_data {
+	u32 beacon_silence_rssi_a;
+	u32 beacon_silence_rssi_b;
+	u32 beacon_silence_rssi_c;
+	u32 beacon_energy_a;
+	u32 beacon_energy_b;
+	u32 beacon_energy_c;
+};
+
+/* Sensitivity calib data */
+struct iwl_sensitivity_data {
+	u32 auto_corr_ofdm;
+	u32 auto_corr_ofdm_mrc;
+	u32 auto_corr_ofdm_x1;
+	u32 auto_corr_ofdm_mrc_x1;
+	u32 auto_corr_cck;
+	u32 auto_corr_cck_mrc;
+
+	u32 last_bad_plcp_cnt_ofdm;
+	u32 last_fa_cnt_ofdm;
+	u32 last_bad_plcp_cnt_cck;
+	u32 last_fa_cnt_cck;
+
+	u32 nrg_curr_state;
+	u32 nrg_prev_state;
+	u32 nrg_value[10];
+	u8  nrg_silence_rssi[NRG_NUM_PREV_STAT_L];
+	u32 nrg_silence_ref;
+	u32 nrg_energy_idx;
+	u32 nrg_silence_idx;
+	u32 nrg_th_cck;
+	s32 nrg_auto_corr_silence_diff;
+	u32 num_in_cck_no_fa;
+	u32 nrg_th_ofdm;
+
+	u8 state;
+};
+
+/* Chain noise (differential Rx gain) calib data */
+struct iwl_chain_noise_data {
+	u8 state;
+	u16 beacon_count;
+	u32 chain_noise_a;
+	u32 chain_noise_b;
+	u32 chain_noise_c;
+	u32 chain_signal_a;
+	u32 chain_signal_b;
+	u32 chain_signal_c;
+	u8 disconn_array[NUM_RX_CHAINS];
+	u8 delta_gain_code[NUM_RX_CHAINS];
+	u8 radio_write;
+};
+
+/* IWL4965 */
+#define RATE_MCS_CODE_MSK 0x7
+#define RATE_MCS_MIMO_POS 3
+#define RATE_MCS_MIMO_MSK 0x8
+#define RATE_MCS_HT_DUP_POS 5
+#define RATE_MCS_HT_DUP_MSK 0x20
+#define RATE_MCS_FLAGS_POS 8
+#define RATE_MCS_HT_POS 8
+#define RATE_MCS_HT_MSK 0x100
+#define RATE_MCS_CCK_POS 9
+#define RATE_MCS_CCK_MSK 0x200
+#define RATE_MCS_GF_POS 10
+#define RATE_MCS_GF_MSK 0x400
+
+#define RATE_MCS_FAT_POS 11
+#define RATE_MCS_FAT_MSK 0x800
+#define RATE_MCS_DUP_POS 12
+#define RATE_MCS_DUP_MSK 0x1000
+#define RATE_MCS_SGI_POS 13
+#define RATE_MCS_SGI_MSK 0x2000
+
+#define	EEPROM_SEM_TIMEOUT 10
+#define EEPROM_SEM_RETRY_LIMIT 1000
+
+#endif				/* IWL == 4965 */
+#endif				/* __iwl_4965_h__ */
diff -ruN linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-base.c linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-base.c
--- linux-2.6.22/drivers/net/wireless/mac80211/iwlwifi/iwl-base.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.22-iwlwifi/drivers/net/wireless/mac80211/iwlwifi/iwl-base.c	2007-07-31 15:29:35.000000000 +0200
@@ -0,0 +1,9431 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved.
+ *
+ * Portions of this file are derived from the ipw3945 project, as well
+ * as portions of the ieee80211 subsystem header files.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+/*
+ * NOTE:  This file (iwl-base.c) is used to build to multiple hardware targets
+ * by defining IWL to either 3945 or 4965.  The Makefile used when building
+ * the base targets will create base-3945.o and base-4965.o
+ *
+ * The eventual goal is to move as many of the #if IWL / #endif blocks out of
+ * this file and into the hardware specific implementation files (iwl-XXXX.c)
+ * and leave only the common (non #ifdef sprinkled) code in this file
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <linux/firmware.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+
+#include <net/ieee80211_radiotap.h>
+#include <net/mac80211.h>
+
+#include <asm/div64.h>
+
+#include "iwlwifi.h"
+#include "iwl-helpers.h"
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+u32 iwl_debug_level;
+#endif
+
+/******************************************************************************
+ *
+ * module boiler plate
+ *
+ ******************************************************************************/
+
+/* module parameters */
+int iwl_param_disable_hw_scan = 0;
+int iwl_param_debug = 0;
+int iwl_param_disable = 0;      /* def: enable radio */
+int iwl_param_antenna = 0;      /* def: 0 = both antennas (use diversity) */
+int iwl_param_hwcrypto = 0;     /* def: using software encryption */
+int iwl_param_qos_enable = 1;
+
+/*
+ * module name, copyright, version, etc.
+ * NOTE: DRV_NAME is defined in iwlwifi.h for use by iwl-debug.h and printk
+ */
+
+#if IWL == 3945
+#define DRV_DESCRIPTION	\
+"Intel(R) PRO/Wireless 3945ABG/BG Network Connection driver for Linux"
+#elif IWL == 4965
+#define DRV_DESCRIPTION	\
+"Intel(R) Wireless WiFi Link 4965AGN driver for Linux"
+#else
+BUILD_BUG()
+#endif
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+#define VD "d"
+#else
+#define VD
+#endif
+
+#ifdef CONFIG_IWLWIFI_SPECTRUM_MEASUREMENY
+#define VS "s"
+#else
+#define VS
+#endif
+
+#define IWLWIFI_VERSION "0.1.6" VD VS
+#define DRV_COPYRIGHT	"Copyright(c) 2003-2007 Intel Corporation"
+#define DRV_VERSION     IWLWIFI_VERSION
+
+/* Change firmware file name, using "-" and incrementing number,
+ *   *only* when uCode interface or architecture changes so that it
+ *   is not compatible with earlier drivers.
+ * This number will also appear in << 8 position of 1st dword of uCode file */
+#define IWL3945_UCODE_API "-1"
+#define IWL4965_UCODE_API "-1"
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR(DRV_COPYRIGHT);
+MODULE_LICENSE("GPL");
+
+/*************** DMA-QUEUE-GENERAL-FUNCTIONS  *****
+ * DMA services
+ *
+ * Theory of operation
+ *
+ * A queue is a circular buffers with 'Read' and 'Write' pointers.
+ * 2 empty entries always kept in the buffer to protect from overflow.
+ *
+ * For Tx queue, there are low mark and high mark limits. If, after queuing
+ * the packet for Tx, free space become < low mark, Tx queue stopped. When
+ * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
+ * Tx queue resumed.
+ *
+ * The IPW operates with six queues, one receive queue in the device's
+ * sram, one transmit queue for sending commands to the device firmware,
+ * and four transmit queues for data.
+ *
+ * The four transmit queues allow for performing quality of service (qos)
+ * transmissions as per the 802.11 protocol.  Currently Linux does not
+ * provide a mechanism to the user for utilizing prioritized queues, so
+ * we only utilize the first data transmit queue (queue1).
+ ***************************************************/
+
+static int iwl_queue_space(const struct iwl_queue *q)
+{
+	int s = q->last_used - q->first_empty;
+	if (q->last_used > q->first_empty)
+		s -= q->n_bd;
+
+	if (s <= 0)
+		s += q->n_window;
+	/* keep some reserve to not confuse empty and full situations */
+	s -= 2;
+	if (s < 0)
+		s = 0;
+	return s;
+}
+
+static inline int iwl_queue_inc_wrap(int index, int n_bd)
+{
+	return (++index == n_bd) ? 0 : index;
+}
+
+static inline int iwl_queue_dec_wrap(int index, int n_bd)
+{
+	return (index == 0) ? n_bd - 1 : index - 1;
+}
+
+static inline int x2_queue_used(const struct iwl_queue *q, int i)
+{
+	return q->first_empty > q->last_used ?
+	    (i >= q->last_used && i < q->first_empty) :
+	    !(i < q->last_used && i >= q->first_empty);
+}
+
+static inline u8 get_next_cmd_index(struct iwl_queue *q, u32 index, int is_huge)
+{
+	if (is_huge)
+		return q->n_window;
+
+	return (u8) (index % q->n_window);
+}
+
+static int iwl_queue_init(struct iwl_priv *priv, struct iwl_queue *q,
+			  int count, int slots_num, u32 id)
+{
+	q->n_bd = count;
+	q->n_window = slots_num;
+	q->id = id;
+
+	q->low_mark = q->n_window / 4;
+	if (q->low_mark < 4)
+		q->low_mark = 4;
+
+	q->high_mark = q->n_window / 8;
+	if (q->high_mark < 2)
+		q->high_mark = 2;
+
+	q->first_empty = q->last_used = 0;
+
+	return 0;
+}
+
+static int iwl_tx_queue_alloc(struct iwl_priv *priv,
+			      struct iwl_tx_queue *txq, u32 id)
+{
+	struct pci_dev *dev = priv->pci_dev;
+
+	if (id != IWL_CMD_QUEUE_NUM) {
+		txq->txb = kmalloc(sizeof(txq->txb[0]) *
+				   TFD_QUEUE_SIZE_MAX, GFP_ATOMIC);
+		if (!txq->txb) {
+			IWL_ERROR("kmalloc for auxilary BD "
+				  "structures failed\n");
+			goto error;
+		}
+	} else
+		txq->txb = NULL;
+
+	txq->bd = pci_alloc_consistent(dev,
+			sizeof(txq->bd[0]) * TFD_QUEUE_SIZE_MAX,
+			&txq->q.dma_addr);
+
+	if (!txq->bd) {
+		IWL_ERROR("pci_alloc_consistent(%zd) failed\n",
+			  sizeof(txq->bd[0]) * TFD_QUEUE_SIZE_MAX);
+		goto error;
+	}
+	txq->q.id = id;
+
+	return 0;
+
+ error:
+	if (txq->txb) {
+		kfree(txq->txb);
+		txq->txb = NULL;
+	}
+
+	return -ENOMEM;
+}
+
+int iwl_tx_queue_init(struct iwl_priv *priv,
+		      struct iwl_tx_queue *txq, int slots_num, u32 txq_id)
+{
+	struct pci_dev *dev = priv->pci_dev;
+	int len;
+	int rc = 0;
+
+	/* alocate command space + one big command for scan since scan
+	 * command is very huge the system will not have two scan at the
+	 * same time */
+	len = sizeof(struct iwl_cmd) * slots_num;
+	if (txq_id == IWL_CMD_QUEUE_NUM);
+		len +=  IWL_MAX_SCAN_SIZE;
+	txq->cmd = pci_alloc_consistent(dev, len, &txq->dma_addr_cmd);
+	if (!txq->cmd)
+		return -ENOMEM;
+
+	rc = iwl_tx_queue_alloc(priv, txq, txq_id);
+	if (rc) {
+		pci_free_consistent(dev, len, txq->cmd, txq->dma_addr_cmd);
+
+		return -ENOMEM;
+	}
+
+	txq->need_update = 0;
+	iwl_queue_init(priv, &txq->q, TFD_QUEUE_SIZE_MAX, slots_num, txq_id);
+	iwl_hw_tx_queue_init(priv, txq);
+
+	return 0;
+}
+
+/**
+ * iwl_tx_queue_free - Deallocate DMA queue.
+ * @txq: Transmit queue to deallocate.
+ *
+ * Empty queue by removing and destroying all BD's.
+ * Free all buffers.  txq itself is not freed.
+ *
+ */
+void iwl_tx_queue_free(struct iwl_priv *priv, struct iwl_tx_queue *txq)
+{
+	struct iwl_queue *q = &txq->q;
+	struct pci_dev *dev = priv->pci_dev;
+	int len;
+
+	if (q->n_bd == 0)
+		return;
+
+	/* first, empty all BD's */
+	for (; q->first_empty != q->last_used;
+	     q->last_used = iwl_queue_inc_wrap(q->last_used, q->n_bd)) {
+		iwl_hw_tx_queue_free_tfd(priv, txq);
+	}
+
+	len = sizeof(txq->cmd[0]) * q->n_window;
+	if (q->id == IWL_CMD_QUEUE_NUM);
+                len +=  IWL_MAX_SCAN_SIZE;
+	pci_free_consistent(dev, len, txq->cmd, txq->dma_addr_cmd);
+
+	/* free buffers belonging to queue itself */
+	if (txq->q.n_bd)
+		pci_free_consistent(dev, sizeof(struct iwl_tfd_frame) *
+				    txq->q.n_bd, txq->bd, txq->q.dma_addr);
+
+	if (txq->txb) {
+		kfree(txq->txb);
+		txq->txb = NULL;
+	}
+
+	/* 0 fill whole structure */
+	memset(txq, 0, sizeof(*txq));
+}
+
+const u8 BROADCAST_ADDR[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+/*************** STATION TABLE MANAGEMENT ****
+ *
+ * NOTE:  This needs to be overhauled to better synchronize between
+ * how the iwl-4965.c is using iwl_hw_find_station vs. iwl-3945.c
+ *
+ * mac80211 should also be examined to determine if sta_info is duplicating
+ * the functionality provided here
+ */
+
+/**************************************************************/
+
+static u8 iwl_remove_station(struct iwl_priv *priv, const u8 * bssid, int is_ap)
+{
+	int index = IWL_INVALID_STATION;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->sta_lock, flags);
+	if (is_ap) {
+		index = IWL_AP_ID;
+		if ((priv->stations[index].used))
+			priv->stations[index].used = 0;
+	} else if (is_broadcast_ether_addr(bssid)) {
+		index = IWL_BROADCAST_ID;
+		if ((priv->stations[index].used))
+			priv->stations[index].used = 0;
+	} else
+		for (i = IWL_STA_ID; i < priv->num_stations + IWL_STA_ID; i++) {
+			if (priv->stations[i].used &&
+			    !compare_ether_addr(
+				    priv->stations[i].sta.sta.addr, bssid)) {
+				index = i;
+				priv->stations[index].used = 0;
+				break;
+			}
+		}
+
+	if (index != IWL_INVALID_STATION) {
+		if (priv->num_stations > 0)
+			priv->num_stations--;
+	}
+
+	spin_unlock_irqrestore(&priv->sta_lock, flags);
+	return 0;
+}
+
+static void iwl_clear_stations_table(struct iwl_priv *priv)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->sta_lock, flags);
+
+	priv->num_stations = 0;
+	memset(priv->stations, 0,
+	       IWL_STATION_COUNT * sizeof(struct iwl_station_entry));
+	spin_unlock_irqrestore(&priv->sta_lock, flags);
+}
+
+u8 iwl_add_station(struct iwl_priv *priv, const u8 * bssid, int is_ap, u8 flags)
+{
+	int i = IWL_STATION_COUNT;
+	int index = IWL_INVALID_STATION;
+	struct iwl_station_entry *station;
+	unsigned long flags_spin;
+
+	spin_lock_irqsave(&priv->sta_lock, flags_spin);
+	if (is_ap) {
+		index = IWL_AP_ID;
+		if (priv->stations[index].used &&
+		    !compare_ether_addr(priv->stations[index].sta.sta.addr,
+					bssid))
+			goto done;
+	} else if (is_broadcast_ether_addr(bssid)) {
+		index = IWL_BROADCAST_ID;
+		if (priv->stations[index].used &&
+		    !compare_ether_addr(priv->stations[index].sta.sta.addr,
+					bssid))
+			goto done;
+	} else
+		for (i = IWL_STA_ID; i < priv->num_stations + IWL_STA_ID; i++) {
+			if (priv->stations[i].used &&
+			    !compare_ether_addr(priv->stations[i].sta.sta.addr,
+						bssid))
+				goto done;
+
+			if (!priv->stations[i].used &&
+			    index == IWL_INVALID_STATION)
+				index = i;
+		}
+
+	if (index != IWL_INVALID_STATION)
+		i = index;
+
+	if (i == IWL_STATION_COUNT) {
+		index = IWL_INVALID_STATION;
+		goto done;
+	}
+
+	IWL_DEBUG_ASSOC("Adding STA ID %d: " MAC_FMT "\n", i, MAC_ARG(bssid));
+	station = &priv->stations[i];
+
+	station->used = 1;
+	memset(&station->sta, 0, sizeof(struct iwl_addsta_cmd));
+	memcpy(station->sta.sta.addr, bssid, ETH_ALEN);
+	station->sta.mode = 0;
+	station->sta.sta.sta_id = i;
+	station->sta.station_flags = 0;
+#if IWL == 3945
+	station->sta.rate_n_flags = cpu_to_le16(
+		(priv->phymode == MODE_IEEE80211A) ? IWL_RATE_6M_PLCP :
+			IWL_RATE_1M_PLCP | priv->hw_setting.cck_flag);
+
+	/* Turn on both antennas for the station... */
+	station->sta.rate_n_flags |= RATE_MCS_ANT_AB_MSK;
+
+	station->sta.station_flags |= STA_FLG_TX_RATE_MSK;
+	station->current_rate.rate_n_flags = le16_to_cpu(
+		station->sta.rate_n_flags);
+#endif
+
+#if IWL == 4965
+#ifdef CONFIG_IWLWIFI_HT
+	if (is_ap) {
+		iwl4965_set_ht_add_station(priv, i, 0);
+		iwl4965_set_rxon_chain(priv);
+	}
+#endif /*CONFIG_IWLWIFI_HT*/
+#endif
+
+	priv->num_stations++;
+	spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
+	iwl_send_add_station(priv, &station->sta, flags);
+	return i;
+
+ done:
+	spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
+	return index;
+}
+
+/*************** DRIVER STATUS FUNCTIONS   *****/
+
+static inline int iwl_is_ready(struct iwl_priv *priv)
+{
+	/* The adapter is 'ready' if READY and GEO_CONFIGURED bits are
+	 * set but EXIT_PENDING is not */
+	return ((priv->status & (STATUS_READY |
+				 STATUS_GEO_CONFIGURED |
+				 STATUS_EXIT_PENDING)) ==
+		(STATUS_READY | STATUS_GEO_CONFIGURED)) ? 1 : 0;
+}
+
+static inline int iwl_is_alive(struct iwl_priv *priv)
+{
+	return (priv->status & STATUS_ALIVE) ? 1 : 0;
+}
+
+static inline int iwl_is_init(struct iwl_priv *priv)
+{
+	return (priv->status & STATUS_INIT) ? 1 : 0;
+}
+
+static inline int iwl_is_ready_rf(struct iwl_priv *priv)
+{
+
+	if (priv->status & STATUS_RF_KILL_MASK)
+		return 0;
+
+	return iwl_is_ready(priv);
+}
+
+/*************** HOST COMMAND QUEUE FUNCTIONS   *****/
+
+#define IWL_CMD(x) case x : return #x
+
+static const char *get_cmd_string(u8 cmd)
+{
+	switch (cmd) {
+		IWL_CMD(SCAN_START_NOTIFICATION);
+		IWL_CMD(SCAN_RESULTS_NOTIFICATION);
+		IWL_CMD(SCAN_COMPLETE_NOTIFICATION);
+		IWL_CMD(STATISTICS_NOTIFICATION);
+		IWL_CMD(REPLY_ALIVE);
+		IWL_CMD(REPLY_ERROR);
+		IWL_CMD(REPLY_RXON_ASSOC);
+		IWL_CMD(REPLY_RXON);
+		IWL_CMD(REPLY_QOS_PARAM);
+		IWL_CMD(REPLY_RXON_TIMING);
+		IWL_CMD(REPLY_ADD_STA);
+		IWL_CMD(REPLY_TX);
+		IWL_CMD(REPLY_BCON);
+		IWL_CMD(REPLY_RATE_SCALE);
+		IWL_CMD(REPLY_LEDS_CMD);
+		IWL_CMD(REPLY_SCAN_ABORT_CMD);
+		IWL_CMD(REPLY_TX_BEACON);
+		IWL_CMD(REPLY_BT_CONFIG);
+		IWL_CMD(REPLY_SCAN_CMD);
+		IWL_CMD(REPLY_TX_PWR_TABLE_CMD);
+		IWL_CMD(REPLY_STATISTICS_CMD);
+		IWL_CMD(REPLY_CARD_STATE_CMD);
+		IWL_CMD(REPLY_TX_LINK_QUALITY_CMD);
+#if IWL == 3945
+		IWL_CMD(REPLY_3945_RX);
+#elif IWL == 4965
+		IWL_CMD(MISSED_BEACONS_NOTIFICATION_TH_CMD);
+		IWL_CMD(REPLY_CT_KILL_CONFIG_CMD);
+		IWL_CMD(SENSITIVITY_CMD);
+		IWL_CMD(REPLY_RX_MPDU_CMD);
+		IWL_CMD(REPLY_RX_PHY_CMD);
+		IWL_CMD(REPLY_4965_RX);
+#endif
+	case POWER_TABLE_CMD:
+		return "POWER_TABLE_CMD";
+	default:
+		return "UNKNOWN";
+
+	}
+}
+
+#define HOST_COMPLETE_TIMEOUT (HZ / 2)
+
+static inline int is_cmd_sync(struct iwl_host_cmd *cmd)
+{
+	return !(cmd->meta.flags & CMD_ASYNC);
+}
+
+static inline int is_cmd_small(struct iwl_host_cmd *cmd)
+{
+	return !(cmd->meta.flags & CMD_SIZE_HUGE);
+}
+
+static inline int cmd_needs_lock(struct iwl_host_cmd *cmd)
+{
+	return !(cmd->meta.flags & CMD_NO_LOCK);
+}
+
+static int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
+{
+	struct iwl_tx_queue *txq = &priv->txq[IWL_CMD_QUEUE_NUM];
+	struct iwl_queue *q = &txq->q;
+	struct iwl_tfd_frame *tfd;
+	u32 *control_flags;
+	struct iwl_cmd *out_cmd;
+	u32 idx = 0;
+	u16 fix_size = (u16) (cmd->meta.len + sizeof(out_cmd->hdr));
+	dma_addr_t phys_addr;
+#if IWL == 3945
+	int pad;
+	u16 count;
+#elif IWL == 4965
+	int rc;
+#endif
+
+	/* If any of the command structures end up being larger than
+	 * the TFD_MAX_PAYLOAD_SIZE, and it sent as a 'small' command then
+	 * we will need to increase the size of the TFD entries */
+	BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE)
+	       && is_cmd_small(cmd));
+	if (iwl_queue_space(q) < (is_cmd_sync(cmd) ? 1 : 2)) {
+		IWL_ERROR("No space for Tx\n");
+		return -ENOSPC;
+	}
+	tfd = &txq->bd[q->first_empty];
+	memset(tfd, 0, sizeof(*tfd));
+
+	control_flags = (u32 *) tfd;
+
+	idx = get_next_cmd_index(q, q->first_empty,
+				 cmd->meta.flags & CMD_SIZE_HUGE);
+	out_cmd = &txq->cmd[idx];
+
+	out_cmd->hdr.cmd = cmd->id;
+	memcpy(&out_cmd->meta, &cmd->meta, sizeof(cmd->meta));
+	memcpy(&out_cmd->cmd.payload, cmd->data, cmd->meta.len);
+
+	/* At this point, the out_cmd now has all of the incoming cmd
+	 * information */
+
+	out_cmd->hdr.flags = 0;
+	out_cmd->hdr.sequence = QUEUE_TO_SEQ(IWL_CMD_QUEUE_NUM) |
+	    INDEX_TO_SEQ(q->first_empty);
+	if (out_cmd->meta.flags & CMD_SIZE_HUGE)
+		out_cmd->hdr.sequence |= SEQ_HUGE_FRAME;
+
+	phys_addr = txq->dma_addr_cmd + sizeof(txq->cmd[0]) * idx +
+	    offsetof(struct iwl_cmd, hdr);
+	iwl_hw_tx_queue_attach_buffer_to_tfd(priv, tfd, phys_addr, fix_size);
+
+#if IWL == 3945
+	pad = U32_PAD(out_cmd->meta.len);
+	count = TFD_CTL_COUNT_GET(*control_flags);
+	*control_flags = TFD_CTL_COUNT_SET(count) | TFD_CTL_PAD_SET(pad);
+#endif
+
+	IWL_DEBUG_HC("Sending command %s (#%x), seq: 0x%04X, "
+		     "%d bytes at %d[%d]:%d\n",
+		     get_cmd_string(out_cmd->hdr.cmd),
+		     out_cmd->hdr.cmd, out_cmd->hdr.sequence,
+		     fix_size, q->first_empty, idx, IWL_CMD_QUEUE_NUM);
+
+	txq->need_update = 1;
+#if IWL == 4965
+	rc = iwl4965_tx_queue_update_wr_ptr(priv, txq, 0);
+	q->first_empty = iwl_queue_inc_wrap(q->first_empty, q->n_bd);
+	iwl_tx_queue_update_write_ptr(priv, txq);
+	return rc;
+#elif IWL == 3945
+	q->first_empty = iwl_queue_inc_wrap(q->first_empty, q->n_bd);
+
+	return iwl_tx_queue_update_write_ptr(priv, txq);
+#endif
+}
+
+int iwl_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
+{
+	int rc;
+	unsigned long flags = 0;
+
+	/* If this is an asynchronous command, and we are in a shutdown
+	 * process then don't let it start */
+	if (!is_cmd_sync(cmd) && (priv->status & STATUS_EXIT_PENDING))
+		return -EBUSY;
+
+	/*
+	 * The following checks are meant to catch programming API misuse
+	 * and not run-time failures due to timing, resource constraint, etc.
+	 */
+
+	/* A command can not be asynchronous AND expect an SKB to be set */
+	if ((cmd->meta.flags & CMD_ASYNC) && (cmd->meta.flags & CMD_WANT_SKB)) {
+		IWL_ERROR("ASYNC && WANT_SKB\n");
+		return -EINVAL;
+	}
+
+	/* The skb/callback union must be NULL if an SKB is requested */
+	if (cmd->meta.u.skb && (cmd->meta.flags & CMD_WANT_SKB)) {
+		IWL_ERROR("skb != null && WANT_SKB\n");
+		return -EINVAL;
+	}
+
+	/* A command can not be synchronous AND have a callback set */
+	if (is_cmd_sync(cmd) && cmd->meta.u.callback) {
+		IWL_ERROR("callback != null && SYNC\n");
+		return -EINVAL;
+	}
+
+	/* An asynchronous command MUST have a callback */
+	if ((cmd->meta.flags & CMD_ASYNC) && !cmd->meta.u.callback) {
+		IWL_ERROR("callback == null && ASYNC\n");
+		return -EINVAL;
+	}
+
+	/* A command can not be synchronous AND not use locks */
+	if (is_cmd_sync(cmd) && (cmd->meta.flags & CMD_NO_LOCK)) {
+		IWL_ERROR("SYNC && NO_LOCK\n");
+		return -EINVAL;
+	}
+
+	if (cmd_needs_lock(cmd))
+		spin_lock_irqsave(&priv->lock, flags);
+
+	if (is_cmd_sync(cmd) && (priv->status & STATUS_HCMD_ACTIVE)) {
+		IWL_ERROR("Error sending %s: "
+			  "Already sending a host command\n",
+			  get_cmd_string(cmd->id));
+		if (cmd_needs_lock(cmd))
+			spin_unlock_irqrestore(&priv->lock, flags);
+		return -EBUSY;
+	}
+
+	if (is_cmd_sync(cmd))
+		priv->status |= STATUS_HCMD_ACTIVE;
+
+	/* When the SKB is provided in the tasklet, it needs
+	 * a backpointer to the originating caller so it can
+	 * actually copy the skb there */
+	if (cmd->meta.flags & CMD_WANT_SKB) {
+		cmd->meta.source = &cmd->meta;
+		cmd->meta.magic = CMD_VAR_MAGIC;
+	}
+
+	cmd->meta.len = cmd->len;
+
+	rc = iwl_enqueue_hcmd(priv, cmd);
+	if (rc) {
+		if (is_cmd_sync(cmd))
+			priv->status &= ~STATUS_HCMD_ACTIVE;
+		if (cmd_needs_lock(cmd))
+			spin_unlock_irqrestore(&priv->lock, flags);
+
+		IWL_ERROR("Error sending %s: "
+			  "iwl_queue_tx_hcmd failed: %d\n",
+			  get_cmd_string(cmd->id), rc);
+
+		return -ENOSPC;
+	}
+	if (cmd_needs_lock(cmd))
+		spin_unlock_irqrestore(&priv->lock, flags);
+
+	if (is_cmd_sync(cmd)) {
+		rc = wait_event_interruptible_timeout(priv->wait_command_queue,
+						      !(priv->status &
+							STATUS_HCMD_ACTIVE),
+						      HOST_COMPLETE_TIMEOUT);
+		if (rc == 0) {
+			if (cmd_needs_lock(cmd))
+				spin_lock_irqsave(&priv->lock, flags);
+
+			if (priv->status & STATUS_HCMD_ACTIVE) {
+				IWL_ERROR("Error sending %s: "
+					  "time out after %dms.\n",
+					  get_cmd_string(cmd->id),
+					  jiffies_to_msecs
+					  (HOST_COMPLETE_TIMEOUT));
+				priv->status &= ~STATUS_HCMD_ACTIVE;
+				if ((cmd->meta.flags & CMD_WANT_SKB)
+				    && cmd->meta.u.skb) {
+					dev_kfree_skb_any(cmd->meta.u.skb);
+					cmd->meta.u.skb = NULL;
+				}
+
+				if (cmd_needs_lock(cmd))
+					spin_unlock_irqrestore(
+						&priv->lock, flags);
+				cmd->meta.magic = 0;
+				return -ETIMEDOUT;
+			}
+
+			if (cmd_needs_lock(cmd))
+				spin_unlock_irqrestore(&priv->lock, flags);
+		}
+	}
+
+	if (priv->status & STATUS_RF_KILL_HW) {
+		if ((cmd->meta.flags & CMD_WANT_SKB)
+		    && cmd->meta.u.skb) {
+			dev_kfree_skb_any(cmd->meta.u.skb);
+			cmd->meta.u.skb = NULL;
+		}
+
+		IWL_DEBUG_INFO("Command %s aborted: RF KILL Switch\n",
+			       get_cmd_string(cmd->id));
+
+		return -ECANCELED;
+	}
+
+	if (priv->status & STATUS_FW_ERROR) {
+		if ((cmd->meta.flags & CMD_WANT_SKB)
+		    && cmd->meta.u.skb) {
+			dev_kfree_skb_any(cmd->meta.u.skb);
+			cmd->meta.u.skb = NULL;
+		}
+
+		IWL_DEBUG_INFO("Command %s failed: FW Error\n",
+			       get_cmd_string(cmd->id));
+
+		return -EIO;
+	}
+
+	if ((cmd->meta.flags & CMD_WANT_SKB) && !cmd->meta.u.skb) {
+		IWL_ERROR("Error: Response NULL in '%s'\n",
+			  get_cmd_string(cmd->id));
+		return -EIO;
+	}
+
+	return 0;
+}
+
+int iwl_send_cmd_pdu(struct iwl_priv *priv, u8 id, u16 len, const void *data)
+{
+	struct iwl_host_cmd cmd = {
+		.id = id,
+		.len = len,
+		.data = data,
+	};
+
+	return iwl_send_cmd(priv, &cmd);
+}
+
+static int __must_check iwl_send_cmd_u32(struct iwl_priv *priv, u8 id, u32 val)
+{
+	struct iwl_host_cmd cmd = {
+		.id = id,
+		.len = sizeof(val),
+		.data = &val,
+	};
+
+	return iwl_send_cmd(priv, &cmd);
+}
+
+int iwl_send_statistics_request(struct iwl_priv *priv)
+{
+	return iwl_send_cmd_u32(priv, REPLY_STATISTICS_CMD, 0);
+}
+
+/**
+ * iwl_rxon_add_station - add station into station table.
+ *
+ * there is only one AP station with id= IWL_AP_ID
+ * NOTE: mutex must be held before calling the this fnction
+*/
+static int iwl_rxon_add_station(struct iwl_priv *priv,
+				const u8 * addr, int is_ap)
+{
+	u8 i;
+
+	/* Remove this station if it happens to already exist */
+	iwl_remove_station(priv, addr, is_ap);
+
+	i = iwl_add_station(priv, addr, is_ap, 0);
+
+	iwl4965_add_station(priv, addr, is_ap);
+
+	return i;
+}
+
+/**
+ * iwl_set_rxon_channel - Set the phymode and channel values in staging RXON
+ * @phymode: MODE_IEEE80211A sets to 5.2GHz; all else set to 2.4GHz
+ * @channel: Any channel valid for the requested phymode
+
+ * In addition to setting the staging RXON, priv->phymode is also set.
+ *
+ * NOTE:  Does not commit to the hardware; it sets appropriate bit fields
+ * in the staging RXON flag structure based on the phymode
+ */
+static int iwl_set_rxon_channel(struct iwl_priv *priv, u8 phymode, u8 channel)
+{
+	if (!iwl_get_channel_info(priv, phymode, channel)) {
+		IWL_DEBUG_INFO("Could not set channel to %d [%d]\n",
+			       channel, phymode);
+		return -EINVAL;
+	}
+
+	if ((priv->staging_rxon.channel == channel) &&
+	    (priv->phymode == phymode))
+		return 0;
+
+	priv->staging_rxon.channel = channel;
+	if ((phymode == MODE_IEEE80211A) ||
+	    (phymode == MODE_ATHEROS_TURBO))
+		priv->staging_rxon.flags &= ~RXON_FLG_BAND_24G_MSK;
+	else
+		priv->staging_rxon.flags |= RXON_FLG_BAND_24G_MSK;
+
+	priv->phymode = phymode;
+
+	IWL_DEBUG_INFO("Staging channel set to %d [%d]\n", channel, phymode);
+
+	return 0;
+}
+
+/**
+ * iwl_check_rxon_cmd - validate RXON structure is valid
+ *
+ * NOTE:  This is really only useful during development and can eventually
+ * be #ifdef'd out once the driver is stable and folks aren't actively
+ * making changes
+ */
+static int iwl_check_rxon_cmd(struct iwl_rxon_cmd *rxon)
+{
+	int error = 0;
+	int counter = 1;
+
+	if (rxon->flags & RXON_FLG_BAND_24G_MSK) {
+		error |= (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK);
+		error |= (rxon->flags & RXON_FLG_RADAR_DETECT_MSK);
+		if (error)
+			IWL_WARNING("check 24G fields %d | %d\n",
+				    counter++, error);
+	} else {
+		error |= ((rxon->flags & RXON_FLG_SHORT_SLOT_MSK) !=
+			  RXON_FLG_SHORT_SLOT_MSK);
+		if (error)
+			IWL_WARNING("check 52 fields %d | %d\n",
+				    counter++, error);
+		error |= (rxon->flags & RXON_FLG_CCK_MSK);
+		if (error)
+			IWL_WARNING("check 52 CCK %d | %d\n",
+				    counter++, error);
+
+	}
+	error |= (rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1;
+	if (error)
+		IWL_WARNING("check mac addr %d | %d\n", counter++, error);
+
+	/* make sure basic rates 6Mbps and 1Mbps are supported */
+	error |= (((rxon->ofdm_basic_rates & IWL_RATE_6M_MASK) == 0) &&
+		  ((rxon->cck_basic_rates & IWL_RATE_1M_MASK) == 0));
+	if (error)
+		IWL_WARNING("check basic rate %d | %d\n", counter++, error);
+
+	error |= (rxon->assoc_id > 2007);
+	if (error)
+		IWL_WARNING("check assoc id %d | %d\n", counter++, error);
+
+	error |= ((rxon->flags &
+		   (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) ==
+		  (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK));
+	if (error)
+		IWL_WARNING("check CCK and short slot %d | %d\n",
+			    counter++, error);
+
+	error |= ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK))
+		  == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK));
+	if (error)
+		IWL_WARNING("check CCK & auto detect %d | %d\n",
+			    counter++, error);
+
+	error |= ((rxon->flags &
+		   (RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_TGG_PROTECT_MSK)) ==
+		  RXON_FLG_TGG_PROTECT_MSK);
+	if (error)
+		IWL_WARNING("check TGG %d | %d\n", counter++, error);
+
+#if IWL == 3945
+	if ((rxon->flags & RXON_FLG_DIS_DIV_MSK))
+		error |= ((rxon->flags &
+			   (RXON_FLG_ANT_B_MSK | RXON_FLG_ANT_A_MSK)) == 0);
+	if (error)
+		IWL_WARNING("check antenna %d %d\n", counter++, error);
+#endif
+
+	if (error)
+		IWL_WARNING("Tuning to channel %d\n",
+			    le16_to_cpu(rxon->channel));
+
+	if (error) {
+		IWL_ERROR
+		    ("Error not a valid iwl_rxon_assoc_cmd field values\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * iwl_full_rxon_required - determine if RXON_ASSOC can be used in RXON commit
+ * @priv: staging_rxon is comapred to active_rxon
+ *
+ * If the RXON structure is changing sufficient to require a new
+ * tune or to clear and reset the RXON_FILTER_ASSOC_MSK then return 1
+ * to indicate a new tune is required.
+ */
+static int iwl_full_rxon_required(struct iwl_priv *priv)
+{
+
+	/* These items are only settable from the full RXON command */
+	if (!(priv->active_rxon.filter_flags & RXON_FILTER_ASSOC_MSK) ||
+	    compare_ether_addr(priv->staging_rxon.bssid_addr,
+			       priv->active_rxon.bssid_addr) ||
+	    compare_ether_addr(priv->staging_rxon.node_addr,
+			       priv->active_rxon.node_addr) ||
+	    compare_ether_addr(priv->staging_rxon.wlap_bssid_addr,
+			       priv->active_rxon.wlap_bssid_addr) ||
+	    (priv->staging_rxon.dev_type != priv->active_rxon.dev_type) ||
+	    (priv->staging_rxon.channel != priv->active_rxon.channel) ||
+	    (priv->staging_rxon.air_propagation !=
+	     priv->active_rxon.air_propagation) ||
+#if IWL == 4965
+	    (priv->staging_rxon.ofdm_ht_single_stream_basic_rates !=
+	     priv->active_rxon.ofdm_ht_single_stream_basic_rates) ||
+	    (priv->staging_rxon.ofdm_ht_dual_stream_basic_rates !=
+	     priv->active_rxon.ofdm_ht_dual_stream_basic_rates) ||
+	    (priv->staging_rxon.rx_chain != priv->active_rxon.rx_chain) ||
+#endif
+	    (priv->staging_rxon.assoc_id != priv->active_rxon.assoc_id))
+		return 1;
+
+	/* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can
+	 * be updated with the RXON_ASSOC command -- however only some
+	 * flag transitions are allowed using RXON_ASSOC */
+
+	/* Check if we are not switching bands */
+	if ((priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK) !=
+	    (priv->active_rxon.flags & RXON_FLG_BAND_24G_MSK))
+		return 1;
+
+	/* Check if we are switching association toggle */
+	if ((priv->staging_rxon.filter_flags & RXON_FILTER_ASSOC_MSK) !=
+		(priv->active_rxon.filter_flags & RXON_FILTER_ASSOC_MSK))
+		return 1;
+
+	return 0;
+}
+
+static int iwl_send_rxon_assoc(struct iwl_priv *priv)
+{
+	int rc = 0;
+	struct iwl_rx_packet *res = NULL;
+	struct iwl_rxon_assoc_cmd rxon_assoc;
+	struct iwl_host_cmd cmd = {
+		.id = REPLY_RXON_ASSOC,
+		.len = sizeof(rxon_assoc),
+		.meta.flags = CMD_WANT_SKB,
+		.data = &rxon_assoc,
+	};
+	const struct iwl_rxon_cmd *rxon1 = &priv->staging_rxon;
+	const struct iwl_rxon_cmd *rxon2 = &priv->active_rxon;
+
+	if ((rxon1->flags == rxon2->flags) &&
+	    (rxon1->filter_flags == rxon2->filter_flags) &&
+	    (rxon1->cck_basic_rates == rxon2->cck_basic_rates) &&
+#if IWL == 4965
+	    (rxon1->ofdm_ht_single_stream_basic_rates ==
+	     rxon2->ofdm_ht_single_stream_basic_rates) &&
+	    (rxon1->ofdm_ht_dual_stream_basic_rates ==
+	     rxon2->ofdm_ht_dual_stream_basic_rates) &&
+	    (rxon1->rx_chain == rxon2->rx_chain) &&
+#endif
+	    (rxon1->ofdm_basic_rates == rxon2->ofdm_basic_rates)) {
+		IWL_DEBUG_INFO("Using current RXON_ASSOC.  Not resending.\n");
+		return 0;
+	}
+
+	rxon_assoc.flags = priv->staging_rxon.flags;
+	rxon_assoc.filter_flags = priv->staging_rxon.filter_flags;
+	rxon_assoc.ofdm_basic_rates = priv->staging_rxon.ofdm_basic_rates;
+	rxon_assoc.cck_basic_rates = priv->staging_rxon.cck_basic_rates;
+	rxon_assoc.reserved = 0;
+#if IWL == 4965
+	rxon_assoc.ofdm_ht_single_stream_basic_rates =
+	    priv->staging_rxon.ofdm_ht_single_stream_basic_rates;
+	rxon_assoc.ofdm_ht_dual_stream_basic_rates =
+	    priv->staging_rxon.ofdm_ht_dual_stream_basic_rates;
+	rxon_assoc.rx_chain_select_flags = priv->staging_rxon.rx_chain;
+#endif
+
+	rc = iwl_send_cmd(priv, &cmd);
+	if (rc)
+		return rc;
+
+	res = (struct iwl_rx_packet *)cmd.meta.u.skb->data;
+	if (res->hdr.flags & IWL_CMD_FAILED_MSK) {
+		IWL_ERROR("Bad return from REPLY_RXON_ASSOC command\n");
+		rc = -EIO;
+	}
+
+	priv->alloc_rxb_skb--;
+	dev_kfree_skb_any(cmd.meta.u.skb);
+
+	return rc;
+}
+
+/**
+ * iwl_commit_rxon - commit staging_rxon to hardware
+ *
+ * The RXON command in staging_rxon is commited to the hardware and
+ * the active_rxon structure is updated with the new data.  This
+ * function correctly transitions out of the RXON_ASSOC_MSK state if
+ * a HW tune is required based on the RXON structure changes.
+ */
+static int iwl_commit_rxon(struct iwl_priv *priv)
+{
+	/* cast away the const for active_rxon in this function */
+	struct iwl_rxon_cmd *active_rxon = (void *)&priv->active_rxon;
+	int rc = 0;
+
+	if (!iwl_is_alive(priv))
+		return -1;
+
+	/* always get timestamp with Rx frame */
+	priv->staging_rxon.flags |= RXON_FLG_TSF2HOST_MSK;
+
+#if IWL == 3945
+	/* select antenna */
+	priv->staging_rxon.flags &=
+	    ~(RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_SEL_MSK);
+	priv->staging_rxon.flags |= iwl3945_get_antenna_flags(priv);
+#endif
+
+	rc = iwl_check_rxon_cmd(&priv->staging_rxon);
+	if (rc) {
+		IWL_ERROR("Invalid RXON configuration.  Not committing.\n");
+		return -EINVAL;
+	}
+
+	/* If we don't need to send a full RXON, we can use
+	 * iwl_rxon_assoc_cmd which is used to reconfigure filter
+	 * and other flags for the current radio configuration. */
+	if (!iwl_full_rxon_required(priv)) {
+		rc = iwl_send_rxon_assoc(priv);
+		if (rc) {
+			IWL_ERROR("Error setting RXON_ASSOC "
+				  "configuration (%d).\n", rc);
+			return rc;
+		}
+
+		memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon));
+
+		return 0;
+	}
+
+#if IWL == 4965
+#ifdef CONFIG_IWLWIFI_SENSITIVITY
+	priv->sensitivity_data.state = IWL_SENS_CALIB_NEED_REINIT;
+	if (!priv->error_recovering)
+		priv->start_calib = 0;
+
+	iwl4965_init_sensitivity(priv, CMD_ASYNC, 1);
+#endif /* CONFIG_IWLWIFI_SENSITIVITY */
+#endif /* IWL == 4965 */
+
+	/* If we are currently associated and the new config requires
+	 * an RXON_ASSOC and the new config wants the associated mask enabled,
+	 * we must clear the associated from the active configuration
+	 * before we apply the new config */
+	if (iwl_is_associated(priv) &&
+	    (priv->staging_rxon.filter_flags & RXON_FILTER_ASSOC_MSK)) {
+		IWL_DEBUG_INFO("Toggling associated bit on current RXON\n");
+		active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
+
+		rc = iwl_send_cmd_pdu(priv, REPLY_RXON,
+				      sizeof(struct iwl_rxon_cmd),
+				      &priv->active_rxon);
+
+		/* If the mask clearing failed then we set
+		 * active_rxon back to what it was previously */
+		if (rc) {
+			active_rxon->filter_flags |= RXON_FILTER_ASSOC_MSK;
+			IWL_ERROR("Error clearing ASSOC_MSK on current "
+				  "configuration (%d).\n", rc);
+			return rc;
+		}
+
+		/* The RXON bit toggling will have cleared out the
+		 * station table in the uCode, so blank it in the driver
+		 * as well */
+		iwl_clear_stations_table(priv);
+	} else if (priv->staging_rxon.filter_flags & RXON_FILTER_ASSOC_MSK) {
+		/* When switching from non-associated to associated, the
+		 * uCode clears out the station table; so clear it in the
+		 * driver as well */
+		iwl_clear_stations_table(priv);
+	}
+
+	IWL_DEBUG_INFO("Sending RXON\n"
+		       "* with%s RXON_FILTER_ASSOC_MSK\n"
+		       "* channel = %d\n"
+		       "* bssid = " MAC_FMT "\n",
+		       ((priv->staging_rxon.filter_flags &
+			 RXON_FILTER_ASSOC_MSK) ? "" : "out"),
+		       le16_to_cpu(priv->staging_rxon.channel),
+		       MAC_ARG(priv->staging_rxon.bssid_addr));
+
+	/* Apply the new configuration */
+	rc = iwl_send_cmd_pdu(priv, REPLY_RXON,
+			      sizeof(struct iwl_rxon_cmd), &priv->staging_rxon);
+	if (rc) {
+		IWL_ERROR("Error setting new configuration (%d).\n", rc);
+		return rc;
+	}
+
+#if IWL == 4965
+#ifdef CONFIG_IWLWIFI_SENSITIVITY
+	if (!priv->error_recovering)
+		priv->start_calib = 0;
+
+	priv->sensitivity_data.state = IWL_SENS_CALIB_NEED_REINIT;
+	iwl4965_init_sensitivity(priv, CMD_ASYNC, 1);
+#endif /* CONFIG_IWLWIFI_SENSITIVITY */
+#endif /* IWL == 4965 */
+
+	memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon));
+
+	/* If we issue a new RXON command which required a tune then we must
+	 * send a new TXPOWER command or we won't be able to Tx any frames */
+	rc = iwl_hw_reg_send_txpower(priv);
+	if (rc) {
+		IWL_ERROR("Error setting Tx power (%d).\n", rc);
+		return rc;
+	}
+
+	/* Add the broadcast address so we can send broadcast frames */
+	if (iwl_rxon_add_station(priv, BROADCAST_ADDR, 0) ==
+	    IWL_INVALID_STATION) {
+		IWL_ERROR("Error adding BROADCAST address for transmit.\n");
+		return -EIO;
+	}
+
+	/* If we have set the ASSOC_MSK and we are in BSS mode then
+	 * add the IWL_AP_ID to the station rate table */
+	if (iwl_is_associated(priv) &&
+	    (priv->iw_mode == IEEE80211_IF_TYPE_STA)) {
+		if (iwl_rxon_add_station(priv, priv->active_rxon.bssid_addr, 1)
+		    == IWL_INVALID_STATION) {
+			IWL_ERROR("Error adding AP address for transmit.\n");
+			return -EIO;
+		}
+	}
+
+	/* Init the hardware's rate fallback order based on the
+	 * phymode */
+	rc = iwl3945_init_hw_rate_table(priv);
+	if (rc) {
+		IWL_ERROR("Error setting HW rate table: %02X\n", rc);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int iwl_send_bt_config(struct iwl_priv *priv)
+{
+	struct iwl_bt_cmd bt_cmd = {
+		.flags = 3,
+		.lead_time = 0xAA,
+		.max_kill = 1,
+		.kill_ack_mask = 0,
+		.kill_cts_mask = 0,
+	};
+
+	return iwl_send_cmd_pdu(priv, REPLY_BT_CONFIG,
+				sizeof(struct iwl_bt_cmd), &bt_cmd);
+}
+
+static int iwl_send_scan_abort(struct iwl_priv *priv)
+{
+	int rc = 0;
+	struct iwl_rx_packet *res;
+	struct iwl_host_cmd cmd = {
+		.id = REPLY_SCAN_ABORT_CMD,
+		.meta.flags = CMD_WANT_SKB,
+	};
+
+	/* If there isn't a scan actively going on in the hardware
+	 * then we are in between scan bands and not actually
+	 * actively scanning, so don't send the abort command */
+	if (!(priv->status & STATUS_SCAN_HW)) {
+		priv->status &= ~STATUS_SCAN_ABORTING;
+		return 0;
+	}
+
+	rc = iwl_send_cmd(priv, &cmd);
+	if (rc) {
+		priv->status &= ~STATUS_SCAN_ABORTING;
+		return rc;
+	}
+
+	res = (struct iwl_rx_packet *)cmd.meta.u.skb->data;
+	if (res->u.status != CAN_ABORT_STATUS) {
+		/* The scan abort will return 1 for success or
+		 * 2 for "failure".  A failure condition can be
+		 * due to simply not being in an active scan which
+		 * can occur if we send the scan abort before we
+		 * the microcode has notified us that a scan is
+		 * completed. */
+		IWL_DEBUG_INFO("SCAN_ABORT returned %d.\n", res->u.status);
+		priv->status &= ~(STATUS_SCAN_ABORTING | STATUS_SCAN_HW);
+	}
+
+	dev_kfree_skb_any(cmd.meta.u.skb);
+
+	return rc;
+}
+
+static int iwl_card_state_sync_callback(struct iwl_priv *priv,
+					struct iwl_cmd *cmd,
+					struct sk_buff *skb)
+{
+	return 1;
+}
+
+/*
+ * CARD_STATE_CMD
+ *
+ * Use: Sets the internal card state to enable, disable, or halt
+ *
+ * When in the 'enable' state the card operates as normal.
+ * When in the 'disable' state, the card enters into a low power mode.
+ * When in the 'halt' state, the card is shut down and must be fully
+ * restarted to come back on.
+ */
+static int iwl_send_card_state(struct iwl_priv *priv, u32 flags, u8 meta_flag)
+{
+	struct iwl_host_cmd cmd = {
+		.id = REPLY_CARD_STATE_CMD,
+		.len = sizeof(u32),
+		.data = &flags,
+		.meta.flags = meta_flag,
+	};
+
+	if (meta_flag & CMD_ASYNC)
+		cmd.meta.u.callback = iwl_card_state_sync_callback;
+
+	return iwl_send_cmd(priv, &cmd);
+}
+
+static int iwl_add_sta_sync_callback(struct iwl_priv *priv,
+				     struct iwl_cmd *cmd, struct sk_buff *skb)
+{
+	struct iwl_rx_packet *res = NULL;
+
+	if (!skb) {
+		IWL_ERROR("Error: Response NULL in " "REPLY_ADD_STA.\n");
+		return 1;
+	}
+
+	res = (struct iwl_rx_packet *)skb->data;
+	if (res->hdr.flags & IWL_CMD_FAILED_MSK) {
+		IWL_ERROR("Bad return from REPLY_ADD_STA (0x%08X)\n",
+			  res->hdr.flags);
+		return 1;
+	}
+
+	switch (res->u.add_sta.status) {
+	case ADD_STA_SUCCESS_MSK:
+		break;
+	default:
+		break;
+	}
+
+	/* We didn't cache the SKB; let the caller free it */
+	return 1;
+}
+
+int iwl_send_add_station(struct iwl_priv *priv,
+			 struct iwl_addsta_cmd *sta, u8 flags)
+{
+	struct iwl_rx_packet *res = NULL;
+	int rc = 0;
+	struct iwl_host_cmd cmd = {
+		.id = REPLY_ADD_STA,
+		.len = sizeof(struct iwl_addsta_cmd),
+		.meta.flags = flags,
+		.data = sta,
+	};
+
+	if (flags & CMD_ASYNC)
+		cmd.meta.u.callback = iwl_add_sta_sync_callback;
+	else
+		cmd.meta.flags |= CMD_WANT_SKB;
+
+	rc = iwl_send_cmd(priv, &cmd);
+
+	if (rc || (flags & CMD_ASYNC))
+		return rc;
+
+	res = (struct iwl_rx_packet *)cmd.meta.u.skb->data;
+	if (res->hdr.flags & IWL_CMD_FAILED_MSK) {
+		IWL_ERROR("Bad return from REPLY_ADD_STA (0x%08X)\n",
+			  res->hdr.flags);
+		rc = -EIO;
+	}
+
+	if (rc == 0) {
+		switch (res->u.add_sta.status) {
+		case ADD_STA_SUCCESS_MSK:
+			IWL_DEBUG_INFO("REPLY_ADD_STA PASSED\n");
+			break;
+		default:
+			rc = -EIO;
+			IWL_WARNING("REPLY_ADD_STA failed\n");
+			break;
+		}
+	}
+
+	priv->alloc_rxb_skb--;
+	dev_kfree_skb_any(cmd.meta.u.skb);
+
+	return rc;
+}
+
+static int iwl_update_sta_key_info(struct iwl_priv *priv,
+				   struct ieee80211_key_conf *keyconf,
+				   u8 sta_id)
+{
+	unsigned long flags;
+	__le16 key_flags = 0;
+
+	switch (keyconf->alg) {
+	case ALG_CCMP:
+		key_flags |= STA_KEY_FLG_CCMP;
+		key_flags |= cpu_to_le16(
+				keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
+		key_flags &= ~STA_KEY_FLG_INVALID;
+		break;
+	case ALG_TKIP:
+	case ALG_WEP:
+		return -EINVAL;
+	default:
+		return -EINVAL;
+	}
+	spin_lock_irqsave(&priv->sta_lock, flags);
+	priv->stations[sta_id].keyinfo.alg = keyconf->alg;
+	priv->stations[sta_id].keyinfo.keylen = keyconf->keylen;
+	memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key,
+	       keyconf->keylen);
+
+	memcpy(priv->stations[sta_id].sta.key.key, keyconf->key,
+	       keyconf->keylen);
+	priv->stations[sta_id].sta.key.key_flags = key_flags;
+	priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
+	priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+
+	spin_unlock_irqrestore(&priv->sta_lock, flags);
+
+	IWL_DEBUG_INFO("hwcrypto: modify ucode station key info\n");
+	iwl_send_add_station(priv, &priv->stations[sta_id].sta, 0);
+	return 0;
+}
+
+static void iwl_clear_free_frames(struct iwl_priv *priv)
+{
+	struct list_head *element;
+
+	IWL_DEBUG_INFO("%d frames on pre-allocated heap on clear.\n",
+		       priv->frames_count);
+
+	while (!list_empty(&priv->free_frames)) {
+		element = priv->free_frames.next;
+		list_del(element);
+		kfree(list_entry(element, struct iwl_frame, list));
+		priv->frames_count--;
+	}
+
+	if (priv->frames_count) {
+		IWL_WARNING("%d frames still in use.  Did we lose one?\n",
+			    priv->frames_count);
+		priv->frames_count = 0;
+	}
+}
+
+static struct iwl_frame *iwl_get_free_frame(struct iwl_priv *priv)
+{
+	struct iwl_frame *frame;
+	struct list_head *element;
+	if (list_empty(&priv->free_frames)) {
+		frame = kzalloc(sizeof(*frame), GFP_ATOMIC);
+		if (!frame) {
+			IWL_ERROR("Could not allocate frame!\n");
+			return NULL;
+		}
+
+		priv->frames_count++;
+		return frame;
+	}
+
+	element = priv->free_frames.next;
+	list_del(element);
+	return list_entry(element, struct iwl_frame, list);
+}
+
+static void iwl_free_frame(struct iwl_priv *priv, struct iwl_frame *frame)
+{
+	memset(frame, 0, sizeof(*frame));
+	list_add(&frame->list, &priv->free_frames);
+}
+
+int iwl_fill_beacon_frame(struct iwl_priv *priv,
+			  struct ieee80211_hdr *hdr, const u8 * dest, int left)
+{
+
+	if (!iwl_is_associated(priv) || !priv->ibss_beacon ||
+	    ((priv->iw_mode != IEEE80211_IF_TYPE_IBSS) &&
+	     (priv->iw_mode != IEEE80211_IF_TYPE_AP)))
+		return 0;
+
+	if (priv->ibss_beacon->len > left)
+		return 0;
+
+	memcpy(hdr, priv->ibss_beacon->data, priv->ibss_beacon->len);
+
+	return priv->ibss_beacon->len;
+}
+
+static int iwl_send_beacon_cmd(struct iwl_priv *priv)
+{
+	struct iwl_frame *frame;
+	int frame_size, rc;
+	u16 rate;
+
+	frame = iwl_get_free_frame(priv);
+
+	if (!frame) {
+		IWL_ERROR("Coult not obtain free frame buffer for beacon "
+			  "command.\n");
+		return -ENOMEM;
+	}
+
+	if (!(priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK)) {
+		rate = iwl_rate_get_lowest_plcp(priv->active_rate_basic &
+						0xFF0);
+		if (rate <= 0)
+			rate = IWL_RATE_6M_PLCP;
+	} else {
+		rate = iwl_rate_get_lowest_plcp(priv->active_rate_basic & 0xF);
+		if (rate <= 0)
+			rate = IWL_RATE_1M_PLCP;
+	}
+
+	frame_size = iwl_hw_get_beacon_cmd(priv, frame, rate);
+
+	rc = iwl_send_cmd_pdu(priv, REPLY_TX_BEACON, frame_size,
+			      &frame->u.cmd[0]);
+
+	iwl_free_frame(priv, frame);
+
+	return rc;
+}
+
+/******************************************************************************
+ *
+ * EEPROM related functions
+ *
+ ******************************************************************************/
+
+static void get_eeprom_mac(struct iwl_priv *priv, u8 * mac)
+{
+	memcpy(mac, priv->eeprom.mac_address, 6);
+}
+
+/**
+ * iwl_eeprom_init - read EEPROM contents
+ *
+ * Load the EEPROM from adapter into priv->eeprom
+ *
+ * NOTE:  This routine uses the non-debug IO access functions.
+ */
+int iwl_eeprom_init(struct iwl_priv *priv)
+{
+	u16 *e = (u16 *) & priv->eeprom;
+	u32 r;
+	int to;
+	u32 gp = iwl_read32(priv, CSR_EEPROM_GP);
+	u16 sz = sizeof(priv->eeprom);
+	int rc;
+	u16 addr;
+
+	/* The EEPROM structure has several padding buffers within it
+	 * and when adding new EEPROM maps is subject to programmer errors
+	 * which may be very difficult to identify without explicitly
+	 * checking the resulting size of the eeprom map. */
+	BUILD_BUG_ON(sizeof(priv->eeprom) != IWL_EEPROM_IMAGE_SIZE);
+
+	if ((gp & CSR_EEPROM_GP_VALID_MSK) == CSR_EEPROM_GP_BAD_SIGNATURE) {
+		IWL_ERROR("EEPROM not found, EEPROM_GP=0x%08x", gp);
+		return -ENOENT;
+	}
+
+	rc = iwl_eeprom_aqcuire_semaphore(priv);
+	if (rc < 0) {
+		IWL_ERROR("Failed to aqcuire EEPROM semaphore.\n");
+		return -ENOENT;
+	}
+
+	for (addr = 0, r = 0; addr < sz; addr += 2) {
+		_iwl_write32(priv, CSR_EEPROM_REG, addr << 1);
+		_iwl_clear_bit(priv, CSR_EEPROM_REG, 0x00000002);
+		rc = _iwl_grab_restricted_access(priv);
+		if (rc)
+			goto done;
+
+		for (to = 0; to < 50; to++) {
+			r = _iwl_read_restricted(priv, CSR_EEPROM_REG);
+			if (r & 1)
+				break;
+			udelay(10);
+		}
+
+		_iwl_release_restricted_access(priv);
+
+		if (!(r & 1)) {
+			IWL_ERROR("Time out reading EEPROM[%d]", addr);
+			rc = -ETIMEDOUT;
+			goto done;
+		}
+
+		e[addr / 2] = le16_to_cpu(r >> 16);
+	}
+	rc = 0;
+
+done:
+	iwl_eeprom_release_semaphore(priv);
+	return rc;
+}
+
+/******************************************************************************
+ *
+ * Misc. internal state and helper functions
+ *
+ ******************************************************************************/
+#ifdef CONFIG_IWLWIFI_DEBUG
+
+/**
+ * iwl_report_frame - dump frame to syslog during debug sessions
+ *
+ * hack this function to show different aspects of received frames,
+ * including selective frame dumps.
+ * group100 parameter selects whether to show 1 out of 100 good frames.
+ *
+ * TODO:  ieee80211_hdr stuff is common to 3945 and 4965, so frame type
+ *        info output is okay, but some of this stuff (e.g. iwl_rx_frame_stats)
+ *        is 3945-specific and gives bad output for 4965.  Need to split the
+ *        functionality, keep common stuff here.
+ */
+void iwl_report_frame(struct iwl_priv *priv,
+		      struct iwl_rx_packet *pkt,
+		      struct ieee80211_hdr *header, int group100)
+{
+	u32 to_us;
+	u32 print_summary = 0;
+	u32 print_dump = 0;	/* set to 1 to dump all frames' contents */
+	u32 hundred = 0;
+	u32 dataframe = 0;
+
+	/* these are declared without "=" to avoid compiler warnings if we
+	 *   don't use them in the debug messages below */
+	u16 frame_ctl;
+	u16 seq_ctl;
+	u16 channel;
+	u16 phy_flags;
+	int rate_sym;
+	u16 length;
+	u16 status;
+	u16 bcn_tmr;
+	u32 tsf_low;
+	u64 tsf;
+	u8 rssi;
+	u8 agc;
+	u16 sig_avg;
+	u16 noise_diff;
+
+	struct iwl_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt);
+	struct iwl_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
+	struct iwl_rx_frame_end *rx_end = IWL_RX_END(pkt);
+	u8 *data = IWL_RX_DATA(pkt);
+
+	/* MAC header */
+	frame_ctl = le16_to_cpu(header->frame_control);
+	seq_ctl = le16_to_cpu(header->seq_ctrl);
+
+	/* metadata */
+	channel = le16_to_cpu(rx_hdr->channel);
+	phy_flags = le16_to_cpu(rx_hdr->phy_flags);
+	rate_sym = rx_hdr->rate;
+	length = le16_to_cpu(rx_hdr->len);
+
+	/* end-of-frame status and timestamp */
+	status = le32_to_cpu(rx_end->status);
+	bcn_tmr = le32_to_cpu(rx_end->beacon_timestamp);
+	tsf_low = le64_to_cpu(rx_end->timestamp) & 0x0ffffffff;
+	tsf = le64_to_cpu(rx_end->timestamp);
+
+	/* signal statistics */
+	rssi = rx_stats->rssi;
+	agc = rx_stats->agc;
+	sig_avg = le16_to_cpu(rx_stats->sig_avg);
+	noise_diff = le16_to_cpu(rx_stats->noise_diff);
+
+	to_us = !compare_ether_addr(header->addr1, priv->mac_addr);
+
+	/* if data frame is to us and all is good,
+	 *   (optionally) print summary for only 1 out of every 100 */
+	if (to_us && (frame_ctl & ~IEEE80211_FCTL_PROTECTED) ==
+	    (IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) {
+		dataframe = 1;
+		if (!group100) {
+			print_summary = 1;	/* print each frame */
+		} else if (priv->framecnt_to_us < 100) {
+			priv->framecnt_to_us++;
+			print_summary = 0;
+		} else {
+			priv->framecnt_to_us = 0;
+			print_summary = 1;
+			hundred = 1;
+		}
+	} else {
+		/* print summary for all other frames */
+		print_summary = 1;
+	}
+
+	if (print_summary) {
+		char *title;
+		u32 rate;
+
+		if (hundred)
+			title = "100Frames";
+		else if (frame_ctl & IEEE80211_FCTL_RETRY)
+			title = "Retry";
+		else if (ieee80211_is_assoc_response(frame_ctl))
+			title = "AscRsp";
+		else if (ieee80211_is_reassoc_response(frame_ctl))
+			title = "RasRsp";
+		else if (ieee80211_is_probe_response(frame_ctl)) {
+			title = "PrbRsp";
+			print_dump = 1;	/* dump frame contents */
+		} else if (ieee80211_is_beacon(frame_ctl)) {
+			title = "Beacon";
+			print_dump = 1;	/* dump frame contents */
+		} else if (ieee80211_is_atim(frame_ctl))
+			title = "ATIM";
+		else if (ieee80211_is_auth(frame_ctl))
+			title = "Auth";
+		else if (ieee80211_is_deauth(frame_ctl))
+			title = "DeAuth";
+		else if (ieee80211_is_disassoc(frame_ctl))
+			title = "DisAssoc";
+		else
+			title = "Frame";
+
+		rate = iwl_rate_index_from_plcp(rate_sym);
+		if (rate == -1)
+			rate = 0;
+		else
+			rate = iwl_rates[rate].ieee / 2;
+
+		/* print frame summary.
+		 * MAC addresses show just the last byte (for brevity),
+		 *    but you can hack it to show more, if you'd like to. */
+		if (dataframe) {
+			IWL_DEBUG_RX("%s: mhd=0x%04x, dst=0x%02x, "
+				     "len=%u, rssi=%d, chnl=%d, rate=%u, \n",
+				     title, frame_ctl, header->addr1[5],
+				     length, rssi, channel, rate);
+		} else {
+			/* src/dst addresses assume managed mode */
+			IWL_DEBUG_RX("%s: 0x%04x, dst=0x%02x, "
+				     "src=0x%02x, rssi=%u, tim=%lu usec, "
+				     "phy=0x%02x, chnl=%d\n",
+				     title, frame_ctl, header->addr1[5],
+				     header->addr3[5], rssi,
+				     tsf_low - priv->scan_start_tsf,
+				     phy_flags, channel);
+		}
+	}
+	if (print_dump)
+		printk_buf(IWL_DL_RX, data, length);
+}
+#endif
+
+static void iwl_unset_hw_setting(struct iwl_priv *priv)
+{
+	if (priv->hw_setting.shared_virt)
+		pci_free_consistent(priv->pci_dev,
+				    sizeof(struct iwl_shared),
+				    priv->hw_setting.shared_virt,
+				    priv->hw_setting.shared_phys);
+}
+
+/**
+ * iwl_supported_rate_to_ie - fill in the supported rate in IE field
+ *
+ * return : set the bit for each supported rate insert in ie
+ */
+static u16 iwl_supported_rate_to_ie(u8 * ie, u16 supported_rate,
+				    u16 basic_rate, int max_count)
+{
+	u16 ret_rates = 0, bit;
+	int i;
+	u8 *rates;
+
+	rates = &(ie[1]);
+
+	for (bit = 1, i = 0; i < IWL_RATE_COUNT; i++, bit <<= 1) {
+		if (bit & supported_rate) {
+			ret_rates |= bit;
+			rates[*ie] = iwl_rates[i].ieee |
+			    ((bit & basic_rate) ? 0x80 : 0x00);
+			*ie = *ie + 1;
+			if (*ie >= max_count)
+				break;
+		}
+	}
+
+	return ret_rates;
+}
+
+#if IWL == 4965
+#ifdef CONFIG_IWLWIFI_HT
+void static iwl_set_ht_capab(struct ieee80211_hw *hw,
+			     struct ieee80211_ht_capability *ht_cap,
+			     u8 use_wide_chan);
+#endif
+#endif
+
+/**
+ * iwl_fill_probe_req - fill in all required fields and IE for probe request
+ */
+static int iwl_fill_probe_req(struct iwl_priv *priv,
+			      struct ieee80211_mgmt *frame,
+			      int left, int is_direct)
+{
+	int len = 0;
+	u8 *pos = NULL;
+	u16 ret_rates;
+
+	/* Make sure there is enough space for the probe request,
+	 * two mandatory IEs and the data */
+	left -= 24;
+	if (left < 0)
+		return 0;
+	len += 24;
+
+	frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
+	memcpy(frame->da, BROADCAST_ADDR, ETH_ALEN);
+	memcpy(frame->sa, priv->mac_addr, ETH_ALEN);
+	memcpy(frame->bssid, BROADCAST_ADDR, ETH_ALEN);
+	frame->seq_ctrl = 0;
+
+	/* fill in our indirect SSID IE */
+	/* ...next IE... */
+
+	left -= 2;
+	if (left < 0)
+		return 0;
+	len += 2;
+	pos = &(frame->u.probe_req.variable[0]);
+	*pos++ = WLAN_EID_SSID;
+	*pos++ = 0;
+
+	/* fill in our direct SSID IE... */
+	if (is_direct) {
+		/* ...next IE... */
+		left -= 2 + priv->essid_len;
+		if (left < 0)
+			return 0;
+		/* ... fill it in... */
+		*pos++ = WLAN_EID_SSID;
+		*pos++ = priv->essid_len;
+		memcpy(pos, priv->essid, priv->essid_len);
+		pos += priv->essid_len;
+		len += 2 + priv->essid_len;
+	}
+
+	/* fill in supported rate */
+	/* ...next IE... */
+	left -= 2;
+	if (left < 0)
+		return 0;
+	/* ... fill it in... */
+	*pos++ = WLAN_EID_SUPP_RATES;
+	*pos = 0;
+	ret_rates = priv->active_rate = priv->rates_mask;
+	priv->active_rate_basic = priv->rates_mask & IWL_BASIC_RATES_MASK;
+
+	iwl_supported_rate_to_ie(pos, priv->active_rate,
+				 priv->active_rate_basic, left);
+	len += 2 + *pos;
+	pos += (*pos) + 1;
+	ret_rates = ~ret_rates & priv->active_rate;
+
+	if (ret_rates == 0)
+		goto fill_end;
+
+	/* fill in supported extended rate */
+	/* ...next IE... */
+	left -= 2;
+	if (left < 0)
+		return 0;
+	/* ... fill it in... */
+	*pos++ = WLAN_EID_EXT_SUPP_RATES;
+	*pos = 0;
+	iwl_supported_rate_to_ie(pos, ret_rates, priv->active_rate_basic, left);
+	if (*pos > 0)
+		len += 2 + *pos;
+#if IWL == 4965
+#ifdef CONFIG_IWLWIFI_HT
+	if (is_direct && priv->is_ht_enabled) {
+		u8 use_wide_chan = 1;
+
+		if (priv->channel_width != IWL_CHANNEL_WIDTH_40MHZ)
+			use_wide_chan = 0;
+		pos += (*pos) + 1;
+		*pos++ = WLAN_EID_HT_CAPABILITY;
+		*pos++ = sizeof(struct ieee80211_ht_capability);
+		iwl_set_ht_capab(NULL, (struct ieee80211_ht_capability *)pos,
+				 use_wide_chan);
+		len += 2 + sizeof(struct ieee80211_ht_capability);
+	}
+#endif  /*CONFIG_IWLWIFI_HT */
+#endif
+
+ fill_end:
+	return len;
+}
+
+/*
+ * QoS  support
+*/
+#ifdef CONFIG_IWLWIFI_QOS
+static int iwl_send_qos_params_command(struct iwl_priv *priv,
+				       struct iwl_qosparam_cmd *qos)
+{
+
+	return iwl_send_cmd_pdu(priv, REPLY_QOS_PARAM,
+				sizeof(struct iwl_qosparam_cmd), qos);
+}
+
+static void iwl_reset_qos(struct iwl_priv *priv)
+{
+	u16 cw_min = 15;
+	u16 cw_max = 1023;
+	u8 aifs = 2;
+	u8 is_legacy = 0;
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->qos_data.qos_active = 0;
+	if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS) {
+		if (priv->qos_data.qos_enable)
+			priv->qos_data.qos_active = IPW_QOS_WMM;
+		if (!(priv->active_rate & 0xfff0)) {
+			cw_min = 31;
+			is_legacy = 1;
+		}
+	} else {
+
+		if (!(priv->staging_rxon.flags & RXON_FLG_SHORT_SLOT_MSK)) {
+			cw_min = 31;
+			is_legacy = 1;
+		}
+	}
+
+	if (priv->qos_data.qos_active)
+		aifs = 3;
+
+	priv->qos_data.def_qos_parm.ac[0].cw_min = cpu_to_le16(cw_min);
+	priv->qos_data.def_qos_parm.ac[0].cw_max = cpu_to_le16(cw_max);
+	priv->qos_data.def_qos_parm.ac[0].aifsn = aifs;
+	priv->qos_data.def_qos_parm.ac[0].edca_txop = 0;
+	priv->qos_data.def_qos_parm.ac[0].reserved1 = 0;
+
+	if (priv->qos_data.qos_active) {
+		i = 1;
+		priv->qos_data.def_qos_parm.ac[i].cw_min = cpu_to_le16(cw_min);
+		priv->qos_data.def_qos_parm.ac[i].cw_max = cpu_to_le16(cw_max);
+		priv->qos_data.def_qos_parm.ac[i].aifsn = 7;
+		priv->qos_data.def_qos_parm.ac[i].edca_txop = 0;
+		priv->qos_data.def_qos_parm.ac[i].reserved1 = 0;
+
+		i = 2;
+		priv->qos_data.def_qos_parm.ac[i].cw_min =
+		    cpu_to_le16((cw_min + 1) / 2 - 1);
+		priv->qos_data.def_qos_parm.ac[i].cw_max =
+		    cpu_to_le16(cw_max);
+		priv->qos_data.def_qos_parm.ac[i].aifsn = 2;
+		if (is_legacy)
+			priv->qos_data.def_qos_parm.ac[i].edca_txop =
+				cpu_to_le16(6016);
+		else
+			priv->qos_data.def_qos_parm.ac[i].edca_txop =
+				cpu_to_le16(3008);
+		priv->qos_data.def_qos_parm.ac[i].reserved1 = 0;
+
+		i = 3;
+		priv->qos_data.def_qos_parm.ac[i].cw_min =
+		    cpu_to_le16((cw_min + 1) / 4 - 1);
+		priv->qos_data.def_qos_parm.ac[i].cw_max =
+		    cpu_to_le16((cw_max + 1) / 2 - 1);
+		priv->qos_data.def_qos_parm.ac[i].aifsn = 2;
+		priv->qos_data.def_qos_parm.ac[i].reserved1 = 0;
+		if (is_legacy)
+			priv->qos_data.def_qos_parm.ac[i].edca_txop =
+				cpu_to_le16(3264);
+		else
+			priv->qos_data.def_qos_parm.ac[i].edca_txop =
+				cpu_to_le16(1504);
+
+	} else {
+		for (i = 1; i < 4; i++) {
+			priv->qos_data.def_qos_parm.ac[i].cw_min =
+				cpu_to_le16(cw_min);
+			priv->qos_data.def_qos_parm.ac[i].cw_max =
+				cpu_to_le16(cw_max);
+			priv->qos_data.def_qos_parm.ac[i].aifsn = aifs;
+			priv->qos_data.def_qos_parm.ac[i].edca_txop = 0;
+			priv->qos_data.def_qos_parm.ac[i].reserved1 = 0;
+		}
+	}
+	IWL_DEBUG_QOS("set QoS to default \n");
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void iwl_activate_qos(struct iwl_priv *priv, u8 force)
+{
+	unsigned long flags;
+
+	if (priv == NULL)
+		return;
+
+	if (priv->status & STATUS_EXIT_PENDING)
+		return;
+
+	if (!priv->qos_data.qos_enable)
+		return;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->qos_data.def_qos_parm.qos_flags = 0;
+
+	if (priv->qos_data.qos_cap.q_AP.queue_request &&
+	    !priv->qos_data.qos_cap.q_AP.txop_request)
+		priv->qos_data.def_qos_parm.qos_flags |=
+		    QOS_PARAM_FLG_TXOP_TYPE_MSK;
+
+	if (priv->qos_data.qos_active)
+		priv->qos_data.def_qos_parm.qos_flags |=
+		    QOS_PARAM_FLG_UPDATE_EDCA_MSK;
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	if (force || iwl_is_associated(priv)) {
+		IWL_DEBUG_QOS("send QoS cmd with Qos active %d \n",
+			      priv->qos_data.qos_active);
+
+		iwl_send_qos_params_command(priv,
+					    &(priv->qos_data.def_qos_parm));
+	}
+}
+
+#endif /* CONFIG_IWLWIFI_QOS */
+/*
+ * Power management (not Tx power!) functions
+ */
+#define MSEC_TO_USEC 1024
+
+#if IWL == 3945
+#define NOSLP 0
+#define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK
+#elif IWL == 4965
+#define NOSLP 0, 0, 0
+#define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0
+#endif
+#define SLP_TIMEOUT(T)  __constant_cpu_to_le32((T) * MSEC_TO_USEC)
+#define SLP_VEC(X0, X1, X2, X3, X4) {__constant_cpu_to_le32(X0), \
+				     __constant_cpu_to_le32(X1), \
+				     __constant_cpu_to_le32(X2), \
+				     __constant_cpu_to_le32(X3), \
+				     __constant_cpu_to_le32(X4)}
+
+
+/* default power management (not Tx power) table values */
+/* for tim  0-10 */
+static struct iwl_power_vec_entry range_0[IWL_POWER_AC] = {
+	{{NOSLP, SLP_TIMEOUT(0), SLP_TIMEOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0},
+	{{SLP, SLP_TIMEOUT(200), SLP_TIMEOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0},
+	{{SLP, SLP_TIMEOUT(200), SLP_TIMEOUT(300), SLP_VEC(2, 4, 6, 7, 7)}, 0},
+	{{SLP, SLP_TIMEOUT(50), SLP_TIMEOUT(100), SLP_VEC(2, 6, 9, 9, 10)}, 0},
+	{{SLP, SLP_TIMEOUT(50), SLP_TIMEOUT(25), SLP_VEC(2, 7, 9, 9, 10)}, 1},
+	{{SLP, SLP_TIMEOUT(25), SLP_TIMEOUT(25), SLP_VEC(4, 7, 10, 10, 10)}, 1}
+};
+
+/* for tim > 10 */
+static struct iwl_power_vec_entry range_1[IWL_POWER_AC] = {
+	{{NOSLP, SLP_TIMEOUT(0), SLP_TIMEOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0},
+	{{SLP, SLP_TIMEOUT(200), SLP_TIMEOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF)}, 0},
+	{{SLP, SLP_TIMEOUT(200), SLP_TIMEOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF)}, 0},
+	{{SLP, SLP_TIMEOUT(50), SLP_TIMEOUT(100), SLP_VEC(2, 6, 9, 9, 0xFF)}, 0},
+	{{SLP, SLP_TIMEOUT(50), SLP_TIMEOUT(25), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0},
+	{{SLP, SLP_TIMEOUT(25), SLP_TIMEOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF)}, 0}
+};
+
+int iwl_power_init_handle(struct iwl_priv *priv)
+{
+	int rc = 0, i;
+	struct iwl_power_mgr *pow_data;
+	int size = sizeof(struct iwl_power_vec_entry) * IWL_POWER_AC;
+	u16 pci_pm;
+
+	IWL_DEBUG_POWER("Initialize power \n");
+
+	pow_data = &(priv->power_data);
+
+	memset(pow_data, 0, sizeof(*pow_data));
+
+	pow_data->active_index = IWL_POWER_RANGE_0;
+	pow_data->dtim_val = 0xffff;
+
+	memcpy(&pow_data->pwr_range_0[0], &range_0[0], size);
+	memcpy(&pow_data->pwr_range_1[0], &range_1[0], size);
+
+	rc = pci_read_config_word(priv->pci_dev, PCI_LINK_CTRL, &pci_pm);
+	if (rc != 0)
+		return 0;
+	else {
+		struct iwl_powertable_cmd *cmd;
+
+		IWL_DEBUG_POWER("adjust power command flags\n");
+
+		for (i = 0; i < IWL_POWER_AC; i++) {
+			cmd = &pow_data->pwr_range_0[i].cmd;
+
+			if (pci_pm & 0x1)
+				cmd->flags &= ~IWL_POWER_PCI_PM_MSK;
+			else
+				cmd->flags |= IWL_POWER_PCI_PM_MSK;
+		}
+	}
+	return rc;
+}
+
+static int iwl_update_power_cmd(struct iwl_priv *priv,
+				struct iwl_powertable_cmd *cmd, u32 mode)
+{
+	int rc = 0, i;
+	u8 skip;
+	u32 max_sleep = 0;
+	struct iwl_power_vec_entry *range;
+	u8 period = 0;
+	struct iwl_power_mgr *pow_data;
+
+	if (mode > IWL_POWER_INDEX_5) {
+		IWL_DEBUG_POWER("Error invalid power mode \n");
+		return -1;
+	}
+	pow_data = &(priv->power_data);
+
+	if (pow_data->active_index == IWL_POWER_RANGE_0)
+		range = &pow_data->pwr_range_0[0];
+	else
+		range = &pow_data->pwr_range_1[1];
+
+	memcpy(cmd, &range[mode].cmd, sizeof(struct iwl_powertable_cmd));
+
+#ifdef IWL_MAC80211_DISABLE
+	if (priv->assoc_network != NULL) {
+		unsigned long flags;
+
+		period = priv->assoc_network->tim.tim_period;
+	}
+#endif	/*IWL_MAC80211_DISABLE */
+	skip = range[mode].no_dtim;
+
+	if (period == 0) {
+		period = 1;
+		skip = 0;
+	}
+
+	if (skip == 0) {
+		max_sleep = period;
+		cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK;
+	} else {
+		__le32 slp_itrvl = cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1];
+		max_sleep = (le32_to_cpu(slp_itrvl) / period) * period;
+		cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK;
+	}
+
+	for (i = 0; i < IWL_POWER_VEC_SIZE; i++) {
+		if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep)
+			cmd->sleep_interval[i] = cpu_to_le32(max_sleep);
+	}
+
+	IWL_DEBUG_POWER("Flags value = 0x%08X\n", cmd->flags);
+	IWL_DEBUG_POWER("Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout));
+	IWL_DEBUG_POWER("Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout));
+	IWL_DEBUG_POWER("Sleep interval vector = { %d , %d , %d , %d , %d }\n",
+			le32_to_cpu(cmd->sleep_interval[0]),
+			le32_to_cpu(cmd->sleep_interval[1]),
+			le32_to_cpu(cmd->sleep_interval[2]),
+			le32_to_cpu(cmd->sleep_interval[3]),
+			le32_to_cpu(cmd->sleep_interval[4]));
+
+	return rc;
+}
+
+static int iwl_send_power_mode(struct iwl_priv *priv, u32 mode)
+{
+	u32 final_mode = mode;
+	int rc;
+	unsigned long flags;
+	struct iwl_powertable_cmd cmd;
+
+	/* If on battery, set to 3,
+	 * if plugged into AC power, set to CAM ("continuosly aware mode"),
+	 * else user level */
+	switch (mode) {
+	case IWL_POWER_BATTERY:
+		final_mode = IWL_POWER_INDEX_3;
+		break;
+	case IWL_POWER_AC:
+		final_mode = IWL_POWER_MODE_CAM;
+		break;
+	default:
+		final_mode = mode;
+		break;
+	}
+
+#if IWL == 4965
+	cmd.keep_alive_beacons = 0;
+#endif
+
+	iwl_update_power_cmd(priv, &cmd, final_mode);
+
+	rc = iwl_send_cmd_pdu(priv, POWER_TABLE_CMD, sizeof(cmd), &cmd);
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	if (final_mode == IWL_POWER_MODE_CAM)
+		priv->status &= ~STATUS_POWER_PMI;
+	else
+		priv->status |= STATUS_POWER_PMI;
+
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+	return rc;
+}
+
+int iwl_is_network_packet(struct iwl_priv *priv, struct ieee80211_hdr *header)
+{
+	/* Filter incoming packets to determine if they are targeted toward
+	 * this network, discarding packets coming from ourselves */
+	switch (priv->iw_mode) {
+	case IEEE80211_IF_TYPE_IBSS: /* Header: Dest. | Source    | BSSID */
+		/* packets from our adapter are dropped (echo) */
+		if (!compare_ether_addr(header->addr2, priv->mac_addr))
+			return 0;
+		/* {broad,multi}cast packets to our IBSS go through */
+		if (is_multicast_ether_addr(header->addr1))
+			return !compare_ether_addr(header->addr3, priv->bssid);
+		/* packets to our adapter go through */
+		return !compare_ether_addr(header->addr1, priv->mac_addr);
+	case IEEE80211_IF_TYPE_STA: /* Header: Dest. | AP{BSSID} | Source */
+		/* packets from our adapter are dropped (echo) */
+		if (!compare_ether_addr(header->addr3, priv->mac_addr))
+			return 0;
+		/* {broad,multi}cast packets to our BSS go through */
+		if (is_multicast_ether_addr(header->addr1))
+			return !compare_ether_addr(header->addr2, priv->bssid);
+		/* packets to our adapter go through */
+		return !compare_ether_addr(header->addr1, priv->mac_addr);
+	}
+
+	return 1;
+}
+
+#define TX_STATUS_ENTRY(x) case TX_STATUS_FAIL_ ## x: return #x
+
+const char *iwl_get_tx_fail_reason(u32 status)
+{
+	switch (status & TX_STATUS_MSK) {
+	case TX_STATUS_SUCCESS:
+		return "SUCCESS";
+		TX_STATUS_ENTRY(SHORT_LIMIT);
+		TX_STATUS_ENTRY(LONG_LIMIT);
+		TX_STATUS_ENTRY(FIFO_UNDERRUN);
+		TX_STATUS_ENTRY(MGMNT_ABORT);
+		TX_STATUS_ENTRY(NEXT_FRAG);
+		TX_STATUS_ENTRY(LIFE_EXPIRE);
+		TX_STATUS_ENTRY(DEST_PS);
+		TX_STATUS_ENTRY(ABORTED);
+		TX_STATUS_ENTRY(BT_RETRY);
+		TX_STATUS_ENTRY(STA_INVALID);
+		TX_STATUS_ENTRY(FRAG_DROPPED);
+		TX_STATUS_ENTRY(TID_DISABLE);
+		TX_STATUS_ENTRY(FRAME_FLUSHED);
+		TX_STATUS_ENTRY(INSUFFICIENT_CF_POLL);
+		TX_STATUS_ENTRY(TX_LOCKED);
+		TX_STATUS_ENTRY(NO_BEACON_ON_RADAR);
+	}
+
+	return "UNKNOWN";
+}
+
+/**
+ * iwl_scan_cancel - Cancel any currently executing HW scan
+ * @ms: amount of time to wait (in milliseconds) for scan to abort
+ *
+ * NOTE: priv->mutex must be held before calling this function
+ */
+static int iwl_scan_cancel(struct iwl_priv *priv, unsigned long ms)
+{
+	unsigned long now = jiffies;
+
+	if (!(priv->status & STATUS_SCAN_HW)) {
+		priv->status &= ~STATUS_SCANNING;
+		return 0;
+	}
+
+	if (priv->status & STATUS_SCAN_PENDING) {
+		IWL_DEBUG_SCAN("Canceling pending scan request.\n");
+		priv->status &= ~STATUS_SCAN_PENDING;
+	}
+
+	if (priv->status & STATUS_SCANNING) {
+		if (!(priv->status & STATUS_SCAN_ABORTING)) {
+			IWL_DEBUG_SCAN("Queuing scan abort.\n");
+			priv->status |= STATUS_SCAN_ABORTING;
+			queue_work(priv->workqueue, &priv->abort_scan);
+
+		} else
+			IWL_DEBUG_SCAN("Scan abort already in progress.\n");
+
+
+		mutex_unlock(&priv->mutex);
+		if (ms)
+			while (!time_after(jiffies,
+					   now + msecs_to_jiffies(ms)) &&
+			       priv->status & STATUS_SCANNING)
+				msleep(1);
+
+		mutex_lock(&priv->mutex);
+	}
+
+	return (priv->status & STATUS_SCANNING);
+}
+
+static void iwl_sequence_reset(struct iwl_priv *priv)
+{
+	/* Reset ieee stats */
+
+	/* We don't reset the net_device_stats (ieee->stats) on
+	 * re-association */
+
+	priv->last_seq_num = -1;
+	priv->last_frag_num = -1;
+	priv->last_packet_time = 0;
+
+	iwl_scan_cancel(priv, 0);
+}
+
+#if IWL == 4965
+#define MAX_UCODE_BEACON_INTERVAL	4096
+#else
+#define MAX_UCODE_BEACON_INTERVAL	1024
+#endif
+#define INTEL_CONN_LISTEN_INTERVAL	0xA
+
+static u16 iwl_adjust_beacon_interval(u16 beacon_val)
+{
+	u16 new_val = 0;
+	u16 beacon_factor = 0;
+
+	beacon_factor =
+	    (beacon_val +
+	     MAX_UCODE_BEACON_INTERVAL) / MAX_UCODE_BEACON_INTERVAL;
+	new_val = beacon_val / beacon_factor;
+
+	return new_val;
+}
+
+static void iwl_setup_rxon_timing(struct iwl_priv *priv)
+{
+	u64 interval_tm_unit;
+	u64 tsf, result;
+	unsigned long flags;
+	struct ieee80211_conf *conf = NULL;
+	u16 beacon_int = 0;
+
+	conf = ieee80211_get_hw_conf(priv->hw);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->rxon_timing.timestamp.dw[1] = priv->timestamp1;
+	priv->rxon_timing.timestamp.dw[0] = priv->timestamp0;
+
+	priv->rxon_timing.listen_interval = INTEL_CONN_LISTEN_INTERVAL;
+
+	tsf = priv->timestamp1;
+	tsf = ((tsf << 32) | priv->timestamp0);
+
+	beacon_int = priv->beacon_int;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	if (priv->iw_mode == IEEE80211_IF_TYPE_STA) {
+		if (beacon_int == 0) {
+			priv->rxon_timing.beacon_interval = 100;
+			priv->rxon_timing.beacon_init_val = 102400;
+		} else {
+			priv->rxon_timing.beacon_interval = beacon_int;
+			priv->rxon_timing.beacon_interval =
+			    iwl_adjust_beacon_interval(
+				    priv->rxon_timing.beacon_interval);
+		}
+
+		priv->rxon_timing.atim_window = 0;
+	} else {
+		priv->rxon_timing.beacon_interval =
+			iwl_adjust_beacon_interval(conf->beacon_int);
+		/* TODO: we need to get atim_window from upper stack
+		 * for now we set to 0 */
+		priv->rxon_timing.atim_window = 0;
+	}
+
+	interval_tm_unit = (priv->rxon_timing.beacon_interval * 1024);
+	result = do_div(tsf, interval_tm_unit);
+	priv->rxon_timing.beacon_init_val =
+	    (u32) ((u64) interval_tm_unit - result);
+
+	IWL_DEBUG_ASSOC
+	    ("beacon interval %d beacon timer %d beacon tim %d\n",
+	     priv->rxon_timing.beacon_interval,
+	     priv->rxon_timing.beacon_init_val, priv->rxon_timing.atim_window);
+}
+
+static int iwl_scan_initiate(struct iwl_priv *priv)
+{
+	if (priv->iw_mode == IEEE80211_IF_TYPE_AP) {
+		IWL_ERROR("APs don't scan.\n");
+		return 0;
+	}
+
+	if (!iwl_is_ready_rf(priv)) {
+		IWL_DEBUG_SCAN("Aborting scan due to not ready.\n");
+		return -EIO;
+	}
+
+	if (priv->status & STATUS_SCANNING) {
+		IWL_DEBUG_SCAN("Scan already in progress.\n");
+		return -EAGAIN;
+	}
+
+	if (priv->status & STATUS_SCAN_ABORTING) {
+		IWL_DEBUG_SCAN("Scan request while abort pending.  "
+			       "Queuing.\n");
+		return -EAGAIN;
+	}
+
+	IWL_DEBUG_INFO("Starting scan...\n");
+	priv->scan_bands = 2;
+	priv->status |= STATUS_SCANNING;
+	priv->scan_start = jiffies;
+	priv->scan_pass_start = priv->scan_start;
+
+	queue_work(priv->workqueue, &priv->request_scan);
+
+	return 0;
+}
+
+static int iwl_set_rxon_hwcrypto(struct iwl_priv *priv, int hw_decrypt)
+{
+	struct iwl_rxon_cmd *rxon = &priv->staging_rxon;
+
+	if (hw_decrypt)
+		rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK;
+	else
+		rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK;
+
+	return 0;
+}
+
+static void iwl_set_flags_for_phymode(struct iwl_priv *priv, u8 phymode)
+{
+	if ((phymode == MODE_IEEE80211A) ||
+	    (phymode == MODE_ATHEROS_TURBO)) {
+		priv->staging_rxon.flags &=
+		    ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK
+		      | RXON_FLG_CCK_MSK);
+		priv->staging_rxon.flags |= RXON_FLG_SHORT_SLOT_MSK;
+	} else {
+		/* Copied from iwl_bg_post_associate() */
+		if (priv->assoc_capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
+			priv->staging_rxon.flags |= RXON_FLG_SHORT_SLOT_MSK;
+		else
+			priv->staging_rxon.flags &= ~RXON_FLG_SHORT_SLOT_MSK;
+
+		if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS)
+			priv->staging_rxon.flags &= ~RXON_FLG_SHORT_SLOT_MSK;
+
+		priv->staging_rxon.flags |= RXON_FLG_BAND_24G_MSK;
+		priv->staging_rxon.flags |= RXON_FLG_AUTO_DETECT_MSK;
+		priv->staging_rxon.flags &= ~RXON_FLG_CCK_MSK;
+	}
+}
+
+/*
+ * initilize rxon structure with default values fromm eeprom
+ */
+static void iwl_connection_init_rx_config(struct iwl_priv *priv)
+{
+	const struct iwl_channel_info *ch_info;
+
+	memset(&priv->staging_rxon, 0, sizeof(priv->staging_rxon));
+
+	switch (priv->iw_mode) {
+	case IEEE80211_IF_TYPE_AP:
+		priv->staging_rxon.dev_type = RXON_DEV_TYPE_AP;
+		break;
+
+	case IEEE80211_IF_TYPE_STA:
+		priv->staging_rxon.dev_type = RXON_DEV_TYPE_ESS;
+		priv->staging_rxon.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK;
+		break;
+
+	case IEEE80211_IF_TYPE_IBSS:
+		priv->staging_rxon.dev_type = RXON_DEV_TYPE_IBSS;
+		priv->staging_rxon.flags = RXON_FLG_SHORT_PREAMBLE_MSK;
+		priv->staging_rxon.filter_flags = RXON_FILTER_BCON_AWARE_MSK |
+						  RXON_FILTER_ACCEPT_GRP_MSK;
+		break;
+
+	case IEEE80211_IF_TYPE_MNTR:
+		priv->staging_rxon.dev_type = RXON_DEV_TYPE_SNIFFER;
+		priv->staging_rxon.filter_flags = RXON_FILTER_PROMISC_MSK |
+		    RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_ACCEPT_GRP_MSK;
+		break;
+	}
+
+#if 0
+	/* TODO:  Figure out when short_preamble would be set and cache from
+	 * that */
+	if (!hw_to_local(priv->hw)->short_preamble)
+		priv->staging_rxon.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK;
+	else
+		priv->staging_rxon.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
+#endif
+
+	ch_info = iwl_get_channel_info(priv, priv->phymode,
+				       priv->staging_rxon.channel);
+
+	if (!ch_info)
+		ch_info = &priv->channel_info[0];
+
+	/*
+	 * in some case A channels are all non IBSS
+	 * in this case force B/G channel
+	 */
+	if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) &&
+	    !(is_channel_ibss(ch_info)))
+		ch_info = &priv->channel_info[0];
+
+	priv->staging_rxon.channel = ch_info->channel;
+	if (is_channel_a_band(ch_info))
+		priv->phymode = MODE_IEEE80211A;
+	else
+		priv->phymode = MODE_IEEE80211G;
+
+	iwl_set_flags_for_phymode(priv, priv->phymode);
+
+	priv->staging_rxon.ofdm_basic_rates =
+	    (IWL_OFDM_RATES_MASK >> IWL_FIRST_OFDM_RATE) & 0xFF;
+	priv->staging_rxon.cck_basic_rates =
+	    (IWL_CCK_RATES_MASK >> IWL_FIRST_CCK_RATE) & 0xF;
+
+#if IWL == 4965
+	priv->staging_rxon.flags |= RXON_FLG_CHANNEL_MODE_LEGACY_MSK;
+	memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN);
+	memcpy(priv->staging_rxon.wlap_bssid_addr, priv->mac_addr, ETH_ALEN);
+	priv->staging_rxon.ofdm_ht_single_stream_basic_rates = 0xff;
+	priv->staging_rxon.ofdm_ht_dual_stream_basic_rates = 0xff;
+	iwl4965_set_rxon_chain(priv);
+#endif
+}
+
+static int iwl_set_mode(struct iwl_priv *priv, int mode)
+{
+	if (!iwl_is_ready_rf(priv))
+		return -EAGAIN;
+
+	if (mode == IEEE80211_IF_TYPE_IBSS) {
+		const struct iwl_channel_info *ch_info;
+
+		ch_info = iwl_get_channel_info(priv,
+					       priv->phymode,
+					       priv->staging_rxon.channel);
+
+		if (!ch_info || !is_channel_ibss(ch_info)) {
+			IWL_ERROR("channel %d not IBSS channel\n",
+				  priv->staging_rxon.channel);
+			return -EINVAL;
+		}
+	}
+
+	cancel_delayed_work(&priv->scan_check);
+	priv->status &= ~STATUS_SCAN_PENDING;
+	if (iwl_scan_cancel(priv, 100)) {
+		IWL_WARNING("Aborted scan still in progress " "after 100ms\n");
+		IWL_DEBUG_MAC80211("leaving - scan abort " "failed.\n");
+		return -EAGAIN;
+	}
+
+	priv->iw_mode = mode;
+
+	iwl_connection_init_rx_config(priv);
+	memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN);
+
+	iwl_clear_stations_table(priv);
+
+	iwl_commit_rxon(priv);
+
+	return 0;
+}
+
+static void iwl_build_tx_cmd_hwcrypto(struct iwl_priv *priv,
+				      struct ieee80211_tx_control *ctl,
+				      struct iwl_cmd *cmd,
+				      struct sk_buff *skb_frag,
+				      int last_frag)
+{
+	struct iwl_hw_key *keyinfo = &priv->stations[ctl->key_idx].keyinfo;
+
+	switch (keyinfo->alg) {
+	case ALG_CCMP:
+		cmd->cmd.tx.sec_ctl = TX_CMD_SEC_CCM;
+
+		cmd->cmd.tx.hdr[0].frame_control |=
+		    cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+		/* XXX: ACK flag must be set for CCMP even if it
+		 * is a multicast/broadcast packet, because CCMP
+		 * group communication encrypted by GTK is
+		 * actually done by the AP. */
+		cmd->cmd.tx.tx_flags |= TX_CMD_FLG_ACK_MSK;
+		memcpy(cmd->cmd.tx.key, keyinfo->key, keyinfo->keylen);
+		IWL_DEBUG_TX("tx_cmd with aes  hwcrypto\n");
+		break;
+	case ALG_TKIP:
+#if 0
+		cmd->cmd.tx.sec_ctl = TX_CMD_SEC_TKIP;
+
+		if (last_frag)
+			memcpy(cmd->cmd.tx.tkip_mic.byte, skb_frag->tail - 8,
+			       8);
+		else
+			memset(cmd->cmd.tx.tkip_mic.byte, 0, 8);
+
+		cmd->cmd.tx.hdr[0].frame_control |=
+		    cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+		/* XXX: ACK flag must be set for CCMP even if it
+		 * is a multicast/broadcast packet, because CCMP
+		 * group communication encrypted by GTK is
+		 * actually done by the AP. */
+		cmd->cmd.tx.tx_flags |= TX_CMD_FLG_ACK_MSK;
+#endif
+		break;
+	case ALG_WEP:
+		cmd->cmd.tx.sec_ctl = 1 |	/* WEP */
+		    (ctl->key_idx & 0x3) << 6;
+
+		if (keyinfo->keylen == 13)
+			cmd->cmd.tx.sec_ctl |= (1 << 3);	/* 128-bit */
+
+		memcpy(&cmd->cmd.tx.key[3], keyinfo->key, keyinfo->keylen);
+
+		cmd->cmd.tx.hdr[0].frame_control |=
+		    cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+
+		IWL_DEBUG_TX("Configuring packet for WEP encryption "
+			     "with key %d\n", ctl->key_idx);
+		break;
+
+	case ALG_NONE:
+		IWL_DEBUG_TX("Tx packet in the clear "
+			     "(encrypt requested).\n");
+		break;
+
+	default:
+		printk(KERN_ERR "Unknown encode alg %d\n", keyinfo->alg);
+		break;
+	}
+
+}
+
+/*
+ * handle build REPLY_TX command notification.
+ */
+static void iwl_build_tx_cmd_basic(struct iwl_priv *priv,
+				  struct iwl_cmd *cmd,
+				  struct ieee80211_tx_control *ctrl,
+				  struct ieee80211_hdr *hdr,
+				  int is_unicast, u8 std_id)
+{
+	u32 tx_flags;
+	u16 fc = le16_to_cpu(hdr->frame_control);
+	__le16 *qc;
+
+	tx_flags = cmd->cmd.tx.tx_flags;
+
+	cmd->cmd.tx.stop_time.life_time = 0xFFFFFFFF;
+	if (!(ctrl->flags & IEEE80211_TXCTL_NO_ACK)) {
+		tx_flags |= TX_CMD_FLG_ACK_MSK;
+		if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT)
+			tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
+		if (ieee80211_is_probe_response(fc) &&
+		    !(le16_to_cpu(hdr->seq_ctrl) & 0xf))
+			tx_flags |= TX_CMD_FLG_TSF_MSK;
+	} else {
+		tx_flags &= (~TX_CMD_FLG_ACK_MSK);
+		tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
+	}
+
+	cmd->cmd.tx.sta_id = std_id;
+	if (ieee80211_get_morefrag(hdr))
+		tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK;
+
+	qc = ieee80211_get_qos_ctrl(hdr);
+	if (qc) {
+		cmd->cmd.tx.tid_tspec = (u8) (le16_to_cpu(*qc) & 0xf);
+		tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK;
+	} else
+		tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
+
+	if (ctrl->flags & IEEE80211_TXCTL_USE_RTS_CTS) {
+		tx_flags |= TX_CMD_FLG_RTS_MSK;
+		tx_flags &= ~TX_CMD_FLG_CTS_MSK;
+	} else if (ctrl->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) {
+		tx_flags &= ~TX_CMD_FLG_RTS_MSK;
+		tx_flags |= TX_CMD_FLG_CTS_MSK;
+	}
+
+	if ((tx_flags & TX_CMD_FLG_RTS_MSK) || (tx_flags & TX_CMD_FLG_CTS_MSK))
+		tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK;
+
+	tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK);
+	if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT) {
+		if (((WLAN_FC_GET_STYPE(fc)) == IEEE80211_STYPE_ASSOC_REQ) ||
+		    ((WLAN_FC_GET_STYPE(fc)) == IEEE80211_STYPE_REASSOC_REQ))
+			cmd->cmd.tx.timeout.pm_frame_timeout = 3;
+		else
+			cmd->cmd.tx.timeout.pm_frame_timeout = 2;
+	} else
+		cmd->cmd.tx.timeout.pm_frame_timeout = 0;
+
+	cmd->cmd.tx.driver_txop = 0;
+	cmd->cmd.tx.tx_flags = tx_flags;
+	cmd->cmd.tx.next_frame_len = 0;
+}
+
+static int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr)
+{
+	int sta_id;
+	u16 fc = le16_to_cpu(hdr->frame_control);
+
+	/* If this frame is broadcast or not data then use the broadcast
+	 * station id */
+	if ((WLAN_FC_GET_TYPE(fc) != IEEE80211_FTYPE_DATA) ||
+	    is_multicast_ether_addr(hdr->addr1))
+		return IWL_BROADCAST_ID;
+
+	/* If this frame is part of a BSS network (we're a station), then
+	 * we use the AP's station id */
+	if (priv->iw_mode == IEEE80211_IF_TYPE_STA)
+		return IWL_AP_ID;
+
+	/* If this frame is part of a IBSS network, then we use the
+	 * target specific station id */
+	if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS) {
+		sta_id = iwl_hw_find_station(priv, hdr->addr1);
+		if (sta_id != IWL_INVALID_STATION)
+			return sta_id;
+
+		sta_id = iwl_add_station(priv, hdr->addr1, 0,
+					 (CMD_ASYNC | CMD_NO_LOCK));
+
+		if (sta_id != IWL_INVALID_STATION)
+			return sta_id;
+
+		IWL_DEBUG_DROP("Station " MAC_FMT " not in station map. "
+			       "Defaulting to broadcast...\n",
+			       MAC_ARG(hdr->addr1));
+		printk_buf(IWL_DL_DROP, (u8 *) hdr, sizeof(*hdr));
+		return IWL_BROADCAST_ID;
+	}
+
+	/* Otherwise we default to the broadcast station id */
+	return IWL_BROADCAST_ID;
+}
+
+/*
+ * start REPLY_TX command process
+ */
+static int iwl_tx_skb(struct iwl_priv *priv,
+		      struct sk_buff *skb, struct ieee80211_tx_control *ctl)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	struct iwl_tfd_frame *tfd;
+	u32 *control_flags;
+	int txq_id = ctl->queue;
+	struct iwl_tx_queue *txq = NULL;
+	struct iwl_queue *q = NULL;
+	dma_addr_t phys_addr;
+	dma_addr_t txcmd_phys;
+	struct iwl_cmd *out_cmd = NULL;
+	u16 len, idx, len_org;
+	u8 id, hdr_len, unicast;
+	u8 sta_id;
+	u16 seq_number = 0;
+	u16 fc;
+	__le16 *qc;
+	u8 wait_write_ptr = 0;
+	unsigned long flags;
+	int rc;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (priv->status & STATUS_RF_KILL_MASK) {
+		IWL_DEBUG_DROP("Dropping - RF KILL\n");
+		goto drop;
+	}
+
+	if (!priv->interface_id) {
+		IWL_DEBUG_DROP("Dropping - !priv->interface_id\n");
+		goto drop;
+	}
+
+	if ((ctl->tx_rate & 0xFF) == IWL_INVALID_RATE) {
+		IWL_ERROR("ERROR: No TX rate available.\n");
+		goto drop;
+	}
+
+	unicast = !is_multicast_ether_addr(hdr->addr1);
+	id = 0;
+
+	fc = le16_to_cpu(hdr->frame_control);
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+	if (ieee80211_is_auth(fc))
+		IWL_DEBUG_TX("Sending AUTH frame\n");
+	else if (ieee80211_is_assoc_request(fc))
+		IWL_DEBUG_TX("Sending ASSOC frame\n");
+	else if (ieee80211_is_reassoc_request(fc))
+		IWL_DEBUG_TX("Sending REASSOC frame\n");
+#endif
+
+	if (!iwl_is_associated(priv) &&
+	    (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA)) {
+		IWL_DEBUG_DROP("Dropping - !iwl_is_associated\n");
+		goto drop;
+	}
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	hdr_len = ieee80211_get_hdrlen(fc);
+	sta_id = iwl_get_sta_id(priv, hdr);
+	if (sta_id == IWL_INVALID_STATION) {
+		IWL_DEBUG_DROP("Dropping - INVALID STATION: " MAC_FMT "\n",
+			       MAC_ARG(hdr->addr1));
+		spin_lock_irqsave(&priv->lock, flags);
+		goto drop;
+	}
+
+	IWL_DEBUG_RATE("station Id %d\n", sta_id);
+
+	qc = ieee80211_get_qos_ctrl(hdr);
+	if (qc) {
+		u8 tid = (u8)(le16_to_cpu(*qc) & 0xf);
+		seq_number = priv->stations[sta_id].tid[tid].seq_number &
+				IEEE80211_SCTL_SEQ;
+		hdr->seq_ctrl = cpu_to_le16(seq_number) |
+			(hdr->seq_ctrl & IEEE80211_SCTL_FRAG);
+		seq_number += 0x10;
+#if IWL == 4965
+#ifdef CONFIG_IWLWIFI_HT
+#ifdef CONFIG_IWLWIFI_HT_AGG
+		/* aggregation is on for this <sta,tid> */
+		if (ctl->flags & IEEE80211_TXCTL_HT_MPDU_AGG)
+			txq_id = priv->stations[sta_id].tid[tid].agg.txq_id;
+#endif /* CONFIG_IWLWIFI_HT_AGG */
+#endif /* CONFIG_IWLWIFI_HT */
+#endif
+
+	}
+
+	txq = &priv->txq[txq_id];
+	q = &txq->q;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	tfd = &txq->bd[q->first_empty];
+	memset(tfd, 0, sizeof(*tfd));
+	control_flags = (u32 *) tfd;
+	idx = get_next_cmd_index(q, q->first_empty, 0);
+
+	memset(&(txq->txb[q->first_empty]), 0, sizeof(struct iwl_tx_info));
+	txq->txb[q->first_empty].skb[0] = skb;
+	memcpy(&(txq->txb[q->first_empty].status.control),
+	       ctl, sizeof(struct ieee80211_tx_control));
+	out_cmd = &txq->cmd[idx];
+	memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr));
+	memset(&out_cmd->cmd.tx, 0, sizeof(out_cmd->cmd.tx));
+	out_cmd->hdr.cmd = REPLY_TX;
+	out_cmd->hdr.sequence = QUEUE_TO_SEQ(txq_id) |
+		INDEX_TO_SEQ(q->first_empty);
+	/* copy frags header */
+	memcpy(out_cmd->cmd.tx.hdr, hdr, hdr_len);
+
+	/* hdr = (struct ieee80211_hdr *)out_cmd->cmd.tx.hdr; */
+	len = priv->hw_setting.tx_cmd_len +
+		sizeof(struct iwl_cmd_header) + hdr_len;
+
+	len_org = len;
+	len = (len + 3) & ~3;
+
+	if (len_org != len) {
+		len_org = 1;
+	} else
+		len_org = 0;
+	txcmd_phys =
+		txq->dma_addr_cmd + sizeof(struct iwl_cmd) * idx +
+		offsetof(struct iwl_cmd, hdr);
+
+	iwl_hw_tx_queue_attach_buffer_to_tfd(priv, tfd, txcmd_phys, len);
+
+	if (ctl->key_idx != -1)
+		iwl_build_tx_cmd_hwcrypto(priv, ctl, out_cmd, skb, 0);
+
+	/* 802.11 null functions have no payload... */
+	len = skb->len - hdr_len;
+	if (len) {
+		phys_addr = pci_map_single(priv->pci_dev,
+						       skb->data + hdr_len,
+						       len, PCI_DMA_TODEVICE);
+		iwl_hw_tx_queue_attach_buffer_to_tfd(priv, tfd, phys_addr, len);
+	}
+
+	out_cmd->cmd.tx.len = skb->len;
+
+#if IWL == 3945
+	/* If there is no payload, then only one TFD is used */
+	if (!len)
+		*control_flags = TFD_CTL_COUNT_SET(1);
+	else
+		*control_flags = TFD_CTL_COUNT_SET(2) |
+			TFD_CTL_PAD_SET(U32_PAD(len));
+#else
+	if (len_org)
+		out_cmd->cmd.tx.tx_flags |= TX_CMD_FLG_MH_PAD_MSK;
+#endif
+
+	/* TODO need this for burst mode later on */
+	iwl_build_tx_cmd_basic(priv, out_cmd, ctl, hdr, unicast, sta_id);
+
+	/* set is_hcca to 0; it probably will never be implemented */
+	iwl_hw_build_tx_cmd_rate(priv, out_cmd, ctl, hdr, sta_id, 0);
+
+	len = out_cmd->cmd.tx.len;
+
+#if IWL == 4965
+	iwl4965_tx_cmd(priv, out_cmd, sta_id, txcmd_phys,
+		       hdr, hdr_len, ctl, NULL);
+#elif IWL == 3945
+	out_cmd->cmd.tx.tx_flags &= ~TX_CMD_FLG_ANT_A_MSK;
+	out_cmd->cmd.tx.tx_flags &= ~TX_CMD_FLG_ANT_B_MSK;
+#endif
+
+	if (!(ieee80211_get_morefrag(hdr))) {
+		txq->need_update = 1;
+		if ( qc ) {
+			u8 tid = (u8)(le16_to_cpu(*qc) & 0xf);
+			priv->stations[sta_id].tid[tid].seq_number =
+				seq_number;
+		}
+	} else {
+		wait_write_ptr = 1;
+		txq->need_update = 0;
+	}
+
+	printk_buf(IWL_DL_TX, out_cmd->cmd.payload, sizeof(out_cmd->cmd.tx));
+
+	printk_buf(IWL_DL_TX, (u8 *) out_cmd->cmd.tx.hdr,
+		   ieee80211_get_hdrlen(out_cmd->cmd.tx.hdr->frame_control));
+
+	iwl4965_tx_queue_update_wr_ptr(priv, txq, len);
+
+	q->first_empty = iwl_queue_inc_wrap(q->first_empty, q->n_bd);
+	rc = iwl_tx_queue_update_write_ptr(priv, txq);
+	spin_unlock_irqrestore(&priv->lock, flags);
+	if (rc)
+		return rc;
+
+	if ((iwl_queue_space(q) < q->high_mark)
+	    && priv->mac80211_registered) {
+		if (wait_write_ptr) {
+			spin_lock_irqsave(&priv->lock, flags);
+			txq->need_update = 1;
+			iwl_tx_queue_update_write_ptr(priv, txq);
+			spin_unlock_irqrestore(&priv->lock, flags);
+		}
+
+		ieee80211_stop_queue(priv->hw, ctl->queue);
+	}
+
+	return 0;
+
+ drop:
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return -1;
+}
+
+static void iwl_set_rate(struct iwl_priv *priv)
+{
+	const struct ieee80211_hw_mode *hw = NULL;
+	struct ieee80211_rate *rate;
+	int i;
+
+	hw = iwl_get_hw_mode(priv, priv->phymode);
+
+	priv->active_rate = 0;
+	priv->active_rate_basic = 0;
+
+	IWL_DEBUG_RATE("Setting rates for 802.11%c\n",
+		       ((hw->mode == MODE_IEEE80211A) ||
+			(hw->mode == MODE_ATHEROS_TURBO)) ?
+		       'a' : ((hw->mode == MODE_IEEE80211B) ? 'b' : 'g'));
+
+	for (i = 0; i < hw->num_rates; i++) {
+		rate = &(hw->rates[i]);
+		if ((rate->val < IWL_RATE_COUNT) &&
+		    (rate->flags & IEEE80211_RATE_SUPPORTED)) {
+			IWL_DEBUG_RATE("Adding rate index %d (plcp %d)%s\n",
+				       rate->val, iwl_rates[rate->val].plcp,
+				       (rate->flags & IEEE80211_RATE_BASIC) ?
+				       "*" : "");
+			priv->active_rate |= (1 << rate->val);
+			if (rate->flags & IEEE80211_RATE_BASIC)
+				priv->active_rate_basic |= (1 << rate->val);
+		} else {
+			IWL_DEBUG_RATE("Not adding rate %d (plcp %d)\n",
+				       rate->val, iwl_rates[rate->val].plcp);
+		}
+	}
+
+	IWL_DEBUG_RATE("Set active_rate = %0x, active_rate_basic = %0x\n",
+		       priv->active_rate, priv->active_rate_basic);
+
+	/*
+	 * If a basic rate is configured, then use it (adding IWL_RATE_1M_MASK)
+	 * otherwise set it to the default of all CCK rates and 6, 12, 24 for
+	 * OFDM
+	 */
+	if (priv->active_rate_basic & IWL_CCK_BASIC_RATES_MASK)
+		priv->staging_rxon.cck_basic_rates =
+		    ((priv->active_rate_basic &
+		      IWL_CCK_RATES_MASK) >> IWL_FIRST_CCK_RATE) & 0xF;
+	else
+		priv->staging_rxon.cck_basic_rates =
+		    (IWL_CCK_BASIC_RATES_MASK >> IWL_FIRST_CCK_RATE) & 0xF;
+
+	if (priv->active_rate_basic & IWL_OFDM_BASIC_RATES_MASK)
+		priv->staging_rxon.ofdm_basic_rates =
+		    ((priv->active_rate_basic &
+		      (IWL_OFDM_BASIC_RATES_MASK | IWL_RATE_6M_MASK)) >>
+		      IWL_FIRST_OFDM_RATE) & 0xFF;
+	else
+		priv->staging_rxon.ofdm_basic_rates =
+		   (IWL_OFDM_BASIC_RATES_MASK >> IWL_FIRST_OFDM_RATE) & 0xFF;
+}
+
+static void iwl_radio_kill_sw(struct iwl_priv *priv, int disable_radio)
+{
+	unsigned long flags;
+
+	if ((disable_radio ? 1 : 0) ==
+	    ((priv->status & STATUS_RF_KILL_SW) ? 1 : 0))
+		return;
+
+	IWL_DEBUG_RF_KILL("Manual SW RF KILL set to: RADIO %s\n",
+			  disable_radio ? "OFF" : "ON");
+
+	if (disable_radio) {
+		iwl_scan_cancel(priv, 0);
+		/* FIXME: This is a workaround for AP */
+		if (priv->iw_mode != IEEE80211_IF_TYPE_AP) {
+			spin_lock_irqsave(&priv->lock, flags);
+			iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
+				    CSR_UCODE_SW_BIT_RFKILL);
+			spin_unlock_irqrestore(&priv->lock, flags);
+			iwl_send_card_state(priv, CARD_STATE_CMD_DISABLE, 0);
+			priv->status |= STATUS_RF_KILL_SW;
+		}
+		return;
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+	iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
+
+	priv->status &= ~STATUS_RF_KILL_SW;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* wake up ucode */
+	msleep(10);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	iwl_read32(priv, CSR_UCODE_DRV_GP1);
+	if (!iwl_grab_restricted_access(priv))
+		iwl_release_restricted_access(priv);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	if (priv->status & STATUS_RF_KILL_HW) {
+		IWL_DEBUG_RF_KILL("Can not turn radio back on - "
+				  "disabled by HW switch\n");
+		return;
+	}
+
+	queue_work(priv->workqueue, &priv->restart);
+	return;
+}
+
+void iwl_set_decrypted_flag(struct iwl_priv *priv, struct sk_buff *skb,
+			    u32 decrypt_res, struct ieee80211_rx_status *stats)
+{
+	u16 fc =
+	    le16_to_cpu(((struct ieee80211_hdr *)skb->data)->frame_control);
+
+	if (priv->active_rxon.filter_flags & RXON_FILTER_DIS_DECRYPT_MSK)
+		return;
+
+	if (!(fc & IEEE80211_FCTL_PROTECTED))
+		return;
+
+	IWL_DEBUG_RX("decrypt_res:0x%x\n", decrypt_res);
+	switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) {
+	case RX_RES_STATUS_SEC_TYPE_TKIP:
+		if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
+		    RX_RES_STATUS_BAD_ICV_MIC)
+			stats->flag |= RX_FLAG_MMIC_ERROR;
+	case RX_RES_STATUS_SEC_TYPE_WEP:
+	case RX_RES_STATUS_SEC_TYPE_CCMP:
+		if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) ==
+		    RX_RES_STATUS_DECRYPT_OK) {
+			IWL_DEBUG_RX("hw decrypt successfully!!!\n");
+			stats->flag |= RX_FLAG_DECRYPTED;
+		}
+		break;
+
+	default:
+		break;
+	}
+}
+
+void iwl_handle_data_packet_monitor(struct iwl_priv *priv,
+				    struct iwl_rx_mem_buffer *rxb,
+				    void *data, short len,
+				    struct ieee80211_rx_status *stats,
+				    u16 phy_flags)
+{
+	struct iwl_rt_rx_hdr *iwl_rt;
+
+	/* First cache any information we need before we overwrite
+	 * the information provided in the skb from the hardware */
+	s8 signal = stats->ssi;
+	s8 noise = 0;
+	int rate = stats->rate;
+	u64 tsf = stats->mactime;
+
+	/* We received data from the HW, so stop the watchdog */
+	if (len > IWL_RX_BUF_SIZE - sizeof(*iwl_rt)) {
+		IWL_DEBUG_DROP("Dropping too large packet in monitor\n");
+		return;
+	}
+
+	/* copy the frame data to write after where the radiotap header goes */
+	iwl_rt = (void *)rxb->skb->data;
+	memmove(iwl_rt->payload, data, len);
+
+	iwl_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
+	iwl_rt->rt_hdr.it_pad = 0; /* always good to zero */
+
+	/* total header + data */
+	iwl_rt->rt_hdr.it_len = cpu_to_le16(sizeof(*iwl_rt));
+
+	/* Set the size of the skb to the size of the frame */
+	skb_put(rxb->skb, sizeof(*iwl_rt) + len);
+
+	/* Big bitfield of all the fields we provide in radiotap */
+	iwl_rt->rt_hdr.it_present =
+	    cpu_to_le32((1 << IEEE80211_RADIOTAP_TSFT) |
+			(1 << IEEE80211_RADIOTAP_FLAGS) |
+			(1 << IEEE80211_RADIOTAP_RATE) |
+			(1 << IEEE80211_RADIOTAP_CHANNEL) |
+			(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
+			(1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) |
+			(1 << IEEE80211_RADIOTAP_ANTENNA));
+
+	/* Zero the flags, we'll add to them as we go */
+	iwl_rt->rt_flags = 0;
+
+	iwl_rt->rt_tsf = cpu_to_le64(tsf);
+
+	/* Convert to dBm */
+	iwl_rt->rt_dbmsignal = signal;
+	iwl_rt->rt_dbmnoise = noise;
+
+	/* Convert the channel frequency and set the flags */
+	iwl_rt->rt_channelMHz = cpu_to_le16(stats->freq);
+	if (!(phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK)) {
+		iwl_rt->rt_chbitmask =
+		    cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ));
+	} else if (phy_flags & RX_RES_PHY_FLAGS_MOD_CCK_MSK) {
+		iwl_rt->rt_chbitmask =
+		    cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ));
+	} else {		/* 802.11g */
+		iwl_rt->rt_chbitmask =
+		    cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ));
+	}
+
+	rate = iwl_rate_index_from_plcp(rate);
+	if (rate == -1)
+		iwl_rt->rt_rate = 0;
+	else
+		iwl_rt->rt_rate = iwl_rates[rate].ieee;
+
+	/* antenna number */
+	iwl_rt->rt_antenna = (phy_flags & RX_RES_PHY_FLAGS_ANTENNA_MSK) >> 4;
+
+	/* set the preamble flag if we have it */
+	if (phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK)
+		iwl_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
+
+	IWL_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
+
+	stats->flag |= RX_FLAG_RADIOTAP;
+	ieee80211_rx_irqsafe(priv->hw, rxb->skb, stats);
+	rxb->skb = NULL;
+}
+
+
+#define IWL_PACKET_RETRY_TIME HZ
+
+int is_duplicate_packet(struct iwl_priv *priv, struct ieee80211_hdr *header)
+{
+	u16 sc = le16_to_cpu(header->seq_ctrl);
+	u16 seq = WLAN_GET_SEQ_SEQ(sc);
+	u16 frag = WLAN_GET_SEQ_FRAG(sc);
+	u16 *last_seq, *last_frag;
+	unsigned long *last_time;
+
+	switch (priv->iw_mode) {
+	case IEEE80211_IF_TYPE_IBSS:{
+		struct list_head *p;
+		struct iwl_ibss_seq *entry = NULL;
+		u8 *mac = header->addr2;
+		int index = mac[5] % IWL_IBSS_MAC_HASH_SIZE;
+
+		__list_for_each(p, &priv->ibss_mac_hash[index]) {
+			entry =
+				list_entry(p, struct iwl_ibss_seq, list);
+			if (!compare_ether_addr(entry->mac, mac))
+				break;
+		}
+		if (p == &priv->ibss_mac_hash[index]) {
+			entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
+			if (!entry) {
+				IWL_ERROR
+					("Cannot malloc new mac entry\n");
+				return 0;
+			}
+			memcpy(entry->mac, mac, ETH_ALEN);
+			entry->seq_num = seq;
+			entry->frag_num = frag;
+			entry->packet_time = jiffies;
+			list_add(&entry->list,
+				 &priv->ibss_mac_hash[index]);
+			return 0;
+		}
+		last_seq = &entry->seq_num;
+		last_frag = &entry->frag_num;
+		last_time = &entry->packet_time;
+		break;
+	}
+	case IEEE80211_IF_TYPE_STA:
+		last_seq = &priv->last_seq_num;
+		last_frag = &priv->last_frag_num;
+		last_time = &priv->last_packet_time;
+		break;
+	default:
+		return 0;
+	}
+	if ((*last_seq == seq) &&
+	    time_after(*last_time + IWL_PACKET_RETRY_TIME, jiffies)) {
+		if (*last_frag == frag)
+			goto drop;
+		if (*last_frag + 1 != frag)
+			/* out-of-order fragment */
+			goto drop;
+	} else
+		*last_seq = seq;
+
+	*last_frag = frag;
+	*last_time = jiffies;
+	return 0;
+
+ drop:
+	return 1;
+}
+
+#ifdef CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT
+
+#include "iwl-spectrum.h"
+
+#define BEACON_TIME_MASK_LOW	0x00FFFFFF
+#define BEACON_TIME_MASK_HIGH	0xFF000000
+#define TIME_UNIT		1024
+
+/*
+ * extended beacon time format
+ * time in usec will be changed into a 32-bit value in 8:24 format
+ * the high 1 byte is the beacon counts
+ * the lower 3 bytes is the time in usec within one beacon interval
+ */
+
+static u32 iwl_usecs_to_beacons(u32 usec, u32 beacon_interval)
+{
+	u32 quot;
+	u32 rem;
+	u32 interval = beacon_interval * 1024;
+
+	if (!interval || !usec)
+		return 0;
+
+	quot = (usec / interval) % 0x100;
+	rem = (usec % interval) % BEACON_TIME_MASK_LOW;
+
+	return (quot << 24) + rem;
+}
+
+/* base is usually what we get from ucode with each received frame,
+ * the same as HW timer counter counting down
+ */
+
+static u32 iwl_add_beacon_time(u32 base, u32 addon, u32 beacon_interval)
+{
+	u32 base_low = base & BEACON_TIME_MASK_LOW;
+	u32 addon_low = addon & BEACON_TIME_MASK_LOW;
+	u32 interval = beacon_interval * TIME_UNIT;
+	u32 res = (base & BEACON_TIME_MASK_HIGH) +
+	    (addon & BEACON_TIME_MASK_HIGH);
+
+	if (base_low > addon_low)
+		res += base_low - addon_low;
+	else if (base_low < addon_low) {
+		res += interval + base_low - addon_low;
+		res += (1 << 24);
+	} else
+		res += (1 << 24);
+
+	return res;
+}
+
+static int iwl_get_measurement(struct iwl_priv *priv,
+			       struct ieee80211_measurement_params *params,
+			       u8 type)
+{
+	struct iwl_spectrum_cmd spectrum;
+	struct iwl_rx_packet *res;
+	struct iwl_host_cmd cmd = {
+		.id = REPLY_SPECTRUM_MEASUREMENT_CMD,
+		.data = (void *)&spectrum,
+		.meta.flags = CMD_WANT_SKB,
+	};
+	u32 add_time = params->start_time;
+	int rc;
+
+	if (iwl_is_associated(priv))
+		add_time =
+		    iwl_usecs_to_beacons(params->start_time - priv->last_tsf,
+					 priv->rxon_timing.beacon_interval);
+
+	memset(&spectrum, 0, sizeof(spectrum));
+
+	spectrum.channel_count = cpu_to_le32(1);
+	spectrum.flags =
+	    RXON_FLG_TSF2HOST_MSK | RXON_FLG_ANT_A_MSK | RXON_FLG_DIS_DIV_MSK;
+	spectrum.filter_flags = MEASUREMENT_FILTER_FLAG;
+	cmd.len = sizeof(spectrum);
+	spectrum.len = cmd.len - sizeof(spectrum.len);
+
+	if (iwl_is_associated(priv))
+		spectrum.start_time =
+		    iwl_add_beacon_time(priv->last_beacon_time,
+					add_time,
+					priv->rxon_timing.beacon_interval);
+	else
+		spectrum.start_time = params->start_time;
+
+	spectrum.channels[0].duration = params->duration * TIME_UNIT;
+	spectrum.channels[0].channel = params->channel;
+	spectrum.channels[0].type = type;
+	if (priv->active_rxon.flags & RXON_FLG_BAND_24G_MSK)
+		spectrum.flags |= RXON_FLG_BAND_24G_MSK |
+		    RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_TGG_PROTECT_MSK;
+
+	rc = iwl_send_cmd(priv, &cmd);
+	if (rc)
+		return rc;
+
+	res = (struct iwl_rx_packet *)cmd.meta.u.skb->data;
+	if (res->hdr.flags & IWL_CMD_FAILED_MSK) {
+		IWL_ERROR("Bad return from REPLY_RX_ON_ASSOC command\n");
+		rc = -EIO;
+	}
+
+	switch (res->u.spectrum.status) {
+	case 0:		/* Command will be handled */
+		if (res->u.spectrum.id != 0xff) {
+			IWL_DEBUG_INFO
+			    ("Replaced existing measurement: %d\n",
+			     res->u.spectrum.id);
+			priv->measurement_status &= ~MEASUREMENT_READY;
+		}
+		priv->measurement_status |= MEASUREMENT_ACTIVE;
+		rc = 0;
+		break;
+
+	case 1:		/* Command will not be handled */
+		rc = -EAGAIN;
+		break;
+	}
+
+	dev_kfree_skb_any(cmd.meta.u.skb);
+
+	return rc;
+}
+#endif
+
+static void iwl_txstatus_to_ieee(struct iwl_priv *priv,
+				 struct iwl_tx_info *tx_sta)
+{
+
+	tx_sta->status.ack_signal = 0;
+	tx_sta->status.excessive_retries = 0;
+	tx_sta->status.queue_length = 0;
+	tx_sta->status.queue_number = 0;
+
+	if (in_interrupt())
+		ieee80211_tx_status_irqsafe(priv->hw,
+					    tx_sta->skb[0], &(tx_sta->status));
+	else
+		ieee80211_tx_status(priv->hw,
+				    tx_sta->skb[0], &(tx_sta->status));
+
+	tx_sta->skb[0] = NULL;
+}
+
+/**
+ * iwl_tx_queue_reclaim - Reclaim Tx queue entries no more used by NIC.
+ *
+ * When FW advances 'R' index, all entries between old and
+ * new 'R' index need to be reclaimed. As result, some free space
+ * forms. If there is enough free space (> low mark), wake Tx queue.
+ */
+int iwl_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index)
+{
+	struct iwl_tx_queue *txq = &priv->txq[txq_id];
+	struct iwl_queue *q = &txq->q;
+	u8 is_next = 0;
+	int used;
+	if ((index >= q->n_bd) || (x2_queue_used(q, index) == 0)) {
+		IWL_ERROR("Read index for DMA queue (%d) is out of "
+			  "range [0-%d) %d %d\n",
+			  index, q->n_bd, q->first_empty, q->last_used);
+		goto done;
+	}
+	index = iwl_queue_inc_wrap(index, q->n_bd);
+	for (; q->last_used != index;
+	     q->last_used = iwl_queue_inc_wrap(q->last_used, q->n_bd)) {
+		if (is_next) {
+			IWL_WARNING("command skipped\n");
+			queue_work(priv->workqueue, &priv->restart);
+		}
+		if (txq_id != IWL_CMD_QUEUE_NUM) {
+			iwl_txstatus_to_ieee(priv,
+					     &(txq->txb[txq->q.last_used]));
+			iwl_hw_tx_queue_free_tfd(priv, txq);
+		} else
+			is_next = 1;
+	}
+ done:
+	if (iwl_queue_space(q) > q->low_mark && (txq_id >= 0)
+	    && (txq_id != IWL_CMD_QUEUE_NUM)
+	    && priv->mac80211_registered)
+		ieee80211_wake_queue(priv->hw, txq_id);
+
+	used = q->first_empty - q->last_used;
+	if (used < 0)
+		used += q->n_window;
+	return used;
+}
+
+static int iwl_is_tx_success(u32 status)
+{
+#if IWL == 3945
+	return (status & 0xFF) == 0x1;
+#elif IWL == 4965
+	status &= TX_STATUS_MSK;
+	return (status == TX_STATUS_SUCCESS)
+	    || (status == TX_STATUS_DIRECT_DONE);
+#endif
+}
+
+/******************************************************************************
+ *
+ * Generic RX handler implementations
+ *
+ ******************************************************************************/
+#if IWL == 4965
+#ifdef  CONFIG_IWLWIFI_HT
+#ifdef  CONFIG_IWLWIFI_HT_AGG
+
+#define MAX_SN             ((IEEE80211_SCTL_SEQ) >> 4)
+
+static inline int iwl_get_ra_sta_id(struct iwl_priv *priv,
+				    struct ieee80211_hdr *hdr)
+{
+	if (priv->iw_mode == IEEE80211_IF_TYPE_STA)
+		return IWL_AP_ID;
+	else {
+		u8 *da = ieee80211_get_DA(hdr);
+		return iwl_hw_find_station(priv, da);
+	}
+}
+
+static struct ieee80211_hdr *iwl_tx_queue_get_hdr(
+	struct iwl_priv *priv, int txq_id, int idx)
+{
+	if (priv->txq[txq_id].txb[idx].skb[0])
+		return (struct ieee80211_hdr *)priv->txq[txq_id].
+				txb[idx].skb[0]->data;
+	return NULL;
+}
+
+int iwl4965_tx_status_reply_tx(struct iwl_priv *priv,
+				      struct iwl_ht_agg *agg,
+				      struct iwl_tx_resp *tx_resp,
+				      u16 start_idx)
+{
+	u16 status;
+	u16 seq;
+	u32 *frame_status = (u32 *)&tx_resp->status;
+
+	int txq_id, idx;
+	struct ieee80211_tx_status *tx_status = NULL;
+
+	struct ieee80211_hdr *hdr;
+	int i, sh;
+
+	if (agg->wait_for_ba) {
+		IWL_DEBUG_TX_REPLY("got tx repsons w/o back\n");
+	}
+
+
+	agg->frame_count = tx_resp->frame_count;
+	agg->start_idx = start_idx;
+	memcpy(&agg->rate, &tx_resp->rate, sizeof(agg->rate));
+	agg->bitmap0 = agg->bitmap1 = 0;
+	if (agg->frame_count == 1) {
+		status = frame_status[0] & 0xff;
+		seq  = frame_status[0] >> 16;
+		idx = SEQ_TO_INDEX(seq);
+		txq_id = SEQ_TO_QUEUE(seq);
+
+/* FIXME: code repetition */
+		IWL_DEBUG_TX_REPLY("FrameCnt = %d, StartIdx=%d idx=%d\n",
+				agg->frame_count, agg->start_idx, idx);
+
+		tx_status = &(priv->txq[txq_id].txb[idx].status);
+		tx_status->retry_count = tx_resp->failure_frame;
+		tx_status->queue_number = tx_resp->status;
+		tx_status->queue_length = tx_resp->bt_kill_count;
+		tx_status->queue_length |= tx_resp->failure_rts;
+
+		tx_status->flags = iwl_is_tx_success(tx_resp->status)?
+			IEEE80211_TX_STATUS_ACK : 0;
+		tx_status->control.tx_rate = tx_resp->rate.s.rate |
+					     (tx_resp->rate.s.flags << 8);
+
+/* FIXME: code repetition end */
+
+
+		IWL_DEBUG_TX_REPLY("1 Frame 0x%x idx %d failure :%d\n",
+				    status & 0xff, idx, tx_resp->failure_frame);
+		IWL_DEBUG_TX_REPLY("Rate Info rate=%d flags=0x%x extf=0x%x\n",
+				    tx_resp->rate.s.rate, tx_resp->rate.s.flags,
+				    tx_resp->rate.s.ext_flags);
+
+		agg->wait_for_ba = 0;
+	} else {
+		u64 bitmap = 0;
+		int start = agg->start_idx;
+		for (i = 0; i < agg->frame_count; i++) {
+
+			status = frame_status[i] & 0xff;
+			seq  = frame_status[i] >> 16;
+			idx = SEQ_TO_INDEX(seq);
+			txq_id = SEQ_TO_QUEUE(seq);
+
+			IWL_DEBUG_TX_REPLY("FrameCnt = %d, txq_id=%d idx=%d\n",
+					   agg->frame_count, txq_id, idx);
+			hdr = iwl_tx_queue_get_hdr(priv, txq_id, idx);
+
+			if (idx != (SEQ_TO_SN(hdr->seq_ctrl) & 0xff)) {
+				IWL_ERROR("BUG_ON idx doesn't match seq control"
+					  " idx=%d, seq_idx=%d, seq=%d\n",
+					  idx, SEQ_TO_SN(hdr->seq_ctrl),
+					  hdr->seq_ctrl);
+				return -1;
+			}
+
+
+			IWL_DEBUG_TX_REPLY("AGG Frame i=%d idx %d seq=%d\n",
+					   i, idx, SEQ_TO_SN(hdr->seq_ctrl));
+
+
+			sh = idx - start;
+			if (sh > 64 ) {
+				sh = (start - idx) + 0xff;
+				bitmap = bitmap << sh;
+				sh = 0;
+				start = idx;
+			} else if ( sh < -64) {
+				sh  = 0xff - (start - idx);
+			} else if (sh < 0) {
+				sh = start - idx;
+				start = idx;
+				bitmap = bitmap << sh;
+				sh = 0;
+			}
+			bitmap |= (1 << sh);
+			IWL_DEBUG_TX_REPLY("start=%d bitmap=0x%x\n",
+					   start, (u32)(bitmap & 0xFFFFFFFF));
+		}
+
+		agg->bitmap0 = bitmap & 0xFFFFFFFF;
+		agg->bitmap1 = bitmap >> 32;
+		agg->start_idx = start;
+		memcpy(&agg->rate, &tx_resp->rate, sizeof(agg->rate));
+		IWL_DEBUG_TX_REPLY("Frames %d start_idx=%d bitmap=0x%x\n",
+				agg->frame_count,
+				agg->start_idx,
+				agg->bitmap0);
+
+		agg->wait_for_ba = 1;
+	}
+	return 0;
+}
+#endif
+#endif
+#endif
+
+static void iwl_rx_reply_tx(struct iwl_priv *priv,
+			    struct iwl_rx_mem_buffer *rxb)
+{
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	u16 sequence = pkt->hdr.sequence;
+	int txq_id = SEQ_TO_QUEUE(sequence);
+	int index = SEQ_TO_INDEX(sequence);
+	struct iwl_tx_queue *txq = &priv->txq[txq_id];
+	struct ieee80211_tx_status *status;
+	struct iwl_tx_resp *resp = (void *)&pkt->u.raw[0];
+#if IWL == 4965
+#ifdef  CONFIG_IWLWIFI_HT
+#ifdef  CONFIG_IWLWIFI_HT_AGG
+	int tid, sta_id;
+#endif
+#endif
+#endif
+
+	if ((index >= txq->q.n_bd) || (x2_queue_used(&txq->q, index) == 0)) {
+		IWL_ERROR("Read index for DMA queue (%d) "
+			  "is out of range [0-%d) %d %d\n",
+			  index, txq->q.n_bd, txq->q.first_empty,
+			  txq->q.last_used);
+		return;
+	}
+
+#if IWL == 4965
+#ifdef  CONFIG_IWLWIFI_HT
+#ifdef  CONFIG_IWLWIFI_HT_AGG
+	if (txq->sched_retry) {
+		const int scd_ssn = *((u32 *)&resp->status + resp->frame_count) &
+				      MAX_SN;
+		struct ieee80211_hdr *hdr = iwl_tx_queue_get_hdr(priv, txq_id,
+								 index);
+		struct iwl_ht_agg *agg = NULL;
+		__le16 *qc = ieee80211_get_qos_ctrl(hdr);
+
+		if(qc == NULL) {
+			IWL_ERROR("BUG_ON qc is null!!!!\n");
+			return;
+		}
+
+		tid = le16_to_cpu(*qc) & 0xf;
+
+		sta_id = iwl_get_ra_sta_id(priv, hdr);
+		if (unlikely(sta_id == IWL_INVALID_STATION)) {
+			IWL_ERROR("Station not known for\n");
+			return;
+		}
+
+		agg = &priv->stations[sta_id].tid[tid].agg;
+
+		iwl4965_tx_status_reply_tx(priv, agg, resp, index);
+
+		if ((resp->frame_count == 1) && !iwl_is_tx_success(resp->status) ) {
+
+		}
+
+		if (txq->q.last_used != (scd_ssn & 0xff)) {
+			index = iwl_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd);
+			IWL_DEBUG_TX_REPLY("Retry scheduler reclaim scd_ssn %d index %d\n",
+					    scd_ssn , index);
+			iwl_tx_queue_reclaim(priv, txq_id, index);
+		}
+	} else {
+#endif /* CONFIG_IWLWIFI_HT_AGG */
+#endif /* CONFIG_IWLWIFI_HT */
+#endif /* 4965 */
+	status = &(txq->txb[txq->q.last_used].status);
+
+	status->retry_count = resp->failure_frame;
+	status->queue_number = resp->status;
+	status->queue_length = resp->bt_kill_count;
+	status->queue_length |= resp->failure_rts;
+
+	status->flags =
+	    iwl_is_tx_success(resp->status) ? IEEE80211_TX_STATUS_ACK : 0;
+
+#if IWL == 3945
+
+	status->control.tx_rate = iwl_rate_index_from_plcp(resp->rate);
+
+	IWL_DEBUG_TX("Tx queue %d Status %s (0x%08x) plcp rate %d retries %d\n",
+		     txq_id, iwl_get_tx_fail_reason(resp->status),
+		     resp->status, resp->rate, resp->failure_frame);
+
+#elif IWL == 4965
+
+	status->control.tx_rate = (u16) resp->rate.rate_n_flags;
+
+	IWL_DEBUG_TX("Tx queue %d Status %s (0x%08x) plcp rate 0x%x%02x	"
+			"retries %d\n",
+		     txq_id, iwl_get_tx_fail_reason(resp->status),
+		     resp->status, resp->rate.s.flags,
+		     resp->rate.s.rate, resp->failure_frame);
+#endif
+
+	IWL_DEBUG_TX_REPLY("Tx queue reclaim %d\n", index);
+	if (index != -1)
+		iwl_tx_queue_reclaim(priv, txq_id, index);
+#if IWL == 4965
+#ifdef  CONFIG_IWLWIFI_HT
+#ifdef  CONFIG_IWLWIFI_HT_AGG
+	}
+#endif /* CONFIG_IWLWIFI_HT_AGG */
+#endif /* CONFIG_IWLWIFI_HT */
+#endif /* 4965 */
+
+	if (iwl_check_bits(resp->status, TX_ABORT_REQUIRED_MSK))
+		IWL_ERROR("TODO:  Implement Tx ABORT REQUIRED!!!\n");
+}
+
+
+static void iwl_rx_reply_alive(struct iwl_priv *priv,
+			   struct iwl_rx_mem_buffer *rxb)
+{
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	struct iwl_alive_resp *palive;
+	struct delayed_work *pwork;
+
+	palive = &pkt->u.alive_frame;
+
+	IWL_DEBUG_INFO("Alive ucode status 0x%08X revision "
+		       "0x%01X 0x%01X\n",
+		       palive->is_valid, palive->ver_type,
+		       palive->ver_subtype);
+
+	if (palive->ver_subtype == INITIALIZE_SUBTYPE) {
+		IWL_DEBUG_INFO("Initialization Alive received.\n");
+		memcpy(&priv->card_alive_init,
+		       &pkt->u.alive_frame,
+		       sizeof(struct iwl_init_alive_resp));
+		pwork = &priv->init_alive_start;
+	} else {
+		IWL_DEBUG_INFO("Runtime Alive received.\n");
+		memcpy(&priv->card_alive, &pkt->u.alive_frame,
+		       sizeof(struct iwl_alive_resp));
+		pwork = &priv->alive_start;
+#if IWL == 3945
+		/* For debugging (selective disable not supported in 4965) */
+		iwl_disable_events(priv);
+#endif
+	}
+
+	/* We delay the ALIVE response by 5ms to
+	 * give the HW RF Kill time to activate... */
+	if (palive->is_valid == UCODE_VALID_OK)
+		queue_delayed_work(priv->workqueue, pwork,
+				   msecs_to_jiffies(5));
+	else
+		IWL_WARNING("uCode did not respond OK.\n");
+}
+
+static void iwl_rx_reply_add_sta(struct iwl_priv *priv,
+				 struct iwl_rx_mem_buffer *rxb)
+{
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	IWL_DEBUG_RX("Received REPLY_ADD_STA: 0x%02X\n", pkt->u.status);
+	return;
+}
+
+static void iwl_rx_reply_error(struct iwl_priv *priv,
+			       struct iwl_rx_mem_buffer *rxb)
+{
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	u32 err_type = pkt->u.err_resp.error_type;
+	u8 cmd_id = pkt->u.err_resp.cmd_id;
+	u16 seq = pkt->u.err_resp.bad_cmd_seq_num;
+	u32 ser = pkt->u.err_resp.error_info;
+	IWL_ERROR("Error Reply type 0x%08X cmd %s (0x%02X) "
+		  "seq 0x%04X ser 0x%08X\n",
+		  err_type, get_cmd_string(cmd_id), cmd_id, seq, ser);
+	return;
+}
+
+#define TX_STATUS_ENTRY(x) case TX_STATUS_FAIL_ ## x: return #x
+
+static void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
+{
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	struct iwl_rxon_cmd *rxon = (void *)&priv->active_rxon;
+	struct iwl_csa_notification *csa = &(pkt->u.csa_notif);
+	IWL_DEBUG_11H("CSA notif: channel %d, status %d\n",
+		      csa->channel, csa->status);
+	rxon->channel = priv->staging_rxon.channel = cpu_to_le16(csa->channel);
+}
+
+static void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv,
+					  struct iwl_rx_mem_buffer *rxb)
+{
+#ifdef CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	struct iwl_spectrum_notification *report = &(pkt->u.spectrum_notif);
+
+	if (!report->state) {
+		IWL_DEBUG(IWL_DL_11H | IWL_DL_INFO,
+			  "Spectrum Measure Notification: Start\n");
+		return;
+	}
+
+	memcpy(&priv->measure_report, report, sizeof(*report));
+	priv->measurement_status |= MEASUREMENT_READY;
+#endif
+}
+
+static void iwl_rx_pm_sleep_notif(struct iwl_priv *priv,
+				  struct iwl_rx_mem_buffer *rxb)
+{
+#ifdef CONFIG_IWLWIFI_DEBUG
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	struct iwl_sleep_notification *sleep = &(pkt->u.sleep_notif);
+	IWL_DEBUG_RX("sleep mode: %d, src: %d\n",
+		     sleep->pm_sleep_mode, sleep->pm_wakeup_src);
+#endif
+}
+
+static void iwl_rx_pm_debug_statistics_notif(struct iwl_priv *priv,
+					     struct iwl_rx_mem_buffer *rxb)
+{
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	IWL_DEBUG_RADIO("Dumping %d bytes of unhandled "
+			"notification for %s:\n",
+			le32_to_cpu(pkt->len), get_cmd_string(pkt->hdr.cmd));
+	printk_buf(IWL_DL_RADIO, pkt->u.raw, le32_to_cpu(pkt->len));
+}
+
+static void iwl_rx_beacon_notif(struct iwl_priv *priv,
+				struct iwl_rx_mem_buffer *rxb)
+{
+#ifdef CONFIG_IWLWIFI_DEBUG
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	struct iwl_beacon_notif *beacon = &(pkt->u.beacon_status);
+#if IWL == 3945
+	u8 rate = beacon->beacon_notify_hdr.rate;
+#elif IWL == 4965
+	u8 rate = beacon->beacon_notify_hdr.rate.s.rate;
+#endif
+	IWL_DEBUG_RX("beacon status %x retries %d iss %d "
+		     "tsf %d %d rate %d\n",
+		     beacon->beacon_notify_hdr.status & TX_STATUS_MSK,
+		     beacon->beacon_notify_hdr.failure_frame,
+		     beacon->ibss_mgr_status,
+		     beacon->high_tsf, beacon->low_tsf, rate);
+#endif
+}
+
+static void iwl_rx_reply_scan(struct iwl_priv *priv,
+			      struct iwl_rx_mem_buffer *rxb)
+{
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	struct iwl_scanreq_notification *notif =
+	    (struct iwl_scanreq_notification *)pkt->u.raw;
+	IWL_DEBUG_RX("Scan request status = 0x%x\n", notif->status);
+}
+
+static void iwl_rx_scan_start_notif(struct iwl_priv *priv,
+				    struct iwl_rx_mem_buffer *rxb)
+{
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	struct iwl_scanstart_notification *notif =
+	    (struct iwl_scanstart_notification *)pkt->u.raw;
+	priv->scan_start_tsf = notif->tsf_low;
+	IWL_DEBUG_SCAN("Scan start: "
+		       "%d [802.11%s] "
+		       "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n",
+		       notif->channel,
+		       notif->band ? "bg" : "a",
+		       notif->tsf_high,
+		       notif->tsf_low, notif->status, notif->beacon_timer);
+}
+
+static void iwl_rx_scan_results_notif(struct iwl_priv *priv,
+				      struct iwl_rx_mem_buffer *rxb)
+{
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	struct iwl_scanresults_notification *notif =
+	    (struct iwl_scanresults_notification *)pkt->u.raw;
+
+	IWL_DEBUG_SCAN("Scan ch.res: "
+		       "%d [802.11%s] "
+		       "(TSF: 0x%08X:%08X) - %d "
+		       "elapsed=%lu usec (%dms since last)\n",
+		       notif->channel,
+		       notif->band ? "bg" : "a",
+		       notif->tsf_high,
+		       notif->tsf_low,
+		       notif->statistics[0],
+		       notif->tsf_low - priv->scan_start_tsf,
+		       jiffies_to_msecs(elapsed_jiffies
+					(priv->last_scan_jiffies, jiffies)));
+
+	priv->last_scan_jiffies = jiffies;
+}
+
+static void iwl_rx_scan_complete_notif(struct iwl_priv *priv,
+				       struct iwl_rx_mem_buffer *rxb)
+{
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	struct iwl_scancomplete_notification *scan_notif =
+	    (struct iwl_scancomplete_notification *)pkt->u.raw;
+	IWL_DEBUG_SCAN("Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n",
+		       scan_notif->scanned_channels,
+		       scan_notif->tsf_low,
+		       scan_notif->tsf_high, scan_notif->status);
+
+	/* The HW is no longer scanning */
+	priv->status &= ~STATUS_SCAN_HW;
+
+	/* The scan completion notification came in, so kill that timer... */
+	cancel_delayed_work(&priv->scan_check);
+
+	IWL_DEBUG_INFO("Scan pass on %sGHz took %dms\n",
+		       (priv->scan_bands == 2) ? "2.4" : "5.2",
+		       jiffies_to_msecs(elapsed_jiffies
+					(priv->scan_pass_start, jiffies)));
+
+	/* Remove this scanned band from the list
+	 * of pending bands to scan */
+	priv->scan_bands--;
+
+	/* If a request to abort was given, or the scan did not succeed
+	 * then we reset the scan state machine and terminate,
+	 * re-queuing another scan if one has been requested */
+	if (priv->status & STATUS_SCAN_ABORTING) {
+		IWL_DEBUG_INFO("Aborted scan completed.\n");
+		priv->status &= ~STATUS_SCAN_ABORTING;
+	} else {
+		/* If there are more bands on this scan pass reschedule */
+		if (priv->scan_bands > 0)
+			goto reschedule;
+	}
+
+	priv->last_scan_jiffies = jiffies;
+	IWL_DEBUG_INFO("Setting scan to off\n");
+
+	priv->status &= ~STATUS_SCANNING;
+
+	IWL_DEBUG_INFO("Scan took %dms\n",
+		       jiffies_to_msecs(elapsed_jiffies
+					(priv->scan_start, jiffies)));
+
+	queue_work(priv->workqueue, &priv->scan_completed);
+
+	return;
+
+ reschedule:
+	priv->scan_pass_start = jiffies;
+	queue_work(priv->workqueue, &priv->request_scan);
+}
+
+/* Handle notification from uCode that card's power state is changing
+ * due to software, hardware, or critical temperature RFKILL */
+static void iwl_rx_card_state_notif(struct iwl_priv *priv,
+				    struct iwl_rx_mem_buffer *rxb)
+{
+	struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+	u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags);
+	u32 status = priv->status;
+	IWL_DEBUG_RF_KILL("Card state received: HW:%s SW:%s\n",
+			  (flags & HW_CARD_DISABLED) ? "Kill" : "On",
+			  (flags & SW_CARD_DISABLED) ? "Kill" : "On");
+#if IWL == 4965
+	if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED |
+		     RF_CARD_DISABLED)) {
+
+		iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
+			    CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
+
+		if (!iwl_grab_restricted_access(priv)) {
+			iwl_write_restricted(
+				priv, HBUS_TARG_MBX_C,
+				HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
+
+			iwl_release_restricted_access(priv);
+		}
+
+		if (!(flags & RXON_CARD_DISABLED)) {
+			iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
+				    CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
+			if (!iwl_grab_restricted_access(priv)) {
+				iwl_write_restricted(
+					priv, HBUS_TARG_MBX_C,
+					HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
+
+				iwl_release_restricted_access(priv);
+			}
+		}
+
+		if (flags & RF_CARD_DISABLED) {
+			iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
+				    CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
+			iwl_read32(priv, CSR_UCODE_DRV_GP1);
+			if (!iwl_grab_restricted_access(priv))
+				iwl_release_restricted_access(priv);
+		}
+	}
+#else
+	iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
+		    CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
+#endif
+	if (flags & HW_CARD_DISABLED)
+		priv->status |= STATUS_RF_KILL_HW;
+	else
+		priv->status &= ~STATUS_RF_KILL_HW;
+
+
+	if (flags & SW_CARD_DISABLED)
+		priv->status |= STATUS_RF_KILL_SW;
+	else
+		priv->status &= ~STATUS_RF_KILL_SW;
+
+#if IWL == 4965
+	if (!(flags & RXON_CARD_DISABLED))
+		iwl_scan_cancel(priv, 0);
+#else
+		iwl_scan_cancel(priv, 0);
+#endif
+
+	if (((status & STATUS_RF_KILL_HW) != (priv->status & STATUS_RF_KILL_HW))
+	    || ((status & STATUS_RF_KILL_SW) !=
+		(priv->status & STATUS_RF_KILL_SW)))
+		queue_work(priv->workqueue, &priv->rf_kill);
+	else
+		wake_up_interruptible(&priv->wait_command_queue);
+}
+
+/**
+ * iwl_setup_rx_handlers - Initialize Rx handler callbacks
+ *
+ * Setup the RX handlers for each of the reply types sent from the uCode
+ * to the host.
+ *
+ * This function chains into the hardware specific files for them to setup
+ * any hardware specific handlers as well.
+ */
+static void iwl_setup_rx_handlers(struct iwl_priv *priv)
+{
+	priv->rx_handlers[REPLY_ALIVE] = iwl_rx_reply_alive;
+	priv->rx_handlers[REPLY_ADD_STA] = iwl_rx_reply_add_sta;
+	priv->rx_handlers[REPLY_ERROR] = iwl_rx_reply_error;
+	priv->rx_handlers[CHANNEL_SWITCH_NOTIFICATION] = iwl_rx_csa;
+	priv->rx_handlers[SPECTRUM_MEASURE_NOTIFICATION] =
+	    iwl_rx_spectrum_measure_notif;
+	priv->rx_handlers[PM_SLEEP_NOTIFICATION] = iwl_rx_pm_sleep_notif;
+	priv->rx_handlers[PM_DEBUG_STATISTIC_NOTIFIC] =
+	    iwl_rx_pm_debug_statistics_notif;
+	priv->rx_handlers[BEACON_NOTIFICATION] = iwl_rx_beacon_notif;
+
+	/* NOTE:  iwl_rx_statistics is different based on whether
+	 * the build is for the 3945 or the 4965.  See the
+	 * corresponding implementation in iwl-XXXX.c
+	 *
+	 * The same handler is used for both the REPLY to a
+	 * discrete statistics request from the host as well as
+	 * for the periodic statistics notification from the uCode
+	 */
+	priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl_hw_rx_statistics;
+	priv->rx_handlers[STATISTICS_NOTIFICATION] = iwl_hw_rx_statistics;
+
+	priv->rx_handlers[REPLY_SCAN_CMD] = iwl_rx_reply_scan;
+	priv->rx_handlers[SCAN_START_NOTIFICATION] = iwl_rx_scan_start_notif;
+	priv->rx_handlers[SCAN_RESULTS_NOTIFICATION] =
+	    iwl_rx_scan_results_notif;
+	priv->rx_handlers[SCAN_COMPLETE_NOTIFICATION] =
+	    iwl_rx_scan_complete_notif;
+	priv->rx_handlers[CARD_STATE_NOTIFICATION] = iwl_rx_card_state_notif;
+	priv->rx_handlers[REPLY_TX] = iwl_rx_reply_tx;
+
+	/* Setup hardware specific Rx handlers */
+	iwl_hw_rx_handler_setup(priv);
+}
+
+/**
+ * iwl_tx_cmd_complete - Pull unused buffers off the queue and reclaim them
+ * @rxb: Rx buffer to reclaim
+ *
+ * If an Rx buffer has an async callback associated with it the callback
+ * will be executed.  The attached skb (if present) will only be freed
+ * if the callback returns 1
+ */
+static void iwl_tx_cmd_complete(struct iwl_priv *priv,
+				struct iwl_rx_mem_buffer *rxb)
+{
+	struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+	int txq_id = SEQ_TO_QUEUE(pkt->hdr.sequence);
+	int index = SEQ_TO_INDEX(pkt->hdr.sequence);
+	int is_huge = (pkt->hdr.sequence & SEQ_HUGE_FRAME);
+	int cmd_index;
+	struct iwl_cmd *cmd;
+
+	/* If a Tx command is being handled and it isn't in the actual
+	 * command queue then there a command routing bug has been introduced
+	 * in the queue management code. */
+	if (txq_id != IWL_CMD_QUEUE_NUM)
+		IWL_ERROR("Error wrong command queue %d command id 0x%X\n",
+			  txq_id, pkt->hdr.cmd);
+	BUG_ON(txq_id != IWL_CMD_QUEUE_NUM);
+
+	cmd_index = get_next_cmd_index(&priv->txq[IWL_CMD_QUEUE_NUM].q, index,
+				       is_huge);
+	cmd = &priv->txq[IWL_CMD_QUEUE_NUM].cmd[cmd_index];
+
+	/* Input error checking is done when commands are added to queue. */
+	if (cmd->meta.flags & CMD_WANT_SKB) {
+		/* FIXME: we use cmd->meta.magic to indicate the
+		 * memory cmd->meta.source points to is still valid or
+		 * not at this point since caller may pass a local
+		 * variable to us and returned before we get here.
+		 * In this case, caller must ensure the ->magic field
+		 * is set correctly to indicate the availability of the
+		 * pointer cmd->meta.source. */
+		if (cmd->meta.source->magic == CMD_VAR_MAGIC) {
+			cmd->meta.source->u.skb = rxb->skb;
+			cmd->meta.source->magic = 0;
+			rxb->skb = NULL;
+		}
+	} else if (cmd->meta.u.callback &&
+		   !cmd->meta.u.callback(priv, cmd, rxb->skb))
+		rxb->skb = NULL;
+
+	iwl_tx_queue_reclaim(priv, txq_id, index);
+
+	if (!(cmd->meta.flags & CMD_ASYNC)) {
+		priv->status &= ~STATUS_HCMD_ACTIVE;
+		wake_up_interruptible(&priv->wait_command_queue);
+	}
+}
+
+/************************** RX-FUNCTIONS ****************************/
+/*
+ * Rx theory of operation
+ *
+ * The host allocates 32 DMA target addresses and passes the host address
+ * to the firmware at register IWL_RFDS_TABLE_LOWER + N * RFD_SIZE where N is
+ * 0 to 31
+ *
+ * Rx Queue Indexes
+ * The host/firmware share two index registers for managing the Rx buffers.
+ *
+ * The READ index maps to the first position that the firmware may be writing
+ * to -- the driver can read up to (but not including) this position and get
+ * good data.
+ * The READ index is managed by the firmware once the card is enabled.
+ *
+ * The WRITE index maps to the last position the driver has read from -- the
+ * position preceding WRITE is the last slot the firmware can place a packet.
+ *
+ * The queue is empty (no good data) if WRITE = READ - 1, and is full if
+ * WRITE = READ.
+ *
+ * During initialization the host sets up the READ queue position to the first
+ * INDEX position, and WRITE to the last (READ - 1 wrapped)
+ *
+ * When the firmware places a packet in a buffer it will advance the READ index
+ * and fire the RX interrupt.  The driver can then query the READ index and
+ * process as many packets as possible, moving the WRITE index forward as it
+ * resets the Rx queue buffers with new memory.
+ *
+ * The management in the driver is as follows:
+ * + A list of pre-allocated SKBs is stored in ipw->rxq->rx_free.  When
+ *   ipw->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled
+ *   to replensish the ipw->rxq->rx_free.
+ * + In iwl_rx_replenish (scheduled) if 'processed' != 'read' then the
+ *   ipw->rxq is replenished and the READ INDEX is updated (updating the
+ *   'processed' and 'read' driver indexes as well)
+ * + A received packet is processed and handed to the kernel network stack,
+ *   detached from the ipw->rxq.  The driver 'processed' index is updated.
+ * + The Host/Firmware ipw->rxq is replenished at tasklet time from the rx_free
+ *   list. If there are no allocated buffers in ipw->rxq->rx_free, the READ
+ *   INDEX is not incremented and ipw->status(RX_STALLED) is set.  If there
+ *   were enough free buffers and RX_STALLED is set it is cleared.
+ *
+ *
+ * Driver sequence:
+ *
+ * iwl_rx_queue_alloc()       Allocates rx_free
+ * iwl_rx_replenish()         Replenishes rx_free list from rx_used, and calls
+ *                            iwl_rx_queue_restock
+ * iwl_rx_queue_restock()     Moves available buffers from rx_free into Rx
+ *                            queue, updates firmware pointers, and updates
+ *                            the WRITE index.  If insufficient rx_free buffers
+ *                            are available, schedules iwl_rx_replenish
+ *
+ * -- enable interrupts --
+ * ISR - iwl_rx()             Detach iwl_rx_mem_buffers from pool up to the
+ *                            READ INDEX, detaching the SKB from the pool.
+ *                            Moves the packet buffer from queue to rx_used.
+ *                            Calls iwl_rx_queue_restock to refill any empty
+ *                            slots.
+ * ...
+ *
+ */
+
+/**
+ * iwl_rx_queue_space - Return number of free slots available in queue.
+ */
+static int iwl_rx_queue_space(const struct iwl_rx_queue *q)
+{
+	int s = q->read - q->write;
+	if (s <= 0)
+		s += RX_QUEUE_SIZE;
+	/* keep some buffer to not confuse full and empty queue */
+	s -= 2;
+	if (s < 0)
+		s = 0;
+	return s;
+}
+
+/**
+ * iwl_rx_queue_update_write_ptr - Update the write pointer for the RX queue
+ *
+ * NOTE: This function has 3945 and 4965 specific code sections
+ * but is declared in base due to the majority of the
+ * implementation being the same (only a numeric constant is
+ * different)
+ *
+ */
+int iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, struct iwl_rx_queue *q)
+{
+	u32 reg = 0;
+	int rc = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&q->lock, flags);
+
+	if (q->need_update == 0)
+		goto exit_unlock;
+
+	if (priv->status & STATUS_POWER_PMI) {
+		reg = iwl_read32(priv, CSR_UCODE_DRV_GP1);
+
+		if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
+			iwl_set_bit(priv, CSR_GP_CNTRL,
+				    CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+			goto exit_unlock;
+		}
+
+		rc = iwl_grab_restricted_access(priv);
+		if (rc)
+			goto exit_unlock;
+
+		iwl_write_restricted(priv,
+				     FH_RSCSR_CHNL0_WPTR, q->write & ~0x7);
+		iwl_release_restricted_access(priv);
+	} else
+		iwl_write32(priv, FH_RSCSR_CHNL0_WPTR, q->write & ~0x7);
+
+
+	q->need_update = 0;
+
+ exit_unlock:
+	spin_unlock_irqrestore(&q->lock, flags);
+	return rc;
+}
+
+/**
+ * iwl_rx_queue_restock - refill RX queue from pre-allocated pool
+ *
+ * If there are slots in the RX queue that  need to be restocked,
+ * and we have free pre-allocated buffers, fill the ranks as much
+ * as we can pulling from rx_free.
+ *
+ * This moves the 'write' index forward to catch up with 'processed', and
+ * also updates the memory address in the firmware to reference the new
+ * target buffer.
+ */
+int iwl_rx_queue_restock(struct iwl_priv *priv)
+{
+	struct iwl_rx_queue *rxq = &priv->rxq;
+	struct list_head *element;
+	struct iwl_rx_mem_buffer *rxb;
+	unsigned long flags;
+	int write, rc;
+
+	spin_lock_irqsave(&rxq->lock, flags);
+	write = rxq->write & ~0x7;
+	while ((iwl_rx_queue_space(rxq) > 0) && (rxq->free_count)) {
+		element = rxq->rx_free.next;
+		rxb = list_entry(element, struct iwl_rx_mem_buffer, list);
+		list_del(element);
+		rxq->bd[rxq->write] =
+		    iwl_dma_addr2rbd_ptr(priv, rxb->dma_addr);
+		rxq->queue[rxq->write] = rxb;
+		rxq->write = (rxq->write + 1) % RX_QUEUE_SIZE;
+		rxq->free_count--;
+	}
+	spin_unlock_irqrestore(&rxq->lock, flags);
+	/* If the pre-allocated buffer pool is dropping low, schedule to
+	 * refill it */
+	if (rxq->free_count <= RX_LOW_WATERMARK)
+		queue_work(priv->workqueue, &priv->rx_replenish);
+
+
+	/* If we've added more space for the firmware to place data, tell it */
+	if ((write != (rxq->write & ~0x7))
+	    || (abs(rxq->write - rxq->read) > 7)) {
+		spin_lock_irqsave(&rxq->lock, flags);
+		rxq->need_update = 1;
+		spin_unlock_irqrestore(&rxq->lock, flags);
+		rc = iwl_rx_queue_update_write_ptr(priv, rxq);
+		if (rc)
+			return rc;
+	}
+
+	return 0;
+}
+
+/**
+ * iwl_rx_replensih - Move all used packet from rx_used to rx_free
+ *
+ * When moving to rx_free an SKB is allocated for the slot.
+ *
+ * Also restock the Rx queue via iwl_rx_queue_restock.
+ * This is called as a scheduled work item (except for during intialization)
+ */
+void iwl_rx_replenish(void *data)
+{
+	struct iwl_priv *priv = data;
+	struct iwl_rx_queue *rxq = &priv->rxq;
+	struct list_head *element;
+	struct iwl_rx_mem_buffer *rxb;
+	unsigned long flags;
+	spin_lock_irqsave(&rxq->lock, flags);
+	while (!list_empty(&rxq->rx_used)) {
+		element = rxq->rx_used.next;
+		rxb = list_entry(element, struct iwl_rx_mem_buffer, list);
+		rxb->skb =
+		    alloc_skb(IWL_RX_BUF_SIZE, __GFP_NOWARN | GFP_ATOMIC);
+		if (!rxb->skb) {
+			if (net_ratelimit())
+				printk(KERN_CRIT DRV_NAME
+				       ": Can not allocate SKB buffers\n");
+			/* We don't reschedule replenish work here -- we will
+			 * call the restock method and if it still needs
+			 * more buffers it will schedule replenish */
+			break;
+		}
+		priv->alloc_rxb_skb++;
+		list_del(element);
+		rxb->dma_addr =
+		    pci_map_single(priv->pci_dev, rxb->skb->data,
+				   IWL_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+		list_add_tail(&rxb->list, &rxq->rx_free);
+		rxq->free_count++;
+	}
+	spin_unlock_irqrestore(&rxq->lock, flags);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	iwl_rx_queue_restock(priv);
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
+ * If an SKB has been detached, the POOL needs to have it's SKB set to NULL
+ * This free routine walks the list of POOL entries and if SKB is set to
+ * non NULL it is unmapped and freed
+ */
+void iwl_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
+{
+	int i;
+	for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
+		if (rxq->pool[i].skb != NULL) {
+			pci_unmap_single(priv->pci_dev,
+					 rxq->pool[i].dma_addr,
+					 IWL_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+			dev_kfree_skb(rxq->pool[i].skb);
+		}
+	}
+
+	pci_free_consistent(priv->pci_dev, 4 * RX_QUEUE_SIZE, rxq->bd,
+			    rxq->dma_addr);
+	rxq->bd = NULL;
+}
+
+int iwl_rx_queue_alloc(struct iwl_priv *priv)
+{
+	struct iwl_rx_queue *rxq = &priv->rxq;
+	struct pci_dev *dev = priv->pci_dev;
+	int i;
+
+	spin_lock_init(&rxq->lock);
+	INIT_LIST_HEAD(&rxq->rx_free);
+	INIT_LIST_HEAD(&rxq->rx_used);
+	rxq->bd = pci_alloc_consistent(dev, 4 * RX_QUEUE_SIZE, &rxq->dma_addr);
+	if (!rxq->bd)
+		return -ENOMEM;
+	/* Fill the rx_used queue with _all_ of the Rx buffers */
+	for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
+		list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
+	/* Set us so that we have processed and used all buffers, but have
+	 * not restocked the Rx queue with fresh buffers */
+	rxq->read = rxq->write = 0;
+	rxq->free_count = 0;
+	rxq->need_update = 0;
+	return 0;
+}
+
+void iwl_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
+{
+	unsigned long flags;
+	int i;
+	spin_lock_irqsave(&rxq->lock, flags);
+	INIT_LIST_HEAD(&rxq->rx_free);
+	INIT_LIST_HEAD(&rxq->rx_used);
+	/* Fill the rx_used queue with _all_ of the Rx buffers */
+	for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
+		/* In the reset function, these buffers may have been allocated
+		 * to an SKB, so we need to unmap and free potential storage */
+		if (rxq->pool[i].skb != NULL) {
+			pci_unmap_single(priv->pci_dev,
+					 rxq->pool[i].dma_addr,
+					 IWL_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+			priv->alloc_rxb_skb--;
+			dev_kfree_skb(rxq->pool[i].skb);
+			rxq->pool[i].skb = NULL;
+		}
+		list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
+	}
+
+	/* Set us so that we have processed and used all buffers, but have
+	 * not restocked the Rx queue with fresh buffers */
+	rxq->read = rxq->write = 0;
+	rxq->free_count = 0;
+	spin_unlock_irqrestore(&rxq->lock, flags);
+}
+
+/* Convert linear signal-to-noise ratio into dB */
+static u8 ratio2dB[100] = {
+/*	 0   1   2   3   4   5   6   7   8   9 */
+	 0,  0,  6, 10, 12, 14, 16, 17, 18, 19, /* 00 - 09 */
+	20, 21, 22, 22, 23, 23, 24, 25, 26, 26, /* 10 - 19 */
+	26, 26, 26, 27, 27, 28, 28, 28, 29, 29, /* 20 - 29 */
+	29, 30, 30, 30, 31, 31, 31, 31, 32, 32, /* 30 - 39 */
+	32, 32, 32, 33, 33, 33, 33, 33, 34, 34, /* 40 - 49 */
+	34, 34, 34, 34, 35, 35, 35, 35, 35, 35, /* 50 - 59 */
+	36, 36, 36, 36, 36, 36, 36, 37, 37, 37, /* 60 - 69 */
+	37, 37, 37, 37, 37, 38, 38, 38, 38, 38, /* 70 - 79 */
+	38, 38, 38, 38, 38, 39, 39, 39, 39, 39, /* 80 - 89 */
+	39, 39, 39, 39, 39, 40, 40, 40, 40, 40  /* 90 - 99 */
+};
+
+/* Calculates a relative dB value from a ratio of linear
+ *   (i.e. not dB) signal levels.
+ * Conversion assumes that levels are voltages (20*log), not powers (10*log). */
+int iwl_calc_db_from_ratio(int sig_ratio)
+{
+	/* Anything above 1000:1 just report as 60 dB */
+	if (sig_ratio > 1000)
+		return 60;
+
+	/* Above 100:1, divide by 10 and use table,
+	 *   add 20 dB to make up for divide by 10 */
+	if (sig_ratio > 100)
+		return (20 + (int)ratio2dB[sig_ratio/10]);
+
+	/* We shouldn't see this */
+	if (sig_ratio < 1)
+		return 0;
+
+	/* Use table for ratios 1:1 - 99:1 */
+	return (int)ratio2dB[sig_ratio];
+}
+
+#define PERFECT_RSSI (-20) /* dBm */
+#define WORST_RSSI (-95)   /* dBm */
+#define RSSI_RANGE (PERFECT_RSSI - WORST_RSSI)
+
+/* Calculate an indication of rx signal quality (a percentage, not dBm!).
+ * See http://www.ces.clemson.edu/linux/signal_quality.shtml for info
+ *   about formulas used below. */
+int iwl_calc_sig_qual(int rssi_dbm, int noise_dbm)
+{
+	int sig_qual;
+	int degradation = PERFECT_RSSI - rssi_dbm;
+
+	/* If we get a noise measurement, use signal-to-noise ratio (SNR)
+	 * as indicator; formula is (signal dbm - noise dbm).
+	 * SNR at or above 40 is a great signal (100%).
+	 * Below that, scale to fit SNR of 0 - 40 dB within 0 - 100% indicator.
+	 * Weakest usable signal is usually 10 - 15 dB SNR. */
+	if (noise_dbm) {
+		if (rssi_dbm - noise_dbm >= 40)
+			return 100;
+		else if (rssi_dbm < noise_dbm)
+			return 0;
+		sig_qual = ((rssi_dbm - noise_dbm) * 5) / 2;
+
+	/* Else use just the signal level.
+	 * This formula is a least squares fit of data points collected and
+	 *   compared with a reference system that had a percentage (%) display
+	 *   for signal quality. */
+	} else {
+		sig_qual =
+			(100 * (RSSI_RANGE * RSSI_RANGE) -
+			degradation * (15 * RSSI_RANGE + 62 * degradation)) /
+			(RSSI_RANGE * RSSI_RANGE);
+	}
+	if (sig_qual > 100)
+		sig_qual = 100;
+	else if (sig_qual < 1)
+		sig_qual = 0;
+	return sig_qual;
+}
+
+/**
+ * iwl_rx_handle - Main entry function for receiving responses from the uCode
+ *
+ * Uses the priv->rx_handlers callback function array to invoke
+ * the appropriate handlers including command response and 802.11
+ * frame availability.
+ */
+static void iwl_rx_handle(struct iwl_priv *priv)
+{
+	struct iwl_rx_mem_buffer *rxb;
+	struct iwl_rx_packet *pkt;
+	struct iwl_rx_queue *rxq = &priv->rxq;
+	u32 r, i;
+	int reclaim;
+	unsigned long flags;
+
+	r = iwl_hw_get_rx_read(priv);
+	i = rxq->read;
+
+	while (i != r) {
+		rxb = rxq->queue[i];
+
+		/* If an RXB doesn't have a queue slot associated with it
+		 * then a bug has been introduced in the queue refilling
+		 * routines -- catch it here */
+		BUG_ON(rxb == NULL);
+
+		rxq->queue[i] = NULL;
+
+		pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr,
+					    IWL_RX_BUF_SIZE,
+					    PCI_DMA_FROMDEVICE);
+		pkt = (struct iwl_rx_packet *)rxb->skb->data;
+
+		/* need to reclaim cmd buffer(s) */
+		reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME) &&
+#if IWL == 4965
+			(pkt->hdr.cmd != REPLY_RX_PHY_CMD) &&
+			(pkt->hdr.cmd != REPLY_4965_RX) &&
+#endif
+			(pkt->hdr.cmd != STATISTICS_NOTIFICATION) &&
+			(pkt->hdr.cmd != REPLY_TX);
+
+		/* Based on type of command response or notification,
+		 *   handle those that need handling via function in
+		 *   rx_handlers table.  See iwl_setup_rx_handlers() */
+		if (priv->rx_handlers[pkt->hdr.cmd]) {
+			priv->rx_handlers[pkt->hdr.cmd] (priv, rxb);
+			IWL_DEBUG(IWL_DL_RX | IWL_DL_ISR,
+				"r = %d, i = %d, rx_handler %s\n", r, i,
+				get_cmd_string(pkt->hdr.cmd));
+		} else {
+			/* No handling needed */
+			IWL_DEBUG_HC("UNHANDLED - #0x%02x %s\n",
+				     pkt->hdr.cmd,
+				     get_cmd_string(pkt->hdr.cmd));
+		}
+
+		if (reclaim) {
+			/* Invoke any callbacks, transfer the skb to
+			 * caller, and fire off the (possibly) blocking
+			 * iwl_send_cmd() via as we reclaim the queue... */
+			if (rxb && rxb->skb)
+				iwl_tx_cmd_complete(priv, rxb);
+			else
+				IWL_WARNING("Claim null rxb?\n");
+		}
+
+		/* For now we just don't re-use anything.  We can tweak this
+		 * later to try and re-use notification packets and SKBs that
+		 * fail to Rx correctly */
+		if (rxb->skb != NULL) {
+			priv->alloc_rxb_skb--;
+			dev_kfree_skb_any(rxb->skb);
+			rxb->skb = NULL;
+		}
+
+		pci_unmap_single(priv->pci_dev, rxb->dma_addr,
+				 IWL_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+		spin_lock_irqsave(&rxq->lock, flags);
+		list_add_tail(&rxb->list, &priv->rxq.rx_used);
+		spin_unlock_irqrestore(&rxq->lock, flags);
+		i = (i + 1) % RX_QUEUE_SIZE;
+	}
+
+	/* Backtrack one entry */
+	priv->rxq.read = i;
+	iwl_rx_queue_restock(priv);
+}
+
+int iwl_tx_queue_update_write_ptr(struct iwl_priv *priv,
+				  struct iwl_tx_queue *txq)
+{
+	u32 reg = 0;
+	int rc = 0;
+	int txq_id = txq->q.id;
+
+	if (txq->need_update == 0)
+		return rc;
+
+	/* if we're trying to save power */
+	if (priv->status & STATUS_POWER_PMI) {
+		/* wake up nic if it's powered down ...
+		 * uCode will wake up, and interrupt us again, so next
+		 * time we'll skip this part. */
+		reg = iwl_read32(priv, CSR_UCODE_DRV_GP1);
+
+		if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
+			IWL_DEBUG_INFO("Requesting wakeup, GP1 = 0x%x\n", reg);
+			iwl_set_bit(priv, CSR_GP_CNTRL,
+				    CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+			return rc;
+		}
+
+		/* restore this queue's parameters in nic hardware. */
+		rc = iwl_grab_restricted_access(priv);
+		if (rc)
+			return rc;
+		iwl_write_restricted(priv, HBUS_TARG_WRPTR,
+				     txq->q.first_empty | (txq_id << 8));
+		iwl_release_restricted_access(priv);
+
+	/* else not in power-save mode, uCode will never sleep when we're
+	 * trying to tx (during RFKILL, we're not trying to tx). */
+	} else {
+		iwl_write32(priv, HBUS_TARG_WRPTR,
+			    txq->q.first_empty | (txq_id << 8));
+	}
+
+	txq->need_update = 0;
+
+	return rc;
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+
+static void iwl_print_rx_config_cmd(struct iwl_rxon_cmd *rxon)
+{
+	IWL_DEBUG_RADIO("RX CONFIG:\n");
+	printk_buf(IWL_DL_RADIO, (u8 *) rxon, sizeof(*rxon));
+	IWL_DEBUG_RADIO("u16 channel: 0x%x\n", le16_to_cpu(rxon->channel));
+	IWL_DEBUG_RADIO("u32 flags: 0x%08X " BIT_FMT32 "\n",
+			le32_to_cpu(rxon->flags),
+			BIT_ARG32(le32_to_cpu(rxon->flags)));
+	IWL_DEBUG_RADIO("u32 filter_flags: 0x%08x " BIT_FMT32 "\n",
+			le32_to_cpu(rxon->filter_flags),
+			BIT_ARG32(le32_to_cpu(rxon->filter_flags)));
+	IWL_DEBUG_RADIO("u8 dev_type: 0x%x\n", rxon->dev_type);
+	IWL_DEBUG_RADIO("u8 ofdm_basic_rates: 0x%02x " BIT_FMT8 "\n",
+			rxon->ofdm_basic_rates,
+			BIT_ARG8(rxon->ofdm_basic_rates));
+	IWL_DEBUG_RADIO("u8 cck_basic_rates: 0x%02x " BIT_FMT8 "\n",
+			rxon->cck_basic_rates, BIT_ARG8(rxon->cck_basic_rates));
+	IWL_DEBUG_RADIO("u8[6] node_addr: " MAC_FMT "\n",
+			MAC_ARG(rxon->node_addr));
+	IWL_DEBUG_RADIO("u8[6] bssid_addr: " MAC_FMT "\n",
+			MAC_ARG(rxon->bssid_addr));
+	IWL_DEBUG_RADIO("u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id));
+}
+
+#endif
+
+static void iwl_enable_interrupts(struct iwl_priv *priv)
+{
+	IWL_DEBUG_ISR("Enabling interrupts\n");
+	priv->status |= STATUS_INT_ENABLED;
+	iwl_write32(priv, CSR_INT_MASK, CSR_INI_SET_MASK);
+}
+
+static inline void iwl_disable_interrupts(struct iwl_priv *priv)
+{
+	priv->status &= ~STATUS_INT_ENABLED;
+
+	/* disable interrupts from uCode/NIC to host */
+	iwl_write32(priv, CSR_INT_MASK, 0x00000000);
+
+	/* acknowledge/clear/reset any interrupts still pending
+	 * from uCode or flow handler (Rx/Tx DMA) */
+	iwl_write32(priv, CSR_INT, 0xffffffff);
+	iwl_write32(priv, CSR_FH_INT_STATUS, 0xffffffff);
+	IWL_DEBUG_ISR("Disabled interrupts\n");
+}
+
+static const char *desc_lookup(int i)
+{
+	switch (i) {
+	case 1:
+		return "FAIL";
+	case 2:
+		return "BAD_PARAM";
+	case 3:
+		return "BAD_CHECKSUM";
+	case 4:
+		return "NMI_INTERRUPT";
+	case 5:
+		return "SYSASSERT";
+	case 6:
+		return "FATAL_ERROR";
+	}
+
+	return "UNKNOWN";
+}
+
+#define ERROR_START_OFFSET  (1 * sizeof(u32))
+#define ERROR_ELEM_SIZE     (7 * sizeof(u32))
+
+static void iwl_dump_nic_error_log(struct iwl_priv *priv)
+{
+#if IWL == 3945
+	u32 i;
+#else /* IWL == 4965 */
+	u32 data2, line;
+#endif
+	u32 desc, time, count, base, data1;
+	u32 blink1, blink2, ilink1, ilink2;
+	int rc;
+
+	base = le32_to_cpu(priv->card_alive.error_event_table_ptr);
+
+	if (!VALID_RTC_DATA_ADDR(base)) {
+		IWL_ERROR("Not valid error log pointer 0x%08X\n", base);
+		return;
+	}
+
+	rc = iwl_grab_restricted_access(priv);
+	if (rc) {
+		IWL_WARNING("Can not read from adapter at this time.\n");
+		return;
+	}
+
+	count = iwl_read_restricted_mem(priv, base);
+
+	if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
+		IWL_ERROR("Start IWL Error Log Dump:\n");
+		IWL_ERROR("Status: 0x%08X, Config: %08X count: %d\n",
+			  priv->status, priv->config, count);
+	}
+
+#if IWL == 3945
+	IWL_ERROR("Desc       Time       asrtPC  blink2 "
+		  "ilink1  nmiPC   Line\n");
+	for (i = ERROR_START_OFFSET;
+	     i < (count * ERROR_ELEM_SIZE) + ERROR_START_OFFSET;
+	     i += ERROR_ELEM_SIZE) {
+		desc = iwl_read_restricted_mem(priv, base + i);
+		time =
+		    iwl_read_restricted_mem(priv, base + i + 1 * sizeof(u32));
+		blink1 =
+		    iwl_read_restricted_mem(priv, base + i + 2 * sizeof(u32));
+		blink2 =
+		    iwl_read_restricted_mem(priv, base + i + 3 * sizeof(u32));
+		ilink1 =
+		    iwl_read_restricted_mem(priv, base + i + 4 * sizeof(u32));
+		ilink2 =
+		    iwl_read_restricted_mem(priv, base + i + 5 * sizeof(u32));
+		data1 =
+		    iwl_read_restricted_mem(priv, base + i + 6 * sizeof(u32));
+
+		IWL_ERROR
+		    ("%-13s (#%d) %010u 0x%05X 0x%05X 0x%05X 0x%05X %u\n\n",
+		     desc_lookup(desc), desc, time, blink1, blink2,
+		     ilink1, ilink2, data1);
+	}
+#else  /* 4965 Error format */
+	desc = iwl_read_restricted_mem(priv, base + 1 * sizeof(u32));
+	blink1 = iwl_read_restricted_mem(priv, base + 3 * sizeof(u32));
+	blink2 = iwl_read_restricted_mem(priv, base + 4 * sizeof(u32));
+	ilink1 = iwl_read_restricted_mem(priv, base + 5 * sizeof(u32));
+	ilink2 = iwl_read_restricted_mem(priv, base + 6 * sizeof(u32));
+	data1 = iwl_read_restricted_mem(priv, base + 7 * sizeof(u32));
+	data2 = iwl_read_restricted_mem(priv, base + 8 * sizeof(u32));
+	line = iwl_read_restricted_mem(priv, base + 9 * sizeof(u32));
+	time = iwl_read_restricted_mem(priv, base + 11 * sizeof(u32));
+
+	IWL_ERROR("Desc               Time       "
+		  "data1      data2      line\n");
+	IWL_ERROR
+	    ("%-13s (#%d) %010u 0x%08X 0x%08X %u\n",
+	     desc_lookup(desc), desc, time, data1, data2, line);
+	IWL_ERROR("blink1  blink2  ilink1  ilink2\n");
+	IWL_ERROR
+	    ("0x%05X 0x%05X 0x%05X 0x%05X\n", blink1, blink2, ilink1, ilink2);
+
+#endif  /* IWL 3945 */
+
+	iwl_release_restricted_access(priv);
+
+}
+
+#define EVENT_START_OFFSET  (4 * sizeof(u32))
+
+/**
+ * iwl_print_event_log - Dump error event log to syslog
+ *
+ * NOTE: Must be called with iwl_grab_restricted_access() already obtained!
+ */
+static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
+				u32 num_events, u32 mode)
+{
+	u32 i;
+	u32 base;       /* SRAM byte address of event log header */
+	u32 event_size;	/* 2 u32s, or 3 u32s if timestamp recorded */
+	u32 ptr;        /* SRAM byte address of log data */
+	u32 ev, time, data; /* event log data */
+
+	if (num_events == 0)
+		return;
+
+	base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
+
+	if (mode == 0)
+		event_size = 2 * sizeof(u32);
+	else
+		event_size = 3 * sizeof(u32);
+
+	ptr = base + EVENT_START_OFFSET + (start_idx * event_size);
+
+	/* "time" is actually "data" for mode 0 (no timestamp).
+	 * place event id # at far right for easier visual parsing. */
+	for (i = 0; i < num_events; i++) {
+		ev = iwl_read_restricted_mem(priv, ptr);
+		ptr += sizeof(u32);
+		time = iwl_read_restricted_mem(priv, ptr);
+		ptr += sizeof(u32);
+		if (mode == 0) {
+			IWL_ERROR("0x%08x\t%04u\n", time, ev); /* data, ev */
+		} else {
+			data = iwl_read_restricted_mem(priv, ptr);
+			ptr += sizeof(u32);
+			IWL_ERROR("%010u\t0x%08x\t%04u\n", time, data, ev);
+		}
+	}
+}
+
+static void iwl_dump_nic_event_log(struct iwl_priv *priv)
+{
+	int rc;
+	u32 base;       /* SRAM byte address of event log header */
+	u32 capacity;   /* event log capacity in # entries */
+	u32 mode;       /* 0 - no timestamp, 1 - timestamp recorded */
+	u32 num_wraps;  /* # times uCode wrapped to top of log */
+	u32 next_entry; /* index of next entry to be written by uCode */
+	u32 size;       /* # entries that we'll print */
+
+	base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
+	if (!VALID_RTC_DATA_ADDR(base)) {
+		IWL_ERROR("Invalid event log pointer 0x%08X\n", base);
+		return;
+	}
+
+	rc = iwl_grab_restricted_access(priv);
+	if (rc) {
+		IWL_WARNING("Can not read from adapter at this time.\n");
+		return;
+	}
+
+	/* event log header */
+	capacity = iwl_read_restricted_mem(priv, base);
+	mode = iwl_read_restricted_mem(priv, base + (1 * sizeof(u32)));
+	num_wraps = iwl_read_restricted_mem(priv, base + (2 * sizeof(u32)));
+	next_entry = iwl_read_restricted_mem(priv, base + (3 * sizeof(u32)));
+
+	size = num_wraps ? capa