Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
diff --git a/Documentation/DocBook/mac80211.tmpl b/Documentation/DocBook/mac80211.tmpl
index fbeaffc..e369866 100644
--- a/Documentation/DocBook/mac80211.tmpl
+++ b/Documentation/DocBook/mac80211.tmpl
@@ -145,7 +145,6 @@
         interface in STA mode at first!
       </para>
 !Finclude/net/mac80211.h ieee80211_if_init_conf
-!Finclude/net/mac80211.h ieee80211_if_conf
     </chapter>
 
     <chapter id="rx-tx">
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 91be3e7..a67d292 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -501,5 +501,6 @@
 source "drivers/net/wireless/rt2x00/Kconfig"
 source "drivers/net/wireless/orinoco/Kconfig"
 source "drivers/net/wireless/wl12xx/Kconfig"
+source "drivers/net/wireless/iwmc3200wifi/Kconfig"
 
 endmenu
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index f2b1861..7a4647e 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -60,3 +60,5 @@
 obj-$(CONFIG_MAC80211_HWSIM)	+= mac80211_hwsim.o
 
 obj-$(CONFIG_WL12XX)	+= wl12xx/
+
+obj-$(CONFIG_IWM)	+= iwmc3200wifi/
diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c
index 4ef1d2f..99df9dd 100644
--- a/drivers/net/wireless/ath/ar9170/main.c
+++ b/drivers/net/wireless/ath/ar9170/main.c
@@ -1555,7 +1555,7 @@
 
 	switch (key->alg) {
 	case ALG_WEP:
-		if (key->keylen == LEN_WEP40)
+		if (key->keylen == WLAN_KEY_LEN_WEP40)
 			ktype = AR9170_ENC_ALG_WEP64;
 		else
 			ktype = AR9170_ENC_ALG_WEP128;
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index dbfe9f4..fb51937 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -242,8 +242,8 @@
 static u64 ath5k_get_tsf(struct ieee80211_hw *hw);
 static void ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf);
 static void ath5k_reset_tsf(struct ieee80211_hw *hw);
-static int ath5k_beacon_update(struct ath5k_softc *sc,
-		struct sk_buff *skb);
+static int ath5k_beacon_update(struct ieee80211_hw *hw,
+		struct ieee80211_vif *vif);
 static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
 		struct ieee80211_vif *vif,
 		struct ieee80211_bss_conf *bss_conf,
@@ -2127,8 +2127,10 @@
 		/* NB: hw still stops DMA, so proceed */
 	}
 
-	/* Note: Beacon buffer is updated on beacon_update when mac80211
-	 * calls config_interface */
+	/* refresh the beacon for AP mode */
+	if (sc->opmode == NL80211_IFTYPE_AP)
+		ath5k_beacon_update(sc->hw, sc->vif);
+
 	ath5k_hw_set_txdp(ah, sc->bhalq, bf->daddr);
 	ath5k_hw_start_tx_dma(ah, sc->bhalq);
 	ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "TXDP[%u] = %llx (%p)\n",
@@ -3047,28 +3049,55 @@
 		ath5k_hw_reset_tsf(sc->ah);
 }
 
+/*
+ * Updates the beacon that is sent by ath5k_beacon_send.  For adhoc,
+ * this is called only once at config_bss time, for AP we do it every
+ * SWBA interrupt so that the TIM will reflect buffered frames.
+ *
+ * Called with the beacon lock.
+ */
 static int
-ath5k_beacon_update(struct ath5k_softc *sc, struct sk_buff *skb)
+ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 {
-	unsigned long flags;
 	int ret;
+	struct ath5k_softc *sc = hw->priv;
+	struct sk_buff *skb = ieee80211_beacon_get(hw, vif);
+
+	if (!skb) {
+		ret = -ENOMEM;
+		goto out;
+	}
 
 	ath5k_debug_dump_skb(sc, skb, "BC  ", 1);
 
-	spin_lock_irqsave(&sc->block, flags);
 	ath5k_txbuf_free(sc, sc->bbuf);
 	sc->bbuf->skb = skb;
 	ret = ath5k_beacon_setup(sc, sc->bbuf);
 	if (ret)
 		sc->bbuf->skb = NULL;
+out:
+	return ret;
+}
+
+/*
+ *  Update the beacon and reconfigure the beacon queues.
+ */
+static void
+ath5k_beacon_reconfig(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+	int ret;
+	unsigned long flags;
+	struct ath5k_softc *sc = hw->priv;
+
+	spin_lock_irqsave(&sc->block, flags);
+	ret = ath5k_beacon_update(hw, vif);
 	spin_unlock_irqrestore(&sc->block, flags);
-	if (!ret) {
+	if (ret == 0) {
 		ath5k_beacon_config(sc);
 		mmiowb();
 	}
-
-	return ret;
 }
+
 static void
 set_beacon_filter(struct ieee80211_hw *hw, bool enable)
 {
@@ -3118,10 +3147,7 @@
 	    (vif->type == NL80211_IFTYPE_ADHOC ||
 	     vif->type == NL80211_IFTYPE_MESH_POINT ||
 	     vif->type == NL80211_IFTYPE_AP)) {
-		struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
-
-		if (beacon)
-			ath5k_beacon_update(sc, beacon);
+		ath5k_beacon_reconfig(hw, vif);
 	}
 
  unlock:
diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c
index 579aa0a..ec35503 100644
--- a/drivers/net/wireless/ath/ath5k/pcu.c
+++ b/drivers/net/wireless/ath/ath5k/pcu.c
@@ -1038,9 +1038,9 @@
 	case ALG_CCMP:
 		return AR5K_KEYTABLE_TYPE_CCM;
 	case ALG_WEP:
-		if (key->keylen == LEN_WEP40)
+		if (key->keylen == WLAN_KEY_LEN_WEP40)
 			return AR5K_KEYTABLE_TYPE_40;
-		else if (key->keylen == LEN_WEP104)
+		else if (key->keylen == WLAN_KEY_LEN_WEP104)
 			return AR5K_KEYTABLE_TYPE_104;
 		return -EINVAL;
 	default:
diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c
index d0d1c35..a876ca8 100644
--- a/drivers/net/wireless/ath/ath5k/phy.c
+++ b/drivers/net/wireless/ath/ath5k/phy.c
@@ -1897,6 +1897,9 @@
 	s16 min_pwrL, min_pwrR;
 	s16 pwr_i;
 
+	if (WARN_ON(stepL[0] == stepL[1] || stepR[0] == stepR[1]))
+		return 0;
+
 	if (pwrL[0] == pwrL[1])
 		min_pwrL = pwrL[0];
 	else {
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 10ffc94..796a3ad 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -515,6 +515,10 @@
 #define SC_OP_LED_ON            BIT(13)
 #define SC_OP_SCANNING          BIT(14)
 #define SC_OP_TSF_RESET         BIT(15)
+#define SC_OP_WAIT_FOR_CAB      BIT(16)
+#define SC_OP_WAIT_FOR_PSPOLL_DATA BIT(17)
+#define SC_OP_WAIT_FOR_TX_ACK   BIT(18)
+#define SC_OP_BEACON_SYNC       BIT(19)
 
 struct ath_bus_ops {
 	void		(*read_cachesize)(struct ath_softc *sc, int *csz);
@@ -599,6 +603,7 @@
 	struct ath9k_debug debug;
 #endif
 	struct ath_bus_ops *bus_ops;
+	struct ath_beacon_config cur_beacon_conf;
 };
 
 struct ath_wiphy {
@@ -676,7 +681,9 @@
 {
 	if (atomic_dec_and_test(&sc->ps_usecount))
 		if ((sc->hw->conf.flags & IEEE80211_CONF_PS) &&
-		    !(sc->sc_flags & SC_OP_WAIT_FOR_BEACON))
+		    !(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON |
+				      SC_OP_WAIT_FOR_PSPOLL_DATA |
+				      SC_OP_WAIT_FOR_TX_ACK)))
 			ath9k_hw_setpower(sc->sc_ah,
 					  sc->sc_ah->restore_mode);
 }
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index 3a7154b..a21b213 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -507,8 +507,7 @@
  * slot. Slots that are not occupied will generate nothing.
  */
 static void ath_beacon_config_ap(struct ath_softc *sc,
-				 struct ath_beacon_config *conf,
-				 struct ath_vif *avp)
+				 struct ath_beacon_config *conf)
 {
 	u32 nexttbtt, intval;
 
@@ -553,14 +552,14 @@
  * we've associated with.
  */
 static void ath_beacon_config_sta(struct ath_softc *sc,
-				  struct ath_beacon_config *conf,
-				  struct ath_vif *avp)
+				  struct ath_beacon_config *conf)
 {
 	struct ath9k_beacon_state bs;
 	int dtimperiod, dtimcount, sleepduration;
 	int cfpperiod, cfpcount;
 	u32 nexttbtt = 0, intval, tsftu;
 	u64 tsf;
+	int num_beacons, offset, dtim_dec_count, cfp_dec_count;
 
 	memset(&bs, 0, sizeof(bs));
 	intval = conf->beacon_interval & ATH9K_BEACON_PERIOD;
@@ -588,14 +587,27 @@
 	 */
 	tsf = ath9k_hw_gettsf64(sc->sc_ah);
 	tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
-	do {
+
+	num_beacons = tsftu / intval + 1;
+	offset = tsftu % intval;
+	nexttbtt = tsftu - offset;
+	if (offset)
 		nexttbtt += intval;
-		if (--dtimcount < 0) {
-			dtimcount = dtimperiod - 1;
-			if (--cfpcount < 0)
-				cfpcount = cfpperiod - 1;
-		}
-	} while (nexttbtt < tsftu);
+
+	/* DTIM Beacon every dtimperiod Beacon */
+	dtim_dec_count = num_beacons % dtimperiod;
+	/* CFP every cfpperiod DTIM Beacon */
+	cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod;
+	if (dtim_dec_count)
+		cfp_dec_count++;
+
+	dtimcount -= dtim_dec_count;
+	if (dtimcount < 0)
+		dtimcount += dtimperiod;
+
+	cfpcount -= cfp_dec_count;
+	if (cfpcount < 0)
+		cfpcount += cfpperiod;
 
 	bs.bs_intval = intval;
 	bs.bs_nexttbtt = nexttbtt;
@@ -654,7 +666,6 @@
 
 static void ath_beacon_config_adhoc(struct ath_softc *sc,
 				    struct ath_beacon_config *conf,
-				    struct ath_vif *avp,
 				    struct ieee80211_vif *vif)
 {
 	u64 tsf;
@@ -698,43 +709,50 @@
 	sc->beacon.bmisscnt = 0;
 	ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
 
-	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)
+	/* FIXME: Handle properly when vif is NULL */
+	if (vif && sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)
 		ath_beacon_start_adhoc(sc, vif);
 }
 
 void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
 {
-	struct ath_beacon_config conf;
+	struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+	enum nl80211_iftype iftype;
 
 	/* Setup the beacon configuration parameters */
 
-	memset(&conf, 0, sizeof(struct ath_beacon_config));
-	conf.beacon_interval = sc->beacon_interval ? : ATH_DEFAULT_BINTVAL;
-	conf.listen_interval = 1;
-	conf.dtim_period = conf.beacon_interval;
-	conf.dtim_count = 1;
-	conf.bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf.beacon_interval;
-
 	if (vif) {
-		struct ath_vif *avp = (struct ath_vif *)vif->drv_priv;
+		struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
 
-		switch(avp->av_opmode) {
-		case NL80211_IFTYPE_AP:
-			ath_beacon_config_ap(sc, &conf, avp);
-			break;
-		case NL80211_IFTYPE_ADHOC:
-		case NL80211_IFTYPE_MESH_POINT:
-			ath_beacon_config_adhoc(sc, &conf, avp, vif);
-			break;
-		case NL80211_IFTYPE_STATION:
-			ath_beacon_config_sta(sc, &conf, avp);
-			break;
-		default:
-			DPRINTF(sc, ATH_DBG_CONFIG,
-				"Unsupported beaconing mode\n");
-			return;
-		}
+		iftype = vif->type;
 
-		sc->sc_flags |= SC_OP_BEACONS;
+		cur_conf->beacon_interval = bss_conf->beacon_int;
+		cur_conf->dtim_period = bss_conf->dtim_period;
+		cur_conf->listen_interval = 1;
+		cur_conf->dtim_count = 1;
+		cur_conf->bmiss_timeout =
+			ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval;
+	} else {
+		iftype = sc->sc_ah->opmode;
 	}
+
+
+	switch (iftype) {
+	case NL80211_IFTYPE_AP:
+		ath_beacon_config_ap(sc, cur_conf);
+		break;
+	case NL80211_IFTYPE_ADHOC:
+	case NL80211_IFTYPE_MESH_POINT:
+		ath_beacon_config_adhoc(sc, cur_conf, vif);
+		break;
+	case NL80211_IFTYPE_STATION:
+		ath_beacon_config_sta(sc, cur_conf);
+		break;
+	default:
+		DPRINTF(sc, ATH_DBG_CONFIG,
+			"Unsupported beaconing mode\n");
+		return;
+	}
+
+	sc->sc_flags |= SC_OP_BEACONS;
 }
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h
index 23298b9..db845cf 100644
--- a/drivers/net/wireless/ath/ath9k/debug.h
+++ b/drivers/net/wireless/ath/ath9k/debug.h
@@ -29,6 +29,7 @@
 	ATH_DBG_BEACON		= 0x00000100,
 	ATH_DBG_CONFIG		= 0x00000200,
 	ATH_DBG_FATAL		= 0x00000400,
+	ATH_DBG_PS		= 0x00000800,
 	ATH_DBG_ANY		= 0xffffffff
 };
 
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 4acfab5..1579c94 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -2472,14 +2472,14 @@
 		}
 		break;
 	case ATH9K_CIPHER_WEP:
-		if (k->kv_len < LEN_WEP40) {
+		if (k->kv_len < WLAN_KEY_LEN_WEP40) {
 			DPRINTF(ah->ah_sc, ATH_DBG_ANY,
 				"WEP key length %u too small\n", k->kv_len);
 			return false;
 		}
-		if (k->kv_len <= LEN_WEP40)
+		if (k->kv_len <= WLAN_KEY_LEN_WEP40)
 			keyType = AR_KEYTABLE_TYPE_40;
-		else if (k->kv_len <= LEN_WEP104)
+		else if (k->kv_len <= WLAN_KEY_LEN_WEP104)
 			keyType = AR_KEYTABLE_TYPE_104;
 		else
 			keyType = AR_KEYTABLE_TYPE_128;
@@ -2498,7 +2498,7 @@
 	key2 = get_unaligned_le32(k->kv_val + 6);
 	key3 = get_unaligned_le16(k->kv_val + 10);
 	key4 = get_unaligned_le32(k->kv_val + 12);
-	if (k->kv_len <= LEN_WEP104)
+	if (k->kv_len <= WLAN_KEY_LEN_WEP104)
 		key4 &= 0xff;
 
 	/*
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index bbbfdcd..61da08a 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -35,14 +35,14 @@
 #define CHAN2G(_freq, _idx)  { \
 	.center_freq = (_freq), \
 	.hw_value = (_idx), \
-	.max_power = 30, \
+	.max_power = 20, \
 }
 
 #define CHAN5G(_freq, _idx) { \
 	.band = IEEE80211_BAND_5GHZ, \
 	.center_freq = (_freq), \
 	.hw_value = (_idx), \
-	.max_power = 30, \
+	.max_power = 20, \
 }
 
 /* Some 2 GHz radios are actually tunable on 2312-2732
@@ -280,7 +280,7 @@
 	if (r) {
 		DPRINTF(sc, ATH_DBG_FATAL,
 			"Unable to reset channel (%u Mhz) "
-			"reset status %u\n",
+			"reset status %d\n",
 			channel->center_freq, r);
 		spin_unlock_bh(&sc->sc_resetlock);
 		return r;
@@ -329,6 +329,12 @@
 	if (sc->sc_flags & SC_OP_SCANNING)
 		goto set_timer;
 
+	/* Only calibrate if awake */
+	if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE)
+		goto set_timer;
+
+	ath9k_ps_wakeup(sc);
+
 	/* Long calibration runs independently of short calibration. */
 	if ((timestamp - sc->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
 		longcal = true;
@@ -380,6 +386,8 @@
 		}
 	}
 
+	ath9k_ps_restore(sc);
+
 set_timer:
 	/*
 	* Set timer interval based on previous results.
@@ -455,8 +463,11 @@
 	struct ath_softc *sc = (struct ath_softc *)data;
 	u32 status = sc->intrstatus;
 
+	ath9k_ps_wakeup(sc);
+
 	if (status & ATH9K_INT_FATAL) {
 		ath_reset(sc, false);
+		ath9k_ps_restore(sc);
 		return;
 	}
 
@@ -469,8 +480,19 @@
 	if (status & ATH9K_INT_TX)
 		ath_tx_tasklet(sc);
 
+	if ((status & ATH9K_INT_TSFOOR) &&
+	    (sc->hw->conf.flags & IEEE80211_CONF_PS)) {
+		/*
+		 * TSF sync does not look correct; remain awake to sync with
+		 * the next Beacon.
+		 */
+		DPRINTF(sc, ATH_DBG_PS, "TSFOOR - Sync with next Beacon\n");
+		sc->sc_flags |= SC_OP_WAIT_FOR_BEACON | SC_OP_BEACON_SYNC;
+	}
+
 	/* re-enable hardware interrupt */
 	ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
+	ath9k_ps_restore(sc);
 }
 
 irqreturn_t ath_isr(int irq, void *dev)
@@ -498,14 +520,11 @@
 	if (sc->sc_flags & SC_OP_INVALID)
 		return IRQ_NONE;
 
-	ath9k_ps_wakeup(sc);
 
 	/* shared irq, not for us */
 
-	if (!ath9k_hw_intrpend(ah)) {
-		ath9k_ps_restore(sc);
+	if (!ath9k_hw_intrpend(ah))
 		return IRQ_NONE;
-	}
 
 	/*
 	 * Figure out the reason(s) for the interrupt.  Note
@@ -520,10 +539,8 @@
 	 * If there are no status bits set, then this interrupt was not
 	 * for me (should have been caught above).
 	 */
-	if (!status) {
-		ath9k_ps_restore(sc);
+	if (!status)
 		return IRQ_NONE;
-	}
 
 	/* Cache the status */
 	sc->intrstatus = status;
@@ -560,20 +577,17 @@
 		ath9k_hw_set_interrupts(ah, sc->imask);
 	}
 
-	if (status & ATH9K_INT_TIM_TIMER) {
-		if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
+	if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
+		if (status & ATH9K_INT_TIM_TIMER) {
 			/* Clear RxAbort bit so that we can
 			 * receive frames */
 			ath9k_hw_setpower(ah, ATH9K_PM_AWAKE);
-			ath9k_hw_setrxabort(ah, 0);
-			sched = true;
+			ath9k_hw_setrxabort(sc->sc_ah, 0);
 			sc->sc_flags |= SC_OP_WAIT_FOR_BEACON;
 		}
-	}
 
 chip_reset:
 
-	ath9k_ps_restore(sc);
 	ath_debug_stat_interrupt(sc, status);
 
 	if (sched) {
@@ -900,6 +914,13 @@
 		if (avp->av_opmode == NL80211_IFTYPE_STATION) {
 			sc->curaid = bss_conf->aid;
 			ath9k_hw_write_associd(sc);
+
+			/*
+			 * Request a re-configuration of Beacon related timers
+			 * on the receipt of the first Beacon frame (i.e.,
+			 * after time sync with the AP).
+			 */
+			sc->sc_flags |= SC_OP_BEACON_SYNC;
 		}
 
 		/* Configure the beacon */
@@ -1094,7 +1115,7 @@
 	if (r) {
 		DPRINTF(sc, ATH_DBG_FATAL,
 			"Unable to reset channel %u (%uMhz) ",
-			"reset status %u\n",
+			"reset status %d\n",
 			channel->center_freq, r);
 	}
 	spin_unlock_bh(&sc->sc_resetlock);
@@ -1146,7 +1167,7 @@
 	if (r) {
 		DPRINTF(sc, ATH_DBG_FATAL,
 			"Unable to reset channel %u (%uMhz) "
-			"reset status %u\n",
+			"reset status %d\n",
 			channel->center_freq, r);
 	}
 	spin_unlock_bh(&sc->sc_resetlock);
@@ -1416,8 +1437,6 @@
 	for (i = 0; i < sc->keymax; i++)
 		ath9k_hw_keyreset(ah, (u16) i);
 
-	error = ath_regd_init(&sc->sc_ah->regulatory, sc->hw->wiphy,
-			      ath9k_reg_notifier);
 	if (error)
 		goto bad;
 
@@ -1630,14 +1649,19 @@
 	if (error != 0)
 		return error;
 
-	reg = &sc->sc_ah->regulatory;
-
 	/* get mac address from hardware and set in mac80211 */
 
 	SET_IEEE80211_PERM_ADDR(hw, sc->sc_ah->macaddr);
 
 	ath_set_hw_capab(sc, hw);
 
+	error = ath_regd_init(&sc->sc_ah->regulatory, sc->hw->wiphy,
+			      ath9k_reg_notifier);
+	if (error)
+		return error;
+
+	reg = &sc->sc_ah->regulatory;
+
 	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
 		setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
 		if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes))
@@ -1709,7 +1733,7 @@
 	r = ath9k_hw_reset(ah, sc->sc_ah->curchan, false);
 	if (r)
 		DPRINTF(sc, ATH_DBG_FATAL,
-			"Unable to reset hardware; reset status %u\n", r);
+			"Unable to reset hardware; reset status %d\n", r);
 	spin_unlock_bh(&sc->sc_resetlock);
 
 	if (ath_startrecv(sc) != 0)
@@ -2001,7 +2025,7 @@
 	r = ath9k_hw_reset(sc->sc_ah, init_channel, false);
 	if (r) {
 		DPRINTF(sc, ATH_DBG_FATAL,
-			"Unable to reset hardware; reset status %u "
+			"Unable to reset hardware; reset status %d "
 			"(freq %u MHz)\n", r,
 			curchan->center_freq);
 		spin_unlock_bh(&sc->sc_resetlock);
@@ -2074,6 +2098,46 @@
 		goto exit;
 	}
 
+	if (sc->hw->conf.flags & IEEE80211_CONF_PS) {
+		struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+		/*
+		 * mac80211 does not set PM field for normal data frames, so we
+		 * need to update that based on the current PS mode.
+		 */
+		if (ieee80211_is_data(hdr->frame_control) &&
+		    !ieee80211_is_nullfunc(hdr->frame_control) &&
+		    !ieee80211_has_pm(hdr->frame_control)) {
+			DPRINTF(sc, ATH_DBG_PS, "Add PM=1 for a TX frame "
+				"while in PS mode\n");
+			hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+		}
+	}
+
+	if (unlikely(sc->sc_ah->power_mode != ATH9K_PM_AWAKE)) {
+		/*
+		 * We are using PS-Poll and mac80211 can request TX while in
+		 * power save mode. Need to wake up hardware for the TX to be
+		 * completed and if needed, also for RX of buffered frames.
+		 */
+		struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+		ath9k_ps_wakeup(sc);
+		ath9k_hw_setrxabort(sc->sc_ah, 0);
+		if (ieee80211_is_pspoll(hdr->frame_control)) {
+			DPRINTF(sc, ATH_DBG_PS, "Sending PS-Poll to pick a "
+				"buffered frame\n");
+			sc->sc_flags |= SC_OP_WAIT_FOR_PSPOLL_DATA;
+		} else {
+			DPRINTF(sc, ATH_DBG_PS, "Wake up to complete TX\n");
+			sc->sc_flags |= SC_OP_WAIT_FOR_TX_ACK;
+		}
+		/*
+		 * The actual restore operation will happen only after
+		 * the sc_flags bit is cleared. We are just dropping
+		 * the ps_usecount here.
+		 */
+		ath9k_ps_restore(sc);
+	}
+
 	memset(&txctl, 0, sizeof(struct ath_tx_control));
 
 	/*
@@ -2311,7 +2375,10 @@
 			if (!(ah->caps.hw_caps &
 			      ATH9K_HW_CAP_AUTOSLEEP)) {
 				ath9k_hw_setrxabort(sc->sc_ah, 0);
-				sc->sc_flags &= ~SC_OP_WAIT_FOR_BEACON;
+				sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON |
+						  SC_OP_WAIT_FOR_CAB |
+						  SC_OP_WAIT_FOR_PSPOLL_DATA |
+						  SC_OP_WAIT_FOR_TX_ACK);
 				if (sc->imask & ATH9K_INT_TIM_TIMER) {
 					sc->imask &= ~ATH9K_INT_TIM_TIMER;
 					ath9k_hw_set_interrupts(sc->sc_ah,
@@ -2386,8 +2453,10 @@
 	*total_flags &= SUPPORTED_FILTERS;
 
 	sc->rx.rxfilter = *total_flags;
+	ath9k_ps_wakeup(sc);
 	rfilt = ath_calcrxfilter(sc);
 	ath9k_hw_setrxfilter(sc->sc_ah, rfilt);
+	ath9k_ps_restore(sc);
 
 	DPRINTF(sc, ATH_DBG_CONFIG, "Set HW RX filter: 0x%x\n", sc->rx.rxfilter);
 }
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index b46badd..5014a19 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -473,6 +473,159 @@
 	spin_unlock_bh(&sc->rx.rxflushlock);
 }
 
+static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb)
+{
+	/* Check whether the Beacon frame has DTIM indicating buffered bc/mc */
+	struct ieee80211_mgmt *mgmt;
+	u8 *pos, *end, id, elen;
+	struct ieee80211_tim_ie *tim;
+
+	mgmt = (struct ieee80211_mgmt *)skb->data;
+	pos = mgmt->u.beacon.variable;
+	end = skb->data + skb->len;
+
+	while (pos + 2 < end) {
+		id = *pos++;
+		elen = *pos++;
+		if (pos + elen > end)
+			break;
+
+		if (id == WLAN_EID_TIM) {
+			if (elen < sizeof(*tim))
+				break;
+			tim = (struct ieee80211_tim_ie *) pos;
+			if (tim->dtim_count != 0)
+				break;
+			return tim->bitmap_ctrl & 0x01;
+		}
+
+		pos += elen;
+	}
+
+	return false;
+}
+
+static void ath_rx_ps_back_to_sleep(struct ath_softc *sc)
+{
+	sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON | SC_OP_WAIT_FOR_CAB);
+}
+
+static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb)
+{
+	struct ieee80211_mgmt *mgmt;
+
+	if (skb->len < 24 + 8 + 2 + 2)
+		return;
+
+	mgmt = (struct ieee80211_mgmt *)skb->data;
+	if (memcmp(sc->curbssid, mgmt->bssid, ETH_ALEN) != 0)
+		return; /* not from our current AP */
+
+	if (sc->sc_flags & SC_OP_BEACON_SYNC) {
+		sc->sc_flags &= ~SC_OP_BEACON_SYNC;
+		DPRINTF(sc, ATH_DBG_PS, "Reconfigure Beacon timers based on "
+			"timestamp from the AP\n");
+		ath_beacon_config(sc, NULL);
+	}
+
+	if (!(sc->hw->conf.flags & IEEE80211_CONF_PS)) {
+		/* We are not in PS mode anymore; remain awake */
+		DPRINTF(sc, ATH_DBG_PS, "Not in PS mode anymore, remain "
+			"awake\n");
+		sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON | SC_OP_WAIT_FOR_CAB);
+		return;
+	}
+
+	if (ath_beacon_dtim_pending_cab(skb)) {
+		/*
+		 * Remain awake waiting for buffered broadcast/multicast
+		 * frames.
+		 */
+		DPRINTF(sc, ATH_DBG_PS, "Received DTIM beacon indicating "
+			"buffered broadcast/multicast frame(s)\n");
+		sc->sc_flags |= SC_OP_WAIT_FOR_CAB;
+		return;
+	}
+
+	if (sc->sc_flags & SC_OP_WAIT_FOR_CAB) {
+		/*
+		 * This can happen if a broadcast frame is dropped or the AP
+		 * fails to send a frame indicating that all CAB frames have
+		 * been delivered.
+		 */
+		DPRINTF(sc, ATH_DBG_PS, "PS wait for CAB frames timed out\n");
+	}
+
+	/* No more broadcast/multicast frames to be received at this point. */
+	ath_rx_ps_back_to_sleep(sc);
+}
+
+static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr;
+
+	hdr = (struct ieee80211_hdr *)skb->data;
+
+	/* Process Beacon and CAB receive in PS state */
+	if ((sc->sc_flags & SC_OP_WAIT_FOR_BEACON) &&
+	    ieee80211_is_beacon(hdr->frame_control))
+		ath_rx_ps_beacon(sc, skb);
+	else if ((sc->sc_flags & SC_OP_WAIT_FOR_CAB) &&
+		 (ieee80211_is_data(hdr->frame_control) ||
+		  ieee80211_is_action(hdr->frame_control)) &&
+		 is_multicast_ether_addr(hdr->addr1) &&
+		 !ieee80211_has_moredata(hdr->frame_control)) {
+		DPRINTF(sc, ATH_DBG_PS, "All PS CAB frames received, back to "
+			"sleep\n");
+		/*
+		 * No more broadcast/multicast frames to be received at this
+		 * point.
+		 */
+		ath_rx_ps_back_to_sleep(sc);
+	} else if ((sc->sc_flags & SC_OP_WAIT_FOR_PSPOLL_DATA) &&
+		   !is_multicast_ether_addr(hdr->addr1) &&
+		   !ieee80211_has_morefrags(hdr->frame_control)) {
+		sc->sc_flags &= ~SC_OP_WAIT_FOR_PSPOLL_DATA;
+		DPRINTF(sc, ATH_DBG_PS, "Going back to sleep after having "
+			"received PS-Poll data (0x%x)\n",
+			sc->sc_flags & (SC_OP_WAIT_FOR_BEACON |
+					SC_OP_WAIT_FOR_CAB |
+					SC_OP_WAIT_FOR_PSPOLL_DATA |
+					SC_OP_WAIT_FOR_TX_ACK));
+	}
+}
+
+static void ath_rx_send_to_mac80211(struct ath_softc *sc, struct sk_buff *skb,
+				    struct ieee80211_rx_status *rx_status)
+{
+	struct ieee80211_hdr *hdr;
+
+	hdr = (struct ieee80211_hdr *)skb->data;
+
+	/* Send the frame to mac80211 */
+	if (is_multicast_ether_addr(hdr->addr1)) {
+		int i;
+		/*
+		 * Deliver broadcast/multicast frames to all suitable
+		 * virtual wiphys.
+		 */
+		/* TODO: filter based on channel configuration */
+		for (i = 0; i < sc->num_sec_wiphy; i++) {
+			struct ath_wiphy *aphy = sc->sec_wiphy[i];
+			struct sk_buff *nskb;
+			if (aphy == NULL)
+				continue;
+			nskb = skb_copy(skb, GFP_ATOMIC);
+			if (nskb)
+				__ieee80211_rx(aphy->hw, nskb, rx_status);
+		}
+		__ieee80211_rx(sc->hw, skb, rx_status);
+	} else {
+		/* Deliver unicast frames based on receiver address */
+		__ieee80211_rx(ath_get_virt_hw(sc, hdr), skb, rx_status);
+	}
+}
+
 int ath_rx_tasklet(struct ath_softc *sc, int flush)
 {
 #define PA2DESC(_sc, _pa)                                               \
@@ -622,7 +775,7 @@
 
 		if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error) {
 			rx_status.flag |= RX_FLAG_DECRYPTED;
-		} else if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_PROTECTED)
+		} else if (ieee80211_has_protected(fc)
 			   && !decrypt_error && skb->len >= hdrlen + 4) {
 			keyix = skb->data[hdrlen + 3] >> 6;
 
@@ -631,36 +784,11 @@
 		}
 		if (ah->sw_mgmt_crypto &&
 		    (rx_status.flag & RX_FLAG_DECRYPTED) &&
-		    ieee80211_is_mgmt(hdr->frame_control)) {
+		    ieee80211_is_mgmt(fc)) {
 			/* Use software decrypt for management frames. */
 			rx_status.flag &= ~RX_FLAG_DECRYPTED;
 		}
 
-		/* Send the frame to mac80211 */
-		if (hdr->addr1[5] & 0x01) {
-			int i;
-			/*
-			 * Deliver broadcast/multicast frames to all suitable
-			 * virtual wiphys.
-			 */
-			/* TODO: filter based on channel configuration */
-			for (i = 0; i < sc->num_sec_wiphy; i++) {
-				struct ath_wiphy *aphy = sc->sec_wiphy[i];
-				struct sk_buff *nskb;
-				if (aphy == NULL)
-					continue;
-				nskb = skb_copy(skb, GFP_ATOMIC);
-				if (nskb)
-					__ieee80211_rx(aphy->hw, nskb,
-						       &rx_status);
-			}
-			__ieee80211_rx(sc->hw, skb, &rx_status);
-		} else {
-			/* Deliver unicast frames based on receiver address */
-			__ieee80211_rx(ath_get_virt_hw(sc, hdr), skb,
-				       &rx_status);
-		}
-
 		/* We will now give hardware our shiny new allocated skb */
 		bf->bf_mpdu = requeue_skb;
 		bf->bf_buf_addr = dma_map_single(sc->dev, requeue_skb->data,
@@ -672,6 +800,7 @@
 			bf->bf_mpdu = NULL;
 			DPRINTF(sc, ATH_DBG_FATAL,
 				"dma_mapping_error() on RX\n");
+			ath_rx_send_to_mac80211(sc, skb, &rx_status);
 			break;
 		}
 		bf->bf_dmacontext = bf->bf_buf_addr;
@@ -687,11 +816,12 @@
 			sc->rx.rxotherant = 0;
 		}
 
-		if (ieee80211_is_beacon(fc) &&
-				(sc->sc_flags & SC_OP_WAIT_FOR_BEACON)) {
-			sc->sc_flags &= ~SC_OP_WAIT_FOR_BEACON;
-			ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP);
-		}
+		if (unlikely(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON |
+					     SC_OP_WAIT_FOR_PSPOLL_DATA)))
+			ath_rx_ps(sc, skb);
+
+		ath_rx_send_to_mac80211(sc, skb, &rx_status);
+
 requeue:
 		list_move_tail(&bf->list, &sc->rx.rxbuf);
 		ath_rx_buf_link(sc, bf);
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 41c4282..a8def4f 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1070,7 +1070,7 @@
 		r = ath9k_hw_reset(ah, sc->sc_ah->curchan, true);
 		if (r)
 			DPRINTF(sc, ATH_DBG_FATAL,
-				"Unable to reset hardware; reset status %u\n",
+				"Unable to reset hardware; reset status %d\n",
 				r);
 		spin_unlock_bh(&sc->sc_resetlock);
 	}
@@ -1790,6 +1790,16 @@
 		skb_pull(skb, padsize);
 	}
 
+	if (sc->sc_flags & SC_OP_WAIT_FOR_TX_ACK) {
+		sc->sc_flags &= ~SC_OP_WAIT_FOR_TX_ACK;
+		DPRINTF(sc, ATH_DBG_PS, "Going back to sleep after having "
+			"received TX status (0x%x)\n",
+			sc->sc_flags & (SC_OP_WAIT_FOR_BEACON |
+					SC_OP_WAIT_FOR_CAB |
+					SC_OP_WAIT_FOR_PSPOLL_DATA |
+					SC_OP_WAIT_FOR_TX_ACK));
+	}
+
 	if (frame_type == ATH9K_NOT_INTERNAL)
 		ieee80211_tx_status(hw, skb);
 	else
diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
index fdf07c8..7a89f9f 100644
--- a/drivers/net/wireless/ath/regd.c
+++ b/drivers/net/wireless/ath/regd.c
@@ -200,8 +200,10 @@
 				continue;
 
 			if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
-				r = freq_reg_info(wiphy, ch->center_freq,
-					&bandwidth, &reg_rule);
+				r = freq_reg_info(wiphy,
+						  ch->center_freq,
+						  bandwidth,
+						  &reg_rule);
 				if (r)
 					continue;
 				/*
@@ -265,7 +267,7 @@
 	 */
 
 	ch = &sband->channels[11]; /* CH 12 */
-	r = freq_reg_info(wiphy, ch->center_freq, &bandwidth, &reg_rule);
+	r = freq_reg_info(wiphy, ch->center_freq, bandwidth, &reg_rule);
 	if (!r) {
 		if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
 			if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
@@ -273,7 +275,7 @@
 	}
 
 	ch = &sband->channels[12]; /* CH 13 */
-	r = freq_reg_info(wiphy, ch->center_freq, &bandwidth, &reg_rule);
+	r = freq_reg_info(wiphy, ch->center_freq, bandwidth, &reg_rule);
 	if (!r) {
 		if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
 			if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index 2615aaf..cb4a871 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -3553,28 +3553,26 @@
 
 	B43_WARN_ON(wl->vif != vif);
 
+	spin_lock_irqsave(&wl->irq_lock, flags);
 	if (changed & BSS_CHANGED_BSSID) {
-		spin_lock_irqsave(&wl->irq_lock, flags);
 		if (conf->bssid)
 			memcpy(wl->bssid, conf->bssid, ETH_ALEN);
 		else
 			memset(wl->bssid, 0, ETH_ALEN);
-
-		if (b43_status(dev) >= B43_STAT_INITIALIZED) {
-			if (b43_is_mode(wl, NL80211_IFTYPE_AP) ||
-			    b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT)) {
-				B43_WARN_ON(vif->type != wl->if_type);
-				if (changed & BSS_CHANGED_BEACON)
-					b43_update_templates(wl);
-			} else if (b43_is_mode(wl, NL80211_IFTYPE_ADHOC)) {
-				if (changed & BSS_CHANGED_BEACON)
-					b43_update_templates(wl);
-			}
-			b43_write_mac_bssid_templates(dev);
-		}
-		spin_unlock_irqrestore(&wl->irq_lock, flags);
 	}
 
+	if (b43_status(dev) >= B43_STAT_INITIALIZED) {
+		if (changed & BSS_CHANGED_BEACON &&
+		    (b43_is_mode(wl, NL80211_IFTYPE_AP) ||
+		     b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT) ||
+		     b43_is_mode(wl, NL80211_IFTYPE_ADHOC)))
+			b43_update_templates(wl);
+
+		if (changed & BSS_CHANGED_BSSID)
+			b43_write_mac_bssid_templates(dev);
+	}
+	spin_unlock_irqrestore(&wl->irq_lock, flags);
+
 	b43_mac_suspend(dev);
 
 	/* Update templates for AP/mesh mode. */
@@ -3639,7 +3637,7 @@
 	err = -EINVAL;
 	switch (key->alg) {
 	case ALG_WEP:
-		if (key->keylen == LEN_WEP40)
+		if (key->keylen == WLAN_KEY_LEN_WEP40)
 			algorithm = B43_SEC_ALGO_WEP40;
 		else
 			algorithm = B43_SEC_ALGO_WEP104;
diff --git a/drivers/net/wireless/b43legacy/b43legacy.h b/drivers/net/wireless/b43legacy/b43legacy.h
index da59ef0..19a4b0b 100644
--- a/drivers/net/wireless/b43legacy/b43legacy.h
+++ b/drivers/net/wireless/b43legacy/b43legacy.h
@@ -694,8 +694,8 @@
 	/* Reason code of the last interrupt. */
 	u32 irq_reason;
 	u32 dma_reason[6];
-	/* saved irq enable/disable state bitfield. */
-	u32 irq_savedstate;
+	/* The currently active generic-interrupt mask. */
+	u32 irq_mask;
 	/* Link Quality calculation context. */
 	struct b43legacy_noise_calculation noisecalc;
 	/* if > 0 MAC is suspended. if == 0 MAC is enabled. */
diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c
index 07c7898..f6f3fbf 100644
--- a/drivers/net/wireless/b43legacy/main.c
+++ b/drivers/net/wireless/b43legacy/main.c
@@ -583,35 +583,6 @@
 	b43legacy_set_slot_time(dev, 20);
 }
 
-/* Enable a Generic IRQ. "mask" is the mask of which IRQs to enable.
- * Returns the _previously_ enabled IRQ mask.
- */
-static inline u32 b43legacy_interrupt_enable(struct b43legacy_wldev *dev,
-					     u32 mask)
-{
-	u32 old_mask;
-
-	old_mask = b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_MASK);
-	b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, old_mask |
-			  mask);
-
-	return old_mask;
-}
-
-/* Disable a Generic IRQ. "mask" is the mask of which IRQs to disable.
- * Returns the _previously_ enabled IRQ mask.
- */
-static inline u32 b43legacy_interrupt_disable(struct b43legacy_wldev *dev,
-					      u32 mask)
-{
-	u32 old_mask;
-
-	old_mask = b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_MASK);
-	b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, old_mask & ~mask);
-
-	return old_mask;
-}
-
 /* Synchronize IRQ top- and bottom-half.
  * IRQs must be masked before calling this.
  * This must not be called with the irq_lock held.
@@ -1200,7 +1171,7 @@
 	/* This is the bottom half of the asynchronous beacon update. */
 
 	/* Ignore interrupt in the future. */
-	dev->irq_savedstate &= ~B43legacy_IRQ_BEACON;
+	dev->irq_mask &= ~B43legacy_IRQ_BEACON;
 
 	cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD);
 	beacon0_valid = (cmd & B43legacy_MACCMD_BEACON0_VALID);
@@ -1209,7 +1180,7 @@
 	/* Schedule interrupt manually, if busy. */
 	if (beacon0_valid && beacon1_valid) {
 		b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON, B43legacy_IRQ_BEACON);
-		dev->irq_savedstate |= B43legacy_IRQ_BEACON;
+		dev->irq_mask |= B43legacy_IRQ_BEACON;
 		return;
 	}
 
@@ -1247,12 +1218,11 @@
 	dev = wl->current_dev;
 	if (likely(dev && (b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED))) {
 		spin_lock_irq(&wl->irq_lock);
-		/* update beacon right away or defer to irq */
-		dev->irq_savedstate = b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_MASK);
+		/* Update beacon right away or defer to IRQ. */
 		handle_irq_beacon(dev);
 		/* The handler might have updated the IRQ mask. */
 		b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK,
-			    dev->irq_savedstate);
+				  dev->irq_mask);
 		mmiowb();
 		spin_unlock_irq(&wl->irq_lock);
 	}
@@ -1398,7 +1368,7 @@
 	if (reason & B43legacy_IRQ_TX_OK)
 		handle_irq_transmit_status(dev);
 
-	b43legacy_interrupt_enable(dev, dev->irq_savedstate);
+	b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask);
 	mmiowb();
 	spin_unlock_irqrestore(&dev->wl->irq_lock, flags);
 }
@@ -1450,18 +1420,18 @@
 	struct b43legacy_wldev *dev = dev_id;
 	u32 reason;
 
-	if (!dev)
-		return IRQ_NONE;
+	B43legacy_WARN_ON(!dev);
 
 	spin_lock(&dev->wl->irq_lock);
 
-	if (b43legacy_status(dev) < B43legacy_STAT_STARTED)
+	if (unlikely(b43legacy_status(dev) < B43legacy_STAT_STARTED))
+		/* This can only happen on shared IRQ lines. */
 		goto out;
 	reason = b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON);
 	if (reason == 0xffffffff) /* shared IRQ */
 		goto out;
 	ret = IRQ_HANDLED;
-	reason &= b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_MASK);
+	reason &= dev->irq_mask;
 	if (!reason)
 		goto out;
 
@@ -1485,10 +1455,9 @@
 					      & 0x0000DC00;
 
 	b43legacy_interrupt_ack(dev, reason);
-	/* disable all IRQs. They are enabled again in the bottom half. */
-	dev->irq_savedstate = b43legacy_interrupt_disable(dev,
-							  B43legacy_IRQ_ALL);
-	/* save the reason code and call our bottom half. */
+	/* Disable all IRQs. They are enabled again in the bottom half. */
+	b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0);
+	/* Save the reason code and call our bottom half. */
 	dev->irq_reason = reason;
 	tasklet_schedule(&dev->isr_tasklet);
 out:
@@ -1948,7 +1917,8 @@
 
 		/* Re-enable IRQs. */
 		spin_lock_irq(&dev->wl->irq_lock);
-		b43legacy_interrupt_enable(dev, dev->irq_savedstate);
+		b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK,
+				  dev->irq_mask);
 		spin_unlock_irq(&dev->wl->irq_lock);
 	}
 }
@@ -1967,10 +1937,9 @@
 		/* Mask IRQs before suspending MAC. Otherwise
 		 * the MAC stays busy and won't suspend. */
 		spin_lock_irq(&dev->wl->irq_lock);
-		tmp = b43legacy_interrupt_disable(dev, B43legacy_IRQ_ALL);
+		b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0);
 		spin_unlock_irq(&dev->wl->irq_lock);
 		b43legacy_synchronize_irq(dev);
-		dev->irq_savedstate = tmp;
 
 		b43legacy_power_saving_ctl_bits(dev, -1, 1);
 		b43legacy_write32(dev, B43legacy_MMIO_MACCTL,
@@ -2659,7 +2628,6 @@
 	int antenna_tx;
 	int antenna_rx;
 	int err = 0;
-	u32 savedirqs;
 
 	antenna_tx = B43legacy_ANTENNA_DEFAULT;
 	antenna_rx = B43legacy_ANTENNA_DEFAULT;
@@ -2699,7 +2667,7 @@
 		spin_unlock_irqrestore(&wl->irq_lock, flags);
 		goto out_unlock_mutex;
 	}
-	savedirqs = b43legacy_interrupt_disable(dev, B43legacy_IRQ_ALL);
+	b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0);
 	spin_unlock_irqrestore(&wl->irq_lock, flags);
 	b43legacy_synchronize_irq(dev);
 
@@ -2738,7 +2706,7 @@
 	}
 
 	spin_lock_irqsave(&wl->irq_lock, flags);
-	b43legacy_interrupt_enable(dev, savedirqs);
+	b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask);
 	mmiowb();
 	spin_unlock_irqrestore(&wl->irq_lock, flags);
 out_unlock_mutex:
@@ -2801,7 +2769,6 @@
 	struct b43legacy_wldev *dev;
 	struct b43legacy_phy *phy;
 	unsigned long flags;
-	u32 savedirqs;
 
 	mutex_lock(&wl->mutex);
 	B43legacy_WARN_ON(wl->vif != vif);
@@ -2817,31 +2784,28 @@
 		spin_unlock_irqrestore(&wl->irq_lock, flags);
 		goto out_unlock_mutex;
 	}
-	savedirqs = b43legacy_interrupt_disable(dev, B43legacy_IRQ_ALL);
+	b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0);
 
 	if (changed & BSS_CHANGED_BSSID) {
-		spin_unlock_irqrestore(&wl->irq_lock, flags);
 		b43legacy_synchronize_irq(dev);
 
 		if (conf->bssid)
 			memcpy(wl->bssid, conf->bssid, ETH_ALEN);
 		else
 			memset(wl->bssid, 0, ETH_ALEN);
-
-		if (b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED) {
-			if (b43legacy_is_mode(wl, NL80211_IFTYPE_AP)) {
-				B43legacy_WARN_ON(vif->type != NL80211_IFTYPE_AP);
-				if (changed & BSS_CHANGED_BEACON)
-					b43legacy_update_templates(wl);
-			} else if (b43legacy_is_mode(wl, NL80211_IFTYPE_ADHOC)) {
-				if (changed & BSS_CHANGED_BEACON)
-					b43legacy_update_templates(wl);
-			}
-			b43legacy_write_mac_bssid_templates(dev);
-		}
-		spin_unlock_irqrestore(&wl->irq_lock, flags);
 	}
 
+	if (b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED) {
+		if (changed & BSS_CHANGED_BEACON &&
+		    (b43legacy_is_mode(wl, NL80211_IFTYPE_AP) ||
+		     b43legacy_is_mode(wl, NL80211_IFTYPE_ADHOC)))
+			b43legacy_update_templates(wl);
+
+		if (changed & BSS_CHANGED_BSSID)
+			b43legacy_write_mac_bssid_templates(dev);
+	}
+	spin_unlock_irqrestore(&wl->irq_lock, flags);
+
 	b43legacy_mac_suspend(dev);
 
 	if (changed & BSS_CHANGED_BEACON_INT &&
@@ -2862,7 +2826,7 @@
 	b43legacy_mac_enable(dev);
 
 	spin_lock_irqsave(&wl->irq_lock, flags);
-	b43legacy_interrupt_enable(dev, savedirqs);
+	b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask);
 	/* XXX: why? */
 	mmiowb();
 	spin_unlock_irqrestore(&wl->irq_lock, flags);
@@ -2922,8 +2886,7 @@
 	 * setting the status to INITIALIZED, as the interrupt handler
 	 * won't care about IRQs then. */
 	spin_lock_irqsave(&wl->irq_lock, flags);
-	dev->irq_savedstate = b43legacy_interrupt_disable(dev,
-							  B43legacy_IRQ_ALL);
+	b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0);
 	b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_MASK); /* flush */
 	spin_unlock_irqrestore(&wl->irq_lock, flags);
 	b43legacy_synchronize_irq(dev);
@@ -2963,7 +2926,7 @@
 
 	/* Start data flow (TX/RX) */
 	b43legacy_mac_enable(dev);
-	b43legacy_interrupt_enable(dev, dev->irq_savedstate);
+	b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask);
 
 	/* Start maintenance work */
 	b43legacy_periodic_tasks_setup(dev);
@@ -3126,7 +3089,7 @@
 	/* IRQ related flags */
 	dev->irq_reason = 0;
 	memset(dev->dma_reason, 0, sizeof(dev->dma_reason));
-	dev->irq_savedstate = B43legacy_IRQ_MASKTEMPLATE;
+	dev->irq_mask = B43legacy_IRQ_MASKTEMPLATE;
 
 	dev->mac_suspended = 1;
 
diff --git a/drivers/net/wireless/b43legacy/pio.c b/drivers/net/wireless/b43legacy/pio.c
index 746d536..51866c9 100644
--- a/drivers/net/wireless/b43legacy/pio.c
+++ b/drivers/net/wireless/b43legacy/pio.c
@@ -443,7 +443,7 @@
 	pio->queue3 = queue;
 
 	if (dev->dev->id.revision < 3)
-		dev->irq_savedstate |= B43legacy_IRQ_PIO_WORKAROUND;
+		dev->irq_mask |= B43legacy_IRQ_PIO_WORKAROUND;
 
 	b43legacydbg(dev->wl, "PIO initialized\n");
 	err = 0;
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-led.c b/drivers/net/wireless/iwlwifi/iwl-3945-led.c
index ac22f59..bd7e520 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945-led.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-led.c
@@ -44,6 +44,15 @@
 #include "iwl-core.h"
 #include "iwl-dev.h"
 
+#ifdef CONFIG_IWLWIFI_DEBUG
+static const char *led_type_str[] = {
+	__stringify(IWL_LED_TRG_TX),
+	__stringify(IWL_LED_TRG_RX),
+	__stringify(IWL_LED_TRG_ASSOC),
+	__stringify(IWL_LED_TRG_RADIO),
+	NULL
+};
+#endif /* CONFIG_IWLWIFI_DEBUG */
 
 static const struct {
 	u16 brightness;
@@ -61,7 +70,7 @@
 	{10, 110, 110},
 	{5, 130, 130},
 	{0, 167, 167},
-	/*SOLID_ON*/
+	/* SOLID_ON */
 	{-1, IWL_LED_SOLID, 0}
 };
 
@@ -143,6 +152,30 @@
 }
 
 /*
+ *  Set led on in case of association
+ *  */
+static int iwl3945_led_associate(struct iwl_priv *priv, int led_id)
+{
+	IWL_DEBUG_LED(priv, "Associated\n");
+
+	priv->allow_blinking = 1;
+	return iwl3945_led_on(priv, led_id);
+}
+/* Set Led off in case of disassociation */
+static int iwl3945_led_disassociate(struct iwl_priv *priv, int led_id)
+{
+	IWL_DEBUG_LED(priv, "Disassociated\n");
+
+	priv->allow_blinking = 0;
+	if (iwl_is_rfkill(priv))
+		iwl3945_led_off(priv, led_id);
+	else
+		iwl3945_led_on(priv, led_id);
+
+	return 0;
+}
+
+/*
  * brightness call back function for Tx/Rx LED
  */
 static int iwl3945_led_associated(struct iwl_priv *priv, int led_id)
@@ -165,26 +198,21 @@
 				enum led_brightness brightness)
 {
 	struct iwl_led *led = container_of(led_cdev,
-					       struct iwl_led, led_dev);
+					   struct iwl_led, led_dev);
 	struct iwl_priv *priv = led->priv;
 
 	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
 		return;
 
+	IWL_DEBUG_LED(priv, "Led type = %s brightness = %d\n",
+			led_type_str[led->type], brightness);
+
 	switch (brightness) {
 	case LED_FULL:
-		if (led->type == IWL_LED_TRG_ASSOC) {
-			priv->allow_blinking = 1;
-			IWL_DEBUG_LED(priv, "MAC is  associated\n");
-		}
 		if (led->led_on)
 			led->led_on(priv, IWL_LED_LINK);
 		break;
 	case LED_OFF:
-		if (led->type == IWL_LED_TRG_ASSOC) {
-			priv->allow_blinking = 0;
-			IWL_DEBUG_LED(priv, "MAC is disassociated\n");
-		}
 		if (led->led_off)
 			led->led_off(priv, IWL_LED_LINK);
 		break;
@@ -197,8 +225,6 @@
 	}
 }
 
-
-
 /*
  * Register led class with the system
  */
@@ -237,12 +263,12 @@
 static inline u8 get_blink_rate(struct iwl_priv *priv)
 {
 	int index;
-	u64 current_tpt = priv->rxtxpackets;
-	s64 tpt = current_tpt - priv->led_tpt;
+	s64 tpt = priv->rxtxpackets;
 
 	if (tpt < 0)
 		tpt = -tpt;
-	priv->led_tpt = current_tpt;
+
+	IWL_DEBUG_LED(priv, "tpt %lld \n", (long long)tpt);
 
 	if (!priv->allow_blinking)
 		index = IWL_MAX_BLINK_TBL;
@@ -250,13 +276,9 @@
 		for (index = 0; index < IWL_MAX_BLINK_TBL; index++)
 			if (tpt > (blink_tbl[index].brightness * IWL_1MB_RATE))
 				break;
-	return index;
-}
 
-static inline int is_rf_kill(struct iwl_priv *priv)
-{
-	return test_bit(STATUS_RF_KILL_HW, &priv->status) ||
-		test_bit(STATUS_RF_KILL_SW, &priv->status);
+	IWL_DEBUG_LED(priv, "LED BLINK IDX=%d\n", index);
+	return index;
 }
 
 /*
@@ -272,7 +294,7 @@
 		priv->last_blink_time = 0;
 		return;
 	}
-	if (is_rf_kill(priv)) {
+	if (iwl_is_rfkill(priv)) {
 		priv->last_blink_time = 0;
 		return;
 	}
@@ -341,8 +363,8 @@
 				   IWL_LED_TRG_ASSOC, 0, trigger);
 
 	/* for assoc always turn led on */
-	priv->led[IWL_LED_TRG_ASSOC].led_on = iwl3945_led_on;
-	priv->led[IWL_LED_TRG_ASSOC].led_off = iwl3945_led_on;
+	priv->led[IWL_LED_TRG_ASSOC].led_on = iwl3945_led_associate;
+	priv->led[IWL_LED_TRG_ASSOC].led_off = iwl3945_led_disassociate;
 	priv->led[IWL_LED_TRG_ASSOC].led_pattern = NULL;
 
 	if (ret)
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c
index 5b0c6e5..fd65e1c3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.c
@@ -98,7 +98,6 @@
  *   ... and set IWL_EVT_DISABLE to 1. */
 void iwl3945_disable_events(struct iwl_priv *priv)
 {
-	int ret;
 	int i;
 	u32 base;		/* SRAM address of event log header */
 	u32 disable_ptr;	/* SRAM address of event-disable bitmap array */
@@ -159,26 +158,17 @@
 		return;
 	}
 
-	ret = iwl_grab_nic_access(priv);
-	if (ret) {
-		IWL_WARN(priv, "Can not read from adapter at this time.\n");
-		return;
-	}
-
 	disable_ptr = iwl_read_targ_mem(priv, base + (4 * sizeof(u32)));
 	array_size = iwl_read_targ_mem(priv, base + (5 * sizeof(u32)));
-	iwl_release_nic_access(priv);
 
 	if (IWL_EVT_DISABLE && (array_size == IWL_EVT_DISABLE_SIZE)) {
 		IWL_DEBUG_INFO(priv, "Disabling selected uCode log events at 0x%x\n",
 			       disable_ptr);
-		ret = iwl_grab_nic_access(priv);
 		for (i = 0; i < IWL_EVT_DISABLE_SIZE; i++)
 			iwl_write_targ_mem(priv,
 					   disable_ptr + (i * sizeof(u32)),
 					   evt_disable[i]);
 
-		iwl_release_nic_access(priv);
 	} else {
 		IWL_DEBUG_INFO(priv, "Selected uCode log events may be disabled\n");
 		IWL_DEBUG_INFO(priv, "  by writing \"1\"s into disable bitmap\n");
@@ -908,55 +898,30 @@
 
 static int iwl3945_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src)
 {
-	int ret;
-	unsigned long flags;
-
-	spin_lock_irqsave(&priv->lock, flags);
-	ret = iwl_grab_nic_access(priv);
-	if (ret) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		return ret;
-	}
-
 	if (src == IWL_PWR_SRC_VAUX) {
 		if (pci_pme_capable(priv->pci_dev, PCI_D3cold)) {
 			iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG,
 					APMG_PS_CTRL_VAL_PWR_SRC_VAUX,
 					~APMG_PS_CTRL_MSK_PWR_SRC);
-			iwl_release_nic_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_nic_access(priv);
 		}
 	} else {
 		iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG,
 				APMG_PS_CTRL_VAL_PWR_SRC_VMAIN,
 				~APMG_PS_CTRL_MSK_PWR_SRC);
 
-		iwl_release_nic_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 ret;
+	return 0;
 }
 
 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_nic_access(priv);
-	if (rc) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		return rc;
-	}
-
 	iwl_write_direct32(priv, FH39_RCSR_RBD_BASE(0), rxq->dma_addr);
 	iwl_write_direct32(priv, FH39_RCSR_RPTR_ADDR(0), rxq->rb_stts_dma);
 	iwl_write_direct32(priv, FH39_RCSR_WPTR(0), 0);
@@ -973,23 +938,11 @@
 	/* fake read to flush all prev I/O */
 	iwl_read_direct32(priv, FH39_RSSR_CTRL);
 
-	iwl_release_nic_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_nic_access(priv);
-	if (rc) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		return rc;
-	}
 
 	/* bypass mode */
 	iwl_write_prph(priv, ALM_SCD_MODE_REG, 0x2);
@@ -1017,8 +970,6 @@
 		FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RSP_WAIT_TH |
 		FH39_TSSR_TX_MSG_CONFIG_REG_VAL_RSP_WAIT_TH);
 
-	iwl_release_nic_access(priv);
-	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return 0;
 }
@@ -1061,7 +1012,7 @@
 
 static int iwl3945_apm_init(struct iwl_priv *priv)
 {
-	int ret = 0;
+	int ret;
 
 	iwl_power_initialize(priv);
 
@@ -1083,10 +1034,6 @@
 		goto out;
 	}
 
-	ret = iwl_grab_nic_access(priv);
-	if (ret)
-		goto out;
-
 	/* enable DMA */
 	iwl_write_prph(priv, APMG_CLK_CTRL_REG, APMG_CLK_VAL_DMA_CLK_RQT |
 						APMG_CLK_VAL_BSM_CLK_RQT);
@@ -1097,7 +1044,6 @@
 	iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG,
 			  APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
 
-	iwl_release_nic_access(priv);
 out:
 	return ret;
 }
@@ -1196,22 +1142,13 @@
 
 	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_nic_access(priv);
-	if (rc) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		return rc;
-	}
 	iwl_write_direct32(priv, FH39_RCSR_WPTR(0), rxq->write & ~7);
-	iwl_release_nic_access(priv);
-
-	spin_unlock_irqrestore(&priv->lock, flags);
 
 	rc = iwl3945_txq_ctx_reset(priv);
 	if (rc)
@@ -1243,14 +1180,6 @@
 void iwl3945_hw_txq_ctx_stop(struct iwl_priv *priv)
 {
 	int txq_id;
-	unsigned long flags;
-
-	spin_lock_irqsave(&priv->lock, flags);
-	if (iwl_grab_nic_access(priv)) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		iwl3945_hw_txq_ctx_free(priv);
-		return;
-	}
 
 	/* stop SCD */
 	iwl_write_prph(priv, ALM_SCD_MODE_REG, 0);
@@ -1263,9 +1192,6 @@
 				1000);
 	}
 
-	iwl_release_nic_access(priv);
-	spin_unlock_irqrestore(&priv->lock, flags);
-
 	iwl3945_hw_txq_ctx_free(priv);
 }
 
@@ -1310,12 +1236,8 @@
 
 static int iwl3945_apm_reset(struct iwl_priv *priv)
 {
-	int rc;
-	unsigned long flags;
-
 	iwl3945_apm_stop_master(priv);
 
-	spin_lock_irqsave(&priv->lock, flags);
 
 	iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
 	udelay(10);
@@ -1325,36 +1247,31 @@
 	iwl_poll_direct_bit(priv, CSR_GP_CNTRL,
 			 CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
 
-	rc = iwl_grab_nic_access(priv);
-	if (!rc) {
-		iwl_write_prph(priv, APMG_CLK_CTRL_REG,
-					 APMG_CLK_VAL_BSM_CLK_RQT);
+	iwl_write_prph(priv, APMG_CLK_CTRL_REG,
+				APMG_CLK_VAL_BSM_CLK_RQT);
 
-		iwl_write_prph(priv, APMG_RTC_INT_MSK_REG, 0x0);
-		iwl_write_prph(priv, APMG_RTC_INT_STT_REG,
+	iwl_write_prph(priv, APMG_RTC_INT_MSK_REG, 0x0);
+	iwl_write_prph(priv, APMG_RTC_INT_STT_REG,
 					0xFFFFFFFF);
 
-		/* enable DMA */
-		iwl_write_prph(priv, APMG_CLK_EN_REG,
-					 APMG_CLK_VAL_DMA_CLK_RQT |
-					 APMG_CLK_VAL_BSM_CLK_RQT);
-		udelay(10);
+	/* enable DMA */
+	iwl_write_prph(priv, APMG_CLK_EN_REG,
+				APMG_CLK_VAL_DMA_CLK_RQT |
+				APMG_CLK_VAL_BSM_CLK_RQT);
+	udelay(10);
 
-		iwl_set_bits_prph(priv, APMG_PS_CTRL_REG,
+	iwl_set_bits_prph(priv, APMG_PS_CTRL_REG,
 				APMG_PS_CTRL_VAL_RESET_REQ);
-		udelay(5);
-		iwl_clear_bits_prph(priv, APMG_PS_CTRL_REG,
+	udelay(5);
+	iwl_clear_bits_prph(priv, APMG_PS_CTRL_REG,
 				APMG_PS_CTRL_VAL_RESET_REQ);
-		iwl_release_nic_access(priv);
-	}
 
 	/* Clear the 'host command active' bit... */
 	clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
 
 	wake_up_interruptible(&priv->wait_command_queue);
-	spin_unlock_irqrestore(&priv->lock, flags);
 
-	return rc;
+	return 0;
 }
 
 /**
@@ -2500,14 +2417,6 @@
 int iwl3945_hw_rxq_stop(struct iwl_priv *priv)
 {
 	int rc;
-	unsigned long flags;
-
-	spin_lock_irqsave(&priv->lock, flags);
-	rc = iwl_grab_nic_access(priv);
-	if (rc) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		return rc;
-	}
 
 	iwl_write_direct32(priv, FH39_RCSR_CONFIG(0), 0);
 	rc = iwl_poll_direct_bit(priv, FH39_RSSR_STATUS,
@@ -2515,28 +2424,17 @@
 	if (rc < 0)
 		IWL_ERR(priv, "Can't stop Rx DMA.\n");
 
-	iwl_release_nic_access(priv);
-	spin_unlock_irqrestore(&priv->lock, flags);
-
 	return 0;
 }
 
 int iwl3945_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 iwl3945_shared *shared_data = priv->shared_virt;
 
 	shared_data->tx_base_ptr[txq_id] = cpu_to_le32((u32)txq->q.dma_addr);
 
-	spin_lock_irqsave(&priv->lock, flags);
-	rc = iwl_grab_nic_access(priv);
-	if (rc) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		return rc;
-	}
 	iwl_write_direct32(priv, FH39_CBCC_CTRL(txq_id), 0);
 	iwl_write_direct32(priv, FH39_CBCC_BASE(txq_id), 0);
 
@@ -2546,11 +2444,9 @@
 		FH39_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD |
 		FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL |
 		FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE);
-	iwl_release_nic_access(priv);
 
 	/* fake read to flush all prev. writes */
 	iwl_read32(priv, FH39_TSSR_CBB_BASE);
-	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return 0;
 }
@@ -2858,10 +2754,6 @@
 	inst_len = priv->ucode_init.len;
 	data_len = priv->ucode_init_data.len;
 
-	rc = iwl_grab_nic_access(priv);
-	if (rc)
-		return rc;
-
 	iwl_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst);
 	iwl_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata);
 	iwl_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG, inst_len);
@@ -2875,10 +2767,8 @@
 					  le32_to_cpu(*image));
 
 	rc = iwl3945_verify_bsm(priv);
-	if (rc) {
-		iwl_release_nic_access(priv);
+	if (rc)
 		return rc;
-	}
 
 	/* Tell BSM to copy from BSM SRAM into instruction SRAM, when asked */
 	iwl_write_prph(priv, BSM_WR_MEM_SRC_REG, 0x0);
@@ -2910,8 +2800,6 @@
 	iwl_write_prph(priv, BSM_WR_CTRL_REG,
 		BSM_WR_CTRL_REG_BIT_START_EN);
 
-	iwl_release_nic_access(priv);
-
 	return 0;
 }
 
@@ -2950,6 +2838,7 @@
 	.send_tx_power	= iwl3945_send_tx_power,
 	.is_valid_rtc_data_addr = iwl3945_hw_valid_rtc_data_addr,
 	.post_associate = iwl3945_post_associate,
+	.isr = iwl_isr_legacy,
 	.config_ap = iwl3945_config_ap,
 };
 
@@ -2983,7 +2872,8 @@
 	.eeprom_size = IWL3945_EEPROM_IMG_SIZE,
 	.eeprom_ver = EEPROM_3945_EEPROM_VERSION,
 	.ops = &iwl3945_ops,
-	.mod_params = &iwl3945_mod_params
+	.mod_params = &iwl3945_mod_params,
+	.use_isr_legacy = true
 };
 
 static struct iwl_cfg iwl3945_abg_cfg = {
@@ -2995,7 +2885,8 @@
 	.eeprom_size = IWL3945_EEPROM_IMG_SIZE,
 	.eeprom_ver = EEPROM_3945_EEPROM_VERSION,
 	.ops = &iwl3945_ops,
-	.mod_params = &iwl3945_mod_params
+	.mod_params = &iwl3945_mod_params,
+	.use_isr_legacy = true
 };
 
 struct pci_device_id iwl3945_hw_card_ids[] = {
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c
index a98ff4e..a0b2941 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965.c
+++ b/drivers/net/wireless/iwlwifi/iwl-4965.c
@@ -163,10 +163,6 @@
 	inst_len = priv->ucode_init.len;
 	data_len = priv->ucode_init_data.len;
 
-	ret = iwl_grab_nic_access(priv);
-	if (ret)
-		return ret;
-
 	iwl_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst);
 	iwl_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata);
 	iwl_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG, inst_len);
@@ -179,10 +175,8 @@
 		_iwl_write_prph(priv, reg_offset, le32_to_cpu(*image));
 
 	ret = iwl4965_verify_bsm(priv);
-	if (ret) {
-		iwl_release_nic_access(priv);
+	if (ret)
 		return ret;
-	}
 
 	/* Tell BSM to copy from BSM SRAM into instruction SRAM, when asked */
 	iwl_write_prph(priv, BSM_WR_MEM_SRC_REG, 0x0);
@@ -211,7 +205,6 @@
 	 *   (e.g. when powering back up after power-save shutdown) */
 	iwl_write_prph(priv, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START_EN);
 
-	iwl_release_nic_access(priv);
 
 	return 0;
 }
@@ -229,20 +222,12 @@
 {
 	dma_addr_t pinst;
 	dma_addr_t pdata;
-	unsigned long flags;
 	int ret = 0;
 
 	/* bits 35:4 for 4965 */
 	pinst = priv->ucode_code.p_addr >> 4;
 	pdata = priv->ucode_data_backup.p_addr >> 4;
 
-	spin_lock_irqsave(&priv->lock, flags);
-	ret = iwl_grab_nic_access(priv);
-	if (ret) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		return ret;
-	}
-
 	/* Tell bootstrap uCode where to find image to load */
 	iwl_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst);
 	iwl_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata);
@@ -253,10 +238,6 @@
 	 *   that all new ptr/size info is in place */
 	iwl_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG,
 				 priv->ucode_code.len | BSM_DRAM_INST_LOAD);
-	iwl_release_nic_access(priv);
-
-	spin_unlock_irqrestore(&priv->lock, flags);
-
 	IWL_DEBUG_INFO(priv, "Runtime uCode pointers are set.\n");
 
 	return ret;
@@ -312,10 +293,12 @@
 	queue_work(priv->workqueue, &priv->restart);
 }
 
-static int is_fat_channel(__le32 rxon_flags)
+static bool is_fat_channel(__le32 rxon_flags)
 {
-	return (rxon_flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK) ||
-		(rxon_flags & RXON_FLG_CHANNEL_MODE_MIXED_MSK);
+	int chan_mod = le32_to_cpu(rxon_flags & RXON_FLG_CHANNEL_MODE_MSK)
+				    >> RXON_FLG_CHANNEL_MODE_POS;
+	return ((chan_mod == CHANNEL_MODE_PURE_40) ||
+		  (chan_mod == CHANNEL_MODE_MIXED));
 }
 
 /*
@@ -358,10 +341,6 @@
 		goto out;
 	}
 
-	ret = iwl_grab_nic_access(priv);
-	if (ret)
-		goto out;
-
 	/* enable DMA */
 	iwl_write_prph(priv, APMG_CLK_CTRL_REG, APMG_CLK_VAL_DMA_CLK_RQT |
 						APMG_CLK_VAL_BSM_CLK_RQT);
@@ -372,7 +351,6 @@
 	iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG,
 			  APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
 
-	iwl_release_nic_access(priv);
 out:
 	return ret;
 }
@@ -454,11 +432,9 @@
 static int iwl4965_apm_reset(struct iwl_priv *priv)
 {
 	int ret = 0;
-	unsigned long flags;
 
 	iwl4965_apm_stop_master(priv);
 
-	spin_lock_irqsave(&priv->lock, flags);
 
 	iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
 
@@ -475,9 +451,6 @@
 
 	udelay(10);
 
-	ret = iwl_grab_nic_access(priv);
-	if (ret)
-		goto out;
 	/* Enable DMA and BSM Clock */
 	iwl_write_prph(priv, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT |
 					      APMG_CLK_VAL_BSM_CLK_RQT);
@@ -488,14 +461,10 @@
 	iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG,
 			  APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
 
-	iwl_release_nic_access(priv);
-
 	clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
 	wake_up_interruptible(&priv->wait_command_queue);
 
 out:
-	spin_unlock_irqrestore(&priv->lock, flags);
-
 	return ret;
 }
 
@@ -681,18 +650,11 @@
 {
 	u32 a;
 	unsigned long flags;
-	int ret;
 	int i, chan;
 	u32 reg_val;
 
 	spin_lock_irqsave(&priv->lock, flags);
 
-	ret = iwl_grab_nic_access(priv);
-	if (ret) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		return ret;
-	}
-
 	/* Clear 4965's internal Tx Scheduler data base */
 	priv->scd_base_addr = iwl_read_prph(priv, IWL49_SCD_SRAM_BASE_ADDR);
 	a = priv->scd_base_addr + IWL49_SCD_CONTEXT_DATA_OFFSET;
@@ -759,10 +721,9 @@
 		iwl4965_tx_queue_set_status(priv, &priv->txq[i], ac, 0);
 	}
 
-	iwl_release_nic_access(priv);
 	spin_unlock_irqrestore(&priv->lock, flags);
 
-	return ret;
+	return 0;
 }
 
 static struct iwl_sensitivity_ranges iwl4965_sensitivity = {
@@ -788,6 +749,12 @@
 	.nrg_th_ofdm = 100,
 };
 
+static void iwl4965_set_ct_threshold(struct iwl_priv *priv)
+{
+	/* want Kelvin */
+	priv->hw_params.ct_kill_threshold = CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD);
+}
+
 /**
  * iwl4965_hw_set_hw_params
  *
@@ -822,7 +789,8 @@
 	priv->hw_params.rx_chains_num = 2;
 	priv->hw_params.valid_tx_ant = ANT_A | ANT_B;
 	priv->hw_params.valid_rx_ant = ANT_A | ANT_B;
-	priv->hw_params.ct_kill_threshold = CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD);
+	if (priv->cfg->ops->lib->temp_ops.set_ct_kill)
+		priv->cfg->ops->lib->temp_ops.set_ct_kill(priv);
 
 	priv->hw_params.sens = &iwl4965_sensitivity;
 
@@ -1524,7 +1492,7 @@
 	struct iwl4965_txpowertable_cmd cmd = { 0 };
 	int ret;
 	u8 band = 0;
-	u8 is_fat = 0;
+	bool is_fat = false;
 	u8 ctrl_chan_high = 0;
 
 	if (test_bit(STATUS_SCANNING, &priv->status)) {
@@ -1602,7 +1570,7 @@
 {
 	int rc;
 	u8 band = 0;
-	u8 is_fat = 0;
+	bool is_fat = false;
 	u8 ctrl_chan_high = 0;
 	struct iwl4965_channel_switch_cmd cmd = { 0 };
 	const struct iwl_channel_info *ch_info;
@@ -1833,8 +1801,6 @@
 static int iwl4965_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
 				   u16 ssn_idx, u8 tx_fifo)
 {
-	int ret = 0;
-
 	if ((IWL49_FIRST_AMPDU_QUEUE > txq_id) ||
 	    (IWL49_FIRST_AMPDU_QUEUE + IWL49_NUM_AMPDU_QUEUES <= txq_id)) {
 		IWL_WARN(priv,
@@ -1844,10 +1810,6 @@
 		return -EINVAL;
 	}
 
-	ret = iwl_grab_nic_access(priv);
-	if (ret)
-		return ret;
-
 	iwl4965_tx_queue_stop_scheduler(priv, txq_id);
 
 	iwl_clear_bits_prph(priv, IWL49_SCD_QUEUECHAIN_SEL, (1 << txq_id));
@@ -1861,8 +1823,6 @@
 	iwl_txq_ctx_deactivate(priv, txq_id);
 	iwl4965_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0);
 
-	iwl_release_nic_access(priv);
-
 	return 0;
 }
 
@@ -1904,7 +1864,6 @@
 				  int tx_fifo, int sta_id, int tid, u16 ssn_idx)
 {
 	unsigned long flags;
-	int ret;
 	u16 ra_tid;
 
 	if ((IWL49_FIRST_AMPDU_QUEUE > txq_id) ||
@@ -1922,11 +1881,6 @@
 	iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
 
 	spin_lock_irqsave(&priv->lock, flags);
-	ret = iwl_grab_nic_access(priv);
-	if (ret) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		return ret;
-	}
 
 	/* Stop this Tx queue before configuring it */
 	iwl4965_tx_queue_stop_scheduler(priv, txq_id);
@@ -1959,7 +1913,6 @@
 	/* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */
 	iwl4965_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1);
 
-	iwl_release_nic_access(priv);
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return 0;
@@ -2331,9 +2284,13 @@
 	},
 	.send_tx_power	= iwl4965_send_tx_power,
 	.update_chain_flags = iwl_update_chain_flags,
-	.temperature = iwl4965_temperature_calib,
 	.post_associate = iwl_post_associate,
 	.config_ap = iwl_config_ap,
+	.isr = iwl_isr_legacy,
+	.temp_ops = {
+		.temperature = iwl4965_temperature_calib,
+		.set_ct_kill = iwl4965_set_ct_threshold,
+	},
 };
 
 static struct iwl_ops iwl4965_ops = {
@@ -2354,6 +2311,7 @@
 	.eeprom_calib_ver = EEPROM_4965_TX_POWER_VERSION,
 	.ops = &iwl4965_ops,
 	.mod_params = &iwl4965_mod_params,
+	.use_isr_legacy = true
 };
 
 /* Module firmware */
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000-hw.h b/drivers/net/wireless/iwlwifi/iwl-5000-hw.h
index 15cac70..4ef6804 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000-hw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-5000-hw.h
@@ -87,6 +87,18 @@
 #define IWL50_NUM_AMPDU_QUEUES		  10
 #define IWL50_FIRST_AMPDU_QUEUE		  10
 
+/* 5150 only */
+#define IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF	(-5)
+
+static inline s32 iwl_temp_calib_to_offset(struct iwl_priv *priv)
+{
+	u16 *temp_calib = (u16 *)iwl_eeprom_query_addr(priv,
+						       EEPROM_5000_TEMPERATURE);
+	/* offset =  temperature -  voltage / coef */
+	s32 offset = (s32)(temp_calib[0] - temp_calib[1] / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF);
+	return offset;
+}
+
 /* Fixed (non-configurable) rx data from phy */
 
 /**
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index d731a83..89e1477 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -124,10 +124,6 @@
 		return ret;
 	}
 
-	ret = iwl_grab_nic_access(priv);
-	if (ret)
-		return ret;
-
 	/* enable DMA */
 	iwl_write_prph(priv, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT);
 
@@ -137,8 +133,6 @@
 	iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG,
 			  APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
 
-	iwl_release_nic_access(priv);
-
 	return ret;
 }
 
@@ -165,12 +159,9 @@
 static int iwl5000_apm_reset(struct iwl_priv *priv)
 {
 	int ret = 0;
-	unsigned long flags;
 
 	iwl5000_apm_stop_master(priv);
 
-	spin_lock_irqsave(&priv->lock, flags);
-
 	iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
 
 	udelay(10);
@@ -193,10 +184,6 @@
 		goto out;
 	}
 
-	ret = iwl_grab_nic_access(priv);
-	if (ret)
-		goto out;
-
 	/* enable DMA */
 	iwl_write_prph(priv, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT);
 
@@ -205,11 +192,7 @@
 	/* disable L1-Active */
 	iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG,
 			  APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
-
-	iwl_release_nic_access(priv);
-
 out:
-	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return ret;
 }
@@ -252,11 +235,9 @@
 	 * (PCIe power is lost before PERST# is asserted),
 	 * causing ME FW to lose ownership and not being able to obtain it back.
 	 */
-	iwl_grab_nic_access(priv);
 	iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG,
 				APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
 				~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
-	iwl_release_nic_access(priv);
 
 	spin_unlock_irqrestore(&priv->lock, flags);
 }
@@ -434,15 +415,19 @@
 	return &priv->eeprom[address];
 }
 
-static s32 iwl5150_get_ct_threshold(struct iwl_priv *priv)
+static void iwl5150_set_ct_threshold(struct iwl_priv *priv)
 {
-	const s32 volt2temp_coef = -5;
-	u16 *temp_calib = (u16 *)iwl_eeprom_query_addr(priv,
-						EEPROM_5000_TEMPERATURE);
-	/* offset =  temperate -  voltage / coef */
-	s32 offset = temp_calib[0] - temp_calib[1] / volt2temp_coef;
-	s32 threshold = (s32)CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD) - offset;
-	return threshold * volt2temp_coef;
+	const s32 volt2temp_coef = IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF;
+	s32 threshold = (s32)CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD) -
+			iwl_temp_calib_to_offset(priv);
+
+	priv->hw_params.ct_kill_threshold = threshold * volt2temp_coef;
+}
+
+static void iwl5000_set_ct_threshold(struct iwl_priv *priv)
+{
+	/* want Celsius */
+	priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD;
 }
 
 /*
@@ -533,19 +518,9 @@
 				struct fw_desc *image,
 				u32 dst_addr)
 {
-	int ret = 0;
-	unsigned long flags;
-
 	dma_addr_t phy_addr = image->p_addr;
 	u32 byte_cnt = image->len;
 
-	spin_lock_irqsave(&priv->lock, flags);
-	ret = iwl_grab_nic_access(priv);
-	if (ret) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		return ret;
-	}
-
 	iwl_write_direct32(priv,
 		FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL),
 		FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE);
@@ -574,8 +549,6 @@
 		FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE	|
 		FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD);
 
-	iwl_release_nic_access(priv);
-	spin_unlock_irqrestore(&priv->lock, flags);
 	return 0;
 }
 
@@ -736,18 +709,11 @@
 {
 	u32 a;
 	unsigned long flags;
-	int ret;
 	int i, chan;
 	u32 reg_val;
 
 	spin_lock_irqsave(&priv->lock, flags);
 
-	ret = iwl_grab_nic_access(priv);
-	if (ret) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		return ret;
-	}
-
 	priv->scd_base_addr = iwl_read_prph(priv, IWL50_SCD_SRAM_BASE_ADDR);
 	a = priv->scd_base_addr + IWL50_SCD_CONTEXT_DATA_OFFSET;
 	for (; a < priv->scd_base_addr + IWL50_SCD_TX_STTS_BITMAP_OFFSET;
@@ -815,7 +781,6 @@
 	iwl_txq_ctx_activate(priv, 8);
 	iwl_txq_ctx_activate(priv, 9);
 
-	iwl_release_nic_access(priv);
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 
@@ -868,17 +833,8 @@
 	priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant;
 	priv->hw_params.valid_rx_ant = priv->cfg->valid_rx_ant;
 
-	switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) {
-	case CSR_HW_REV_TYPE_5150:
-		/* 5150 wants in Kelvin */
-		priv->hw_params.ct_kill_threshold =
-				iwl5150_get_ct_threshold(priv);
-		break;
-	default:
-		/* all others want Celsius */
-		priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD;
-		break;
-	}
+	if (priv->cfg->ops->lib->temp_ops.set_ct_kill)
+		priv->cfg->ops->lib->temp_ops.set_ct_kill(priv);
 
 	/* Set initial calibration set */
 	switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) {
@@ -900,7 +856,6 @@
 		break;
 	}
 
-
 	return 0;
 }
 
@@ -1006,7 +961,6 @@
 				  int tx_fifo, int sta_id, int tid, u16 ssn_idx)
 {
 	unsigned long flags;
-	int ret;
 	u16 ra_tid;
 
 	if ((IWL50_FIRST_AMPDU_QUEUE > txq_id) ||
@@ -1024,11 +978,6 @@
 	iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
 
 	spin_lock_irqsave(&priv->lock, flags);
-	ret = iwl_grab_nic_access(priv);
-	if (ret) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		return ret;
-	}
 
 	/* Stop this Tx queue before configuring it */
 	iwl5000_tx_queue_stop_scheduler(priv, txq_id);
@@ -1064,7 +1013,6 @@
 	/* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */
 	iwl5000_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1);
 
-	iwl_release_nic_access(priv);
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return 0;
@@ -1073,8 +1021,6 @@
 static int iwl5000_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
 				   u16 ssn_idx, u8 tx_fifo)
 {
-	int ret;
-
 	if ((IWL50_FIRST_AMPDU_QUEUE > txq_id) ||
 	    (IWL50_FIRST_AMPDU_QUEUE + IWL50_NUM_AMPDU_QUEUES <= txq_id)) {
 		IWL_ERR(priv,
@@ -1084,10 +1030,6 @@
 		return -EINVAL;
 	}
 
-	ret = iwl_grab_nic_access(priv);
-	if (ret)
-		return ret;
-
 	iwl5000_tx_queue_stop_scheduler(priv, txq_id);
 
 	iwl_clear_bits_prph(priv, IWL50_SCD_AGGR_SEL, (1 << txq_id));
@@ -1101,8 +1043,6 @@
 	iwl_txq_ctx_deactivate(priv, txq_id);
 	iwl5000_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0);
 
-	iwl_release_nic_access(priv);
-
 	return 0;
 }
 
@@ -1434,6 +1374,17 @@
 	priv->temperature = le32_to_cpu(priv->statistics.general.temperature);
 }
 
+static void iwl5150_temperature(struct iwl_priv *priv)
+{
+	u32 vt = 0;
+	s32 offset =  iwl_temp_calib_to_offset(priv);
+
+	vt = le32_to_cpu(priv->statistics.general.temperature);
+	vt = vt / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF + offset;
+	/* now vt hold the temperature in Kelvin */
+	priv->temperature = KELVIN_TO_CELSIUS(vt);
+}
+
 /* Calc max signal level (dBm) among 3 possible receivers */
 int iwl5000_calc_rssi(struct iwl_priv *priv,
 			     struct iwl_rx_phy_res *rx_resp)
@@ -1511,7 +1462,6 @@
 	.init_alive_start = iwl5000_init_alive_start,
 	.alive_notify = iwl5000_alive_notify,
 	.send_tx_power = iwl5000_send_tx_power,
-	.temperature = iwl5000_temperature,
 	.update_chain_flags = iwl_update_chain_flags,
 	.apm_ops = {
 		.init =	iwl5000_apm_init,
@@ -1537,7 +1487,62 @@
 		.query_addr = iwl5000_eeprom_query_addr,
 	},
 	.post_associate = iwl_post_associate,
+	.isr = iwl_isr_ict,
 	.config_ap = iwl_config_ap,
+	.temp_ops = {
+		.temperature = iwl5000_temperature,
+		.set_ct_kill = iwl5000_set_ct_threshold,
+	 },
+};
+
+static struct iwl_lib_ops iwl5150_lib = {
+	.set_hw_params = iwl5000_hw_set_hw_params,
+	.txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl,
+	.txq_inval_byte_cnt_tbl = iwl5000_txq_inval_byte_cnt_tbl,
+	.txq_set_sched = iwl5000_txq_set_sched,
+	.txq_agg_enable = iwl5000_txq_agg_enable,
+	.txq_agg_disable = iwl5000_txq_agg_disable,
+	.txq_attach_buf_to_tfd = iwl_hw_txq_attach_buf_to_tfd,
+	.txq_free_tfd = iwl_hw_txq_free_tfd,
+	.txq_init = iwl_hw_tx_queue_init,
+	.rx_handler_setup = iwl5000_rx_handler_setup,
+	.setup_deferred_work = iwl5000_setup_deferred_work,
+	.is_valid_rtc_data_addr = iwl5000_hw_valid_rtc_data_addr,
+	.load_ucode = iwl5000_load_ucode,
+	.init_alive_start = iwl5000_init_alive_start,
+	.alive_notify = iwl5000_alive_notify,
+	.send_tx_power = iwl5000_send_tx_power,
+	.update_chain_flags = iwl_update_chain_flags,
+	.apm_ops = {
+		.init =	iwl5000_apm_init,
+		.reset = iwl5000_apm_reset,
+		.stop = iwl5000_apm_stop,
+		.config = iwl5000_nic_config,
+		.set_pwr_src = iwl_set_pwr_src,
+	},
+	.eeprom_ops = {
+		.regulatory_bands = {
+			EEPROM_5000_REG_BAND_1_CHANNELS,
+			EEPROM_5000_REG_BAND_2_CHANNELS,
+			EEPROM_5000_REG_BAND_3_CHANNELS,
+			EEPROM_5000_REG_BAND_4_CHANNELS,
+			EEPROM_5000_REG_BAND_5_CHANNELS,
+			EEPROM_5000_REG_BAND_24_FAT_CHANNELS,
+			EEPROM_5000_REG_BAND_52_FAT_CHANNELS
+		},
+		.verify_signature  = iwlcore_eeprom_verify_signature,
+		.acquire_semaphore = iwlcore_eeprom_acquire_semaphore,
+		.release_semaphore = iwlcore_eeprom_release_semaphore,
+		.calib_version	= iwl5000_eeprom_calib_version,
+		.query_addr = iwl5000_eeprom_query_addr,
+	},
+	.post_associate = iwl_post_associate,
+	.isr = iwl_isr_ict,
+	.config_ap = iwl_config_ap,
+	.temp_ops = {
+		.temperature = iwl5150_temperature,
+		.set_ct_kill = iwl5150_set_ct_threshold,
+	 },
 };
 
 struct iwl_ops iwl5000_ops = {
@@ -1547,6 +1552,13 @@
 	.smgmt = &iwl5000_station_mgmt,
 };
 
+static struct iwl_ops iwl5150_ops = {
+	.lib = &iwl5150_lib,
+	.hcmd = &iwl5000_hcmd,
+	.utils = &iwl5000_hcmd_utils,
+	.smgmt = &iwl5000_station_mgmt,
+};
+
 struct iwl_mod_params iwl50_mod_params = {
 	.num_of_queues = IWL50_NUM_QUEUES,
 	.num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES,
@@ -1642,7 +1654,7 @@
 	.ucode_api_max = IWL5150_UCODE_API_MAX,
 	.ucode_api_min = IWL5150_UCODE_API_MIN,
 	.sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
-	.ops = &iwl5000_ops,
+	.ops = &iwl5150_ops,
 	.eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
 	.eeprom_ver = EEPROM_5050_EEPROM_VERSION,
 	.eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION,
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
index 4c88e87..23a58b0 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
@@ -270,6 +270,8 @@
   {"60", "64QAM 5/6"}
 };
 
+#define MCS_INDEX_PER_STREAM	(8)
+
 static inline u8 rs_extract_rate(u32 rate_n_flags)
 {
 	return (u8)(rate_n_flags & 0xFF);
@@ -652,19 +654,19 @@
 	return 1;
 }
 
-/* FIXME:RS: in 4965 we don't use greenfield at all */
-/* FIXME:RS: don't use greenfield for now in TX */
-#if 0
-static inline u8 rs_use_green(struct iwl_priv *priv, struct ieee80211_conf *conf)
+/* in 4965 we don't use greenfield at all */
+static inline u8 rs_use_green(struct iwl_priv *priv,
+			      struct ieee80211_conf *conf)
 {
-	return (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) &&
-		priv->current_ht_config.is_green_field &&
-		!priv->current_ht_config.non_GF_STA_present;
-}
-#endif
-static inline u8 rs_use_green(struct iwl_priv *priv, struct ieee80211_conf *conf)
-{
-	return 0;
+	u8 is_green;
+
+	if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965)
+		is_green = 0;
+	else
+		is_green = (conf_is_ht(conf) &&
+			   priv->current_ht_config.is_green_field &&
+			   !priv->current_ht_config.non_GF_STA_present);
+	return is_green;
 }
 
 /**
@@ -2061,6 +2063,10 @@
 		active_tbl = 1 - lq_sta->active_tbl;
 
 	tbl = &(lq_sta->lq_info[active_tbl]);
+	if (is_legacy(tbl->lq_type))
+		lq_sta->is_green = 0;
+	else
+		lq_sta->is_green = rs_use_green(priv, conf);
 	is_green = lq_sta->is_green;
 
 	/* current tx rate */
@@ -2514,12 +2520,33 @@
 		}
 	}
 
-	if (rate_idx < 0 || rate_idx > IWL_RATE_COUNT)
-		rate_idx = rate_lowest_index(sband, sta);
-	else if (sband->band == IEEE80211_BAND_5GHZ)
+	if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) {
 		rate_idx -= IWL_FIRST_OFDM_RATE;
-
+		/* 6M and 9M shared same MCS index */
+		rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0;
+		if (rs_extract_rate(lq_sta->last_rate_n_flags) >=
+		    IWL_RATE_MIMO3_6M_PLCP)
+			rate_idx = rate_idx + (2 * MCS_INDEX_PER_STREAM);
+		else if (rs_extract_rate(lq_sta->last_rate_n_flags) >=
+			 IWL_RATE_MIMO2_6M_PLCP)
+			rate_idx = rate_idx + MCS_INDEX_PER_STREAM;
+		info->control.rates[0].flags = IEEE80211_TX_RC_MCS;
+		if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK)
+			info->control.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI;
+		if (lq_sta->last_rate_n_flags & RATE_MCS_DUP_MSK)
+			info->control.rates[0].flags |= IEEE80211_TX_RC_DUP_DATA;
+		if (lq_sta->last_rate_n_flags & RATE_MCS_FAT_MSK)
+			info->control.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+		if (lq_sta->last_rate_n_flags & RATE_MCS_GF_MSK)
+			info->control.rates[0].flags |= IEEE80211_TX_RC_GREEN_FIELD;
+	} else {
+		if (rate_idx < 0 || rate_idx > IWL_RATE_COUNT)
+			rate_idx = rate_lowest_index(sband, sta);
+		else if (sband->band == IEEE80211_BAND_5GHZ)
+			rate_idx -= IWL_FIRST_OFDM_RATE;
+	}
 	info->control.rates[0].idx = rate_idx;
+
 }
 
 static void *rs_alloc_sta(void *priv_rate, struct ieee80211_sta *sta,
@@ -2896,7 +2923,8 @@
 		   ((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3"));
 		   desc += sprintf(buff+desc, " %s",
 		   (tbl->is_fat) ? "40MHz" : "20MHz");
-		desc += sprintf(buff+desc, " %s\n", (tbl->is_SGI) ? "SGI" : "");
+		   desc += sprintf(buff+desc, " %s %s\n", (tbl->is_SGI) ? "SGI" : "",
+		   (lq_sta->is_green) ? "GF enabled" : "");
 	}
 	desc += sprintf(buff+desc, "last tx rate=0x%X\n",
 		lq_sta->last_rate_n_flags);
@@ -2959,13 +2987,14 @@
 		return -ENOMEM;
 
 	for (i = 0; i < LQ_SIZE; i++) {
-		desc += sprintf(buff+desc, "%s type=%d SGI=%d FAT=%d DUP=%d\n"
+		desc += sprintf(buff+desc, "%s type=%d SGI=%d FAT=%d DUP=%d GF=%d\n"
 				"rate=0x%X\n",
 				lq_sta->active_tbl == i ? "*" : "x",
 				lq_sta->lq_info[i].lq_type,
 				lq_sta->lq_info[i].is_SGI,
 				lq_sta->lq_info[i].is_fat,
 				lq_sta->lq_info[i].is_dup,
+				lq_sta->is_green,
 				lq_sta->lq_info[i].current_rate);
 		for (j = 0; j < IWL_RATE_COUNT; j++) {
 			desc += sprintf(buff+desc,
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 596977d..0a5507c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -503,24 +503,12 @@
 int iwl_hw_tx_queue_init(struct iwl_priv *priv,
 			 struct iwl_tx_queue *txq)
 {
-	int ret;
-	unsigned long flags;
 	int txq_id = txq->q.id;
 
-	spin_lock_irqsave(&priv->lock, flags);
-	ret = iwl_grab_nic_access(priv);
-	if (ret) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		return ret;
-	}
-
 	/* Circular buffer (TFD queue in DRAM) physical base address */
 	iwl_write_direct32(priv, FH_MEM_CBBC_QUEUE(txq_id),
 			     txq->q.dma_addr >> 8);
 
-	iwl_release_nic_access(priv);
-	spin_unlock_irqrestore(&priv->lock, flags);
-
 	return 0;
 }
 
@@ -709,6 +697,7 @@
 	struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
 	u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags);
 	unsigned long status = priv->status;
+	unsigned long reg_flags;
 
 	IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s\n",
 			  (flags & HW_CARD_DISABLED) ? "Kill" : "On",
@@ -720,32 +709,25 @@
 		iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
 			    CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
 
-		if (!iwl_grab_nic_access(priv)) {
-			iwl_write_direct32(
-				priv, HBUS_TARG_MBX_C,
-				HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
-
-			iwl_release_nic_access(priv);
-		}
+		iwl_write_direct32(priv, HBUS_TARG_MBX_C,
+					HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
 
 		if (!(flags & RXON_CARD_DISABLED)) {
 			iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
 				    CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
-			if (!iwl_grab_nic_access(priv)) {
-				iwl_write_direct32(
-					priv, HBUS_TARG_MBX_C,
+			iwl_write_direct32(priv, HBUS_TARG_MBX_C,
 					HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
 
-				iwl_release_nic_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);
+			spin_lock_irqsave(&priv->reg_lock, reg_flags);
 			if (!iwl_grab_nic_access(priv))
 				iwl_release_nic_access(priv);
+			spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
 		}
 	}
 
@@ -774,14 +756,6 @@
 
 int iwl_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src)
 {
-	int ret;
-	unsigned long flags;
-
-	spin_lock_irqsave(&priv->lock, flags);
-	ret = iwl_grab_nic_access(priv);
-	if (ret)
-		goto err;
-
 	if (src == IWL_PWR_SRC_VAUX) {
 		if (pci_pme_capable(priv->pci_dev, PCI_D3cold))
 			iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG,
@@ -793,10 +767,7 @@
 				       ~APMG_PS_CTRL_MSK_PWR_SRC);
 	}
 
-	iwl_release_nic_access(priv);
-err:
-	spin_unlock_irqrestore(&priv->lock, flags);
-	return ret;
+	return 0;
 }
 
 /**
@@ -860,6 +831,7 @@
 	unsigned long flags;
 	u8 fill_rx = 0;
 	u32 count = 8;
+	int total_empty;
 
 	/* uCode's read index (stored in shared DRAM) indicates the last Rx
 	 * buffer that the driver may process (last buffer filled by ucode). */
@@ -870,7 +842,12 @@
 	if (i == r)
 		IWL_DEBUG_RX(priv, "r = %d, i = %d\n", r, i);
 
-	if (iwl_rx_queue_space(rxq) > (RX_QUEUE_SIZE / 2))
+	/* calculate total frames need to be restock after handling RX */
+	total_empty = r - priv->rxq.write_actual;
+	if (total_empty < 0)
+		total_empty += RX_QUEUE_SIZE;
+
+	if (total_empty > (RX_QUEUE_SIZE / 2))
 		fill_rx = 1;
 
 	while (i != r) {
@@ -947,7 +924,7 @@
 			count++;
 			if (count >= 8) {
 				priv->rxq.read = i;
-				iwl_rx_queue_restock(priv);
+				iwl_rx_replenish_now(priv);
 				count = 0;
 			}
 		}
@@ -955,7 +932,10 @@
 
 	/* Backtrack one entry */
 	priv->rxq.read = i;
-	iwl_rx_queue_restock(priv);
+	if (fill_rx)
+		iwl_rx_replenish_now(priv);
+	else
+		iwl_rx_queue_restock(priv);
 }
 
 /* call this function to flush any scheduled tasklet */
@@ -966,7 +946,7 @@
 	tasklet_kill(&priv->irq_tasklet);
 }
 
-static void iwl_irq_tasklet(struct iwl_priv *priv)
+static void iwl_irq_tasklet_legacy(struct iwl_priv *priv)
 {
 	u32 inta, handled = 0;
 	u32 inta_fh;
@@ -1127,9 +1107,9 @@
 		priv->isr_stats.unhandled++;
 	}
 
-	if (inta & ~CSR_INI_SET_MASK) {
+	if (inta & ~(priv->inta_mask)) {
 		IWL_WARN(priv, "Disabled INTA bits 0x%08x were pending\n",
-			 inta & ~CSR_INI_SET_MASK);
+			 inta & ~priv->inta_mask);
 		IWL_WARN(priv, "   with FH_INT = 0x%08x\n", inta_fh);
 	}
 
@@ -1150,6 +1130,199 @@
 	spin_unlock_irqrestore(&priv->lock, flags);
 }
 
+/* tasklet for iwlagn interrupt */
+static void iwl_irq_tasklet(struct iwl_priv *priv)
+{
+	u32 inta = 0;
+	u32 handled = 0;
+	unsigned long flags;
+#ifdef CONFIG_IWLWIFI_DEBUG
+	u32 inta_mask;
+#endif
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	/* Ack/clear/reset pending uCode interrupts.
+	 * Note:  Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS,
+	 */
+	iwl_write32(priv, CSR_INT, priv->inta);
+
+	inta = priv->inta;
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+	if (priv->debug_level & IWL_DL_ISR) {
+		/* just for debug */
+		inta_mask = iwl_read32(priv, CSR_INT_MASK);
+		IWL_DEBUG_ISR(priv, "inta 0x%08x, enabled 0x%08x\n ",
+				inta, inta_mask);
+	}
+#endif
+	/* saved interrupt in inta variable now we can reset priv->inta */
+	priv->inta = 0;
+
+	/* Now service all interrupt bits discovered above. */
+	if (inta & CSR_INT_BIT_HW_ERR) {
+		IWL_ERR(priv, "Microcode HW error detected.  Restarting.\n");
+
+		/* Tell the device to stop sending interrupts */
+		iwl_disable_interrupts(priv);
+
+		priv->isr_stats.hw++;
+		iwl_irq_handle_error(priv);
+
+		handled |= CSR_INT_BIT_HW_ERR;
+
+		spin_unlock_irqrestore(&priv->lock, flags);
+
+		return;
+	}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+	if (priv->debug_level & (IWL_DL_ISR)) {
+		/* NIC fires this, but we don't use it, redundant with WAKEUP */
+		if (inta & CSR_INT_BIT_SCD) {
+			IWL_DEBUG_ISR(priv, "Scheduler finished to transmit "
+				      "the frame/frames.\n");
+			priv->isr_stats.sch++;
+		}
+
+		/* Alive notification via Rx interrupt will do the real work */
+		if (inta & CSR_INT_BIT_ALIVE) {
+			IWL_DEBUG_ISR(priv, "Alive interrupt\n");
+			priv->isr_stats.alive++;
+		}
+	}
+#endif
+	/* Safely ignore these bits for debug checks below */
+	inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE);
+
+	/* HW RF KILL switch toggled */
+	if (inta & CSR_INT_BIT_RF_KILL) {
+		int hw_rf_kill = 0;
+		if (!(iwl_read32(priv, CSR_GP_CNTRL) &
+				CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW))
+			hw_rf_kill = 1;
+
+		IWL_DEBUG_RF_KILL(priv, "RF_KILL bit toggled to %s.\n",
+				hw_rf_kill ? "disable radio" : "enable radio");
+
+		priv->isr_stats.rfkill++;
+
+		/* driver only loads ucode once setting the interface up.
+		 * the driver allows loading the ucode even if the radio
+		 * is killed. Hence update the killswitch state here. The
+		 * rfkill handler will care about restarting if needed.
+		 */
+		if (!test_bit(STATUS_ALIVE, &priv->status)) {
+			if (hw_rf_kill)
+				set_bit(STATUS_RF_KILL_HW, &priv->status);
+			else
+				clear_bit(STATUS_RF_KILL_HW, &priv->status);
+			queue_work(priv->workqueue, &priv->rf_kill);
+		}
+
+		handled |= CSR_INT_BIT_RF_KILL;
+	}
+
+	/* Chip got too hot and stopped itself */
+	if (inta & CSR_INT_BIT_CT_KILL) {
+		IWL_ERR(priv, "Microcode CT kill error detected.\n");
+		priv->isr_stats.ctkill++;
+		handled |= CSR_INT_BIT_CT_KILL;
+	}
+
+	/* Error detected by uCode */
+	if (inta & CSR_INT_BIT_SW_ERR) {
+		IWL_ERR(priv, "Microcode SW error detected. "
+			" Restarting 0x%X.\n", inta);
+		priv->isr_stats.sw++;
+		priv->isr_stats.sw_err = inta;
+		iwl_irq_handle_error(priv);
+		handled |= CSR_INT_BIT_SW_ERR;
+	}
+
+	/* uCode wakes up after power-down sleep */
+	if (inta & CSR_INT_BIT_WAKEUP) {
+		IWL_DEBUG_ISR(priv, "Wakeup interrupt\n");
+		iwl_rx_queue_update_write_ptr(priv, &priv->rxq);
+		iwl_txq_update_write_ptr(priv, &priv->txq[0]);
+		iwl_txq_update_write_ptr(priv, &priv->txq[1]);
+		iwl_txq_update_write_ptr(priv, &priv->txq[2]);
+		iwl_txq_update_write_ptr(priv, &priv->txq[3]);
+		iwl_txq_update_write_ptr(priv, &priv->txq[4]);
+		iwl_txq_update_write_ptr(priv, &priv->txq[5]);
+
+		priv->isr_stats.wakeup++;
+
+		handled |= CSR_INT_BIT_WAKEUP;
+	}
+
+	/* All uCode command responses, including Tx command responses,
+	 * Rx "responses" (frame-received notification), and other
+	 * notifications from uCode come through here*/
+	if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX |
+			CSR_INT_BIT_RX_PERIODIC)) {
+		IWL_DEBUG_ISR(priv, "Rx interrupt\n");
+		if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) {
+			handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX);
+			iwl_write32(priv, CSR_FH_INT_STATUS,
+					CSR49_FH_INT_RX_MASK);
+		}
+		if (inta & CSR_INT_BIT_RX_PERIODIC) {
+			handled |= CSR_INT_BIT_RX_PERIODIC;
+			iwl_write32(priv, CSR_INT, CSR_INT_BIT_RX_PERIODIC);
+		}
+		/* Sending RX interrupt require many steps to be done in the
+		 * the device:
+		 * 1- write interrupt to current index in ICT table.
+		 * 2- dma RX frame.
+		 * 3- update RX shared data to indicate last write index.
+		 * 4- send interrupt.
+		 * This could lead to RX race, driver could receive RX interrupt
+		 * but the shared data changes does not reflect this.
+		 * this could lead to RX race, RX periodic will solve this race
+		 */
+		iwl_write32(priv, CSR_INT_PERIODIC_REG,
+			    CSR_INT_PERIODIC_DIS);
+		iwl_rx_handle(priv);
+		/* Only set RX periodic if real RX is received. */
+		if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX))
+			iwl_write32(priv, CSR_INT_PERIODIC_REG,
+				    CSR_INT_PERIODIC_ENA);
+
+		priv->isr_stats.rx++;
+	}
+
+	if (inta & CSR_INT_BIT_FH_TX) {
+		iwl_write32(priv, CSR_FH_INT_STATUS, CSR49_FH_INT_TX_MASK);
+		IWL_DEBUG_ISR(priv, "Tx interrupt\n");
+		priv->isr_stats.tx++;
+		handled |= CSR_INT_BIT_FH_TX;
+		/* FH finished to write, send event */
+		priv->ucode_write_complete = 1;
+		wake_up_interruptible(&priv->wait_command_queue);
+	}
+
+	if (inta & ~handled) {
+		IWL_ERR(priv, "Unhandled INTA bits 0x%08x\n", inta & ~handled);
+		priv->isr_stats.unhandled++;
+	}
+
+	if (inta & ~(priv->inta_mask)) {
+		IWL_WARN(priv, "Disabled INTA bits 0x%08x were pending\n",
+			 inta & ~priv->inta_mask);
+	}
+
+
+	/* Re-enable all interrupts */
+	/* only Re-enable if diabled by irq */
+	if (test_bit(STATUS_INT_ENABLED, &priv->status))
+		iwl_enable_interrupts(priv);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+}
+
 
 /******************************************************************************
  *
@@ -1579,6 +1752,8 @@
 		       test_bit(STATUS_EXIT_PENDING, &priv->status) <<
 				STATUS_EXIT_PENDING;
 
+	/* device going down, Stop using ICT table */
+	iwl_disable_ict(priv);
 	spin_lock_irqsave(&priv->lock, flags);
 	iwl_clear_bit(priv, CSR_GP_CNTRL,
 			 CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
@@ -1587,13 +1762,8 @@
 	iwl_txq_ctx_stop(priv);
 	iwl_rxq_stop(priv);
 
-	spin_lock_irqsave(&priv->lock, flags);
-	if (!iwl_grab_nic_access(priv)) {
-		iwl_write_prph(priv, APMG_CLK_DIS_REG,
-					 APMG_CLK_VAL_DMA_CLK_RQT);
-		iwl_release_nic_access(priv);
-	}
-	spin_unlock_irqrestore(&priv->lock, flags);
+	iwl_write_prph(priv, APMG_CLK_DIS_REG,
+				APMG_CLK_VAL_DMA_CLK_RQT);
 
 	udelay(5);
 
@@ -1622,6 +1792,49 @@
 	iwl_cancel_deferred_work(priv);
 }
 
+#define HW_READY_TIMEOUT (50)
+
+static int iwl_set_hw_ready(struct iwl_priv *priv)
+{
+	int ret = 0;
+
+	iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+		CSR_HW_IF_CONFIG_REG_BIT_NIC_READY);
+
+	/* See if we got it */
+	ret = iwl_poll_bit(priv, CSR_HW_IF_CONFIG_REG,
+				CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
+				CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
+				HW_READY_TIMEOUT);
+	if (ret != -ETIMEDOUT)
+		priv->hw_ready = true;
+	else
+		priv->hw_ready = false;
+
+	IWL_DEBUG_INFO(priv, "hardware %s\n",
+		      (priv->hw_ready == 1) ? "ready" : "not ready");
+	return ret;
+}
+
+static int iwl_prepare_card_hw(struct iwl_priv *priv)
+{
+	int ret = 0;
+
+	IWL_DEBUG_INFO(priv, "iwl_prepare_card_hw enter \n");
+
+	iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+			CSR_HW_IF_CONFIG_REG_PREPARE);
+
+	ret = iwl_poll_bit(priv, CSR_HW_IF_CONFIG_REG,
+			~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE,
+			CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000);
+
+	if (ret != -ETIMEDOUT)
+		iwl_set_hw_ready(priv);
+
+	return ret;
+}
+
 #define MAX_HW_RESTARTS 5
 
 static int __iwl_up(struct iwl_priv *priv)
@@ -1639,6 +1852,13 @@
 		return -EIO;
 	}
 
+	iwl_prepare_card_hw(priv);
+
+	if (!priv->hw_ready) {
+		IWL_WARN(priv, "Exit HW not ready\n");
+		return -EIO;
+	}
+
 	/* If platform's RF_KILL switch is NOT set to KILL */
 	if (iwl_read32(priv, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)
 		clear_bit(STATUS_RF_KILL_HW, &priv->status);
@@ -1667,6 +1887,8 @@
 
 	/* clear (again), then enable host interrupts */
 	iwl_write32(priv, CSR_INT, 0xFFFFFFFF);
+	/* enable dram interrupt */
+	iwl_reset_ict(priv);
 	iwl_enable_interrupts(priv);
 
 	/* really make sure rfkill handshake bits are cleared */
@@ -2298,8 +2520,10 @@
 
 	if (priv->eeprom) {
 		eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION);
-		pos += sprintf(buf + pos, "EEPROM version: 0x%x\n",
-				 eeprom_ver);
+		pos += sprintf(buf + pos, "NVM Type: %s, version: 0x%x\n",
+			       (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP)
+			       ? "OTP" : "EEPROM", eeprom_ver);
+
 	} else {
 		pos += sprintf(buf + pos, "EEPROM not initialzed\n");
 	}
@@ -2478,7 +2702,7 @@
 static ssize_t show_qos(struct device *d,
 				struct device_attribute *attr, char *buf)
 {
-	struct iwl_priv *priv = (struct iwl_priv *)d->driver_data;
+	struct iwl_priv *priv = dev_get_drvdata(d);
 	char *p = buf;
 	int   q;
 
@@ -2565,8 +2789,12 @@
 	priv->statistics_periodic.data = (unsigned long)priv;
 	priv->statistics_periodic.function = iwl_bg_statistics_periodic;
 
-	tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
-		     iwl_irq_tasklet, (unsigned long)priv);
+	if (!priv->cfg->use_isr_legacy)
+		tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
+			iwl_irq_tasklet, (unsigned long)priv);
+	else
+		tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
+			iwl_irq_tasklet_legacy, (unsigned long)priv);
 }
 
 static void iwl_cancel_deferred_work(struct iwl_priv *priv)
@@ -2655,6 +2883,7 @@
 	IWL_DEBUG_INFO(priv, "*** LOAD DRIVER ***\n");
 	priv->cfg = cfg;
 	priv->pci_dev = pdev;
+	priv->inta_mask = CSR_INI_SET_MASK;
 
 #ifdef CONFIG_IWLWIFI_DEBUG
 	priv->debug_level = priv->cfg->mod_params->debug;
@@ -2705,6 +2934,10 @@
 		(unsigned long long) pci_resource_len(pdev, 0));
 	IWL_DEBUG_INFO(priv, "pci_resource_base = %p\n", priv->hw_base);
 
+	/* this spin lock will be used in apm_ops.init and EEPROM access
+	 * we should init now
+	 */
+	spin_lock_init(&priv->reg_lock);
 	iwl_hw_detect(priv);
 	IWL_INFO(priv, "Detected Intel Wireless WiFi Link %s REV=0x%X\n",
 		priv->cfg->name, priv->hw_rev);
@@ -2713,6 +2946,12 @@
 	 * PCI Tx retries from interfering with C3 CPU state */
 	pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
 
+	iwl_prepare_card_hw(priv);
+	if (!priv->hw_ready) {
+		IWL_WARN(priv, "Failed, HW not ready\n");
+		goto out_iounmap;
+	}
+
 	/* amp init */
 	err = priv->cfg->ops->lib->apm_ops.init(priv);
 	if (err < 0) {
@@ -2763,8 +3002,9 @@
 
 	pci_enable_msi(priv->pci_dev);
 
-	err = request_irq(priv->pci_dev->irq, iwl_isr, IRQF_SHARED,
-			  DRV_NAME, priv);
+	iwl_alloc_isr_ict(priv);
+	err = request_irq(priv->pci_dev->irq, priv->cfg->ops->lib->isr,
+			  IRQF_SHARED, DRV_NAME, priv);
 	if (err) {
 		IWL_ERR(priv, "Error allocating IRQ %d\n", priv->pci_dev->irq);
 		goto out_disable_msi;
@@ -2821,6 +3061,7 @@
 	sysfs_remove_group(&pdev->dev.kobj, &iwl_attribute_group);
  out_free_irq:
 	free_irq(priv->pci_dev->irq, priv);
+	iwl_free_isr_ict(priv);
  out_disable_msi:
 	pci_disable_msi(priv->pci_dev);
 	iwl_uninit_drv(priv);
@@ -2902,6 +3143,8 @@
 
 	iwl_uninit_drv(priv);
 
+	iwl_free_isr_ict(priv);
+
 	if (priv->ibss_beacon)
 		dev_kfree_skb(priv->ibss_beacon);
 
diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h
index 7b84d52..e581dc3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-commands.h
+++ b/drivers/net/wireless/iwlwifi/iwl-commands.h
@@ -614,8 +614,18 @@
 
 #define RXON_FLG_CHANNEL_MODE_POS		(25)
 #define RXON_FLG_CHANNEL_MODE_MSK		cpu_to_le32(0x3 << 25)
-#define RXON_FLG_CHANNEL_MODE_PURE_40_MSK	cpu_to_le32(0x1 << 25)
-#define RXON_FLG_CHANNEL_MODE_MIXED_MSK		cpu_to_le32(0x2 << 25)
+
+/* channel mode */
+enum {
+	CHANNEL_MODE_LEGACY = 0,
+	CHANNEL_MODE_PURE_40 = 1,
+	CHANNEL_MODE_MIXED = 2,
+	CHANNEL_MODE_RESERVED = 3,
+};
+#define RXON_FLG_CHANNEL_MODE_LEGACY	cpu_to_le32(CHANNEL_MODE_LEGACY << RXON_FLG_CHANNEL_MODE_POS)
+#define RXON_FLG_CHANNEL_MODE_PURE_40	cpu_to_le32(CHANNEL_MODE_PURE_40 << RXON_FLG_CHANNEL_MODE_POS)
+#define RXON_FLG_CHANNEL_MODE_MIXED	cpu_to_le32(CHANNEL_MODE_MIXED << RXON_FLG_CHANNEL_MODE_POS)
+
 /* CTS to self (if spec allows) flag */
 #define RXON_FLG_SELF_CTS_EN			cpu_to_le32(0x1<<30)
 
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
index 5393fb3f..e93ddb7 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -39,6 +39,7 @@
 #include "iwl-rfkill.h"
 #include "iwl-power.h"
 #include "iwl-sta.h"
+#include "iwl-helpers.h"
 
 
 MODULE_DESCRIPTION("iwl core");
@@ -59,6 +60,8 @@
 				    IWL_RATE_##pp##M_INDEX,    \
 				    IWL_RATE_##np##M_INDEX }
 
+static irqreturn_t iwl_isr(int irq, void *data);
+
 /*
  * Parameter order:
  *   rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate
@@ -603,10 +606,10 @@
 
 	if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE)
 		return !(ch_info->fat_extension_channel &
-					IEEE80211_CHAN_NO_FAT_ABOVE);
+					IEEE80211_CHAN_NO_HT40PLUS);
 	else if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW)
 		return !(ch_info->fat_extension_channel &
-					IEEE80211_CHAN_NO_FAT_BELOW);
+					IEEE80211_CHAN_NO_HT40MINUS);
 
 	return 0;
 }
@@ -617,19 +620,23 @@
 	struct iwl_ht_info *iwl_ht_conf = &priv->current_ht_config;
 
 	if ((!iwl_ht_conf->is_ht) ||
-	   (iwl_ht_conf->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ) ||
-	   (iwl_ht_conf->extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_NONE))
+	    (iwl_ht_conf->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ))
 		return 0;
 
+	/* We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40
+	 * the bit will not set if it is pure 40MHz case
+	 */
 	if (sta_ht_inf) {
-		if ((!sta_ht_inf->ht_supported) ||
-		   (!(sta_ht_inf->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)))
+		if (!sta_ht_inf->ht_supported)
 			return 0;
 	}
 
-	return iwl_is_channel_extension(priv, priv->band,
-					le16_to_cpu(priv->staging_rxon.channel),
-					iwl_ht_conf->extension_chan_offset);
+	if (iwl_ht_conf->ht_protection & IEEE80211_HT_OP_MODE_PROTECTION_20MHZ)
+		return 1;
+	else
+		return iwl_is_channel_extension(priv, priv->band,
+				le16_to_cpu(priv->staging_rxon.channel),
+				iwl_ht_conf->extension_chan_offset);
 }
 EXPORT_SYMBOL(iwl_is_fat_tx_allowed);
 
@@ -799,42 +806,51 @@
 void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info)
 {
 	struct iwl_rxon_cmd *rxon = &priv->staging_rxon;
-	u32 val;
 
 	if (!ht_info->is_ht) {
-		rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED_MSK |
-			RXON_FLG_CHANNEL_MODE_PURE_40_MSK |
+		rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK |
 			RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK |
 			RXON_FLG_FAT_PROT_MSK |
 			RXON_FLG_HT_PROT_MSK);
 		return;
 	}
 
-	/* Set up channel bandwidth:  20 MHz only, or 20/40 mixed if fat ok */
-	if (iwl_is_fat_tx_allowed(priv, NULL))
-		rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED_MSK;
-	else
-		rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED_MSK |
-				 RXON_FLG_CHANNEL_MODE_PURE_40_MSK);
+	/* FIXME: if the definition of ht_protection changed, the "translation"
+	 * will be needed for rxon->flags
+	 */
+	rxon->flags |= cpu_to_le32(ht_info->ht_protection << RXON_FLG_HT_OPERATING_MODE_POS);
 
-	/* Note: control channel is opposite of extension channel */
-	switch (ht_info->extension_chan_offset) {
-	case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
-		rxon->flags &= ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
-		break;
-	case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
-		rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
-		break;
-	case IEEE80211_HT_PARAM_CHA_SEC_NONE:
-	default:
-		rxon->flags &= ~RXON_FLG_CHANNEL_MODE_MIXED_MSK;
-		break;
+	/* Set up channel bandwidth:
+	 * 20 MHz only, 20/40 mixed or pure 40 if fat ok */
+	/* clear the HT channel mode before set the mode */
+	rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK |
+			 RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
+	if (iwl_is_fat_tx_allowed(priv, NULL)) {
+		/* pure 40 fat */
+		if (rxon->flags & RXON_FLG_FAT_PROT_MSK)
+			rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40;
+		else {
+			/* Note: control channel is opposite of extension channel */
+			switch (ht_info->extension_chan_offset) {
+			case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+				rxon->flags &= ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
+				rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
+				break;
+			case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+				rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
+				rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
+				break;
+			case IEEE80211_HT_PARAM_CHA_SEC_NONE:
+			default:
+				/* channel location only valid if in Mixed mode */
+				IWL_ERR(priv, "invalid extension channel offset\n");
+				break;
+			}
+		}
+	} else {
+		rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY;
 	}
 
-	val = ht_info->ht_protection;
-
-	rxon->flags |= cpu_to_le32(val << RXON_FLG_HT_OPERATING_MODE_POS);
-
 	if (priv->cfg->ops->hcmd->set_rxon_chain)
 		priv->cfg->ops->hcmd->set_rxon_chain(priv);
 
@@ -1122,8 +1138,9 @@
 	priv->staging_rxon.cck_basic_rates =
 	    (IWL_CCK_RATES_MASK >> IWL_FIRST_CCK_RATE) & 0xF;
 
-	priv->staging_rxon.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED_MSK |
-					RXON_FLG_CHANNEL_MODE_PURE_40_MSK);
+	/* clear both MIX and PURE40 mode flag */
+	priv->staging_rxon.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED |
+					RXON_FLG_CHANNEL_MODE_PURE_40);
 	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;
@@ -1483,11 +1500,272 @@
 {
 	IWL_DEBUG_ISR(priv, "Enabling interrupts\n");
 	set_bit(STATUS_INT_ENABLED, &priv->status);
-	iwl_write32(priv, CSR_INT_MASK, CSR_INI_SET_MASK);
+	iwl_write32(priv, CSR_INT_MASK, priv->inta_mask);
 }
 EXPORT_SYMBOL(iwl_enable_interrupts);
 
-irqreturn_t iwl_isr(int irq, void *data)
+
+#define ICT_COUNT (PAGE_SIZE/sizeof(u32))
+
+/* Free dram table */
+void iwl_free_isr_ict(struct iwl_priv *priv)
+{
+	if (priv->ict_tbl_vir) {
+		pci_free_consistent(priv->pci_dev, (sizeof(u32) * ICT_COUNT) +
+					PAGE_SIZE, priv->ict_tbl_vir,
+					priv->ict_tbl_dma);
+		priv->ict_tbl_vir = NULL;
+	}
+}
+EXPORT_SYMBOL(iwl_free_isr_ict);
+
+
+/* allocate dram shared table it is a PAGE_SIZE aligned
+ * also reset all data related to ICT table interrupt.
+ */
+int iwl_alloc_isr_ict(struct iwl_priv *priv)
+{
+
+	if (priv->cfg->use_isr_legacy)
+		return 0;
+	/* allocate shrared data table */
+	priv->ict_tbl_vir = pci_alloc_consistent(priv->pci_dev, (sizeof(u32) *
+						  ICT_COUNT) + PAGE_SIZE,
+						  &priv->ict_tbl_dma);
+	if (!priv->ict_tbl_vir)
+		return -ENOMEM;
+
+	/* align table to PAGE_SIZE boundry */
+	priv->aligned_ict_tbl_dma = ALIGN(priv->ict_tbl_dma, PAGE_SIZE);
+
+	IWL_DEBUG_ISR(priv, "ict dma addr %Lx dma aligned %Lx diff %d\n",
+			     (unsigned long long)priv->ict_tbl_dma,
+			     (unsigned long long)priv->aligned_ict_tbl_dma,
+			(int)(priv->aligned_ict_tbl_dma - priv->ict_tbl_dma));
+
+	priv->ict_tbl =  priv->ict_tbl_vir +
+			  (priv->aligned_ict_tbl_dma - priv->ict_tbl_dma);
+
+	IWL_DEBUG_ISR(priv, "ict vir addr %p vir aligned %p diff %d\n",
+			     priv->ict_tbl, priv->ict_tbl_vir,
+			(int)(priv->aligned_ict_tbl_dma - priv->ict_tbl_dma));
+
+	/* reset table and index to all 0 */
+	memset(priv->ict_tbl_vir,0, (sizeof(u32) * ICT_COUNT) + PAGE_SIZE);
+	priv->ict_index = 0;
+
+	/* add periodic RX interrupt */
+	priv->inta_mask |= CSR_INT_BIT_RX_PERIODIC;
+	return 0;
+}
+EXPORT_SYMBOL(iwl_alloc_isr_ict);
+
+/* Device is going up inform it about using ICT interrupt table,
+ * also we need to tell the driver to start using ICT interrupt.
+ */
+int iwl_reset_ict(struct iwl_priv *priv)
+{
+	u32 val;
+	unsigned long flags;
+
+	if (!priv->ict_tbl_vir)
+		return 0;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	iwl_disable_interrupts(priv);
+
+	memset(&priv->ict_tbl[0],0, sizeof(u32) * ICT_COUNT);
+
+	val = priv->aligned_ict_tbl_dma >> PAGE_SHIFT;
+
+	val |= CSR_DRAM_INT_TBL_ENABLE;
+	val |= CSR_DRAM_INIT_TBL_WRAP_CHECK;
+
+	IWL_DEBUG_ISR(priv, "CSR_DRAM_INT_TBL_REG =0x%X "
+			"aligned dma address %Lx\n",
+			val, (unsigned long long)priv->aligned_ict_tbl_dma);
+
+	iwl_write32(priv, CSR_DRAM_INT_TBL_REG, val);
+	priv->use_ict = true;
+	priv->ict_index = 0;
+	iwl_write32(priv, CSR_INT, priv->inta_mask);
+	iwl_enable_interrupts(priv);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(iwl_reset_ict);
+
+/* Device is going down disable ict interrupt usage */
+void iwl_disable_ict(struct iwl_priv *priv)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->use_ict = false;
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+EXPORT_SYMBOL(iwl_disable_ict);
+
+/* interrupt handler using ict table, with this interrupt driver will
+ * stop using INTA register to get device's interrupt, reading this register
+ * is expensive, device will write interrupts in ICT dram table, increment
+ * index then will fire interrupt to driver, driver will OR all ICT table
+ * entries from current index up to table entry with 0 value. the result is
+ * the interrupt we need to service, driver will set the entries back to 0 and
+ * set index.
+ */
+irqreturn_t iwl_isr_ict(int irq, void *data)
+{
+	struct iwl_priv *priv = data;
+	u32 inta, inta_mask;
+	u32 val = 0;
+
+	if (!priv)
+		return IRQ_NONE;
+
+	/* dram interrupt table not set yet,
+	 * use legacy interrupt.
+	 */
+	if (!priv->use_ict)
+		return iwl_isr(irq, data);
+
+	spin_lock(&priv->lock);
+
+	/* Disable (but don't clear!) interrupts here to avoid
+	 * back-to-back ISRs and sporadic interrupts from our NIC.
+	 * If we have something to service, the tasklet will re-enable ints.
+	 * If we *don't* have something, we'll re-enable before leaving here.
+	 */
+	inta_mask = iwl_read32(priv, CSR_INT_MASK);  /* just for debug */
+	iwl_write32(priv, CSR_INT_MASK, 0x00000000);
+
+
+	/* Ignore interrupt if there's nothing in NIC to service.
+	 * This may be due to IRQ shared with another device,
+	 * or due to sporadic interrupts thrown from our NIC. */
+	if (!priv->ict_tbl[priv->ict_index]) {
+		IWL_DEBUG_ISR(priv, "Ignore interrupt, inta == 0\n");
+		goto none;
+	}
+
+	/* read all entries that not 0 start with ict_index */
+	while (priv->ict_tbl[priv->ict_index]) {
+
+		val |= priv->ict_tbl[priv->ict_index];
+		IWL_DEBUG_ISR(priv, "ICT index %d value 0x%08X\n",
+					priv->ict_index,
+					priv->ict_tbl[priv->ict_index]);
+		priv->ict_tbl[priv->ict_index] = 0;
+		priv->ict_index = iwl_queue_inc_wrap(priv->ict_index,
+								ICT_COUNT);
+
+	}
+
+	/* We should not get this value, just ignore it. */
+	if (val == 0xffffffff)
+		val = 0;
+
+	inta = (0xff & val) | ((0xff00 & val) << 16);
+	IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n",
+			inta, inta_mask, val);
+
+	inta &= priv->inta_mask;
+	priv->inta |= inta;
+
+	/* iwl_irq_tasklet() will service interrupts and re-enable them */
+	if (likely(inta))
+		tasklet_schedule(&priv->irq_tasklet);
+	else if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta) {
+		/* Allow interrupt if was disabled by this handler and
+		 * no tasklet was schedules, We should not enable interrupt,
+		 * tasklet will enable it.
+		 */
+		iwl_enable_interrupts(priv);
+	}
+
+	spin_unlock(&priv->lock);
+	return IRQ_HANDLED;
+
+ none:
+	/* re-enable interrupts here since we don't have anything to service.
+	 * only Re-enable if disabled by irq.
+	 */
+	if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta)
+		iwl_enable_interrupts(priv);
+
+	spin_unlock(&priv->lock);
+	return IRQ_NONE;
+}
+EXPORT_SYMBOL(iwl_isr_ict);
+
+
+static irqreturn_t iwl_isr(int irq, void *data)
+{
+	struct iwl_priv *priv = data;
+	u32 inta, inta_mask;
+	u32 inta_fh;
+
+	if (!priv)
+		return IRQ_NONE;
+
+	spin_lock(&priv->lock);
+
+	/* Disable (but don't clear!) interrupts here to avoid
+	 *    back-to-back ISRs and sporadic interrupts from our NIC.
+	 * If we have something to service, the tasklet will re-enable ints.
+	 * If we *don't* have something, we'll re-enable before leaving here. */
+	inta_mask = iwl_read32(priv, CSR_INT_MASK);  /* just for debug */
+	iwl_write32(priv, CSR_INT_MASK, 0x00000000);
+
+	/* Discover which interrupts are active/pending */
+	inta = iwl_read32(priv, CSR_INT);
+
+	/* Ignore interrupt if there's nothing in NIC to service.
+	 * This may be due to IRQ shared with another device,
+	 * or due to sporadic interrupts thrown from our NIC. */
+	if (!inta) {
+		IWL_DEBUG_ISR(priv, "Ignore interrupt, inta == 0\n");
+		goto none;
+	}
+
+	if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) {
+		/* Hardware disappeared. It might have already raised
+		 * an interrupt */
+		IWL_WARN(priv, "HARDWARE GONE?? INTA == 0x%08x\n", inta);
+		goto unplugged;
+	}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+	if (priv->debug_level & (IWL_DL_ISR)) {
+		inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS);
+		IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x, "
+			      "fh 0x%08x\n", inta, inta_mask, inta_fh);
+	}
+#endif
+
+	priv->inta |= inta;
+	/* iwl_irq_tasklet() will service interrupts and re-enable them */
+	if (likely(inta))
+		tasklet_schedule(&priv->irq_tasklet);
+	else if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta)
+		iwl_enable_interrupts(priv);
+
+ unplugged:
+	spin_unlock(&priv->lock);
+	return IRQ_HANDLED;
+
+ none:
+	/* re-enable interrupts here since we don't have anything to service. */
+	/* only Re-enable if diabled by irq  and no schedules tasklet. */
+	if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta)
+		iwl_enable_interrupts(priv);
+
+	spin_unlock(&priv->lock);
+	return IRQ_NONE;
+}
+
+irqreturn_t iwl_isr_legacy(int irq, void *data)
 {
 	struct iwl_priv *priv = data;
 	u32 inta, inta_mask;
@@ -1544,7 +1822,7 @@
 	spin_unlock(&priv->lock);
 	return IRQ_NONE;
 }
-EXPORT_SYMBOL(iwl_isr);
+EXPORT_SYMBOL(iwl_isr_legacy);
 
 int iwl_send_bt_config(struct iwl_priv *priv)
 {
@@ -1588,10 +1866,6 @@
 
 	IWL_DEBUG_INFO(priv, "ucode inst image size is %u\n", len);
 
-	ret = iwl_grab_nic_access(priv);
-	if (ret)
-		return ret;
-
 	for (i = 0; i < len; i += 100, image += 100/sizeof(u32)) {
 		/* read data comes through single port, auto-incr addr */
 		/* NOTE: Use the debugless read so we don't flood kernel log
@@ -1607,8 +1881,6 @@
 		}
 	}
 
-	iwl_release_nic_access(priv);
-
 	return ret;
 }
 
@@ -1626,10 +1898,6 @@
 
 	IWL_DEBUG_INFO(priv, "ucode inst image size is %u\n", len);
 
-	ret = iwl_grab_nic_access(priv);
-	if (ret)
-		return ret;
-
 	iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR,
 			   IWL49_RTC_INST_LOWER_BOUND);
 
@@ -1650,8 +1918,6 @@
 		}
 	}
 
-	iwl_release_nic_access(priv);
-
 	if (!errcnt)
 		IWL_DEBUG_INFO(priv,
 		    "ucode image in INSTRUCTION memory is good\n");
@@ -1760,7 +2026,6 @@
 	u32 data2, line;
 	u32 desc, time, count, base, data1;
 	u32 blink1, blink2, ilink1, ilink2;
-	int ret;
 
 	if (priv->ucode_type == UCODE_INIT)
 		base = le32_to_cpu(priv->card_alive_init.error_event_table_ptr);
@@ -1772,12 +2037,6 @@
 		return;
 	}
 
-	ret = iwl_grab_nic_access(priv);
-	if (ret) {
-		IWL_WARN(priv, "Can not read from adapter at this time.\n");
-		return;
-	}
-
 	count = iwl_read_targ_mem(priv, base);
 
 	if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
@@ -1804,7 +2063,6 @@
 	IWL_ERR(priv, "0x%05X 0x%05X 0x%05X 0x%05X\n", blink1, blink2,
 		ilink1, ilink2);
 
-	iwl_release_nic_access(priv);
 }
 EXPORT_SYMBOL(iwl_dump_nic_error_log);
 
@@ -1813,7 +2071,6 @@
 /**
  * iwl_print_event_log - Dump error event log to syslog
  *
- * NOTE: Must be called with iwl_grab_nic_access() already obtained!
  */
 static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
 				u32 num_events, u32 mode)
@@ -1859,7 +2116,6 @@
 
 void iwl_dump_nic_event_log(struct iwl_priv *priv)
 {
-	int ret;
 	u32 base;       /* SRAM byte address of event log header */
 	u32 capacity;   /* event log capacity in # entries */
 	u32 mode;       /* 0 - no timestamp, 1 - timestamp recorded */
@@ -1877,12 +2133,6 @@
 		return;
 	}
 
-	ret = iwl_grab_nic_access(priv);
-	if (ret) {
-		IWL_WARN(priv, "Can not read from adapter at this time.\n");
-		return;
-	}
-
 	/* event log header */
 	capacity = iwl_read_targ_mem(priv, base);
 	mode = iwl_read_targ_mem(priv, base + (1 * sizeof(u32)));
@@ -1894,7 +2144,6 @@
 	/* bail out if nothing in log */
 	if (size == 0) {
 		IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n");
-		iwl_release_nic_access(priv);
 		return;
 	}
 
@@ -1909,7 +2158,6 @@
 	/* (then/else) start at top of log */
 	iwl_print_event_log(priv, 0, next_entry, mode);
 
-	iwl_release_nic_access(priv);
 }
 EXPORT_SYMBOL(iwl_dump_nic_event_log);
 
@@ -2016,11 +2264,11 @@
 	/* wake up ucode */
 	msleep(10);
 
-	spin_lock_irqsave(&priv->lock, flags);
 	iwl_read32(priv, CSR_UCODE_DRV_GP1);
+	spin_lock_irqsave(&priv->reg_lock, flags);
 	if (!iwl_grab_nic_access(priv))
 		iwl_release_nic_access(priv);
-	spin_unlock_irqrestore(&priv->lock, flags);
+	spin_unlock_irqrestore(&priv->reg_lock, flags);
 
 	if (test_bit(STATUS_RF_KILL_HW, &priv->status)) {
 		IWL_DEBUG_RF_KILL(priv, "Can not turn radio back on - "
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index f3544ea..87df1b7 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -112,6 +112,19 @@
 			  struct iwl_rx_phy_res *rx_resp);
 };
 
+struct iwl_apm_ops {
+	int (*init)(struct iwl_priv *priv);
+	int (*reset)(struct iwl_priv *priv);
+	void (*stop)(struct iwl_priv *priv);
+	void (*config)(struct iwl_priv *priv);
+	int (*set_pwr_src)(struct iwl_priv *priv, enum iwl_pwr_src src);
+};
+
+struct iwl_temp_ops {
+	void (*temperature)(struct iwl_priv *priv);
+	void (*set_ct_kill)(struct iwl_priv *priv);
+};
+
 struct iwl_lib_ops {
 	/* set hw dependent parameters */
 	int (*set_hw_params)(struct iwl_priv *priv);
@@ -149,23 +162,21 @@
 	int (*is_valid_rtc_data_addr)(u32 addr);
 	/* 1st ucode load */
 	int (*load_ucode)(struct iwl_priv *priv);
-	 /* power management */
-	struct {
-		int (*init)(struct iwl_priv *priv);
-		int (*reset)(struct iwl_priv *priv);
-		void (*stop)(struct iwl_priv *priv);
-		void (*config)(struct iwl_priv *priv);
-		int (*set_pwr_src)(struct iwl_priv *priv, enum iwl_pwr_src src);
-	} apm_ops;
+	/* power management */
+	struct iwl_apm_ops apm_ops;
+
 	/* power */
 	int (*send_tx_power) (struct iwl_priv *priv);
 	void (*update_chain_flags)(struct iwl_priv *priv);
-	void (*temperature) (struct iwl_priv *priv);
 	void (*post_associate) (struct iwl_priv *priv);
 	void (*config_ap) (struct iwl_priv *priv);
+	irqreturn_t (*isr) (int irq, void *data);
 
 	/* eeprom operations (as defined in iwl-eeprom.h) */
 	struct iwl_eeprom_ops eeprom_ops;
+
+	/* temperature */
+	struct iwl_temp_ops temp_ops;
 };
 
 struct iwl_ops {
@@ -229,6 +240,7 @@
 	u8   valid_tx_ant;
 	u8   valid_rx_ant;
 	bool need_pll_cfg;
+	bool use_isr_legacy;
 };
 
 /***************************
@@ -306,10 +318,11 @@
 				  struct iwl_rx_queue *q);
 void iwl_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
 void iwl_rx_replenish(struct iwl_priv *priv);
+void iwl_rx_replenish_now(struct iwl_priv *priv);
 int iwl_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
 int iwl_rx_queue_restock(struct iwl_priv *priv);
 int iwl_rx_queue_space(const struct iwl_rx_queue *q);
-void iwl_rx_allocate(struct iwl_priv *priv);
+void iwl_rx_allocate(struct iwl_priv *priv, gfp_t priority);
 void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb);
 int iwl_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index);
 /* Handlers */
@@ -456,7 +469,13 @@
  *****************************************************/
 void iwl_disable_interrupts(struct iwl_priv *priv);
 void iwl_enable_interrupts(struct iwl_priv *priv);
-irqreturn_t iwl_isr(int irq, void *data);
+irqreturn_t iwl_isr_legacy(int irq, void *data);
+int iwl_reset_ict(struct iwl_priv *priv);
+void iwl_disable_ict(struct iwl_priv *priv);
+int iwl_alloc_isr_ict(struct iwl_priv *priv);
+void iwl_free_isr_ict(struct iwl_priv *priv);
+irqreturn_t iwl_isr_ict(int irq, void *data);
+
 static inline u16 iwl_pcie_link_ctl(struct iwl_priv *priv)
 {
 	int pos;
diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h
index 6e98314..f03dae1 100644
--- a/drivers/net/wireless/iwlwifi/iwl-csr.h
+++ b/drivers/net/wireless/iwlwifi/iwl-csr.h
@@ -89,6 +89,7 @@
 /* EEPROM reads */
 #define CSR_EEPROM_REG          (CSR_BASE+0x02c)
 #define CSR_EEPROM_GP           (CSR_BASE+0x030)
+#define CSR_OTP_GP_REG   	(CSR_BASE+0x034)
 #define CSR_GIO_REG		(CSR_BASE+0x03C)
 #define CSR_GP_UCODE		(CSR_BASE+0x044)
 #define CSR_UCODE_DRV_GP1       (CSR_BASE+0x054)
@@ -96,8 +97,10 @@
 #define CSR_UCODE_DRV_GP1_CLR   (CSR_BASE+0x05c)
 #define CSR_UCODE_DRV_GP2       (CSR_BASE+0x060)
 #define CSR_LED_REG             (CSR_BASE+0x094)
+#define CSR_DRAM_INT_TBL_REG	(CSR_BASE+0x0A0)
 #define CSR_GIO_CHICKEN_BITS    (CSR_BASE+0x100)
 
+#define CSR_INT_PERIODIC_REG	(CSR_BASE+0x005)
 /* Analog phase-lock-loop configuration  */
 #define CSR_ANA_PLL_CFG         (CSR_BASE+0x20c)
 /*
@@ -123,16 +126,18 @@
 
 #define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A		(0x00080000)
 #define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM		(0x00200000)
-#define CSR_HW_IF_CONFIG_REG_BIT_PCI_OWN_SEM		(0x00400000)
-#define CSR_HW_IF_CONFIG_REG_BIT_ME_OWN			(0x02000000)
-#define CSR_HW_IF_CONFIG_REG_BIT_WAKE_ME		(0x08000000)
+#define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY		(0x00400000)
+#define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE	(0x02000000)
+#define CSR_HW_IF_CONFIG_REG_PREPARE			(0x08000000)
 
+#define CSR_INT_PERIODIC_DIS			(0x00)
+#define CSR_INT_PERIODIC_ENA			(0xFF)
 
 /* interrupt flags in INTA, set by uCode or hardware (e.g. dma),
  * acknowledged (reset) by host writing "1" to flagged bits. */
 #define CSR_INT_BIT_FH_RX        (1 << 31) /* Rx DMA, cmd responses, FH_INT[17:16] */
 #define CSR_INT_BIT_HW_ERR       (1 << 29) /* DMA hardware error FH_INT[31] */
-#define CSR_INT_BIT_DNLD         (1 << 28) /* uCode Download */
+#define CSR_INT_BIT_RX_PERIODIC	 (1 << 28) /* Rx periodic */
 #define CSR_INT_BIT_FH_TX        (1 << 27) /* Tx DMA FH_INT[1:0] */
 #define CSR_INT_BIT_SCD          (1 << 26) /* TXQ pointer advanced */
 #define CSR_INT_BIT_SW_ERR       (1 << 25) /* uCode error */
@@ -226,6 +231,10 @@
 #define CSR_EEPROM_GP_VALID_MSK		(0x00000007)
 #define CSR_EEPROM_GP_BAD_SIGNATURE	(0x00000000)
 #define CSR_EEPROM_GP_IF_OWNER_MSK	(0x00000180)
+#define CSR_OTP_GP_REG_DEVICE_SELECT	(0x00010000) /* 0 - EEPROM, 1 - OTP */
+#define CSR_OTP_GP_REG_OTP_ACCESS_MODE	(0x00020000) /* 0 - absolute, 1 - relative */
+#define CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK          (0x00100000) /* bit 20 */
+#define CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK        (0x00200000) /* bit 21 */
 
 /* CSR GIO */
 #define CSR_GIO_REG_VAL_L0S_ENABLED	(0x00000002)
@@ -251,6 +260,11 @@
 
 /* HPET MEM debug */
 #define CSR_DBG_HPET_MEM_REG_VAL	(0xFFFF0000)
+
+/* DRAM INT TABLE */
+#define CSR_DRAM_INT_TBL_ENABLE		(1 << 31)
+#define CSR_DRAM_INIT_TBL_WRAP_CHECK	(1 << 27)
+
 /*=== HBUS (Host-side Bus) ===*/
 #define HBUS_BASE	(0x400)
 /*
diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h
index db06980..2cf014f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debug.h
+++ b/drivers/net/wireless/iwlwifi/iwl-debug.h
@@ -68,7 +68,7 @@
 	struct dentry *dir_rf;
 	struct dir_data_files {
 		struct dentry *file_sram;
-		struct dentry *file_eeprom;
+		struct dentry *file_nvm;
 		struct dentry *file_stations;
 		struct dentry *file_rx_statistics;
 		struct dentry *file_tx_statistics;
diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
index ffc4be3..af70229 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
@@ -172,7 +172,6 @@
 	struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
 	const size_t bufsz = sizeof(buf);
 
-	iwl_grab_nic_access(priv);
 	for (i = priv->dbgfs->sram_len; i > 0; i -= 4) {
 		val = iwl_read_targ_mem(priv, priv->dbgfs->sram_offset + \
 					priv->dbgfs->sram_len - i);
@@ -192,7 +191,6 @@
 		pos += scnprintf(buf + pos, bufsz - pos, "0x%08x ", val);
 	}
 	pos += scnprintf(buf + pos, bufsz - pos, "\n");
-	iwl_release_nic_access(priv);
 
 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 	return ret;
@@ -292,7 +290,7 @@
 	return ret;
 }
 
-static ssize_t iwl_dbgfs_eeprom_read(struct file *file,
+static ssize_t iwl_dbgfs_nvm_read(struct file *file,
 				       char __user *user_buf,
 				       size_t count,
 				       loff_t *ppos)
@@ -306,7 +304,7 @@
 	buf_size = 4 * eeprom_len + 256;
 
 	if (eeprom_len % 16) {
-		IWL_ERR(priv, "EEPROM size is not multiple of 16.\n");
+		IWL_ERR(priv, "NVM size is not multiple of 16.\n");
 		return -ENODATA;
 	}
 
@@ -318,6 +316,13 @@
 	}
 
 	ptr = priv->eeprom;
+	if (!ptr) {
+		IWL_ERR(priv, "Invalid EEPROM/OTP memory\n");
+		return -ENOMEM;
+	}
+	pos += scnprintf(buf + pos, buf_size - pos, "NVM Type: %s\n",
+			(priv->nvm_device_type == NVM_DEVICE_TYPE_OTP)
+			? "OTP" : "EEPROM");
 	for (ofs = 0 ; ofs < eeprom_len ; ofs += 16) {
 		pos += scnprintf(buf + pos, buf_size - pos, "0x%.4x ", ofs);
 		hex_dump_to_buffer(ptr + ofs, 16 , 16, 2, buf + pos,
@@ -375,51 +380,53 @@
 	}
 
 	supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_2GHZ);
-	channels = supp_band->channels;
+	if (supp_band) {
+		channels = supp_band->channels;
 
-	pos += scnprintf(buf + pos, bufsz - pos,
-			"Displaying %d channels in 2.4GHz band 802.11bg):\n",
-			 supp_band->n_channels);
-
-	for (i = 0; i < supp_band->n_channels; i++)
 		pos += scnprintf(buf + pos, bufsz - pos,
-				"%d: %ddBm: BSS%s%s, %s.\n",
-				ieee80211_frequency_to_channel(
-				channels[i].center_freq),
-				channels[i].max_power,
-				channels[i].flags & IEEE80211_CHAN_RADAR ?
-				" (IEEE 802.11h required)" : "",
-				((channels[i].flags & IEEE80211_CHAN_NO_IBSS)
-				|| (channels[i].flags &
-				IEEE80211_CHAN_RADAR)) ? "" :
-				", IBSS",
-				channels[i].flags &
-				IEEE80211_CHAN_PASSIVE_SCAN ?
-				"passive only" : "active/passive");
+				"Displaying %d channels in 2.4GHz band 802.11bg):\n",
+				supp_band->n_channels);
 
+		for (i = 0; i < supp_band->n_channels; i++)
+			pos += scnprintf(buf + pos, bufsz - pos,
+					"%d: %ddBm: BSS%s%s, %s.\n",
+					ieee80211_frequency_to_channel(
+					channels[i].center_freq),
+					channels[i].max_power,
+					channels[i].flags & IEEE80211_CHAN_RADAR ?
+					" (IEEE 802.11h required)" : "",
+					((channels[i].flags & IEEE80211_CHAN_NO_IBSS)
+					|| (channels[i].flags &
+					IEEE80211_CHAN_RADAR)) ? "" :
+					", IBSS",
+					channels[i].flags &
+					IEEE80211_CHAN_PASSIVE_SCAN ?
+					"passive only" : "active/passive");
+	}
 	supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_5GHZ);
-	channels = supp_band->channels;
+	if (supp_band) {
+		channels = supp_band->channels;
 
-	pos += scnprintf(buf + pos, bufsz - pos,
-			"Displaying %d channels in 5.2GHz band (802.11a)\n",
-			supp_band->n_channels);
-
-	for (i = 0; i < supp_band->n_channels; i++)
 		pos += scnprintf(buf + pos, bufsz - pos,
-				"%d: %ddBm: BSS%s%s, %s.\n",
-				ieee80211_frequency_to_channel(
-				channels[i].center_freq),
-				channels[i].max_power,
-				channels[i].flags & IEEE80211_CHAN_RADAR ?
-				" (IEEE 802.11h required)" : "",
-				((channels[i].flags & IEEE80211_CHAN_NO_IBSS)
-				|| (channels[i].flags &
-				IEEE80211_CHAN_RADAR)) ? "" :
-				", IBSS",
-				channels[i].flags &
-				IEEE80211_CHAN_PASSIVE_SCAN ?
-				"passive only" : "active/passive");
+				"Displaying %d channels in 5.2GHz band (802.11a)\n",
+				supp_band->n_channels);
 
+		for (i = 0; i < supp_band->n_channels; i++)
+			pos += scnprintf(buf + pos, bufsz - pos,
+					"%d: %ddBm: BSS%s%s, %s.\n",
+					ieee80211_frequency_to_channel(
+					channels[i].center_freq),
+					channels[i].max_power,
+					channels[i].flags & IEEE80211_CHAN_RADAR ?
+					" (IEEE 802.11h required)" : "",
+					((channels[i].flags & IEEE80211_CHAN_NO_IBSS)
+					|| (channels[i].flags &
+					IEEE80211_CHAN_RADAR)) ? "" :
+					", IBSS",
+					channels[i].flags &
+					IEEE80211_CHAN_PASSIVE_SCAN ?
+					"passive only" : "active/passive");
+	}
 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 	kfree(buf);
 	return ret;
@@ -564,7 +571,7 @@
 
 DEBUGFS_READ_WRITE_FILE_OPS(sram);
 DEBUGFS_WRITE_FILE_OPS(log_event);
-DEBUGFS_READ_FILE_OPS(eeprom);
+DEBUGFS_READ_FILE_OPS(nvm);
 DEBUGFS_READ_FILE_OPS(stations);
 DEBUGFS_READ_FILE_OPS(rx_statistics);
 DEBUGFS_READ_FILE_OPS(tx_statistics);
@@ -598,7 +605,7 @@
 
 	DEBUGFS_ADD_DIR(data, dbgfs->dir_drv);
 	DEBUGFS_ADD_DIR(rf, dbgfs->dir_drv);
-	DEBUGFS_ADD_FILE(eeprom, data);
+	DEBUGFS_ADD_FILE(nvm, data);
 	DEBUGFS_ADD_FILE(sram, data);
 	DEBUGFS_ADD_FILE(log_event, data);
 	DEBUGFS_ADD_FILE(stations, data);
@@ -629,7 +636,7 @@
 	if (!priv->dbgfs)
 		return;
 
-	DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_eeprom);
+	DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_nvm);
 	DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_rx_statistics);
 	DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_tx_statistics);
 	DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_sram);
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
index 3049ba2..2dafc26 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -382,6 +382,7 @@
 	u32 read;
 	u32 write;
 	u32 free_count;
+	u32 write_actual;
 	struct list_head rx_free;
 	struct list_head rx_used;
 	int need_update;
@@ -499,22 +500,13 @@
 #define STA_PS_STATUS_WAKE             0
 #define STA_PS_STATUS_SLEEP            1
 
-struct iwl3945_tid_data {
-	u16 seq_number;
-};
-
-struct iwl3945_hw_key {
-	enum ieee80211_key_alg alg;
-	int keylen;
-	u8 key[32];
-};
 
 struct iwl3945_station_entry {
 	struct iwl3945_addsta_cmd sta;
-	struct iwl3945_tid_data tid[MAX_TID_COUNT];
+	struct iwl_tid_data tid[MAX_TID_COUNT];
 	u8 used;
 	u8 ps_status;
-	struct iwl3945_hw_key keyinfo;
+	struct iwl_hw_key keyinfo;
 };
 
 struct iwl_station_entry {
@@ -823,6 +815,11 @@
 	MEASUREMENT_ACTIVE = (1 << 1),
 };
 
+enum iwl_nvm_type {
+	NVM_DEVICE_TYPE_EEPROM = 0,
+	NVM_DEVICE_TYPE_OTP,
+};
+
 /* interrupt statistics */
 struct isr_statistics {
 	u32 hw;
@@ -900,6 +897,7 @@
 	/* spinlock */
 	spinlock_t lock;	/* protect general shared data */
 	spinlock_t hcmd_lock;	/* protect hcmd */
+	spinlock_t reg_lock;	/* protect hw register access */
 	struct mutex mutex;
 
 	/* basic pci-network driver stuff */
@@ -1033,6 +1031,7 @@
 
 	/* eeprom */
 	u8 *eeprom;
+	int    nvm_device_type;
 	struct iwl_eeprom_calib_info *calib_info;
 
 	enum nl80211_iftype iw_mode;
@@ -1050,7 +1049,16 @@
 	/*End*/
 	struct iwl_hw_params hw_params;
 
+	/* INT ICT Table */
+	u32 *ict_tbl;
+	dma_addr_t ict_tbl_dma;
+	dma_addr_t aligned_ict_tbl_dma;
+	int ict_index;
+	void *ict_tbl_vir;
+	u32 inta;
+	bool use_ict;
 
+	u32 inta_mask;
 	/* Current association information needed to configure the
 	 * hardware */
 	u16 assoc_id;
@@ -1105,7 +1113,7 @@
 	u32 disable_tx_power_cal;
 	struct work_struct run_time_calib_work;
 	struct timer_list statistics_periodic;
-
+	bool hw_ready;
 	/*For 3945*/
 #define IWL_DEFAULT_TX_POWER 0x0F
 
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c
index 401438a..cefa501 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c
@@ -152,6 +152,32 @@
 }
 EXPORT_SYMBOL(iwlcore_eeprom_verify_signature);
 
+static int iwlcore_get_nvm_type(struct iwl_priv *priv)
+{
+	u32 otpgp;
+	int nvm_type;
+
+	/* OTP only valid for CP/PP and after */
+	switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) {
+	case CSR_HW_REV_TYPE_3945:
+	case CSR_HW_REV_TYPE_4965:
+	case CSR_HW_REV_TYPE_5300:
+	case CSR_HW_REV_TYPE_5350:
+	case CSR_HW_REV_TYPE_5100:
+	case CSR_HW_REV_TYPE_5150:
+		nvm_type = NVM_DEVICE_TYPE_EEPROM;
+		break;
+	default:
+		otpgp = iwl_read32(priv, CSR_OTP_GP_REG);
+		if (otpgp & CSR_OTP_GP_REG_DEVICE_SELECT)
+			nvm_type = NVM_DEVICE_TYPE_OTP;
+		else
+			nvm_type = NVM_DEVICE_TYPE_EEPROM;
+		break;
+	}
+	return  nvm_type;
+}
+
 /*
  * The device's EEPROM semaphore prevents conflicts between driver and uCode
  * when accessing the EEPROM; each access is a series of pulses to/from the
@@ -198,6 +224,33 @@
 }
 EXPORT_SYMBOL(iwlcore_eeprom_query_addr);
 
+static int iwl_init_otp_access(struct iwl_priv *priv)
+{
+	int ret;
+
+	/* Enable 40MHz radio clock */
+	_iwl_write32(priv, CSR_GP_CNTRL,
+		     _iwl_read32(priv, CSR_GP_CNTRL) |
+		     CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+
+	/* wait for clock to be ready */
+	ret = iwl_poll_direct_bit(priv, CSR_GP_CNTRL,
+				  CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+				  25000);
+	if (ret < 0)
+		IWL_ERR(priv, "Time out access OTP\n");
+	else {
+		if (!ret) {
+			iwl_set_bits_prph(priv, APMG_PS_CTRL_REG,
+					  APMG_PS_CTRL_VAL_RESET_REQ);
+			udelay(5);
+			iwl_clear_bits_prph(priv, APMG_PS_CTRL_REG,
+					    APMG_PS_CTRL_VAL_RESET_REQ);
+		}
+	}
+	return ret;
+}
+
 /**
  * iwl_eeprom_init - read EEPROM contents
  *
@@ -209,11 +262,18 @@
 {
 	u16 *e;
 	u32 gp = iwl_read32(priv, CSR_EEPROM_GP);
-	int sz = priv->cfg->eeprom_size;
+	int sz;
 	int ret;
 	u16 addr;
+	u32 otpgp;
+
+	priv->nvm_device_type = iwlcore_get_nvm_type(priv);
 
 	/* allocate eeprom */
+	if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP)
+		priv->cfg->eeprom_size =
+			OTP_BLOCK_SIZE * OTP_LOWER_BLOCKS_TOTAL;
+	sz = priv->cfg->eeprom_size;
 	priv->eeprom = kzalloc(sz, GFP_KERNEL);
 	if (!priv->eeprom) {
 		ret = -ENOMEM;
@@ -235,30 +295,77 @@
 		ret = -ENOENT;
 		goto err;
 	}
-
-	/* eeprom is an array of 16bit values */
-	for (addr = 0; addr < sz; addr += sizeof(u16)) {
-		u32 r;
-
-		_iwl_write32(priv, CSR_EEPROM_REG,
-			     CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
-
-		ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG,
-					  CSR_EEPROM_REG_READ_VALID_MSK,
-					  IWL_EEPROM_ACCESS_TIMEOUT);
-		if (ret < 0) {
-			IWL_ERR(priv, "Time out reading EEPROM[%d]\n", addr);
-			goto done;
+	if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) {
+		ret = iwl_init_otp_access(priv);
+		if (ret) {
+			IWL_ERR(priv, "Failed to initialize OTP access.\n");
+			ret = -ENOENT;
+			goto err;
 		}
-		r = _iwl_read_direct32(priv, CSR_EEPROM_REG);
-		e[addr / 2] = le16_to_cpu((__force __le16)(r >> 16));
+		_iwl_write32(priv, CSR_EEPROM_GP,
+			     iwl_read32(priv, CSR_EEPROM_GP) &
+			     ~CSR_EEPROM_GP_IF_OWNER_MSK);
+		/* clear */
+		_iwl_write32(priv, CSR_OTP_GP_REG,
+			     iwl_read32(priv, CSR_OTP_GP_REG) |
+			     CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK |
+			     CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK);
+
+		for (addr = 0; addr < sz; addr += sizeof(u16)) {
+			u32 r;
+
+			_iwl_write32(priv, CSR_EEPROM_REG,
+				     CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
+
+			ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG,
+						  CSR_EEPROM_REG_READ_VALID_MSK,
+						  IWL_EEPROM_ACCESS_TIMEOUT);
+			if (ret < 0) {
+				IWL_ERR(priv, "Time out reading OTP[%d]\n", addr);
+				goto done;
+			}
+			r = _iwl_read_direct32(priv, CSR_EEPROM_REG);
+			/* check for ECC errors: */
+			otpgp = iwl_read32(priv, CSR_OTP_GP_REG);
+			if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) {
+				/* stop in this case */
+				IWL_ERR(priv, "Uncorrectable OTP ECC error, Abort OTP read\n");
+				goto done;
+			}
+			if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) {
+				/* continue in this case */
+				_iwl_write32(priv, CSR_OTP_GP_REG,
+					     iwl_read32(priv, CSR_OTP_GP_REG) |
+					     CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK);
+				IWL_ERR(priv, "Correctable OTP ECC error, continue read\n");
+			}
+			e[addr / 2] = le16_to_cpu((__force __le16)(r >> 16));
+		}
+	} else {
+		/* eeprom is an array of 16bit values */
+		for (addr = 0; addr < sz; addr += sizeof(u16)) {
+			u32 r;
+
+			_iwl_write32(priv, CSR_EEPROM_REG,
+				     CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
+
+			ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG,
+						  CSR_EEPROM_REG_READ_VALID_MSK,
+						  IWL_EEPROM_ACCESS_TIMEOUT);
+			if (ret < 0) {
+				IWL_ERR(priv, "Time out reading EEPROM[%d]\n", addr);
+				goto done;
+			}
+			r = _iwl_read_direct32(priv, CSR_EEPROM_REG);
+			e[addr / 2] = le16_to_cpu((__force __le16)(r >> 16));
+		}
 	}
 	ret = 0;
 done:
 	priv->cfg->ops->lib->eeprom_ops.release_semaphore(priv);
 err:
 	if (ret)
-		kfree(priv->eeprom);
+		iwl_eeprom_free(priv);
 alloc_err:
 	return ret;
 }
@@ -301,6 +408,8 @@
 
 u16 iwl_eeprom_query16(const struct iwl_priv *priv, size_t offset)
 {
+	if (!priv->eeprom)
+		return 0;
 	return (u16)priv->eeprom[offset] | ((u16)priv->eeprom[offset + 1] << 8);
 }
 EXPORT_SYMBOL(iwl_eeprom_query16);
@@ -481,8 +590,8 @@
 			/* First write that fat is not enabled, and then enable
 			 * one by one */
 			ch_info->fat_extension_channel =
-				(IEEE80211_CHAN_NO_FAT_ABOVE |
-				 IEEE80211_CHAN_NO_FAT_BELOW);
+				(IEEE80211_CHAN_NO_HT40PLUS |
+				 IEEE80211_CHAN_NO_HT40MINUS);
 
 			if (!(is_channel_valid(ch_info))) {
 				IWL_DEBUG_INFO(priv, "Ch. %d Flags %x [%sGHz] - "
@@ -561,7 +670,7 @@
 				fat_extension_chan = 0;
 			else
 				fat_extension_chan =
-					IEEE80211_CHAN_NO_FAT_BELOW;
+					IEEE80211_CHAN_NO_HT40MINUS;
 
 			/* Set up driver's info for lower half */
 			iwl_set_fat_chan_info(priv, ieeeband,
@@ -573,7 +682,7 @@
 			iwl_set_fat_chan_info(priv, ieeeband,
 						(eeprom_ch_index[ch] + 4),
 						&(eeprom_ch_info[ch]),
-						IEEE80211_CHAN_NO_FAT_ABOVE);
+						IEEE80211_CHAN_NO_HT40PLUS);
 		}
 	}
 
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.h b/drivers/net/wireless/iwlwifi/iwl-eeprom.h
index 3479153..195b4ef 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom.h
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.h
@@ -179,6 +179,10 @@
 #define EEPROM_5050_TX_POWER_VERSION    (4)
 #define EEPROM_5050_EEPROM_VERSION	(0x21E)
 
+/* OTP */
+#define OTP_LOWER_BLOCKS_TOTAL		(3)
+#define OTP_BLOCK_SIZE			(0x400)
+
 /* 2.4 GHz */
 extern const u8 iwl_eeprom_band_1[14];
 
diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h
index 083ea1f..d30cb02 100644
--- a/drivers/net/wireless/iwlwifi/iwl-io.h
+++ b/drivers/net/wireless/iwlwifi/iwl-io.h
@@ -131,9 +131,23 @@
 	IWL_DEBUG_IO(priv, "set_bit(0x%08X, 0x%08X) = 0x%08X\n", reg, mask, val);
 	_iwl_write32(priv, reg, val);
 }
-#define iwl_set_bit(p, r, m) __iwl_set_bit(__FILE__, __LINE__, p, r, m)
+static inline void iwl_set_bit(struct iwl_priv *p, u32 r, u32 m)
+{
+	unsigned long reg_flags;
+
+	spin_lock_irqsave(&p->reg_lock, reg_flags);
+	__iwl_set_bit(__FILE__, __LINE__, p, r, m);
+	spin_unlock_irqrestore(&p->reg_lock, reg_flags);
+}
 #else
-#define iwl_set_bit(p, r, m) _iwl_set_bit(p, r, m)
+static inline void iwl_set_bit(struct iwl_priv *p, u32 r, u32 m)
+{
+	unsigned long reg_flags;
+
+	spin_lock_irqsave(&p->reg_lock, reg_flags);
+	_iwl_set_bit(p, r, m);
+	spin_unlock_irqrestore(&p->reg_lock, reg_flags);
+}
 #endif
 
 static inline void _iwl_clear_bit(struct iwl_priv *priv, u32 reg, u32 mask)
@@ -148,19 +162,30 @@
 	IWL_DEBUG_IO(priv, "clear_bit(0x%08X, 0x%08X) = 0x%08X\n", reg, mask, val);
 	_iwl_write32(priv, reg, val);
 }
-#define iwl_clear_bit(p, r, m) __iwl_clear_bit(__FILE__, __LINE__, p, r, m)
+static inline void iwl_clear_bit(struct iwl_priv *p, u32 r, u32 m)
+{
+	unsigned long reg_flags;
+
+	spin_lock_irqsave(&p->reg_lock, reg_flags);
+	__iwl_clear_bit(__FILE__, __LINE__, p, r, m);
+	spin_unlock_irqrestore(&p->reg_lock, reg_flags);
+}
 #else
-#define iwl_clear_bit(p, r, m) _iwl_clear_bit(p, r, m)
+static inline void iwl_clear_bit(struct iwl_priv *p, u32 r, u32 m)
+{
+	unsigned long reg_flags;
+
+	spin_lock_irqsave(&p->reg_lock, reg_flags);
+	_iwl_clear_bit(p, r, m);
+	spin_unlock_irqrestore(&p->reg_lock, reg_flags);
+}
 #endif
 
 static inline int _iwl_grab_nic_access(struct iwl_priv *priv)
 {
 	int ret;
 	u32 val;
-#ifdef CONFIG_IWLWIFI_DEBUG
-	if (atomic_read(&priv->restrict_refcnt))
-		return 0;
-#endif
+
 	/* this bit wakes up the NIC */
 	_iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
 	ret = _iwl_poll_bit(priv, CSR_GP_CNTRL,
@@ -170,12 +195,10 @@
 	if (ret < 0) {
 		val = _iwl_read32(priv, CSR_GP_CNTRL);
 		IWL_ERR(priv, "MAC is in deep sleep!.  CSR_GP_CNTRL = 0x%08X\n", val);
+		_iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI);
 		return -EIO;
 	}
 
-#ifdef CONFIG_IWLWIFI_DEBUG
-	atomic_inc(&priv->restrict_refcnt);
-#endif
 	return 0;
 }
 
@@ -183,9 +206,6 @@
 static inline int __iwl_grab_nic_access(const char *f, u32 l,
 					       struct iwl_priv *priv)
 {
-	if (atomic_read(&priv->restrict_refcnt))
-		IWL_ERR(priv, "Grabbing access while already held %s %d.\n", f, l);
-
 	IWL_DEBUG_IO(priv, "grabbing nic access - %s %d\n", f, l);
 	return _iwl_grab_nic_access(priv);
 }
@@ -198,18 +218,13 @@
 
 static inline void _iwl_release_nic_access(struct iwl_priv *priv)
 {
-#ifdef CONFIG_IWLWIFI_DEBUG
-	if (atomic_dec_and_test(&priv->restrict_refcnt))
-#endif
-		_iwl_clear_bit(priv, CSR_GP_CNTRL,
-			       CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+	_iwl_clear_bit(priv, CSR_GP_CNTRL,
+			CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
 }
 #ifdef CONFIG_IWLWIFI_DEBUG
 static inline void __iwl_release_nic_access(const char *f, u32 l,
 					    struct iwl_priv *priv)
 {
-	if (atomic_read(&priv->restrict_refcnt) <= 0)
-		IWL_ERR(priv, "Release unheld nic access at line %s %d.\n", f, l);
 
 	IWL_DEBUG_IO(priv, "releasing nic access - %s %d\n", f, l);
 	_iwl_release_nic_access(priv);
@@ -230,16 +245,37 @@
 					struct iwl_priv *priv, u32 reg)
 {
 	u32 value = _iwl_read_direct32(priv, reg);
-	if (!atomic_read(&priv->restrict_refcnt))
-		IWL_ERR(priv, "Nic access not held from %s %d\n", f, l);
 	IWL_DEBUG_IO(priv, "read_direct32(0x%4X) = 0x%08x - %s %d \n", reg, value,
 		     f, l);
 	return value;
 }
-#define iwl_read_direct32(priv, reg) \
-	__iwl_read_direct32(__FILE__, __LINE__, priv, reg)
+static inline u32 iwl_read_direct32(struct iwl_priv *priv, u32 reg)
+{
+	u32 value;
+	unsigned long reg_flags;
+
+	spin_lock_irqsave(&priv->reg_lock, reg_flags);
+	iwl_grab_nic_access(priv);
+	value = __iwl_read_direct32(__FILE__, __LINE__, priv, reg);
+	iwl_release_nic_access(priv);
+	spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
+	return value;
+}
+
 #else
-#define iwl_read_direct32 _iwl_read_direct32
+static inline u32 iwl_read_direct32(struct iwl_priv *priv, u32 reg)
+{
+	u32 value;
+	unsigned long reg_flags;
+
+	spin_lock_irqsave(&priv->reg_lock, reg_flags);
+	iwl_grab_nic_access(priv);
+	value = _iwl_read_direct32(priv, reg);
+	iwl_release_nic_access(priv);
+	spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
+	return value;
+
+}
 #endif
 
 static inline void _iwl_write_direct32(struct iwl_priv *priv,
@@ -247,19 +283,17 @@
 {
 	_iwl_write32(priv, reg, value);
 }
-#ifdef CONFIG_IWLWIFI_DEBUG
-static void __iwl_write_direct32(const char *f , u32 line,
-				   struct iwl_priv *priv, u32 reg, u32 value)
+static inline void iwl_write_direct32(struct iwl_priv *priv, u32 reg, u32 value)
 {
-	if (!atomic_read(&priv->restrict_refcnt))
-		IWL_ERR(priv, "Nic access not held from %s line %d\n", f, line);
-	_iwl_write_direct32(priv, reg, value);
+	unsigned long reg_flags;
+
+	spin_lock_irqsave(&priv->reg_lock, reg_flags);
+	if (!iwl_grab_nic_access(priv)) {
+		_iwl_write_direct32(priv, reg, value);
+		iwl_release_nic_access(priv);
+	}
+	spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
 }
-#define iwl_write_direct32(priv, reg, value) \
-	__iwl_write_direct32(__func__, __LINE__, priv, reg, value)
-#else
-#define iwl_write_direct32 _iwl_write_direct32
-#endif
 
 static inline void iwl_write_reg_buf(struct iwl_priv *priv,
 					       u32 reg, u32 len, u32 *values)
@@ -268,14 +302,23 @@
 
 	if ((priv != NULL) && (values != NULL)) {
 		for (; 0 < len; len -= count, reg += count, values++)
-			_iwl_write_direct32(priv, reg, *values);
+			iwl_write_direct32(priv, reg, *values);
 	}
 }
 
 static inline int _iwl_poll_direct_bit(struct iwl_priv *priv, u32 addr,
 				       u32 mask, int timeout)
 {
-	return _iwl_poll_bit(priv, addr, mask, mask, timeout);
+	int t = 0;
+
+	do {
+		if ((iwl_read_direct32(priv, addr) & mask) == mask)
+			return t;
+		udelay(IWL_POLL_INTERVAL);
+		t += IWL_POLL_INTERVAL;
+	} while (t < timeout);
+
+	return -ETIMEDOUT;
 }
 
 #ifdef CONFIG_IWLWIFI_DEBUG
@@ -305,20 +348,18 @@
 	rmb();
 	return _iwl_read_direct32(priv, HBUS_TARG_PRPH_RDAT);
 }
-#ifdef CONFIG_IWLWIFI_DEBUG
-static inline u32 __iwl_read_prph(const char *f, u32 line,
-				  struct iwl_priv *priv, u32 reg)
+static inline u32 iwl_read_prph(struct iwl_priv *priv, u32 reg)
 {
-	if (!atomic_read(&priv->restrict_refcnt))
-		IWL_ERR(priv, "Nic access not held from %s line %d\n", f, line);
-	return _iwl_read_prph(priv, reg);
-}
+	unsigned long reg_flags;
+	u32 val;
 
-#define iwl_read_prph(priv, reg) \
-	__iwl_read_prph(__func__, __LINE__, priv, reg)
-#else
-#define iwl_read_prph _iwl_read_prph
-#endif
+	spin_lock_irqsave(&priv->reg_lock, reg_flags);
+	iwl_grab_nic_access(priv);
+	val = _iwl_read_prph(priv, reg);
+	iwl_release_nic_access(priv);
+	spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
+	return val;
+}
 
 static inline void _iwl_write_prph(struct iwl_priv *priv,
 					     u32 addr, u32 val)
@@ -328,83 +369,107 @@
 	wmb();
 	_iwl_write_direct32(priv, HBUS_TARG_PRPH_WDAT, val);
 }
-#ifdef CONFIG_IWLWIFI_DEBUG
-static inline void __iwl_write_prph(const char *f, u32 line,
-				    struct iwl_priv *priv, u32 addr, u32 val)
-{
-	if (!atomic_read(&priv->restrict_refcnt))
-		IWL_ERR(priv, "Nic access not held from %s line %d\n", f, line);
-	_iwl_write_prph(priv, addr, val);
-}
 
-#define iwl_write_prph(priv, addr, val) \
-	__iwl_write_prph(__func__, __LINE__, priv, addr, val);
-#else
-#define iwl_write_prph _iwl_write_prph
-#endif
+static inline void iwl_write_prph(struct iwl_priv *priv, u32 addr, u32 val)
+{
+	unsigned long reg_flags;
+
+	spin_lock_irqsave(&priv->reg_lock, reg_flags);
+	if (!iwl_grab_nic_access(priv)) {
+		_iwl_write_prph(priv, addr, val);
+		iwl_release_nic_access(priv);
+	}
+	spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
+}
 
 #define _iwl_set_bits_prph(priv, reg, mask) \
 	_iwl_write_prph(priv, reg, (_iwl_read_prph(priv, reg) | mask))
-#ifdef CONFIG_IWLWIFI_DEBUG
-static inline void __iwl_set_bits_prph(const char *f, u32 line,
-				       struct iwl_priv *priv,
-				       u32 reg, u32 mask)
-{
-	if (!atomic_read(&priv->restrict_refcnt))
-		IWL_ERR(priv, "Nic access not held from %s line %d\n", f, line);
 
+static inline void iwl_set_bits_prph(struct iwl_priv *priv, u32 reg, u32 mask)
+{
+	unsigned long reg_flags;
+
+	spin_lock_irqsave(&priv->reg_lock, reg_flags);
+	iwl_grab_nic_access(priv);
 	_iwl_set_bits_prph(priv, reg, mask);
+	iwl_release_nic_access(priv);
+	spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
 }
-#define iwl_set_bits_prph(priv, reg, mask) \
-	__iwl_set_bits_prph(__func__, __LINE__, priv, reg, mask)
-#else
-#define iwl_set_bits_prph _iwl_set_bits_prph
-#endif
 
 #define _iwl_set_bits_mask_prph(priv, reg, bits, mask) \
 	_iwl_write_prph(priv, reg, ((_iwl_read_prph(priv, reg) & mask) | bits))
 
-#ifdef CONFIG_IWLWIFI_DEBUG
-static inline void __iwl_set_bits_mask_prph(const char *f, u32 line,
-		struct iwl_priv *priv, u32 reg, u32 bits, u32 mask)
+static inline void iwl_set_bits_mask_prph(struct iwl_priv *priv, u32 reg,
+				u32 bits, u32 mask)
 {
-	if (!atomic_read(&priv->restrict_refcnt))
-		IWL_ERR(priv, "Nic access not held from %s line %d\n", f, line);
+	unsigned long reg_flags;
+
+	spin_lock_irqsave(&priv->reg_lock, reg_flags);
+	iwl_grab_nic_access(priv);
 	_iwl_set_bits_mask_prph(priv, reg, bits, mask);
+	iwl_release_nic_access(priv);
+	spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
 }
-#define iwl_set_bits_mask_prph(priv, reg, bits, mask) \
-	__iwl_set_bits_mask_prph(__func__, __LINE__, priv, reg, bits, mask)
-#else
-#define iwl_set_bits_mask_prph _iwl_set_bits_mask_prph
-#endif
 
 static inline void iwl_clear_bits_prph(struct iwl_priv
 						 *priv, u32 reg, u32 mask)
 {
-	u32 val = _iwl_read_prph(priv, reg);
+	unsigned long reg_flags;
+	u32 val;
+
+	spin_lock_irqsave(&priv->reg_lock, reg_flags);
+	iwl_grab_nic_access(priv);
+	val = _iwl_read_prph(priv, reg);
 	_iwl_write_prph(priv, reg, (val & ~mask));
+	iwl_release_nic_access(priv);
+	spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
 }
 
 static inline u32 iwl_read_targ_mem(struct iwl_priv *priv, u32 addr)
 {
-	iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR, addr);
+	unsigned long reg_flags;
+	u32 value;
+
+	spin_lock_irqsave(&priv->reg_lock, reg_flags);
+	iwl_grab_nic_access(priv);
+
+	_iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR, addr);
 	rmb();
-	return iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
+	value = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
+
+	iwl_release_nic_access(priv);
+	spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
+	return value;
 }
 
 static inline void iwl_write_targ_mem(struct iwl_priv *priv, u32 addr, u32 val)
 {
-	iwl_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr);
-	wmb();
-	iwl_write_direct32(priv, HBUS_TARG_MEM_WDAT, val);
+	unsigned long reg_flags;
+
+	spin_lock_irqsave(&priv->reg_lock, reg_flags);
+	if (!iwl_grab_nic_access(priv)) {
+		_iwl_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr);
+		wmb();
+		_iwl_write_direct32(priv, HBUS_TARG_MEM_WDAT, val);
+		iwl_release_nic_access(priv);
+	}
+	spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
 }
 
 static inline void iwl_write_targ_mem_buf(struct iwl_priv *priv, u32 addr,
 					  u32 len, u32 *values)
 {
-	iwl_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr);
-	wmb();
-	for (; 0 < len; len -= sizeof(u32), values++)
-		iwl_write_direct32(priv, HBUS_TARG_MEM_WDAT, *values);
+	unsigned long reg_flags;
+
+	spin_lock_irqsave(&priv->reg_lock, reg_flags);
+	if (!iwl_grab_nic_access(priv)) {
+		_iwl_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr);
+		wmb();
+		for (; 0 < len; len -= sizeof(u32), values++)
+			_iwl_write_direct32(priv, HBUS_TARG_MEM_WDAT, *values);
+
+		iwl_release_nic_access(priv);
+	}
+	spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
 }
 #endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c
index fae8426..2b8d40b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-rx.c
@@ -145,18 +145,14 @@
 			goto exit_unlock;
 		}
 
-		ret = iwl_grab_nic_access(priv);
-		if (ret)
-			goto exit_unlock;
-
-		/* Device expects a multiple of 8 */
-		iwl_write_direct32(priv, rx_wrt_ptr_reg, q->write & ~0x7);
-		iwl_release_nic_access(priv);
+		q->write_actual = (q->write & ~0x7);
+		iwl_write_direct32(priv, rx_wrt_ptr_reg, q->write_actual);
 
 	/* Else device is assumed to be awake */
 	} else {
 		/* Device expects a multiple of 8 */
-		iwl_write32(priv, rx_wrt_ptr_reg, q->write & ~0x7);
+		q->write_actual = (q->write & ~0x7);
+		iwl_write_direct32(priv, rx_wrt_ptr_reg, q->write_actual);
 	}
 
 	q->need_update = 0;
@@ -218,7 +214,7 @@
 
 	/* If we've added more space for the firmware to place data, tell it.
 	 * Increment device's write pointer in multiples of 8. */
-	if (write != (rxq->write & ~0x7)) {
+	if (rxq->write_actual != (rxq->write & ~0x7)) {
 		spin_lock_irqsave(&rxq->lock, flags);
 		rxq->need_update = 1;
 		spin_unlock_irqrestore(&rxq->lock, flags);
@@ -238,7 +234,7 @@
  * Also restock the Rx queue via iwl_rx_queue_restock.
  * This is called as a scheduled work item (except for during initialization)
  */
-void iwl_rx_allocate(struct iwl_priv *priv)
+void iwl_rx_allocate(struct iwl_priv *priv, gfp_t priority)
 {
 	struct iwl_rx_queue *rxq = &priv->rxq;
 	struct list_head *element;
@@ -260,7 +256,8 @@
 
 		/* Alloc a new receive buffer */
 		rxb->skb = alloc_skb(priv->hw_params.rx_buf_size + 256,
-				     GFP_KERNEL);
+						priority);
+
 		if (!rxb->skb) {
 			IWL_CRIT(priv, "Can not allocate SKB buffers\n");
 			/* We don't reschedule replenish work here -- we will
@@ -295,7 +292,7 @@
 {
 	unsigned long flags;
 
-	iwl_rx_allocate(priv);
+	iwl_rx_allocate(priv, GFP_KERNEL);
 
 	spin_lock_irqsave(&priv->lock, flags);
 	iwl_rx_queue_restock(priv);
@@ -303,6 +300,14 @@
 }
 EXPORT_SYMBOL(iwl_rx_replenish);
 
+void iwl_rx_replenish_now(struct iwl_priv *priv)
+{
+	iwl_rx_allocate(priv, GFP_ATOMIC);
+
+	iwl_rx_queue_restock(priv);
+}
+EXPORT_SYMBOL(iwl_rx_replenish_now);
+
 
 /* Assumes that the skb field of the buffers in 'pool' is kept accurate.
  * If an SKB has been detached, the POOL needs to have its SKB set to NULL
@@ -358,6 +363,7 @@
 	/* 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->write_actual = 0;
 	rxq->free_count = 0;
 	rxq->need_update = 0;
 	return 0;
@@ -396,6 +402,7 @@
 	/* 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->write_actual = 0;
 	rxq->free_count = 0;
 	spin_unlock_irqrestore(&rxq->lock, flags);
 }
@@ -403,18 +410,12 @@
 
 int iwl_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
 {
-	int ret;
-	unsigned long flags;
 	u32 rb_size;
 	const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */
-	const u32 rb_timeout = 0; /* FIXME: RX_RB_TIMEOUT why this stalls RX */
+	u32 rb_timeout = 0; /* FIXME: RX_RB_TIMEOUT for all devices? */
 
-	spin_lock_irqsave(&priv->lock, flags);
-	ret = iwl_grab_nic_access(priv);
-	if (ret) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		return ret;
-	}
+	if (!priv->cfg->use_isr_legacy)
+		rb_timeout = RX_RB_TIMEOUT;
 
 	if (priv->cfg->mod_params->amsdu_size_8K)
 		rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K;
@@ -452,35 +453,19 @@
 			   (rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)|
 			   (rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS));
 
-	iwl_release_nic_access(priv);
-
 	iwl_write32(priv, CSR_INT_COALESCING, 0x40);
 
-	spin_unlock_irqrestore(&priv->lock, flags);
-
 	return 0;
 }
 
 int iwl_rxq_stop(struct iwl_priv *priv)
 {
-	int ret;
-	unsigned long flags;
-
-	spin_lock_irqsave(&priv->lock, flags);
-	ret = iwl_grab_nic_access(priv);
-	if (unlikely(ret)) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		return ret;
-	}
 
 	/* stop Rx DMA */
 	iwl_write_direct32(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);
 	iwl_poll_direct_bit(priv, FH_MEM_RSSR_RX_STATUS_REG,
 			    FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000);
 
-	iwl_release_nic_access(priv);
-	spin_unlock_irqrestore(&priv->lock, flags);
-
 	return 0;
 }
 EXPORT_SYMBOL(iwl_rxq_stop);
@@ -582,8 +567,8 @@
 
 	iwl_leds_background(priv);
 
-	if (priv->cfg->ops->lib->temperature && change)
-		priv->cfg->ops->lib->temperature(priv);
+	if (priv->cfg->ops->lib->temp_ops.temperature && change)
+		priv->cfg->ops->lib->temp_ops.temperature(priv);
 }
 EXPORT_SYMBOL(iwl_rx_statistics);
 
diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c
index 065214b..f6d4af5 100644
--- a/drivers/net/wireless/iwlwifi/iwl-scan.c
+++ b/drivers/net/wireless/iwlwifi/iwl-scan.c
@@ -586,6 +586,7 @@
 	u8 rx_ant = priv->hw_params.valid_rx_ant;
 	u8 rate;
 	bool is_active = false;
+	int  chan_mod;
 
 	conf = ieee80211_get_hw_conf(priv->hw);
 
@@ -703,7 +704,9 @@
 	if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) {
 		band = IEEE80211_BAND_2GHZ;
 		scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
-		if (priv->active_rxon.flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK) {
+		chan_mod = le32_to_cpu(priv->active_rxon.flags & RXON_FLG_CHANNEL_MODE_MSK)
+				       >> RXON_FLG_CHANNEL_MODE_POS;
+		if (chan_mod == CHANNEL_MODE_PURE_40) {
 			rate = IWL_RATE_6M_PLCP;
 		} else {
 			rate = IWL_RATE_1M_PLCP;
diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c
index a82cca0..85ae7a6 100644
--- a/drivers/net/wireless/iwlwifi/iwl-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-tx.c
@@ -102,13 +102,8 @@
 			return ret;
 		}
 
-		/* restore this queue's parameters in nic hardware. */
-		ret = iwl_grab_nic_access(priv);
-		if (ret)
-			return ret;
 		iwl_write_direct32(priv, HBUS_TARG_WRPTR,
 				     txq->q.write_ptr | (txq_id << 8));
-		iwl_release_nic_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). */
@@ -429,11 +424,6 @@
 		goto error_kw;
 	}
 	spin_lock_irqsave(&priv->lock, flags);
-	ret = iwl_grab_nic_access(priv);
-	if (unlikely(ret)) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		goto error_reset;
-	}
 
 	/* Turn off all Tx DMA fifos */
 	priv->cfg->ops->lib->txq_set_sched(priv, 0);
@@ -441,7 +431,6 @@
 	/* Tell NIC where to find the "keep warm" buffer */
 	iwl_write_direct32(priv, FH_KW_MEM_ADDR_REG, priv->kw.dma >> 4);
 
-	iwl_release_nic_access(priv);
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	/* Alloc and init all Tx queues, including the command queue (#4) */
@@ -460,7 +449,6 @@
 
  error:
 	iwl_hw_txq_ctx_free(priv);
- error_reset:
 	iwl_free_dma_ptr(priv, &priv->kw);
  error_kw:
 	iwl_free_dma_ptr(priv, &priv->scd_bc_tbls);
@@ -478,10 +466,6 @@
 
 	/* Turn off all Tx DMA fifos */
 	spin_lock_irqsave(&priv->lock, flags);
-	if (iwl_grab_nic_access(priv)) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		return;
-	}
 
 	priv->cfg->ops->lib->txq_set_sched(priv, 0);
 
@@ -492,7 +476,6 @@
 				    FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch),
 				    1000);
 	}
-	iwl_release_nic_access(priv);
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	/* Deallocate memory for all Tx queues */
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index c32ec80..c434f49 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -340,7 +340,7 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(&priv->sta_lock, flags);
-	memset(&priv->stations_39[sta_id].keyinfo, 0, sizeof(struct iwl3945_hw_key));
+	memset(&priv->stations_39[sta_id].keyinfo, 0, sizeof(struct iwl_hw_key));
 	memset(&priv->stations_39[sta_id].sta.key, 0,
 		sizeof(struct iwl4965_keyinfo));
 	priv->stations_39[sta_id].sta.key.key_flags = STA_KEY_FLG_NO_ENC;
@@ -578,8 +578,7 @@
 				      int sta_id)
 {
 	struct iwl3945_tx_cmd *tx = (struct iwl3945_tx_cmd *)cmd->cmd.payload;
-	struct iwl3945_hw_key *keyinfo =
-	    &priv->stations_39[sta_id].keyinfo;
+	struct iwl_hw_key *keyinfo = &priv->stations_39[sta_id].keyinfo;
 
 	switch (keyinfo->alg) {
 	case ALG_CCMP:
@@ -1687,7 +1686,6 @@
 	u32 i;
 	u32 desc, time, count, base, data1;
 	u32 blink1, blink2, ilink1, ilink2;
-	int rc;
 
 	base = le32_to_cpu(priv->card_alive.error_event_table_ptr);
 
@@ -1696,11 +1694,6 @@
 		return;
 	}
 
-	rc = iwl_grab_nic_access(priv);
-	if (rc) {
-		IWL_WARN(priv, "Can not read from adapter at this time.\n");
-		return;
-	}
 
 	count = iwl_read_targ_mem(priv, base);
 
@@ -1735,8 +1728,6 @@
 			ilink1, ilink2, data1);
 	}
 
-	iwl_release_nic_access(priv);
-
 }
 
 #define EVENT_START_OFFSET  (6 * sizeof(u32))
@@ -1744,7 +1735,6 @@
 /**
  * iwl3945_print_event_log - Dump error event log to syslog
  *
- * NOTE: Must be called with iwl_grab_nic_access() already obtained!
  */
 static void iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx,
 				u32 num_events, u32 mode)
@@ -1787,7 +1777,6 @@
 
 static void iwl3945_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 */
@@ -1801,12 +1790,6 @@
 		return;
 	}
 
-	rc = iwl_grab_nic_access(priv);
-	if (rc) {
-		IWL_WARN(priv, "Can not read from adapter at this time.\n");
-		return;
-	}
-
 	/* event log header */
 	capacity = iwl_read_targ_mem(priv, base);
 	mode = iwl_read_targ_mem(priv, base + (1 * sizeof(u32)));
@@ -1818,7 +1801,6 @@
 	/* bail out if nothing in log */
 	if (size == 0) {
 		IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n");
-		iwl_release_nic_access(priv);
 		return;
 	}
 
@@ -1834,7 +1816,6 @@
 	/* (then/else) start at top of log */
 	iwl3945_print_event_log(priv, 0, next_entry, mode);
 
-	iwl_release_nic_access(priv);
 }
 
 static void iwl3945_irq_tasklet(struct iwl_priv *priv)
@@ -1953,11 +1934,8 @@
 		priv->isr_stats.tx++;
 
 		iwl_write32(priv, CSR_FH_INT_STATUS, (1 << 6));
-		if (!iwl_grab_nic_access(priv)) {
-			iwl_write_direct32(priv, FH39_TCSR_CREDIT
-					     (FH39_SRVC_CHNL), 0x0);
-			iwl_release_nic_access(priv);
-		}
+		iwl_write_direct32(priv, FH39_TCSR_CREDIT
+					(FH39_SRVC_CHNL), 0x0);
 		handled |= CSR_INT_BIT_FH_TX;
 	}
 
@@ -1966,9 +1944,9 @@
 		priv->isr_stats.unhandled++;
 	}
 
-	if (inta & ~CSR_INI_SET_MASK) {
+	if (inta & ~priv->inta_mask) {
 		IWL_WARN(priv, "Disabled INTA bits 0x%08x were pending\n",
-			 inta & ~CSR_INI_SET_MASK);
+			 inta & ~priv->inta_mask);
 		IWL_WARN(priv, "   with FH_INT = 0x%08x\n", inta_fh);
 	}
 
@@ -2132,10 +2110,6 @@
 
 	IWL_DEBUG_INFO(priv, "ucode inst image size is %u\n", len);
 
-	rc = iwl_grab_nic_access(priv);
-	if (rc)
-		return rc;
-
 	iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR,
 			       IWL39_RTC_INST_LOWER_BOUND);
 
@@ -2156,7 +2130,6 @@
 		}
 	}
 
-	iwl_release_nic_access(priv);
 
 	if (!errcnt)
 		IWL_DEBUG_INFO(priv,
@@ -2180,10 +2153,6 @@
 
 	IWL_DEBUG_INFO(priv, "ucode inst image size is %u\n", len);
 
-	rc = iwl_grab_nic_access(priv);
-	if (rc)
-		return rc;
-
 	for (i = 0; i < len; i += 100, image += 100/sizeof(u32)) {
 		/* read data comes through single port, auto-incr addr */
 		/* NOTE: Use the debugless read so we don't flood kernel log
@@ -2204,8 +2173,6 @@
 		}
 	}
 
-	iwl_release_nic_access(priv);
-
 	return rc;
 }
 
@@ -2529,20 +2496,11 @@
 {
 	dma_addr_t pinst;
 	dma_addr_t pdata;
-	int rc = 0;
-	unsigned long flags;
 
 	/* bits 31:0 for 3945 */
 	pinst = priv->ucode_code.p_addr;
 	pdata = priv->ucode_data_backup.p_addr;
 
-	spin_lock_irqsave(&priv->lock, flags);
-	rc = iwl_grab_nic_access(priv);
-	if (rc) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		return rc;
-	}
-
 	/* Tell bootstrap uCode where to find image to load */
 	iwl_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst);
 	iwl_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata);
@@ -2554,13 +2512,9 @@
 	iwl_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG,
 				 priv->ucode_code.len | BSM_DRAM_INST_LOAD);
 
-	iwl_release_nic_access(priv);
-
-	spin_unlock_irqrestore(&priv->lock, flags);
-
 	IWL_DEBUG_INFO(priv, "Runtime uCode pointers are set.\n");
 
-	return rc;
+	return 0;
 }
 
 /**
@@ -2613,7 +2567,6 @@
  */
 static void iwl3945_alive_start(struct iwl_priv *priv)
 {
-	int rc = 0;
 	int thermal_spin = 0;
 	u32 rfkill;
 
@@ -2638,15 +2591,8 @@
 
 	priv->cfg->ops->smgmt->clear_station_table(priv);
 
-	rc = iwl_grab_nic_access(priv);
-	if (rc) {
-		IWL_WARN(priv, "Can not read RFKILL status from adapter\n");
-		return;
-	}
-
 	rfkill = iwl_read_prph(priv, APMG_RFKILL_REG);
 	IWL_DEBUG_INFO(priv, "RFKILL status: 0x%x\n", rfkill);
-	iwl_release_nic_access(priv);
 
 	if (rfkill & 0x1) {
 		clear_bit(STATUS_RF_KILL_HW, &priv->status);
@@ -2792,13 +2738,8 @@
 	iwl3945_hw_txq_ctx_stop(priv);
 	iwl3945_hw_rxq_stop(priv);
 
-	spin_lock_irqsave(&priv->lock, flags);
-	if (!iwl_grab_nic_access(priv)) {
-		iwl_write_prph(priv, APMG_CLK_DIS_REG,
-					 APMG_CLK_VAL_DMA_CLK_RQT);
-		iwl_release_nic_access(priv);
-	}
-	spin_unlock_irqrestore(&priv->lock, flags);
+	iwl_write_prph(priv, APMG_CLK_DIS_REG,
+				APMG_CLK_VAL_DMA_CLK_RQT);
 
 	udelay(5);
 
@@ -4243,6 +4184,7 @@
 	IWL_DEBUG_INFO(priv, "*** LOAD DRIVER ***\n");
 	priv->cfg = cfg;
 	priv->pci_dev = pdev;
+	priv->inta_mask = CSR_INI_SET_MASK;
 
 #ifdef CONFIG_IWLWIFI_DEBUG
 	priv->debug_level = iwl3945_mod_params.debug;
@@ -4289,6 +4231,11 @@
 	 * PCI Tx retries from interfering with C3 CPU state */
 	pci_write_config_byte(pdev, 0x41, 0x00);
 
+	/* this spin lock will be used in apm_ops.init and EEPROM access
+	 * we should init now
+	 */
+	spin_lock_init(&priv->reg_lock);
+
 	/* amp init */
 	err = priv->cfg->ops->lib->apm_ops.init(priv);
 	if (err < 0) {
@@ -4344,8 +4291,8 @@
 
 	pci_enable_msi(priv->pci_dev);
 
-	err = request_irq(priv->pci_dev->irq, iwl_isr, IRQF_SHARED,
-			  DRV_NAME, priv);
+	err = request_irq(priv->pci_dev->irq, priv->cfg->ops->lib->isr,
+			  IRQF_SHARED, DRV_NAME, priv);
 	if (err) {
 		IWL_ERR(priv, "Error allocating IRQ %d\n", priv->pci_dev->irq);
 		goto out_disable_msi;
diff --git a/drivers/net/wireless/iwmc3200wifi/Kconfig b/drivers/net/wireless/iwmc3200wifi/Kconfig
new file mode 100644
index 0000000..ae84dda
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/Kconfig
@@ -0,0 +1,23 @@
+config IWM
+	tristate "Intel Wireless Multicomm 3200 WiFi driver"
+	depends on MMC && WLAN_80211 && EXPERIMENTAL
+	select LIB80211
+	select FW_LOADER
+	select RFKILL
+
+config IWM_DEBUG
+	bool "Enable full debugging output in iwmc3200wifi"
+	depends on IWM && DEBUG_FS
+	---help---
+	  This option will enable debug tracing and setting for iwm
+
+	  You can set the debug level and module through debugfs. By
+	  default all modules are set to the IWL_DL_ERR level.
+	  To see the list of debug modules and levels, see iwm/debug.h
+
+	  For example, if you want the full MLME debug output:
+	  echo 0xff > /debug/iwm/phyN/debug/mlme
+
+	  Or, if you want the full debug, for all modules:
+	  echo 0xff > /debug/iwm/phyN/debug/level
+	  echo 0xff > /debug/iwm/phyN/debug/modules
diff --git a/drivers/net/wireless/iwmc3200wifi/Makefile b/drivers/net/wireless/iwmc3200wifi/Makefile
new file mode 100644
index 0000000..7cb415e
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_IWM) := iwmc3200wifi.o
+iwmc3200wifi-objs += main.o netdev.o rx.o tx.o sdio.o hal.o fw.o
+iwmc3200wifi-objs += commands.o wext.o cfg80211.o eeprom.o rfkill.o
+
+iwmc3200wifi-$(CONFIG_IWM_DEBUG) += debugfs.o
diff --git a/drivers/net/wireless/iwmc3200wifi/bus.h b/drivers/net/wireless/iwmc3200wifi/bus.h
new file mode 100644
index 0000000..836663e
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/bus.h
@@ -0,0 +1,57 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 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-1301, USA.
+ *
+ */
+
+#ifndef __IWM_BUS_H__
+#define __IWM_BUS_H__
+
+#include "iwm.h"
+
+struct iwm_if_ops {
+	int (*enable)(struct iwm_priv *iwm);
+	int (*disable)(struct iwm_priv *iwm);
+	int (*send_chunk)(struct iwm_priv *iwm, u8* buf, int count);
+
+	int (*debugfs_init)(struct iwm_priv *iwm, struct dentry *parent_dir);
+	void (*debugfs_exit)(struct iwm_priv *iwm);
+
+	const char *umac_name;
+	const char *calib_lmac_name;
+	const char *lmac_name;
+};
+
+static inline int iwm_bus_send_chunk(struct iwm_priv *iwm, u8 *buf, int count)
+{
+	return iwm->bus_ops->send_chunk(iwm, buf, count);
+}
+
+static inline int iwm_bus_enable(struct iwm_priv *iwm)
+{
+	return iwm->bus_ops->enable(iwm);
+}
+
+static inline int iwm_bus_disable(struct iwm_priv *iwm)
+{
+	return iwm->bus_ops->disable(iwm);
+}
+
+#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
new file mode 100644
index 0000000..3256ad2
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
@@ -0,0 +1,409 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 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-1301, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+
+#include "iwm.h"
+#include "commands.h"
+#include "cfg80211.h"
+#include "debug.h"
+
+#define RATETAB_ENT(_rate, _rateid, _flags) \
+	{								\
+		.bitrate	= (_rate),				\
+		.hw_value	= (_rateid),				\
+		.flags		= (_flags),				\
+	}
+
+#define CHAN2G(_channel, _freq, _flags) {			\
+	.band			= IEEE80211_BAND_2GHZ,		\
+	.center_freq		= (_freq),			\
+	.hw_value		= (_channel),			\
+	.flags			= (_flags),			\
+	.max_antenna_gain	= 0,				\
+	.max_power		= 30,				\
+}
+
+#define CHAN5G(_channel, _flags) {				\
+	.band			= IEEE80211_BAND_5GHZ,		\
+	.center_freq		= 5000 + (5 * (_channel)),	\
+	.hw_value		= (_channel),			\
+	.flags			= (_flags),			\
+	.max_antenna_gain	= 0,				\
+	.max_power		= 30,				\
+}
+
+static struct ieee80211_rate iwm_rates[] = {
+	RATETAB_ENT(10,  0x1,   0),
+	RATETAB_ENT(20,  0x2,   0),
+	RATETAB_ENT(55,  0x4,   0),
+	RATETAB_ENT(110, 0x8,   0),
+	RATETAB_ENT(60,  0x10,  0),
+	RATETAB_ENT(90,  0x20,  0),
+	RATETAB_ENT(120, 0x40,  0),
+	RATETAB_ENT(180, 0x80,  0),
+	RATETAB_ENT(240, 0x100, 0),
+	RATETAB_ENT(360, 0x200, 0),
+	RATETAB_ENT(480, 0x400, 0),
+	RATETAB_ENT(540, 0x800, 0),
+};
+
+#define iwm_a_rates		(iwm_rates + 4)
+#define iwm_a_rates_size	8
+#define iwm_g_rates		(iwm_rates + 0)
+#define iwm_g_rates_size	12
+
+static struct ieee80211_channel iwm_2ghz_channels[] = {
+	CHAN2G(1, 2412, 0),
+	CHAN2G(2, 2417, 0),
+	CHAN2G(3, 2422, 0),
+	CHAN2G(4, 2427, 0),
+	CHAN2G(5, 2432, 0),
+	CHAN2G(6, 2437, 0),
+	CHAN2G(7, 2442, 0),
+	CHAN2G(8, 2447, 0),
+	CHAN2G(9, 2452, 0),
+	CHAN2G(10, 2457, 0),
+	CHAN2G(11, 2462, 0),
+	CHAN2G(12, 2467, 0),
+	CHAN2G(13, 2472, 0),
+	CHAN2G(14, 2484, 0),
+};
+
+static struct ieee80211_channel iwm_5ghz_a_channels[] = {
+	CHAN5G(34, 0),		CHAN5G(36, 0),
+	CHAN5G(38, 0),		CHAN5G(40, 0),
+	CHAN5G(42, 0),		CHAN5G(44, 0),
+	CHAN5G(46, 0),		CHAN5G(48, 0),
+	CHAN5G(52, 0),		CHAN5G(56, 0),
+	CHAN5G(60, 0),		CHAN5G(64, 0),
+	CHAN5G(100, 0),		CHAN5G(104, 0),
+	CHAN5G(108, 0),		CHAN5G(112, 0),
+	CHAN5G(116, 0),		CHAN5G(120, 0),
+	CHAN5G(124, 0),		CHAN5G(128, 0),
+	CHAN5G(132, 0),		CHAN5G(136, 0),
+	CHAN5G(140, 0),		CHAN5G(149, 0),
+	CHAN5G(153, 0),		CHAN5G(157, 0),
+	CHAN5G(161, 0),		CHAN5G(165, 0),
+	CHAN5G(184, 0),		CHAN5G(188, 0),
+	CHAN5G(192, 0),		CHAN5G(196, 0),
+	CHAN5G(200, 0),		CHAN5G(204, 0),
+	CHAN5G(208, 0),		CHAN5G(212, 0),
+	CHAN5G(216, 0),
+};
+
+static struct ieee80211_supported_band iwm_band_2ghz = {
+	.channels = iwm_2ghz_channels,
+	.n_channels = ARRAY_SIZE(iwm_2ghz_channels),
+	.bitrates = iwm_g_rates,
+	.n_bitrates = iwm_g_rates_size,
+};
+
+static struct ieee80211_supported_band iwm_band_5ghz = {
+	.channels = iwm_5ghz_a_channels,
+	.n_channels = ARRAY_SIZE(iwm_5ghz_a_channels),
+	.bitrates = iwm_a_rates,
+	.n_bitrates = iwm_a_rates_size,
+};
+
+int iwm_cfg80211_inform_bss(struct iwm_priv *iwm)
+{
+	struct wiphy *wiphy = iwm_to_wiphy(iwm);
+	struct iwm_bss_info *bss, *next;
+	struct iwm_umac_notif_bss_info *umac_bss;
+	struct ieee80211_mgmt *mgmt;
+	struct ieee80211_channel *channel;
+	struct ieee80211_supported_band *band;
+	s32 signal;
+	int freq;
+
+	list_for_each_entry_safe(bss, next, &iwm->bss_list, node) {
+		umac_bss = bss->bss;
+		mgmt = (struct ieee80211_mgmt *)(umac_bss->frame_buf);
+
+		if (umac_bss->band == UMAC_BAND_2GHZ)
+			band = wiphy->bands[IEEE80211_BAND_2GHZ];
+		else if (umac_bss->band == UMAC_BAND_5GHZ)
+			band = wiphy->bands[IEEE80211_BAND_5GHZ];
+		else {
+			IWM_ERR(iwm, "Invalid band: %d\n", umac_bss->band);
+			return -EINVAL;
+		}
+
+		freq = ieee80211_channel_to_frequency(umac_bss->channel);
+		channel = ieee80211_get_channel(wiphy, freq);
+		signal = umac_bss->rssi * 100;
+
+		if (!cfg80211_inform_bss_frame(wiphy, channel, mgmt,
+					       le16_to_cpu(umac_bss->frame_len),
+					       signal, GFP_KERNEL))
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int iwm_cfg80211_change_iface(struct wiphy *wiphy, int ifindex,
+				     enum nl80211_iftype type, u32 *flags,
+				     struct vif_params *params)
+{
+	struct net_device *ndev;
+	struct wireless_dev *wdev;
+	struct iwm_priv *iwm;
+	u32 old_mode;
+
+	/* we're under RTNL */
+	ndev = __dev_get_by_index(&init_net, ifindex);
+	if (!ndev)
+		return -ENODEV;
+
+	wdev = ndev->ieee80211_ptr;
+	iwm = ndev_to_iwm(ndev);
+	old_mode = iwm->conf.mode;
+
+	switch (type) {
+	case NL80211_IFTYPE_STATION:
+		iwm->conf.mode = UMAC_MODE_BSS;
+		break;
+	case NL80211_IFTYPE_ADHOC:
+		iwm->conf.mode = UMAC_MODE_IBSS;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	wdev->iftype = type;
+
+	if ((old_mode == iwm->conf.mode) || !iwm->umac_profile)
+		return 0;
+
+	iwm->umac_profile->mode = cpu_to_le32(iwm->conf.mode);
+
+	if (iwm->umac_profile_active) {
+		int ret = iwm_invalidate_mlme_profile(iwm);
+		if (ret < 0)
+			IWM_ERR(iwm, "Couldn't invalidate profile\n");
+	}
+
+	return 0;
+}
+
+static int iwm_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
+			     struct cfg80211_scan_request *request)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(ndev);
+	int ret;
+
+	if (!test_bit(IWM_STATUS_READY, &iwm->status)) {
+		IWM_ERR(iwm, "Scan while device is not ready\n");
+		return -EIO;
+	}
+
+	if (test_bit(IWM_STATUS_SCANNING, &iwm->status)) {
+		IWM_ERR(iwm, "Scanning already\n");
+		return -EAGAIN;
+	}
+
+	if (test_bit(IWM_STATUS_SCAN_ABORTING, &iwm->status)) {
+		IWM_ERR(iwm, "Scanning being aborted\n");
+		return -EAGAIN;
+	}
+
+	set_bit(IWM_STATUS_SCANNING, &iwm->status);
+
+	ret = iwm_scan_ssids(iwm, request->ssids, request->n_ssids);
+	if (ret) {
+		clear_bit(IWM_STATUS_SCANNING, &iwm->status);
+		return ret;
+	}
+
+	iwm->scan_request = request;
+	return 0;
+}
+
+static int iwm_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+	struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
+
+	if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
+	    (iwm->conf.rts_threshold != wiphy->rts_threshold)) {
+		int ret;
+
+		iwm->conf.rts_threshold = wiphy->rts_threshold;
+
+		ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
+					     CFG_RTS_THRESHOLD,
+					     iwm->conf.rts_threshold);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
+	    (iwm->conf.frag_threshold != wiphy->frag_threshold)) {
+		int ret;
+
+		iwm->conf.frag_threshold = wiphy->frag_threshold;
+
+		ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
+					     CFG_FRAG_THRESHOLD,
+					     iwm->conf.frag_threshold);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int iwm_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
+				  struct cfg80211_ibss_params *params)
+{
+	struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
+	struct ieee80211_channel *chan = params->channel;
+	struct cfg80211_bss *bss;
+
+	if (!test_bit(IWM_STATUS_READY, &iwm->status))
+		return -EIO;
+
+	/* UMAC doesn't support creating IBSS network with specified bssid.
+	 * This should be removed after we have join only mode supported. */
+	if (params->bssid)
+		return -EOPNOTSUPP;
+
+	bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL,
+				params->ssid, params->ssid_len);
+	if (!bss) {
+		iwm_scan_one_ssid(iwm, params->ssid, params->ssid_len);
+		schedule_timeout_interruptible(2 * HZ);
+		bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL,
+					params->ssid, params->ssid_len);
+	}
+	/* IBSS join only mode is not supported by UMAC ATM */
+	if (bss) {
+		cfg80211_put_bss(bss);
+		return -EOPNOTSUPP;
+	}
+
+	iwm->channel = ieee80211_frequency_to_channel(chan->center_freq);
+	iwm->umac_profile->ibss.band = chan->band;
+	iwm->umac_profile->ibss.channel = iwm->channel;
+	iwm->umac_profile->ssid.ssid_len = params->ssid_len;
+	memcpy(iwm->umac_profile->ssid.ssid, params->ssid, params->ssid_len);
+
+	if (params->bssid)
+		memcpy(&iwm->umac_profile->bssid[0], params->bssid, ETH_ALEN);
+
+	return iwm_send_mlme_profile(iwm);
+}
+
+static int iwm_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
+{
+	struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
+
+	if (iwm->umac_profile_active)
+		return iwm_invalidate_mlme_profile(iwm);
+
+	return 0;
+}
+
+static struct cfg80211_ops iwm_cfg80211_ops = {
+	.change_virtual_intf = iwm_cfg80211_change_iface,
+	.scan = iwm_cfg80211_scan,
+	.set_wiphy_params = iwm_cfg80211_set_wiphy_params,
+	.join_ibss = iwm_cfg80211_join_ibss,
+	.leave_ibss = iwm_cfg80211_leave_ibss,
+};
+
+struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev)
+{
+	int ret = 0;
+	struct wireless_dev *wdev;
+
+	/*
+	 * We're trying to have the following memory
+	 * layout:
+	 *
+	 * +-------------------------+
+	 * | struct wiphy	     |
+	 * +-------------------------+
+	 * | struct iwm_priv         |
+	 * +-------------------------+
+	 * | bus private data        |
+	 * | (e.g. iwm_priv_sdio)    |
+	 * +-------------------------+
+	 *
+	 */
+
+	wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
+	if (!wdev) {
+		dev_err(dev, "Couldn't allocate wireless device\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	wdev->wiphy = wiphy_new(&iwm_cfg80211_ops,
+				sizeof(struct iwm_priv) + sizeof_bus);
+	if (!wdev->wiphy) {
+		dev_err(dev, "Couldn't allocate wiphy device\n");
+		ret = -ENOMEM;
+		goto out_err_new;
+	}
+
+	set_wiphy_dev(wdev->wiphy, dev);
+	wdev->wiphy->max_scan_ssids = UMAC_WIFI_IF_PROBE_OPTION_MAX;
+	wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+				       BIT(NL80211_IFTYPE_ADHOC);
+	wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &iwm_band_2ghz;
+	wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &iwm_band_5ghz;
+	wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+
+	ret = wiphy_register(wdev->wiphy);
+	if (ret < 0) {
+		dev_err(dev, "Couldn't register wiphy device\n");
+		goto out_err_register;
+	}
+
+	return wdev;
+
+ out_err_register:
+	wiphy_free(wdev->wiphy);
+
+ out_err_new:
+	kfree(wdev);
+
+	return ERR_PTR(ret);
+}
+
+void iwm_wdev_free(struct iwm_priv *iwm)
+{
+	struct wireless_dev *wdev = iwm_to_wdev(iwm);
+
+	if (!wdev)
+		return;
+
+	wiphy_unregister(wdev->wiphy);
+	wiphy_free(wdev->wiphy);
+	kfree(wdev);
+}
diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.h b/drivers/net/wireless/iwmc3200wifi/cfg80211.h
new file mode 100644
index 0000000..56a3414
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.h
@@ -0,0 +1,31 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 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-1301, USA.
+ *
+ */
+
+#ifndef __IWM_CFG80211_H__
+#define __IWM_CFG80211_H__
+
+int iwm_cfg80211_inform_bss(struct iwm_priv *iwm);
+struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev);
+void iwm_wdev_free(struct iwm_priv *iwm);
+
+#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.c b/drivers/net/wireless/iwmc3200wifi/commands.c
new file mode 100644
index 0000000..834a7f5
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/commands.c
@@ -0,0 +1,920 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 of 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.
+ *
+ *
+ * Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/wireless.h>
+#include <linux/etherdevice.h>
+#include <linux/ieee80211.h>
+
+#include "iwm.h"
+#include "bus.h"
+#include "hal.h"
+#include "umac.h"
+#include "commands.h"
+#include "debug.h"
+
+static int iwm_send_lmac_ptrough_cmd(struct iwm_priv *iwm,
+				     u8 lmac_cmd_id,
+				     const void *lmac_payload,
+				     u16 lmac_payload_size,
+				     u8 resp)
+{
+	struct iwm_udma_wifi_cmd udma_cmd = UDMA_LMAC_INIT;
+	struct iwm_umac_cmd umac_cmd;
+	struct iwm_lmac_cmd lmac_cmd;
+
+	lmac_cmd.id = lmac_cmd_id;
+
+	umac_cmd.id = UMAC_CMD_OPCODE_WIFI_PASS_THROUGH;
+	umac_cmd.resp = resp;
+
+	return iwm_hal_send_host_cmd(iwm, &udma_cmd, &umac_cmd, &lmac_cmd,
+				     lmac_payload, lmac_payload_size);
+}
+
+int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size,
+			 bool resp)
+{
+	struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
+	struct iwm_umac_cmd umac_cmd;
+
+	umac_cmd.id = UMAC_CMD_OPCODE_WIFI_IF_WRAPPER;
+	umac_cmd.resp = resp;
+
+	return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd,
+				     payload, payload_size);
+}
+
+static struct coex_event iwm_sta_xor_prio_tbl[COEX_EVENTS_NUM] =
+{
+	{4, 3, 0, COEX_UNASSOC_IDLE_FLAGS},
+	{4, 3, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS},
+	{4, 3, 0, COEX_UNASSOC_AUTO_SCAN_FLAGS},
+	{4, 3, 0, COEX_CALIBRATION_FLAGS},
+	{4, 3, 0, COEX_PERIODIC_CALIBRATION_FLAGS},
+	{4, 3, 0, COEX_CONNECTION_ESTAB_FLAGS},
+	{4, 3, 0, COEX_ASSOCIATED_IDLE_FLAGS},
+	{4, 3, 0, COEX_ASSOC_MANUAL_SCAN_FLAGS},
+	{4, 3, 0, COEX_ASSOC_AUTO_SCAN_FLAGS},
+	{4, 3, 0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS},
+	{6, 3, 0, COEX_XOR_RF_ON_FLAGS},
+	{4, 3, 0, COEX_RF_OFF_FLAGS},
+	{6, 6, 0, COEX_STAND_ALONE_DEBUG_FLAGS},
+	{4, 3, 0, COEX_IPAN_ASSOC_LEVEL_FLAGS},
+	{4, 3, 0, COEX_RSRVD1_FLAGS},
+	{4, 3, 0, COEX_RSRVD2_FLAGS}
+};
+
+static struct coex_event iwm_sta_cm_prio_tbl[COEX_EVENTS_NUM] =
+{
+	{1, 1, 0, COEX_UNASSOC_IDLE_FLAGS},
+	{4, 3, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS},
+	{3, 3, 0, COEX_UNASSOC_AUTO_SCAN_FLAGS},
+	{5, 5, 0, COEX_CALIBRATION_FLAGS},
+	{4, 4, 0, COEX_PERIODIC_CALIBRATION_FLAGS},
+	{5, 4, 0, COEX_CONNECTION_ESTAB_FLAGS},
+	{4, 4, 0, COEX_ASSOCIATED_IDLE_FLAGS},
+	{4, 4, 0, COEX_ASSOC_MANUAL_SCAN_FLAGS},
+	{4, 4, 0, COEX_ASSOC_AUTO_SCAN_FLAGS},
+	{4, 4, 0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS},
+	{1, 1, 0, COEX_RF_ON_FLAGS},
+	{1, 1, 0, COEX_RF_OFF_FLAGS},
+	{6, 6, 0, COEX_STAND_ALONE_DEBUG_FLAGS},
+	{5, 4, 0, COEX_IPAN_ASSOC_LEVEL_FLAGS},
+	{1, 1, 0, COEX_RSRVD1_FLAGS},
+	{1, 1, 0, COEX_RSRVD2_FLAGS}
+};
+
+int iwm_send_prio_table(struct iwm_priv *iwm)
+{
+	struct iwm_coex_prio_table_cmd coex_table_cmd;
+	u32 coex_enabled, mode_enabled;
+
+	memset(&coex_table_cmd, 0, sizeof(struct iwm_coex_prio_table_cmd));
+
+	coex_table_cmd.flags = COEX_FLAGS_STA_TABLE_VALID_MSK;
+
+	switch (iwm->conf.coexist_mode) {
+	case COEX_MODE_XOR:
+	case COEX_MODE_CM:
+		coex_enabled = 1;
+		break;
+	default:
+		coex_enabled = 0;
+		break;
+	}
+
+	switch (iwm->conf.mode) {
+	case UMAC_MODE_BSS:
+	case UMAC_MODE_IBSS:
+		mode_enabled = 1;
+		break;
+	default:
+		mode_enabled = 0;
+		break;
+	}
+
+	if (coex_enabled && mode_enabled) {
+		coex_table_cmd.flags |= COEX_FLAGS_COEX_ENABLE_MSK |
+					COEX_FLAGS_ASSOC_WAKEUP_UMASK_MSK |
+					COEX_FLAGS_UNASSOC_WAKEUP_UMASK_MSK;
+
+		switch (iwm->conf.coexist_mode) {
+		case COEX_MODE_XOR:
+			memcpy(coex_table_cmd.sta_prio, iwm_sta_xor_prio_tbl,
+			       sizeof(iwm_sta_xor_prio_tbl));
+			break;
+		case COEX_MODE_CM:
+			memcpy(coex_table_cmd.sta_prio, iwm_sta_cm_prio_tbl,
+			       sizeof(iwm_sta_cm_prio_tbl));
+			break;
+		default:
+			IWM_ERR(iwm, "Invalid coex_mode 0x%x\n",
+				iwm->conf.coexist_mode);
+			break;
+		}
+	} else
+		IWM_WARN(iwm, "coexistense disabled\n");
+
+	return iwm_send_lmac_ptrough_cmd(iwm, COEX_PRIORITY_TABLE_CMD,
+				&coex_table_cmd,
+				sizeof(struct iwm_coex_prio_table_cmd), 1);
+}
+
+int iwm_send_init_calib_cfg(struct iwm_priv *iwm, u8 calib_requested)
+{
+	struct iwm_lmac_cal_cfg_cmd cal_cfg_cmd;
+
+	memset(&cal_cfg_cmd, 0, sizeof(struct iwm_lmac_cal_cfg_cmd));
+
+	cal_cfg_cmd.ucode_cfg.init.enable = cpu_to_le32(calib_requested);
+	cal_cfg_cmd.ucode_cfg.init.start = cpu_to_le32(calib_requested);
+	cal_cfg_cmd.ucode_cfg.init.send_res = cpu_to_le32(calib_requested);
+	cal_cfg_cmd.ucode_cfg.flags =
+		cpu_to_le32(CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_AFTER_MSK);
+
+	return iwm_send_lmac_ptrough_cmd(iwm, CALIBRATION_CFG_CMD, &cal_cfg_cmd,
+				sizeof(struct iwm_lmac_cal_cfg_cmd), 1);
+}
+
+int iwm_send_periodic_calib_cfg(struct iwm_priv *iwm, u8 calib_requested)
+{
+	struct iwm_lmac_cal_cfg_cmd cal_cfg_cmd;
+
+	memset(&cal_cfg_cmd, 0, sizeof(struct iwm_lmac_cal_cfg_cmd));
+
+	cal_cfg_cmd.ucode_cfg.periodic.enable = cpu_to_le32(calib_requested);
+	cal_cfg_cmd.ucode_cfg.periodic.start = cpu_to_le32(calib_requested);
+
+	return iwm_send_lmac_ptrough_cmd(iwm, CALIBRATION_CFG_CMD, &cal_cfg_cmd,
+				sizeof(struct iwm_lmac_cal_cfg_cmd), 0);
+}
+
+int iwm_store_rxiq_calib_result(struct iwm_priv *iwm)
+{
+	struct iwm_calib_rxiq *rxiq;
+	u8 *eeprom_rxiq = iwm_eeprom_access(iwm, IWM_EEPROM_CALIB_RXIQ);
+	int grplen = sizeof(struct iwm_calib_rxiq_group);
+
+	rxiq = kzalloc(sizeof(struct iwm_calib_rxiq), GFP_KERNEL);
+	if (!rxiq) {
+		IWM_ERR(iwm, "Couldn't alloc memory for RX IQ\n");
+		return -ENOMEM;
+	}
+
+	eeprom_rxiq = iwm_eeprom_access(iwm, IWM_EEPROM_CALIB_RXIQ);
+	if (IS_ERR(eeprom_rxiq)) {
+		IWM_ERR(iwm, "Couldn't access EEPROM RX IQ entry\n");
+		return PTR_ERR(eeprom_rxiq);
+	}
+
+	iwm->calib_res[SHILOH_PHY_CALIBRATE_RX_IQ_CMD].buf = (u8 *)rxiq;
+	iwm->calib_res[SHILOH_PHY_CALIBRATE_RX_IQ_CMD].size = sizeof(*rxiq);
+
+	rxiq->hdr.opcode = SHILOH_PHY_CALIBRATE_RX_IQ_CMD;
+	rxiq->hdr.first_grp = 0;
+	rxiq->hdr.grp_num = 1;
+	rxiq->hdr.all_data_valid = 1;
+
+	memcpy(&rxiq->group[0], eeprom_rxiq, 4 * grplen);
+	memcpy(&rxiq->group[4], eeprom_rxiq + 6 * grplen, grplen);
+
+	return 0;
+}
+
+int iwm_send_calib_results(struct iwm_priv *iwm)
+{
+	int i, ret = 0;
+
+	for (i = PHY_CALIBRATE_OPCODES_NUM; i < CALIBRATION_CMD_NUM; i++) {
+		if (test_bit(i - PHY_CALIBRATE_OPCODES_NUM,
+			     &iwm->calib_done_map)) {
+			IWM_DBG_CMD(iwm, DBG,
+				    "Send calibration %d result\n", i);
+			ret |= iwm_send_lmac_ptrough_cmd(iwm,
+					REPLY_PHY_CALIBRATION_CMD,
+					iwm->calib_res[i].buf,
+					iwm->calib_res[i].size, 0);
+
+			kfree(iwm->calib_res[i].buf);
+			iwm->calib_res[i].buf = NULL;
+			iwm->calib_res[i].size = 0;
+		}
+	}
+
+	return ret;
+}
+
+int iwm_send_umac_reset(struct iwm_priv *iwm, __le32 reset_flags, bool resp)
+{
+	struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
+	struct iwm_umac_cmd umac_cmd;
+	struct iwm_umac_cmd_reset reset;
+
+	reset.flags = reset_flags;
+
+	umac_cmd.id = UMAC_CMD_OPCODE_RESET;
+	umac_cmd.resp = resp;
+
+	return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &reset,
+				     sizeof(struct iwm_umac_cmd_reset));
+}
+
+int iwm_umac_set_config_fix(struct iwm_priv *iwm, u16 tbl, u16 key, u32 value)
+{
+	struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
+	struct iwm_umac_cmd umac_cmd;
+	struct iwm_umac_cmd_set_param_fix param;
+
+	if ((tbl != UMAC_PARAM_TBL_CFG_FIX) &&
+	    (tbl != UMAC_PARAM_TBL_FA_CFG_FIX))
+		return -EINVAL;
+
+	umac_cmd.id = UMAC_CMD_OPCODE_SET_PARAM_FIX;
+	umac_cmd.resp = 0;
+
+	param.tbl = cpu_to_le16(tbl);
+	param.key = cpu_to_le16(key);
+	param.value = cpu_to_le32(value);
+
+	return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &param,
+				     sizeof(struct iwm_umac_cmd_set_param_fix));
+}
+
+int iwm_umac_set_config_var(struct iwm_priv *iwm, u16 key,
+			    void *payload, u16 payload_size)
+{
+	struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
+	struct iwm_umac_cmd umac_cmd;
+	struct iwm_umac_cmd_set_param_var *param_hdr;
+	u8 *param;
+	int ret;
+
+	param = kzalloc(payload_size +
+			sizeof(struct iwm_umac_cmd_set_param_var), GFP_KERNEL);
+	if (!param) {
+		IWM_ERR(iwm, "Couldn't allocate param\n");
+		return -ENOMEM;
+	}
+
+	param_hdr = (struct iwm_umac_cmd_set_param_var *)param;
+
+	umac_cmd.id = UMAC_CMD_OPCODE_SET_PARAM_VAR;
+	umac_cmd.resp = 0;
+
+	param_hdr->tbl = cpu_to_le16(UMAC_PARAM_TBL_CFG_VAR);
+	param_hdr->key = cpu_to_le16(key);
+	param_hdr->len = cpu_to_le16(payload_size);
+	memcpy(param + sizeof(struct iwm_umac_cmd_set_param_var),
+	       payload, payload_size);
+
+	ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, param,
+				    sizeof(struct iwm_umac_cmd_set_param_var) +
+				    payload_size);
+	kfree(param);
+
+	return ret;
+}
+
+int iwm_send_umac_config(struct iwm_priv *iwm,
+			 __le32 reset_flags)
+{
+	int ret;
+
+	/* Use UMAC default values */
+	ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
+				      CFG_POWER_INDEX, iwm->conf.power_index);
+	if (ret < 0)
+		return ret;
+
+	ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_FA_CFG_FIX,
+				      CFG_FRAG_THRESHOLD,
+				      iwm->conf.frag_threshold);
+	if (ret < 0)
+		return ret;
+
+	ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
+				      CFG_RTS_THRESHOLD,
+				      iwm->conf.rts_threshold);
+	if (ret < 0)
+		return ret;
+
+	ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
+				      CFG_CTS_TO_SELF, iwm->conf.cts_to_self);
+	if (ret < 0)
+		return ret;
+
+	ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
+				      CFG_COEX_MODE, iwm->conf.coexist_mode);
+	if (ret < 0)
+		return ret;
+
+	/*
+	ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
+				      CFG_ASSOCIATION_TIMEOUT,
+				      iwm->conf.assoc_timeout);
+	if (ret < 0)
+		return ret;
+
+	ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
+				      CFG_ROAM_TIMEOUT,
+				      iwm->conf.roam_timeout);
+	if (ret < 0)
+		return ret;
+
+	ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
+				      CFG_WIRELESS_MODE,
+				      WIRELESS_MODE_11A | WIRELESS_MODE_11G);
+	if (ret < 0)
+		return ret;
+	*/
+
+	ret = iwm_umac_set_config_var(iwm, CFG_NET_ADDR,
+				      iwm_to_ndev(iwm)->dev_addr, ETH_ALEN);
+	if (ret < 0)
+		return ret;
+
+	/* UMAC PM static configurations */
+	ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
+				      CFG_PM_LEGACY_RX_TIMEOUT, 0x12C);
+	if (ret < 0)
+		return ret;
+
+	ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
+				      CFG_PM_LEGACY_TX_TIMEOUT, 0x15E);
+	if (ret < 0)
+		return ret;
+
+	ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
+				      CFG_PM_CTRL_FLAGS, 0x30001);
+	if (ret < 0)
+		return ret;
+
+	ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
+				      CFG_PM_KEEP_ALIVE_IN_BEACONS, 0x80);
+	if (ret < 0)
+		return ret;
+
+	/* reset UMAC */
+	ret = iwm_send_umac_reset(iwm, reset_flags, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_RESET, IWM_SRC_UMAC,
+			       WAIT_NOTIF_TIMEOUT);
+	if (ret) {
+		IWM_ERR(iwm, "Wait for UMAC RESET timeout\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+int iwm_send_packet(struct iwm_priv *iwm, struct sk_buff *skb, int pool_id)
+{
+	struct iwm_udma_wifi_cmd udma_cmd;
+	struct iwm_umac_cmd umac_cmd;
+	struct iwm_tx_info *tx_info = skb_to_tx_info(skb);
+
+	udma_cmd.eop = 1; /* always set eop for non-concatenated Tx */
+	udma_cmd.credit_group = pool_id;
+	udma_cmd.ra_tid = tx_info->sta << 4 | tx_info->tid;
+	udma_cmd.lmac_offset = 0;
+
+	umac_cmd.id = REPLY_TX;
+	umac_cmd.color = tx_info->color;
+	umac_cmd.resp = 0;
+
+	return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd,
+				     skb->data, skb->len);
+}
+
+static int iwm_target_read(struct iwm_priv *iwm, __le32 address,
+			   u8 *response, u32 resp_size)
+{
+	struct iwm_udma_nonwifi_cmd target_cmd;
+	struct iwm_nonwifi_cmd *cmd;
+	u16 seq_num;
+	int ret = 0;
+
+	target_cmd.opcode = UMAC_HDI_OUT_OPCODE_READ;
+	target_cmd.addr = address;
+	target_cmd.op1_sz = cpu_to_le32(resp_size);
+	target_cmd.op2 = 0;
+	target_cmd.handle_by_hw = 0;
+	target_cmd.resp = 1;
+	target_cmd.eop = 1;
+
+	ret = iwm_hal_send_target_cmd(iwm, &target_cmd, NULL);
+	if (ret < 0)
+		IWM_ERR(iwm, "Couldn't send READ command\n");
+
+	/* When succeding, the send_target routine returns the seq number */
+	seq_num = ret;
+
+	ret = wait_event_interruptible_timeout(iwm->nonwifi_queue,
+		(cmd = iwm_get_pending_nonwifi_cmd(iwm, seq_num,
+					  UMAC_HDI_OUT_OPCODE_READ)) != NULL,
+					       2 * HZ);
+
+	if (!ret) {
+		IWM_ERR(iwm, "Didn't receive a target READ answer\n");
+		return ret;
+	}
+
+	memcpy(response, cmd->buf.hdr + sizeof(struct iwm_udma_in_hdr),
+	       resp_size);
+
+	kfree(cmd);
+
+	return ret;
+}
+
+int iwm_read_mac(struct iwm_priv *iwm, u8 *mac)
+{
+	int ret;
+	u8 mac_align[ALIGN(ETH_ALEN, 8)];
+
+	ret = iwm_target_read(iwm, cpu_to_le32(WICO_MAC_ADDRESS_ADDR),
+			      mac_align, sizeof(mac_align));
+	if (ret < 0)
+		return ret;
+
+	if (is_valid_ether_addr(mac_align))
+		memcpy(mac, mac_align, ETH_ALEN);
+	else {
+		IWM_ERR(iwm, "Invalid EEPROM MAC\n");
+		memcpy(mac, iwm->conf.mac_addr, ETH_ALEN);
+		get_random_bytes(&mac[3], 3);
+	}
+
+	return 0;
+}
+
+int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx)
+{
+	struct iwm_umac_tx_key_id tx_key_id;
+
+	if (!iwm->default_key || !iwm->default_key->in_use)
+		return -EINVAL;
+
+	tx_key_id.hdr.oid = UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID;
+	tx_key_id.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_tx_key_id) -
+					     sizeof(struct iwm_umac_wifi_if));
+
+	tx_key_id.key_idx = key_idx;
+
+	return iwm_send_wifi_if_cmd(iwm, &tx_key_id, sizeof(tx_key_id), 1);
+}
+
+static int iwm_check_profile(struct iwm_priv *iwm)
+{
+	if (!iwm->umac_profile_active)
+		return -EAGAIN;
+
+	if (iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_40 &&
+	    iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_104 &&
+	    iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_TKIP &&
+	    iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_CCMP) {
+		IWM_ERR(iwm, "Wrong unicast cipher: 0x%x\n",
+			iwm->umac_profile->sec.ucast_cipher);
+		return -EAGAIN;
+	}
+
+	if (iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_WEP_40 &&
+	    iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_WEP_104 &&
+	    iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_TKIP &&
+	    iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_CCMP) {
+		IWM_ERR(iwm, "Wrong multicast cipher: 0x%x\n",
+			iwm->umac_profile->sec.mcast_cipher);
+		return -EAGAIN;
+	}
+
+	if ((iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_40 ||
+	     iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_104) &&
+	    (iwm->umac_profile->sec.ucast_cipher !=
+	     iwm->umac_profile->sec.mcast_cipher)) {
+		IWM_ERR(iwm, "Unicast and multicast ciphers differ for WEP\n");
+	}
+
+	return 0;
+}
+
+int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
+		struct iwm_key *key)
+{
+	int ret;
+	u8 cmd[64], *sta_addr, *key_data, key_len;
+	s8 key_idx;
+	u16 cmd_size = 0;
+	struct iwm_umac_key_hdr *key_hdr = &key->hdr;
+	struct iwm_umac_key_wep40 *wep40 = (struct iwm_umac_key_wep40 *)cmd;
+	struct iwm_umac_key_wep104 *wep104 = (struct iwm_umac_key_wep104 *)cmd;
+	struct iwm_umac_key_tkip *tkip = (struct iwm_umac_key_tkip *)cmd;
+	struct iwm_umac_key_ccmp *ccmp = (struct iwm_umac_key_ccmp *)cmd;
+
+	if (set_tx_key)
+		iwm->default_key = key;
+
+	/*
+	 * We check if our current profile is valid.
+	 * If not, we dont push the key, we just cache them,
+	 * so that with the next siwsessid call, the keys
+	 * will be actually pushed.
+	 */
+	if (!remove) {
+		ret = iwm_check_profile(iwm);
+		if (ret < 0)
+			return ret;
+	}
+
+	sta_addr = key->hdr.mac;
+	key_data = key->key;
+	key_len = key->key_len;
+	key_idx = key->hdr.key_idx;
+
+	if (!remove) {
+		IWM_DBG_WEXT(iwm, DBG, "key_idx:%d set tx key:%d\n",
+			     key_idx, set_tx_key);
+		IWM_DBG_WEXT(iwm, DBG, "key_len:%d\n", key_len);
+		IWM_DBG_WEXT(iwm, DBG, "MAC:%pM, idx:%d, multicast:%d\n",
+		       key_hdr->mac, key_hdr->key_idx, key_hdr->multicast);
+
+		IWM_DBG_WEXT(iwm, DBG, "profile: mcast:0x%x, ucast:0x%x\n",
+			     iwm->umac_profile->sec.mcast_cipher,
+			     iwm->umac_profile->sec.ucast_cipher);
+		IWM_DBG_WEXT(iwm, DBG, "profile: auth_type:0x%x, flags:0x%x\n",
+			     iwm->umac_profile->sec.auth_type,
+			     iwm->umac_profile->sec.flags);
+
+		switch (key->alg) {
+		case UMAC_CIPHER_TYPE_WEP_40:
+			wep40->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP40_KEY;
+			wep40->hdr.buf_size =
+				cpu_to_le16(sizeof(struct iwm_umac_key_wep40) -
+					    sizeof(struct iwm_umac_wifi_if));
+
+			memcpy(&wep40->key_hdr, key_hdr,
+			       sizeof(struct iwm_umac_key_hdr));
+			memcpy(wep40->key, key_data, key_len);
+			wep40->static_key = 1;
+
+			cmd_size = sizeof(struct iwm_umac_key_wep40);
+			break;
+
+		case UMAC_CIPHER_TYPE_WEP_104:
+			wep104->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP104_KEY;
+			wep104->hdr.buf_size =
+				cpu_to_le16(sizeof(struct iwm_umac_key_wep104) -
+					    sizeof(struct iwm_umac_wifi_if));
+
+			memcpy(&wep104->key_hdr, key_hdr,
+			       sizeof(struct iwm_umac_key_hdr));
+			memcpy(wep104->key, key_data, key_len);
+			wep104->static_key = 1;
+
+			cmd_size = sizeof(struct iwm_umac_key_wep104);
+			break;
+
+		case UMAC_CIPHER_TYPE_CCMP:
+			key_hdr->key_idx++;
+			ccmp->hdr.oid = UMAC_WIFI_IF_CMD_ADD_CCMP_KEY;
+			ccmp->hdr.buf_size =
+				cpu_to_le16(sizeof(struct iwm_umac_key_ccmp) -
+					    sizeof(struct iwm_umac_wifi_if));
+
+			memcpy(&ccmp->key_hdr, key_hdr,
+			       sizeof(struct iwm_umac_key_hdr));
+
+			memcpy(ccmp->key, key_data, key_len);
+
+			if (key->flags & IW_ENCODE_EXT_RX_SEQ_VALID)
+				memcpy(ccmp->iv_count, key->rx_seq, 6);
+
+			cmd_size = sizeof(struct iwm_umac_key_ccmp);
+			break;
+
+		case UMAC_CIPHER_TYPE_TKIP:
+			key_hdr->key_idx++;
+			tkip->hdr.oid = UMAC_WIFI_IF_CMD_ADD_TKIP_KEY;
+			tkip->hdr.buf_size =
+				cpu_to_le16(sizeof(struct iwm_umac_key_tkip) -
+					    sizeof(struct iwm_umac_wifi_if));
+
+			memcpy(&tkip->key_hdr, key_hdr,
+			       sizeof(struct iwm_umac_key_hdr));
+
+			memcpy(tkip->tkip_key, key_data, IWM_TKIP_KEY_SIZE);
+			memcpy(tkip->mic_tx_key, key_data + IWM_TKIP_KEY_SIZE,
+			       IWM_TKIP_MIC_SIZE);
+			memcpy(tkip->mic_rx_key,
+			       key_data + IWM_TKIP_KEY_SIZE + IWM_TKIP_MIC_SIZE,
+			       IWM_TKIP_MIC_SIZE);
+
+			if (key->flags & IW_ENCODE_EXT_RX_SEQ_VALID)
+				memcpy(ccmp->iv_count, key->rx_seq, 6);
+
+			cmd_size = sizeof(struct iwm_umac_key_tkip);
+			break;
+
+		default:
+			return -ENOTSUPP;
+		}
+
+		if ((key->alg == UMAC_CIPHER_TYPE_CCMP) ||
+		    (key->alg == UMAC_CIPHER_TYPE_TKIP))
+			/*
+			 * UGLY_UGLY_UGLY
+			 * Copied HACK from the MWG driver.
+			 * Without it, the key is set before the second
+			 * EAPOL frame is sent, and the latter is thus
+			 * encrypted.
+			 */
+			schedule_timeout_interruptible(usecs_to_jiffies(300));
+
+		ret =  iwm_send_wifi_if_cmd(iwm, cmd, cmd_size, 1);
+		if (ret < 0)
+			goto err;
+
+		/*
+		 * We need a default key only if it is set and
+		 * if we're doing WEP.
+		 */
+		if (iwm->default_key == key &&
+			((key->alg == UMAC_CIPHER_TYPE_WEP_40) ||
+			 (key->alg == UMAC_CIPHER_TYPE_WEP_104))) {
+			ret = iwm_set_tx_key(iwm, key_idx);
+			if (ret < 0)
+				goto err;
+		}
+	} else {
+		struct iwm_umac_key_remove key_remove;
+
+		key_remove.hdr.oid = UMAC_WIFI_IF_CMD_REMOVE_KEY;
+		key_remove.hdr.buf_size =
+			cpu_to_le16(sizeof(struct iwm_umac_key_remove) -
+				    sizeof(struct iwm_umac_wifi_if));
+		memcpy(&key_remove.key_hdr, key_hdr,
+		       sizeof(struct iwm_umac_key_hdr));
+
+		ret =  iwm_send_wifi_if_cmd(iwm, &key_remove,
+					    sizeof(struct iwm_umac_key_remove),
+					    1);
+		if (ret < 0)
+			return ret;
+
+		iwm->keys[key_idx].in_use = 0;
+	}
+
+	return 0;
+
+ err:
+	kfree(key);
+	return ret;
+}
+
+
+int iwm_send_mlme_profile(struct iwm_priv *iwm)
+{
+	int ret, i;
+	struct iwm_umac_profile profile;
+
+	memcpy(&profile, iwm->umac_profile, sizeof(profile));
+
+	profile.hdr.oid = UMAC_WIFI_IF_CMD_SET_PROFILE;
+	profile.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_profile) -
+					   sizeof(struct iwm_umac_wifi_if));
+
+	ret = iwm_send_wifi_if_cmd(iwm, &profile, sizeof(profile), 1);
+	if (ret < 0) {
+		IWM_ERR(iwm, "Send profile command failed\n");
+		return ret;
+	}
+
+	/* Wait for the profile to be active */
+	ret = wait_event_interruptible_timeout(iwm->mlme_queue,
+					       iwm->umac_profile_active == 1,
+					       3 * HZ);
+	if (!ret)
+		return -EBUSY;
+
+
+	for (i = 0; i < IWM_NUM_KEYS; i++)
+		if (iwm->keys[i].in_use) {
+			int default_key = 0;
+			struct iwm_key *key = &iwm->keys[i];
+
+			if (key == iwm->default_key)
+				default_key = 1;
+
+			/* Wait for the profile before sending the keys */
+			wait_event_interruptible_timeout(iwm->mlme_queue,
+			     (test_bit(IWM_STATUS_ASSOCIATING, &iwm->status) ||
+			      test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)),
+							 3 * HZ);
+
+			ret = iwm_set_key(iwm, 0, default_key, key);
+			if (ret < 0)
+				return ret;
+		}
+
+	return 0;
+}
+
+int iwm_invalidate_mlme_profile(struct iwm_priv *iwm)
+{
+	int ret;
+	struct iwm_umac_invalidate_profile invalid;
+
+	invalid.hdr.oid = UMAC_WIFI_IF_CMD_INVALIDATE_PROFILE;
+	invalid.hdr.buf_size =
+		cpu_to_le16(sizeof(struct iwm_umac_invalidate_profile) -
+			    sizeof(struct iwm_umac_wifi_if));
+
+	invalid.reason = WLAN_REASON_UNSPECIFIED;
+
+	ret = iwm_send_wifi_if_cmd(iwm, &invalid, sizeof(invalid), 1);
+	if (ret < 0)
+		return ret;
+
+	ret = wait_event_interruptible_timeout(iwm->mlme_queue,
+				 (iwm->umac_profile_active == 0),
+					       2 * HZ);
+	if (!ret)
+		return -EBUSY;
+
+	return 0;
+}
+
+int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags)
+{
+	struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
+	struct iwm_umac_cmd umac_cmd;
+	struct iwm_umac_cmd_stats_req stats_req;
+
+	stats_req.flags = cpu_to_le32(flags);
+
+	umac_cmd.id = UMAC_CMD_OPCODE_STATISTIC_REQUEST;
+	umac_cmd.resp = 0;
+
+	return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &stats_req,
+				     sizeof(struct iwm_umac_cmd_stats_req));
+}
+
+int iwm_send_umac_channel_list(struct iwm_priv *iwm)
+{
+	struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
+	struct iwm_umac_cmd umac_cmd;
+	struct iwm_umac_cmd_get_channel_list *ch_list;
+	int size = sizeof(struct iwm_umac_cmd_get_channel_list) +
+		   sizeof(struct iwm_umac_channel_info) * 4;
+	int ret;
+
+	ch_list = kzalloc(size, GFP_KERNEL);
+	if (!ch_list) {
+		IWM_ERR(iwm, "Couldn't allocate channel list cmd\n");
+		return -ENOMEM;
+	}
+
+	ch_list->ch[0].band = UMAC_BAND_2GHZ;
+	ch_list->ch[0].type = UMAC_CHANNEL_WIDTH_20MHZ;
+	ch_list->ch[0].flags = UMAC_CHANNEL_FLAG_VALID;
+
+	ch_list->ch[1].band = UMAC_BAND_5GHZ;
+	ch_list->ch[1].type = UMAC_CHANNEL_WIDTH_20MHZ;
+	ch_list->ch[1].flags = UMAC_CHANNEL_FLAG_VALID;
+
+	ch_list->ch[2].band = UMAC_BAND_2GHZ;
+	ch_list->ch[2].type = UMAC_CHANNEL_WIDTH_20MHZ;
+	ch_list->ch[2].flags = UMAC_CHANNEL_FLAG_VALID | UMAC_CHANNEL_FLAG_IBSS;
+
+	ch_list->ch[3].band = UMAC_BAND_5GHZ;
+	ch_list->ch[3].type = UMAC_CHANNEL_WIDTH_20MHZ;
+	ch_list->ch[3].flags = UMAC_CHANNEL_FLAG_VALID | UMAC_CHANNEL_FLAG_IBSS;
+
+	ch_list->count = cpu_to_le16(4);
+
+	umac_cmd.id = UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST;
+	umac_cmd.resp = 1;
+
+	ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, ch_list, size);
+
+	kfree(ch_list);
+
+	return ret;
+}
+
+int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids,
+		   int ssid_num)
+{
+	struct iwm_umac_cmd_scan_request req;
+	int i, ret;
+
+	memset(&req, 0, sizeof(struct iwm_umac_cmd_scan_request));
+
+	req.hdr.oid = UMAC_WIFI_IF_CMD_SCAN_REQUEST;
+	req.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_cmd_scan_request)
+				       - sizeof(struct iwm_umac_wifi_if));
+	req.type = UMAC_WIFI_IF_SCAN_TYPE_USER;
+	req.timeout = 2;
+	req.seq_num = iwm->scan_id;
+	req.ssid_num = min(ssid_num, UMAC_WIFI_IF_PROBE_OPTION_MAX);
+
+	for (i = 0; i < req.ssid_num; i++) {
+		memcpy(req.ssids[i].ssid, ssids[i].ssid, ssids[i].ssid_len);
+		req.ssids[i].ssid_len = ssids[i].ssid_len;
+	}
+
+	ret = iwm_send_wifi_if_cmd(iwm, &req, sizeof(req), 0);
+	if (ret < 0) {
+		IWM_ERR(iwm, "Couldn't send scan request\n");
+		return ret;
+	}
+
+	iwm->scan_id = iwm->scan_id++ % IWM_SCAN_ID_MAX;
+
+	return 0;
+}
+
+int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len)
+{
+	struct cfg80211_ssid one_ssid;
+
+	if (test_and_set_bit(IWM_STATUS_SCANNING, &iwm->status))
+		return 0;
+
+	one_ssid.ssid_len = min(ssid_len, IEEE80211_MAX_SSID_LEN);
+	memcpy(&one_ssid.ssid, ssid, one_ssid.ssid_len);
+
+	return iwm_scan_ssids(iwm, &one_ssid, 1);
+}
+
+int iwm_target_reset(struct iwm_priv *iwm)
+{
+	struct iwm_udma_nonwifi_cmd target_cmd;
+
+	target_cmd.opcode = UMAC_HDI_OUT_OPCODE_REBOOT;
+	target_cmd.addr = 0;
+	target_cmd.op1_sz = 0;
+	target_cmd.op2 = 0;
+	target_cmd.handle_by_hw = 0;
+	target_cmd.resp = 0;
+	target_cmd.eop = 1;
+
+	return iwm_hal_send_target_cmd(iwm, &target_cmd, NULL);
+}
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.h b/drivers/net/wireless/iwmc3200wifi/commands.h
new file mode 100644
index 0000000..36b13a1
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/commands.h
@@ -0,0 +1,419 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 of 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.
+ *
+ *
+ * Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ */
+
+#ifndef __IWM_COMMANDS_H__
+#define __IWM_COMMANDS_H__
+
+#include <linux/ieee80211.h>
+
+#define IWM_BARKER_REBOOT_NOTIFICATION	0xF
+#define IWM_ACK_BARKER_NOTIFICATION	0x10
+
+/* UMAC commands */
+#define UMAC_RST_CTRL_FLG_LARC_CLK_EN	0x0001
+#define UMAC_RST_CTRL_FLG_LARC_RESET	0x0002
+#define UMAC_RST_CTRL_FLG_FUNC_RESET	0x0004
+#define UMAC_RST_CTRL_FLG_DEV_RESET	0x0008
+#define UMAC_RST_CTRL_FLG_WIFI_CORE_EN	0x0010
+#define UMAC_RST_CTRL_FLG_WIFI_LINK_EN	0x0040
+#define UMAC_RST_CTRL_FLG_WIFI_MLME_EN	0x0080
+#define UMAC_RST_CTRL_FLG_NVM_RELOAD	0x0100
+
+struct iwm_umac_cmd_reset {
+	__le32 flags;
+} __attribute__ ((packed));
+
+#define UMAC_PARAM_TBL_ORD_FIX    0x0
+#define UMAC_PARAM_TBL_ORD_VAR    0x1
+#define UMAC_PARAM_TBL_CFG_FIX    0x2
+#define UMAC_PARAM_TBL_CFG_VAR    0x3
+#define UMAC_PARAM_TBL_BSS_TRK    0x4
+#define UMAC_PARAM_TBL_FA_CFG_FIX 0x5
+#define UMAC_PARAM_TBL_STA        0x6
+#define UMAC_PARAM_TBL_CHN        0x7
+#define UMAC_PARAM_TBL_STATISTICS 0x8
+
+/* fast access table */
+enum {
+	CFG_FRAG_THRESHOLD = 0,
+	CFG_FRAME_RETRY_LIMIT,
+	CFG_OS_QUEUE_UTIL_TH,
+	CFG_RX_FILTER,
+	/* <-- LAST --> */
+	FAST_ACCESS_CFG_TBL_FIX_LAST
+};
+
+/* fixed size table */
+enum {
+	CFG_POWER_INDEX = 0,
+	CFG_PM_LEGACY_RX_TIMEOUT,
+	CFG_PM_LEGACY_TX_TIMEOUT,
+	CFG_PM_CTRL_FLAGS,
+	CFG_PM_KEEP_ALIVE_IN_BEACONS,
+	CFG_BT_ON_THRESHOLD,
+	CFG_RTS_THRESHOLD,
+	CFG_CTS_TO_SELF,
+	CFG_COEX_MODE,
+	CFG_WIRELESS_MODE,
+	CFG_ASSOCIATION_TIMEOUT,
+	CFG_ROAM_TIMEOUT,
+	CFG_CAPABILITY_SUPPORTED_RATES,
+	CFG_SCAN_ALLOWED_UNASSOC_FLAGS,
+	CFG_SCAN_ALLOWED_MAIN_ASSOC_FLAGS,
+	CFG_SCAN_ALLOWED_PAN_ASSOC_FLAGS,
+	CFG_SCAN_INTERNAL_PERIODIC_ENABLED,
+	CFG_SCAN_IMM_INTERNAL_PERIODIC_SCAN_ON_INIT,
+	CFG_SCAN_DEFAULT_PERIODIC_FREQ_SEC,
+	CFG_SCAN_NUM_PASSIVE_CHAN_PER_PARTIAL_SCAN,
+	CFG_TLC_SUPPORTED_TX_HT_RATES,
+	CFG_TLC_SUPPORTED_TX_RATES,
+	CFG_TLC_VALID_ANTENNA,
+	CFG_TLC_SPATIAL_STREAM_SUPPORTED,
+	CFG_TLC_RETRY_PER_RATE,
+	CFG_TLC_RETRY_PER_HT_RATE,
+	CFG_TLC_FIXED_RATE,
+	CFG_TLC_FIXED_RATE_FLAGS,
+	CFG_TLC_CONTROL_FLAGS,
+	CFG_TLC_SR_MIN_FAIL,
+	CFG_TLC_SR_MIN_PASS,
+	CFG_TLC_HT_STAY_IN_COL_PASS_THRESH,
+	CFG_TLC_HT_STAY_IN_COL_FAIL_THRESH,
+	CFG_TLC_LEGACY_STAY_IN_COL_PASS_THRESH,
+	CFG_TLC_LEGACY_STAY_IN_COL_FAIL_THRESH,
+	CFG_TLC_HT_FLUSH_STATS_PACKETS,
+	CFG_TLC_LEGACY_FLUSH_STATS_PACKETS,
+	CFG_TLC_LEGACY_FLUSH_STATS_MS,
+	CFG_TLC_HT_FLUSH_STATS_MS,
+	CFG_TLC_STAY_IN_COL_TIME_OUT,
+	CFG_TLC_AGG_SHORT_LIM,
+	CFG_TLC_AGG_LONG_LIM,
+	CFG_TLC_HT_SR_NO_DECREASE,
+	CFG_TLC_LEGACY_SR_NO_DECREASE,
+	CFG_TLC_SR_FORCE_DECREASE,
+	CFG_TLC_SR_ALLOW_INCREASE,
+	CFG_TLC_AGG_SET_LONG,
+	CFG_TLC_AUTO_AGGREGATION,
+	CFG_TLC_AGG_THRESHOLD,
+	CFG_TLC_TID_LOAD_THRESHOLD,
+	CFG_TLC_BLOCK_ACK_TIMEOUT,
+	CFG_TLC_NO_BA_COUNTED_AS_ONE,
+	CFG_TLC_NUM_BA_STREAMS_ALLOWED,
+	CFG_TLC_NUM_BA_STREAMS_PRESENT,
+	CFG_TLC_RENEW_ADDBA_DELAY,
+	CFG_TLC_NUM_OF_MULTISEC_TO_COUN_LOAD,
+	CFG_TLC_IS_STABLE_IN_HT,
+	CFG_RLC_CHAIN_CTRL,
+	CFG_TRK_TABLE_OP_MODE,
+	CFG_TRK_TABLE_RSSI_THRESHOLD,
+	CFG_TX_PWR_TARGET, /* Used By xVT */
+	CFG_TX_PWR_LIMIT_USR,
+	CFG_TX_PWR_LIMIT_BSS, /* 11d limit */
+	CFG_TX_PWR_LIMIT_BSS_CONSTRAINT, /* 11h constraint */
+	CFG_TX_PWR_MODE,
+	CFG_MLME_DBG_NOTIF_BLOCK,
+	CFG_BT_OFF_BECONS_INTERVALS,
+	CFG_BT_FRAG_DURATION,
+
+	/* <-- LAST --> */
+	CFG_TBL_FIX_LAST
+};
+
+/* variable size table */
+enum {
+	CFG_NET_ADDR = 0,
+	CFG_PROFILE,
+	/* <-- LAST --> */
+	CFG_TBL_VAR_LAST
+};
+
+struct iwm_umac_cmd_set_param_fix {
+	__le16 tbl;
+	__le16 key;
+	__le32 value;
+} __attribute__ ((packed));
+
+struct iwm_umac_cmd_set_param_var {
+	__le16 tbl;
+	__le16 key;
+	__le16 len;
+	__le16 reserved;
+} __attribute__ ((packed));
+
+struct iwm_umac_cmd_get_param {
+	__le16 tbl;
+	__le16 key;
+} __attribute__ ((packed));
+
+struct iwm_umac_cmd_get_param_resp {
+	__le16 tbl;
+	__le16 key;
+	__le16 len;
+	__le16 reserved;
+} __attribute__ ((packed));
+
+struct iwm_umac_cmd_eeprom_proxy_hdr {
+	__le32 type;
+	__le32 offset;
+	__le32 len;
+} __attribute__ ((packed));
+
+struct iwm_umac_cmd_eeprom_proxy {
+	struct iwm_umac_cmd_eeprom_proxy_hdr hdr;
+	u8 buf[0];
+} __attribute__ ((packed));
+
+#define IWM_UMAC_CMD_EEPROM_TYPE_READ       0x1
+#define IWM_UMAC_CMD_EEPROM_TYPE_WRITE      0x2
+
+#define UMAC_CHANNEL_FLAG_VALID		BIT(0)
+#define UMAC_CHANNEL_FLAG_IBSS		BIT(1)
+#define UMAC_CHANNEL_FLAG_ACTIVE	BIT(3)
+#define UMAC_CHANNEL_FLAG_RADAR		BIT(4)
+#define UMAC_CHANNEL_FLAG_DFS		BIT(7)
+
+struct iwm_umac_channel_info {
+	u8 band;
+	u8 type;
+	u8 reserved;
+	u8 flags;
+	__le32 channels_mask;
+} __attribute__ ((packed));
+
+struct iwm_umac_cmd_get_channel_list {
+	__le16 count;
+	__le16 reserved;
+	struct iwm_umac_channel_info ch[0];
+} __attribute__ ((packed));
+
+
+/* UMAC WiFi interface commands */
+
+/* Coexistence mode */
+#define COEX_MODE_SA  0x1
+#define COEX_MODE_XOR 0x2
+#define COEX_MODE_CM  0x3
+#define COEX_MODE_MAX 0x4
+
+/* Wireless mode */
+#define WIRELESS_MODE_11A  0x1
+#define WIRELESS_MODE_11G  0x2
+
+#define UMAC_PROFILE_EX_IE_REQUIRED	0x1
+#define UMAC_PROFILE_QOS_ALLOWED	0x2
+
+/* Scanning */
+#define UMAC_WIFI_IF_PROBE_OPTION_MAX        10
+
+#define UMAC_WIFI_IF_SCAN_TYPE_USER          0x0
+#define UMAC_WIFI_IF_SCAN_TYPE_UMAC_RESERVED 0x1
+#define UMAC_WIFI_IF_SCAN_TYPE_HOST_PERIODIC 0x2
+#define UMAC_WIFI_IF_SCAN_TYPE_MAX           0x3
+
+struct iwm_umac_ssid {
+	u8 ssid_len;
+	u8 ssid[IEEE80211_MAX_SSID_LEN];
+	u8 reserved[3];
+} __attribute__ ((packed));
+
+struct iwm_umac_cmd_scan_request {
+	struct iwm_umac_wifi_if hdr;
+	__le32 type; /* UMAC_WIFI_IF_SCAN_TYPE_* */
+	u8 ssid_num;
+	u8 seq_num;
+	u8 timeout; /* In seconds */
+	u8 reserved;
+	struct iwm_umac_ssid ssids[UMAC_WIFI_IF_PROBE_OPTION_MAX];
+} __attribute__ ((packed));
+
+#define UMAC_CIPHER_TYPE_NONE		0xFF
+#define UMAC_CIPHER_TYPE_USE_GROUPCAST	0x00
+#define UMAC_CIPHER_TYPE_WEP_40		0x01
+#define UMAC_CIPHER_TYPE_WEP_104	0x02
+#define UMAC_CIPHER_TYPE_TKIP		0x04
+#define UMAC_CIPHER_TYPE_CCMP		0x08
+
+/* Supported authentication types - bitmap */
+#define UMAC_AUTH_TYPE_OPEN		0x00
+#define UMAC_AUTH_TYPE_LEGACY_PSK	0x01
+#define UMAC_AUTH_TYPE_8021X		0x02
+#define UMAC_AUTH_TYPE_RSNA_PSK		0x04
+
+/* iwm_umac_security.flag is WPA supported -- bits[0:0] */
+#define UMAC_SEC_FLG_WPA_ON_POS		0
+#define UMAC_SEC_FLG_WPA_ON_SEED	1
+#define UMAC_SEC_FLG_WPA_ON_MSK (UMAC_SEC_FLG_WPA_ON_SEED << \
+				 UMAC_SEC_FLG_WPA_ON_POS)
+
+/* iwm_umac_security.flag is WPA2 supported -- bits [1:1] */
+#define UMAC_SEC_FLG_RSNA_ON_POS	1
+#define UMAC_SEC_FLG_RSNA_ON_SEED	1
+#define UMAC_SEC_FLG_RSNA_ON_MSK        (UMAC_SEC_FLG_RSNA_ON_SEED << \
+					 UMAC_SEC_FLG_RSNA_ON_POS)
+
+/* iwm_umac_security.flag is WSC mode on -- bits [2:2] */
+#define UMAC_SEC_FLG_WSC_ON_POS		2
+#define UMAC_SEC_FLG_WSC_ON_SEED	1
+
+/* Legacy profile can use only WEP40 and WEP104 for encryption and
+ * OPEN or PSK for authentication */
+#define UMAC_SEC_FLG_LEGACY_PROFILE	0
+
+struct iwm_umac_security {
+	u8 auth_type;
+	u8 ucast_cipher;
+	u8 mcast_cipher;
+	u8 flags;
+} __attribute__ ((packed));
+
+struct iwm_umac_ibss {
+	u8 beacon_interval;	/* in millisecond */
+	u8 atim;		/* in millisecond */
+	s8 join_only;
+	u8 band;
+	u8 channel;
+	u8 reserved[3];
+} __attribute__ ((packed));
+
+#define UMAC_MODE_BSS	0
+#define UMAC_MODE_IBSS	1
+
+#define UMAC_BSSID_MAX	4
+
+struct iwm_umac_profile {
+	struct iwm_umac_wifi_if hdr;
+	__le32 mode;
+	struct iwm_umac_ssid ssid;
+	u8 bssid[UMAC_BSSID_MAX][ETH_ALEN];
+	struct iwm_umac_security sec;
+	struct iwm_umac_ibss ibss;
+	__le32 channel_2ghz;
+	__le32 channel_5ghz;
+	__le16 flags;
+	u8 wireless_mode;
+	u8 bss_num;
+} __attribute__ ((packed));
+
+struct iwm_umac_invalidate_profile {
+	struct iwm_umac_wifi_if hdr;
+	u8 reason;
+	u8 reserved[3];
+} __attribute__ ((packed));
+
+/* Encryption key commands */
+struct iwm_umac_key_wep40 {
+	struct iwm_umac_wifi_if hdr;
+	struct iwm_umac_key_hdr key_hdr;
+	u8 key[WLAN_KEY_LEN_WEP40];
+	u8 static_key;
+	u8 reserved[2];
+} __attribute__ ((packed));
+
+struct iwm_umac_key_wep104 {
+	struct iwm_umac_wifi_if hdr;
+	struct iwm_umac_key_hdr key_hdr;
+	u8 key[WLAN_KEY_LEN_WEP104];
+	u8 static_key;
+	u8 reserved[2];
+} __attribute__ ((packed));
+
+#define IWM_TKIP_KEY_SIZE 16
+#define IWM_TKIP_MIC_SIZE 8
+struct iwm_umac_key_tkip {
+	struct iwm_umac_wifi_if hdr;
+	struct iwm_umac_key_hdr key_hdr;
+	u8 iv_count[6];
+	u8 reserved[2];
+	u8 tkip_key[IWM_TKIP_KEY_SIZE];
+	u8 mic_rx_key[IWM_TKIP_MIC_SIZE];
+	u8 mic_tx_key[IWM_TKIP_MIC_SIZE];
+} __attribute__ ((packed));
+
+struct iwm_umac_key_ccmp {
+	struct iwm_umac_wifi_if hdr;
+	struct iwm_umac_key_hdr key_hdr;
+	u8 iv_count[6];
+	u8 reserved[2];
+	u8 key[WLAN_KEY_LEN_CCMP];
+} __attribute__ ((packed));
+
+struct iwm_umac_key_remove {
+	struct iwm_umac_wifi_if hdr;
+	struct iwm_umac_key_hdr key_hdr;
+} __attribute__ ((packed));
+
+struct iwm_umac_tx_key_id {
+	struct iwm_umac_wifi_if hdr;
+	u8 key_idx;
+	u8 reserved[3];
+} __attribute__ ((packed));
+
+struct iwm_umac_cmd_stats_req {
+	__le32 flags;
+} __attribute__ ((packed));
+
+/* LMAC commands */
+int iwm_read_mac(struct iwm_priv *iwm, u8 *mac);
+int iwm_send_prio_table(struct iwm_priv *iwm);
+int iwm_send_init_calib_cfg(struct iwm_priv *iwm, u8 calib_requested);
+int iwm_send_periodic_calib_cfg(struct iwm_priv *iwm, u8 calib_requested);
+int iwm_send_calib_results(struct iwm_priv *iwm);
+int iwm_store_rxiq_calib_result(struct iwm_priv *iwm);
+
+/* UMAC commands */
+int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size,
+			 bool resp);
+int iwm_send_umac_reset(struct iwm_priv *iwm, __le32 reset_flags, bool resp);
+int iwm_umac_set_config_fix(struct iwm_priv *iwm, u16 tbl, u16 key, u32 value);
+int iwm_umac_set_config_var(struct iwm_priv *iwm, u16 key,
+			    void *payload, u16 payload_size);
+int iwm_send_umac_config(struct iwm_priv *iwm, __le32 reset_flags);
+int iwm_send_mlme_profile(struct iwm_priv *iwm);
+int iwm_invalidate_mlme_profile(struct iwm_priv *iwm);
+int iwm_send_packet(struct iwm_priv *iwm, struct sk_buff *skb, int pool_id);
+int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx);
+int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
+		struct iwm_key *key);
+int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags);
+int iwm_send_umac_channel_list(struct iwm_priv *iwm);
+int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids,
+		   int ssid_num);
+int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len);
+
+/* UDMA commands */
+int iwm_target_reset(struct iwm_priv *iwm);
+#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/debug.h b/drivers/net/wireless/iwmc3200wifi/debug.h
new file mode 100644
index 0000000..8fbb42d
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/debug.h
@@ -0,0 +1,124 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 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-1301, USA.
+ *
+ */
+
+#ifndef __IWM_DEBUG_H__
+#define __IWM_DEBUG_H__
+
+#define IWM_ERR(p, f, a...) dev_err(iwm_to_dev(p), f, ## a)
+#define IWM_WARN(p, f, a...) dev_warn(iwm_to_dev(p), f, ## a)
+#define IWM_INFO(p, f, a...) dev_info(iwm_to_dev(p), f, ## a)
+#define IWM_CRIT(p, f, a...) dev_crit(iwm_to_dev(p), f, ## a)
+
+#ifdef CONFIG_IWM_DEBUG
+
+#define IWM_DEBUG_MODULE(i, level, module, f, a...)			     \
+do {									     \
+	if (unlikely(i->dbg.dbg_module[IWM_DM_##module] >= (IWM_DL_##level)))\
+		dev_printk(KERN_INFO, (iwm_to_dev(i)),	             \
+			   "%s " f, __func__ , ## a);			     \
+} while (0)
+
+#define IWM_HEXDUMP(i, level, module, pref, buf, len)		             \
+do {									     \
+	if (unlikely(i->dbg.dbg_module[IWM_DM_##module] >= (IWM_DL_##level)))\
+		print_hex_dump(KERN_INFO, pref, DUMP_PREFIX_OFFSET,	     \
+			       16, 1, buf, len, 1);			     \
+} while (0)
+
+#else
+
+#define IWM_DEBUG_MODULE(i, level, module, f, a...)
+#define IWM_HEXDUMP(i, level, module, pref, buf, len)
+
+#endif /* CONFIG_IWM_DEBUG */
+
+/* Debug modules */
+enum iwm_debug_module_id {
+	IWM_DM_BOOT = 0,
+	IWM_DM_FW,
+	IWM_DM_SDIO,
+	IWM_DM_NTF,
+	IWM_DM_RX,
+	IWM_DM_TX,
+	IWM_DM_MLME,
+	IWM_DM_CMD,
+	IWM_DM_WEXT,
+	__IWM_DM_NR,
+};
+#define IWM_DM_DEFAULT 0
+
+#define IWM_DBG_BOOT(i, l, f, a...) IWM_DEBUG_MODULE(i, l, BOOT, f, ## a)
+#define IWM_DBG_FW(i, l, f, a...)   IWM_DEBUG_MODULE(i, l, FW, f, ## a)
+#define IWM_DBG_SDIO(i, l, f, a...) IWM_DEBUG_MODULE(i, l, SDIO, f, ## a)
+#define IWM_DBG_NTF(i, l, f, a...)  IWM_DEBUG_MODULE(i, l, NTF, f, ## a)
+#define IWM_DBG_RX(i, l, f, a...)   IWM_DEBUG_MODULE(i, l, RX, f, ## a)
+#define IWM_DBG_TX(i, l, f, a...)   IWM_DEBUG_MODULE(i, l, TX, f, ## a)
+#define IWM_DBG_MLME(i, l, f, a...) IWM_DEBUG_MODULE(i, l, MLME, f, ## a)
+#define IWM_DBG_CMD(i, l, f, a...)  IWM_DEBUG_MODULE(i, l, CMD, f, ## a)
+#define IWM_DBG_WEXT(i, l, f, a...) IWM_DEBUG_MODULE(i, l, WEXT, f, ## a)
+
+/* Debug levels */
+enum iwm_debug_level {
+	IWM_DL_NONE = 0,
+	IWM_DL_ERR,
+	IWM_DL_WARN,
+	IWM_DL_INFO,
+	IWM_DL_DBG,
+};
+#define IWM_DL_DEFAULT IWM_DL_ERR
+
+struct iwm_debugfs {
+	struct iwm_priv *iwm;
+	struct dentry *rootdir;
+	struct dentry *devdir;
+	struct dentry *dbgdir;
+	struct dentry *txdir;
+	struct dentry *rxdir;
+	struct dentry *busdir;
+
+	u32 dbg_level;
+	struct dentry *dbg_level_dentry;
+
+	unsigned long dbg_modules;
+	struct dentry *dbg_modules_dentry;
+
+	u8 dbg_module[__IWM_DM_NR];
+	struct dentry *dbg_module_dentries[__IWM_DM_NR];
+
+	struct dentry *txq_dentry;
+	struct dentry *tx_credit_dentry;
+	struct dentry *rx_ticket_dentry;
+};
+
+#ifdef CONFIG_IWM_DEBUG
+int iwm_debugfs_init(struct iwm_priv *iwm);
+void iwm_debugfs_exit(struct iwm_priv *iwm);
+#else
+static inline int iwm_debugfs_init(struct iwm_priv *iwm)
+{
+	return 0;
+}
+static inline void iwm_debugfs_exit(struct iwm_priv *iwm) {}
+#endif
+
+#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/debugfs.c b/drivers/net/wireless/iwmc3200wifi/debugfs.c
new file mode 100644
index 0000000..0fa7b91
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/debugfs.c
@@ -0,0 +1,453 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 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-1301, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+
+#include "iwm.h"
+#include "bus.h"
+#include "rx.h"
+#include "debug.h"
+
+static struct {
+	u8 id;
+	char *name;
+} iwm_debug_module[__IWM_DM_NR] = {
+	 {IWM_DM_BOOT, "boot"},
+	 {IWM_DM_FW,   "fw"},
+	 {IWM_DM_SDIO, "sdio"},
+	 {IWM_DM_NTF,  "ntf"},
+	 {IWM_DM_RX,   "rx"},
+	 {IWM_DM_TX,   "tx"},
+	 {IWM_DM_MLME, "mlme"},
+	 {IWM_DM_CMD,  "cmd"},
+	 {IWM_DM_WEXT,  "wext"},
+};
+
+#define add_dbg_module(dbg, name, id, initlevel) 	\
+do {							\
+	struct dentry *d;				\
+	dbg.dbg_module[id] = (initlevel);		\
+	d = debugfs_create_x8(name, 0600, dbg.dbgdir,	\
+			     &(dbg.dbg_module[id]));	\
+	if (!IS_ERR(d))					\
+		dbg.dbg_module_dentries[id] = d;        \
+} while (0)
+
+static int iwm_debugfs_u32_read(void *data, u64 *val)
+{
+	struct iwm_priv *iwm = data;
+
+	*val = iwm->dbg.dbg_level;
+	return 0;
+}
+
+static int iwm_debugfs_dbg_level_write(void *data, u64 val)
+{
+	struct iwm_priv *iwm = data;
+	int i;
+
+	iwm->dbg.dbg_level = val;
+
+	for (i = 0; i < __IWM_DM_NR; i++)
+		iwm->dbg.dbg_module[i] = val;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_level,
+			iwm_debugfs_u32_read, iwm_debugfs_dbg_level_write,
+			"%llu\n");
+
+static int iwm_debugfs_dbg_modules_write(void *data, u64 val)
+{
+	struct iwm_priv *iwm = data;
+	int i, bit;
+
+	iwm->dbg.dbg_modules = val;
+
+	for (i = 0; i < __IWM_DM_NR; i++)
+		iwm->dbg.dbg_module[i] = 0;
+
+	for_each_bit(bit, &iwm->dbg.dbg_modules, __IWM_DM_NR)
+		iwm->dbg.dbg_module[bit] = iwm->dbg.dbg_level;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_modules,
+			iwm_debugfs_u32_read, iwm_debugfs_dbg_modules_write,
+			"%llu\n");
+
+static int iwm_txrx_open(struct inode *inode, struct file *filp)
+{
+	filp->private_data = inode->i_private;
+	return 0;
+}
+
+
+static ssize_t iwm_debugfs_txq_read(struct file *filp, char __user *buffer,
+				   size_t count, loff_t *ppos)
+{
+	struct iwm_priv *iwm = filp->private_data;
+	char *buf;
+	int i, buf_len = 4096;
+	size_t len = 0;
+	ssize_t ret;
+
+	if (*ppos != 0)
+		return 0;
+	if (count < sizeof(buf))
+		return -ENOSPC;
+
+	buf = kzalloc(buf_len, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < IWM_TX_QUEUES; i++) {
+		struct iwm_tx_queue *txq = &iwm->txq[i];
+		struct sk_buff *skb;
+		int j;
+		unsigned long flags;
+
+		spin_lock_irqsave(&txq->queue.lock, flags);
+
+		skb = (struct sk_buff *)&txq->queue;
+
+		len += snprintf(buf + len, buf_len - len, "TXQ #%d\n", i);
+		len += snprintf(buf + len, buf_len - len, "\tStopped:     %d\n",
+				__netif_subqueue_stopped(iwm_to_ndev(iwm),
+							 txq->id));
+		len += snprintf(buf + len, buf_len - len, "\tConcat count:%d\n",
+				txq->concat_count);
+		len += snprintf(buf + len, buf_len - len, "\tQueue len:   %d\n",
+				skb_queue_len(&txq->queue));
+		for (j = 0; j < skb_queue_len(&txq->queue); j++) {
+			struct iwm_tx_info *tx_info;
+
+			skb = skb->next;
+			tx_info = skb_to_tx_info(skb);
+
+			len += snprintf(buf + len, buf_len - len,
+					"\tSKB #%d\n", j);
+			len += snprintf(buf + len, buf_len - len,
+					"\t\tsta:   %d\n", tx_info->sta);
+			len += snprintf(buf + len, buf_len - len,
+					"\t\tcolor: %d\n", tx_info->color);
+			len += snprintf(buf + len, buf_len - len,
+					"\t\ttid:   %d\n", tx_info->tid);
+		}
+
+		spin_unlock_irqrestore(&txq->queue.lock, flags);
+	}
+
+	ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t iwm_debugfs_tx_credit_read(struct file *filp,
+					  char __user *buffer,
+					  size_t count, loff_t *ppos)
+{
+	struct iwm_priv *iwm = filp->private_data;
+	struct iwm_tx_credit *credit = &iwm->tx_credit;
+	char *buf;
+	int i, buf_len = 4096;
+	size_t len = 0;
+	ssize_t ret;
+
+	if (*ppos != 0)
+		return 0;
+	if (count < sizeof(buf))
+		return -ENOSPC;
+
+	buf = kzalloc(buf_len, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	len += snprintf(buf + len, buf_len - len,
+			"NR pools:  %d\n", credit->pool_nr);
+	len += snprintf(buf + len, buf_len - len,
+			"pools map: 0x%lx\n", credit->full_pools_map);
+
+	len += snprintf(buf + len, buf_len - len, "\n### POOLS ###\n");
+	for (i = 0; i < IWM_MACS_OUT_GROUPS; i++) {
+		len += snprintf(buf + len, buf_len - len,
+				"pools entry #%d\n", i);
+		len += snprintf(buf + len, buf_len - len,
+				"\tid:          %d\n",
+				credit->pools[i].id);
+		len += snprintf(buf + len, buf_len - len,
+				"\tsid:         %d\n",
+				credit->pools[i].sid);
+		len += snprintf(buf + len, buf_len - len,
+				"\tmin_pages:   %d\n",
+				credit->pools[i].min_pages);
+		len += snprintf(buf + len, buf_len - len,
+				"\tmax_pages:   %d\n",
+				credit->pools[i].max_pages);
+		len += snprintf(buf + len, buf_len - len,
+				"\talloc_pages: %d\n",
+				credit->pools[i].alloc_pages);
+		len += snprintf(buf + len, buf_len - len,
+				"\tfreed_pages: %d\n",
+				credit->pools[i].total_freed_pages);
+	}
+
+	len += snprintf(buf + len, buf_len - len, "\n### SPOOLS ###\n");
+	for (i = 0; i < IWM_MACS_OUT_SGROUPS; i++) {
+		len += snprintf(buf + len, buf_len - len,
+				"spools entry #%d\n", i);
+		len += snprintf(buf + len, buf_len - len,
+				"\tid:          %d\n",
+				credit->spools[i].id);
+		len += snprintf(buf + len, buf_len - len,
+				"\tmax_pages:   %d\n",
+				credit->spools[i].max_pages);
+		len += snprintf(buf + len, buf_len - len,
+				"\talloc_pages: %d\n",
+				credit->spools[i].alloc_pages);
+
+	}
+
+	ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t iwm_debugfs_rx_ticket_read(struct file *filp,
+					  char __user *buffer,
+					  size_t count, loff_t *ppos)
+{
+	struct iwm_priv *iwm = filp->private_data;
+	struct iwm_rx_ticket_node *ticket, *next;
+	char *buf;
+	int buf_len = 4096, i;
+	size_t len = 0;
+	ssize_t ret;
+
+	if (*ppos != 0)
+		return 0;
+	if (count < sizeof(buf))
+		return -ENOSPC;
+
+	buf = kzalloc(buf_len, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	list_for_each_entry_safe(ticket, next, &iwm->rx_tickets, node) {
+		len += snprintf(buf + len, buf_len - len, "Ticket #%d\n",
+				ticket->ticket->id);
+		len += snprintf(buf + len, buf_len - len, "\taction: 0x%x\n",
+				ticket->ticket->action);
+		len += snprintf(buf + len, buf_len - len, "\tflags:  0x%x\n",
+				ticket->ticket->flags);
+	}
+
+	for (i = 0; i < IWM_RX_ID_HASH; i++) {
+		struct iwm_rx_packet *packet, *nxt;
+		struct list_head *pkt_list = &iwm->rx_packets[i];
+		if (!list_empty(pkt_list)) {
+			len += snprintf(buf + len, buf_len - len,
+					"Packet hash #%d\n", i);
+			list_for_each_entry_safe(packet, nxt, pkt_list, node) {
+				len += snprintf(buf + len, buf_len - len,
+						"\tPacket id:     %d\n",
+						packet->id);
+				len += snprintf(buf + len, buf_len - len,
+						"\tPacket length: %lu\n",
+						packet->pkt_size);
+			}
+		}
+	}
+
+	ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
+	kfree(buf);
+
+	return ret;
+}
+
+
+static const struct file_operations iwm_debugfs_txq_fops = {
+	.owner =	THIS_MODULE,
+	.open =		iwm_txrx_open,
+	.read =		iwm_debugfs_txq_read,
+};
+
+static const struct file_operations iwm_debugfs_tx_credit_fops = {
+	.owner =	THIS_MODULE,
+	.open =		iwm_txrx_open,
+	.read =		iwm_debugfs_tx_credit_read,
+};
+
+static const struct file_operations iwm_debugfs_rx_ticket_fops = {
+	.owner =	THIS_MODULE,
+	.open =		iwm_txrx_open,
+	.read =		iwm_debugfs_rx_ticket_read,
+};
+
+int iwm_debugfs_init(struct iwm_priv *iwm)
+{
+	int i, result;
+	char devdir[16];
+
+	iwm->dbg.rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
+	result = PTR_ERR(iwm->dbg.rootdir);
+	if (!result || IS_ERR(iwm->dbg.rootdir)) {
+		if (result == -ENODEV) {
+			IWM_ERR(iwm, "DebugFS (CONFIG_DEBUG_FS) not "
+				"enabled in kernel config\n");
+			result = 0;	/* No debugfs support */
+		}
+		IWM_ERR(iwm, "Couldn't create rootdir: %d\n", result);
+		goto error;
+	}
+
+	snprintf(devdir, sizeof(devdir), "%s", wiphy_name(iwm_to_wiphy(iwm)));
+
+	iwm->dbg.devdir = debugfs_create_dir(devdir, iwm->dbg.rootdir);
+	result = PTR_ERR(iwm->dbg.devdir);
+	if (IS_ERR(iwm->dbg.devdir) && (result != -ENODEV)) {
+		IWM_ERR(iwm, "Couldn't create devdir: %d\n", result);
+		goto error;
+	}
+
+	iwm->dbg.dbgdir = debugfs_create_dir("debug", iwm->dbg.devdir);
+	result = PTR_ERR(iwm->dbg.dbgdir);
+	if (IS_ERR(iwm->dbg.dbgdir) && (result != -ENODEV)) {
+		IWM_ERR(iwm, "Couldn't create dbgdir: %d\n", result);
+		goto error;
+	}
+
+	iwm->dbg.rxdir = debugfs_create_dir("rx", iwm->dbg.devdir);
+	result = PTR_ERR(iwm->dbg.rxdir);
+	if (IS_ERR(iwm->dbg.rxdir) && (result != -ENODEV)) {
+		IWM_ERR(iwm, "Couldn't create rx dir: %d\n", result);
+		goto error;
+	}
+
+	iwm->dbg.txdir = debugfs_create_dir("tx", iwm->dbg.devdir);
+	result = PTR_ERR(iwm->dbg.txdir);
+	if (IS_ERR(iwm->dbg.txdir) && (result != -ENODEV)) {
+		IWM_ERR(iwm, "Couldn't create tx dir: %d\n", result);
+		goto error;
+	}
+
+	iwm->dbg.busdir = debugfs_create_dir("bus", iwm->dbg.devdir);
+	result = PTR_ERR(iwm->dbg.busdir);
+	if (IS_ERR(iwm->dbg.busdir) && (result != -ENODEV)) {
+		IWM_ERR(iwm, "Couldn't create bus dir: %d\n", result);
+		goto error;
+	}
+
+	if (iwm->bus_ops->debugfs_init) {
+		result = iwm->bus_ops->debugfs_init(iwm, iwm->dbg.busdir);
+		if (result < 0) {
+			IWM_ERR(iwm, "Couldn't create bus entry: %d\n", result);
+			goto error;
+		}
+	}
+
+
+	iwm->dbg.dbg_level = IWM_DL_NONE;
+	iwm->dbg.dbg_level_dentry =
+		debugfs_create_file("level", 0200, iwm->dbg.dbgdir, iwm,
+				    &fops_iwm_dbg_level);
+	result = PTR_ERR(iwm->dbg.dbg_level_dentry);
+	if (IS_ERR(iwm->dbg.dbg_level_dentry) && (result != -ENODEV)) {
+		IWM_ERR(iwm, "Couldn't create dbg_level: %d\n", result);
+		goto error;
+	}
+
+
+	iwm->dbg.dbg_modules = IWM_DM_DEFAULT;
+	iwm->dbg.dbg_modules_dentry =
+		debugfs_create_file("modules", 0200, iwm->dbg.dbgdir, iwm,
+				    &fops_iwm_dbg_modules);
+	result = PTR_ERR(iwm->dbg.dbg_modules_dentry);
+	if (IS_ERR(iwm->dbg.dbg_modules_dentry) && (result != -ENODEV)) {
+		IWM_ERR(iwm, "Couldn't create dbg_modules: %d\n", result);
+		goto error;
+	}
+
+	for (i = 0; i < __IWM_DM_NR; i++)
+		add_dbg_module(iwm->dbg, iwm_debug_module[i].name,
+			       iwm_debug_module[i].id, IWM_DL_DEFAULT);
+
+	iwm->dbg.txq_dentry = debugfs_create_file("queues", 0200,
+						  iwm->dbg.txdir, iwm,
+						  &iwm_debugfs_txq_fops);
+	result = PTR_ERR(iwm->dbg.txq_dentry);
+	if (IS_ERR(iwm->dbg.txq_dentry) && (result != -ENODEV)) {
+		IWM_ERR(iwm, "Couldn't create tx queue: %d\n", result);
+		goto error;
+	}
+
+	iwm->dbg.tx_credit_dentry = debugfs_create_file("credits", 0200,
+						   iwm->dbg.txdir, iwm,
+						   &iwm_debugfs_tx_credit_fops);
+	result = PTR_ERR(iwm->dbg.tx_credit_dentry);
+	if (IS_ERR(iwm->dbg.tx_credit_dentry) && (result != -ENODEV)) {
+		IWM_ERR(iwm, "Couldn't create tx credit: %d\n", result);
+		goto error;
+	}
+
+	iwm->dbg.rx_ticket_dentry = debugfs_create_file("tickets", 0200,
+						  iwm->dbg.rxdir, iwm,
+						  &iwm_debugfs_rx_ticket_fops);
+	result = PTR_ERR(iwm->dbg.rx_ticket_dentry);
+	if (IS_ERR(iwm->dbg.rx_ticket_dentry) && (result != -ENODEV)) {
+		IWM_ERR(iwm, "Couldn't create rx ticket: %d\n", result);
+		goto error;
+	}
+
+	return 0;
+
+ error:
+	return result;
+}
+
+void iwm_debugfs_exit(struct iwm_priv *iwm)
+{
+	int i;
+
+	for (i = 0; i < __IWM_DM_NR; i++)
+		debugfs_remove(iwm->dbg.dbg_module_dentries[i]);
+
+	debugfs_remove(iwm->dbg.dbg_modules_dentry);
+	debugfs_remove(iwm->dbg.dbg_level_dentry);
+	debugfs_remove(iwm->dbg.txq_dentry);
+	debugfs_remove(iwm->dbg.tx_credit_dentry);
+	debugfs_remove(iwm->dbg.rx_ticket_dentry);
+	if (iwm->bus_ops->debugfs_exit)
+		iwm->bus_ops->debugfs_exit(iwm);
+
+	debugfs_remove(iwm->dbg.busdir);
+	debugfs_remove(iwm->dbg.dbgdir);
+	debugfs_remove(iwm->dbg.txdir);
+	debugfs_remove(iwm->dbg.rxdir);
+	debugfs_remove(iwm->dbg.devdir);
+	debugfs_remove(iwm->dbg.rootdir);
+}
diff --git a/drivers/net/wireless/iwmc3200wifi/eeprom.c b/drivers/net/wireless/iwmc3200wifi/eeprom.c
new file mode 100644
index 0000000..0f34b84
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/eeprom.c
@@ -0,0 +1,187 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 of 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.
+ *
+ *
+ * Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ */
+
+#include <linux/kernel.h>
+
+#include "iwm.h"
+#include "umac.h"
+#include "commands.h"
+#include "eeprom.h"
+
+static struct iwm_eeprom_entry eeprom_map[] = {
+	[IWM_EEPROM_SIG] =
+	{"Signature", IWM_EEPROM_SIG_OFF, IWM_EEPROM_SIG_LEN},
+
+	[IWM_EEPROM_VERSION] =
+	{"Version", IWM_EEPROM_VERSION_OFF, IWM_EEPROM_VERSION_LEN},
+
+	[IWM_EEPROM_OEM_HW_VERSION] =
+	{"OEM HW version", IWM_EEPROM_OEM_HW_VERSION_OFF,
+	 IWM_EEPROM_OEM_HW_VERSION_LEN},
+
+	[IWM_EEPROM_MAC_VERSION] =
+	{"MAC version", IWM_EEPROM_MAC_VERSION_OFF, IWM_EEPROM_MAC_VERSION_LEN},
+
+	[IWM_EEPROM_CARD_ID] =
+	{"Card ID", IWM_EEPROM_CARD_ID_OFF, IWM_EEPROM_CARD_ID_LEN},
+
+	[IWM_EEPROM_RADIO_CONF] =
+	{"Radio config", IWM_EEPROM_RADIO_CONF_OFF, IWM_EEPROM_RADIO_CONF_LEN},
+
+	[IWM_EEPROM_SKU_CAP] =
+	{"SKU capabilities", IWM_EEPROM_SKU_CAP_OFF, IWM_EEPROM_SKU_CAP_LEN},
+
+	[IWM_EEPROM_CALIB_RXIQ_OFFSET] =
+	{"RX IQ offset", IWM_EEPROM_CALIB_RXIQ_OFF, IWM_EEPROM_INDIRECT_LEN},
+
+	[IWM_EEPROM_CALIB_RXIQ] =
+	{"Calib RX IQ", 0, IWM_EEPROM_CALIB_RXIQ_LEN},
+};
+
+
+static int iwm_eeprom_read(struct iwm_priv *iwm, u8 eeprom_id)
+{
+	int ret;
+	u32 entry_size, chunk_size, data_offset = 0, addr_offset = 0;
+	u32 addr;
+	struct iwm_udma_wifi_cmd udma_cmd;
+	struct iwm_umac_cmd umac_cmd;
+	struct iwm_umac_cmd_eeprom_proxy eeprom_cmd;
+
+	if (eeprom_id > (IWM_EEPROM_LAST - 1))
+		return -EINVAL;
+
+	entry_size = eeprom_map[eeprom_id].length;
+
+	if (eeprom_id >= IWM_EEPROM_INDIRECT_DATA) {
+		/* indirect data */
+		u32 off_id = eeprom_id - IWM_EEPROM_INDIRECT_DATA +
+			     IWM_EEPROM_INDIRECT_OFFSET;
+
+		eeprom_map[eeprom_id].offset =
+			*(u16 *)(iwm->eeprom + eeprom_map[off_id].offset) << 1;
+	}
+
+	addr = eeprom_map[eeprom_id].offset;
+
+	udma_cmd.eop = 1;
+	udma_cmd.credit_group = 0x4;
+	udma_cmd.ra_tid = UMAC_HDI_ACT_TBL_IDX_HOST_CMD;
+	udma_cmd.lmac_offset = 0;
+
+	umac_cmd.id = UMAC_CMD_OPCODE_EEPROM_PROXY;
+	umac_cmd.resp = 1;
+
+	while (entry_size > 0) {
+		chunk_size = min_t(u32, entry_size, IWM_MAX_EEPROM_DATA_LEN);
+
+		eeprom_cmd.hdr.type =
+			cpu_to_le32(IWM_UMAC_CMD_EEPROM_TYPE_READ);
+		eeprom_cmd.hdr.offset = cpu_to_le32(addr + addr_offset);
+		eeprom_cmd.hdr.len = cpu_to_le32(chunk_size);
+
+		ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd,
+					    &umac_cmd, &eeprom_cmd,
+				     sizeof(struct iwm_umac_cmd_eeprom_proxy));
+		if (ret < 0) {
+			IWM_ERR(iwm, "Couldn't read eeprom\n");
+			return ret;
+		}
+
+		ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_EEPROM_PROXY,
+				       IWM_SRC_UMAC, 2*HZ);
+		if (ret < 0) {
+			IWM_ERR(iwm, "Did not get any eeprom answer\n");
+			return ret;
+		}
+
+		data_offset += chunk_size;
+		addr_offset += chunk_size;
+		entry_size -= chunk_size;
+	}
+
+	return 0;
+}
+
+u8 *iwm_eeprom_access(struct iwm_priv *iwm, u8 eeprom_id)
+{
+	if (!iwm->eeprom)
+		return ERR_PTR(-ENODEV);
+
+	return iwm->eeprom + eeprom_map[eeprom_id].offset;
+}
+
+int iwm_eeprom_init(struct iwm_priv *iwm)
+{
+	int i, ret = 0;
+	char name[32];
+
+	iwm->eeprom = kzalloc(IWM_EEPROM_LEN, GFP_KERNEL);
+	if (!iwm->eeprom)
+		return -ENOMEM;
+
+	for (i = IWM_EEPROM_FIRST; i < IWM_EEPROM_LAST; i++) {
+#ifdef CONFIG_IWM_B0_HW_SUPPORT
+		if (iwm->conf.hw_b0 && (i >= IWM_EEPROM_INDIRECT_OFFSET))
+			break;
+#endif
+		ret = iwm_eeprom_read(iwm, i);
+		if (ret < 0) {
+			IWM_ERR(iwm, "Couldn't read eeprom entry #%d: %s\n",
+				i, eeprom_map[i].name);
+			break;
+		}
+	}
+
+	IWM_DBG_BOOT(iwm, DBG, "EEPROM dump:\n");
+	for (i = IWM_EEPROM_FIRST; i < IWM_EEPROM_LAST; i++) {
+		memset(name, 0, 32);
+		sprintf(name, "%s: ", eeprom_map[i].name);
+
+		IWM_HEXDUMP(iwm, DBG, BOOT, name,
+			    iwm->eeprom + eeprom_map[i].offset,
+			    eeprom_map[i].length);
+	}
+
+	return ret;
+}
+
+void iwm_eeprom_exit(struct iwm_priv *iwm)
+{
+	kfree(iwm->eeprom);
+}
diff --git a/drivers/net/wireless/iwmc3200wifi/eeprom.h b/drivers/net/wireless/iwmc3200wifi/eeprom.h
new file mode 100644
index 0000000..cdb31a6
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/eeprom.h
@@ -0,0 +1,114 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 of 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.
+ *
+ *
+ * Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ */
+
+#ifndef __IWM_EEPROM_H__
+#define __IWM_EEPROM_H__
+
+enum {
+	IWM_EEPROM_SIG = 0,
+	IWM_EEPROM_FIRST = IWM_EEPROM_SIG,
+	IWM_EEPROM_VERSION,
+	IWM_EEPROM_OEM_HW_VERSION,
+	IWM_EEPROM_MAC_VERSION,
+	IWM_EEPROM_CARD_ID,
+	IWM_EEPROM_RADIO_CONF,
+	IWM_EEPROM_SKU_CAP,
+
+	IWM_EEPROM_INDIRECT_OFFSET,
+	IWM_EEPROM_CALIB_RXIQ_OFFSET = IWM_EEPROM_INDIRECT_OFFSET,
+
+	IWM_EEPROM_INDIRECT_DATA,
+	IWM_EEPROM_CALIB_RXIQ = IWM_EEPROM_INDIRECT_DATA,
+
+	IWM_EEPROM_LAST,
+};
+
+#define IWM_EEPROM_SIG_OFF             0x00
+#define IWM_EEPROM_VERSION_OFF        (0x54 << 1)
+#define IWM_EEPROM_OEM_HW_VERSION_OFF (0x56 << 1)
+#define IWM_EEPROM_MAC_VERSION_OFF    (0x30 << 1)
+#define IWM_EEPROM_CARD_ID_OFF        (0x5d << 1)
+#define IWM_EEPROM_RADIO_CONF_OFF     (0x58 << 1)
+#define IWM_EEPROM_SKU_CAP_OFF        (0x55 << 1)
+#define IWM_EEPROM_CALIB_CONFIG_OFF   (0x7c << 1)
+
+#define IWM_EEPROM_SIG_LEN              4
+#define IWM_EEPROM_VERSION_LEN          2
+#define IWM_EEPROM_OEM_HW_VERSION_LEN   2
+#define IWM_EEPROM_MAC_VERSION_LEN      1
+#define IWM_EEPROM_CARD_ID_LEN          2
+#define IWM_EEPROM_RADIO_CONF_LEN       2
+#define IWM_EEPROM_SKU_CAP_LEN          2
+#define IWM_EEPROM_INDIRECT_LEN		2
+
+#define IWM_MAX_EEPROM_DATA_LEN         240
+#define IWM_EEPROM_LEN                  0x800
+
+#define IWM_EEPROM_MIN_ALLOWED_VERSION          0x0610
+#define IWM_EEPROM_MAX_ALLOWED_VERSION          0x0700
+#define IWM_EEPROM_CURRENT_VERSION              0x0612
+
+#define IWM_EEPROM_SKU_CAP_BAND_24GHZ           (1 << 4)
+#define IWM_EEPROM_SKU_CAP_BAND_52GHZ           (1 << 5)
+#define IWM_EEPROM_SKU_CAP_11N_ENABLE           (1 << 6)
+
+enum {
+	IWM_EEPROM_CALIB_CAL_HDR,
+	IWM_EEPROM_CALIB_TX_POWER,
+	IWM_EEPROM_CALIB_XTAL,
+	IWM_EEPROM_CALIB_TEMPERATURE,
+	IWM_EEPROM_CALIB_RX_BB_FILTER,
+	IWM_EEPROM_CALIB_RX_IQ,
+	IWM_EEPROM_CALIB_MAX,
+};
+
+#define IWM_EEPROM_CALIB_RXIQ_OFF	(IWM_EEPROM_CALIB_CONFIG_OFF + \
+					 (IWM_EEPROM_CALIB_RX_IQ << 1))
+#define IWM_EEPROM_CALIB_RXIQ_LEN	sizeof(struct iwm_lmac_calib_rxiq)
+
+struct iwm_eeprom_entry {
+	char *name;
+	u32 offset;
+	u32 length;
+};
+
+int iwm_eeprom_init(struct iwm_priv *iwm);
+void iwm_eeprom_exit(struct iwm_priv *iwm);
+u8 *iwm_eeprom_access(struct iwm_priv *iwm, u8 eeprom_id);
+
+#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/fw.c b/drivers/net/wireless/iwmc3200wifi/fw.c
new file mode 100644
index 0000000..db4ba08
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/fw.c
@@ -0,0 +1,388 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 of 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.
+ *
+ *
+ * Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/firmware.h>
+
+#include "iwm.h"
+#include "bus.h"
+#include "hal.h"
+#include "umac.h"
+#include "debug.h"
+#include "fw.h"
+#include "commands.h"
+
+static const char fw_barker[] = "*WESTOPFORNOONE*";
+
+/*
+ * @op_code: Op code we're looking for.
+ * @index: There can be several instances of the same opcode within
+ *         the firmware. Index specifies which one we're looking for.
+ */
+static int iwm_fw_op_offset(struct iwm_priv *iwm, const struct firmware *fw,
+			    u16 op_code, u32 index)
+{
+	int offset = -EINVAL, fw_offset;
+	u32 op_index = 0;
+	const u8 *fw_ptr;
+	struct iwm_fw_hdr_rec *rec;
+
+	fw_offset = 0;
+	fw_ptr = fw->data;
+
+	/* We first need to look for the firmware barker */
+	if (memcmp(fw_ptr, fw_barker, IWM_HDR_BARKER_LEN)) {
+		IWM_ERR(iwm, "No barker string in this FW\n");
+		return -EINVAL;
+	}
+
+	if (fw->size < IWM_HDR_LEN) {
+		IWM_ERR(iwm, "FW is too small (%d)\n", fw->size);
+		return -EINVAL;
+	}
+
+	fw_offset += IWM_HDR_BARKER_LEN;
+
+	while (fw_offset < fw->size) {
+		rec = (struct iwm_fw_hdr_rec *)(fw_ptr + fw_offset);
+
+		IWM_DBG_FW(iwm, DBG, "FW: op_code: 0x%x, len: %d @ 0x%x\n",
+			   rec->op_code, rec->len, fw_offset);
+
+		if (rec->op_code == IWM_HDR_REC_OP_INVALID) {
+			IWM_DBG_FW(iwm, DBG, "Reached INVALID op code\n");
+			break;
+		}
+
+		if (rec->op_code == op_code) {
+			if (op_index == index) {
+				fw_offset += sizeof(struct iwm_fw_hdr_rec);
+				offset = fw_offset;
+				goto out;
+			}
+			op_index++;
+		}
+
+		fw_offset += sizeof(struct iwm_fw_hdr_rec) + rec->len;
+	}
+
+ out:
+	return offset;
+}
+
+static int iwm_load_firmware_chunk(struct iwm_priv *iwm,
+				   const struct firmware *fw,
+				   struct iwm_fw_img_desc *img_desc)
+{
+	struct iwm_udma_nonwifi_cmd target_cmd;
+	u32 chunk_size;
+	const u8 *chunk_ptr;
+	int ret = 0;
+
+	IWM_DBG_FW(iwm, INFO, "Loading FW chunk: %d bytes @ 0x%x\n",
+		   img_desc->length, img_desc->address);
+
+	target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE;
+	target_cmd.handle_by_hw = 1;
+	target_cmd.op2 = 0;
+	target_cmd.resp = 0;
+	target_cmd.eop = 1;
+
+	chunk_size = img_desc->length;
+	chunk_ptr = fw->data + img_desc->offset;
+
+	while (chunk_size > 0) {
+		u32 tmp_chunk_size;
+
+		tmp_chunk_size = min_t(u32, chunk_size,
+				       IWM_MAX_NONWIFI_CMD_BUFF_SIZE);
+
+		target_cmd.addr = cpu_to_le32(img_desc->address +
+				    (chunk_ptr - fw->data - img_desc->offset));
+		target_cmd.op1_sz = cpu_to_le32(tmp_chunk_size);
+
+		IWM_DBG_FW(iwm, DBG, "\t%d bytes @ 0x%x\n",
+			   tmp_chunk_size, target_cmd.addr);
+
+		ret = iwm_hal_send_target_cmd(iwm, &target_cmd, chunk_ptr);
+		if (ret < 0) {
+			IWM_ERR(iwm, "Couldn't load FW chunk\n");
+			break;
+		}
+
+		chunk_size -= tmp_chunk_size;
+		chunk_ptr += tmp_chunk_size;
+	}
+
+	return ret;
+}
+/*
+ * To load a fw image to the target, we basically go through the
+ * fw, looking for OP_MEM_DESC records. Once we found one, we
+ * pass it to iwm_load_firmware_chunk().
+ * The OP_MEM_DESC records contain the actuall memory chunk to be
+ * sent, but also the destination address.
+ */
+static int iwm_load_img(struct iwm_priv *iwm, const char *img_name)
+{
+	const struct firmware *fw;
+	struct iwm_fw_img_desc *img_desc;
+	struct iwm_fw_img_ver *ver;
+	int ret = 0, fw_offset;
+	u32 opcode_idx = 0, build_date;
+	char *build_tag;
+
+	ret = request_firmware(&fw, img_name, iwm_to_dev(iwm));
+	if (ret) {
+		IWM_ERR(iwm, "Request firmware failed");
+		return ret;
+	}
+
+	IWM_DBG_FW(iwm, INFO, "Start to load FW %s\n", img_name);
+
+	while (1) {
+		fw_offset = iwm_fw_op_offset(iwm, fw,
+					     IWM_HDR_REC_OP_MEM_DESC,
+					     opcode_idx);
+		if (fw_offset < 0)
+			break;
+
+		img_desc = (struct iwm_fw_img_desc *)(fw->data + fw_offset);
+		ret = iwm_load_firmware_chunk(iwm, fw, img_desc);
+		if (ret < 0)
+			goto err_release_fw;
+		opcode_idx++;
+	};
+
+	/* Read firmware version */
+	fw_offset = iwm_fw_op_offset(iwm, fw, IWM_HDR_REC_OP_SW_VER, 0);
+	if (fw_offset < 0)
+		goto err_release_fw;
+
+	ver = (struct iwm_fw_img_ver *)(fw->data + fw_offset);
+
+	/* Read build tag */
+	fw_offset = iwm_fw_op_offset(iwm, fw, IWM_HDR_REC_OP_BUILD_TAG, 0);
+	if (fw_offset < 0)
+		goto err_release_fw;
+
+	build_tag = (char *)(fw->data + fw_offset);
+
+	/* Read build date */
+	fw_offset = iwm_fw_op_offset(iwm, fw, IWM_HDR_REC_OP_BUILD_DATE, 0);
+	if (fw_offset < 0)
+		goto err_release_fw;
+
+	build_date = *(u32 *)(fw->data + fw_offset);
+
+	IWM_INFO(iwm, "%s:\n", img_name);
+	IWM_INFO(iwm, "\tVersion:    %02X.%02X\n", ver->major, ver->minor);
+	IWM_INFO(iwm, "\tBuild tag:  %s\n", build_tag);
+	IWM_INFO(iwm, "\tBuild date: %x-%x-%x\n",
+		 IWM_BUILD_YEAR(build_date), IWM_BUILD_MONTH(build_date),
+		 IWM_BUILD_DAY(build_date));
+
+
+ err_release_fw:
+	release_firmware(fw);
+
+	return ret;
+}
+
+static int iwm_load_umac(struct iwm_priv *iwm)
+{
+	struct iwm_udma_nonwifi_cmd target_cmd;
+	int ret;
+
+	ret = iwm_load_img(iwm, iwm->bus_ops->umac_name);
+	if (ret < 0)
+		return ret;
+
+	/* We've loaded the UMAC, we can tell the target to jump there */
+	target_cmd.opcode = UMAC_HDI_OUT_OPCODE_JUMP;
+	target_cmd.addr = cpu_to_le32(UMAC_MU_FW_INST_DATA_12_ADDR);
+	target_cmd.op1_sz = 0;
+	target_cmd.op2 = 0;
+	target_cmd.handle_by_hw = 0;
+	target_cmd.resp = 1 ;
+	target_cmd.eop = 1;
+
+	ret = iwm_hal_send_target_cmd(iwm, &target_cmd, NULL);
+	if (ret < 0)
+		IWM_ERR(iwm, "Couldn't send JMP command\n");
+
+	return ret;
+}
+
+static int iwm_load_lmac(struct iwm_priv *iwm, const char *img_name)
+{
+	int ret;
+
+	ret = iwm_load_img(iwm, img_name);
+	if (ret < 0)
+		return ret;
+
+	return iwm_send_umac_reset(iwm,
+			cpu_to_le32(UMAC_RST_CTRL_FLG_LARC_CLK_EN), 0);
+}
+
+/*
+ * We currently have to load 3 FWs:
+ * 1) The UMAC (Upper MAC).
+ * 2) The calibration LMAC (Lower MAC).
+ *    We then send the calibration init command, so that the device can
+ *    run a first calibration round.
+ * 3) The operational LMAC, which replaces the calibration one when it's
+ *    done with the first calibration round.
+ *
+ * Once those 3 FWs have been loaded, we send the periodic calibration
+ * command, and then the device is available for regular 802.11 operations.
+ */
+int iwm_load_fw(struct iwm_priv *iwm)
+{
+	int ret;
+
+	/* We first start downloading the UMAC */
+	ret = iwm_load_umac(iwm);
+	if (ret < 0) {
+		IWM_ERR(iwm, "UMAC loading failed\n");
+		return ret;
+	}
+
+	/* Handle UMAC_ALIVE notification */
+	ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_ALIVE, IWM_SRC_UMAC,
+			       WAIT_NOTIF_TIMEOUT);
+	if (ret) {
+		IWM_ERR(iwm, "Handle UMAC_ALIVE failed: %d\n", ret);
+		return ret;
+	}
+
+	/* UMAC is alive, we can download the calibration LMAC */
+	ret = iwm_load_lmac(iwm, iwm->bus_ops->calib_lmac_name);
+	if (ret) {
+		IWM_ERR(iwm, "Calibration LMAC loading failed\n");
+		return ret;
+	}
+
+	/* Handle UMAC_INIT_COMPLETE notification */
+	ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_INIT_COMPLETE,
+			       IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
+	if (ret) {
+		IWM_ERR(iwm, "Handle INIT_COMPLETE failed for calibration "
+			"LMAC: %d\n", ret);
+		return ret;
+	}
+
+	/* Read EEPROM data */
+	ret = iwm_eeprom_init(iwm);
+	if (ret < 0) {
+		IWM_ERR(iwm, "Couldn't init eeprom array\n");
+		return ret;
+	}
+
+#ifdef CONFIG_IWM_B0_HW_SUPPORT
+	if (iwm->conf.hw_b0) {
+		clear_bit(PHY_CALIBRATE_RX_IQ_CMD, &iwm->conf.init_calib_map);
+		clear_bit(PHY_CALIBRATE_RX_IQ_CMD,
+			  &iwm->conf.periodic_calib_map);
+	}
+#endif
+	/* Read RX IQ calibration result from EEPROM */
+	if (test_bit(PHY_CALIBRATE_RX_IQ_CMD, &iwm->conf.init_calib_map)) {
+		iwm_store_rxiq_calib_result(iwm);
+		set_bit(PHY_CALIBRATE_RX_IQ_CMD, &iwm->calib_done_map);
+	}
+
+	iwm_send_prio_table(iwm);
+	iwm_send_init_calib_cfg(iwm, iwm->conf.init_calib_map);
+
+	while (iwm->calib_done_map != iwm->conf.init_calib_map) {
+		ret = iwm_notif_handle(iwm, CALIBRATION_RES_NOTIFICATION,
+				       IWM_SRC_LMAC, WAIT_NOTIF_TIMEOUT);
+		if (ret) {
+			IWM_ERR(iwm, "Wait for calibration result timeout\n");
+			goto out;
+		}
+		IWM_DBG_FW(iwm, DBG, "Got calibration result. calib_done_map: "
+			   "0x%lx, requested calibrations: 0x%lx\n",
+			   iwm->calib_done_map, iwm->conf.init_calib_map);
+	}
+
+	/* Handle LMAC CALIBRATION_COMPLETE notification */
+	ret = iwm_notif_handle(iwm, CALIBRATION_COMPLETE_NOTIFICATION,
+			       IWM_SRC_LMAC, WAIT_NOTIF_TIMEOUT);
+	if (ret) {
+		IWM_ERR(iwm, "Wait for CALIBRATION_COMPLETE timeout\n");
+		goto out;
+	}
+
+	IWM_INFO(iwm, "LMAC calibration done: 0x%lx\n", iwm->calib_done_map);
+
+	iwm_send_umac_reset(iwm, cpu_to_le32(UMAC_RST_CTRL_FLG_LARC_RESET), 1);
+
+	ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_RESET, IWM_SRC_UMAC,
+			       WAIT_NOTIF_TIMEOUT);
+	if (ret) {
+		IWM_ERR(iwm, "Wait for UMAC RESET timeout\n");
+		goto out;
+	}
+
+	/* Download the operational LMAC */
+	ret = iwm_load_lmac(iwm, iwm->bus_ops->lmac_name);
+	if (ret) {
+		IWM_ERR(iwm, "LMAC loading failed\n");
+		goto out;
+	}
+
+	ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_INIT_COMPLETE,
+			       IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
+	if (ret) {
+		IWM_ERR(iwm, "Handle INIT_COMPLETE failed for LMAC: %d\n", ret);
+		goto out;
+	}
+
+	iwm_send_prio_table(iwm);
+	iwm_send_calib_results(iwm);
+	iwm_send_periodic_calib_cfg(iwm, iwm->conf.periodic_calib_map);
+
+	return 0;
+
+ out:
+	iwm_eeprom_exit(iwm);
+	return ret;
+}
diff --git a/drivers/net/wireless/iwmc3200wifi/fw.h b/drivers/net/wireless/iwmc3200wifi/fw.h
new file mode 100644
index 0000000..c70a3b4
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/fw.h
@@ -0,0 +1,100 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 of 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.
+ *
+ *
+ * Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ */
+
+#ifndef __IWM_FW_H__
+#define __IWM_FW_H__
+
+/**
+ * struct iwm_fw_hdr_rec - An iwm firmware image is a
+ * concatenation of various records. Each of them is
+ * defined by an ID (aka op code), a length, and the
+ * actual data.
+ * @op_code: The record ID, see IWM_HDR_REC_OP_*
+ *
+ * @len: The record payload length
+ *
+ * @buf: The record payload
+ */
+struct iwm_fw_hdr_rec {
+	u16 op_code;
+	u16 len;
+	u8 buf[0];
+};
+
+/* Header's definitions */
+#define IWM_HDR_LEN                          (512)
+#define IWM_HDR_BARKER_LEN                   (16)
+
+/* Header's opcodes */
+#define IWM_HDR_REC_OP_INVALID             (0x00)
+#define IWM_HDR_REC_OP_BUILD_DATE          (0x01)
+#define IWM_HDR_REC_OP_BUILD_TAG           (0x02)
+#define IWM_HDR_REC_OP_SW_VER              (0x03)
+#define IWM_HDR_REC_OP_HW_SKU              (0x04)
+#define IWM_HDR_REC_OP_BUILD_OPT           (0x05)
+#define IWM_HDR_REC_OP_MEM_DESC            (0x06)
+#define IWM_HDR_REC_USERDEFS               (0x07)
+
+/* Header's records length (in bytes) */
+#define IWM_HDR_REC_LEN_BUILD_DATE           (4)
+#define IWM_HDR_REC_LEN_BUILD_TAG            (64)
+#define IWM_HDR_REC_LEN_SW_VER               (4)
+#define IWM_HDR_REC_LEN_HW_SKU               (4)
+#define IWM_HDR_REC_LEN_BUILD_OPT            (4)
+#define IWM_HDR_REC_LEN_MEM_DESC             (12)
+#define IWM_HDR_REC_LEN_USERDEF              (64)
+
+#define IWM_BUILD_YEAR(date) ((date >> 16) & 0xffff)
+#define IWM_BUILD_MONTH(date) ((date >> 8) & 0xff)
+#define IWM_BUILD_DAY(date) (date & 0xff)
+
+struct iwm_fw_img_desc {
+	u32 offset;
+	u32 address;
+	u32 length;
+};
+
+struct iwm_fw_img_ver {
+	u8 minor;
+	u8 major;
+	u16 reserved;
+};
+
+int iwm_load_fw(struct iwm_priv *iwm);
+
+#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/hal.c b/drivers/net/wireless/iwmc3200wifi/hal.c
new file mode 100644
index 0000000..ee127fe4
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/hal.c
@@ -0,0 +1,464 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 of 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.
+ *
+ *
+ * Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ */
+
+/*
+ * Hardware Abstraction Layer for iwm.
+ *
+ * This file mostly defines an abstraction API for
+ * sending various commands to the target.
+ *
+ * We have 2 types of commands: wifi and non-wifi ones.
+ *
+ * - wifi commands:
+ *   They are used for sending LMAC and UMAC commands,
+ *   and thus are the most commonly used ones.
+ *   There are 2 different wifi command types, the regular
+ *   one and the LMAC one. The former is used to send
+ *   UMAC commands (see UMAC_CMD_OPCODE_* from umac.h)
+ *   while the latter is used for sending commands to the
+ *   LMAC. If you look at LMAC commands you'll se that they
+ *   are actually regular iwlwifi target commands encapsulated
+ *   into a special UMAC command called UMAC passthrough.
+ *   This is due to the fact the the host talks exclusively
+ *   to the UMAC and so there needs to be a special UMAC
+ *   command for talking to the LMAC.
+ *   This is how a wifi command is layed out:
+ *    ------------------------
+ *   | iwm_udma_out_wifi_hdr  |
+ *    ------------------------
+ *   | SW meta_data (32 bits) |
+ *    ------------------------
+ *   | iwm_dev_cmd_hdr        |
+ *    ------------------------
+ *   | payload                |
+ *   | ....                   |
+ *
+ * - non-wifi, or general commands:
+ *   Those commands are handled by the device's bootrom,
+ *   and are typically sent when the UMAC and the LMAC
+ *   are not yet available.
+ *    *   This is how a non-wifi command is layed out:
+ *    ---------------------------
+ *   | iwm_udma_out_nonwifi_hdr  |
+ *    ---------------------------
+ *   | payload                   |
+ *   | ....                      |
+
+ *
+ * All the commands start with a UDMA header, which is
+ * basically a 32 bits field. The 4 LSB there define
+ * an opcode that allows the target to differentiate
+ * between wifi (opcode is 0xf) and non-wifi commands
+ * (opcode is [0..0xe]).
+ *
+ * When a command (wifi or non-wifi) is supposed to receive
+ * an answer, we queue the command buffer. When we do receive
+ * a command response from the UMAC, we go through the list
+ * of pending command, and pass both the command and the answer
+ * to the rx handler. Each command is sent with a unique
+ * sequence id, and the answer is sent with the same one. This
+ * is how we're supposed to match an answer with its command.
+ * See rx.c:iwm_rx_handle_[non]wifi() and iwm_get_pending_[non]wifi()
+ * for the implementation details.
+ */
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+
+#include "iwm.h"
+#include "bus.h"
+#include "hal.h"
+#include "umac.h"
+#include "debug.h"
+
+static void iwm_nonwifi_cmd_init(struct iwm_priv *iwm,
+				 struct iwm_nonwifi_cmd *cmd,
+				 struct iwm_udma_nonwifi_cmd *udma_cmd)
+{
+	INIT_LIST_HEAD(&cmd->pending);
+
+	spin_lock(&iwm->cmd_lock);
+
+	cmd->resp_received = 0;
+
+	cmd->seq_num = iwm->nonwifi_seq_num;
+	udma_cmd->seq_num = cpu_to_le16(cmd->seq_num);
+
+	cmd->seq_num = iwm->nonwifi_seq_num++;
+	iwm->nonwifi_seq_num %= UMAC_NONWIFI_SEQ_NUM_MAX;
+
+	if (udma_cmd->resp)
+		list_add_tail(&cmd->pending, &iwm->nonwifi_pending_cmd);
+
+	spin_unlock(&iwm->cmd_lock);
+
+	cmd->buf.start = cmd->buf.payload;
+	cmd->buf.len = 0;
+
+	memcpy(&cmd->udma_cmd, udma_cmd, sizeof(*udma_cmd));
+}
+
+u16 iwm_alloc_wifi_cmd_seq(struct iwm_priv *iwm)
+{
+	u16 seq_num = iwm->wifi_seq_num;
+
+	iwm->wifi_seq_num++;
+	iwm->wifi_seq_num %= UMAC_WIFI_SEQ_NUM_MAX;
+
+	return seq_num;
+}
+
+static void iwm_wifi_cmd_init(struct iwm_priv *iwm,
+			      struct iwm_wifi_cmd *cmd,
+			      struct iwm_udma_wifi_cmd *udma_cmd,
+			      struct iwm_umac_cmd *umac_cmd,
+			      struct iwm_lmac_cmd *lmac_cmd,
+			      u16 payload_size)
+{
+	INIT_LIST_HEAD(&cmd->pending);
+
+	spin_lock(&iwm->cmd_lock);
+
+	cmd->seq_num = iwm_alloc_wifi_cmd_seq(iwm);
+	umac_cmd->seq_num = cpu_to_le16(cmd->seq_num);
+
+	if (umac_cmd->resp)
+		list_add_tail(&cmd->pending, &iwm->wifi_pending_cmd);
+
+	spin_unlock(&iwm->cmd_lock);
+
+	cmd->buf.start = cmd->buf.payload;
+	cmd->buf.len = 0;
+
+	if (lmac_cmd) {
+		cmd->buf.start -= sizeof(struct iwm_lmac_hdr);
+
+		lmac_cmd->seq_num = cpu_to_le16(cmd->seq_num);
+		lmac_cmd->count = cpu_to_le16(payload_size);
+
+		memcpy(&cmd->lmac_cmd, lmac_cmd, sizeof(*lmac_cmd));
+
+		umac_cmd->count = cpu_to_le16(sizeof(struct iwm_lmac_hdr));
+	} else
+		umac_cmd->count = 0;
+
+	umac_cmd->count = cpu_to_le16(payload_size +
+				      le16_to_cpu(umac_cmd->count));
+	udma_cmd->count = cpu_to_le16(sizeof(struct iwm_umac_fw_cmd_hdr) +
+				      le16_to_cpu(umac_cmd->count));
+
+	memcpy(&cmd->udma_cmd, udma_cmd, sizeof(*udma_cmd));
+	memcpy(&cmd->umac_cmd, umac_cmd, sizeof(*umac_cmd));
+}
+
+void iwm_cmd_flush(struct iwm_priv *iwm)
+{
+	struct iwm_wifi_cmd *wcmd, *wnext;
+	struct iwm_nonwifi_cmd *nwcmd, *nwnext;
+
+	list_for_each_entry_safe(wcmd, wnext, &iwm->wifi_pending_cmd, pending) {
+		list_del(&wcmd->pending);
+		kfree(wcmd);
+	}
+
+	list_for_each_entry_safe(nwcmd, nwnext, &iwm->nonwifi_pending_cmd,
+				 pending) {
+		list_del(&nwcmd->pending);
+		kfree(nwcmd);
+	}
+}
+
+struct iwm_wifi_cmd *iwm_get_pending_wifi_cmd(struct iwm_priv *iwm, u16 seq_num)
+{
+	struct iwm_wifi_cmd *cmd, *next;
+
+	list_for_each_entry_safe(cmd, next, &iwm->wifi_pending_cmd, pending)
+		if (cmd->seq_num == seq_num) {
+			list_del(&cmd->pending);
+			return cmd;
+		}
+
+	return NULL;
+}
+
+struct iwm_nonwifi_cmd *
+iwm_get_pending_nonwifi_cmd(struct iwm_priv *iwm, u8 seq_num, u8 cmd_opcode)
+{
+	struct iwm_nonwifi_cmd *cmd, *next;
+
+	list_for_each_entry_safe(cmd, next, &iwm->nonwifi_pending_cmd, pending)
+		if ((cmd->seq_num == seq_num) &&
+		    (cmd->udma_cmd.opcode == cmd_opcode) &&
+		    (cmd->resp_received)) {
+			list_del(&cmd->pending);
+			return cmd;
+		}
+
+	return NULL;
+}
+
+static void iwm_build_udma_nonwifi_hdr(struct iwm_priv *iwm,
+				       struct iwm_udma_out_nonwifi_hdr *hdr,
+				       struct iwm_udma_nonwifi_cmd *cmd)
+{
+	memset(hdr, 0, sizeof(*hdr));
+
+	SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE, cmd->opcode);
+	SET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_RESP, cmd->resp);
+	SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, 1);
+	SET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW,
+		  cmd->handle_by_hw);
+	SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_SIGNATURE, UMAC_HDI_OUT_SIGNATURE);
+	SET_VAL32(hdr->cmd, UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM,
+		  le16_to_cpu(cmd->seq_num));
+
+	hdr->addr = cmd->addr;
+	hdr->op1_sz = cmd->op1_sz;
+	hdr->op2 = cmd->op2;
+}
+
+static int iwm_send_udma_nonwifi_cmd(struct iwm_priv *iwm,
+				     struct iwm_nonwifi_cmd *cmd)
+{
+	struct iwm_udma_out_nonwifi_hdr *udma_hdr;
+	struct iwm_nonwifi_cmd_buff *buf;
+	struct iwm_udma_nonwifi_cmd *udma_cmd = &cmd->udma_cmd;
+
+	buf = &cmd->buf;
+
+	buf->start -= sizeof(struct iwm_umac_nonwifi_out_hdr);
+	buf->len += sizeof(struct iwm_umac_nonwifi_out_hdr);
+
+	udma_hdr = (struct iwm_udma_out_nonwifi_hdr *)(buf->start);
+
+	iwm_build_udma_nonwifi_hdr(iwm, udma_hdr, udma_cmd);
+
+	IWM_DBG_CMD(iwm, DBG,
+		    "Send UDMA nonwifi cmd: opcode = 0x%x, resp = 0x%x, "
+		    "hw = 0x%x, seqnum = %d, addr = 0x%x, op1_sz = 0x%x, "
+		    "op2 = 0x%x\n", udma_cmd->opcode, udma_cmd->resp,
+		    udma_cmd->handle_by_hw, cmd->seq_num, udma_cmd->addr,
+		    udma_cmd->op1_sz, udma_cmd->op2);
+
+	return iwm_bus_send_chunk(iwm, buf->start, buf->len);
+}
+
+void iwm_udma_wifi_hdr_set_eop(struct iwm_priv *iwm, u8 *buf, u8 eop)
+{
+	struct iwm_udma_out_wifi_hdr *hdr = (struct iwm_udma_out_wifi_hdr *)buf;
+
+	SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, eop);
+}
+
+void iwm_build_udma_wifi_hdr(struct iwm_priv *iwm,
+			     struct iwm_udma_out_wifi_hdr *hdr,
+			     struct iwm_udma_wifi_cmd *cmd)
+{
+	memset(hdr, 0, sizeof(*hdr));
+
+	SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE, UMAC_HDI_OUT_OPCODE_WIFI);
+	SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, cmd->eop);
+	SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_SIGNATURE, UMAC_HDI_OUT_SIGNATURE);
+
+	SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_BYTE_COUNT,
+		  le16_to_cpu(cmd->count));
+	SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_CREDIT_GRP, cmd->credit_group);
+	SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_RATID, cmd->ra_tid);
+	SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_LMAC_OFFSET, cmd->lmac_offset);
+}
+
+void iwm_build_umac_hdr(struct iwm_priv *iwm,
+			struct iwm_umac_fw_cmd_hdr *hdr,
+			struct iwm_umac_cmd *cmd)
+{
+	memset(hdr, 0, sizeof(*hdr));
+
+	SET_VAL32(hdr->meta_data, UMAC_FW_CMD_BYTE_COUNT,
+		  le16_to_cpu(cmd->count));
+	SET_VAL32(hdr->meta_data, UMAC_FW_CMD_TX_STA_COLOR, cmd->color);
+	SET_VAL8(hdr->cmd.flags, UMAC_DEV_CMD_FLAGS_RESP_REQ, cmd->resp);
+
+	hdr->cmd.cmd = cmd->id;
+	hdr->cmd.seq_num = cmd->seq_num;
+}
+
+static int iwm_send_udma_wifi_cmd(struct iwm_priv *iwm,
+				  struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_umac_wifi_out_hdr *umac_hdr;
+	struct iwm_wifi_cmd_buff *buf;
+	struct iwm_udma_wifi_cmd *udma_cmd = &cmd->udma_cmd;
+	struct iwm_umac_cmd *umac_cmd = &cmd->umac_cmd;
+	int ret;
+
+	buf = &cmd->buf;
+
+	buf->start -= sizeof(struct iwm_umac_wifi_out_hdr);
+	buf->len += sizeof(struct iwm_umac_wifi_out_hdr);
+
+	umac_hdr = (struct iwm_umac_wifi_out_hdr *)(buf->start);
+
+	iwm_build_udma_wifi_hdr(iwm, &umac_hdr->hw_hdr, udma_cmd);
+	iwm_build_umac_hdr(iwm, &umac_hdr->sw_hdr, umac_cmd);
+
+	IWM_DBG_CMD(iwm, DBG,
+		    "Send UDMA wifi cmd: opcode = 0x%x, UMAC opcode = 0x%x, "
+		    "eop = 0x%x, count = 0x%x, credit_group = 0x%x, "
+		    "ra_tid = 0x%x, lmac_offset = 0x%x, seqnum = %d\n",
+		    UMAC_HDI_OUT_OPCODE_WIFI, umac_cmd->id,
+		    udma_cmd->eop, udma_cmd->count, udma_cmd->credit_group,
+		    udma_cmd->ra_tid, udma_cmd->lmac_offset, cmd->seq_num);
+
+	if (umac_cmd->id == UMAC_CMD_OPCODE_WIFI_PASS_THROUGH)
+		IWM_DBG_CMD(iwm, DBG, "\tLMAC opcode: 0x%x\n",
+			    cmd->lmac_cmd.id);
+
+	ret = iwm_tx_credit_alloc(iwm, udma_cmd->credit_group, buf->len);
+
+	/* We keep sending UMAC reset regardless of the command credits.
+	 * The UMAC is supposed to be reset anyway and the Tx credits are
+	 * reinitialized afterwards. If we are lucky, the reset could
+	 * still be done even though we have run out of credits for the
+	 * command pool at this moment.*/
+	if (ret && (umac_cmd->id != UMAC_CMD_OPCODE_RESET)) {
+		IWM_DBG_TX(iwm, DBG, "Failed to alloc tx credit for cmd %d\n",
+			   umac_cmd->id);
+		return ret;
+	}
+
+	return iwm_bus_send_chunk(iwm, buf->start, buf->len);
+}
+
+/* target_cmd a.k.a udma_nonwifi_cmd can be sent when UMAC is not available */
+int iwm_hal_send_target_cmd(struct iwm_priv *iwm,
+			    struct iwm_udma_nonwifi_cmd *udma_cmd,
+			    const void *payload)
+{
+	struct iwm_nonwifi_cmd *cmd;
+	int ret;
+
+	cmd = kzalloc(sizeof(struct iwm_nonwifi_cmd), GFP_KERNEL);
+	if (!cmd) {
+		IWM_ERR(iwm, "Couldn't alloc memory for hal cmd\n");
+		return -ENOMEM;
+	}
+
+	iwm_nonwifi_cmd_init(iwm, cmd, udma_cmd);
+
+	if (cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE ||
+	    cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE_PERSISTENT) {
+		cmd->buf.len = le32_to_cpu(cmd->udma_cmd.op1_sz);
+		memcpy(&cmd->buf.payload, payload, cmd->buf.len);
+	}
+
+	ret = iwm_send_udma_nonwifi_cmd(iwm, cmd);
+
+	if (!udma_cmd->resp)
+		kfree(cmd);
+
+	if (ret < 0)
+		return ret;
+
+	return cmd->seq_num;
+}
+
+static void iwm_build_lmac_hdr(struct iwm_priv *iwm, struct iwm_lmac_hdr *hdr,
+			       struct iwm_lmac_cmd *cmd)
+{
+	memset(hdr, 0, sizeof(*hdr));
+
+	hdr->id = cmd->id;
+	hdr->flags = 0; /* Is this ever used? */
+	hdr->seq_num = cmd->seq_num;
+}
+
+/*
+ * iwm_hal_send_host_cmd(): sends commands to the UMAC or the LMAC.
+ * Sending command to the LMAC is equivalent to sending a
+ * regular UMAC command with the LMAC passtrough or the LMAC
+ * wrapper UMAC command IDs.
+ */
+int iwm_hal_send_host_cmd(struct iwm_priv *iwm,
+			  struct iwm_udma_wifi_cmd *udma_cmd,
+			  struct iwm_umac_cmd *umac_cmd,
+			  struct iwm_lmac_cmd *lmac_cmd,
+			  const void *payload, u16 payload_size)
+{
+	struct iwm_wifi_cmd *cmd;
+	struct iwm_lmac_hdr *hdr;
+	int lmac_hdr_len = 0;
+	int ret;
+
+	cmd = kzalloc(sizeof(struct iwm_wifi_cmd), GFP_KERNEL);
+	if (!cmd) {
+		IWM_ERR(iwm, "Couldn't alloc memory for wifi hal cmd\n");
+		return -ENOMEM;
+	}
+
+	iwm_wifi_cmd_init(iwm, cmd, udma_cmd, umac_cmd, lmac_cmd, payload_size);
+
+	if (lmac_cmd) {
+		hdr = (struct iwm_lmac_hdr *)(cmd->buf.start);
+
+		iwm_build_lmac_hdr(iwm, hdr, &cmd->lmac_cmd);
+		lmac_hdr_len = sizeof(struct iwm_lmac_hdr);
+	}
+
+	memcpy(cmd->buf.payload, payload, payload_size);
+	cmd->buf.len = le16_to_cpu(umac_cmd->count);
+
+	ret = iwm_send_udma_wifi_cmd(iwm, cmd);
+
+	/* We free the cmd if we're not expecting any response */
+	if (!umac_cmd->resp)
+		kfree(cmd);
+	return ret;
+}
+
+/*
+ * iwm_hal_send_umac_cmd(): This is a special case for
+ * iwm_hal_send_host_cmd() to send direct UMAC cmd (without
+ * LMAC involved).
+ */
+int iwm_hal_send_umac_cmd(struct iwm_priv *iwm,
+			  struct iwm_udma_wifi_cmd *udma_cmd,
+			  struct iwm_umac_cmd *umac_cmd,
+			  const void *payload, u16 payload_size)
+{
+	return iwm_hal_send_host_cmd(iwm, udma_cmd, umac_cmd, NULL,
+				     payload, payload_size);
+}
diff --git a/drivers/net/wireless/iwmc3200wifi/hal.h b/drivers/net/wireless/iwmc3200wifi/hal.h
new file mode 100644
index 0000000..0adfdc8
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/hal.h
@@ -0,0 +1,236 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 of 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.
+ *
+ *
+ * Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ */
+
+#ifndef _IWM_HAL_H_
+#define _IWM_HAL_H_
+
+#include "umac.h"
+
+#define GET_VAL8(s, name)	((s >> name##_POS) & name##_SEED)
+#define GET_VAL16(s, name)	((le16_to_cpu(s) >> name##_POS) & name##_SEED)
+#define GET_VAL32(s, name)	((le32_to_cpu(s) >> name##_POS) & name##_SEED)
+
+#define SET_VAL8(s, name, val)						  \
+do {									  \
+	s = (s & ~(name##_SEED << name##_POS)) |			  \
+	    ((val & name##_SEED) << name##_POS);			  \
+} while (0)
+
+#define SET_VAL16(s, name, val)						  \
+do {									  \
+	s = cpu_to_le16((le16_to_cpu(s) & ~(name##_SEED << name##_POS)) | \
+			((val & name##_SEED) << name##_POS));		  \
+} while (0)
+
+#define SET_VAL32(s, name, val)						  \
+do {									  \
+	s = cpu_to_le32((le32_to_cpu(s) & ~(name##_SEED << name##_POS)) | \
+			((val & name##_SEED) << name##_POS));		  \
+} while (0)
+
+
+#define UDMA_UMAC_INIT	{	.eop = 1,				  \
+				.credit_group = 0x4,			  \
+				.ra_tid = UMAC_HDI_ACT_TBL_IDX_HOST_CMD,  \
+				.lmac_offset = 0 }
+#define UDMA_LMAC_INIT	{	.eop = 1,				  \
+				.credit_group = 0x4,			  \
+				.ra_tid = UMAC_HDI_ACT_TBL_IDX_HOST_CMD,  \
+				.lmac_offset = 4 }
+
+
+/* UDMA IN OP CODE -- cmd bits [3:0] */
+#define UDMA_IN_OPCODE_MASK			0xF
+
+#define UDMA_IN_OPCODE_GENERAL_RESP		0x0
+#define UDMA_IN_OPCODE_READ_RESP		0x1
+#define UDMA_IN_OPCODE_WRITE_RESP		0x2
+#define UDMA_IN_OPCODE_PERS_WRITE_RESP		0x5
+#define UDMA_IN_OPCODE_PERS_READ_RESP		0x6
+#define UDMA_IN_OPCODE_RD_MDFY_WR_RESP		0x7
+#define UDMA_IN_OPCODE_EP_MNGMT_MSG		0x8
+#define UDMA_IN_OPCODE_CRDT_CHNG_MSG		0x9
+#define UDMA_IN_OPCODE_CNTRL_DATABASE_MSG	0xA
+#define UDMA_IN_OPCODE_SW_MSG			0xB
+#define UDMA_IN_OPCODE_WIFI			0xF
+#define UDMA_IN_OPCODE_WIFI_LMAC		0x1F
+#define UDMA_IN_OPCODE_WIFI_UMAC		0x2F
+
+/* HW API: udma_hdi_nonwifi API (OUT and IN) */
+
+/* iwm_udma_nonwifi_cmd request response -- bits [9:9] */
+#define UDMA_HDI_OUT_NW_CMD_RESP_POS		9
+#define UDMA_HDI_OUT_NW_CMD_RESP_SEED		0x1
+
+/* iwm_udma_nonwifi_cmd handle by HW -- bits [11:11] */
+#define UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW_POS	11
+#define UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW_SEED	0x1
+
+/* iwm_udma_nonwifi_cmd sequence-number -- bits [12:15] */
+#define UDMA_HDI_OUT_NW_CMD_SEQ_NUM_POS		12
+#define UDMA_HDI_OUT_NW_CMD_SEQ_NUM_SEED	0xF
+
+/* UDMA IN Non-WIFI HW sequence number -- bits [12:15] */
+#define UDMA_IN_NW_HW_SEQ_NUM_POS		12
+#define UDMA_IN_NW_HW_SEQ_NUM_SEED		0xF
+
+/* UDMA IN Non-WIFI HW signature -- bits [16:31] */
+#define UDMA_IN_NW_HW_SIG_POS			16
+#define UDMA_IN_NW_HW_SIG_SEED			0xFFFF
+
+/* fixed signature */
+#define UDMA_IN_NW_HW_SIG			0xCBBC
+
+/* UDMA IN Non-WIFI HW block length -- bits [32:35] */
+#define UDMA_IN_NW_HW_LENGTH_SEED		0xF
+#define UDMA_IN_NW_HW_LENGTH_POS		32
+
+/* End of HW API: udma_hdi_nonwifi API (OUT and IN) */
+
+#define IWM_SDIO_FW_MAX_CHUNK_SIZE	2032
+#define IWM_MAX_WIFI_HEADERS_SIZE	32
+#define IWM_MAX_NONWIFI_HEADERS_SIZE	16
+#define IWM_MAX_NONWIFI_CMD_BUFF_SIZE	(IWM_SDIO_FW_MAX_CHUNK_SIZE - \
+					 IWM_MAX_NONWIFI_HEADERS_SIZE)
+#define IWM_MAX_WIFI_CMD_BUFF_SIZE	(IWM_SDIO_FW_MAX_CHUNK_SIZE - \
+					 IWM_MAX_WIFI_HEADERS_SIZE)
+
+#define IWM_HAL_CONCATENATE_BUF_SIZE	8192
+
+struct iwm_wifi_cmd_buff {
+	u16 len;
+	u8 *start;
+	u8 hdr[IWM_MAX_WIFI_HEADERS_SIZE];
+	u8 payload[IWM_MAX_WIFI_CMD_BUFF_SIZE];
+};
+
+struct iwm_nonwifi_cmd_buff {
+	u16 len;
+	u8 *start;
+	u8 hdr[IWM_MAX_NONWIFI_HEADERS_SIZE];
+	u8 payload[IWM_MAX_NONWIFI_CMD_BUFF_SIZE];
+};
+
+struct iwm_udma_nonwifi_cmd {
+	u8 opcode;
+	u8 eop;
+	u8 resp;
+	u8 handle_by_hw;
+	__le32 addr;
+	__le32 op1_sz;
+	__le32 op2;
+	__le16 seq_num;
+};
+
+struct iwm_udma_wifi_cmd {
+	__le16 count;
+	u8 eop;
+	u8 credit_group;
+	u8 ra_tid;
+	u8 lmac_offset;
+};
+
+struct iwm_umac_cmd {
+	u8 id;
+	__le16 count;
+	u8 resp;
+	__le16 seq_num;
+	u8 color;
+};
+
+struct iwm_lmac_cmd {
+	u8 id;
+	__le16 count;
+	u8 resp;
+	__le16 seq_num;
+};
+
+struct iwm_nonwifi_cmd {
+	u16 seq_num;
+	bool resp_received;
+	struct list_head pending;
+	struct iwm_udma_nonwifi_cmd udma_cmd;
+	struct iwm_umac_cmd umac_cmd;
+	struct iwm_lmac_cmd lmac_cmd;
+	struct iwm_nonwifi_cmd_buff buf;
+	u32 flags;
+};
+
+struct iwm_wifi_cmd {
+	u16 seq_num;
+	struct list_head pending;
+	struct iwm_udma_wifi_cmd udma_cmd;
+	struct iwm_umac_cmd umac_cmd;
+	struct iwm_lmac_cmd lmac_cmd;
+	struct iwm_wifi_cmd_buff buf;
+	u32 flags;
+};
+
+void iwm_cmd_flush(struct iwm_priv *iwm);
+
+struct iwm_wifi_cmd *iwm_get_pending_wifi_cmd(struct iwm_priv *iwm,
+					      u16 seq_num);
+struct iwm_nonwifi_cmd *iwm_get_pending_nonwifi_cmd(struct iwm_priv *iwm,
+						    u8 seq_num, u8 cmd_opcode);
+
+
+int iwm_hal_send_target_cmd(struct iwm_priv *iwm,
+			    struct iwm_udma_nonwifi_cmd *ucmd,
+			    const void *payload);
+
+int iwm_hal_send_host_cmd(struct iwm_priv *iwm,
+			  struct iwm_udma_wifi_cmd *udma_cmd,
+			  struct iwm_umac_cmd *umac_cmd,
+			  struct iwm_lmac_cmd *lmac_cmd,
+			  const void *payload, u16 payload_size);
+
+int iwm_hal_send_umac_cmd(struct iwm_priv *iwm,
+			  struct iwm_udma_wifi_cmd *udma_cmd,
+			  struct iwm_umac_cmd *umac_cmd,
+			  const void *payload, u16 payload_size);
+
+u16 iwm_alloc_wifi_cmd_seq(struct iwm_priv *iwm);
+
+void iwm_udma_wifi_hdr_set_eop(struct iwm_priv *iwm, u8 *buf, u8 eop);
+void iwm_build_udma_wifi_hdr(struct iwm_priv *iwm,
+			     struct iwm_udma_out_wifi_hdr *hdr,
+			     struct iwm_udma_wifi_cmd *cmd);
+void iwm_build_umac_hdr(struct iwm_priv *iwm,
+			struct iwm_umac_fw_cmd_hdr *hdr,
+			struct iwm_umac_cmd *cmd);
+#endif /* _IWM_HAL_H_ */
diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
new file mode 100644
index 0000000..3b29681
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
@@ -0,0 +1,350 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 of 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.
+ *
+ *
+ * Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ */
+
+#ifndef __IWM_H__
+#define __IWM_H__
+
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <net/cfg80211.h>
+
+#include "debug.h"
+#include "hal.h"
+#include "umac.h"
+#include "lmac.h"
+#include "eeprom.h"
+
+#define IWM_COPYRIGHT "Copyright(c) 2009 Intel Corporation"
+#define IWM_AUTHOR "<ilw@linux.intel.com>"
+
+#define CONFIG_IWM_B0_HW_SUPPORT	1
+
+#define IWM_SRC_LMAC	UMAC_HDI_IN_SOURCE_FHRX
+#define IWM_SRC_UDMA	UMAC_HDI_IN_SOURCE_UDMA
+#define IWM_SRC_UMAC	UMAC_HDI_IN_SOURCE_FW
+#define IWM_SRC_NUM	3
+
+#define IWM_POWER_INDEX_MIN	0
+#define IWM_POWER_INDEX_MAX	5
+#define IWM_POWER_INDEX_DEFAULT	3
+
+struct iwm_conf {
+	u32 sdio_ior_timeout;
+	unsigned long init_calib_map;
+	unsigned long periodic_calib_map;
+	bool reset_on_fatal_err;
+	bool auto_connect;
+	bool wimax_not_present;
+	bool enable_qos;
+	u32 mode;
+
+	u32 power_index;
+	u32 frag_threshold;
+	u32 rts_threshold;
+	bool cts_to_self;
+
+	u32 assoc_timeout;
+	u32 roam_timeout;
+	u32 wireless_mode;
+	u32 coexist_mode;
+
+	u8 ibss_band;
+	u8 ibss_channel;
+
+	u8 mac_addr[ETH_ALEN];
+#ifdef CONFIG_IWM_B0_HW_SUPPORT
+	bool hw_b0;
+#endif
+};
+
+enum {
+	COEX_MODE_SA = 1,
+	COEX_MODE_XOR,
+	COEX_MODE_CM,
+	COEX_MODE_MAX,
+};
+
+struct iwm_if_ops;
+struct iwm_wifi_cmd;
+
+struct pool_entry {
+	int id;		/* group id */
+	int sid;	/* super group id */
+	int min_pages;	/* min capacity in pages */
+	int max_pages;	/* max capacity in pages */
+	int alloc_pages;	/* allocated # of pages. incresed by driver */
+	int total_freed_pages;	/* total freed # of pages. incresed by UMAC */
+};
+
+struct spool_entry {
+	int id;
+	int max_pages;
+	int alloc_pages;
+};
+
+struct iwm_tx_credit {
+	spinlock_t lock;
+	int pool_nr;
+	unsigned long full_pools_map; /* bitmap for # of filled tx pools */
+	struct pool_entry pools[IWM_MACS_OUT_GROUPS];
+	struct spool_entry spools[IWM_MACS_OUT_SGROUPS];
+};
+
+struct iwm_notif {
+	struct list_head pending;
+	u32 cmd_id;
+	void *cmd;
+	u8 src;
+	void *buf;
+	unsigned long buf_size;
+};
+
+struct iwm_sta_info {
+	u8 addr[ETH_ALEN];
+	bool valid;
+	bool qos;
+	u8 color;
+};
+
+struct iwm_tx_info {
+	u8 sta;
+	u8 color;
+	u8 tid;
+};
+
+struct iwm_rx_info {
+	unsigned long rx_size;
+	unsigned long rx_buf_size;
+};
+
+#define IWM_NUM_KEYS 4
+
+struct iwm_umac_key_hdr {
+	u8 mac[ETH_ALEN];
+	u8 key_idx;
+	u8 multicast; /* BCast encrypt & BCast decrypt of frames FROM mac */
+} __attribute__ ((packed));
+
+struct iwm_key {
+	struct iwm_umac_key_hdr hdr;
+	u8 in_use;
+	u8 alg;
+	u32 flags;
+	u8 tx_seq[IW_ENCODE_SEQ_MAX_SIZE];
+	u8 rx_seq[IW_ENCODE_SEQ_MAX_SIZE];
+	u8 key_len;
+	u8 key[32];
+};
+
+#define IWM_RX_ID_HASH  0xff
+#define IWM_RX_ID_GET_HASH(id) ((id) % IWM_RX_ID_HASH)
+
+#define IWM_STA_TABLE_NUM	16
+#define IWM_TX_LIST_SIZE	64
+#define IWM_RX_LIST_SIZE        256
+
+#define IWM_SCAN_ID_MAX 0xff
+
+#define IWM_STATUS_READY		0
+#define IWM_STATUS_SCANNING		1
+#define IWM_STATUS_SCAN_ABORTING	2
+#define IWM_STATUS_ASSOCIATING		3
+#define IWM_STATUS_ASSOCIATED		4
+
+#define IWM_RADIO_RFKILL_OFF		0
+#define IWM_RADIO_RFKILL_HW		1
+#define IWM_RADIO_RFKILL_SW		2
+
+struct iwm_tx_queue {
+	int id;
+	struct sk_buff_head queue;
+	struct workqueue_struct *wq;
+	struct work_struct worker;
+	u8 concat_buf[IWM_HAL_CONCATENATE_BUF_SIZE];
+	int concat_count;
+	u8 *concat_ptr;
+};
+
+/* Queues 0 ~ 3 for AC data, 5 for iPAN */
+#define IWM_TX_QUEUES		5
+#define IWM_TX_DATA_QUEUES	4
+#define IWM_TX_CMD_QUEUE	4
+
+struct iwm_bss_info {
+	struct list_head node;
+	struct cfg80211_bss *cfg_bss;
+	struct iwm_umac_notif_bss_info *bss;
+};
+
+typedef int (*iwm_handler)(struct iwm_priv *priv, u8 *buf,
+			   unsigned long buf_size, struct iwm_wifi_cmd *cmd);
+
+#define IWM_WATCHDOG_PERIOD	(6 * HZ)
+
+struct iwm_priv {
+	struct wireless_dev *wdev;
+	struct iwm_if_ops *bus_ops;
+
+	struct iwm_conf conf;
+
+	unsigned long status;
+	unsigned long radio;
+
+	struct list_head pending_notif;
+	wait_queue_head_t notif_queue;
+
+	wait_queue_head_t nonwifi_queue;
+
+	unsigned long calib_done_map;
+	struct {
+		u8 *buf;
+		u32 size;
+	} calib_res[CALIBRATION_CMD_NUM];
+
+	struct iwm_umac_profile *umac_profile;
+	bool umac_profile_active;
+
+	u8 bssid[ETH_ALEN];
+	u8 channel;
+	u16 rate;
+
+	struct iwm_sta_info sta_table[IWM_STA_TABLE_NUM];
+	struct list_head bss_list;
+
+	void (*nonwifi_rx_handlers[UMAC_HDI_IN_OPCODE_NONWIFI_MAX])
+	(struct iwm_priv *priv, u8 *buf, unsigned long buf_size);
+
+	const iwm_handler *umac_handlers;
+	const iwm_handler *lmac_handlers;
+	DECLARE_BITMAP(lmac_handler_map, LMAC_COMMAND_ID_NUM);
+	DECLARE_BITMAP(umac_handler_map, LMAC_COMMAND_ID_NUM);
+	DECLARE_BITMAP(udma_handler_map, LMAC_COMMAND_ID_NUM);
+
+	struct list_head wifi_pending_cmd;
+	struct list_head nonwifi_pending_cmd;
+	u16 wifi_seq_num;
+	u8 nonwifi_seq_num;
+	spinlock_t cmd_lock;
+
+	u32 core_enabled;
+
+	u8 scan_id;
+	struct cfg80211_scan_request *scan_request;
+
+	struct sk_buff_head rx_list;
+	struct list_head rx_tickets;
+	struct list_head rx_packets[IWM_RX_ID_HASH];
+	struct workqueue_struct *rx_wq;
+	struct work_struct rx_worker;
+
+	struct iwm_tx_credit tx_credit;
+	struct iwm_tx_queue txq[IWM_TX_QUEUES];
+
+	struct iwm_key keys[IWM_NUM_KEYS];
+	struct iwm_key *default_key;
+
+	wait_queue_head_t mlme_queue;
+
+	struct iw_statistics wstats;
+	struct delayed_work stats_request;
+
+	struct iwm_debugfs dbg;
+
+	u8 *eeprom;
+	struct timer_list watchdog;
+	struct work_struct reset_worker;
+	struct rfkill *rfkill;
+
+	char private[0] __attribute__((__aligned__(NETDEV_ALIGN)));
+};
+
+static inline void *iwm_private(struct iwm_priv *iwm)
+{
+	BUG_ON(!iwm);
+	return &iwm->private;
+}
+
+#define hw_to_iwm(h) (h->iwm)
+#define iwm_to_dev(i) (wiphy_dev(i->wdev->wiphy))
+#define iwm_to_wiphy(i) (i->wdev->wiphy)
+#define wiphy_to_iwm(w) (struct iwm_priv *)(wiphy_priv(w))
+#define iwm_to_wdev(i) (i->wdev)
+#define wdev_to_iwm(w) (struct iwm_priv *)(wdev_priv(w))
+#define iwm_to_ndev(i) (i->wdev->netdev)
+#define ndev_to_iwm(n) (wdev_to_iwm(n->ieee80211_ptr))
+#define skb_to_rx_info(s) ((struct iwm_rx_info *)(s->cb))
+#define skb_to_tx_info(s) ((struct iwm_tx_info *)s->cb)
+
+extern const struct iw_handler_def iwm_iw_handler_def;
+
+void *iwm_if_alloc(int sizeof_bus, struct device *dev,
+		   struct iwm_if_ops *if_ops);
+void iwm_if_free(struct iwm_priv *iwm);
+int iwm_mode_to_nl80211_iftype(int mode);
+int iwm_priv_init(struct iwm_priv *iwm);
+void iwm_reset(struct iwm_priv *iwm);
+void iwm_tx_credit_init_pools(struct iwm_priv *iwm,
+			      struct iwm_umac_notif_alive *alive);
+int iwm_tx_credit_alloc(struct iwm_priv *iwm, int id, int nb);
+int iwm_notif_send(struct iwm_priv *iwm, struct iwm_wifi_cmd *cmd,
+		   u8 cmd_id, u8 source, u8 *buf, unsigned long buf_size);
+int iwm_notif_handle(struct iwm_priv *iwm, u32 cmd, u8 source, long timeout);
+void iwm_init_default_profile(struct iwm_priv *iwm,
+			      struct iwm_umac_profile *profile);
+void iwm_link_on(struct iwm_priv *iwm);
+void iwm_link_off(struct iwm_priv *iwm);
+int iwm_up(struct iwm_priv *iwm);
+int iwm_down(struct iwm_priv *iwm);
+
+/* TX API */
+void iwm_tx_credit_inc(struct iwm_priv *iwm, int id, int total_freed_pages);
+void iwm_tx_worker(struct work_struct *work);
+int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
+
+/* RX API */
+void iwm_rx_setup_handlers(struct iwm_priv *iwm);
+int iwm_rx_handle(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size);
+int iwm_rx_handle_resp(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size,
+		       struct iwm_wifi_cmd *cmd);
+void iwm_rx_free(struct iwm_priv *iwm);
+
+/* RF Kill API */
+int iwm_rfkill_init(struct iwm_priv *iwm);
+void iwm_rfkill_exit(struct iwm_priv *iwm);
+
+#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/lmac.h b/drivers/net/wireless/iwmc3200wifi/lmac.h
new file mode 100644
index 0000000..db2e5ee
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/lmac.h
@@ -0,0 +1,457 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 of 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.
+ *
+ *
+ * Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ */
+
+#ifndef __IWM_LMAC_H__
+#define __IWM_LMAC_H__
+
+struct iwm_lmac_hdr {
+	u8 id;
+	u8 flags;
+	__le16 seq_num;
+} __attribute__ ((packed));
+
+/* LMAC commands */
+#define CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_AFTER_MSK  0x1
+
+struct iwm_lmac_cal_cfg_elt {
+	__le32 enable; /* 1 means LMAC needs to do something */
+	__le32 start;  /* 1 to start calibration, 0 to stop */
+	__le32 send_res; /* 1 for sending back results */
+	__le32 apply_res; /* 1 for applying calibration results to HW */
+	__le32 reserved;
+} __attribute__ ((packed));
+
+struct iwm_lmac_cal_cfg_status {
+	struct iwm_lmac_cal_cfg_elt init;
+	struct iwm_lmac_cal_cfg_elt periodic;
+	__le32 flags; /* CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_AFTER_MSK */
+} __attribute__ ((packed));
+
+struct iwm_lmac_cal_cfg_cmd {
+	struct iwm_lmac_cal_cfg_status ucode_cfg;
+	struct iwm_lmac_cal_cfg_status driver_cfg;
+	__le32 reserved;
+} __attribute__ ((packed));
+
+struct iwm_lmac_cal_cfg_resp {
+	__le32 status;
+} __attribute__ ((packed));
+
+#define IWM_CARD_STATE_SW_HW_ENABLED	0x00
+#define IWM_CARD_STATE_HW_DISABLED	0x01
+#define IWM_CARD_STATE_SW_DISABLED	0x02
+#define IWM_CARD_STATE_CTKILL_DISABLED	0x04
+#define IWM_CARD_STATE_IS_RXON		0x10
+
+struct iwm_lmac_card_state {
+	__le32 flags;
+} __attribute__ ((packed));
+
+/**
+ * COEX_PRIORITY_TABLE_CMD
+ *
+ * Priority entry for each state
+ * Will keep two tables, for STA and WIPAN
+ */
+enum {
+	/* UN-ASSOCIATION PART */
+	COEX_UNASSOC_IDLE = 0,
+	COEX_UNASSOC_MANUAL_SCAN,
+	COEX_UNASSOC_AUTO_SCAN,
+
+	/* CALIBRATION */
+	COEX_CALIBRATION,
+	COEX_PERIODIC_CALIBRATION,
+
+	/* CONNECTION */
+	COEX_CONNECTION_ESTAB,
+
+	/* ASSOCIATION PART */
+	COEX_ASSOCIATED_IDLE,
+	COEX_ASSOC_MANUAL_SCAN,
+	COEX_ASSOC_AUTO_SCAN,
+	COEX_ASSOC_ACTIVE_LEVEL,
+
+	/* RF ON/OFF */
+	COEX_RF_ON,
+	COEX_RF_OFF,
+	COEX_STAND_ALONE_DEBUG,
+
+	/* IPNN */
+	COEX_IPAN_ASSOC_LEVEL,
+
+	/* RESERVED */
+	COEX_RSRVD1,
+	COEX_RSRVD2,
+
+	COEX_EVENTS_NUM
+};
+
+#define COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK	0x1
+#define COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK	0x2
+#define COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK	0x4
+
+struct coex_event {
+	u8 req_prio;
+	u8 win_med_prio;
+	u8 reserved;
+	u8 flags;
+} __attribute__ ((packed));
+
+#define COEX_FLAGS_STA_TABLE_VALID_MSK		0x1
+#define COEX_FLAGS_UNASSOC_WAKEUP_UMASK_MSK	0x4
+#define COEX_FLAGS_ASSOC_WAKEUP_UMASK_MSK	0x8
+#define COEX_FLAGS_COEX_ENABLE_MSK		0x80
+
+struct iwm_coex_prio_table_cmd {
+	u8 flags;
+	u8 reserved[3];
+	struct coex_event sta_prio[COEX_EVENTS_NUM];
+} __attribute__ ((packed));
+
+/* Coexistence definitions
+ *
+ * Constants to fill in the Priorities' Tables
+ * RP - Requested Priority
+ * WP - Win Medium Priority: priority assigned when the contention has been won
+ * FLAGS - Combination of COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK and
+ * 	   COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK
+ */
+
+#define COEX_UNASSOC_IDLE_FLAGS		0
+#define COEX_UNASSOC_MANUAL_SCAN_FLAGS	(COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
+					 COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
+#define COEX_UNASSOC_AUTO_SCAN_FLAGS	(COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
+					 COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
+#define COEX_CALIBRATION_FLAGS		(COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
+					 COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
+#define COEX_PERIODIC_CALIBRATION_FLAGS	0
+/* COEX_CONNECTION_ESTAB: we need DELAY_MEDIUM_FREE_NTFY to let WiMAX
+ * disconnect from network. */
+#define COEX_CONNECTION_ESTAB_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
+				     COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK | \
+				     COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK)
+#define COEX_ASSOCIATED_IDLE_FLAGS	0
+#define COEX_ASSOC_MANUAL_SCAN_FLAGS	(COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
+					 COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
+#define COEX_ASSOC_AUTO_SCAN_FLAGS	(COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
+					 COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
+#define COEX_ASSOC_ACTIVE_LEVEL_FLAGS	0
+#define COEX_RF_ON_FLAGS		0
+#define COEX_RF_OFF_FLAGS		0
+#define COEX_STAND_ALONE_DEBUG_FLAGS	(COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
+					 COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
+#define COEX_IPAN_ASSOC_LEVEL_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
+				     COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK | \
+				     COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK)
+#define COEX_RSRVD1_FLAGS		0
+#define COEX_RSRVD2_FLAGS		0
+/* XOR_RF_ON is the event wrapping all radio ownership. We need
+ * DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network. */
+#define COEX_XOR_RF_ON_FLAGS	    (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
+				     COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK | \
+				     COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK)
+
+/* LMAC OP CODES */
+#define REPLY_PAD			0x0
+#define REPLY_ALIVE			0x1
+#define REPLY_ERROR			0x2
+#define REPLY_ECHO			0x3
+#define REPLY_HALT			0x6
+
+/* RXON state commands */
+#define REPLY_RX_ON			0x10
+#define REPLY_RX_ON_ASSOC		0x11
+#define REPLY_RX_OFF			0x12
+#define REPLY_QOS_PARAM			0x13
+#define REPLY_RX_ON_TIMING		0x14
+#define REPLY_INTERNAL_QOS_PARAM	0x15
+#define REPLY_RX_INT_TIMEOUT_CNFG	0x16
+#define REPLY_NULL			0x17
+
+/* Multi-Station support */
+#define REPLY_ADD_STA			0x18
+#define REPLY_REMOVE_STA		0x19
+#define REPLY_RESET_ALL_STA		0x1a
+
+/* RX, TX */
+#define REPLY_ALM_RX			0x1b
+#define REPLY_TX			0x1c
+#define REPLY_TXFIFO_FLUSH		0x1e
+
+/* MISC commands */
+#define REPLY_MGMT_MCAST_KEY		0x1f
+#define REPLY_WEPKEY			0x20
+#define REPLY_INIT_IV			0x21
+#define REPLY_WRITE_MIB			0x22
+#define REPLY_READ_MIB			0x23
+#define REPLY_RADIO_FE			0x24
+#define REPLY_TXFIFO_CFG		0x25
+#define REPLY_WRITE_READ		0x26
+#define REPLY_INSTALL_SEC_KEY		0x27
+
+
+#define REPLY_RATE_SCALE		0x47
+#define REPLY_LEDS_CMD			0x48
+#define REPLY_TX_LINK_QUALITY_CMD	0x4e
+#define REPLY_ANA_MIB_OVERRIDE_CMD	0x4f
+#define REPLY_WRITE2REG_CMD		0x50
+
+/* winfi-wifi coexistence */
+#define COEX_PRIORITY_TABLE_CMD		0x5a
+#define COEX_MEDIUM_NOTIFICATION	0x5b
+#define COEX_EVENT_CMD			0x5c
+
+/* more Protocol and Protocol-test commands */
+#define REPLY_MAX_SLEEP_TIME_CMD	0x61
+#define CALIBRATION_CFG_CMD		0x65
+#define CALIBRATION_RES_NOTIFICATION	0x66
+#define CALIBRATION_COMPLETE_NOTIFICATION	0x67
+
+/* Measurements */
+#define REPLY_QUIET_CMD			0x71
+#define REPLY_CHANNEL_SWITCH		0x72
+#define CHANNEL_SWITCH_NOTIFICATION	0x73
+
+#define REPLY_SPECTRUM_MEASUREMENT_CMD	0x74
+#define SPECTRUM_MEASURE_NOTIFICATION	0x75
+#define REPLY_MEASUREMENT_ABORT_CMD	0x76
+
+/* Power Management */
+#define POWER_TABLE_CMD			0x77
+#define SAVE_RESTORE_ADRESS_CMD		0x78
+#define REPLY_WATERMARK_CMD		0x79
+#define PM_DEBUG_STATISTIC_NOTIFIC	0x7B
+#define PD_FLUSH_N_NOTIFICATION		0x7C
+
+/* Scan commands and notifications */
+#define REPLY_SCAN_REQUEST_CMD		0x80
+#define REPLY_SCAN_ABORT_CMD		0x81
+#define SCAN_START_NOTIFICATION		0x82
+#define SCAN_RESULTS_NOTIFICATION	0x83
+#define SCAN_COMPLETE_NOTIFICATION	0x84
+
+/* Continuous TX commands */
+#define REPLY_CONT_TX_CMD		0x85
+#define END_OF_CONT_TX_NOTIFICATION	0x86
+
+/* Timer/Eeprom commands */
+#define TIMER_CMD			0x87
+#define EEPROM_WRITE_CMD		0x88
+
+/* PAPD commands */
+#define FEEDBACK_REQUEST_NOTIFICATION	0x8b
+#define REPLY_CW_CMD			0x8c
+
+/* IBSS/AP commands Continue */
+#define BEACON_NOTIFICATION		0x90
+#define REPLY_TX_BEACON			0x91
+#define REPLY_REQUEST_ATIM		0x93
+#define WHO_IS_AWAKE_NOTIFICATION	0x94
+#define TX_PWR_DBM_LIMIT_CMD		0x95
+#define QUIET_NOTIFICATION		0x96
+#define TX_PWR_TABLE_CMD		0x97
+#define TX_ANT_CONFIGURATION_CMD	0x98
+#define MEASURE_ABORT_NOTIFICATION	0x99
+#define REPLY_CALIBRATION_TUNE		0x9a
+
+/* bt config command */
+#define REPLY_BT_CONFIG			0x9b
+#define REPLY_STATISTICS_CMD		0x9c
+#define STATISTICS_NOTIFICATION		0x9d
+
+/* RF-KILL commands and notifications */
+#define REPLY_CARD_STATE_CMD		0xa0
+#define CARD_STATE_NOTIFICATION		0xa1
+
+/* Missed beacons notification */
+#define MISSED_BEACONS_NOTIFICATION	0xa2
+#define MISSED_BEACONS_NOTIFICATION_TH_CMD	0xa3
+
+#define REPLY_CT_KILL_CONFIG_CMD	0xa4
+
+/* HD commands and notifications */
+#define REPLY_HD_PARAMS_CMD		0xa6
+#define HD_PARAMS_NOTIFICATION		0xa7
+#define SENSITIVITY_CMD			0xa8
+#define U_APSD_PARAMS_CMD		0xa9
+#define NOISY_PLATFORM_CMD		0xaa
+#define ILLEGAL_CMD			0xac
+#define REPLY_PHY_CALIBRATION_CMD	0xb0
+#define REPLAY_RX_GAIN_CALIB_CMD	0xb1
+
+/* WiPAN commands */
+#define REPLY_WIPAN_PARAMS_CMD		0xb2
+#define REPLY_WIPAN_RX_ON_CMD		0xb3
+#define REPLY_WIPAN_RX_ON_TIMING	0xb4
+#define REPLY_WIPAN_TX_PWR_TABLE_CMD	0xb5
+#define REPLY_WIPAN_RXON_ASSOC_CMD	0xb6
+#define REPLY_WIPAN_QOS_PARAM		0xb7
+#define WIPAN_REPLY_WEPKEY		0xb8
+
+/* BeamForming commands */
+#define BEAMFORMER_CFG_CMD		0xba
+#define BEAMFORMEE_NOTIFICATION		0xbb
+
+/* TGn new Commands */
+#define REPLY_RX_PHY_CMD		0xc0
+#define REPLY_RX_MPDU_CMD		0xc1
+#define REPLY_MULTICAST_HASH		0xc2
+#define REPLY_KDR_RX			0xc3
+#define REPLY_RX_DSP_EXT_INFO		0xc4
+#define REPLY_COMPRESSED_BA		0xc5
+
+/* PNC commands */
+#define PNC_CONFIG_CMD			0xc8
+#define PNC_UPDATE_TABLE_CMD		0xc9
+#define XVT_GENERAL_CTRL_CMD		0xca
+#define REPLY_LEGACY_RADIO_FE		0xdd
+
+/* WoWLAN commands */
+#define WOWLAN_PATTERNS			0xe0
+#define WOWLAN_WAKEUP_FILTER		0xe1
+#define WOWLAN_TSC_RSC_PARAM		0xe2
+#define WOWLAN_TKIP_PARAM		0xe3
+#define WOWLAN_KEK_KCK_MATERIAL		0xe4
+#define WOWLAN_GET_STATUSES		0xe5
+#define WOWLAN_TX_POWER_PER_DB		0xe6
+#define REPLY_WOWLAN_GET_STATUSES       WOWLAN_GET_STATUSES
+
+#define REPLY_DEBUG_CMD			0xf0
+#define REPLY_DSP_DEBUG_CMD		0xf1
+#define REPLY_DEBUG_MONITOR_CMD		0xf2
+#define REPLY_DEBUG_XVT_CMD		0xf3
+#define REPLY_DEBUG_DC_CALIB		0xf4
+#define REPLY_DYNAMIC_BP		0xf5
+
+/* General purpose Commands */
+#define REPLY_GP1_CMD			0xfa
+#define REPLY_GP2_CMD			0xfb
+#define REPLY_GP3_CMD			0xfc
+#define REPLY_GP4_CMD			0xfd
+#define REPLY_REPLAY_WRAPPER		0xfe
+#define REPLY_FRAME_DURATION_CALC_CMD	0xff
+
+#define LMAC_COMMAND_ID_MAX		0xff
+#define LMAC_COMMAND_ID_NUM		(LMAC_COMMAND_ID_MAX + 1)
+
+
+/* Calibration */
+
+enum {
+	PHY_CALIBRATE_DC_CMD			= 0,
+	PHY_CALIBRATE_LO_CMD			= 1,
+	PHY_CALIBRATE_RX_BB_CMD			= 2,
+	PHY_CALIBRATE_TX_IQ_CMD			= 3,
+	PHY_CALIBRATE_RX_IQ_CMD			= 4,
+	PHY_CALIBRATION_NOISE_CMD		= 5,
+	PHY_CALIBRATE_AGC_TABLE_CMD		= 6,
+	PHY_CALIBRATE_CRYSTAL_FRQ_CMD		= 7,
+	PHY_CALIBRATE_OPCODES_NUM,
+	SHILOH_PHY_CALIBRATE_DC_CMD		= 8,
+	SHILOH_PHY_CALIBRATE_LO_CMD		= 9,
+	SHILOH_PHY_CALIBRATE_RX_BB_CMD		= 10,
+	SHILOH_PHY_CALIBRATE_TX_IQ_CMD		= 11,
+	SHILOH_PHY_CALIBRATE_RX_IQ_CMD		= 12,
+	SHILOH_PHY_CALIBRATION_NOISE_CMD	= 13,
+	SHILOH_PHY_CALIBRATE_AGC_TABLE_CMD	= 14,
+	SHILOH_PHY_CALIBRATE_CRYSTAL_FRQ_CMD	= 15,
+	SHILOH_PHY_CALIBRATE_BASE_BAND_CMD	= 16,
+	SHILOH_PHY_CALIBRATE_TXIQ_PERIODIC_CMD	= 17,
+	CALIBRATION_CMD_NUM,
+};
+
+struct iwm_lmac_calib_hdr {
+	u8 opcode;
+	u8 first_grp;
+	u8 grp_num;
+	u8 all_data_valid;
+} __attribute__ ((packed));
+
+#define IWM_LMAC_CALIB_FREQ_GROUPS_NR	7
+#define IWM_CALIB_FREQ_GROUPS_NR	5
+#define IWM_CALIB_DC_MODES_NR		12
+
+struct iwm_calib_rxiq_entry {
+	u16 ptam_postdist_ars;
+	u16 ptam_postdist_arc;
+} __attribute__ ((packed));
+
+struct iwm_calib_rxiq_group {
+	struct iwm_calib_rxiq_entry mode[IWM_CALIB_DC_MODES_NR];
+} __attribute__ ((packed));
+
+struct iwm_lmac_calib_rxiq {
+	struct iwm_calib_rxiq_group group[IWM_LMAC_CALIB_FREQ_GROUPS_NR];
+} __attribute__ ((packed));
+
+struct iwm_calib_rxiq {
+	struct iwm_lmac_calib_hdr hdr;
+	struct iwm_calib_rxiq_group group[IWM_CALIB_FREQ_GROUPS_NR];
+} __attribute__ ((packed));
+
+#define LMAC_STA_ID_SEED	0x0f
+#define LMAC_STA_ID_POS		0
+
+#define LMAC_STA_COLOR_SEED	0x7
+#define LMAC_STA_COLOR_POS	4
+
+struct iwm_lmac_power_report {
+	u8 pa_status;
+	u8 pa_integ_res_A[3];
+	u8 pa_integ_res_B[3];
+	u8 pa_integ_res_C[3];
+} __attribute__ ((packed));
+
+struct iwm_lmac_tx_resp {
+	u8 frame_cnt; /* 1-no aggregation, greater then 1 - aggregation */
+	u8 bt_kill_cnt;
+	__le16 retry_cnt;
+	__le32 initial_tx_rate;
+	__le16 wireless_media_time;
+	struct iwm_lmac_power_report power_report;
+	__le32 tfd_info;
+	__le16 seq_ctl;
+	__le16 byte_cnt;
+	u8 tlc_rate_info;
+	u8 ra_tid;
+	__le16 frame_ctl;
+	__le32 status;
+} __attribute__ ((packed));
+
+#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
new file mode 100644
index 0000000..6a2640f
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/main.c
@@ -0,0 +1,680 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 of 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.
+ *
+ *
+ * Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/ieee80211.h>
+#include <linux/wireless.h>
+
+#include "iwm.h"
+#include "debug.h"
+#include "bus.h"
+#include "umac.h"
+#include "commands.h"
+#include "hal.h"
+#include "fw.h"
+#include "rx.h"
+
+static struct iwm_conf def_iwm_conf = {
+
+	.sdio_ior_timeout	= 5000,
+	.init_calib_map		= BIT(PHY_CALIBRATE_DC_CMD)	|
+				  BIT(PHY_CALIBRATE_LO_CMD)	|
+				  BIT(PHY_CALIBRATE_TX_IQ_CMD)	|
+				  BIT(PHY_CALIBRATE_RX_IQ_CMD),
+	.periodic_calib_map	= BIT(PHY_CALIBRATE_DC_CMD)	|
+				  BIT(PHY_CALIBRATE_LO_CMD)	|
+				  BIT(PHY_CALIBRATE_TX_IQ_CMD)	|
+				  BIT(PHY_CALIBRATE_RX_IQ_CMD)	|
+				  BIT(SHILOH_PHY_CALIBRATE_BASE_BAND_CMD),
+	.reset_on_fatal_err	= 1,
+	.auto_connect		= 1,
+	.wimax_not_present	= 0,
+	.enable_qos		= 1,
+	.mode			= UMAC_MODE_BSS,
+
+	/* UMAC configuration */
+	.power_index		= 0,
+	.frag_threshold		= IEEE80211_MAX_FRAG_THRESHOLD,
+	.rts_threshold		= IEEE80211_MAX_RTS_THRESHOLD,
+	.cts_to_self		= 0,
+
+	.assoc_timeout		= 2,
+	.roam_timeout		= 10,
+	.wireless_mode		= WIRELESS_MODE_11A | WIRELESS_MODE_11G,
+	.coexist_mode		= COEX_MODE_CM,
+
+	/* IBSS */
+	.ibss_band		= UMAC_BAND_2GHZ,
+	.ibss_channel		= 1,
+
+	.mac_addr		= {0x00, 0x02, 0xb3, 0x01, 0x02, 0x03},
+};
+
+static int modparam_reset;
+module_param_named(reset, modparam_reset, bool, 0644);
+MODULE_PARM_DESC(reset, "reset on firmware errors (default 0 [not reset])");
+
+int iwm_mode_to_nl80211_iftype(int mode)
+{
+	switch (mode) {
+	case UMAC_MODE_BSS:
+		return NL80211_IFTYPE_STATION;
+	case UMAC_MODE_IBSS:
+		return NL80211_IFTYPE_ADHOC;
+	default:
+		return NL80211_IFTYPE_UNSPECIFIED;
+	}
+
+	return 0;
+}
+
+static void iwm_statistics_request(struct work_struct *work)
+{
+	struct iwm_priv *iwm =
+		container_of(work, struct iwm_priv, stats_request.work);
+
+	iwm_send_umac_stats_req(iwm, 0);
+}
+
+static void iwm_reset_worker(struct work_struct *work)
+{
+	struct iwm_priv *iwm;
+	struct iwm_umac_profile *profile = NULL;
+	int uninitialized_var(ret), retry = 0;
+
+	iwm = container_of(work, struct iwm_priv, reset_worker);
+
+	if (iwm->umac_profile_active) {
+		profile = kmalloc(sizeof(struct iwm_umac_profile), GFP_KERNEL);
+		if (profile)
+			memcpy(profile, iwm->umac_profile, sizeof(*profile));
+		else
+			IWM_ERR(iwm, "Couldn't alloc memory for profile\n");
+	}
+
+	iwm_down(iwm);
+
+	while (retry++ < 3) {
+		ret = iwm_up(iwm);
+		if (!ret)
+			break;
+
+		schedule_timeout_uninterruptible(10 * HZ);
+	}
+
+	if (ret) {
+		IWM_WARN(iwm, "iwm_up() failed: %d\n", ret);
+
+		kfree(profile);
+		return;
+	}
+
+	if (profile) {
+		IWM_DBG_MLME(iwm, DBG, "Resend UMAC profile\n");
+		memcpy(iwm->umac_profile, profile, sizeof(*profile));
+		iwm_send_mlme_profile(iwm);
+		kfree(profile);
+	}
+}
+
+static void iwm_watchdog(unsigned long data)
+{
+	struct iwm_priv *iwm = (struct iwm_priv *)data;
+
+	IWM_WARN(iwm, "Watchdog expired: UMAC stalls!\n");
+
+	if (modparam_reset)
+		schedule_work(&iwm->reset_worker);
+}
+
+int iwm_priv_init(struct iwm_priv *iwm)
+{
+	int i;
+	char name[32];
+
+	iwm->status = 0;
+	INIT_LIST_HEAD(&iwm->pending_notif);
+	init_waitqueue_head(&iwm->notif_queue);
+	init_waitqueue_head(&iwm->nonwifi_queue);
+	init_waitqueue_head(&iwm->mlme_queue);
+	memcpy(&iwm->conf, &def_iwm_conf, sizeof(struct iwm_conf));
+	spin_lock_init(&iwm->tx_credit.lock);
+	INIT_LIST_HEAD(&iwm->wifi_pending_cmd);
+	INIT_LIST_HEAD(&iwm->nonwifi_pending_cmd);
+	iwm->wifi_seq_num = UMAC_WIFI_SEQ_NUM_BASE;
+	iwm->nonwifi_seq_num = UMAC_NONWIFI_SEQ_NUM_BASE;
+	spin_lock_init(&iwm->cmd_lock);
+	iwm->scan_id = 1;
+	INIT_DELAYED_WORK(&iwm->stats_request, iwm_statistics_request);
+	INIT_WORK(&iwm->reset_worker, iwm_reset_worker);
+	INIT_LIST_HEAD(&iwm->bss_list);
+
+	skb_queue_head_init(&iwm->rx_list);
+	INIT_LIST_HEAD(&iwm->rx_tickets);
+	for (i = 0; i < IWM_RX_ID_HASH; i++)
+		INIT_LIST_HEAD(&iwm->rx_packets[i]);
+
+	INIT_WORK(&iwm->rx_worker, iwm_rx_worker);
+
+	iwm->rx_wq = create_singlethread_workqueue(KBUILD_MODNAME "_rx");
+	if (!iwm->rx_wq)
+		return -EAGAIN;
+
+	for (i = 0; i < IWM_TX_QUEUES; i++) {
+		INIT_WORK(&iwm->txq[i].worker, iwm_tx_worker);
+		snprintf(name, 32, KBUILD_MODNAME "_tx_%d", i);
+		iwm->txq[i].id = i;
+		iwm->txq[i].wq = create_singlethread_workqueue(name);
+		if (!iwm->txq[i].wq)
+			return -EAGAIN;
+
+		skb_queue_head_init(&iwm->txq[i].queue);
+	}
+
+	for (i = 0; i < IWM_NUM_KEYS; i++)
+		memset(&iwm->keys[i], 0, sizeof(struct iwm_key));
+
+	iwm->default_key = NULL;
+
+	init_timer(&iwm->watchdog);
+	iwm->watchdog.function = iwm_watchdog;
+	iwm->watchdog.data = (unsigned long)iwm;
+
+	return 0;
+}
+
+/*
+ * We reset all the structures, and we reset the UMAC.
+ * After calling this routine, you're expected to reload
+ * the firmware.
+ */
+void iwm_reset(struct iwm_priv *iwm)
+{
+	struct iwm_notif *notif, *next;
+
+	if (test_bit(IWM_STATUS_READY, &iwm->status))
+		iwm_target_reset(iwm);
+
+	iwm->status = 0;
+	iwm->scan_id = 1;
+
+	list_for_each_entry_safe(notif, next, &iwm->pending_notif, pending) {
+		list_del(&notif->pending);
+		kfree(notif->buf);
+		kfree(notif);
+	}
+
+	iwm_cmd_flush(iwm);
+
+	flush_workqueue(iwm->rx_wq);
+
+	iwm_link_off(iwm);
+}
+
+/*
+ * Notification code:
+ *
+ * We're faced with the following issue: Any host command can
+ * have an answer or not, and if there's an answer to expect,
+ * it can be treated synchronously or asynchronously.
+ * To work around the synchronous answer case, we implemented
+ * our notification mechanism.
+ * When a code path needs to wait for a command response
+ * synchronously, it calls notif_handle(), which waits for the
+ * right notification to show up, and then process it. Before
+ * starting to wait, it registered as a waiter for this specific
+ * answer (by toggling a bit in on of the handler_map), so that
+ * the rx code knows that it needs to send a notification to the
+ * waiting processes. It does so by calling iwm_notif_send(),
+ * which adds the notification to the pending notifications list,
+ * and then wakes the waiting processes up.
+ */
+int iwm_notif_send(struct iwm_priv *iwm, struct iwm_wifi_cmd *cmd,
+		   u8 cmd_id, u8 source, u8 *buf, unsigned long buf_size)
+{
+	struct iwm_notif *notif;
+
+	notif = kzalloc(sizeof(struct iwm_notif), GFP_KERNEL);
+	if (!notif) {
+		IWM_ERR(iwm, "Couldn't alloc memory for notification\n");
+		return -ENOMEM;
+	}
+
+	INIT_LIST_HEAD(&notif->pending);
+	notif->cmd = cmd;
+	notif->cmd_id = cmd_id;
+	notif->src = source;
+	notif->buf = kzalloc(buf_size, GFP_KERNEL);
+	if (!notif->buf) {
+		IWM_ERR(iwm, "Couldn't alloc notification buffer\n");
+		kfree(notif);
+		return -ENOMEM;
+	}
+	notif->buf_size = buf_size;
+	memcpy(notif->buf, buf, buf_size);
+	list_add_tail(&notif->pending, &iwm->pending_notif);
+
+	wake_up_interruptible(&iwm->notif_queue);
+
+	return 0;
+}
+
+static struct iwm_notif *iwm_notif_find(struct iwm_priv *iwm, u32 cmd,
+					u8 source)
+{
+	struct iwm_notif *notif, *next;
+
+	list_for_each_entry_safe(notif, next, &iwm->pending_notif, pending) {
+		if ((notif->cmd_id == cmd) && (notif->src == source)) {
+			list_del(&notif->pending);
+			return notif;
+		}
+	}
+
+	return NULL;
+}
+
+static struct iwm_notif *iwm_notif_wait(struct iwm_priv *iwm, u32 cmd,
+					u8 source, long timeout)
+{
+	int ret;
+	struct iwm_notif *notif;
+	unsigned long *map = NULL;
+
+	switch (source) {
+	case IWM_SRC_LMAC:
+		map = &iwm->lmac_handler_map[0];
+		break;
+	case IWM_SRC_UMAC:
+		map = &iwm->umac_handler_map[0];
+		break;
+	case IWM_SRC_UDMA:
+		map = &iwm->udma_handler_map[0];
+		break;
+	}
+
+	set_bit(cmd, map);
+
+	ret = wait_event_interruptible_timeout(iwm->notif_queue,
+			 ((notif = iwm_notif_find(iwm, cmd, source)) != NULL),
+					       timeout);
+	clear_bit(cmd, map);
+
+	if (!ret)
+		return NULL;
+
+	return notif;
+}
+
+int iwm_notif_handle(struct iwm_priv *iwm, u32 cmd, u8 source, long timeout)
+{
+	int ret;
+	struct iwm_notif *notif;
+
+	notif = iwm_notif_wait(iwm, cmd, source, timeout);
+	if (!notif)
+		return -ETIME;
+
+	ret = iwm_rx_handle_resp(iwm, notif->buf, notif->buf_size, notif->cmd);
+	kfree(notif->buf);
+	kfree(notif);
+
+	return ret;
+}
+
+static int iwm_config_boot_params(struct iwm_priv *iwm)
+{
+	struct iwm_udma_nonwifi_cmd target_cmd;
+	int ret;
+
+	/* check Wimax is off and config debug monitor */
+	if (iwm->conf.wimax_not_present) {
+		u32 data1 = 0x1f;
+		u32 addr1 = 0x606BE258;
+
+		u32 data2_set = 0x0;
+		u32 data2_clr = 0x1;
+		u32 addr2 = 0x606BE100;
+
+		u32 data3 = 0x1;
+		u32 addr3 = 0x606BEC00;
+
+		target_cmd.resp = 0;
+		target_cmd.handle_by_hw = 0;
+		target_cmd.eop = 1;
+
+		target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE;
+		target_cmd.addr = cpu_to_le32(addr1);
+		target_cmd.op1_sz = cpu_to_le32(sizeof(u32));
+		target_cmd.op2 = 0;
+
+		ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data1);
+		if (ret < 0) {
+			IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n");
+			return ret;
+		}
+
+		target_cmd.opcode = UMAC_HDI_OUT_OPCODE_READ_MODIFY_WRITE;
+		target_cmd.addr = cpu_to_le32(addr2);
+		target_cmd.op1_sz = cpu_to_le32(data2_set);
+		target_cmd.op2 = cpu_to_le32(data2_clr);
+
+		ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data1);
+		if (ret < 0) {
+			IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n");
+			return ret;
+		}
+
+		target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE;
+		target_cmd.addr = cpu_to_le32(addr3);
+		target_cmd.op1_sz = cpu_to_le32(sizeof(u32));
+		target_cmd.op2 = 0;
+
+		ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data3);
+		if (ret < 0) {
+			IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+void iwm_init_default_profile(struct iwm_priv *iwm,
+			      struct iwm_umac_profile *profile)
+{
+	memset(profile, 0, sizeof(struct iwm_umac_profile));
+
+	profile->sec.auth_type = UMAC_AUTH_TYPE_OPEN;
+	profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
+	profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_NONE;
+	profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_NONE;
+
+	if (iwm->conf.enable_qos)
+		profile->flags |= cpu_to_le16(UMAC_PROFILE_QOS_ALLOWED);
+
+	profile->wireless_mode = iwm->conf.wireless_mode;
+	profile->mode = cpu_to_le32(iwm->conf.mode);
+
+	profile->ibss.atim = 0;
+	profile->ibss.beacon_interval = 100;
+	profile->ibss.join_only = 0;
+	profile->ibss.band = iwm->conf.ibss_band;
+	profile->ibss.channel = iwm->conf.ibss_channel;
+}
+
+void iwm_link_on(struct iwm_priv *iwm)
+{
+	netif_carrier_on(iwm_to_ndev(iwm));
+	netif_tx_wake_all_queues(iwm_to_ndev(iwm));
+
+	iwm_send_umac_stats_req(iwm, 0);
+}
+
+void iwm_link_off(struct iwm_priv *iwm)
+{
+	struct iw_statistics *wstats = &iwm->wstats;
+	int i;
+
+	netif_tx_stop_all_queues(iwm_to_ndev(iwm));
+	netif_carrier_off(iwm_to_ndev(iwm));
+
+	for (i = 0; i < IWM_TX_QUEUES; i++) {
+		skb_queue_purge(&iwm->txq[i].queue);
+
+		iwm->txq[i].concat_count = 0;
+		iwm->txq[i].concat_ptr = iwm->txq[i].concat_buf;
+
+		flush_workqueue(iwm->txq[i].wq);
+	}
+
+	iwm_rx_free(iwm);
+
+	cancel_delayed_work(&iwm->stats_request);
+	memset(wstats, 0, sizeof(struct iw_statistics));
+	wstats->qual.updated = IW_QUAL_ALL_INVALID;
+
+	del_timer_sync(&iwm->watchdog);
+}
+
+static void iwm_bss_list_clean(struct iwm_priv *iwm)
+{
+	struct iwm_bss_info *bss, *next;
+
+	list_for_each_entry_safe(bss, next, &iwm->bss_list, node) {
+		list_del(&bss->node);
+		kfree(bss->bss);
+		kfree(bss);
+	}
+}
+
+static int iwm_channels_init(struct iwm_priv *iwm)
+{
+	int ret;
+
+#ifdef CONFIG_IWM_B0_HW_SUPPORT
+	if (iwm->conf.hw_b0) {
+		IWM_INFO(iwm, "Workaround EEPROM channels for B0 hardware\n");
+		return 0;
+	}
+#endif
+
+	ret = iwm_send_umac_channel_list(iwm);
+	if (ret) {
+		IWM_ERR(iwm, "Send channel list failed\n");
+		return ret;
+	}
+
+	ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST,
+			       IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
+	if (ret) {
+		IWM_ERR(iwm, "Didn't get a channel list notification\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+int iwm_up(struct iwm_priv *iwm)
+{
+	int ret;
+	struct iwm_notif *notif_reboot, *notif_ack = NULL;
+
+	ret = iwm_bus_enable(iwm);
+	if (ret) {
+		IWM_ERR(iwm, "Couldn't enable function\n");
+		return ret;
+	}
+
+	iwm_rx_setup_handlers(iwm);
+
+	/* Wait for initial BARKER_REBOOT from hardware */
+	notif_reboot = iwm_notif_wait(iwm, IWM_BARKER_REBOOT_NOTIFICATION,
+				      IWM_SRC_UDMA, 2 * HZ);
+	if (!notif_reboot) {
+		IWM_ERR(iwm, "Wait for REBOOT_BARKER timeout\n");
+		goto err_disable;
+	}
+
+	/* We send the barker back */
+	ret = iwm_bus_send_chunk(iwm, notif_reboot->buf, 16);
+	if (ret) {
+		IWM_ERR(iwm, "REBOOT barker response failed\n");
+		kfree(notif_reboot);
+		goto err_disable;
+	}
+
+	kfree(notif_reboot->buf);
+	kfree(notif_reboot);
+
+	/* Wait for ACK_BARKER from hardware */
+	notif_ack = iwm_notif_wait(iwm, IWM_ACK_BARKER_NOTIFICATION,
+				   IWM_SRC_UDMA, 2 * HZ);
+	if (!notif_ack) {
+		IWM_ERR(iwm, "Wait for ACK_BARKER timeout\n");
+		goto err_disable;
+	}
+
+	kfree(notif_ack->buf);
+	kfree(notif_ack);
+
+	/* We start to config static boot parameters */
+	ret = iwm_config_boot_params(iwm);
+	if (ret) {
+		IWM_ERR(iwm, "Config boot parameters failed\n");
+		goto err_disable;
+	}
+
+	ret = iwm_read_mac(iwm, iwm_to_ndev(iwm)->dev_addr);
+	if (ret) {
+		IWM_ERR(iwm, "MAC reading failed\n");
+		goto err_disable;
+	}
+
+	/* We can load the FWs */
+	ret = iwm_load_fw(iwm);
+	if (ret) {
+		IWM_ERR(iwm, "FW loading failed\n");
+		goto err_disable;
+	}
+
+	/* We configure the UMAC and enable the wifi module */
+	ret = iwm_send_umac_config(iwm,
+			cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_CORE_EN) |
+			cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_LINK_EN) |
+			cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_MLME_EN));
+	if (ret) {
+		IWM_ERR(iwm, "UMAC config failed\n");
+		goto err_fw;
+	}
+
+	ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS,
+			       IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
+	if (ret) {
+		IWM_ERR(iwm, "Didn't get a wifi core status notification\n");
+		goto err_fw;
+	}
+
+	if (iwm->core_enabled != (UMAC_NTFY_WIFI_CORE_STATUS_LINK_EN |
+				  UMAC_NTFY_WIFI_CORE_STATUS_MLME_EN)) {
+		IWM_DBG_BOOT(iwm, DBG, "Not all cores enabled:0x%x\n",
+			     iwm->core_enabled);
+		ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS,
+			       IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
+		if (ret) {
+			IWM_ERR(iwm, "Didn't get a core status notification\n");
+			goto err_fw;
+		}
+
+		if (iwm->core_enabled != (UMAC_NTFY_WIFI_CORE_STATUS_LINK_EN |
+					  UMAC_NTFY_WIFI_CORE_STATUS_MLME_EN)) {
+			IWM_ERR(iwm, "Not all cores enabled: 0x%x\n",
+				iwm->core_enabled);
+			goto err_fw;
+		} else {
+			IWM_INFO(iwm, "All cores enabled\n");
+		}
+	}
+
+	iwm->umac_profile = kmalloc(sizeof(struct iwm_umac_profile),
+				    GFP_KERNEL);
+	if (!iwm->umac_profile) {
+		IWM_ERR(iwm, "Couldn't alloc memory for profile\n");
+		goto err_fw;
+	}
+
+	iwm_init_default_profile(iwm, iwm->umac_profile);
+
+	ret = iwm_channels_init(iwm);
+	if (ret < 0) {
+		IWM_ERR(iwm, "Couldn't init channels\n");
+		goto err_profile;
+	}
+
+	/* Set the READY bit to indicate interface is brought up successfully */
+	set_bit(IWM_STATUS_READY, &iwm->status);
+
+	return 0;
+
+ err_profile:
+	kfree(iwm->umac_profile);
+	iwm->umac_profile = NULL;
+
+ err_fw:
+	iwm_eeprom_exit(iwm);
+
+ err_disable:
+	ret = iwm_bus_disable(iwm);
+	if (ret < 0)
+		IWM_ERR(iwm, "Couldn't disable function\n");
+
+	return -EIO;
+}
+
+int iwm_down(struct iwm_priv *iwm)
+{
+	int ret;
+
+	/* The interface is already down */
+	if (!test_bit(IWM_STATUS_READY, &iwm->status))
+		return 0;
+
+	if (iwm->scan_request) {
+		cfg80211_scan_done(iwm->scan_request, true);
+		iwm->scan_request = NULL;
+	}
+
+	clear_bit(IWM_STATUS_READY, &iwm->status);
+
+	iwm_eeprom_exit(iwm);
+	kfree(iwm->umac_profile);
+	iwm->umac_profile = NULL;
+	iwm_bss_list_clean(iwm);
+
+	iwm->default_key = NULL;
+	iwm->core_enabled = 0;
+
+	ret = iwm_bus_disable(iwm);
+	if (ret < 0) {
+		IWM_ERR(iwm, "Couldn't disable function\n");
+		return ret;
+	}
+
+	return 0;
+}
diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c
new file mode 100644
index 0000000..eec7201
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/netdev.c
@@ -0,0 +1,172 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 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-1301, USA.
+ *
+ */
+
+/*
+ * This is the netdev related hooks for iwm.
+ *
+ * Some interesting code paths:
+ *
+ * iwm_open() (Called at netdev interface bringup time)
+ *  -> iwm_up() (main.c)
+ *      -> iwm_bus_enable()
+ *          -> if_sdio_enable() (In case of an SDIO bus)
+ *              -> sdio_enable_func()
+ *      -> iwm_notif_wait(BARKER_REBOOT) (wait for reboot barker)
+ *      -> iwm_notif_wait(ACK_BARKER) (wait for ACK barker)
+ *      -> iwm_load_fw() (fw.c)
+ *          -> iwm_load_umac()
+ *          -> iwm_load_lmac() (Calibration LMAC)
+ *          -> iwm_load_lmac() (Operational LMAC)
+ *      -> iwm_send_umac_config()
+ *
+ * iwm_stop() (Called at netdev interface bringdown time)
+ *  -> iwm_down()
+ *      -> iwm_bus_disable()
+ *          -> if_sdio_disable() (In case of an SDIO bus)
+ *              -> sdio_disable_func()
+ */
+#include <linux/netdevice.h>
+
+#include "iwm.h"
+#include "cfg80211.h"
+#include "debug.h"
+
+static int iwm_open(struct net_device *ndev)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(ndev);
+	int ret = 0;
+
+	if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
+		ret = iwm_up(iwm);
+
+	return ret;
+}
+
+static int iwm_stop(struct net_device *ndev)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(ndev);
+	int ret = 0;
+
+	if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
+		ret = iwm_down(iwm);
+
+	return ret;
+}
+
+/*
+ * iwm AC to queue mapping
+ *
+ * AC_VO -> queue 3
+ * AC_VI -> queue 2
+ * AC_BE -> queue 1
+ * AC_BK -> queue 0
+ */
+static const u16 iwm_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
+
+static u16 iwm_select_queue(struct net_device *dev, struct sk_buff *skb)
+{
+	skb->priority = cfg80211_classify8021d(skb);
+
+	return iwm_1d_to_queue[skb->priority];
+}
+
+static const struct net_device_ops iwm_netdev_ops = {
+	.ndo_open		= iwm_open,
+	.ndo_stop		= iwm_stop,
+	.ndo_start_xmit		= iwm_xmit_frame,
+	.ndo_select_queue	= iwm_select_queue,
+};
+
+void *iwm_if_alloc(int sizeof_bus, struct device *dev,
+		   struct iwm_if_ops *if_ops)
+{
+	struct net_device *ndev;
+	struct wireless_dev *wdev;
+	struct iwm_priv *iwm;
+	int ret = 0;
+
+	wdev = iwm_wdev_alloc(sizeof_bus, dev);
+	if (!wdev) {
+		dev_err(dev, "no memory for wireless device instance\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	iwm = wdev_to_iwm(wdev);
+	iwm->bus_ops = if_ops;
+	iwm->wdev = wdev;
+	iwm_priv_init(iwm);
+	wdev->iftype = iwm_mode_to_nl80211_iftype(iwm->conf.mode);
+
+	ndev = alloc_netdev_mq(0, "wlan%d", ether_setup,
+			       IWM_TX_QUEUES);
+	if (!ndev) {
+		dev_err(dev, "no memory for network device instance\n");
+		goto out_wdev;
+	}
+
+	ndev->netdev_ops = &iwm_netdev_ops;
+	ndev->wireless_handlers = &iwm_iw_handler_def;
+	ndev->ieee80211_ptr = wdev;
+	SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
+	ret = register_netdev(ndev);
+	if (ret < 0) {
+		dev_err(dev, "Failed to register netdev: %d\n", ret);
+		goto out_ndev;
+	}
+
+	wdev->netdev = ndev;
+
+	ret = iwm_rfkill_init(iwm);
+	if (ret) {
+		dev_err(dev, "Failed to init rfkill\n");
+		goto out_rfkill;
+	}
+
+	return iwm;
+
+ out_rfkill:
+	unregister_netdev(ndev);
+
+ out_ndev:
+	free_netdev(ndev);
+
+ out_wdev:
+	iwm_wdev_free(iwm);
+	return ERR_PTR(ret);
+}
+
+void iwm_if_free(struct iwm_priv *iwm)
+{
+	int i;
+
+	if (!iwm_to_ndev(iwm))
+		return;
+
+	iwm_rfkill_exit(iwm);
+	unregister_netdev(iwm_to_ndev(iwm));
+	free_netdev(iwm_to_ndev(iwm));
+	iwm_wdev_free(iwm);
+	destroy_workqueue(iwm->rx_wq);
+	for (i = 0; i < IWM_TX_QUEUES; i++)
+		destroy_workqueue(iwm->txq[i].wq);
+}
diff --git a/drivers/net/wireless/iwmc3200wifi/rfkill.c b/drivers/net/wireless/iwmc3200wifi/rfkill.c
new file mode 100644
index 0000000..4ca8b49
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/rfkill.c
@@ -0,0 +1,88 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 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-1301, USA.
+ *
+ */
+
+#include <linux/rfkill.h>
+
+#include "iwm.h"
+
+static int iwm_rfkill_soft_toggle(void *data, enum rfkill_state state)
+{
+	struct iwm_priv *iwm = data;
+
+	switch (state) {
+	case RFKILL_STATE_UNBLOCKED:
+		if (test_bit(IWM_RADIO_RFKILL_HW, &iwm->radio))
+			return -EBUSY;
+
+		if (test_and_clear_bit(IWM_RADIO_RFKILL_SW, &iwm->radio) &&
+		    (iwm_to_ndev(iwm)->flags & IFF_UP))
+			iwm_up(iwm);
+
+		break;
+	case RFKILL_STATE_SOFT_BLOCKED:
+		if (!test_and_set_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
+			iwm_down(iwm);
+
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+int iwm_rfkill_init(struct iwm_priv *iwm)
+{
+	int ret;
+
+	iwm->rfkill = rfkill_allocate(iwm_to_dev(iwm), RFKILL_TYPE_WLAN);
+	if (!iwm->rfkill) {
+		IWM_ERR(iwm, "Unable to allocate rfkill device\n");
+		return -ENOMEM;
+	}
+
+	iwm->rfkill->name = KBUILD_MODNAME;
+	iwm->rfkill->data = iwm;
+	iwm->rfkill->state = RFKILL_STATE_UNBLOCKED;
+	iwm->rfkill->toggle_radio = iwm_rfkill_soft_toggle;
+
+	ret = rfkill_register(iwm->rfkill);
+	if (ret) {
+		IWM_ERR(iwm, "Failed to register rfkill device\n");
+		goto fail;
+	}
+
+	return 0;
+ fail:
+	rfkill_free(iwm->rfkill);
+	return ret;
+}
+
+void iwm_rfkill_exit(struct iwm_priv *iwm)
+{
+	if (iwm->rfkill)
+		rfkill_unregister(iwm->rfkill);
+
+	rfkill_free(iwm->rfkill);
+	iwm->rfkill = NULL;
+}
diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c
new file mode 100644
index 0000000..d73cf96
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/rx.c
@@ -0,0 +1,1431 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 of 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.
+ *
+ *
+ * Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#include <linux/ieee80211.h>
+#include <linux/if_arp.h>
+#include <linux/list.h>
+#include <net/iw_handler.h>
+
+#include "iwm.h"
+#include "debug.h"
+#include "hal.h"
+#include "umac.h"
+#include "lmac.h"
+#include "commands.h"
+#include "rx.h"
+#include "cfg80211.h"
+#include "eeprom.h"
+
+static int iwm_rx_check_udma_hdr(struct iwm_udma_in_hdr *hdr)
+{
+	if ((le32_to_cpu(hdr->cmd) == UMAC_PAD_TERMINAL) ||
+	    (le32_to_cpu(hdr->size) == UMAC_PAD_TERMINAL))
+		return -EINVAL;
+
+	return 0;
+}
+
+static inline int iwm_rx_resp_size(struct iwm_udma_in_hdr *hdr)
+{
+	return ALIGN(le32_to_cpu(hdr->size) + sizeof(struct iwm_udma_in_hdr),
+		     16);
+}
+
+/*
+ * Notification handlers:
+ *
+ * For every possible notification we can receive from the
+ * target, we have a handler.
+ * When we get a target notification, and there is no one
+ * waiting for it, it's just processed through the rx code
+ * path:
+ *
+ * iwm_rx_handle()
+ *  -> iwm_rx_handle_umac()
+ *      -> iwm_rx_handle_wifi()
+ *          -> iwm_rx_handle_resp()
+ *              -> iwm_ntf_*()
+ *
+ *      OR
+ *
+ *      -> iwm_rx_handle_non_wifi()
+ *
+ * If there are processes waiting for this notification, then
+ * iwm_rx_handle_wifi() just wakes those processes up and they
+ * grab the pending notification.
+ */
+static int iwm_ntf_error(struct iwm_priv *iwm, u8 *buf,
+			 unsigned long buf_size, struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_umac_notif_error *error;
+	struct iwm_fw_error_hdr *fw_err;
+
+	error = (struct iwm_umac_notif_error *)buf;
+	fw_err = &error->err;
+
+
+	IWM_ERR(iwm, "%cMAC FW ERROR:\n",
+	 (le32_to_cpu(fw_err->category) == UMAC_SYS_ERR_CAT_LMAC) ? 'L' : 'U');
+	IWM_ERR(iwm, "\tCategory:    %d\n", le32_to_cpu(fw_err->category));
+	IWM_ERR(iwm, "\tStatus:      0x%x\n", le32_to_cpu(fw_err->status));
+	IWM_ERR(iwm, "\tPC:          0x%x\n", le32_to_cpu(fw_err->pc));
+	IWM_ERR(iwm, "\tblink1:      %d\n", le32_to_cpu(fw_err->blink1));
+	IWM_ERR(iwm, "\tblink2:      %d\n", le32_to_cpu(fw_err->blink2));
+	IWM_ERR(iwm, "\tilink1:      %d\n", le32_to_cpu(fw_err->ilink1));
+	IWM_ERR(iwm, "\tilink2:      %d\n", le32_to_cpu(fw_err->ilink2));
+	IWM_ERR(iwm, "\tData1:       0x%x\n", le32_to_cpu(fw_err->data1));
+	IWM_ERR(iwm, "\tData2:       0x%x\n", le32_to_cpu(fw_err->data2));
+	IWM_ERR(iwm, "\tLine number: %d\n", le32_to_cpu(fw_err->line_num));
+	IWM_ERR(iwm, "\tUMAC status: 0x%x\n", le32_to_cpu(fw_err->umac_status));
+	IWM_ERR(iwm, "\tLMAC status: 0x%x\n", le32_to_cpu(fw_err->lmac_status));
+	IWM_ERR(iwm, "\tSDIO status: 0x%x\n", le32_to_cpu(fw_err->sdio_status));
+
+	return 0;
+}
+
+static int iwm_ntf_umac_alive(struct iwm_priv *iwm, u8 *buf,
+			      unsigned long buf_size, struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_umac_notif_alive *alive_resp =
+			(struct iwm_umac_notif_alive *)(buf);
+	u16 status = le16_to_cpu(alive_resp->status);
+
+	if (status == UMAC_NTFY_ALIVE_STATUS_ERR) {
+		IWM_ERR(iwm, "Receive error UMAC_ALIVE\n");
+		return -EIO;
+	}
+
+	iwm_tx_credit_init_pools(iwm, alive_resp);
+
+	return 0;
+}
+
+static int iwm_ntf_init_complete(struct iwm_priv *iwm, u8 *buf,
+				 unsigned long buf_size,
+				 struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_umac_notif_init_complete *init_complete =
+			(struct iwm_umac_notif_init_complete *)(buf);
+	u16 status = le16_to_cpu(init_complete->status);
+
+	if (status == UMAC_NTFY_INIT_COMPLETE_STATUS_ERR) {
+		IWM_DBG_NTF(iwm, DBG, "Hardware rf kill is on (radio off)\n");
+		set_bit(IWM_RADIO_RFKILL_HW, &iwm->radio);
+	} else {
+		IWM_DBG_NTF(iwm, DBG, "Hardware rf kill is off (radio on)\n");
+		clear_bit(IWM_RADIO_RFKILL_HW, &iwm->radio);
+	}
+
+	return 0;
+}
+
+static int iwm_ntf_tx_credit_update(struct iwm_priv *iwm, u8 *buf,
+				    unsigned long buf_size,
+				    struct iwm_wifi_cmd *cmd)
+{
+	int pool_nr, total_freed_pages;
+	unsigned long pool_map;
+	int i, id;
+	struct iwm_umac_notif_page_dealloc *dealloc =
+			(struct iwm_umac_notif_page_dealloc *)buf;
+
+	pool_nr = GET_VAL32(dealloc->changes, UMAC_DEALLOC_NTFY_CHANGES_CNT);
+	pool_map = GET_VAL32(dealloc->changes, UMAC_DEALLOC_NTFY_CHANGES_MSK);
+
+	IWM_DBG_TX(iwm, DBG, "UMAC dealloc notification: pool nr %d, "
+		   "update map 0x%lx\n", pool_nr, pool_map);
+
+	spin_lock(&iwm->tx_credit.lock);
+
+	for (i = 0; i < pool_nr; i++) {
+		id = GET_VAL32(dealloc->grp_info[i],
+			       UMAC_DEALLOC_NTFY_GROUP_NUM);
+		if (test_bit(id, &pool_map)) {
+			total_freed_pages = GET_VAL32(dealloc->grp_info[i],
+					      UMAC_DEALLOC_NTFY_PAGE_CNT);
+			iwm_tx_credit_inc(iwm, id, total_freed_pages);
+		}
+	}
+
+	spin_unlock(&iwm->tx_credit.lock);
+
+	return 0;
+}
+
+static int iwm_ntf_umac_reset(struct iwm_priv *iwm, u8 *buf,
+			      unsigned long buf_size, struct iwm_wifi_cmd *cmd)
+{
+	IWM_DBG_NTF(iwm, DBG, "UMAC RESET done\n");
+
+	return 0;
+}
+
+static int iwm_ntf_lmac_version(struct iwm_priv *iwm, u8 *buf,
+				unsigned long buf_size,
+				struct iwm_wifi_cmd *cmd)
+{
+	IWM_DBG_NTF(iwm, INFO, "LMAC Version: %x.%x\n", buf[9], buf[8]);
+
+	return 0;
+}
+
+static int iwm_ntf_tx(struct iwm_priv *iwm, u8 *buf,
+		      unsigned long buf_size, struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_lmac_tx_resp *tx_resp;
+	struct iwm_umac_wifi_in_hdr *hdr;
+
+	tx_resp = (struct iwm_lmac_tx_resp *)
+		(buf + sizeof(struct iwm_umac_wifi_in_hdr));
+	hdr = (struct iwm_umac_wifi_in_hdr *)buf;
+
+	IWM_DBG_NTF(iwm, DBG, "REPLY_TX, buf size: %lu\n", buf_size);
+
+	IWM_DBG_NTF(iwm, DBG, "Seqnum: %d\n",
+		    le16_to_cpu(hdr->sw_hdr.cmd.seq_num));
+	IWM_DBG_NTF(iwm, DBG, "\tFrame cnt: %d\n", tx_resp->frame_cnt);
+	IWM_DBG_NTF(iwm, DBG, "\tRetry cnt: %d\n",
+		    le16_to_cpu(tx_resp->retry_cnt));
+	IWM_DBG_NTF(iwm, DBG, "\tSeq ctl: %d\n", le16_to_cpu(tx_resp->seq_ctl));
+	IWM_DBG_NTF(iwm, DBG, "\tByte cnt: %d\n",
+		    le16_to_cpu(tx_resp->byte_cnt));
+	IWM_DBG_NTF(iwm, DBG, "\tStatus: 0x%x\n", le32_to_cpu(tx_resp->status));
+
+	return 0;
+}
+
+
+static int iwm_ntf_calib_res(struct iwm_priv *iwm, u8 *buf,
+			     unsigned long buf_size, struct iwm_wifi_cmd *cmd)
+{
+	u8 opcode;
+	u8 *calib_buf;
+	struct iwm_lmac_calib_hdr *hdr = (struct iwm_lmac_calib_hdr *)
+				(buf + sizeof(struct iwm_umac_wifi_in_hdr));
+
+	opcode = hdr->opcode;
+
+	BUG_ON(opcode >= CALIBRATION_CMD_NUM ||
+	       opcode < PHY_CALIBRATE_OPCODES_NUM);
+
+	IWM_DBG_NTF(iwm, DBG, "Store calibration result for opcode: %d\n",
+		    opcode);
+
+	buf_size -= sizeof(struct iwm_umac_wifi_in_hdr);
+	calib_buf = iwm->calib_res[opcode].buf;
+
+	if (!calib_buf || (iwm->calib_res[opcode].size < buf_size)) {
+		kfree(calib_buf);
+		calib_buf = kzalloc(buf_size, GFP_KERNEL);
+		if (!calib_buf) {
+			IWM_ERR(iwm, "Memory allocation failed: calib_res\n");
+			return -ENOMEM;
+		}
+		iwm->calib_res[opcode].buf = calib_buf;
+		iwm->calib_res[opcode].size = buf_size;
+	}
+
+	memcpy(calib_buf, hdr, buf_size);
+	set_bit(opcode - PHY_CALIBRATE_OPCODES_NUM, &iwm->calib_done_map);
+
+	return 0;
+}
+
+static int iwm_ntf_calib_complete(struct iwm_priv *iwm, u8 *buf,
+				  unsigned long buf_size,
+				  struct iwm_wifi_cmd *cmd)
+{
+	IWM_DBG_NTF(iwm, DBG, "Calibration completed\n");
+
+	return 0;
+}
+
+static int iwm_ntf_calib_cfg(struct iwm_priv *iwm, u8 *buf,
+			     unsigned long buf_size, struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_lmac_cal_cfg_resp *cal_resp;
+
+	cal_resp = (struct iwm_lmac_cal_cfg_resp *)
+			(buf + sizeof(struct iwm_umac_wifi_in_hdr));
+
+	IWM_DBG_NTF(iwm, DBG, "Calibration CFG command status: %d\n",
+		    le32_to_cpu(cal_resp->status));
+
+	return 0;
+}
+
+static int iwm_ntf_wifi_status(struct iwm_priv *iwm, u8 *buf,
+			       unsigned long buf_size, struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_umac_notif_wifi_status *status =
+		(struct iwm_umac_notif_wifi_status *)buf;
+
+	iwm->core_enabled |= le16_to_cpu(status->status);
+
+	return 0;
+}
+
+static struct iwm_rx_ticket_node *
+iwm_rx_ticket_node_alloc(struct iwm_priv *iwm, struct iwm_rx_ticket *ticket)
+{
+	struct iwm_rx_ticket_node *ticket_node;
+
+	ticket_node = kzalloc(sizeof(struct iwm_rx_ticket_node), GFP_KERNEL);
+	if (!ticket_node) {
+		IWM_ERR(iwm, "Couldn't allocate ticket node\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	ticket_node->ticket = kzalloc(sizeof(struct iwm_rx_ticket), GFP_KERNEL);
+	if (!ticket_node->ticket) {
+		IWM_ERR(iwm, "Couldn't allocate RX ticket\n");
+		kfree(ticket_node);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	memcpy(ticket_node->ticket, ticket, sizeof(struct iwm_rx_ticket));
+	INIT_LIST_HEAD(&ticket_node->node);
+
+	return ticket_node;
+}
+
+static void iwm_rx_ticket_node_free(struct iwm_rx_ticket_node *ticket_node)
+{
+	kfree(ticket_node->ticket);
+	kfree(ticket_node);
+}
+
+static struct iwm_rx_packet *iwm_rx_packet_get(struct iwm_priv *iwm, u16 id)
+{
+	u8 id_hash = IWM_RX_ID_GET_HASH(id);
+	struct list_head *packet_list;
+	struct iwm_rx_packet *packet, *next;
+
+	packet_list = &iwm->rx_packets[id_hash];
+
+	list_for_each_entry_safe(packet, next, packet_list, node)
+		if (packet->id == id)
+			return packet;
+
+	return NULL;
+}
+
+static struct iwm_rx_packet *iwm_rx_packet_alloc(struct iwm_priv *iwm, u8 *buf,
+						 u32 size, u16 id)
+{
+	struct iwm_rx_packet *packet;
+
+	packet = kzalloc(sizeof(struct iwm_rx_packet), GFP_KERNEL);
+	if (!packet) {
+		IWM_ERR(iwm, "Couldn't allocate packet\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	packet->skb = dev_alloc_skb(size);
+	if (!packet->skb) {
+		IWM_ERR(iwm, "Couldn't allocate packet SKB\n");
+		kfree(packet);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	packet->pkt_size = size;
+
+	skb_put(packet->skb, size);
+	memcpy(packet->skb->data, buf, size);
+	INIT_LIST_HEAD(&packet->node);
+	packet->id = id;
+
+	return packet;
+}
+
+void iwm_rx_free(struct iwm_priv *iwm)
+{
+	struct iwm_rx_ticket_node *ticket, *nt;
+	struct iwm_rx_packet *packet, *np;
+	int i;
+
+	list_for_each_entry_safe(ticket, nt, &iwm->rx_tickets, node) {
+		list_del(&ticket->node);
+		iwm_rx_ticket_node_free(ticket);
+	}
+
+	for (i = 0; i < IWM_RX_ID_HASH; i++) {
+		list_for_each_entry_safe(packet, np, &iwm->rx_packets[i],
+					 node) {
+			list_del(&packet->node);
+			kfree_skb(packet->skb);
+			kfree(packet);
+		}
+	}
+}
+
+static int iwm_ntf_rx_ticket(struct iwm_priv *iwm, u8 *buf,
+			     unsigned long buf_size, struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_umac_notif_rx_ticket *ntf_rx_ticket =
+		(struct iwm_umac_notif_rx_ticket *)buf;
+	struct iwm_rx_ticket *ticket =
+		(struct iwm_rx_ticket *)ntf_rx_ticket->tickets;
+	int i, schedule_rx = 0;
+
+	for (i = 0; i < ntf_rx_ticket->num_tickets; i++) {
+		struct iwm_rx_ticket_node *ticket_node;
+
+		switch (le16_to_cpu(ticket->action)) {
+		case IWM_RX_TICKET_RELEASE:
+		case IWM_RX_TICKET_DROP:
+			/* We can push the packet to the stack */
+			ticket_node = iwm_rx_ticket_node_alloc(iwm, ticket);
+			if (IS_ERR(ticket_node))
+				return PTR_ERR(ticket_node);
+
+			IWM_DBG_NTF(iwm, DBG, "TICKET RELEASE(%d)\n",
+				    ticket->id);
+			list_add_tail(&ticket_node->node, &iwm->rx_tickets);
+
+			/*
+			 * We received an Rx ticket, most likely there's
+			 * a packet pending for it, it's not worth going
+			 * through the packet hash list to double check.
+			 * Let's just fire the rx worker..
+			 */
+			schedule_rx = 1;
+
+			break;
+
+		default:
+			IWM_ERR(iwm, "Invalid RX ticket action: 0x%x\n",
+				ticket->action);
+		}
+
+		ticket++;
+	}
+
+	if (schedule_rx)
+		queue_work(iwm->rx_wq, &iwm->rx_worker);
+
+	return 0;
+}
+
+static int iwm_ntf_rx_packet(struct iwm_priv *iwm, u8 *buf,
+			     unsigned long buf_size, struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_umac_wifi_in_hdr *wifi_hdr;
+	struct iwm_rx_packet *packet;
+	u16 id, buf_offset;
+	u32 packet_size;
+
+	IWM_DBG_NTF(iwm, DBG, "\n");
+
+	wifi_hdr = (struct iwm_umac_wifi_in_hdr *)buf;
+	id = le16_to_cpu(wifi_hdr->sw_hdr.cmd.seq_num);
+	buf_offset = sizeof(struct iwm_umac_wifi_in_hdr);
+	packet_size = buf_size - sizeof(struct iwm_umac_wifi_in_hdr);
+
+	IWM_DBG_NTF(iwm, DBG, "CMD:0x%x, seqnum: %d, packet size: %d\n",
+		    wifi_hdr->sw_hdr.cmd.cmd, id, packet_size);
+	IWM_DBG_RX(iwm, DBG, "Packet id: %d\n", id);
+	IWM_HEXDUMP(iwm, DBG, RX, "PACKET: ", buf + buf_offset, packet_size);
+
+	packet = iwm_rx_packet_alloc(iwm, buf + buf_offset, packet_size, id);
+	if (IS_ERR(packet))
+		return PTR_ERR(packet);
+
+	list_add_tail(&packet->node, &iwm->rx_packets[IWM_RX_ID_GET_HASH(id)]);
+
+	/* We might (unlikely) have received the packet _after_ the ticket */
+	queue_work(iwm->rx_wq, &iwm->rx_worker);
+
+	return 0;
+}
+
+/* MLME handlers */
+static int iwm_mlme_assoc_start(struct iwm_priv *iwm, u8 *buf,
+				unsigned long buf_size,
+				struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_umac_notif_assoc_start *start;
+
+	start = (struct iwm_umac_notif_assoc_start *)buf;
+
+	set_bit(IWM_STATUS_ASSOCIATING, &iwm->status);
+
+	IWM_DBG_MLME(iwm, INFO, "Association with %pM Started, reason: %d\n",
+		     start->bssid, le32_to_cpu(start->roam_reason));
+
+	wake_up_interruptible(&iwm->mlme_queue);
+
+	return 0;
+}
+
+static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf,
+				   unsigned long buf_size,
+				   struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_umac_notif_assoc_complete *complete =
+		(struct iwm_umac_notif_assoc_complete *)buf;
+	union iwreq_data wrqu;
+
+	IWM_DBG_MLME(iwm, INFO, "Association with %pM completed, status: %d\n",
+		     complete->bssid, complete->status);
+
+	memset(&wrqu, 0, sizeof(wrqu));
+
+	clear_bit(IWM_STATUS_ASSOCIATING, &iwm->status);
+
+	switch (le32_to_cpu(complete->status)) {
+	case UMAC_ASSOC_COMPLETE_SUCCESS:
+		set_bit(IWM_STATUS_ASSOCIATED, &iwm->status);
+		memcpy(iwm->bssid, complete->bssid, ETH_ALEN);
+		iwm->channel = complete->channel;
+
+		iwm_link_on(iwm);
+
+		memcpy(wrqu.ap_addr.sa_data, complete->bssid, ETH_ALEN);
+		break;
+	case UMAC_ASSOC_COMPLETE_FAILURE:
+		clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status);
+		memset(iwm->bssid, 0, ETH_ALEN);
+		iwm->channel = 0;
+
+		iwm_link_off(iwm);
+	default:
+		break;
+	}
+
+	if (iwm->conf.mode == UMAC_MODE_IBSS) {
+		cfg80211_ibss_joined(iwm_to_ndev(iwm), iwm->bssid, GFP_KERNEL);
+		return 0;
+	}
+
+	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+	wireless_send_event(iwm_to_ndev(iwm), SIOCGIWAP, &wrqu, NULL);
+
+	return 0;
+}
+
+static int iwm_mlme_profile_invalidate(struct iwm_priv *iwm, u8 *buf,
+				       unsigned long buf_size,
+				       struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_umac_notif_profile_invalidate *invalid;
+
+	invalid = (struct iwm_umac_notif_profile_invalidate *)buf;
+
+	IWM_DBG_MLME(iwm, INFO, "Profile Invalidated. Reason: %d\n",
+		     le32_to_cpu(invalid->reason));
+
+	clear_bit(IWM_STATUS_ASSOCIATING, &iwm->status);
+	clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status);
+
+	iwm->umac_profile_active = 0;
+	memset(iwm->bssid, 0, ETH_ALEN);
+	iwm->channel = 0;
+
+	iwm_link_off(iwm);
+
+	wake_up_interruptible(&iwm->mlme_queue);
+
+	return 0;
+}
+
+static int iwm_mlme_scan_complete(struct iwm_priv *iwm, u8 *buf,
+				  unsigned long buf_size,
+				  struct iwm_wifi_cmd *cmd)
+{
+	int ret;
+	struct iwm_umac_notif_scan_complete *scan_complete =
+		(struct iwm_umac_notif_scan_complete *)buf;
+	u32 result = le32_to_cpu(scan_complete->result);
+
+	IWM_DBG_MLME(iwm, INFO, "type:0x%x result:0x%x seq:%d\n",
+		     le32_to_cpu(scan_complete->type),
+		     le32_to_cpu(scan_complete->result),
+		     scan_complete->seq_num);
+
+	if (!test_and_clear_bit(IWM_STATUS_SCANNING, &iwm->status)) {
+		IWM_ERR(iwm, "Scan complete while device not scanning\n");
+		return -EIO;
+	}
+	if (!iwm->scan_request)
+		return 0;
+
+	ret = iwm_cfg80211_inform_bss(iwm);
+
+	cfg80211_scan_done(iwm->scan_request,
+			   (result & UMAC_SCAN_RESULT_ABORTED) ? 1 : !!ret);
+	iwm->scan_request = NULL;
+
+	return ret;
+}
+
+static int iwm_mlme_update_sta_table(struct iwm_priv *iwm, u8 *buf,
+				     unsigned long buf_size,
+				     struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_umac_notif_sta_info *umac_sta =
+			(struct iwm_umac_notif_sta_info *)buf;
+	struct iwm_sta_info *sta;
+	int i;
+
+	switch (le32_to_cpu(umac_sta->opcode)) {
+	case UMAC_OPCODE_ADD_MODIFY:
+		sta = &iwm->sta_table[GET_VAL8(umac_sta->sta_id, LMAC_STA_ID)];
+
+		IWM_DBG_MLME(iwm, INFO, "%s STA: ID = %d, Color = %d, "
+			     "addr = %pM, qos = %d\n",
+			     sta->valid ? "Modify" : "Add",
+			     GET_VAL8(umac_sta->sta_id, LMAC_STA_ID),
+			     GET_VAL8(umac_sta->sta_id, LMAC_STA_COLOR),
+			     umac_sta->mac_addr,
+			     umac_sta->flags & UMAC_STA_FLAG_QOS);
+
+		sta->valid = 1;
+		sta->qos = umac_sta->flags & UMAC_STA_FLAG_QOS;
+		sta->color = GET_VAL8(umac_sta->sta_id, LMAC_STA_COLOR);
+		memcpy(sta->addr, umac_sta->mac_addr, ETH_ALEN);
+		break;
+	case UMAC_OPCODE_REMOVE:
+		IWM_DBG_MLME(iwm, INFO, "Remove STA: ID = %d, Color = %d, "
+			     "addr = %pM\n",
+			     GET_VAL8(umac_sta->sta_id, LMAC_STA_ID),
+			     GET_VAL8(umac_sta->sta_id, LMAC_STA_COLOR),
+			     umac_sta->mac_addr);
+
+		sta = &iwm->sta_table[GET_VAL8(umac_sta->sta_id, LMAC_STA_ID)];
+
+		if (!memcmp(sta->addr, umac_sta->mac_addr, ETH_ALEN))
+			sta->valid = 0;
+
+		break;
+	case UMAC_OPCODE_CLEAR_ALL:
+		for (i = 0; i < IWM_STA_TABLE_NUM; i++)
+			iwm->sta_table[i].valid = 0;
+
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int iwm_mlme_update_bss_table(struct iwm_priv *iwm, u8 *buf,
+				     unsigned long buf_size,
+				     struct iwm_wifi_cmd *cmd)
+{
+	struct wiphy *wiphy = iwm_to_wiphy(iwm);
+	struct ieee80211_mgmt *mgmt;
+	struct iwm_umac_notif_bss_info *umac_bss =
+			(struct iwm_umac_notif_bss_info *)buf;
+	struct ieee80211_channel *channel;
+	struct ieee80211_supported_band *band;
+	struct iwm_bss_info *bss, *next;
+	s32 signal;
+	int freq;
+	u16 frame_len = le16_to_cpu(umac_bss->frame_len);
+	size_t bss_len = sizeof(struct iwm_umac_notif_bss_info) + frame_len;
+
+	mgmt = (struct ieee80211_mgmt *)(umac_bss->frame_buf);
+
+	IWM_DBG_MLME(iwm, DBG, "New BSS info entry: %pM\n", mgmt->bssid);
+	IWM_DBG_MLME(iwm, DBG, "\tType: 0x%x\n", le32_to_cpu(umac_bss->type));
+	IWM_DBG_MLME(iwm, DBG, "\tTimestamp: %d\n",
+		     le32_to_cpu(umac_bss->timestamp));
+	IWM_DBG_MLME(iwm, DBG, "\tTable Index: %d\n",
+		     le16_to_cpu(umac_bss->table_idx));
+	IWM_DBG_MLME(iwm, DBG, "\tBand: %d\n", umac_bss->band);
+	IWM_DBG_MLME(iwm, DBG, "\tChannel: %d\n", umac_bss->channel);
+	IWM_DBG_MLME(iwm, DBG, "\tRSSI: %d\n", umac_bss->rssi);
+	IWM_DBG_MLME(iwm, DBG, "\tFrame Length: %d\n", frame_len);
+
+	list_for_each_entry_safe(bss, next, &iwm->bss_list, node)
+		if (bss->bss->table_idx == umac_bss->table_idx)
+			break;
+
+	if (&bss->node != &iwm->bss_list) {
+		/* Remove the old BSS entry, we will add it back later. */
+		list_del(&bss->node);
+		kfree(bss->bss);
+	} else {
+		/* New BSS entry */
+
+		bss = kzalloc(sizeof(struct iwm_bss_info), GFP_KERNEL);
+		if (!bss) {
+			IWM_ERR(iwm, "Couldn't allocate bss_info\n");
+			return -ENOMEM;
+		}
+	}
+
+	bss->bss = kzalloc(bss_len, GFP_KERNEL);
+	if (!bss) {
+		kfree(bss);
+		IWM_ERR(iwm, "Couldn't allocate bss\n");
+		return -ENOMEM;
+	}
+
+	INIT_LIST_HEAD(&bss->node);
+	memcpy(bss->bss, umac_bss, bss_len);
+
+	if (umac_bss->band == UMAC_BAND_2GHZ)
+		band = wiphy->bands[IEEE80211_BAND_2GHZ];
+	else if (umac_bss->band == UMAC_BAND_5GHZ)
+		band = wiphy->bands[IEEE80211_BAND_5GHZ];
+	else {
+		IWM_ERR(iwm, "Invalid band: %d\n", umac_bss->band);
+		goto err;
+	}
+
+	freq = ieee80211_channel_to_frequency(umac_bss->channel);
+	channel = ieee80211_get_channel(wiphy, freq);
+	signal = umac_bss->rssi * 100;
+
+	bss->cfg_bss = cfg80211_inform_bss_frame(wiphy, channel,
+						 mgmt, frame_len,
+						 signal, GFP_KERNEL);
+	if (!bss->cfg_bss)
+		goto err;
+
+	list_add_tail(&bss->node, &iwm->bss_list);
+
+	return 0;
+ err:
+	kfree(bss->bss);
+	kfree(bss);
+
+	return -EINVAL;
+}
+
+static int iwm_mlme_remove_bss(struct iwm_priv *iwm, u8 *buf,
+			       unsigned long buf_size, struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_umac_notif_bss_removed *bss_rm =
+		(struct iwm_umac_notif_bss_removed *)buf;
+	struct iwm_bss_info *bss, *next;
+	u16 table_idx;
+	int i;
+
+	for (i = 0; i < le32_to_cpu(bss_rm->count); i++) {
+		table_idx = (le16_to_cpu(bss_rm->entries[i])
+			     & IWM_BSS_REMOVE_INDEX_MSK);
+		list_for_each_entry_safe(bss, next, &iwm->bss_list, node)
+			if (bss->bss->table_idx == cpu_to_le16(table_idx)) {
+				struct ieee80211_mgmt *mgmt;
+
+				mgmt = (struct ieee80211_mgmt *)
+					(bss->bss->frame_buf);
+				IWM_DBG_MLME(iwm, ERR,
+					     "BSS removed: %pM\n",
+					     mgmt->bssid);
+				list_del(&bss->node);
+				kfree(bss->bss);
+				kfree(bss);
+			}
+	}
+
+	return 0;
+}
+
+static int iwm_mlme_mgt_frame(struct iwm_priv *iwm, u8 *buf,
+			      unsigned long buf_size, struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_umac_notif_mgt_frame *mgt_frame =
+	(struct iwm_umac_notif_mgt_frame *)buf;
+	struct ieee80211_mgmt *mgt = (struct ieee80211_mgmt *)mgt_frame->frame;
+	u8 *ie;
+	unsigned int event;
+	union iwreq_data wrqu;
+
+	IWM_HEXDUMP(iwm, DBG, MLME, "MGT: ", mgt_frame->frame,
+		    le16_to_cpu(mgt_frame->len));
+
+	if (ieee80211_is_assoc_req(mgt->frame_control)) {
+		ie = mgt->u.assoc_req.variable;;
+		event = IWEVASSOCREQIE;
+	} else if (ieee80211_is_reassoc_req(mgt->frame_control)) {
+		ie = mgt->u.reassoc_req.variable;;
+		event = IWEVASSOCREQIE;
+	} else if (ieee80211_is_assoc_resp(mgt->frame_control)) {
+		ie = mgt->u.assoc_resp.variable;;
+		event = IWEVASSOCRESPIE;
+	} else if (ieee80211_is_reassoc_resp(mgt->frame_control)) {
+		ie = mgt->u.reassoc_resp.variable;;
+		event = IWEVASSOCRESPIE;
+	} else {
+		IWM_ERR(iwm, "Unsupported management frame");
+		return 0;
+	}
+
+	wrqu.data.length = le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt);
+
+	IWM_HEXDUMP(iwm, DBG, MLME, "EVT: ", ie, wrqu.data.length);
+	wireless_send_event(iwm_to_ndev(iwm), event, &wrqu, ie);
+
+	return 0;
+}
+
+static int iwm_ntf_mlme(struct iwm_priv *iwm, u8 *buf,
+			unsigned long buf_size, struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_umac_notif_wifi_if *notif =
+		(struct iwm_umac_notif_wifi_if *)buf;
+
+	switch (notif->status) {
+	case WIFI_IF_NTFY_ASSOC_START:
+		return iwm_mlme_assoc_start(iwm, buf, buf_size, cmd);
+	case WIFI_IF_NTFY_ASSOC_COMPLETE:
+		return iwm_mlme_assoc_complete(iwm, buf, buf_size, cmd);
+	case WIFI_IF_NTFY_PROFILE_INVALIDATE_COMPLETE:
+		return iwm_mlme_profile_invalidate(iwm, buf, buf_size, cmd);
+	case WIFI_IF_NTFY_CONNECTION_TERMINATED:
+		IWM_DBG_MLME(iwm, DBG, "Connection terminated\n");
+		break;
+	case WIFI_IF_NTFY_SCAN_COMPLETE:
+		return iwm_mlme_scan_complete(iwm, buf, buf_size, cmd);
+	case WIFI_IF_NTFY_STA_TABLE_CHANGE:
+		return iwm_mlme_update_sta_table(iwm, buf, buf_size, cmd);
+	case WIFI_IF_NTFY_EXTENDED_IE_REQUIRED:
+		IWM_DBG_MLME(iwm, DBG, "Extended IE required\n");
+		break;
+	case WIFI_IF_NTFY_BSS_TRK_TABLE_CHANGED:
+		return iwm_mlme_update_bss_table(iwm, buf, buf_size, cmd);
+	case WIFI_IF_NTFY_BSS_TRK_ENTRIES_REMOVED:
+		return iwm_mlme_remove_bss(iwm, buf, buf_size, cmd);
+		break;
+	case WIFI_IF_NTFY_MGMT_FRAME:
+		return iwm_mlme_mgt_frame(iwm, buf, buf_size, cmd);
+	case WIFI_DBG_IF_NTFY_SCAN_SUPER_JOB_START:
+	case WIFI_DBG_IF_NTFY_SCAN_SUPER_JOB_COMPLETE:
+	case WIFI_DBG_IF_NTFY_SCAN_CHANNEL_START:
+	case WIFI_DBG_IF_NTFY_SCAN_CHANNEL_RESULT:
+	case WIFI_DBG_IF_NTFY_SCAN_MINI_JOB_START:
+	case WIFI_DBG_IF_NTFY_SCAN_MINI_JOB_COMPLETE:
+	case WIFI_DBG_IF_NTFY_CNCT_ATC_START:
+	case WIFI_DBG_IF_NTFY_COEX_NOTIFICATION:
+	case WIFI_DBG_IF_NTFY_COEX_HANDLE_ENVELOP:
+	case WIFI_DBG_IF_NTFY_COEX_HANDLE_RELEASE_ENVELOP:
+		IWM_DBG_MLME(iwm, DBG, "MLME debug notification: 0x%x\n",
+			     notif->status);
+		break;
+	default:
+		IWM_ERR(iwm, "Unhandled notification: 0x%x\n", notif->status);
+		break;
+	}
+
+	return 0;
+}
+
+#define IWM_STATS_UPDATE_INTERVAL		(2 * HZ)
+
+static int iwm_ntf_statistics(struct iwm_priv *iwm, u8 *buf,
+			      unsigned long buf_size, struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_umac_notif_stats *stats = (struct iwm_umac_notif_stats *)buf;
+	struct iw_statistics *wstats = &iwm->wstats;
+	u16 max_rate = 0;
+	int i;
+
+	IWM_DBG_MLME(iwm, DBG, "Statistics notification received\n");
+
+	if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
+		for (i = 0; i < UMAC_NTF_RATE_SAMPLE_NR; i++) {
+			max_rate = max_t(u16, max_rate,
+					 max(le16_to_cpu(stats->tx_rate[i]),
+					     le16_to_cpu(stats->rx_rate[i])));
+		}
+		/* UMAC passes rate info multiplies by 2 */
+		iwm->rate = max_rate >> 1;
+	}
+
+	wstats->status = 0;
+
+	wstats->discard.nwid = le32_to_cpu(stats->rx_drop_other_bssid);
+	wstats->discard.code = le32_to_cpu(stats->rx_drop_decode);
+	wstats->discard.fragment = le32_to_cpu(stats->rx_drop_reassembly);
+	wstats->discard.retries = le32_to_cpu(stats->tx_drop_max_retry);
+
+	wstats->miss.beacon = le32_to_cpu(stats->missed_beacons);
+
+	/* according to cfg80211 */
+	if (stats->rssi_dbm < -110)
+		wstats->qual.qual = 0;
+	else if (stats->rssi_dbm > -40)
+		wstats->qual.qual = 70;
+	else
+		wstats->qual.qual = stats->rssi_dbm + 110;
+
+	wstats->qual.level = stats->rssi_dbm;
+	wstats->qual.noise = stats->noise_dbm;
+	wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+
+	schedule_delayed_work(&iwm->stats_request, IWM_STATS_UPDATE_INTERVAL);
+
+	mod_timer(&iwm->watchdog, round_jiffies(jiffies + IWM_WATCHDOG_PERIOD));
+
+	return 0;
+}
+
+static int iwm_ntf_eeprom_proxy(struct iwm_priv *iwm, u8 *buf,
+				unsigned long buf_size,
+				struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_umac_cmd_eeprom_proxy *eeprom_proxy =
+		(struct iwm_umac_cmd_eeprom_proxy *)
+		(buf + sizeof(struct iwm_umac_wifi_in_hdr));
+	struct iwm_umac_cmd_eeprom_proxy_hdr *hdr = &eeprom_proxy->hdr;
+	u32 hdr_offset = le32_to_cpu(hdr->offset);
+	u32 hdr_len = le32_to_cpu(hdr->len);
+	u32 hdr_type = le32_to_cpu(hdr->type);
+
+	IWM_DBG_NTF(iwm, DBG, "type: 0x%x, len: %d, offset: 0x%x\n",
+		    hdr_type, hdr_len, hdr_offset);
+
+	if ((hdr_offset + hdr_len) > IWM_EEPROM_LEN)
+		return -EINVAL;
+
+#ifdef CONFIG_IWM_B0_HW_SUPPORT
+	if (hdr_offset == IWM_EEPROM_SKU_CAP_OFF) {
+		if (eeprom_proxy->buf[0] == 0xff)
+			iwm->conf.hw_b0 = 1;
+	}
+#endif
+
+	switch (hdr_type) {
+	case IWM_UMAC_CMD_EEPROM_TYPE_READ:
+		memcpy(iwm->eeprom + hdr_offset, eeprom_proxy->buf, hdr_len);
+		break;
+	case IWM_UMAC_CMD_EEPROM_TYPE_WRITE:
+	default:
+		return -ENOTSUPP;
+	}
+
+	return 0;
+}
+
+static int iwm_ntf_channel_info_list(struct iwm_priv *iwm, u8 *buf,
+				     unsigned long buf_size,
+				     struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_umac_cmd_get_channel_list *ch_list =
+			(struct iwm_umac_cmd_get_channel_list *)
+			(buf + sizeof(struct iwm_umac_wifi_in_hdr));
+	struct wiphy *wiphy = iwm_to_wiphy(iwm);
+	struct ieee80211_supported_band *band;
+	int i;
+
+	band = wiphy->bands[IEEE80211_BAND_2GHZ];
+
+	for (i = 0; i < band->n_channels; i++) {
+		unsigned long ch_mask_0 =
+			le32_to_cpu(ch_list->ch[0].channels_mask);
+		unsigned long ch_mask_2 =
+			le32_to_cpu(ch_list->ch[2].channels_mask);
+
+		if (!test_bit(i, &ch_mask_0))
+			band->channels[i].flags |= IEEE80211_CHAN_DISABLED;
+
+		if (!test_bit(i, &ch_mask_2))
+			band->channels[i].flags |= IEEE80211_CHAN_NO_IBSS;
+	}
+
+	band = wiphy->bands[IEEE80211_BAND_5GHZ];
+
+	for (i = 0; i < min(band->n_channels, 32); i++) {
+		unsigned long ch_mask_1 =
+			le32_to_cpu(ch_list->ch[1].channels_mask);
+		unsigned long ch_mask_3 =
+			le32_to_cpu(ch_list->ch[3].channels_mask);
+
+		if (!test_bit(i, &ch_mask_1))
+			band->channels[i].flags |= IEEE80211_CHAN_DISABLED;
+
+		if (!test_bit(i, &ch_mask_3))
+			band->channels[i].flags |= IEEE80211_CHAN_NO_IBSS;
+	}
+
+	return 0;
+}
+
+static int iwm_ntf_wifi_if_wrapper(struct iwm_priv *iwm, u8 *buf,
+				   unsigned long buf_size,
+				   struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_umac_wifi_if *hdr =
+			(struct iwm_umac_wifi_if *)cmd->buf.payload;
+
+	IWM_DBG_NTF(iwm, DBG, "WIFI_IF_WRAPPER cmd is delivered to UMAC: "
+		    "oid is %d\n", hdr->oid);
+
+	switch (hdr->oid) {
+	case UMAC_WIFI_IF_CMD_SET_PROFILE:
+		iwm->umac_profile_active = 1;
+		wake_up_interruptible(&iwm->mlme_queue);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int iwm_ntf_card_state(struct iwm_priv *iwm, u8 *buf,
+			      unsigned long buf_size, struct iwm_wifi_cmd *cmd)
+{
+	struct iwm_lmac_card_state *state = (struct iwm_lmac_card_state *)
+				(buf + sizeof(struct iwm_umac_wifi_in_hdr));
+	u32 flags = le32_to_cpu(state->flags);
+
+	IWM_INFO(iwm, "HW RF Kill %s, CT Kill %s\n",
+		 flags & IWM_CARD_STATE_HW_DISABLED ? "ON" : "OFF",
+		 flags & IWM_CARD_STATE_CTKILL_DISABLED ? "ON" : "OFF");
+
+	if (flags & IWM_CARD_STATE_HW_DISABLED)
+		set_bit(IWM_RADIO_RFKILL_HW, &iwm->radio);
+	else
+		clear_bit(IWM_RADIO_RFKILL_HW, &iwm->radio);
+
+	return 0;
+}
+
+static int iwm_rx_handle_wifi(struct iwm_priv *iwm, u8 *buf,
+			      unsigned long buf_size)
+{
+	struct iwm_umac_wifi_in_hdr *wifi_hdr;
+	struct iwm_wifi_cmd *cmd;
+	u8 source, cmd_id;
+	u16 seq_num;
+	u32 count;
+	u8 resp;
+
+	wifi_hdr = (struct iwm_umac_wifi_in_hdr *)buf;
+	cmd_id = wifi_hdr->sw_hdr.cmd.cmd;
+
+	source = GET_VAL32(wifi_hdr->hw_hdr.cmd, UMAC_HDI_IN_CMD_SOURCE);
+	if (source >= IWM_SRC_NUM) {
+		IWM_CRIT(iwm, "invalid source %d\n", source);
+		return -EINVAL;
+	}
+
+	count = (GET_VAL32(wifi_hdr->sw_hdr.meta_data, UMAC_FW_CMD_BYTE_COUNT));
+	count += sizeof(struct iwm_umac_wifi_in_hdr) -
+		 sizeof(struct iwm_dev_cmd_hdr);
+	if (count > buf_size) {
+		IWM_CRIT(iwm, "count %d, buf size:%ld\n", count, buf_size);
+		return -EINVAL;
+	}
+
+	resp = GET_VAL32(wifi_hdr->sw_hdr.meta_data, UMAC_FW_CMD_STATUS);
+
+	seq_num = le16_to_cpu(wifi_hdr->sw_hdr.cmd.seq_num);
+
+	IWM_DBG_RX(iwm, DBG, "CMD:0x%x, source: 0x%x, seqnum: %d\n",
+		   cmd_id, source, seq_num);
+
+	/*
+	 * If this is a response to a previously sent command, there must
+	 * be a pending command for this sequence number.
+	 */
+	cmd = iwm_get_pending_wifi_cmd(iwm, seq_num);
+
+	/* Notify the caller only for sync commands. */
+	switch (source) {
+	case UMAC_HDI_IN_SOURCE_FHRX:
+		if (iwm->lmac_handlers[cmd_id] &&
+		    test_bit(cmd_id, &iwm->lmac_handler_map[0]))
+			return iwm_notif_send(iwm, cmd, cmd_id, source,
+					      buf, count);
+		break;
+	case UMAC_HDI_IN_SOURCE_FW:
+		if (iwm->umac_handlers[cmd_id] &&
+		    test_bit(cmd_id, &iwm->umac_handler_map[0]))
+			return iwm_notif_send(iwm, cmd, cmd_id, source,
+					      buf, count);
+		break;
+	case UMAC_HDI_IN_SOURCE_UDMA:
+		break;
+	}
+
+	return iwm_rx_handle_resp(iwm, buf, count, cmd);
+}
+
+int iwm_rx_handle_resp(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size,
+		       struct iwm_wifi_cmd *cmd)
+{
+	u8 source, cmd_id;
+	struct iwm_umac_wifi_in_hdr *wifi_hdr;
+	int ret = 0;
+
+	wifi_hdr = (struct iwm_umac_wifi_in_hdr *)buf;
+	cmd_id = wifi_hdr->sw_hdr.cmd.cmd;
+
+	source = GET_VAL32(wifi_hdr->hw_hdr.cmd, UMAC_HDI_IN_CMD_SOURCE);
+
+	IWM_DBG_RX(iwm, DBG, "CMD:0x%x, source: 0x%x\n", cmd_id, source);
+
+	switch (source) {
+	case UMAC_HDI_IN_SOURCE_FHRX:
+		if (iwm->lmac_handlers[cmd_id])
+			ret = iwm->lmac_handlers[cmd_id]
+					(iwm, buf, buf_size, cmd);
+		break;
+	case UMAC_HDI_IN_SOURCE_FW:
+		if (iwm->umac_handlers[cmd_id])
+			ret = iwm->umac_handlers[cmd_id]
+					(iwm, buf, buf_size, cmd);
+		break;
+	case UMAC_HDI_IN_SOURCE_UDMA:
+		ret = -EINVAL;
+		break;
+	}
+
+	kfree(cmd);
+
+	return ret;
+}
+
+static int iwm_rx_handle_nonwifi(struct iwm_priv *iwm, u8 *buf,
+				 unsigned long buf_size)
+{
+	u8 seq_num;
+	struct iwm_udma_in_hdr *hdr = (struct iwm_udma_in_hdr *)buf;
+	struct iwm_nonwifi_cmd *cmd, *next;
+
+	seq_num = GET_VAL32(hdr->cmd, UDMA_HDI_IN_CMD_NON_WIFI_HW_SEQ_NUM);
+
+	/*
+	 * We received a non wifi answer.
+	 * Let's check if there's a pending command for it, and if so
+	 * replace the command payload with the buffer, and then wake the
+	 * callers up.
+	 * That means we only support synchronised non wifi command response
+	 * schemes.
+	 */
+	list_for_each_entry_safe(cmd, next, &iwm->nonwifi_pending_cmd, pending)
+		if (cmd->seq_num == seq_num) {
+			cmd->resp_received = 1;
+			cmd->buf.len = buf_size;
+			memcpy(cmd->buf.hdr, buf, buf_size);
+			wake_up_interruptible(&iwm->nonwifi_queue);
+		}
+
+	return 0;
+}
+
+static int iwm_rx_handle_umac(struct iwm_priv *iwm, u8 *buf,
+			      unsigned long buf_size)
+{
+	int ret = 0;
+	u8 op_code;
+	unsigned long buf_offset = 0;
+	struct iwm_udma_in_hdr *hdr;
+
+	/*
+	 * To allow for a more efficient bus usage, UMAC
+	 * messages are encapsulated into UDMA ones. This
+	 * way we can have several UMAC messages in one bus
+	 * transfer.
+	 * A UDMA frame size is always aligned on 16 bytes,
+	 * and a UDMA frame must not start with a UMAC_PAD_TERMINAL
+	 * word. This is how we parse a bus frame into several
+	 * UDMA ones.
+	 */
+	while (buf_offset < buf_size) {
+
+		hdr = (struct iwm_udma_in_hdr *)(buf + buf_offset);
+
+		if (iwm_rx_check_udma_hdr(hdr) < 0) {
+			IWM_DBG_RX(iwm, DBG, "End of frame\n");
+			break;
+		}
+
+		op_code = GET_VAL32(hdr->cmd, UMAC_HDI_IN_CMD_OPCODE);
+
+		IWM_DBG_RX(iwm, DBG, "Op code: 0x%x\n", op_code);
+
+		if (op_code == UMAC_HDI_IN_OPCODE_WIFI) {
+			ret |= iwm_rx_handle_wifi(iwm, buf + buf_offset,
+						  buf_size - buf_offset);
+		} else if (op_code < UMAC_HDI_IN_OPCODE_NONWIFI_MAX) {
+			if (GET_VAL32(hdr->cmd,
+				      UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG) !=
+			    UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG) {
+				IWM_ERR(iwm, "Incorrect hw signature\n");
+				return -EINVAL;
+			}
+			ret |= iwm_rx_handle_nonwifi(iwm, buf + buf_offset,
+						     buf_size - buf_offset);
+		} else {
+			IWM_ERR(iwm, "Invalid RX opcode: 0x%x\n", op_code);
+			ret |= -EINVAL;
+		}
+
+		buf_offset += iwm_rx_resp_size(hdr);
+	}
+
+	return ret;
+}
+
+int iwm_rx_handle(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size)
+{
+	struct iwm_udma_in_hdr *hdr;
+
+	hdr = (struct iwm_udma_in_hdr *)buf;
+
+	switch (le32_to_cpu(hdr->cmd)) {
+	case UMAC_REBOOT_BARKER:
+		return iwm_notif_send(iwm, NULL, IWM_BARKER_REBOOT_NOTIFICATION,
+				      IWM_SRC_UDMA, buf, buf_size);
+	case UMAC_ACK_BARKER:
+		return iwm_notif_send(iwm, NULL, IWM_ACK_BARKER_NOTIFICATION,
+				      IWM_SRC_UDMA, NULL, 0);
+	default:
+		IWM_DBG_RX(iwm, DBG, "Received cmd: 0x%x\n", hdr->cmd);
+		return iwm_rx_handle_umac(iwm, buf, buf_size);
+	}
+
+	return 0;
+}
+
+static const iwm_handler iwm_umac_handlers[] =
+{
+	[UMAC_NOTIFY_OPCODE_ERROR]		= iwm_ntf_error,
+	[UMAC_NOTIFY_OPCODE_ALIVE]		= iwm_ntf_umac_alive,
+	[UMAC_NOTIFY_OPCODE_INIT_COMPLETE]	= iwm_ntf_init_complete,
+	[UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS]	= iwm_ntf_wifi_status,
+	[UMAC_NOTIFY_OPCODE_WIFI_IF_WRAPPER]	= iwm_ntf_mlme,
+	[UMAC_NOTIFY_OPCODE_PAGE_DEALLOC]	= iwm_ntf_tx_credit_update,
+	[UMAC_NOTIFY_OPCODE_RX_TICKET]		= iwm_ntf_rx_ticket,
+	[UMAC_CMD_OPCODE_RESET]			= iwm_ntf_umac_reset,
+	[UMAC_NOTIFY_OPCODE_STATS]		= iwm_ntf_statistics,
+	[UMAC_CMD_OPCODE_EEPROM_PROXY]		= iwm_ntf_eeprom_proxy,
+	[UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST]	= iwm_ntf_channel_info_list,
+	[REPLY_RX_MPDU_CMD]			= iwm_ntf_rx_packet,
+	[UMAC_CMD_OPCODE_WIFI_IF_WRAPPER]	= iwm_ntf_wifi_if_wrapper,
+};
+
+static const iwm_handler iwm_lmac_handlers[] =
+{
+	[REPLY_TX]				= iwm_ntf_tx,
+	[REPLY_ALIVE]				= iwm_ntf_lmac_version,
+	[CALIBRATION_RES_NOTIFICATION]		= iwm_ntf_calib_res,
+	[CALIBRATION_COMPLETE_NOTIFICATION]	= iwm_ntf_calib_complete,
+	[CALIBRATION_CFG_CMD]			= iwm_ntf_calib_cfg,
+	[REPLY_RX_MPDU_CMD]			= iwm_ntf_rx_packet,
+	[CARD_STATE_NOTIFICATION]		= iwm_ntf_card_state,
+};
+
+void iwm_rx_setup_handlers(struct iwm_priv *iwm)
+{
+	iwm->umac_handlers = (iwm_handler *) iwm_umac_handlers;
+	iwm->lmac_handlers = (iwm_handler *) iwm_lmac_handlers;
+}
+
+static void iwm_remove_iv(struct sk_buff *skb, u32 hdr_total_len)
+{
+	struct ieee80211_hdr *hdr;
+	unsigned int hdr_len;
+
+	hdr = (struct ieee80211_hdr *)skb->data;
+
+	if (!ieee80211_has_protected(hdr->frame_control))
+		return;
+
+	hdr_len = ieee80211_hdrlen(hdr->frame_control);
+	if (hdr_total_len <= hdr_len)
+		return;
+
+	memmove(skb->data + (hdr_total_len - hdr_len), skb->data, hdr_len);
+	skb_pull(skb, (hdr_total_len - hdr_len));
+}
+
+static void iwm_rx_adjust_packet(struct iwm_priv *iwm,
+				 struct iwm_rx_packet *packet,
+				 struct iwm_rx_ticket_node *ticket_node)
+{
+	u32 payload_offset = 0, payload_len;
+	struct iwm_rx_ticket *ticket = ticket_node->ticket;
+	struct iwm_rx_mpdu_hdr *mpdu_hdr;
+	struct ieee80211_hdr *hdr;
+
+	mpdu_hdr = (struct iwm_rx_mpdu_hdr *)packet->skb->data;
+	payload_offset += sizeof(struct iwm_rx_mpdu_hdr);
+	/* Padding is 0 or 2 bytes */
+	payload_len = le16_to_cpu(mpdu_hdr->len) +
+		(le16_to_cpu(ticket->flags) & IWM_RX_TICKET_PAD_SIZE_MSK);
+	payload_len -= ticket->tail_len;
+
+	IWM_DBG_RX(iwm, DBG, "Packet adjusted, len:%d, offset:%d, "
+		   "ticket offset:%d ticket tail len:%d\n",
+		   payload_len, payload_offset, ticket->payload_offset,
+		   ticket->tail_len);
+
+	IWM_HEXDUMP(iwm, DBG, RX, "RAW: ", packet->skb->data, packet->skb->len);
+
+	skb_pull(packet->skb, payload_offset);
+	skb_trim(packet->skb, payload_len);
+
+	iwm_remove_iv(packet->skb, ticket->payload_offset);
+
+	hdr = (struct ieee80211_hdr *) packet->skb->data;
+	if (ieee80211_is_data_qos(hdr->frame_control)) {
+		/* UMAC handed QOS_DATA frame with 2 padding bytes appended
+		 * to the qos_ctl field in IEEE 802.11 headers. */
+		memmove(packet->skb->data + IEEE80211_QOS_CTL_LEN + 2,
+			packet->skb->data,
+			ieee80211_hdrlen(hdr->frame_control) -
+			IEEE80211_QOS_CTL_LEN);
+		hdr = (struct ieee80211_hdr *) skb_pull(packet->skb,
+				IEEE80211_QOS_CTL_LEN + 2);
+		hdr->frame_control &= ~cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
+	}
+
+	IWM_HEXDUMP(iwm, DBG, RX, "ADJUSTED: ",
+		    packet->skb->data, packet->skb->len);
+}
+
+static void classify8023(struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+
+	if (ieee80211_is_data_qos(hdr->frame_control)) {
+		u8 *qc = ieee80211_get_qos_ctl(hdr);
+		/* frame has qos control */
+		skb->priority = *qc & IEEE80211_QOS_CTL_TID_MASK;
+	} else {
+		skb->priority = 0;
+	}
+}
+
+static void iwm_rx_process_packet(struct iwm_priv *iwm,
+				  struct iwm_rx_packet *packet,
+				  struct iwm_rx_ticket_node *ticket_node)
+{
+	int ret;
+	struct sk_buff *skb = packet->skb;
+	struct wireless_dev *wdev = iwm_to_wdev(iwm);
+	struct net_device *ndev = iwm_to_ndev(iwm);
+
+	IWM_DBG_RX(iwm, DBG, "Processing packet ID %d\n", packet->id);
+
+	switch (le16_to_cpu(ticket_node->ticket->action)) {
+	case IWM_RX_TICKET_RELEASE:
+		IWM_DBG_RX(iwm, DBG, "RELEASE packet\n");
+		classify8023(skb);
+		iwm_rx_adjust_packet(iwm, packet, ticket_node);
+		ret = ieee80211_data_to_8023(skb, ndev->dev_addr, wdev->iftype);
+		if (ret < 0) {
+			IWM_DBG_RX(iwm, DBG, "Couldn't convert 802.11 header - "
+				   "%d\n", ret);
+			break;
+		}
+
+		IWM_HEXDUMP(iwm, DBG, RX, "802.3: ", skb->data, skb->len);
+
+		skb->dev = iwm_to_ndev(iwm);
+		skb->protocol = eth_type_trans(skb, ndev);
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+		memset(skb->cb, 0, sizeof(skb->cb));
+
+		ndev->stats.rx_packets++;
+		ndev->stats.rx_bytes += skb->len;
+
+		if (netif_rx(skb) == NET_RX_DROP) {
+			IWM_ERR(iwm, "Packet dropped\n");
+			ndev->stats.rx_dropped++;
+		}
+		break;
+	case IWM_RX_TICKET_DROP:
+		IWM_DBG_RX(iwm, DBG, "DROP packet\n");
+		kfree_skb(packet->skb);
+		break;
+	default:
+		IWM_ERR(iwm, "Unknow ticket action: %d\n",
+			le16_to_cpu(ticket_node->ticket->action));
+		kfree_skb(packet->skb);
+	}
+
+	kfree(packet);
+	iwm_rx_ticket_node_free(ticket_node);
+}
+
+/*
+ * Rx data processing:
+ *
+ * We're receiving Rx packet from the LMAC, and Rx ticket from
+ * the UMAC.
+ * To forward a target data packet upstream (i.e. to the
+ * kernel network stack), we must have received an Rx ticket
+ * that tells us we're allowed to release this packet (ticket
+ * action is IWM_RX_TICKET_RELEASE). The Rx ticket also indicates,
+ * among other things, where valid data actually starts in the Rx
+ * packet.
+ */
+void iwm_rx_worker(struct work_struct *work)
+{
+	struct iwm_priv *iwm;
+	struct iwm_rx_ticket_node *ticket, *next;
+
+	iwm = container_of(work, struct iwm_priv, rx_worker);
+
+	/*
+	 * We go through the tickets list and if there is a pending
+	 * packet for it, we push it upstream.
+	 * We stop whenever a ticket is missing its packet, as we're
+	 * supposed to send the packets in order.
+	 */
+	list_for_each_entry_safe(ticket, next, &iwm->rx_tickets, node) {
+		struct iwm_rx_packet *packet =
+			iwm_rx_packet_get(iwm, le16_to_cpu(ticket->ticket->id));
+
+		if (!packet) {
+			IWM_DBG_RX(iwm, DBG, "Skip rx_work: Wait for ticket %d "
+				   "to be handled first\n",
+				   le16_to_cpu(ticket->ticket->id));
+			return;
+		}
+
+		list_del(&ticket->node);
+		list_del(&packet->node);
+		iwm_rx_process_packet(iwm, packet, ticket);
+	}
+}
+
diff --git a/drivers/net/wireless/iwmc3200wifi/rx.h b/drivers/net/wireless/iwmc3200wifi/rx.h
new file mode 100644
index 0000000..da0db91
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/rx.h
@@ -0,0 +1,60 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 of 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.
+ *
+ *
+ * Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ */
+
+#ifndef __IWM_RX_H__
+#define __IWM_RX_H__
+
+#include <linux/skbuff.h>
+
+#include "umac.h"
+
+struct iwm_rx_ticket_node {
+	struct list_head node;
+	struct iwm_rx_ticket *ticket;
+};
+
+struct iwm_rx_packet {
+	struct list_head node;
+	u16 id;
+	struct sk_buff *skb;
+	unsigned long pkt_size;
+};
+
+void iwm_rx_worker(struct work_struct *work);
+
+#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.c b/drivers/net/wireless/iwmc3200wifi/sdio.c
new file mode 100644
index 0000000..edc0a00
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/sdio.c
@@ -0,0 +1,516 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 of 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.
+ *
+ *
+ * Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ */
+
+/*
+ * This is the SDIO bus specific hooks for iwm.
+ * It also is the module's entry point.
+ *
+ * Interesting code paths:
+ * iwm_sdio_probe() (Called by an SDIO bus scan)
+ *  -> iwm_if_alloc() (netdev.c)
+ *      -> iwm_wdev_alloc() (cfg80211.c, allocates and register our wiphy)
+ *          -> wiphy_new()
+ *          -> wiphy_register()
+ *      -> alloc_netdev_mq()
+ *      -> register_netdev()
+ *
+ * iwm_sdio_remove()
+ *  -> iwm_if_free() (netdev.c)
+ *      -> unregister_netdev()
+ *      -> iwm_wdev_free() (cfg80211.c)
+ *          -> wiphy_unregister()
+ *          -> wiphy_free()
+ *
+ * iwm_sdio_isr() (called in process context from the SDIO core code)
+ *  -> queue_work(.., isr_worker)
+ *      -- [async] --> iwm_sdio_isr_worker()
+ *                      -> iwm_rx_handle()
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/debugfs.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+
+#include "iwm.h"
+#include "debug.h"
+#include "bus.h"
+#include "sdio.h"
+
+static void iwm_sdio_isr_worker(struct work_struct *work)
+{
+	struct iwm_sdio_priv *hw;
+	struct iwm_priv *iwm;
+	struct iwm_rx_info *rx_info;
+	struct sk_buff *skb;
+	u8 *rx_buf;
+	unsigned long rx_size;
+
+	hw = container_of(work, struct iwm_sdio_priv, isr_worker);
+	iwm = hw_to_iwm(hw);
+
+	while (!skb_queue_empty(&iwm->rx_list)) {
+		skb = skb_dequeue(&iwm->rx_list);
+		rx_info = skb_to_rx_info(skb);
+		rx_size = rx_info->rx_size;
+		rx_buf = skb->data;
+
+		IWM_HEXDUMP(iwm, DBG, SDIO, "RX: ", rx_buf, rx_size);
+		if (iwm_rx_handle(iwm, rx_buf, rx_size) < 0)
+			IWM_WARN(iwm, "RX error\n");
+
+		kfree_skb(skb);
+	}
+}
+
+static void iwm_sdio_isr(struct sdio_func *func)
+{
+	struct iwm_priv *iwm;
+	struct iwm_sdio_priv *hw;
+	struct iwm_rx_info *rx_info;
+	struct sk_buff *skb;
+	unsigned long buf_size, read_size;
+	int ret;
+	u8 val;
+
+	hw = sdio_get_drvdata(func);
+	iwm = hw_to_iwm(hw);
+
+	buf_size = hw->blk_size;
+
+	/* We're checking the status */
+	val = sdio_readb(func, IWM_SDIO_INTR_STATUS_ADDR, &ret);
+	if (val == 0 || ret < 0) {
+		IWM_ERR(iwm, "Wrong INTR_STATUS\n");
+		return;
+	}
+
+	/* See if we have free buffers */
+	if (skb_queue_len(&iwm->rx_list) > IWM_RX_LIST_SIZE) {
+		IWM_ERR(iwm, "No buffer for more Rx frames\n");
+		return;
+	}
+
+	/* We first read the transaction size */
+	read_size = sdio_readb(func, IWM_SDIO_INTR_GET_SIZE_ADDR + 1, &ret);
+	read_size = read_size << 8;
+
+	if (ret < 0) {
+		IWM_ERR(iwm, "Couldn't read the xfer size\n");
+		return;
+	}
+
+	/* We need to clear the INT register */
+	sdio_writeb(func, 1, IWM_SDIO_INTR_CLEAR_ADDR, &ret);
+	if (ret < 0) {
+		IWM_ERR(iwm, "Couldn't clear the INT register\n");
+		return;
+	}
+
+	while (buf_size < read_size)
+		buf_size <<= 1;
+
+	skb = dev_alloc_skb(buf_size);
+	if (!skb) {
+		IWM_ERR(iwm, "Couldn't alloc RX skb\n");
+		return;
+	}
+	rx_info = skb_to_rx_info(skb);
+	rx_info->rx_size = read_size;
+	rx_info->rx_buf_size = buf_size;
+
+	/* Now we can read the actual buffer */
+	ret = sdio_memcpy_fromio(func, skb_put(skb, read_size),
+				 IWM_SDIO_DATA_ADDR, read_size);
+
+	/* The skb is put on a driver's specific Rx SKB list */
+	skb_queue_tail(&iwm->rx_list, skb);
+
+	/* We can now schedule the actual worker */
+	queue_work(hw->isr_wq, &hw->isr_worker);
+}
+
+static void iwm_sdio_rx_free(struct iwm_sdio_priv *hw)
+{
+	struct iwm_priv *iwm = hw_to_iwm(hw);
+
+	flush_workqueue(hw->isr_wq);
+
+	skb_queue_purge(&iwm->rx_list);
+}
+
+/* Bus ops */
+static int if_sdio_enable(struct iwm_priv *iwm)
+{
+	struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
+	int ret;
+
+	sdio_claim_host(hw->func);
+
+	ret = sdio_enable_func(hw->func);
+	if (ret) {
+		IWM_ERR(iwm, "Couldn't enable the device: is TOP driver "
+			"loaded and functional?\n");
+		goto release_host;
+	}
+
+	iwm_reset(iwm);
+
+	ret = sdio_claim_irq(hw->func, iwm_sdio_isr);
+	if (ret) {
+		IWM_ERR(iwm, "Failed to claim irq: %d\n", ret);
+		goto release_host;
+	}
+
+	sdio_writeb(hw->func, 1, IWM_SDIO_INTR_ENABLE_ADDR, &ret);
+	if (ret < 0) {
+		IWM_ERR(iwm, "Couldn't enable INTR: %d\n", ret);
+		goto release_irq;
+	}
+
+	sdio_release_host(hw->func);
+
+	IWM_DBG_SDIO(iwm, INFO, "IWM SDIO enable\n");
+
+	return 0;
+
+ release_irq:
+	sdio_release_irq(hw->func);
+ release_host:
+	sdio_release_host(hw->func);
+
+	return ret;
+}
+
+static int if_sdio_disable(struct iwm_priv *iwm)
+{
+	struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
+	int ret;
+
+	iwm_reset(iwm);
+
+	sdio_claim_host(hw->func);
+	sdio_writeb(hw->func, 0, IWM_SDIO_INTR_ENABLE_ADDR, &ret);
+	if (ret < 0)
+		IWM_WARN(iwm, "Couldn't disable INTR: %d\n", ret);
+
+	sdio_release_irq(hw->func);
+	sdio_disable_func(hw->func);
+	sdio_release_host(hw->func);
+
+	iwm_sdio_rx_free(hw);
+
+	IWM_DBG_SDIO(iwm, INFO, "IWM SDIO disable\n");
+
+	return 0;
+}
+
+static int if_sdio_send_chunk(struct iwm_priv *iwm, u8 *buf, int count)
+{
+	struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
+	int aligned_count = ALIGN(count, hw->blk_size);
+	int ret;
+
+	if ((unsigned long)buf & 0x3) {
+		IWM_ERR(iwm, "buf <%p> is not dword aligned\n", buf);
+		/* TODO: Is this a hardware limitation? use get_unligned */
+		return -EINVAL;
+	}
+
+	sdio_claim_host(hw->func);
+	ret = sdio_memcpy_toio(hw->func, IWM_SDIO_DATA_ADDR, buf,
+			       aligned_count);
+	sdio_release_host(hw->func);
+
+	return ret;
+}
+
+/* debugfs hooks */
+static int iwm_debugfs_sdio_open(struct inode *inode, struct file *filp)
+{
+	filp->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t iwm_debugfs_sdio_read(struct file *filp, char __user *buffer,
+				     size_t count, loff_t *ppos)
+{
+	struct iwm_priv *iwm = filp->private_data;
+	struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
+	char *buf;
+	u8 cccr;
+	int buf_len = 4096, ret;
+	size_t len = 0;
+
+	if (*ppos != 0)
+		return 0;
+	if (count < sizeof(buf))
+		return -ENOSPC;
+
+	buf = kzalloc(buf_len, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	sdio_claim_host(hw->func);
+
+	cccr =  sdio_f0_readb(hw->func, SDIO_CCCR_IOEx, &ret);
+	if (ret) {
+		IWM_ERR(iwm, "Could not read SDIO_CCCR_IOEx\n");
+		goto err;
+	}
+	len += snprintf(buf + len, buf_len - len, "CCCR_IOEx:  0x%x\n", cccr);
+
+	cccr =  sdio_f0_readb(hw->func, SDIO_CCCR_IORx, &ret);
+	if (ret) {
+		IWM_ERR(iwm, "Could not read SDIO_CCCR_IORx\n");
+		goto err;
+	}
+	len += snprintf(buf + len, buf_len - len, "CCCR_IORx:  0x%x\n", cccr);
+
+
+	cccr =  sdio_f0_readb(hw->func, SDIO_CCCR_IENx, &ret);
+	if (ret) {
+		IWM_ERR(iwm, "Could not read SDIO_CCCR_IENx\n");
+		goto err;
+	}
+	len += snprintf(buf + len, buf_len - len, "CCCR_IENx:  0x%x\n", cccr);
+
+
+	cccr =  sdio_f0_readb(hw->func, SDIO_CCCR_INTx, &ret);
+	if (ret) {
+		IWM_ERR(iwm, "Could not read SDIO_CCCR_INTx\n");
+		goto err;
+	}
+	len += snprintf(buf + len, buf_len - len, "CCCR_INTx:  0x%x\n", cccr);
+
+
+	cccr =  sdio_f0_readb(hw->func, SDIO_CCCR_ABORT, &ret);
+	if (ret) {
+		IWM_ERR(iwm, "Could not read SDIO_CCCR_ABORTx\n");
+		goto err;
+	}
+	len += snprintf(buf + len, buf_len - len, "CCCR_ABORT: 0x%x\n", cccr);
+
+	cccr =  sdio_f0_readb(hw->func, SDIO_CCCR_IF, &ret);
+	if (ret) {
+		IWM_ERR(iwm, "Could not read SDIO_CCCR_IF\n");
+		goto err;
+	}
+	len += snprintf(buf + len, buf_len - len, "CCCR_IF:    0x%x\n", cccr);
+
+
+	cccr =  sdio_f0_readb(hw->func, SDIO_CCCR_CAPS, &ret);
+	if (ret) {
+		IWM_ERR(iwm, "Could not read SDIO_CCCR_CAPS\n");
+		goto err;
+	}
+	len += snprintf(buf + len, buf_len - len, "CCCR_CAPS:  0x%x\n", cccr);
+
+	cccr =  sdio_f0_readb(hw->func, SDIO_CCCR_CIS, &ret);
+	if (ret) {
+		IWM_ERR(iwm, "Could not read SDIO_CCCR_CIS\n");
+		goto err;
+	}
+	len += snprintf(buf + len, buf_len - len, "CCCR_CIS:   0x%x\n", cccr);
+
+	ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
+err:
+	sdio_release_host(hw->func);
+
+	kfree(buf);
+
+	return ret;
+}
+
+static const struct file_operations iwm_debugfs_sdio_fops = {
+	.owner =	THIS_MODULE,
+	.open =		iwm_debugfs_sdio_open,
+	.read =		iwm_debugfs_sdio_read,
+};
+
+static int if_sdio_debugfs_init(struct iwm_priv *iwm, struct dentry *parent_dir)
+{
+	int result;
+	struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
+
+	hw->cccr_dentry = debugfs_create_file("cccr", 0200,
+					      parent_dir, iwm,
+					      &iwm_debugfs_sdio_fops);
+	result = PTR_ERR(hw->cccr_dentry);
+	if (IS_ERR(hw->cccr_dentry) && (result != -ENODEV)) {
+		IWM_ERR(iwm, "Couldn't create CCCR entry: %d\n", result);
+		return result;
+	}
+
+	return 0;
+}
+
+static void if_sdio_debugfs_exit(struct iwm_priv *iwm)
+{
+	struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
+
+	debugfs_remove(hw->cccr_dentry);
+}
+
+static struct iwm_if_ops if_sdio_ops = {
+	.enable = if_sdio_enable,
+	.disable = if_sdio_disable,
+	.send_chunk = if_sdio_send_chunk,
+	.debugfs_init = if_sdio_debugfs_init,
+	.debugfs_exit = if_sdio_debugfs_exit,
+	.umac_name = "iwmc3200wifi-umac-sdio.bin",
+	.calib_lmac_name = "iwmc3200wifi-lmac-calib-sdio.bin",
+	.lmac_name = "iwmc3200wifi-lmac-sdio.bin",
+};
+
+static int iwm_sdio_probe(struct sdio_func *func,
+			  const struct sdio_device_id *id)
+{
+	struct iwm_priv *iwm;
+	struct iwm_sdio_priv *hw;
+	struct device *dev = &func->dev;
+	int ret;
+
+	/* check if TOP has already initialized the card */
+	sdio_claim_host(func);
+	ret = sdio_enable_func(func);
+	if (ret) {
+		dev_err(dev, "wait for TOP to enable the device\n");
+		sdio_release_host(func);
+		return ret;
+	}
+
+	ret = sdio_set_block_size(func, IWM_SDIO_BLK_SIZE);
+
+	sdio_disable_func(func);
+	sdio_release_host(func);
+
+	if (ret < 0) {
+		dev_err(dev, "Failed to set block size: %d\n", ret);
+		return ret;
+	}
+
+	iwm = iwm_if_alloc(sizeof(struct iwm_sdio_priv), dev, &if_sdio_ops);
+	if (IS_ERR(iwm)) {
+		dev_err(dev, "allocate SDIO interface failed\n");
+		return PTR_ERR(iwm);
+	}
+
+	hw = iwm_private(iwm);
+	hw->iwm = iwm;
+
+	ret = iwm_debugfs_init(iwm);
+	if (ret < 0) {
+		IWM_ERR(iwm, "Debugfs registration failed\n");
+		goto if_free;
+	}
+
+	sdio_set_drvdata(func, hw);
+
+	hw->func = func;
+	hw->blk_size = IWM_SDIO_BLK_SIZE;
+
+	hw->isr_wq = create_singlethread_workqueue(KBUILD_MODNAME "_sdio");
+	if (!hw->isr_wq) {
+		ret = -ENOMEM;
+		goto debugfs_exit;
+	}
+
+	INIT_WORK(&hw->isr_worker, iwm_sdio_isr_worker);
+
+	dev_info(dev, "IWM SDIO probe\n");
+
+	return 0;
+
+ debugfs_exit:
+	iwm_debugfs_exit(iwm);
+ if_free:
+	iwm_if_free(iwm);
+	return ret;
+}
+
+static void iwm_sdio_remove(struct sdio_func *func)
+{
+	struct iwm_sdio_priv *hw = sdio_get_drvdata(func);
+	struct iwm_priv *iwm = hw_to_iwm(hw);
+	struct device *dev = &func->dev;
+
+	iwm_debugfs_exit(iwm);
+	iwm_if_free(iwm);
+	destroy_workqueue(hw->isr_wq);
+
+	sdio_set_drvdata(func, NULL);
+
+	dev_info(dev, "IWM SDIO remove\n");
+
+	return;
+}
+
+static const struct sdio_device_id iwm_sdio_ids[] = {
+	{ SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, SDIO_DEVICE_ID_IWM) },
+	{ /* end: all zeroes */	},
+};
+MODULE_DEVICE_TABLE(sdio, iwm_sdio_ids);
+
+static struct sdio_driver iwm_sdio_driver = {
+	.name		= "iwm_sdio",
+	.id_table	= iwm_sdio_ids,
+	.probe		= iwm_sdio_probe,
+	.remove		= iwm_sdio_remove,
+};
+
+static int __init iwm_sdio_init_module(void)
+{
+	int ret;
+
+	ret = sdio_register_driver(&iwm_sdio_driver);
+
+	return ret;
+}
+
+static void __exit iwm_sdio_exit_module(void)
+{
+	sdio_unregister_driver(&iwm_sdio_driver);
+}
+
+module_init(iwm_sdio_init_module);
+module_exit(iwm_sdio_exit_module);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(IWM_COPYRIGHT " " IWM_AUTHOR);
diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.h b/drivers/net/wireless/iwmc3200wifi/sdio.h
new file mode 100644
index 0000000..b3c156b
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/sdio.h
@@ -0,0 +1,67 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 of 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.
+ *
+ *
+ * Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ */
+
+#ifndef __IWM_SDIO_H__
+#define __IWM_SDIO_H__
+
+#define SDIO_VENDOR_ID_INTEL 0x89
+#define SDIO_DEVICE_ID_IWM   0x1403
+
+#define IWM_SDIO_DATA_ADDR           0x0
+#define IWM_SDIO_INTR_ENABLE_ADDR    0x14
+#define IWM_SDIO_INTR_STATUS_ADDR    0x13
+#define IWM_SDIO_INTR_CLEAR_ADDR     0x13
+#define IWM_SDIO_INTR_GET_SIZE_ADDR  0x2C
+
+#define IWM_SDIO_BLK_SIZE       256
+
+#define iwm_to_if_sdio(i) (struct iwm_sdio_priv *)(iwm->private)
+
+struct iwm_sdio_priv {
+	struct sdio_func *func;
+	struct iwm_priv *iwm;
+
+	struct workqueue_struct *isr_wq;
+	struct work_struct isr_worker;
+
+	struct dentry *cccr_dentry;
+
+	unsigned int blk_size;
+};
+
+#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/tx.c b/drivers/net/wireless/iwmc3200wifi/tx.c
new file mode 100644
index 0000000..e3b4f79
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/tx.c
@@ -0,0 +1,492 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 of 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.
+ *
+ *
+ * Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ */
+
+/*
+ * iwm Tx theory of operation:
+ *
+ * 1) We receive a 802.3 frame from the stack
+ * 2) We convert it to a 802.11 frame [iwm_xmit_frame]
+ * 3) We queue it to its corresponding tx queue [iwm_xmit_frame]
+ * 4) We schedule the tx worker. There is one worker per tx
+ *    queue. [iwm_xmit_frame]
+ * 5) The tx worker is scheduled
+ * 6) We go through every queued skb on the tx queue, and for each
+ *    and every one of them: [iwm_tx_worker]
+ *    a) We check if we have enough Tx credits (see below for a Tx
+ *       credits description) for the frame length. [iwm_tx_worker]
+ *    b) If we do, we aggregate the Tx frame into a UDMA one, by
+ *       concatenating one REPLY_TX command per Tx frame. [iwm_tx_worker]
+ *    c) When we run out of credits, or when we reach the maximum
+ *       concatenation size, we actually send the concatenated UDMA
+ *       frame. [iwm_tx_worker]
+ *
+ * When we run out of Tx credits, the skbs are filling the tx queue,
+ * and eventually we will stop the netdev queue. [iwm_tx_worker]
+ * The tx queue is emptied as we're getting new tx credits, by
+ * scheduling the tx_worker. [iwm_tx_credit_inc]
+ * The netdev queue is started again when we have enough tx credits,
+ * and when our tx queue has some reasonable amout of space available
+ * (i.e. half of the max size). [iwm_tx_worker]
+ */
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/ieee80211.h>
+
+#include "iwm.h"
+#include "debug.h"
+#include "commands.h"
+#include "hal.h"
+#include "umac.h"
+#include "bus.h"
+
+#define IWM_UMAC_PAGE_ALLOC_WRAP 0xffff
+
+#define BYTES_TO_PAGES(n)	 (1 + ((n) >> ilog2(IWM_UMAC_PAGE_SIZE)) - \
+				 (((n) & (IWM_UMAC_PAGE_SIZE - 1)) == 0))
+
+#define pool_id_to_queue(id)	 ((id < IWM_TX_CMD_QUEUE) ? id : id - 1)
+#define queue_to_pool_id(q)	 ((q < IWM_TX_CMD_QUEUE) ? q : q + 1)
+
+/* require to hold tx_credit lock */
+static int iwm_tx_credit_get(struct iwm_tx_credit *tx_credit, int id)
+{
+	struct pool_entry *pool = &tx_credit->pools[id];
+	struct spool_entry *spool = &tx_credit->spools[pool->sid];
+	int spool_pages;
+
+	/* number of pages can be taken from spool by this pool */
+	spool_pages = spool->max_pages - spool->alloc_pages +
+		      max(pool->min_pages - pool->alloc_pages, 0);
+
+	return min(pool->max_pages - pool->alloc_pages, spool_pages);
+}
+
+static bool iwm_tx_credit_ok(struct iwm_priv *iwm, int id, int nb)
+{
+	u32 npages = BYTES_TO_PAGES(nb);
+
+	if (npages <= iwm_tx_credit_get(&iwm->tx_credit, id))
+		return 1;
+
+	set_bit(id, &iwm->tx_credit.full_pools_map);
+
+	IWM_DBG_TX(iwm, DBG, "LINK: stop txq[%d], available credit: %d\n",
+		   pool_id_to_queue(id),
+		   iwm_tx_credit_get(&iwm->tx_credit, id));
+
+	return 0;
+}
+
+void iwm_tx_credit_inc(struct iwm_priv *iwm, int id, int total_freed_pages)
+{
+	struct pool_entry *pool;
+	struct spool_entry *spool;
+	int freed_pages;
+	int queue;
+
+	BUG_ON(id >= IWM_MACS_OUT_GROUPS);
+
+	pool = &iwm->tx_credit.pools[id];
+	spool = &iwm->tx_credit.spools[pool->sid];
+
+	freed_pages = total_freed_pages - pool->total_freed_pages;
+	IWM_DBG_TX(iwm, DBG, "Free %d pages for pool[%d]\n", freed_pages, id);
+
+	if (!freed_pages) {
+		IWM_DBG_TX(iwm, DBG, "No pages are freed by UMAC\n");
+		return;
+	} else if (freed_pages < 0)
+		freed_pages += IWM_UMAC_PAGE_ALLOC_WRAP + 1;
+
+	if (pool->alloc_pages > pool->min_pages) {
+		int spool_pages = pool->alloc_pages - pool->min_pages;
+		spool_pages = min(spool_pages, freed_pages);
+		spool->alloc_pages -= spool_pages;
+	}
+
+	pool->alloc_pages -= freed_pages;
+	pool->total_freed_pages = total_freed_pages;
+
+	IWM_DBG_TX(iwm, DBG, "Pool[%d] pages alloc: %d, total_freed: %d, "
+		   "Spool[%d] pages alloc: %d\n", id, pool->alloc_pages,
+		   pool->total_freed_pages, pool->sid, spool->alloc_pages);
+
+	if (test_bit(id, &iwm->tx_credit.full_pools_map) &&
+	    (pool->alloc_pages < pool->max_pages / 2)) {
+		clear_bit(id, &iwm->tx_credit.full_pools_map);
+
+		queue = pool_id_to_queue(id);
+
+		IWM_DBG_TX(iwm, DBG, "LINK: start txq[%d], available "
+			   "credit: %d\n", queue,
+			   iwm_tx_credit_get(&iwm->tx_credit, id));
+		queue_work(iwm->txq[queue].wq, &iwm->txq[queue].worker);
+	}
+}
+
+static void iwm_tx_credit_dec(struct iwm_priv *iwm, int id, int alloc_pages)
+{
+	struct pool_entry *pool;
+	struct spool_entry *spool;
+	int spool_pages;
+
+	IWM_DBG_TX(iwm, DBG, "Allocate %d pages for pool[%d]\n",
+		   alloc_pages, id);
+
+	BUG_ON(id >= IWM_MACS_OUT_GROUPS);
+
+	pool = &iwm->tx_credit.pools[id];
+	spool = &iwm->tx_credit.spools[pool->sid];
+
+	spool_pages = pool->alloc_pages + alloc_pages - pool->min_pages;
+
+	if (pool->alloc_pages >= pool->min_pages)
+		spool->alloc_pages += alloc_pages;
+	else if (spool_pages > 0)
+		spool->alloc_pages += spool_pages;
+
+	pool->alloc_pages += alloc_pages;
+
+	IWM_DBG_TX(iwm, DBG, "Pool[%d] pages alloc: %d, total_freed: %d, "
+		   "Spool[%d] pages alloc: %d\n", id, pool->alloc_pages,
+		   pool->total_freed_pages, pool->sid, spool->alloc_pages);
+}
+
+int iwm_tx_credit_alloc(struct iwm_priv *iwm, int id, int nb)
+{
+	u32 npages = BYTES_TO_PAGES(nb);
+	int ret = 0;
+
+	spin_lock(&iwm->tx_credit.lock);
+
+	if (!iwm_tx_credit_ok(iwm, id, nb)) {
+		IWM_DBG_TX(iwm, DBG, "No credit avaliable for pool[%d]\n", id);
+		ret = -ENOSPC;
+		goto out;
+	}
+
+	iwm_tx_credit_dec(iwm, id, npages);
+
+ out:
+	spin_unlock(&iwm->tx_credit.lock);
+	return ret;
+}
+
+/*
+ * Since we're on an SDIO or USB bus, we are not sharing memory
+ * for storing to be transmitted frames. The host needs to push
+ * them upstream. As a consequence there needs to be a way for
+ * the target to let us know if it can actually take more TX frames
+ * or not. This is what Tx credits are for.
+ *
+ * For each Tx HW queue, we have a Tx pool, and then we have one
+ * unique super pool (spool), which is actually a global pool of
+ * all the UMAC pages.
+ * For each Tx pool we have a min_pages, a max_pages fields, and a
+ * alloc_pages fields. The alloc_pages tracks the number of pages
+ * currently allocated from the tx pool.
+ * Here are the rules to check if given a tx frame we have enough
+ * tx credits for it:
+ * 1) We translate the frame length into a number of UMAC pages.
+ *    Let's call them n_pages.
+ * 2) For the corresponding tx pool, we check if n_pages +
+ *    pool->alloc_pages is higher than pool->min_pages. min_pages
+ *    represent a set of pre-allocated pages on the tx pool. If
+ *    that's the case, then we need to allocate those pages from
+ *    the spool. We can do so until we reach spool->max_pages.
+ * 3) Each tx pool is not allowed to allocate more than pool->max_pages
+ *    from the spool, so once we're over min_pages, we can allocate
+ *    pages from the spool, but not more than max_pages.
+ *
+ * When the tx code path needs to send a tx frame, it checks first
+ * if it has enough tx credits, following those rules. [iwm_tx_credit_get]
+ * If it does, it then updates the pool and spool counters and
+ * then send the frame. [iwm_tx_credit_alloc and iwm_tx_credit_dec]
+ * On the other side, when the UMAC is done transmitting frames, it
+ * will send a credit update notification to the host. This is when
+ * the pool and spool counters gets to be decreased. [iwm_tx_credit_inc,
+ * called from rx.c:iwm_ntf_tx_credit_update]
+ *
+ */
+void iwm_tx_credit_init_pools(struct iwm_priv *iwm,
+			      struct iwm_umac_notif_alive *alive)
+{
+	int i, sid, pool_pages;
+
+	spin_lock(&iwm->tx_credit.lock);
+
+	iwm->tx_credit.pool_nr = le16_to_cpu(alive->page_grp_count);
+	iwm->tx_credit.full_pools_map = 0;
+	memset(&iwm->tx_credit.spools[0], 0, sizeof(struct spool_entry));
+
+	IWM_DBG_TX(iwm, DBG, "Pools number is %d\n", iwm->tx_credit.pool_nr);
+
+	for (i = 0; i < iwm->tx_credit.pool_nr; i++) {
+		__le32 page_grp_state = alive->page_grp_state[i];
+
+		iwm->tx_credit.pools[i].id = GET_VAL32(page_grp_state,
+				UMAC_ALIVE_PAGE_STS_GRP_NUM);
+		iwm->tx_credit.pools[i].sid = GET_VAL32(page_grp_state,
+				UMAC_ALIVE_PAGE_STS_SGRP_NUM);
+		iwm->tx_credit.pools[i].min_pages = GET_VAL32(page_grp_state,
+				UMAC_ALIVE_PAGE_STS_GRP_MIN_SIZE);
+		iwm->tx_credit.pools[i].max_pages = GET_VAL32(page_grp_state,
+				UMAC_ALIVE_PAGE_STS_GRP_MAX_SIZE);
+		iwm->tx_credit.pools[i].alloc_pages = 0;
+		iwm->tx_credit.pools[i].total_freed_pages = 0;
+
+		sid = iwm->tx_credit.pools[i].sid;
+		pool_pages = iwm->tx_credit.pools[i].min_pages;
+
+		if (iwm->tx_credit.spools[sid].max_pages == 0) {
+			iwm->tx_credit.spools[sid].id = sid;
+			iwm->tx_credit.spools[sid].max_pages =
+				GET_VAL32(page_grp_state,
+					  UMAC_ALIVE_PAGE_STS_SGRP_MAX_SIZE);
+			iwm->tx_credit.spools[sid].alloc_pages = 0;
+		}
+
+		iwm->tx_credit.spools[sid].alloc_pages += pool_pages;
+
+		IWM_DBG_TX(iwm, DBG, "Pool idx: %d, id: %d, sid: %d, capacity "
+			   "min: %d, max: %d, pool alloc: %d, total_free: %d, "
+			   "super poll alloc: %d\n",
+			   i, iwm->tx_credit.pools[i].id,
+			   iwm->tx_credit.pools[i].sid,
+			   iwm->tx_credit.pools[i].min_pages,
+			   iwm->tx_credit.pools[i].max_pages,
+			   iwm->tx_credit.pools[i].alloc_pages,
+			   iwm->tx_credit.pools[i].total_freed_pages,
+			   iwm->tx_credit.spools[sid].alloc_pages);
+	}
+
+	spin_unlock(&iwm->tx_credit.lock);
+}
+
+#define IWM_UDMA_HDR_LEN	sizeof(struct iwm_umac_wifi_out_hdr)
+
+static int iwm_tx_build_packet(struct iwm_priv *iwm, struct sk_buff *skb,
+			       int pool_id, u8 *buf)
+{
+	struct iwm_umac_wifi_out_hdr *hdr = (struct iwm_umac_wifi_out_hdr *)buf;
+	struct iwm_udma_wifi_cmd udma_cmd;
+	struct iwm_umac_cmd umac_cmd;
+	struct iwm_tx_info *tx_info = skb_to_tx_info(skb);
+
+	udma_cmd.count = cpu_to_le16(skb->len +
+				     sizeof(struct iwm_umac_fw_cmd_hdr));
+	/* set EOP to 0 here. iwm_udma_wifi_hdr_set_eop() will be
+	 * called later to set EOP for the last packet. */
+	udma_cmd.eop = 0;
+	udma_cmd.credit_group = pool_id;
+	udma_cmd.ra_tid = tx_info->sta << 4 | tx_info->tid;
+	udma_cmd.lmac_offset = 0;
+
+	umac_cmd.id = REPLY_TX;
+	umac_cmd.count = cpu_to_le16(skb->len);
+	umac_cmd.color = tx_info->color;
+	umac_cmd.resp = 0;
+	umac_cmd.seq_num = cpu_to_le16(iwm_alloc_wifi_cmd_seq(iwm));
+
+	iwm_build_udma_wifi_hdr(iwm, &hdr->hw_hdr, &udma_cmd);
+	iwm_build_umac_hdr(iwm, &hdr->sw_hdr, &umac_cmd);
+
+	memcpy(buf + sizeof(*hdr), skb->data, skb->len);
+
+	return 0;
+}
+
+static int iwm_tx_send_concat_packets(struct iwm_priv *iwm,
+				      struct iwm_tx_queue *txq)
+{
+	int ret;
+
+	if (!txq->concat_count)
+		return 0;
+
+	IWM_DBG_TX(iwm, DBG, "Send concatenated Tx: queue %d, %d bytes\n",
+		   txq->id, txq->concat_count);
+
+	/* mark EOP for the last packet */
+	iwm_udma_wifi_hdr_set_eop(iwm, txq->concat_ptr, 1);
+
+	ret = iwm_bus_send_chunk(iwm, txq->concat_buf, txq->concat_count);
+
+	txq->concat_count = 0;
+	txq->concat_ptr = txq->concat_buf;
+
+	return ret;
+}
+
+#define CONFIG_IWM_TX_CONCATENATED 1
+
+void iwm_tx_worker(struct work_struct *work)
+{
+	struct iwm_priv *iwm;
+	struct iwm_tx_info *tx_info = NULL;
+	struct sk_buff *skb;
+	int cmdlen, ret;
+	struct iwm_tx_queue *txq;
+	int pool_id;
+
+	txq = container_of(work, struct iwm_tx_queue, worker);
+	iwm = container_of(txq, struct iwm_priv, txq[txq->id]);
+
+	pool_id = queue_to_pool_id(txq->id);
+
+	while (!test_bit(pool_id, &iwm->tx_credit.full_pools_map) &&
+	       !skb_queue_empty(&txq->queue)) {
+
+		skb = skb_dequeue(&txq->queue);
+		tx_info = skb_to_tx_info(skb);
+		cmdlen = IWM_UDMA_HDR_LEN + skb->len;
+
+		IWM_DBG_TX(iwm, DBG, "Tx frame on queue %d: skb: 0x%p, sta: "
+			   "%d, color: %d\n", txq->id, skb, tx_info->sta,
+			   tx_info->color);
+
+#if !CONFIG_IWM_TX_CONCATENATED
+		/* temporarily keep this to comparing the performance */
+		ret = iwm_send_packet(iwm, skb, pool_id);
+#else
+
+		if (txq->concat_count + cmdlen > IWM_HAL_CONCATENATE_BUF_SIZE)
+			iwm_tx_send_concat_packets(iwm, txq);
+
+		ret = iwm_tx_credit_alloc(iwm, pool_id, cmdlen);
+		if (ret) {
+			IWM_DBG_TX(iwm, DBG, "not enough tx_credit for queue "
+				   "%d, Tx worker stopped\n", txq->id);
+			skb_queue_head(&txq->queue, skb);
+			break;
+		}
+
+		txq->concat_ptr = txq->concat_buf + txq->concat_count;
+		iwm_tx_build_packet(iwm, skb, pool_id, txq->concat_ptr);
+		txq->concat_count += ALIGN(cmdlen, 16);
+#endif
+		kfree_skb(skb);
+	}
+
+	iwm_tx_send_concat_packets(iwm, txq);
+
+	if (__netif_subqueue_stopped(iwm_to_ndev(iwm), txq->id) &&
+	    !test_bit(pool_id, &iwm->tx_credit.full_pools_map) &&
+	    (skb_queue_len(&txq->queue) < IWM_TX_LIST_SIZE / 2)) {
+		IWM_DBG_TX(iwm, DBG, "LINK: start netif_subqueue[%d]", txq->id);
+		netif_wake_subqueue(iwm_to_ndev(iwm), txq->id);
+	}
+}
+
+int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(netdev);
+	struct net_device *ndev = iwm_to_ndev(iwm);
+	struct wireless_dev *wdev = iwm_to_wdev(iwm);
+	u8 *dst_addr;
+	struct iwm_tx_info *tx_info;
+	struct iwm_tx_queue *txq;
+	struct iwm_sta_info *sta_info;
+	u8 sta_id;
+	u16 queue;
+	int ret;
+
+	if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
+		IWM_DBG_TX(iwm, DBG, "LINK: stop netif_all_queues: "
+			   "not associated\n");
+		netif_tx_stop_all_queues(netdev);
+		goto drop;
+	}
+
+	queue = skb_get_queue_mapping(skb);
+	BUG_ON(queue >= IWM_TX_DATA_QUEUES); /* no iPAN yet */
+
+	txq = &iwm->txq[queue];
+
+	/* No free space for Tx, tx_worker is too slow */
+	if (skb_queue_len(&txq->queue) > IWM_TX_LIST_SIZE) {
+		IWM_DBG_TX(iwm, DBG, "LINK: stop netif_subqueue[%d]\n", queue);
+		netif_stop_subqueue(netdev, queue);
+		return NETDEV_TX_BUSY;
+	}
+
+	ret = ieee80211_data_from_8023(skb, netdev->dev_addr, wdev->iftype,
+				       iwm->bssid, 0);
+	if (ret) {
+		IWM_ERR(iwm, "build wifi header failed\n");
+		goto drop;
+	}
+
+	dst_addr = ((struct ieee80211_hdr *)(skb->data))->addr1;
+
+	for (sta_id = 0; sta_id < IWM_STA_TABLE_NUM; sta_id++) {
+		sta_info = &iwm->sta_table[sta_id];
+		if (sta_info->valid &&
+		    !memcmp(dst_addr, sta_info->addr, ETH_ALEN))
+			break;
+	}
+
+	if (sta_id == IWM_STA_TABLE_NUM) {
+		IWM_ERR(iwm, "STA %pM not found in sta_table, Tx ignored\n",
+			dst_addr);
+		goto drop;
+	}
+
+	tx_info = skb_to_tx_info(skb);
+	tx_info->sta = sta_id;
+	tx_info->color = sta_info->color;
+	/* UMAC uses TID 8 (vs. 0) for non QoS packets */
+	if (sta_info->qos)
+		tx_info->tid = skb->priority;
+	else
+		tx_info->tid = IWM_UMAC_MGMT_TID;
+
+	skb_queue_tail(&iwm->txq[queue].queue, skb);
+
+	queue_work(iwm->txq[queue].wq, &iwm->txq[queue].worker);
+
+	ndev->stats.tx_packets++;
+	ndev->stats.tx_bytes += skb->len;
+	return NETDEV_TX_OK;
+
+ drop:
+	ndev->stats.tx_dropped++;
+	dev_kfree_skb_any(skb);
+	return NETDEV_TX_OK;
+}
diff --git a/drivers/net/wireless/iwmc3200wifi/umac.h b/drivers/net/wireless/iwmc3200wifi/umac.h
new file mode 100644
index 0000000..4a95cce
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/umac.h
@@ -0,0 +1,744 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 of 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.
+ *
+ *
+ * Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ */
+
+#ifndef __IWM_UMAC_H__
+#define __IWM_UMAC_H__
+
+struct iwm_udma_in_hdr {
+	__le32 cmd;
+	__le32 size;
+} __attribute__ ((packed));
+
+struct iwm_udma_out_nonwifi_hdr {
+	__le32 cmd;
+	__le32 addr;
+	__le32 op1_sz;
+	__le32 op2;
+} __attribute__ ((packed));
+
+struct iwm_udma_out_wifi_hdr {
+	__le32 cmd;
+	__le32 meta_data;
+} __attribute__ ((packed));
+
+/* Sequence numbering */
+#define UMAC_WIFI_SEQ_NUM_BASE		1
+#define UMAC_WIFI_SEQ_NUM_MAX		0x4000
+#define UMAC_NONWIFI_SEQ_NUM_BASE	1
+#define UMAC_NONWIFI_SEQ_NUM_MAX	0x10
+
+/* MAC address address */
+#define WICO_MAC_ADDRESS_ADDR               0x604008F8
+
+/* RA / TID */
+#define UMAC_HDI_ACT_TBL_IDX_TID_POS                  0
+#define UMAC_HDI_ACT_TBL_IDX_TID_SEED                 0xF
+
+#define UMAC_HDI_ACT_TBL_IDX_RA_POS                   4
+#define UMAC_HDI_ACT_TBL_IDX_RA_SEED                  0xF
+
+#define UMAC_HDI_ACT_TBL_IDX_RA_UMAC                  0xF
+#define UMAC_HDI_ACT_TBL_IDX_TID_UMAC                 0x9
+#define UMAC_HDI_ACT_TBL_IDX_TID_LMAC                 0xA
+
+#define UMAC_HDI_ACT_TBL_IDX_HOST_CMD \
+	((UMAC_HDI_ACT_TBL_IDX_RA_UMAC << UMAC_HDI_ACT_TBL_IDX_RA_POS) |\
+	 (UMAC_HDI_ACT_TBL_IDX_TID_UMAC << UMAC_HDI_ACT_TBL_IDX_TID_POS))
+#define UMAC_HDI_ACT_TBL_IDX_UMAC_CMD \
+	((UMAC_HDI_ACT_TBL_IDX_RA_UMAC << UMAC_HDI_ACT_TBL_IDX_RA_POS) |\
+	(UMAC_HDI_ACT_TBL_IDX_TID_LMAC << UMAC_HDI_ACT_TBL_IDX_TID_POS))
+
+/* iwm_umac_notif_alive.page_grp_state Group number -- bits [3:0] */
+#define UMAC_ALIVE_PAGE_STS_GRP_NUM_POS		0
+#define UMAC_ALIVE_PAGE_STS_GRP_NUM_SEED	0xF
+
+/* iwm_umac_notif_alive.page_grp_state Super group number -- bits [7:4] */
+#define UMAC_ALIVE_PAGE_STS_SGRP_NUM_POS	4
+#define UMAC_ALIVE_PAGE_STS_SGRP_NUM_SEED	0xF
+
+/* iwm_umac_notif_alive.page_grp_state Group min size -- bits [15:8] */
+#define UMAC_ALIVE_PAGE_STS_GRP_MIN_SIZE_POS	8
+#define UMAC_ALIVE_PAGE_STS_GRP_MIN_SIZE_SEED	0xFF
+
+/* iwm_umac_notif_alive.page_grp_state Group max size -- bits [23:16] */
+#define UMAC_ALIVE_PAGE_STS_GRP_MAX_SIZE_POS	16
+#define UMAC_ALIVE_PAGE_STS_GRP_MAX_SIZE_SEED	0xFF
+
+/* iwm_umac_notif_alive.page_grp_state Super group max size -- bits [31:24] */
+#define UMAC_ALIVE_PAGE_STS_SGRP_MAX_SIZE_POS	24
+#define UMAC_ALIVE_PAGE_STS_SGRP_MAX_SIZE_SEED	0xFF
+
+/* Barkers */
+#define UMAC_REBOOT_BARKER		0xdeadbeef
+#define UMAC_ACK_BARKER			0xfeedbabe
+#define UMAC_PAD_TERMINAL		0xadadadad
+
+/* UMAC JMP address */
+#define UMAC_MU_FW_INST_DATA_12_ADDR    0xBF0000
+
+/* iwm_umac_hdi_out_hdr.cmd OP code -- bits [3:0] */
+#define UMAC_HDI_OUT_CMD_OPCODE_POS	0
+#define UMAC_HDI_OUT_CMD_OPCODE_SEED	0xF
+
+/* iwm_umac_hdi_out_hdr.cmd End-Of-Transfer -- bits [10:10] */
+#define UMAC_HDI_OUT_CMD_EOT_POS	10
+#define UMAC_HDI_OUT_CMD_EOT_SEED	0x1
+
+/* iwm_umac_hdi_out_hdr.cmd UTFD only usage -- bits [11:11] */
+#define UMAC_HDI_OUT_CMD_UTFD_ONLY_POS	11
+#define UMAC_HDI_OUT_CMD_UTFD_ONLY_SEED	0x1
+
+/* iwm_umac_hdi_out_hdr.cmd Non-WiFi HW sequence number -- bits [12:15] */
+#define UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM_POS   12
+#define UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM_SEED  0xF
+
+/* iwm_umac_hdi_out_hdr.cmd Signature -- bits [31:16] */
+#define UMAC_HDI_OUT_CMD_SIGNATURE_POS	16
+#define UMAC_HDI_OUT_CMD_SIGNATURE_SEED	0xFFFF
+
+/* iwm_umac_hdi_out_hdr.meta_data Byte count -- bits [11:0] */
+#define UMAC_HDI_OUT_BYTE_COUNT_POS	0
+#define UMAC_HDI_OUT_BYTE_COUNT_SEED	0xFFF
+
+/* iwm_umac_hdi_out_hdr.meta_data Credit group -- bits [15:12] */
+#define UMAC_HDI_OUT_CREDIT_GRP_POS	12
+#define UMAC_HDI_OUT_CREDIT_GRP_SEED	0xF
+
+/* iwm_umac_hdi_out_hdr.meta_data RA/TID -- bits [23:16] */
+#define UMAC_HDI_OUT_RATID_POS		16
+#define UMAC_HDI_OUT_RATID_SEED		0xFF
+
+/* iwm_umac_hdi_out_hdr.meta_data LMAC offset -- bits [31:24] */
+#define UMAC_HDI_OUT_LMAC_OFFSET_POS	24
+#define UMAC_HDI_OUT_LMAC_OFFSET_SEED	0xFF
+
+/* Signature */
+#define UMAC_HDI_OUT_SIGNATURE		0xCBBC
+
+/* buffer alignment */
+#define UMAC_HDI_BUF_ALIGN_MSK		0xF
+
+/*  iwm_umac_hdi_in_hdr.cmd OP code -- bits [3:0] */
+#define UMAC_HDI_IN_CMD_OPCODE_POS                0
+#define UMAC_HDI_IN_CMD_OPCODE_SEED               0xF
+
+/*  iwm_umac_hdi_in_hdr.cmd Non-WiFi API response -- bits [6:4] */
+#define UMAC_HDI_IN_CMD_NON_WIFI_RESP_POS         4
+#define UMAC_HDI_IN_CMD_NON_WIFI_RESP_SEED        0x7
+
+/* iwm_umac_hdi_in_hdr.cmd WiFi API source -- bits [5:4] */
+#define UMAC_HDI_IN_CMD_SOURCE_POS                4
+#define UMAC_HDI_IN_CMD_SOURCE_SEED               0x3
+
+/* iwm_umac_hdi_in_hdr.cmd WiFi API EOT -- bits [6:6] */
+#define UMAC_HDI_IN_CMD_EOT_POS                   6
+#define UMAC_HDI_IN_CMD_EOT_SEED                  0x1
+
+/* iwm_umac_hdi_in_hdr.cmd timestamp present -- bits [7:7] */
+#define UMAC_HDI_IN_CMD_TIME_STAMP_PRESENT_POS    7
+#define UMAC_HDI_IN_CMD_TIME_STAMP_PRESENT_SEED   0x1
+
+/* iwm_umac_hdi_in_hdr.cmd WiFi Non-last AMSDU -- bits [8:8] */
+#define UMAC_HDI_IN_CMD_NON_LAST_AMSDU_POS        8
+#define UMAC_HDI_IN_CMD_NON_LAST_AMSDU_SEED       0x1
+
+/* iwm_umac_hdi_in_hdr.cmd WiFi HW sequence number -- bits [31:9] */
+#define UMAC_HDI_IN_CMD_HW_SEQ_NUM_POS            9
+#define UMAC_HDI_IN_CMD_HW_SEQ_NUM_SEED           0x7FFFFF
+
+/* iwm_umac_hdi_in_hdr.cmd Non-WiFi HW sequence number -- bits [12:15] */
+#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SEQ_NUM_POS   12
+#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SEQ_NUM_SEED  0xF
+
+/* iwm_umac_hdi_in_hdr.cmd Non-WiFi HW signature -- bits [16:31] */
+#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG_POS       16
+#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG_SEED      0xFFFF
+
+/* Fixed Non-WiFi signature */
+#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG           0xCBBC
+
+/* IN NTFY op-codes */
+#define UMAC_NOTIFY_OPCODE_ALIVE		0xA1
+#define UMAC_NOTIFY_OPCODE_INIT_COMPLETE	0xA2
+#define UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS	0xA3
+#define UMAC_NOTIFY_OPCODE_ERROR		0xA4
+#define UMAC_NOTIFY_OPCODE_DEBUG		0xA5
+#define UMAC_NOTIFY_OPCODE_WIFI_IF_WRAPPER	0xB0
+#define UMAC_NOTIFY_OPCODE_STATS		0xB1
+#define UMAC_NOTIFY_OPCODE_PAGE_DEALLOC		0xB3
+#define UMAC_NOTIFY_OPCODE_RX_TICKET		0xB4
+#define UMAC_NOTIFY_OPCODE_MAX		        (UMAC_NOTIFY_OPCODE_RX_TICKET -\
+						UMAC_NOTIFY_OPCODE_ALIVE + 1)
+#define UMAC_NOTIFY_OPCODE_FIRST		(UMAC_NOTIFY_OPCODE_ALIVE)
+
+/* HDI OUT OP CODE */
+#define UMAC_HDI_OUT_OPCODE_PING		0x0
+#define UMAC_HDI_OUT_OPCODE_READ		0x1
+#define UMAC_HDI_OUT_OPCODE_WRITE		0x2
+#define UMAC_HDI_OUT_OPCODE_JUMP		0x3
+#define UMAC_HDI_OUT_OPCODE_REBOOT		0x4
+#define UMAC_HDI_OUT_OPCODE_WRITE_PERSISTENT	0x5
+#define UMAC_HDI_OUT_OPCODE_READ_PERSISTENT	0x6
+#define UMAC_HDI_OUT_OPCODE_READ_MODIFY_WRITE	0x7
+/* #define UMAC_HDI_OUT_OPCODE_RESERVED		0x8..0xA */
+#define UMAC_HDI_OUT_OPCODE_WRITE_AUX_REG	0xB
+#define UMAC_HDI_OUT_OPCODE_WIFI		0xF
+
+/* HDI IN OP CODE -- Non WiFi*/
+#define UMAC_HDI_IN_OPCODE_PING			0x0
+#define UMAC_HDI_IN_OPCODE_READ			0x1
+#define UMAC_HDI_IN_OPCODE_WRITE		0x2
+#define UMAC_HDI_IN_OPCODE_WRITE_PERSISTENT	0x5
+#define UMAC_HDI_IN_OPCODE_READ_PERSISTENT	0x6
+#define UMAC_HDI_IN_OPCODE_READ_MODIFY_WRITE	0x7
+#define UMAC_HDI_IN_OPCODE_EP_MGMT		0x8
+#define UMAC_HDI_IN_OPCODE_CREDIT_CHANGE	0x9
+#define UMAC_HDI_IN_OPCODE_CTRL_DATABASE	0xA
+#define UMAC_HDI_IN_OPCODE_WRITE_AUX_REG	0xB
+#define UMAC_HDI_IN_OPCODE_NONWIFI_MAX \
+		(UMAC_HDI_IN_OPCODE_WRITE_AUX_REG + 1)
+#define UMAC_HDI_IN_OPCODE_WIFI			0xF
+
+/* HDI IN SOURCE */
+#define UMAC_HDI_IN_SOURCE_FHRX			0x0
+#define UMAC_HDI_IN_SOURCE_UDMA			0x1
+#define UMAC_HDI_IN_SOURCE_FW			0x2
+#define UMAC_HDI_IN_SOURCE_RESERVED		0x3
+
+/* OUT CMD op-codes */
+#define UMAC_CMD_OPCODE_ECHO                    0x01
+#define UMAC_CMD_OPCODE_HALT                    0x02
+#define UMAC_CMD_OPCODE_RESET                   0x03
+#define UMAC_CMD_OPCODE_BULK_EP_INACT_TIMEOUT   0x09
+#define UMAC_CMD_OPCODE_URB_CANCEL_ACK          0x0A
+#define UMAC_CMD_OPCODE_DCACHE_FLUSH            0x0B
+#define UMAC_CMD_OPCODE_EEPROM_PROXY            0x0C
+#define UMAC_CMD_OPCODE_TX_ECHO                 0x0D
+#define UMAC_CMD_OPCODE_DBG_MON                 0x0E
+#define UMAC_CMD_OPCODE_INTERNAL_TX             0x0F
+#define UMAC_CMD_OPCODE_SET_PARAM_FIX           0x10
+#define UMAC_CMD_OPCODE_SET_PARAM_VAR           0x11
+#define UMAC_CMD_OPCODE_GET_PARAM               0x12
+#define UMAC_CMD_OPCODE_DBG_EVENT_WRAPPER       0x13
+#define UMAC_CMD_OPCODE_TARGET                  0x14
+#define UMAC_CMD_OPCODE_STATISTIC_REQUEST       0x15
+#define UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST	0x16
+#define UMAC_CMD_OPCODE_SET_PARAM_LIST		0x17
+#define UMAC_CMD_OPCODE_GET_PARAM_LIST		0x18
+#define UMAC_CMD_OPCODE_BASE_WRAPPER            0xFA
+#define UMAC_CMD_OPCODE_LMAC_WRAPPER            0xFB
+#define UMAC_CMD_OPCODE_HW_TEST_WRAPPER         0xFC
+#define UMAC_CMD_OPCODE_WIFI_IF_WRAPPER         0xFD
+#define UMAC_CMD_OPCODE_WIFI_WRAPPER            0xFE
+#define UMAC_CMD_OPCODE_WIFI_PASS_THROUGH       0xFF
+
+/* UMAC WiFi interface op-codes */
+#define UMAC_WIFI_IF_CMD_SET_PROFILE                     0x11
+#define UMAC_WIFI_IF_CMD_INVALIDATE_PROFILE              0x12
+#define UMAC_WIFI_IF_CMD_SET_EXCLUDE_LIST                0x13
+#define UMAC_WIFI_IF_CMD_SCAN_REQUEST                    0x14
+#define UMAC_WIFI_IF_CMD_SCAN_CONFIG                     0x15
+#define UMAC_WIFI_IF_CMD_ADD_WEP40_KEY                   0x16
+#define UMAC_WIFI_IF_CMD_ADD_WEP104_KEY                  0x17
+#define UMAC_WIFI_IF_CMD_ADD_TKIP_KEY                    0x18
+#define UMAC_WIFI_IF_CMD_ADD_CCMP_KEY                    0x19
+#define UMAC_WIFI_IF_CMD_REMOVE_KEY                      0x1A
+#define UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID                0x1B
+#define UMAC_WIFI_IF_CMD_SET_HOST_EXTENDED_IE            0x1C
+#define UMAC_WIFI_IF_CMD_GET_SUPPORTED_CHANNELS          0x1E
+#define UMAC_WIFI_IF_CMD_TX_PWR_TRIGGER                  0x20
+
+/* UMAC WiFi interface ports */
+#define UMAC_WIFI_IF_FLG_PORT_DEF                        0x00
+#define UMAC_WIFI_IF_FLG_PORT_PAN                        0x01
+#define UMAC_WIFI_IF_FLG_PORT_PAN_INVALID                WIFI_IF_FLG_PORT_DEF
+
+/* UMAC WiFi interface actions */
+#define UMAC_WIFI_IF_FLG_ACT_GET                         0x10
+#define UMAC_WIFI_IF_FLG_ACT_SET                         0x20
+
+/* iwm_umac_fw_cmd_hdr.meta_data byte count -- bits [11:0] */
+#define UMAC_FW_CMD_BYTE_COUNT_POS            0
+#define UMAC_FW_CMD_BYTE_COUNT_SEED           0xFFF
+
+/* iwm_umac_fw_cmd_hdr.meta_data status -- bits [15:12] */
+#define UMAC_FW_CMD_STATUS_POS                12
+#define UMAC_FW_CMD_STATUS_SEED               0xF
+
+/* iwm_umac_fw_cmd_hdr.meta_data full TX command by Driver -- bits [16:16] */
+#define UMAC_FW_CMD_TX_DRV_FULL_CMD_POS       16
+#define UMAC_FW_CMD_TX_DRV_FULL_CMD_SEED      0x1
+
+/* iwm_umac_fw_cmd_hdr.meta_data TX command by FW -- bits [17:17] */
+#define UMAC_FW_CMD_TX_FW_CMD_POS             17
+#define UMAC_FW_CMD_TX_FW_CMD_SEED            0x1
+
+/* iwm_umac_fw_cmd_hdr.meta_data TX plaintext mode -- bits [18:18] */
+#define UMAC_FW_CMD_TX_PLAINTEXT_POS          18
+#define UMAC_FW_CMD_TX_PLAINTEXT_SEED         0x1
+
+/* iwm_umac_fw_cmd_hdr.meta_data STA color -- bits [22:20] */
+#define UMAC_FW_CMD_TX_STA_COLOR_POS          20
+#define UMAC_FW_CMD_TX_STA_COLOR_SEED         0x7
+
+/* iwm_umac_fw_cmd_hdr.meta_data TX life time (TU) -- bits [31:24] */
+#define UMAC_FW_CMD_TX_LIFETIME_TU_POS        24
+#define UMAC_FW_CMD_TX_LIFETIME_TU_SEED       0xFF
+
+/* iwm_dev_cmd_hdr.flags Response required -- bits [5:5] */
+#define UMAC_DEV_CMD_FLAGS_RESP_REQ_POS		5
+#define UMAC_DEV_CMD_FLAGS_RESP_REQ_SEED	0x1
+
+/* iwm_dev_cmd_hdr.flags Aborted command -- bits [6:6] */
+#define UMAC_DEV_CMD_FLAGS_ABORT_POS		6
+#define UMAC_DEV_CMD_FLAGS_ABORT_SEED		0x1
+
+/* iwm_dev_cmd_hdr.flags Internal command -- bits [7:7] */
+#define DEV_CMD_FLAGS_FLD_INTERNAL_POS		7
+#define DEV_CMD_FLAGS_FLD_INTERNAL_SEED		0x1
+
+/* Rx */
+/* Rx actions */
+#define IWM_RX_TICKET_DROP           0x0
+#define IWM_RX_TICKET_RELEASE        0x1
+#define IWM_RX_TICKET_SNIFFER        0x2
+#define IWM_RX_TICKET_ENQUEUE        0x3
+
+/* Rx flags */
+#define IWM_RX_TICKET_PAD_SIZE_MSK        0x2
+#define IWM_RX_TICKET_SPECIAL_SNAP_MSK    0x4
+#define IWM_RX_TICKET_AMSDU_MSK           0x8
+#define IWM_RX_TICKET_DROP_REASON_POS       4
+#define IWM_RX_TICKET_DROP_REASON_MSK (0x1F << RX_TICKET_FLAGS_DROP_REASON_POS)
+
+#define IWM_RX_DROP_NO_DROP                          0x0
+#define IWM_RX_DROP_BAD_CRC                          0x1
+/* L2P no address match */
+#define IWM_RX_DROP_LMAC_ADDR_FILTER                 0x2
+/* Multicast address not in list */
+#define IWM_RX_DROP_MCAST_ADDR_FILTER                0x3
+/* Control frames are not sent to the driver */
+#define IWM_RX_DROP_CTL_FRAME                        0x4
+/* Our frame is back */
+#define IWM_RX_DROP_OUR_TX                           0x5
+/* Association class filtering */
+#define IWM_RX_DROP_CLASS_FILTER                     0x6
+/* Duplicated frame */
+#define IWM_RX_DROP_DUPLICATE_FILTER                 0x7
+/* Decryption error */
+#define IWM_RX_DROP_SEC_ERR                          0x8
+/* Unencrypted frame while encryption is on */
+#define IWM_RX_DROP_SEC_NO_ENCRYPTION                0x9
+/* Replay check failure */
+#define IWM_RX_DROP_SEC_REPLAY_ERR                   0xa
+/* uCode and FW key color mismatch, check before replay */
+#define IWM_RX_DROP_SEC_KEY_COLOR_MISMATCH           0xb
+#define IWM_RX_DROP_SEC_TKIP_COUNTER_MEASURE         0xc
+/* No fragmentations Db is found */
+#define IWM_RX_DROP_FRAG_NO_RESOURCE                 0xd
+/* Fragmention Db has seqCtl mismatch Vs. non-1st frag */
+#define IWM_RX_DROP_FRAG_ERR                         0xe
+#define IWM_RX_DROP_FRAG_LOST                        0xf
+#define IWM_RX_DROP_FRAG_COMPLETE                    0x10
+/* Should be handled by UMAC */
+#define IWM_RX_DROP_MANAGEMENT                       0x11
+/* STA not found by UMAC */
+#define IWM_RX_DROP_NO_STATION                       0x12
+/* NULL or QoS NULL */
+#define IWM_RX_DROP_NULL_DATA                        0x13
+#define IWM_RX_DROP_BA_REORDER_OLD_SEQCTL            0x14
+#define IWM_RX_DROP_BA_REORDER_DUPLICATE             0x15
+
+struct iwm_rx_ticket {
+	__le16 action;
+	__le16 id;
+	__le16 flags;
+	u8 payload_offset; /* includes: MAC header, pad, IV */
+	u8 tail_len; /* includes: MIC, ICV, CRC (w/o STATUS) */
+} __attribute__ ((packed));
+
+struct iwm_rx_mpdu_hdr {
+	__le16 len;
+	__le16 reserved;
+} __attribute__ ((packed));
+
+/* UMAC SW WIFI API */
+
+struct iwm_dev_cmd_hdr {
+	u8 cmd;
+	u8 flags;
+	__le16 seq_num;
+} __attribute__ ((packed));
+
+struct iwm_umac_fw_cmd_hdr {
+	__le32 meta_data;
+	struct iwm_dev_cmd_hdr cmd;
+} __attribute__ ((packed));
+
+struct iwm_umac_wifi_out_hdr {
+	struct iwm_udma_out_wifi_hdr hw_hdr;
+	struct iwm_umac_fw_cmd_hdr sw_hdr;
+} __attribute__ ((packed));
+
+struct iwm_umac_nonwifi_out_hdr {
+	struct iwm_udma_out_nonwifi_hdr hw_hdr;
+} __attribute__ ((packed));
+
+struct iwm_umac_wifi_in_hdr {
+	struct iwm_udma_in_hdr hw_hdr;
+	struct iwm_umac_fw_cmd_hdr sw_hdr;
+} __attribute__ ((packed));
+
+struct iwm_umac_nonwifi_in_hdr {
+	struct iwm_udma_in_hdr hw_hdr;
+	__le32 time_stamp;
+} __attribute__ ((packed));
+
+#define IWM_UMAC_PAGE_SIZE	0x200
+
+/* Notify structures */
+struct iwm_fw_version {
+	u8 minor;
+	u8 major;
+	__le16 id;
+};
+
+struct iwm_fw_build {
+	u8 type;
+	u8 subtype;
+	u8 platform;
+	u8 opt;
+};
+
+struct iwm_fw_alive_hdr {
+	struct iwm_fw_version ver;
+	struct iwm_fw_build build;
+	__le32 os_build;
+	__le32 log_hdr_addr;
+	__le32 log_buf_addr;
+	__le32 sys_timer_addr;
+};
+
+#define WAIT_NOTIF_TIMEOUT     (2 * HZ)
+#define SCAN_COMPLETE_TIMEOUT  (3 * HZ)
+
+#define UMAC_NTFY_ALIVE_STATUS_ERR		0xDEAD
+#define UMAC_NTFY_ALIVE_STATUS_OK		0xCAFE
+
+#define UMAC_NTFY_INIT_COMPLETE_STATUS_ERR	0xDEAD
+#define UMAC_NTFY_INIT_COMPLETE_STATUS_OK	0xCAFE
+
+#define UMAC_NTFY_WIFI_CORE_STATUS_LINK_EN      0x40
+#define UMAC_NTFY_WIFI_CORE_STATUS_MLME_EN      0x80
+
+#define IWM_MACS_OUT_GROUPS	6
+#define IWM_MACS_OUT_SGROUPS	1
+
+
+#define WIFI_IF_NTFY_ASSOC_START			0x80
+#define WIFI_IF_NTFY_ASSOC_COMPLETE			0x81
+#define WIFI_IF_NTFY_PROFILE_INVALIDATE_COMPLETE	0x82
+#define WIFI_IF_NTFY_CONNECTION_TERMINATED		0x83
+#define WIFI_IF_NTFY_SCAN_COMPLETE			0x84
+#define WIFI_IF_NTFY_STA_TABLE_CHANGE			0x85
+#define WIFI_IF_NTFY_EXTENDED_IE_REQUIRED		0x86
+#define WIFI_IF_NTFY_RADIO_PREEMPTION			0x87
+#define WIFI_IF_NTFY_BSS_TRK_TABLE_CHANGED		0x88
+#define WIFI_IF_NTFY_BSS_TRK_ENTRIES_REMOVED		0x89
+#define WIFI_IF_NTFY_LINK_QUALITY_STATISTICS		0x8A
+#define WIFI_IF_NTFY_MGMT_FRAME				0x8B
+
+/* DEBUG INDICATIONS */
+#define WIFI_DBG_IF_NTFY_SCAN_SUPER_JOB_START		0xE0
+#define WIFI_DBG_IF_NTFY_SCAN_SUPER_JOB_COMPLETE	0xE1
+#define WIFI_DBG_IF_NTFY_SCAN_CHANNEL_START		0xE2
+#define WIFI_DBG_IF_NTFY_SCAN_CHANNEL_RESULT		0xE3
+#define WIFI_DBG_IF_NTFY_SCAN_MINI_JOB_START		0xE4
+#define WIFI_DBG_IF_NTFY_SCAN_MINI_JOB_COMPLETE		0xE5
+#define WIFI_DBG_IF_NTFY_CNCT_ATC_START			0xE6
+#define WIFI_DBG_IF_NTFY_COEX_NOTIFICATION		0xE7
+#define WIFI_DBG_IF_NTFY_COEX_HANDLE_ENVELOP		0xE8
+#define WIFI_DBG_IF_NTFY_COEX_HANDLE_RELEASE_ENVELOP	0xE9
+
+/* Notification structures */
+struct iwm_umac_notif_wifi_if {
+	struct iwm_umac_wifi_in_hdr hdr;
+	u8 status;
+	u8 flags;
+	__le16 buf_size;
+} __attribute__ ((packed));
+
+#define UMAC_ROAM_REASON_FIRST_SELECTION	0x1
+#define UMAC_ROAM_REASON_AP_DEAUTH		0x2
+#define UMAC_ROAM_REASON_AP_CONNECT_LOST	0x3
+#define UMAC_ROAM_REASON_RSSI			0x4
+#define UMAC_ROAM_REASON_AP_ASSISTED_ROAM	0x5
+#define UMAC_ROAM_REASON_IBSS_COALESCING	0x6
+
+struct iwm_umac_notif_assoc_start {
+	struct iwm_umac_notif_wifi_if mlme_hdr;
+	__le32 roam_reason;
+	u8 bssid[ETH_ALEN];
+	u8 reserved[2];
+} __attribute__ ((packed));
+
+#define UMAC_ASSOC_COMPLETE_SUCCESS		0x0
+#define UMAC_ASSOC_COMPLETE_FAILURE		0x1
+
+struct iwm_umac_notif_assoc_complete {
+	struct iwm_umac_notif_wifi_if mlme_hdr;
+	__le32 status;
+	u8 bssid[ETH_ALEN];
+	u8 band;
+	u8 channel;
+} __attribute__ ((packed));
+
+#define UMAC_PROFILE_INVALID_ASSOC_TIMEOUT	0x0
+#define UMAC_PROFILE_INVALID_ROAM_TIMEOUT	0x1
+#define UMAC_PROFILE_INVALID_REQUEST		0x2
+#define UMAC_PROFILE_INVALID_RF_PREEMPTED	0x3
+
+struct iwm_umac_notif_profile_invalidate {
+	struct iwm_umac_notif_wifi_if mlme_hdr;
+	__le32 reason;
+} __attribute__ ((packed));
+
+#define UMAC_SCAN_RESULT_SUCCESS  0x0
+#define UMAC_SCAN_RESULT_ABORTED  0x1
+#define UMAC_SCAN_RESULT_REJECTED 0x2
+#define UMAC_SCAN_RESULT_FAILED   0x3
+
+struct iwm_umac_notif_scan_complete {
+	struct iwm_umac_notif_wifi_if mlme_hdr;
+	__le32 type;
+	__le32 result;
+	u8 seq_num;
+} __attribute__ ((packed));
+
+#define UMAC_OPCODE_ADD_MODIFY	0x0
+#define UMAC_OPCODE_REMOVE	0x1
+#define UMAC_OPCODE_CLEAR_ALL	0x2
+
+#define UMAC_STA_FLAG_QOS	0x1
+
+struct iwm_umac_notif_sta_info {
+	struct iwm_umac_notif_wifi_if mlme_hdr;
+	__le32 opcode;
+	u8 mac_addr[ETH_ALEN];
+	u8 sta_id; /* bits 0-3: station ID, bits 4-7: station color */
+	u8 flags;
+} __attribute__ ((packed));
+
+#define UMAC_BAND_2GHZ 0
+#define UMAC_BAND_5GHZ 1
+
+#define UMAC_CHANNEL_WIDTH_20MHZ 0
+#define UMAC_CHANNEL_WIDTH_40MHZ 1
+
+struct iwm_umac_notif_bss_info {
+	struct iwm_umac_notif_wifi_if mlme_hdr;
+	__le32 type;
+	__le32 timestamp;
+	__le16 table_idx;
+	__le16 frame_len;
+	u8 band;
+	u8 channel;
+	s8 rssi;
+	u8 reserved;
+	u8 frame_buf[1];
+} __attribute__ ((packed));
+
+#define IWM_BSS_REMOVE_INDEX_MSK           0x0fff
+#define IWM_BSS_REMOVE_FLAGS_MSK           0xfc00
+
+#define IWM_BSS_REMOVE_FLG_AGE             0x1000
+#define IWM_BSS_REMOVE_FLG_TIMEOUT         0x2000
+#define IWM_BSS_REMOVE_FLG_TABLE_FULL      0x4000
+
+struct iwm_umac_notif_bss_removed {
+	struct iwm_umac_notif_wifi_if mlme_hdr;
+	__le32 count;
+	__le16 entries[0];
+} __attribute__ ((packed));
+
+struct iwm_umac_notif_mgt_frame {
+	struct iwm_umac_notif_wifi_if mlme_hdr;
+	__le16 len;
+	u8 frame[1];
+} __attribute__ ((packed));
+
+struct iwm_umac_notif_alive {
+	struct iwm_umac_wifi_in_hdr hdr;
+	__le16 status;
+	__le16 reserved1;
+	struct iwm_fw_alive_hdr alive_data;
+	__le16 reserved2;
+	__le16 page_grp_count;
+	__le32 page_grp_state[IWM_MACS_OUT_GROUPS];
+} __attribute__ ((packed));
+
+struct iwm_umac_notif_init_complete {
+	__le16 status;
+	__le16 reserved;
+} __attribute__ ((packed));
+
+/* error categories */
+enum {
+	UMAC_SYS_ERR_CAT_NONE = 0,
+	UMAC_SYS_ERR_CAT_BOOT,
+	UMAC_SYS_ERR_CAT_UMAC,
+	UMAC_SYS_ERR_CAT_UAXM,
+	UMAC_SYS_ERR_CAT_LMAC,
+	UMAC_SYS_ERR_CAT_MAX
+};
+
+struct iwm_fw_error_hdr {
+	__le32 category;
+	__le32 status;
+	__le32 pc;
+	__le32 blink1;
+	__le32 blink2;
+	__le32 ilink1;
+	__le32 ilink2;
+	__le32 data1;
+	__le32 data2;
+	__le32 line_num;
+	__le32 umac_status;
+	__le32 lmac_status;
+	__le32 sdio_status;
+} __attribute__ ((packed));
+
+struct iwm_umac_notif_error {
+	struct iwm_umac_wifi_in_hdr hdr;
+	struct iwm_fw_error_hdr err;
+} __attribute__ ((packed));
+
+#define UMAC_DEALLOC_NTFY_CHANGES_CNT_POS	0
+#define UMAC_DEALLOC_NTFY_CHANGES_CNT_SEED	0xff
+#define UMAC_DEALLOC_NTFY_CHANGES_MSK_POS	8
+#define UMAC_DEALLOC_NTFY_CHANGES_MSK_SEED	0xffffff
+#define UMAC_DEALLOC_NTFY_PAGE_CNT_POS		0
+#define UMAC_DEALLOC_NTFY_PAGE_CNT_SEED		0xffffff
+#define UMAC_DEALLOC_NTFY_GROUP_NUM_POS		24
+#define UMAC_DEALLOC_NTFY_GROUP_NUM_SEED	0xf
+
+struct iwm_umac_notif_page_dealloc {
+	struct iwm_umac_wifi_in_hdr hdr;
+	__le32 changes;
+	__le32 grp_info[IWM_MACS_OUT_GROUPS];
+} __attribute__ ((packed));
+
+struct iwm_umac_notif_wifi_status {
+	struct iwm_umac_wifi_in_hdr hdr;
+	__le16 status;
+	__le16 reserved;
+} __attribute__ ((packed));
+
+struct iwm_umac_notif_rx_ticket {
+	struct iwm_umac_wifi_in_hdr hdr;
+	u8 num_tickets;
+	u8 reserved[3];
+	struct iwm_rx_ticket tickets[1];
+} __attribute__ ((packed));
+
+/* Tx/Rx rates window (number of max of last update window per second) */
+#define UMAC_NTF_RATE_SAMPLE_NR	4
+
+#define IWM_UMAC_MGMT_TID	8
+#define IWM_UMAC_TID_NR		8
+
+struct iwm_umac_notif_stats {
+	struct iwm_umac_wifi_in_hdr hdr;
+	__le32 flags;
+	__le32 timestamp;
+	__le16 tid_load[IWM_UMAC_TID_NR + 2]; /* 1 non-QoS + 1 dword align */
+	__le16 tx_rate[UMAC_NTF_RATE_SAMPLE_NR];
+	__le16 rx_rate[UMAC_NTF_RATE_SAMPLE_NR];
+	s32 rssi_dbm;
+	s32 noise_dbm;
+	__le32 supp_rates;
+	__le32 missed_beacons;
+	__le32 rx_beacons;
+	__le32 rx_dir_pkts;
+	__le32 rx_nondir_pkts;
+	__le32 rx_multicast;
+	__le32 rx_errors;
+	__le32 rx_drop_other_bssid;
+	__le32 rx_drop_decode;
+	__le32 rx_drop_reassembly;
+	__le32 rx_drop_bad_len;
+	__le32 rx_drop_overflow;
+	__le32 rx_drop_crc;
+	__le32 rx_drop_missed;
+	__le32 tx_dir_pkts;
+	__le32 tx_nondir_pkts;
+	__le32 tx_failure;
+	__le32 tx_errors;
+	__le32 tx_drop_max_retry;
+	__le32 tx_err_abort;
+	__le32 tx_err_carrier;
+	__le32 rx_bytes;
+	__le32 tx_bytes;
+	__le32 tx_power;
+	__le32 tx_max_power;
+	__le32 roam_threshold;
+	__le32 ap_assoc_nr;
+	__le32 scan_full;
+	__le32 scan_abort;
+	__le32 ap_nr;
+	__le32 roam_nr;
+	__le32 roam_missed_beacons;
+	__le32 roam_rssi;
+	__le32 roam_unassoc;
+	__le32 roam_deauth;
+	__le32 roam_ap_loadblance;
+} __attribute__ ((packed));
+
+/* WiFi interface wrapper header */
+struct iwm_umac_wifi_if {
+	u8 oid;
+	u8 flags;
+	__le16 buf_size;
+} __attribute__ ((packed));
+
+#define IWM_SEQ_NUM_HOST_MSK	0x0000
+#define IWM_SEQ_NUM_UMAC_MSK	0x4000
+#define IWM_SEQ_NUM_LMAC_MSK	0x8000
+#define IWM_SEQ_NUM_MSK		0xC000
+
+#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/wext.c b/drivers/net/wireless/iwmc3200wifi/wext.c
new file mode 100644
index 0000000..584c94d
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/wext.c
@@ -0,0 +1,723 @@
+/*
+ * Intel Wireless Multicomm 3200 WiFi driver
+ *
+ * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
+ * Samuel Ortiz <samuel.ortiz@intel.com>
+ * Zhu Yi <yi.zhu@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 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-1301, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <net/cfg80211.h>
+#include <net/iw_handler.h>
+
+#include "iwm.h"
+#include "umac.h"
+#include "commands.h"
+#include "debug.h"
+
+static struct iw_statistics *iwm_get_wireless_stats(struct net_device *dev)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(dev);
+	struct iw_statistics *wstats = &iwm->wstats;
+
+	if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
+		memset(wstats, 0, sizeof(struct iw_statistics));
+		wstats->qual.updated = IW_QUAL_ALL_INVALID;
+	}
+
+	return wstats;
+}
+
+static int iwm_wext_siwfreq(struct net_device *dev,
+			    struct iw_request_info *info,
+			    struct iw_freq *freq, char *extra)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(dev);
+
+	if (freq->flags == IW_FREQ_AUTO)
+		return 0;
+
+	/* frequency/channel can only be set in IBSS mode */
+	if (iwm->conf.mode != UMAC_MODE_IBSS)
+		return -EOPNOTSUPP;
+
+	return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra);
+}
+
+static int iwm_wext_giwfreq(struct net_device *dev,
+			    struct iw_request_info *info,
+			    struct iw_freq *freq, char *extra)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(dev);
+
+	if (iwm->conf.mode == UMAC_MODE_IBSS)
+		return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra);
+
+	freq->e = 0;
+	freq->m = iwm->channel;
+
+	return 0;
+}
+
+static int iwm_wext_siwap(struct net_device *dev, struct iw_request_info *info,
+			  struct sockaddr *ap_addr, char *extra)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(dev);
+
+	if (iwm->conf.mode == UMAC_MODE_IBSS)
+		return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra);
+
+	if (!test_bit(IWM_STATUS_READY, &iwm->status))
+		return -EIO;
+
+	if (is_zero_ether_addr(ap_addr->sa_data) ||
+	    is_broadcast_ether_addr(ap_addr->sa_data)) {
+		IWM_DBG_WEXT(iwm, DBG, "clear mandatory bssid %pM\n",
+			     iwm->umac_profile->bssid[0]);
+		memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN);
+		iwm->umac_profile->bss_num = 0;
+	} else {
+		IWM_DBG_WEXT(iwm, DBG, "add mandatory bssid %pM\n",
+			     ap_addr->sa_data);
+		memcpy(&iwm->umac_profile->bssid[0], ap_addr->sa_data,
+		       ETH_ALEN);
+		iwm->umac_profile->bss_num = 1;
+	}
+
+	if (iwm->umac_profile_active) {
+		if (!memcmp(&iwm->umac_profile->bssid[0], iwm->bssid, ETH_ALEN))
+			return 0;
+
+		iwm_invalidate_mlme_profile(iwm);
+	}
+
+	if (iwm->umac_profile->ssid.ssid_len)
+		return iwm_send_mlme_profile(iwm);
+
+	return 0;
+}
+
+static int iwm_wext_giwap(struct net_device *dev, struct iw_request_info *info,
+			  struct sockaddr *ap_addr, char *extra)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(dev);
+
+	switch (iwm->conf.mode) {
+	case UMAC_MODE_IBSS:
+		return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra);
+	case UMAC_MODE_BSS:
+		if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
+			ap_addr->sa_family = ARPHRD_ETHER;
+			memcpy(&ap_addr->sa_data, iwm->bssid, ETH_ALEN);
+		} else
+			memset(&ap_addr->sa_data, 0, ETH_ALEN);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int iwm_wext_siwessid(struct net_device *dev,
+			     struct iw_request_info *info,
+			     struct iw_point *data, char *ssid)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(dev);
+	size_t len = data->length;
+	int ret;
+
+	if (iwm->conf.mode == UMAC_MODE_IBSS)
+		return cfg80211_ibss_wext_siwessid(dev, info, data, ssid);
+
+	if (!test_bit(IWM_STATUS_READY, &iwm->status))
+		return -EIO;
+
+	if (len > 0 && ssid[len - 1] == '\0')
+		len--;
+
+	if (iwm->umac_profile_active) {
+		if (iwm->umac_profile->ssid.ssid_len == len &&
+		    !memcmp(iwm->umac_profile->ssid.ssid, ssid, len))
+			return 0;
+
+		ret = iwm_invalidate_mlme_profile(iwm);
+		if (ret < 0) {
+			IWM_ERR(iwm, "Couldn't invalidate profile\n");
+			return ret;
+		}
+	}
+
+	iwm->umac_profile->ssid.ssid_len = len;
+	memcpy(iwm->umac_profile->ssid.ssid, ssid, len);
+
+	return iwm_send_mlme_profile(iwm);
+}
+
+static int iwm_wext_giwessid(struct net_device *dev,
+			     struct iw_request_info *info,
+			     struct iw_point *data, char *ssid)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(dev);
+
+	if (iwm->conf.mode == UMAC_MODE_IBSS)
+		return cfg80211_ibss_wext_giwessid(dev, info, data, ssid);
+
+	if (!test_bit(IWM_STATUS_READY, &iwm->status))
+		return -EIO;
+
+	data->length = iwm->umac_profile->ssid.ssid_len;
+	if (data->length) {
+		memcpy(ssid, iwm->umac_profile->ssid.ssid, data->length);
+		data->flags = 1;
+	} else
+		data->flags = 0;
+
+	return 0;
+}
+
+static struct iwm_key *
+iwm_key_init(struct iwm_priv *iwm, u8 key_idx, bool in_use,
+	     struct iw_encode_ext *ext, u8 alg)
+{
+	struct iwm_key *key = &iwm->keys[key_idx];
+
+	memset(key, 0, sizeof(struct iwm_key));
+	memcpy(key->hdr.mac, ext->addr.sa_data, ETH_ALEN);
+	key->hdr.key_idx = key_idx;
+	if (is_broadcast_ether_addr(ext->addr.sa_data))
+		key->hdr.multicast = 1;
+
+	key->in_use = in_use;
+	key->flags = ext->ext_flags;
+	key->alg = alg;
+	key->key_len = ext->key_len;
+	memcpy(key->key, ext->key, ext->key_len);
+
+	return key;
+}
+
+static int iwm_wext_giwrate(struct net_device *dev,
+			    struct iw_request_info *info,
+			    struct iw_param *rate, char *extra)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(dev);
+
+	rate->value = iwm->rate * 1000000;
+
+	return 0;
+}
+
+static int iwm_wext_siwencode(struct net_device *dev,
+			      struct iw_request_info *info,
+			      struct iw_point *erq, char *key_buf)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(dev);
+	struct iwm_key *uninitialized_var(key);
+	int idx, i, uninitialized_var(alg), remove = 0, ret;
+
+	IWM_DBG_WEXT(iwm, DBG, "key len: %d\n", erq->length);
+	IWM_DBG_WEXT(iwm, DBG, "flags: 0x%x\n", erq->flags);
+
+	if (!iwm->umac_profile) {
+		IWM_ERR(iwm, "UMAC profile not allocated yet\n");
+		return -ENODEV;
+	}
+
+	if (erq->length == WLAN_KEY_LEN_WEP40) {
+		alg = UMAC_CIPHER_TYPE_WEP_40;
+		iwm->umac_profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_WEP_40;
+		iwm->umac_profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_WEP_40;
+	} else if (erq->length == WLAN_KEY_LEN_WEP104) {
+		alg = UMAC_CIPHER_TYPE_WEP_104;
+		iwm->umac_profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_WEP_104;
+		iwm->umac_profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_WEP_104;
+	}
+
+	if (erq->flags & IW_ENCODE_RESTRICTED)
+		iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
+	else
+		iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_OPEN;
+
+	idx = erq->flags & IW_ENCODE_INDEX;
+	if (idx == 0) {
+		if (iwm->default_key)
+			for (i = 0; i < IWM_NUM_KEYS; i++) {
+				if (iwm->default_key == &iwm->keys[i]) {
+					idx = i;
+					break;
+				}
+			}
+		else
+			iwm->default_key = &iwm->keys[idx];
+	} else if (idx < 1 || idx > 4) {
+		return -EINVAL;
+	} else
+		idx--;
+
+	if (erq->flags & IW_ENCODE_DISABLED)
+		remove = 1;
+	else if (erq->length == 0) {
+		if (!iwm->keys[idx].in_use)
+			return -EINVAL;
+		iwm->default_key = &iwm->keys[idx];
+	}
+
+	if (erq->length) {
+		key = &iwm->keys[idx];
+		memset(key, 0, sizeof(struct iwm_key));
+		memset(key->hdr.mac, 0xff, ETH_ALEN);
+		key->hdr.key_idx = idx;
+		key->hdr.multicast = 1;
+		key->in_use = !remove;
+		key->alg = alg;
+		key->key_len = erq->length;
+		memcpy(key->key, key_buf, erq->length);
+
+		IWM_DBG_WEXT(iwm, DBG, "Setting key %d, default: %d\n",
+			     idx, !!iwm->default_key);
+	}
+
+	if (remove) {
+		if ((erq->flags & IW_ENCODE_NOKEY) || (erq->length == 0)) {
+			int j;
+			for (j = 0; j < IWM_NUM_KEYS; j++)
+				if (iwm->keys[j].in_use) {
+					struct iwm_key *k = &iwm->keys[j];
+
+					k->in_use = 0;
+					ret = iwm_set_key(iwm, remove, 0, k);
+					if (ret < 0)
+						return ret;
+				}
+
+			iwm->umac_profile->sec.ucast_cipher =
+							UMAC_CIPHER_TYPE_NONE;
+			iwm->umac_profile->sec.mcast_cipher =
+							UMAC_CIPHER_TYPE_NONE;
+			iwm->umac_profile->sec.auth_type =
+							UMAC_AUTH_TYPE_OPEN;
+
+			return 0;
+		} else {
+			key->in_use = 0;
+			return iwm_set_key(iwm, remove, 0, key);
+		}
+	}
+
+	/*
+	 * If we havent set a profile yet, we cant set keys.
+	 * Keys will be pushed after we're associated.
+	 */
+	if (!iwm->umac_profile_active)
+		return 0;
+
+	/*
+	 * If there is a current active profile, but no
+	 * default key, it's not worth trying to associate again.
+	 */
+	if (!iwm->default_key)
+		return 0;
+
+	/*
+	 * Here we have an active profile, but a key setting changed.
+	 * We thus have to invalidate the current profile, and push the
+	 * new one. Keys will be pushed when association takes place.
+	 */
+	ret = iwm_invalidate_mlme_profile(iwm);
+	if (ret < 0) {
+		IWM_ERR(iwm, "Couldn't invalidate profile\n");
+		return ret;
+	}
+
+	return iwm_send_mlme_profile(iwm);
+}
+
+static int iwm_wext_giwencode(struct net_device *dev,
+			      struct iw_request_info *info,
+			      struct iw_point *erq, char *key)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(dev);
+	int idx, i;
+
+	idx = erq->flags & IW_ENCODE_INDEX;
+	if (idx < 1 || idx > 4) {
+		idx = -1;
+		if (!iwm->default_key) {
+			erq->length = 0;
+			erq->flags |= IW_ENCODE_NOKEY;
+			return 0;
+		} else
+			for (i = 0; i < IWM_NUM_KEYS; i++) {
+				if (iwm->default_key == &iwm->keys[i]) {
+					idx = i;
+					break;
+				}
+			}
+		if (idx < 0)
+			return -EINVAL;
+	} else
+		idx--;
+
+	erq->flags = idx + 1;
+
+	if (!iwm->keys[idx].in_use) {
+		erq->length = 0;
+		erq->flags |= IW_ENCODE_DISABLED;
+		return 0;
+	}
+
+	memcpy(key, iwm->keys[idx].key,
+	       min_t(int, erq->length, iwm->keys[idx].key_len));
+	erq->length = iwm->keys[idx].key_len;
+	erq->flags |= IW_ENCODE_ENABLED;
+
+	if (iwm->umac_profile->mode == UMAC_MODE_BSS) {
+		switch (iwm->umac_profile->sec.auth_type) {
+		case UMAC_AUTH_TYPE_OPEN:
+			erq->flags |= IW_ENCODE_OPEN;
+			break;
+		default:
+			erq->flags |= IW_ENCODE_RESTRICTED;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int iwm_set_wpa_version(struct iwm_priv *iwm, u8 wpa_version)
+{
+	if (wpa_version & IW_AUTH_WPA_VERSION_WPA2)
+		iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK;
+	else if (wpa_version & IW_AUTH_WPA_VERSION_WPA)
+		iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WPA_ON_MSK;
+	else
+		iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
+
+	return 0;
+}
+
+static int iwm_wext_siwpower(struct net_device *dev,
+			     struct iw_request_info *info,
+			     struct iw_param *wrq, char *extra)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(dev);
+	u32 power_index;
+
+	if (wrq->disabled) {
+		power_index = IWM_POWER_INDEX_MIN;
+		goto set;
+	} else
+		power_index = IWM_POWER_INDEX_DEFAULT;
+
+	switch (wrq->flags & IW_POWER_MODE) {
+	case IW_POWER_ON:
+	case IW_POWER_MODE:
+	case IW_POWER_ALL_R:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+ set:
+	if (power_index == iwm->conf.power_index)
+		return 0;
+
+	iwm->conf.power_index = power_index;
+
+	return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
+				       CFG_POWER_INDEX, iwm->conf.power_index);
+}
+
+static int iwm_wext_giwpower(struct net_device *dev,
+			     struct iw_request_info *info,
+			     union iwreq_data *wrqu, char *extra)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(dev);
+
+	wrqu->power.disabled = (iwm->conf.power_index == IWM_POWER_INDEX_MIN);
+
+	return 0;
+}
+
+static int iwm_set_key_mgt(struct iwm_priv *iwm, u8 key_mgt)
+{
+	u8 *auth_type = &iwm->umac_profile->sec.auth_type;
+
+	if (key_mgt == IW_AUTH_KEY_MGMT_802_1X)
+		*auth_type = UMAC_AUTH_TYPE_8021X;
+	else if (key_mgt == IW_AUTH_KEY_MGMT_PSK) {
+		if (iwm->umac_profile->sec.flags &
+		    (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK))
+			*auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
+		else
+			*auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
+	} else {
+		IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int iwm_set_cipher(struct iwm_priv *iwm, u8 cipher, u8 ucast)
+{
+	u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher :
+		&iwm->umac_profile->sec.mcast_cipher;
+
+	switch (cipher) {
+	case IW_AUTH_CIPHER_NONE:
+		*profile_cipher = UMAC_CIPHER_TYPE_NONE;
+		break;
+	case IW_AUTH_CIPHER_WEP40:
+		*profile_cipher = UMAC_CIPHER_TYPE_WEP_40;
+		break;
+	case IW_AUTH_CIPHER_TKIP:
+		*profile_cipher = UMAC_CIPHER_TYPE_TKIP;
+		break;
+	case IW_AUTH_CIPHER_CCMP:
+		*profile_cipher = UMAC_CIPHER_TYPE_CCMP;
+		break;
+	case IW_AUTH_CIPHER_WEP104:
+		*profile_cipher = UMAC_CIPHER_TYPE_WEP_104;
+		break;
+	default:
+		IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher);
+		return -ENOTSUPP;
+	}
+
+	return 0;
+}
+
+static int iwm_set_auth_alg(struct iwm_priv *iwm, u8 auth_alg)
+{
+	u8 *auth_type = &iwm->umac_profile->sec.auth_type;
+
+	switch (auth_alg) {
+	case IW_AUTH_ALG_OPEN_SYSTEM:
+		*auth_type = UMAC_AUTH_TYPE_OPEN;
+		break;
+	case IW_AUTH_ALG_SHARED_KEY:
+		if (iwm->umac_profile->sec.flags &
+		    (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) {
+			if (*auth_type == UMAC_AUTH_TYPE_8021X)
+				return -EINVAL;
+			*auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
+		} else {
+			*auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
+		}
+		break;
+	case IW_AUTH_ALG_LEAP:
+	default:
+		IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", auth_alg);
+		return -ENOTSUPP;
+	}
+
+	return 0;
+}
+
+static int iwm_wext_siwauth(struct net_device *dev,
+			    struct iw_request_info *info,
+			    struct iw_param *data, char *extra)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(dev);
+	int ret;
+
+	if ((data->flags) &
+	    (IW_AUTH_WPA_VERSION | IW_AUTH_KEY_MGMT |
+	     IW_AUTH_WPA_ENABLED | IW_AUTH_80211_AUTH_ALG)) {
+		/* We need to invalidate the current profile */
+		if (iwm->umac_profile_active) {
+			ret = iwm_invalidate_mlme_profile(iwm);
+			if (ret < 0) {
+				IWM_ERR(iwm, "Couldn't invalidate profile\n");
+				return ret;
+			}
+		}
+	}
+
+	switch (data->flags & IW_AUTH_INDEX) {
+	case IW_AUTH_WPA_VERSION:
+		return iwm_set_wpa_version(iwm, data->value);
+		break;
+	case IW_AUTH_CIPHER_PAIRWISE:
+		return iwm_set_cipher(iwm, data->value, 1);
+		break;
+	case IW_AUTH_CIPHER_GROUP:
+		return iwm_set_cipher(iwm, data->value, 0);
+		break;
+	case IW_AUTH_KEY_MGMT:
+		return iwm_set_key_mgt(iwm, data->value);
+		break;
+	case IW_AUTH_80211_AUTH_ALG:
+		return iwm_set_auth_alg(iwm, data->value);
+		break;
+	default:
+		return -ENOTSUPP;
+	}
+
+	return 0;
+}
+
+static int iwm_wext_giwauth(struct net_device *dev,
+			    struct iw_request_info *info,
+			    struct iw_param *data, char *extra)
+{
+	return 0;
+}
+
+static int iwm_wext_siwencodeext(struct net_device *dev,
+				 struct iw_request_info *info,
+				 struct iw_point *erq, char *extra)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(dev);
+	struct iwm_key *key;
+	struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
+	int uninitialized_var(alg), idx, i, remove = 0;
+
+	IWM_DBG_WEXT(iwm, DBG, "alg: 0x%x\n", ext->alg);
+	IWM_DBG_WEXT(iwm, DBG, "key len: %d\n", ext->key_len);
+	IWM_DBG_WEXT(iwm, DBG, "ext_flags: 0x%x\n", ext->ext_flags);
+	IWM_DBG_WEXT(iwm, DBG, "flags: 0x%x\n", erq->flags);
+	IWM_DBG_WEXT(iwm, DBG, "length: 0x%x\n", erq->length);
+
+	switch (ext->alg) {
+	case IW_ENCODE_ALG_NONE:
+		remove = 1;
+		break;
+	case IW_ENCODE_ALG_WEP:
+		if (ext->key_len == WLAN_KEY_LEN_WEP40)
+			alg = UMAC_CIPHER_TYPE_WEP_40;
+		else if (ext->key_len == WLAN_KEY_LEN_WEP104)
+			alg = UMAC_CIPHER_TYPE_WEP_104;
+		else {
+			IWM_ERR(iwm, "Invalid key length: %d\n", ext->key_len);
+			return -EINVAL;
+		}
+
+		break;
+	case IW_ENCODE_ALG_TKIP:
+		alg = UMAC_CIPHER_TYPE_TKIP;
+		break;
+	case IW_ENCODE_ALG_CCMP:
+		alg = UMAC_CIPHER_TYPE_CCMP;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	idx = erq->flags & IW_ENCODE_INDEX;
+
+	if (idx == 0) {
+		if (iwm->default_key)
+			for (i = 0; i < IWM_NUM_KEYS; i++) {
+				if (iwm->default_key == &iwm->keys[i]) {
+					idx = i;
+					break;
+				}
+			}
+	} else if (idx < 1 || idx > 4) {
+		return -EINVAL;
+	} else
+		idx--;
+
+	if (erq->flags & IW_ENCODE_DISABLED)
+		remove = 1;
+	else if ((erq->length == 0) ||
+		 (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) {
+		iwm->default_key = &iwm->keys[idx];
+		if (iwm->umac_profile_active && ext->alg == IW_ENCODE_ALG_WEP)
+			return iwm_set_tx_key(iwm, idx);
+	}
+
+	key = iwm_key_init(iwm, idx, !remove, ext, alg);
+
+	return iwm_set_key(iwm, remove, !iwm->default_key, key);
+}
+
+static const iw_handler iwm_handlers[] =
+{
+	(iw_handler) NULL,				/* SIOCSIWCOMMIT */
+	(iw_handler) cfg80211_wext_giwname,		/* SIOCGIWNAME */
+	(iw_handler) NULL,				/* SIOCSIWNWID */
+	(iw_handler) NULL,				/* SIOCGIWNWID */
+	(iw_handler) iwm_wext_siwfreq,			/* SIOCSIWFREQ */
+	(iw_handler) iwm_wext_giwfreq,			/* SIOCGIWFREQ */
+	(iw_handler) cfg80211_wext_siwmode,		/* SIOCSIWMODE */
+	(iw_handler) cfg80211_wext_giwmode,		/* SIOCGIWMODE */
+	(iw_handler) NULL,				/* SIOCSIWSENS */
+	(iw_handler) NULL,				/* SIOCGIWSENS */
+	(iw_handler) NULL /* not used */,		/* SIOCSIWRANGE */
+	(iw_handler) cfg80211_wext_giwrange,		/* SIOCGIWRANGE */
+	(iw_handler) NULL /* not used */,		/* SIOCSIWPRIV */
+	(iw_handler) NULL /* kernel code */,		/* SIOCGIWPRIV */
+	(iw_handler) NULL /* not used */,		/* SIOCSIWSTATS */
+	(iw_handler) NULL /* kernel code */,		/* SIOCGIWSTATS */
+	(iw_handler) NULL,				/* SIOCSIWSPY */
+	(iw_handler) NULL,				/* SIOCGIWSPY */
+	(iw_handler) NULL,				/* SIOCSIWTHRSPY */
+	(iw_handler) NULL,				/* SIOCGIWTHRSPY */
+	(iw_handler) iwm_wext_siwap,	                /* SIOCSIWAP */
+	(iw_handler) iwm_wext_giwap,			/* SIOCGIWAP */
+	(iw_handler) NULL,			        /* SIOCSIWMLME */
+	(iw_handler) NULL,				/* SIOCGIWAPLIST */
+	(iw_handler) cfg80211_wext_siwscan,		/* SIOCSIWSCAN */
+	(iw_handler) cfg80211_wext_giwscan,		/* SIOCGIWSCAN */
+	(iw_handler) iwm_wext_siwessid,			/* SIOCSIWESSID */
+	(iw_handler) iwm_wext_giwessid,			/* SIOCGIWESSID */
+	(iw_handler) NULL,				/* SIOCSIWNICKN */
+	(iw_handler) NULL,				/* SIOCGIWNICKN */
+	(iw_handler) NULL,				/* -- hole -- */
+	(iw_handler) NULL,				/* -- hole -- */
+	(iw_handler) NULL,				/* SIOCSIWRATE */
+	(iw_handler) iwm_wext_giwrate,			/* SIOCGIWRATE */
+	(iw_handler) cfg80211_wext_siwrts,		/* SIOCSIWRTS */
+	(iw_handler) cfg80211_wext_giwrts,		/* SIOCGIWRTS */
+	(iw_handler) cfg80211_wext_siwfrag,	        /* SIOCSIWFRAG */
+	(iw_handler) cfg80211_wext_giwfrag,		/* SIOCGIWFRAG */
+	(iw_handler) NULL,				/* SIOCSIWTXPOW */
+	(iw_handler) NULL,				/* SIOCGIWTXPOW */
+	(iw_handler) NULL,				/* SIOCSIWRETRY */
+	(iw_handler) NULL,				/* SIOCGIWRETRY */
+	(iw_handler) iwm_wext_siwencode,		/* SIOCSIWENCODE */
+	(iw_handler) iwm_wext_giwencode,		/* SIOCGIWENCODE */
+	(iw_handler) iwm_wext_siwpower,			/* SIOCSIWPOWER */
+	(iw_handler) iwm_wext_giwpower,			/* SIOCGIWPOWER */
+	(iw_handler) NULL,				/* -- hole -- */
+	(iw_handler) NULL,				/* -- hole -- */
+	(iw_handler) NULL,                              /* SIOCSIWGENIE */
+	(iw_handler) NULL,				/* SIOCGIWGENIE */
+	(iw_handler) iwm_wext_siwauth,			/* SIOCSIWAUTH */
+	(iw_handler) iwm_wext_giwauth,			/* SIOCGIWAUTH */
+	(iw_handler) iwm_wext_siwencodeext,	        /* SIOCSIWENCODEEXT */
+	(iw_handler) NULL,				/* SIOCGIWENCODEEXT */
+	(iw_handler) NULL,				/* SIOCSIWPMKSA */
+	(iw_handler) NULL,				/* -- hole -- */
+};
+
+const struct iw_handler_def iwm_iw_handler_def = {
+	.num_standard	= ARRAY_SIZE(iwm_handlers),
+	.standard	= (iw_handler *) iwm_handlers,
+	.get_wireless_stats = iwm_get_wireless_stats,
+};
+
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
index cbaafa6..a4455ec 100644
--- a/drivers/net/wireless/libertas/dev.h
+++ b/drivers/net/wireless/libertas/dev.h
@@ -321,6 +321,8 @@
 
 	u32 monitormode;
 	u8 fw_ready;
+	u8 fn_init_required;
+	u8 fn_shutdown_required;
 };
 
 extern struct cmd_confirm_sleep confirm_sleep;
diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h
index 8ff8ac9..fe8f0cb 100644
--- a/drivers/net/wireless/libertas/host.h
+++ b/drivers/net/wireless/libertas/host.h
@@ -86,6 +86,8 @@
 #define CMD_MESH_CONFIG_OLD			0x00a3
 #define CMD_MESH_CONFIG				0x00ac
 #define	CMD_SET_BOOT2_VER			0x00a5
+#define	CMD_FUNC_INIT				0x00a9
+#define	CMD_FUNC_SHUTDOWN			0x00aa
 #define CMD_802_11_BEACON_CTRL			0x00b0
 
 /* For the IEEE Power Save */
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
index 55864c1..a7e3fc1 100644
--- a/drivers/net/wireless/libertas/if_sdio.c
+++ b/drivers/net/wireless/libertas/if_sdio.c
@@ -61,26 +61,30 @@
 	int model;
 	const char *helper;
 	const char *firmware;
+	struct if_sdio_card *card;
 };
 
 static struct if_sdio_model if_sdio_models[] = {
 	{
 		/* 8385 */
-		.model = 0x04,
+		.model = IF_SDIO_MODEL_8385,
 		.helper = "sd8385_helper.bin",
 		.firmware = "sd8385.bin",
+		.card = NULL,
 	},
 	{
 		/* 8686 */
-		.model = 0x0B,
+		.model = IF_SDIO_MODEL_8686,
 		.helper = "sd8686_helper.bin",
 		.firmware = "sd8686.bin",
+		.card = NULL,
 	},
 	{
 		/* 8688 */
-		.model = 0x10,
+		.model = IF_SDIO_MODEL_8688,
 		.helper = "sd8688_helper.bin",
 		.firmware = "sd8688.bin",
+		.card = NULL,
 	},
 };
 
@@ -96,6 +100,7 @@
 
 	int			model;
 	unsigned long		ioport;
+	unsigned int		scratch_reg;
 
 	const char		*helper;
 	const char		*firmware;
@@ -107,25 +112,29 @@
 
 	struct workqueue_struct	*workqueue;
 	struct work_struct	packet_worker;
+
+	u8			rx_unit;
 };
 
 /********************************************************************/
 /* I/O                                                              */
 /********************************************************************/
 
+/*
+ *  For SD8385/SD8686, this function reads firmware status after
+ *  the image is downloaded, or reads RX packet length when
+ *  interrupt (with IF_SDIO_H_INT_UPLD bit set) is received.
+ *  For SD8688, this function reads firmware status only.
+ */
 static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err)
 {
-	int ret, reg;
+	int ret;
 	u16 scratch;
 
-	if (card->model == 0x04)
-		reg = IF_SDIO_SCRATCH_OLD;
-	else
-		reg = IF_SDIO_SCRATCH;
-
-	scratch = sdio_readb(card->func, reg, &ret);
+	scratch = sdio_readb(card->func, card->scratch_reg, &ret);
 	if (!ret)
-		scratch |= sdio_readb(card->func, reg + 1, &ret) << 8;
+		scratch |= sdio_readb(card->func, card->scratch_reg + 1,
+					&ret) << 8;
 
 	if (err)
 		*err = ret;
@@ -136,6 +145,46 @@
 	return scratch;
 }
 
+static u8 if_sdio_read_rx_unit(struct if_sdio_card *card)
+{
+	int ret;
+	u8 rx_unit;
+
+	rx_unit = sdio_readb(card->func, IF_SDIO_RX_UNIT, &ret);
+
+	if (ret)
+		rx_unit = 0;
+
+	return rx_unit;
+}
+
+static u16 if_sdio_read_rx_len(struct if_sdio_card *card, int *err)
+{
+	int ret;
+	u16 rx_len;
+
+	switch (card->model) {
+	case IF_SDIO_MODEL_8385:
+	case IF_SDIO_MODEL_8686:
+		rx_len = if_sdio_read_scratch(card, &ret);
+		break;
+	case IF_SDIO_MODEL_8688:
+	default: /* for newer chipsets */
+		rx_len = sdio_readb(card->func, IF_SDIO_RX_LEN, &ret);
+		if (!ret)
+			rx_len <<= card->rx_unit;
+		else
+			rx_len = 0xffff;	/* invalid length */
+
+		break;
+	}
+
+	if (err)
+		*err = ret;
+
+	return rx_len;
+}
+
 static int if_sdio_handle_cmd(struct if_sdio_card *card,
 		u8 *buffer, unsigned size)
 {
@@ -216,7 +265,7 @@
 
 	lbs_deb_enter(LBS_DEB_SDIO);
 
-	if (card->model == 0x04) {
+	if (card->model == IF_SDIO_MODEL_8385) {
 		event = sdio_readb(card->func, IF_SDIO_EVENT, &ret);
 		if (ret)
 			goto out;
@@ -254,7 +303,7 @@
 
 	lbs_deb_enter(LBS_DEB_SDIO);
 
-	size = if_sdio_read_scratch(card, &ret);
+	size = if_sdio_read_rx_len(card, &ret);
 	if (ret)
 		goto out;
 
@@ -497,7 +546,6 @@
 	ret = 0;
 
 release:
-	sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE);
 	sdio_release_host(card->func);
 	kfree(chunk_buffer);
 release_fw:
@@ -633,7 +681,6 @@
 	ret = 0;
 
 release:
-	sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE);
 	sdio_release_host(card->func);
 	kfree(chunk_buffer);
 release_fw:
@@ -662,6 +709,8 @@
 	if (ret)
 		goto out;
 
+	lbs_deb_sdio("firmware status = %#x\n", scratch);
+
 	if (scratch == IF_SDIO_FIRMWARE_OK) {
 		lbs_deb_sdio("firmware already loaded\n");
 		goto success;
@@ -676,6 +725,9 @@
 		goto out;
 
 success:
+	sdio_claim_host(card->func);
+	sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE);
+	sdio_release_host(card->func);
 	ret = 0;
 
 out:
@@ -829,10 +881,10 @@
 		if (sscanf(func->card->info[i],
 				"ID: %x", &model) == 1)
 			break;
-               if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) {
-                       model = 4;
-                       break;
-               }
+		if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) {
+			model = IF_SDIO_MODEL_8385;
+			break;
+		}
 	}
 
 	if (i == func->card->num_info) {
@@ -846,6 +898,20 @@
 
 	card->func = func;
 	card->model = model;
+
+	switch (card->model) {
+	case IF_SDIO_MODEL_8385:
+		card->scratch_reg = IF_SDIO_SCRATCH_OLD;
+		break;
+	case IF_SDIO_MODEL_8686:
+		card->scratch_reg = IF_SDIO_SCRATCH;
+		break;
+	case IF_SDIO_MODEL_8688:
+	default: /* for newer chipsets */
+		card->scratch_reg = IF_SDIO_FW_STATUS;
+		break;
+	}
+
 	spin_lock_init(&card->lock);
 	card->workqueue = create_workqueue("libertas_sdio");
 	INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);
@@ -861,6 +927,8 @@
 		goto free;
 	}
 
+	if_sdio_models[i].card = card;
+
 	card->helper = if_sdio_models[i].helper;
 	card->firmware = if_sdio_models[i].firmware;
 
@@ -923,15 +991,32 @@
 
 	priv->fw_ready = 1;
 
+	sdio_claim_host(func);
+
+	/*
+	 * Get rx_unit if the chip is SD8688 or newer.
+	 * SD8385 & SD8686 do not have rx_unit.
+	 */
+	if ((card->model != IF_SDIO_MODEL_8385)
+			&& (card->model != IF_SDIO_MODEL_8686))
+		card->rx_unit = if_sdio_read_rx_unit(card);
+	else
+		card->rx_unit = 0;
+
 	/*
 	 * Enable interrupts now that everything is set up
 	 */
-	sdio_claim_host(func);
 	sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret);
 	sdio_release_host(func);
 	if (ret)
 		goto reclaim;
 
+	/*
+	 * FUNC_INIT is required for SD8688 WLAN/BT multiple functions
+	 */
+	priv->fn_init_required =
+		(card->model == IF_SDIO_MODEL_8688) ? 1 : 0;
+
 	ret = lbs_start_card(priv);
 	if (ret)
 		goto err_activate_card;
@@ -972,23 +1057,30 @@
 {
 	struct if_sdio_card *card;
 	struct if_sdio_packet *packet;
+	int ret;
 
 	lbs_deb_enter(LBS_DEB_SDIO);
 
 	card = sdio_get_drvdata(func);
 
+	lbs_stop_card(card->priv);
+
 	card->priv->surpriseremoved = 1;
 
 	lbs_deb_sdio("call remove card\n");
-	lbs_stop_card(card->priv);
 	lbs_remove_card(card->priv);
 
 	flush_workqueue(card->workqueue);
 	destroy_workqueue(card->workqueue);
 
 	sdio_claim_host(func);
+
+	/* Disable interrupts */
+	sdio_writeb(func, 0x00, IF_SDIO_H_INT_MASK, &ret);
+
 	sdio_release_irq(func);
 	sdio_disable_func(func);
+
 	sdio_release_host(func);
 
 	while (card->packets) {
@@ -1031,8 +1123,23 @@
 
 static void __exit if_sdio_exit_module(void)
 {
+	int i;
+	struct if_sdio_card *card;
+
 	lbs_deb_enter(LBS_DEB_SDIO);
 
+	for (i = 0; i < ARRAY_SIZE(if_sdio_models); i++) {
+		card = if_sdio_models[i].card;
+
+		/*
+		 * FUNC_SHUTDOWN is required for SD8688 WLAN/BT
+		 * multiple functions
+		 */
+		if (card && card->priv)
+			card->priv->fn_shutdown_required =
+				(card->model == IF_SDIO_MODEL_8688) ? 1 : 0;
+	}
+
 	sdio_unregister_driver(&if_sdio_driver);
 
 	lbs_deb_leave(LBS_DEB_SDIO);
diff --git a/drivers/net/wireless/libertas/if_sdio.h b/drivers/net/wireless/libertas/if_sdio.h
index 37ada2c..60c9b2f 100644
--- a/drivers/net/wireless/libertas/if_sdio.h
+++ b/drivers/net/wireless/libertas/if_sdio.h
@@ -12,6 +12,10 @@
 #ifndef _LBS_IF_SDIO_H
 #define _LBS_IF_SDIO_H
 
+#define IF_SDIO_MODEL_8385	0x04
+#define IF_SDIO_MODEL_8686	0x0b
+#define IF_SDIO_MODEL_8688	0x10
+
 #define IF_SDIO_IOPORT		0x00
 
 #define IF_SDIO_H_INT_MASK	0x04
@@ -38,8 +42,12 @@
 
 #define IF_SDIO_SCRATCH		0x34
 #define IF_SDIO_SCRATCH_OLD	0x80fe
+#define IF_SDIO_FW_STATUS	0x40
 #define   IF_SDIO_FIRMWARE_OK	0xfedc
 
+#define IF_SDIO_RX_LEN		0x42
+#define IF_SDIO_RX_UNIT		0x43
+
 #define IF_SDIO_EVENT           0x80fc
 
 #define IF_SDIO_BLOCK_SIZE	256
diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c
index dccd01f..5fa55fe 100644
--- a/drivers/net/wireless/libertas/if_spi.c
+++ b/drivers/net/wireless/libertas/if_spi.c
@@ -814,6 +814,13 @@
 	if (err)
 		goto out;
 
+	/* re-enable the card event interrupt */
+	spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG,
+			~IF_SPI_HICU_CARD_EVENT);
+
+	/* generate a card interrupt */
+	spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, IF_SPI_CIC_HOST_EVENT);
+
 	spin_lock_irqsave(&priv->driver_lock, flags);
 	lbs_queue_event(priv, cause & 0xff);
 	spin_unlock_irqrestore(&priv->driver_lock, flags);
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index 89575e4..a58a123 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -1002,9 +1002,17 @@
 {
 	int ret = -1;
 	s16 curlevel = 0, minlevel = 0, maxlevel = 0;
+	struct cmd_header cmd;
 
 	lbs_deb_enter(LBS_DEB_FW);
 
+	if (priv->fn_init_required) {
+		memset(&cmd, 0, sizeof(cmd));
+		if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd),
+				lbs_cmd_copyback, (unsigned long) &cmd))
+			lbs_pr_alert("CMD_FUNC_INIT command failed\n");
+	}
+
 	/* Read MAC address from firmware */
 	memset(priv->current_addr, 0xff, ETH_ALEN);
 	ret = lbs_update_hw_spec(priv);
@@ -1192,6 +1200,9 @@
 	priv->mesh_open = 0;
 	priv->infra_open = 0;
 
+	priv->fn_init_required = 0;
+	priv->fn_shutdown_required = 0;
+
 	/* Setup the OS Interface to our functions */
  	dev->netdev_ops = &lbs_netdev_ops;
 	dev->watchdog_timeo = 5 * HZ;
@@ -1373,11 +1384,20 @@
 	struct net_device *dev;
 	struct cmd_ctrl_node *cmdnode;
 	unsigned long flags;
+	struct cmd_header cmd;
 
 	lbs_deb_enter(LBS_DEB_MAIN);
 
 	if (!priv)
 		goto out;
+
+	if (priv->fn_shutdown_required) {
+		memset(&cmd, 0, sizeof(cmd));
+		if (__lbs_cmd(priv, CMD_FUNC_SHUTDOWN, &cmd, sizeof(cmd),
+				lbs_cmd_copyback, (unsigned long) &cmd))
+			lbs_pr_alert("CMD_FUNC_SHUTDOWN command failed\n");
+	}
+
 	dev = priv->dev;
 
 	netif_stop_queue(dev);
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 61a4ad7..574b8bb 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -291,6 +291,14 @@
 	bool ps_poll_pending;
 	struct dentry *debugfs;
 	struct dentry *debugfs_ps;
+
+	/*
+	 * Only radios in the same group can communicate together (the
+	 * channel has to match too). Each bit represents a group. A
+	 * radio can be in more then one group.
+	 */
+	u64 group;
+	struct dentry *debugfs_group;
 };
 
 
@@ -412,7 +420,8 @@
 
 		if (!data2->started || !data2->radio_enabled ||
 		    !hwsim_ps_rx_ok(data2, skb) ||
-		    data->channel->center_freq != data2->channel->center_freq)
+		    data->channel->center_freq != data2->channel->center_freq ||
+		    !(data->group & data2->group))
 			continue;
 
 		nskb = skb_copy(skb, GFP_ATOMIC);
@@ -720,6 +729,7 @@
 	spin_unlock_bh(&hwsim_radio_lock);
 
 	list_for_each_entry(data, &tmplist, list) {
+		debugfs_remove(data->debugfs_group);
 		debugfs_remove(data->debugfs_ps);
 		debugfs_remove(data->debugfs);
 		ieee80211_unregister_hw(data->hw);
@@ -872,6 +882,24 @@
 			"%llu\n");
 
 
+static int hwsim_fops_group_read(void *dat, u64 *val)
+{
+	struct mac80211_hwsim_data *data = dat;
+	*val = data->group;
+	return 0;
+}
+
+static int hwsim_fops_group_write(void *dat, u64 val)
+{
+	struct mac80211_hwsim_data *data = dat;
+	data->group = val;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group,
+			hwsim_fops_group_read, hwsim_fops_group_write,
+			"%llx\n");
+
 static int __init init_mac80211_hwsim(void)
 {
 	int i, err = 0;
@@ -976,6 +1004,8 @@
 
 			hw->wiphy->bands[band] = sband;
 		}
+		/* By default all radios are belonging to the first group */
+		data->group = 1;
 
 		/* Work to be done prior to ieee80211_register_hw() */
 		switch (regtest) {
@@ -1100,6 +1130,9 @@
 		data->debugfs_ps = debugfs_create_file("ps", 0666,
 						       data->debugfs, data,
 						       &hwsim_fops_ps);
+		data->debugfs_group = debugfs_create_file("group", 0666,
+							data->debugfs, data,
+							&hwsim_fops_group);
 
 		setup_timer(&data->beacon_timer, mac80211_hwsim_beacon,
 			    (unsigned long) hw);
diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c
index 59a5e77..83116ba 100644
--- a/drivers/net/wireless/p54/p54spi.c
+++ b/drivers/net/wireless/p54/p54spi.c
@@ -96,7 +96,7 @@
 	spi_message_add_tail(&t[0], &m);
 
 	t[1].tx_buf = buf;
-	t[1].len = len;
+	t[1].len = len & ~1;
 	spi_message_add_tail(&t[1], &m);
 
 	if (len % 2) {
@@ -172,8 +172,6 @@
 		__le32 buffer = p54spi_read32(priv, reg);
 		if ((buffer & bits) == bits)
 			return 1;
-
-		msleep(0);
 	}
 	return 0;
 }
@@ -181,9 +179,6 @@
 static int p54spi_spi_write_dma(struct p54s_priv *priv, __le32 base,
 				const void *buf, size_t len)
 {
-	p54spi_write16(priv, SPI_ADRS_DMA_WRITE_CTRL,
-		       cpu_to_le16(SPI_DMA_WRITE_CTRL_ENABLE));
-
 	if (!p54spi_wait_bit(priv, SPI_ADRS_DMA_WRITE_CTRL,
 			     cpu_to_le32(HOST_ALLOWED))) {
 		dev_err(&priv->spi->dev, "spi_write_dma not allowed "
@@ -191,6 +186,9 @@
 		return -EAGAIN;
 	}
 
+	p54spi_write16(priv, SPI_ADRS_DMA_WRITE_CTRL,
+		       cpu_to_le16(SPI_DMA_WRITE_CTRL_ENABLE));
+
 	p54spi_write16(priv, SPI_ADRS_DMA_WRITE_LEN, cpu_to_le16(len));
 	p54spi_write32(priv, SPI_ADRS_DMA_WRITE_BASE, base);
 	p54spi_spi_write(priv, SPI_ADRS_DMA_DATA, buf, len);
@@ -327,7 +325,7 @@
 	p54spi_write32(priv, SPI_ADRS_HOST_INT_ACK, cpu_to_le32(val));
 }
 
-static void p54spi_wakeup(struct p54s_priv *priv)
+static int p54spi_wakeup(struct p54s_priv *priv)
 {
 	/* wake the chip */
 	p54spi_write32(priv, SPI_ADRS_ARM_INTERRUPTS,
@@ -337,13 +335,11 @@
 	if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS,
 			     cpu_to_le32(SPI_HOST_INT_READY))) {
 		dev_err(&priv->spi->dev, "INT_READY timeout\n");
-		goto out;
+		return -EBUSY;
 	}
 
 	p54spi_int_ack(priv, SPI_HOST_INT_READY);
-
-out:
-	return;
+	return 0;
 }
 
 static inline void p54spi_sleep(struct p54s_priv *priv)
@@ -375,31 +371,44 @@
 {
 	struct sk_buff *skb;
 	u16 len;
+	u16 rx_head[2];
+#define READAHEAD_SZ (sizeof(rx_head)-sizeof(u16))
 
-	p54spi_wakeup(priv);
+	if (p54spi_wakeup(priv) < 0)
+		return -EBUSY;
 
-	/* dummy read to flush SPI DMA controller bug */
-	p54spi_read16(priv, SPI_ADRS_GEN_PURP_1);
-
-	len = p54spi_read16(priv, SPI_ADRS_DMA_DATA);
+	/* Read data size and first data word in one SPI transaction
+	 * This is workaround for firmware/DMA bug,
+	 * when first data word gets lost under high load.
+	 */
+	p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, rx_head, sizeof(rx_head));
+	len = rx_head[0];
 
 	if (len == 0) {
-		dev_err(&priv->spi->dev, "rx request of zero bytes");
+		p54spi_sleep(priv);
+		dev_err(&priv->spi->dev, "rx request of zero bytes\n");
 		return 0;
 	}
 
-
 	/* Firmware may insert up to 4 padding bytes after the lmac header,
 	 * but it does not amend the size of SPI data transfer.
 	 * Such packets has correct data size in header, thus referencing
 	 * past the end of allocated skb. Reserve extra 4 bytes for this case */
 	skb = dev_alloc_skb(len + 4);
 	if (!skb) {
+		p54spi_sleep(priv);
 		dev_err(&priv->spi->dev, "could not alloc skb");
-		return 0;
+		return -ENOMEM;
 	}
 
-	p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, skb_put(skb, len), len);
+	if (len <= READAHEAD_SZ) {
+		memcpy(skb_put(skb, len), rx_head + 1, len);
+	} else {
+		memcpy(skb_put(skb, READAHEAD_SZ), rx_head + 1, READAHEAD_SZ);
+		p54spi_spi_read(priv, SPI_ADRS_DMA_DATA,
+				skb_put(skb, len - READAHEAD_SZ),
+				len - READAHEAD_SZ);
+	}
 	p54spi_sleep(priv);
 	/* Put additional bytes to compensate for the possible
 	 * alignment-caused truncation */
@@ -427,7 +436,8 @@
 	struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
 	int ret = 0;
 
-	p54spi_wakeup(priv);
+	if (p54spi_wakeup(priv) < 0)
+		return -EBUSY;
 
 	ret = p54spi_spi_write_dma(priv, hdr->req_id, skb->data, skb->len);
 	if (ret < 0)
@@ -436,16 +446,16 @@
 	if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS,
 			     cpu_to_le32(SPI_HOST_INT_WR_READY))) {
 		dev_err(&priv->spi->dev, "WR_READY timeout\n");
-		ret = -1;
+		ret = -EAGAIN;
 		goto out;
 	}
 
 	p54spi_int_ack(priv, SPI_HOST_INT_WR_READY);
-	p54spi_sleep(priv);
 
 	if (FREE_AFTER_TX(skb))
 		p54_free_skb(priv->hw, skb);
 out:
+	p54spi_sleep(priv);
 	return ret;
 }
 
@@ -515,8 +525,7 @@
 
 	mutex_lock(&priv->mutex);
 
-	if (priv->fw_state == FW_STATE_OFF &&
-	    priv->fw_state == FW_STATE_RESET)
+	if (priv->fw_state == FW_STATE_OFF)
 		goto out;
 
 	ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS);
@@ -543,11 +552,6 @@
 	}
 
 	ret = p54spi_wq_tx(priv);
-	if (ret < 0)
-		goto out;
-
-	ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS);
-
 out:
 	mutex_unlock(&priv->mutex);
 }
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 52fc647..c254fdf 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -2,7 +2,7 @@
  * Driver for RNDIS based wireless USB devices.
  *
  * Copyright (C) 2007 by Bjorge Dijkstra <bjd@jooz.net>
- * Copyright (C) 2008 by Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
+ * Copyright (C) 2008-2009 by Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -196,6 +196,18 @@
 	ndis_80211_priv_8021x_wep
 };
 
+enum ndis_80211_addkey_bits {
+	ndis_80211_addkey_8021x_auth = cpu_to_le32(1 << 28),
+	ndis_80211_addkey_set_init_recv_seq = cpu_to_le32(1 << 29),
+	ndis_80211_addkey_pairwise_key = cpu_to_le32(1 << 30),
+	ndis_80211_addkey_transmit_key = cpu_to_le32(1 << 31),
+};
+
+enum ndis_80211_addwep_bits {
+	ndis_80211_addwep_perclient_key = cpu_to_le32(1 << 30),
+	ndis_80211_addwep_transmit_key = cpu_to_le32(1 << 31),
+};
+
 struct ndis_80211_ssid {
 	__le32 length;
 	u8 essid[NDIS_802_11_LENGTH_SSID];
@@ -309,7 +321,6 @@
 #define CAP_MODE_80211B		2
 #define CAP_MODE_80211G		4
 #define CAP_MODE_MASK		7
-#define CAP_SUPPORT_TXPOWER	8
 
 #define WORK_LINK_UP		(1<<0)
 #define WORK_LINK_DOWN		(1<<1)
@@ -394,6 +405,7 @@
 	int  encr_tx_key_index;
 	char encr_keys[4][32];
 	int  encr_key_len[4];
+	char encr_key_wpa[4];
 	int  wpa_version;
 	int  wpa_keymgmt;
 	int  wpa_authalg;
@@ -945,7 +957,7 @@
 	if (priv->wpa_keymgmt == 0 ||
 		priv->wpa_keymgmt == IW_AUTH_KEY_MGMT_802_1X) {
 		for (i = 0; i < 4; i++) {
-			if (priv->encr_key_len[i] > 0)
+			if (priv->encr_key_len[i] > 0 && !priv->encr_key_wpa[i])
 				add_wep_key(usbdev, priv->encr_keys[i],
 						priv->encr_key_len[i], i);
 		}
@@ -999,7 +1011,7 @@
 	memcpy(&ndis_key.material, key, key_len);
 
 	if (index == priv->encr_tx_key_index) {
-		ndis_key.index |= cpu_to_le32(1 << 31);
+		ndis_key.index |= ndis_80211_addwep_transmit_key;
 		ret = set_encr_mode(usbdev, IW_AUTH_CIPHER_WEP104,
 						IW_AUTH_CIPHER_NONE);
 		if (ret)
@@ -1016,12 +1028,81 @@
 	}
 
 	priv->encr_key_len[index] = key_len;
+	priv->encr_key_wpa[index] = 0;
 	memcpy(&priv->encr_keys[index], key, key_len);
 
 	return 0;
 }
 
 
+static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len,
+			int index, const struct sockaddr *addr,
+			const u8 *rx_seq, int alg, int flags)
+{
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	struct ndis_80211_key ndis_key;
+	int ret;
+
+	if (index < 0 || index >= 4)
+		return -EINVAL;
+	if (key_len > sizeof(ndis_key.material) || key_len < 0)
+		return -EINVAL;
+	if ((flags & ndis_80211_addkey_set_init_recv_seq) && !rx_seq)
+		return -EINVAL;
+	if ((flags & ndis_80211_addkey_pairwise_key) && !addr)
+		return -EINVAL;
+
+	devdbg(usbdev, "add_wpa_key(%i): flags:%i%i%i", index,
+			!!(flags & ndis_80211_addkey_transmit_key),
+			!!(flags & ndis_80211_addkey_pairwise_key),
+			!!(flags & ndis_80211_addkey_set_init_recv_seq));
+
+	memset(&ndis_key, 0, sizeof(ndis_key));
+
+	ndis_key.size = cpu_to_le32(sizeof(ndis_key) -
+				sizeof(ndis_key.material) + key_len);
+	ndis_key.length = cpu_to_le32(key_len);
+	ndis_key.index = cpu_to_le32(index) | flags;
+
+	if (alg == IW_ENCODE_ALG_TKIP && key_len == 32) {
+		/* wpa_supplicant gives us the Michael MIC RX/TX keys in
+		 * different order than NDIS spec, so swap the order here. */
+		memcpy(ndis_key.material, key, 16);
+		memcpy(ndis_key.material + 16, key + 24, 8);
+		memcpy(ndis_key.material + 24, key + 16, 8);
+	} else
+		memcpy(ndis_key.material, key, key_len);
+
+	if (flags & ndis_80211_addkey_set_init_recv_seq)
+		memcpy(ndis_key.rsc, rx_seq, 6);
+
+	if (flags & ndis_80211_addkey_pairwise_key) {
+		/* pairwise key */
+		memcpy(ndis_key.bssid, addr->sa_data, ETH_ALEN);
+	} else {
+		/* group key */
+		if (priv->infra_mode == ndis_80211_infra_adhoc)
+			memset(ndis_key.bssid, 0xff, ETH_ALEN);
+		else
+			get_bssid(usbdev, ndis_key.bssid);
+	}
+
+	ret = rndis_set_oid(usbdev, OID_802_11_ADD_KEY, &ndis_key,
+					le32_to_cpu(ndis_key.size));
+	devdbg(usbdev, "add_wpa_key: OID_802_11_ADD_KEY -> %08X", ret);
+	if (ret != 0)
+		return ret;
+
+	priv->encr_key_len[index] = key_len;
+	priv->encr_key_wpa[index] = 1;
+
+	if (flags & ndis_80211_addkey_transmit_key)
+		priv->encr_tx_key_index = index;
+
+	return 0;
+}
+
+
 /* remove_key is for both wep and wpa */
 static int remove_key(struct usbnet *usbdev, int index, u8 bssid[ETH_ALEN])
 {
@@ -1034,6 +1115,7 @@
 		return 0;
 
 	priv->encr_key_len[index] = 0;
+	priv->encr_key_wpa[index] = 0;
 	memset(&priv->encr_keys[index], 0, sizeof(priv->encr_keys[index]));
 
 	if (priv->wpa_cipher_pair == IW_AUTH_CIPHER_TKIP ||
@@ -1045,7 +1127,8 @@
 		if (bssid) {
 			/* pairwise key */
 			if (memcmp(bssid, ffff_bssid, ETH_ALEN) != 0)
-				remove_key.index |= cpu_to_le32(1 << 30);
+				remove_key.index |=
+					ndis_80211_addkey_pairwise_key;
 			memcpy(remove_key.bssid, bssid,
 					sizeof(remove_key.bssid));
 		} else
@@ -1590,9 +1673,7 @@
 	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
 	struct usbnet *usbdev = netdev_priv(dev);
 	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
-	struct ndis_80211_key ndis_key;
-	int keyidx, ret;
-	u8 *addr;
+	int keyidx, flags;
 
 	keyidx = wrqu->encoding.flags & IW_ENCODE_INDEX;
 
@@ -1615,58 +1696,16 @@
 	    ext->alg == IW_ENCODE_ALG_NONE || ext->key_len == 0)
 		return remove_key(usbdev, keyidx, NULL);
 
-	if (ext->key_len > sizeof(ndis_key.material))
-		return -1;
-
-	memset(&ndis_key, 0, sizeof(ndis_key));
-
-	ndis_key.size = cpu_to_le32(sizeof(ndis_key) -
-				sizeof(ndis_key.material) + ext->key_len);
-	ndis_key.length = cpu_to_le32(ext->key_len);
-	ndis_key.index = cpu_to_le32(keyidx);
-
-	if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) {
-		memcpy(ndis_key.rsc, ext->rx_seq, 6);
-		ndis_key.index |= cpu_to_le32(1 << 29);
-	}
-
-	addr = ext->addr.sa_data;
-	if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
-		/* group key */
-		if (priv->infra_mode == ndis_80211_infra_adhoc)
-			memset(ndis_key.bssid, 0xff, ETH_ALEN);
-		else
-			get_bssid(usbdev, ndis_key.bssid);
-	} else {
-		/* pairwise key */
-		ndis_key.index |= cpu_to_le32(1 << 30);
-		memcpy(ndis_key.bssid, addr, ETH_ALEN);
-	}
-
+	flags = 0;
+	if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID)
+		flags |= ndis_80211_addkey_set_init_recv_seq;
+	if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY))
+		flags |= ndis_80211_addkey_pairwise_key;
 	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)
-		ndis_key.index |= cpu_to_le32(1 << 31);
+		flags |= ndis_80211_addkey_transmit_key;
 
-	if (ext->alg == IW_ENCODE_ALG_TKIP && ext->key_len == 32) {
-		/* wpa_supplicant gives us the Michael MIC RX/TX keys in
-		 * different order than NDIS spec, so swap the order here. */
-		memcpy(ndis_key.material, ext->key, 16);
-		memcpy(ndis_key.material + 16, ext->key + 24, 8);
-		memcpy(ndis_key.material + 24, ext->key + 16, 8);
-	} else
-		memcpy(ndis_key.material, ext->key, ext->key_len);
-
-	ret = rndis_set_oid(usbdev, OID_802_11_ADD_KEY, &ndis_key,
-					le32_to_cpu(ndis_key.size));
-	devdbg(usbdev, "SIOCSIWENCODEEXT: OID_802_11_ADD_KEY -> %08X", ret);
-	if (ret != 0)
-		return ret;
-
-	priv->encr_key_len[keyidx] = ext->key_len;
-	memcpy(&priv->encr_keys[keyidx], ndis_key.material, ext->key_len);
-	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)
-		priv->encr_tx_key_index = keyidx;
-
-	return 0;
+	return add_wpa_key(usbdev, ext->key, ext->key_len, keyidx, &ext->addr,
+				ext->rx_seq, ext->alg, flags);
 }
 
 
@@ -1849,18 +1888,10 @@
 	struct usbnet *usbdev = netdev_priv(dev);
 	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
 	__le32 tx_power;
-	int ret = 0, len;
 
 	if (priv->radio_on) {
-		if (priv->caps & CAP_SUPPORT_TXPOWER) {
-			len = sizeof(tx_power);
-			ret = rndis_query_oid(usbdev, OID_802_11_TX_POWER_LEVEL,
-							&tx_power, &len);
-			if (ret != 0)
-				return ret;
-		} else
-			/* fake incase not supported */
-			tx_power = cpu_to_le32(get_bcm4320_power(priv));
+		/* fake since changing tx_power (by userlevel) not supported */
+		tx_power = cpu_to_le32(get_bcm4320_power(priv));
 
 		wrqu->txpower.flags = IW_TXPOW_MWATT;
 		wrqu->txpower.value = le32_to_cpu(tx_power);
@@ -1873,7 +1904,7 @@
 
 	devdbg(usbdev, "SIOCGIWTXPOW: %d", wrqu->txpower.value);
 
-	return ret;
+	return 0;
 }
 
 
@@ -1883,7 +1914,6 @@
 	struct usbnet *usbdev = netdev_priv(dev);
 	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
 	__le32 tx_power = 0;
-	int ret = 0;
 
 	if (!wrqu->txpower.disabled) {
 		if (wrqu->txpower.flags == IW_TXPOW_MWATT)
@@ -1906,22 +1936,10 @@
 	devdbg(usbdev, "SIOCSIWTXPOW: %d", le32_to_cpu(tx_power));
 
 	if (le32_to_cpu(tx_power) != 0) {
-		if (priv->caps & CAP_SUPPORT_TXPOWER) {
-			/* turn radio on first */
-			if (!priv->radio_on)
-				disassociate(usbdev, 1);
-
-			ret = rndis_set_oid(usbdev, OID_802_11_TX_POWER_LEVEL,
-						&tx_power, sizeof(tx_power));
-			if (ret != 0)
-				ret = -EOPNOTSUPP;
-			return ret;
-		} else {
-			/* txpower unsupported, just turn radio on */
-			if (!priv->radio_on)
-				return disassociate(usbdev, 1);
-			return 0; /* all ready on */
-		}
+		/* txpower unsupported, just turn radio on */
+		if (!priv->radio_on)
+			return disassociate(usbdev, 1);
+		return 0; /* all ready on */
 	}
 
 	/* tx_power == 0, turn off radio */
@@ -2130,16 +2148,8 @@
 		__le32	items[8];
 	} networks_supported;
 	int len, retval, i, n;
-	__le32 tx_power;
 	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
 
-	/* determine if supports setting txpower */
-	len = sizeof(tx_power);
-	retval = rndis_query_oid(usbdev, OID_802_11_TX_POWER_LEVEL, &tx_power,
-									&len);
-	if (retval == 0 && le32_to_cpu(tx_power) != 0xFF)
-		priv->caps |= CAP_SUPPORT_TXPOWER;
-
 	/* determine supported modes */
 	len = sizeof(networks_supported);
 	retval = rndis_query_oid(usbdev, OID_802_11_NETWORK_TYPES_SUPPORTED,
@@ -2275,7 +2285,17 @@
 }
 
 
-static int bcm4320_early_init(struct usbnet *usbdev)
+static int bcm4320a_early_init(struct usbnet *usbdev)
+{
+	/* bcm4320a doesn't handle configuration parameters well. Try
+	 * set any and you get partially zeroed mac and broken device.
+	 */
+
+	return 0;
+}
+
+
+static int bcm4320b_early_init(struct usbnet *usbdev)
 {
 	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
 	char buf[8];
@@ -2515,7 +2535,7 @@
 	.rx_fixup =	rndis_rx_fixup,
 	.tx_fixup =	rndis_tx_fixup,
 	.reset =	rndis_wext_reset,
-	.early_init =	bcm4320_early_init,
+	.early_init =	bcm4320b_early_init,
 	.link_change =	rndis_wext_link_change,
 };
 
@@ -2528,7 +2548,7 @@
 	.rx_fixup =	rndis_rx_fixup,
 	.tx_fixup =	rndis_tx_fixup,
 	.reset =	rndis_wext_reset,
-	.early_init =	bcm4320_early_init,
+	.early_init =	bcm4320a_early_init,
 	.link_change =	rndis_wext_link_change,
 };
 
@@ -2541,7 +2561,7 @@
 	.rx_fixup =	rndis_rx_fixup,
 	.tx_fixup =	rndis_tx_fixup,
 	.reset =	rndis_wext_reset,
-	.early_init =	bcm4320_early_init,
+	.early_init =	bcm4320a_early_init,
 	.link_change =	rndis_wext_link_change,
 };
 
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c
index 6f39ba6..0197531 100644
--- a/drivers/net/wireless/rt2x00/rt2400pci.c
+++ b/drivers/net/wireless/rt2x00/rt2400pci.c
@@ -335,10 +335,11 @@
 	preamble_mask = erp->short_preamble << 3;
 
 	rt2x00pci_register_read(rt2x00dev, TXCSR1, &reg);
-	rt2x00_set_field32(&reg, TXCSR1_ACK_TIMEOUT,
-			   erp->ack_timeout);
+	rt2x00_set_field32(&reg, TXCSR1_ACK_TIMEOUT, erp->ack_timeout);
 	rt2x00_set_field32(&reg, TXCSR1_ACK_CONSUME_TIME,
 			   erp->ack_consume_time);
+	rt2x00_set_field32(&reg, TXCSR1_TSF_OFFSET, IEEE80211_HEADER);
+	rt2x00_set_field32(&reg, TXCSR1_AUTORESPONDER, 1);
 	rt2x00pci_register_write(rt2x00dev, TXCSR1, reg);
 
 	rt2x00pci_register_read(rt2x00dev, ARCSR2, &reg);
@@ -371,6 +372,11 @@
 	rt2x00_set_field32(&reg, CSR11_SLOT_TIME, erp->slot_time);
 	rt2x00pci_register_write(rt2x00dev, CSR11, reg);
 
+	rt2x00pci_register_read(rt2x00dev, CSR12, &reg);
+	rt2x00_set_field32(&reg, CSR12_BEACON_INTERVAL, erp->beacon_int * 16);
+	rt2x00_set_field32(&reg, CSR12_CFP_MAX_DURATION, erp->beacon_int * 16);
+	rt2x00pci_register_write(rt2x00dev, CSR12, reg);
+
 	rt2x00pci_register_read(rt2x00dev, CSR18, &reg);
 	rt2x00_set_field32(&reg, CSR18_SIFS, erp->sifs);
 	rt2x00_set_field32(&reg, CSR18_PIFS, erp->pifs);
@@ -503,24 +509,6 @@
 	rt2x00pci_register_write(rt2x00dev, CSR11, reg);
 }
 
-static void rt2400pci_config_duration(struct rt2x00_dev *rt2x00dev,
-				      struct rt2x00lib_conf *libconf)
-{
-	u32 reg;
-
-	rt2x00pci_register_read(rt2x00dev, TXCSR1, &reg);
-	rt2x00_set_field32(&reg, TXCSR1_TSF_OFFSET, IEEE80211_HEADER);
-	rt2x00_set_field32(&reg, TXCSR1_AUTORESPONDER, 1);
-	rt2x00pci_register_write(rt2x00dev, TXCSR1, reg);
-
-	rt2x00pci_register_read(rt2x00dev, CSR12, &reg);
-	rt2x00_set_field32(&reg, CSR12_BEACON_INTERVAL,
-			   libconf->conf->beacon_int * 16);
-	rt2x00_set_field32(&reg, CSR12_CFP_MAX_DURATION,
-			   libconf->conf->beacon_int * 16);
-	rt2x00pci_register_write(rt2x00dev, CSR12, reg);
-}
-
 static void rt2400pci_config_ps(struct rt2x00_dev *rt2x00dev,
 				struct rt2x00lib_conf *libconf)
 {
@@ -558,8 +546,6 @@
 					 libconf->conf->power_level);
 	if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
 		rt2400pci_config_retry_limit(rt2x00dev, libconf);
-	if (flags & IEEE80211_CONF_CHANGE_BEACON_INTERVAL)
-		rt2400pci_config_duration(rt2x00dev, libconf);
 	if (flags & IEEE80211_CONF_CHANGE_PS)
 		rt2400pci_config_ps(rt2x00dev, libconf);
 }
diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c
index 906960f..f95cb64 100644
--- a/drivers/net/wireless/rt2x00/rt2500pci.c
+++ b/drivers/net/wireless/rt2x00/rt2500pci.c
@@ -341,10 +341,11 @@
 	preamble_mask = erp->short_preamble << 3;
 
 	rt2x00pci_register_read(rt2x00dev, TXCSR1, &reg);
-	rt2x00_set_field32(&reg, TXCSR1_ACK_TIMEOUT,
-			   erp->ack_timeout);
+	rt2x00_set_field32(&reg, TXCSR1_ACK_TIMEOUT, erp->ack_timeout);
 	rt2x00_set_field32(&reg, TXCSR1_ACK_CONSUME_TIME,
 			   erp->ack_consume_time);
+	rt2x00_set_field32(&reg, TXCSR1_TSF_OFFSET, IEEE80211_HEADER);
+	rt2x00_set_field32(&reg, TXCSR1_AUTORESPONDER, 1);
 	rt2x00pci_register_write(rt2x00dev, TXCSR1, reg);
 
 	rt2x00pci_register_read(rt2x00dev, ARCSR2, &reg);
@@ -377,6 +378,11 @@
 	rt2x00_set_field32(&reg, CSR11_SLOT_TIME, erp->slot_time);
 	rt2x00pci_register_write(rt2x00dev, CSR11, reg);
 
+	rt2x00pci_register_read(rt2x00dev, CSR12, &reg);
+	rt2x00_set_field32(&reg, CSR12_BEACON_INTERVAL, erp->beacon_int * 16);
+	rt2x00_set_field32(&reg, CSR12_CFP_MAX_DURATION, erp->beacon_int * 16);
+	rt2x00pci_register_write(rt2x00dev, CSR12, reg);
+
 	rt2x00pci_register_read(rt2x00dev, CSR18, &reg);
 	rt2x00_set_field32(&reg, CSR18_SIFS, erp->sifs);
 	rt2x00_set_field32(&reg, CSR18_PIFS, erp->pifs);
@@ -552,24 +558,6 @@
 	rt2x00pci_register_write(rt2x00dev, CSR11, reg);
 }
 
-static void rt2500pci_config_duration(struct rt2x00_dev *rt2x00dev,
-				      struct rt2x00lib_conf *libconf)
-{
-	u32 reg;
-
-	rt2x00pci_register_read(rt2x00dev, TXCSR1, &reg);
-	rt2x00_set_field32(&reg, TXCSR1_TSF_OFFSET, IEEE80211_HEADER);
-	rt2x00_set_field32(&reg, TXCSR1_AUTORESPONDER, 1);
-	rt2x00pci_register_write(rt2x00dev, TXCSR1, reg);
-
-	rt2x00pci_register_read(rt2x00dev, CSR12, &reg);
-	rt2x00_set_field32(&reg, CSR12_BEACON_INTERVAL,
-			   libconf->conf->beacon_int * 16);
-	rt2x00_set_field32(&reg, CSR12_CFP_MAX_DURATION,
-			   libconf->conf->beacon_int * 16);
-	rt2x00pci_register_write(rt2x00dev, CSR12, reg);
-}
-
 static void rt2500pci_config_ps(struct rt2x00_dev *rt2x00dev,
 				struct rt2x00lib_conf *libconf)
 {
@@ -609,8 +597,6 @@
 					 libconf->conf->power_level);
 	if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
 		rt2500pci_config_retry_limit(rt2x00dev, libconf);
-	if (flags & IEEE80211_CONF_CHANGE_BEACON_INTERVAL)
-		rt2500pci_config_duration(rt2x00dev, libconf);
 	if (flags & IEEE80211_CONF_CHANGE_PS)
 		rt2500pci_config_ps(rt2x00dev, libconf);
 }
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c
index 1debb88..69f966f 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/rt2x00/rt2500usb.c
@@ -503,6 +503,10 @@
 
 	rt2500usb_register_write(rt2x00dev, TXRX_CSR11, erp->basic_rates);
 
+	rt2500usb_register_read(rt2x00dev, TXRX_CSR18, &reg);
+	rt2x00_set_field16(&reg, TXRX_CSR18_INTERVAL, erp->beacon_int * 4);
+	rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg);
+
 	rt2500usb_register_write(rt2x00dev, MAC_CSR10, erp->slot_time);
 	rt2500usb_register_write(rt2x00dev, MAC_CSR11, erp->sifs);
 	rt2500usb_register_write(rt2x00dev, MAC_CSR12, erp->eifs);
@@ -632,17 +636,6 @@
 	rt2500usb_rf_write(rt2x00dev, 3, rf3);
 }
 
-static void rt2500usb_config_duration(struct rt2x00_dev *rt2x00dev,
-				      struct rt2x00lib_conf *libconf)
-{
-	u16 reg;
-
-	rt2500usb_register_read(rt2x00dev, TXRX_CSR18, &reg);
-	rt2x00_set_field16(&reg, TXRX_CSR18_INTERVAL,
-			   libconf->conf->beacon_int * 4);
-	rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg);
-}
-
 static void rt2500usb_config_ps(struct rt2x00_dev *rt2x00dev,
 				struct rt2x00lib_conf *libconf)
 {
@@ -680,8 +673,6 @@
 	    !(flags & IEEE80211_CONF_CHANGE_CHANNEL))
 		rt2500usb_config_txpower(rt2x00dev,
 					 libconf->conf->power_level);
-	if (flags & IEEE80211_CONF_CHANGE_BEACON_INTERVAL)
-		rt2500usb_config_duration(rt2x00dev, libconf);
 	if (flags & IEEE80211_CONF_CHANGE_PS)
 		rt2500usb_config_ps(rt2x00dev, libconf);
 }
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index 257bfb5..142ad34 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -616,6 +616,11 @@
 	rt2x00_set_field32(&reg, XIFS_TIME_CFG_EIFS, erp->eifs);
 	rt2x00_set_field32(&reg, XIFS_TIME_CFG_BB_RXEND_ENABLE, 1);
 	rt2x00usb_register_write(rt2x00dev, XIFS_TIME_CFG, reg);
+
+	rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+	rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL,
+			   erp->beacon_int * 16);
+	rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg);
 }
 
 static void rt2800usb_config_ant(struct rt2x00_dev *rt2x00dev,
@@ -955,17 +960,6 @@
 	rt2x00usb_register_write(rt2x00dev, TX_RTY_CFG, reg);
 }
 
-static void rt2800usb_config_duration(struct rt2x00_dev *rt2x00dev,
-				      struct rt2x00lib_conf *libconf)
-{
-	u32 reg;
-
-	rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
-	rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL,
-			   libconf->conf->beacon_int * 16);
-	rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg);
-}
-
 static void rt2800usb_config_ps(struct rt2x00_dev *rt2x00dev,
 				struct rt2x00lib_conf *libconf)
 {
@@ -1010,8 +1004,6 @@
 		rt2800usb_config_txpower(rt2x00dev, libconf->conf->power_level);
 	if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
 		rt2800usb_config_retry_limit(rt2x00dev, libconf);
-	if (flags & IEEE80211_CONF_CHANGE_BEACON_INTERVAL)
-		rt2800usb_config_duration(rt2x00dev, libconf);
 	if (flags & IEEE80211_CONF_CHANGE_PS)
 		rt2800usb_config_ps(rt2x00dev, libconf);
 }
@@ -2881,8 +2873,6 @@
  * rt2800usb module information.
  */
 static struct usb_device_id rt2800usb_device_table[] = {
-	/* ??? */
-	{ USB_DEVICE(0x177f, 0x0302), USB_DEVICE_DATA(&rt2800usb_ops) },
 	/* Abocom */
 	{ USB_DEVICE(0x07b8, 0x2870), USB_DEVICE_DATA(&rt2800usb_ops) },
 	{ USB_DEVICE(0x07b8, 0x2770), USB_DEVICE_DATA(&rt2800usb_ops) },
@@ -2912,6 +2902,7 @@
 	{ USB_DEVICE(0x050d, 0x8053), USB_DEVICE_DATA(&rt2800usb_ops) },
 	{ USB_DEVICE(0x050d, 0x805c), USB_DEVICE_DATA(&rt2800usb_ops) },
 	{ USB_DEVICE(0x050d, 0x815c), USB_DEVICE_DATA(&rt2800usb_ops) },
+	{ USB_DEVICE(0x050d, 0x825a), USB_DEVICE_DATA(&rt2800usb_ops) },
 	/* Buffalo */
 	{ USB_DEVICE(0x0411, 0x00e8), USB_DEVICE_DATA(&rt2800usb_ops) },
 	{ USB_DEVICE(0x0411, 0x012e), USB_DEVICE_DATA(&rt2800usb_ops) },
@@ -3026,6 +3017,10 @@
 	{ USB_DEVICE(0x083a, 0xc522), USB_DEVICE_DATA(&rt2800usb_ops) },
 	/* Sparklan */
 	{ USB_DEVICE(0x15a9, 0x0006), USB_DEVICE_DATA(&rt2800usb_ops) },
+	/* Sweex */
+	{ USB_DEVICE(0x177f, 0x0153), USB_DEVICE_DATA(&rt2800usb_ops) },
+	{ USB_DEVICE(0x177f, 0x0302), USB_DEVICE_DATA(&rt2800usb_ops) },
+	{ USB_DEVICE(0x177f, 0x0313), USB_DEVICE_DATA(&rt2800usb_ops) },
 	/* U-Media*/
 	{ USB_DEVICE(0x157e, 0x300e), USB_DEVICE_DATA(&rt2800usb_ops) },
 	/* ZCOM */
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 419b1b9..2b64a61 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -417,6 +417,8 @@
 	short pifs;
 	short difs;
 	short eifs;
+
+	u16 beacon_int;
 };
 
 /*
diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c
index 863e399..c5bbf0b 100644
--- a/drivers/net/wireless/rt2x00/rt2x00config.c
+++ b/drivers/net/wireless/rt2x00/rt2x00config.c
@@ -106,6 +106,7 @@
 	}
 
 	erp.basic_rates = bss_conf->basic_rates;
+	erp.beacon_int = bss_conf->beacon_int;
 
 	rt2x00dev->ops->lib->config_erp(rt2x00dev, &erp);
 }
diff --git a/drivers/net/wireless/rt2x00/rt2x00crypto.c b/drivers/net/wireless/rt2x00/rt2x00crypto.c
index 57ab42c..bc4e81e 100644
--- a/drivers/net/wireless/rt2x00/rt2x00crypto.c
+++ b/drivers/net/wireless/rt2x00/rt2x00crypto.c
@@ -33,7 +33,7 @@
 {
 	switch (key->alg) {
 	case ALG_WEP:
-		if (key->keylen == LEN_WEP40)
+		if (key->keylen == WLAN_KEY_LEN_WEP40)
 			return CIPHER_WEP64;
 		else
 			return CIPHER_WEP128;
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index f227084..57813e7 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -260,7 +260,8 @@
 	 * Update TX statistics.
 	 */
 	rt2x00dev->link.qual.tx_success +=
-	    test_bit(TXDONE_SUCCESS, &txdesc->flags);
+	    test_bit(TXDONE_SUCCESS, &txdesc->flags) ||
+	    test_bit(TXDONE_UNKNOWN, &txdesc->flags);
 	rt2x00dev->link.qual.tx_failed +=
 	    test_bit(TXDONE_FAILURE, &txdesc->flags);
 
@@ -278,14 +279,16 @@
 	tx_info->status.rates[1].idx = -1; /* terminate */
 
 	if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) {
-		if (test_bit(TXDONE_SUCCESS, &txdesc->flags))
+		if (test_bit(TXDONE_SUCCESS, &txdesc->flags) ||
+				test_bit(TXDONE_UNKNOWN, &txdesc->flags))
 			tx_info->flags |= IEEE80211_TX_STAT_ACK;
 		else if (test_bit(TXDONE_FAILURE, &txdesc->flags))
 			rt2x00dev->low_level_stats.dot11ACKFailureCount++;
 	}
 
 	if (rate_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
-		if (test_bit(TXDONE_SUCCESS, &txdesc->flags))
+		if (test_bit(TXDONE_SUCCESS, &txdesc->flags) ||
+				test_bit(TXDONE_UNKNOWN, &txdesc->flags))
 			rt2x00dev->low_level_stats.dot11RTSSuccessCount++;
 		else if (test_bit(TXDONE_FAILURE, &txdesc->flags))
 			rt2x00dev->low_level_stats.dot11RTSFailureCount++;
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c
index cb521ee..a8bf5c4 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/rt2x00/rt61pci.c
@@ -603,15 +603,22 @@
 
 	rt2x00pci_register_read(rt2x00dev, TXRX_CSR0, &reg);
 	rt2x00_set_field32(&reg, TXRX_CSR0_RX_ACK_TIMEOUT, erp->ack_timeout);
+	rt2x00_set_field32(&reg, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER);
 	rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg);
 
 	rt2x00pci_register_read(rt2x00dev, TXRX_CSR4, &reg);
+	rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_ENABLE, 1);
 	rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_PREAMBLE,
 			   !!erp->short_preamble);
 	rt2x00pci_register_write(rt2x00dev, TXRX_CSR4, reg);
 
 	rt2x00pci_register_write(rt2x00dev, TXRX_CSR5, erp->basic_rates);
 
+	rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, &reg);
+	rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_INTERVAL,
+			   erp->beacon_int * 16);
+	rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg);
+
 	rt2x00pci_register_read(rt2x00dev, MAC_CSR9, &reg);
 	rt2x00_set_field32(&reg, MAC_CSR9_SLOT_TIME, erp->slot_time);
 	rt2x00pci_register_write(rt2x00dev, MAC_CSR9, reg);
@@ -938,25 +945,6 @@
 	rt2x00pci_register_write(rt2x00dev, TXRX_CSR4, reg);
 }
 
-static void rt61pci_config_duration(struct rt2x00_dev *rt2x00dev,
-				    struct rt2x00lib_conf *libconf)
-{
-	u32 reg;
-
-	rt2x00pci_register_read(rt2x00dev, TXRX_CSR0, &reg);
-	rt2x00_set_field32(&reg, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER);
-	rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg);
-
-	rt2x00pci_register_read(rt2x00dev, TXRX_CSR4, &reg);
-	rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_ENABLE, 1);
-	rt2x00pci_register_write(rt2x00dev, TXRX_CSR4, reg);
-
-	rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, &reg);
-	rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_INTERVAL,
-			   libconf->conf->beacon_int * 16);
-	rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg);
-}
-
 static void rt61pci_config_ps(struct rt2x00_dev *rt2x00dev,
 				struct rt2x00lib_conf *libconf)
 {
@@ -1016,8 +1004,6 @@
 		rt61pci_config_txpower(rt2x00dev, libconf->conf->power_level);
 	if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
 		rt61pci_config_retry_limit(rt2x00dev, libconf);
-	if (flags & IEEE80211_CONF_CHANGE_BEACON_INTERVAL)
-		rt61pci_config_duration(rt2x00dev, libconf);
 	if (flags & IEEE80211_CONF_CHANGE_PS)
 		rt61pci_config_ps(rt2x00dev, libconf);
 }
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index 270dd4e..211a3d6 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -566,15 +566,22 @@
 
 	rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
 	rt2x00_set_field32(&reg, TXRX_CSR0_RX_ACK_TIMEOUT, erp->ack_timeout);
+	rt2x00_set_field32(&reg, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER);
 	rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg);
 
 	rt2x00usb_register_read(rt2x00dev, TXRX_CSR4, &reg);
+	rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_ENABLE, 1);
 	rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_PREAMBLE,
 			   !!erp->short_preamble);
 	rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg);
 
 	rt2x00usb_register_write(rt2x00dev, TXRX_CSR5, erp->basic_rates);
 
+	rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &reg);
+	rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_INTERVAL,
+			   erp->beacon_int * 16);
+	rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg);
+
 	rt2x00usb_register_read(rt2x00dev, MAC_CSR9, &reg);
 	rt2x00_set_field32(&reg, MAC_CSR9_SLOT_TIME, erp->slot_time);
 	rt2x00usb_register_write(rt2x00dev, MAC_CSR9, reg);
@@ -834,25 +841,6 @@
 	rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg);
 }
 
-static void rt73usb_config_duration(struct rt2x00_dev *rt2x00dev,
-				    struct rt2x00lib_conf *libconf)
-{
-	u32 reg;
-
-	rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
-	rt2x00_set_field32(&reg, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER);
-	rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg);
-
-	rt2x00usb_register_read(rt2x00dev, TXRX_CSR4, &reg);
-	rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_ENABLE, 1);
-	rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg);
-
-	rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &reg);
-	rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_INTERVAL,
-			   libconf->conf->beacon_int * 16);
-	rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg);
-}
-
 static void rt73usb_config_ps(struct rt2x00_dev *rt2x00dev,
 				struct rt2x00lib_conf *libconf)
 {
@@ -906,8 +894,6 @@
 		rt73usb_config_txpower(rt2x00dev, libconf->conf->power_level);
 	if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
 		rt73usb_config_retry_limit(rt2x00dev, libconf);
-	if (flags & IEEE80211_CONF_CHANGE_BEACON_INTERVAL)
-		rt73usb_config_duration(rt2x00dev, libconf);
 	if (flags & IEEE80211_CONF_CHANGE_PS)
 		rt73usb_config_ps(rt2x00dev, libconf);
 }
diff --git a/drivers/net/wireless/strip.c b/drivers/net/wireless/strip.c
index f952046..b7b0c46 100644
--- a/drivers/net/wireless/strip.c
+++ b/drivers/net/wireless/strip.c
@@ -2509,7 +2509,7 @@
 	 *  netdev_priv(dev) Already holds a pointer to our struct strip
 	 */
 
-	*(MetricomAddress *) & dev->broadcast = broadcast_address;
+	*(MetricomAddress *)dev->broadcast = broadcast_address;
 	dev->dev_addr[0] = 0;
 	dev->addr_len = sizeof(MetricomAddress);
 
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 6bdb170..40b07b9 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -420,9 +420,9 @@
 	if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
 		cs->control |= ZD_CS_NEED_RANDOM_BACKOFF;
 
-	/* Multicast */
-	if (is_multicast_ether_addr(header->addr1))
-		cs->control |= ZD_CS_MULTICAST;
+	/* No ACK expected (multicast, etc.) */
+	if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+		cs->control |= ZD_CS_NO_ACK;
 
 	/* PS-POLL */
 	if (ieee80211_is_pspoll(header->frame_control))
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h
index 4c05d3ee..7c27591 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.h
+++ b/drivers/net/wireless/zd1211rw/zd_mac.h
@@ -87,7 +87,7 @@
 
 /* zd_ctrlset control field */
 #define ZD_CS_NEED_RANDOM_BACKOFF	0x01
-#define ZD_CS_MULTICAST			0x02
+#define ZD_CS_NO_ACK			0x02
 
 #define ZD_CS_FRAME_TYPE_MASK		0x0c
 #define ZD_CS_DATA_FRAME		0x00
diff --git a/drivers/staging/agnx/pci.c b/drivers/staging/agnx/pci.c
index 25c0ffd..43b3fe3 100644
--- a/drivers/staging/agnx/pci.c
+++ b/drivers/staging/agnx/pci.c
@@ -303,14 +303,18 @@
 	return 0;
 }
 
-static int agnx_config_interface(struct ieee80211_hw *dev,
-				 struct ieee80211_vif *vif,
-				 struct ieee80211_if_conf *conf)
+static void agnx_bss_info_changed(struct ieee80211_hw *dev,
+				  struct ieee80211_vif *vif,
+				  struct ieee80211_bss_conf *conf,
+				  u32 changed)
 {
 	struct agnx_priv *priv = dev->priv;
 	void __iomem *ctl = priv->ctl;
 	AGNX_TRACE;
 
+	if (!(changed & BSS_CHANGED_BSSID))
+		return;
+
 	spin_lock(&priv->lock);
 
 	if (memcmp(conf->bssid, priv->bssid, ETH_ALEN)) {
@@ -323,8 +327,7 @@
 		agnx_write32(ctl, AGNX_BM_MTSM, 0xff & ~0x1);
 	}
 	spin_unlock(&priv->lock);
-	return 0;
-} /* agnx_config_interface */
+} /* agnx_bss_info_changed */
 
 
 static void agnx_configure_filter(struct ieee80211_hw *dev,
@@ -422,7 +425,7 @@
 	.add_interface		= agnx_add_interface,
 	.remove_interface	= agnx_remove_interface,
 	.config			= agnx_config,
-	.config_interface	= agnx_config_interface,
+	.bss_info_changed	= agnx_bss_info_changed,
 	.configure_filter	= agnx_configure_filter,
 	.get_stats		= agnx_get_stats,
 	.get_tx_stats		= agnx_get_tx_stats,
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 05c29c0..34de8b2 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -493,6 +493,7 @@
 /* Mesh flags */
 #define MESH_FLAGS_AE_A4 	0x1
 #define MESH_FLAGS_AE_A5_A6	0x2
+#define MESH_FLAGS_AE		0x3
 #define MESH_FLAGS_PS_DEEP	0x4
 
 /**
@@ -1085,6 +1086,14 @@
 	WLAN_ACTION_SPCT_CHL_SWITCH = 4,
 };
 
+/* Security key length */
+enum ieee80211_key_len {
+	WLAN_KEY_LEN_WEP40 = 5,
+	WLAN_KEY_LEN_WEP104 = 13,
+	WLAN_KEY_LEN_CCMP = 16,
+	WLAN_KEY_LEN_TKIP = 32,
+};
+
 /*
  * IEEE 802.11-2007 7.3.2.9 Country information element
  *
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 9e17a83..f20da7d 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -56,9 +56,9 @@
  *	on this channel.
  * @IEEE80211_CHAN_NO_IBSS: IBSS is not allowed on this channel.
  * @IEEE80211_CHAN_RADAR: Radar detection is required on this channel.
- * @IEEE80211_CHAN_NO_FAT_ABOVE: extension channel above this channel
+ * @IEEE80211_CHAN_NO_HT40PLUS: extension channel above this channel
  * 	is not permitted.
- * @IEEE80211_CHAN_NO_FAT_BELOW: extension channel below this channel
+ * @IEEE80211_CHAN_NO_HT40MINUS: extension channel below this channel
  * 	is not permitted.
  */
 enum ieee80211_channel_flags {
@@ -66,10 +66,13 @@
 	IEEE80211_CHAN_PASSIVE_SCAN	= 1<<1,
 	IEEE80211_CHAN_NO_IBSS		= 1<<2,
 	IEEE80211_CHAN_RADAR		= 1<<3,
-	IEEE80211_CHAN_NO_FAT_ABOVE	= 1<<4,
-	IEEE80211_CHAN_NO_FAT_BELOW	= 1<<5,
+	IEEE80211_CHAN_NO_HT40PLUS	= 1<<4,
+	IEEE80211_CHAN_NO_HT40MINUS	= 1<<5,
 };
 
+#define IEEE80211_CHAN_NO_HT40 \
+	(IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
+
 /**
  * struct ieee80211_channel - channel definition
  *
@@ -778,10 +781,11 @@
  * @get_key: get information about the key with the given parameters.
  *	@mac_addr will be %NULL when requesting information for a group
  *	key. All pointers given to the @callback function need not be valid
- *	after it returns.
+ *	after it returns. This function should return an error if it is
+ *	not possible to retrieve the key, -ENOENT if it doesn't exist.
  *
  * @del_key: remove a key given the @mac_addr (%NULL for a group key)
- *	and @key_index
+ *	and @key_index, return -ENOENT if the key doesn't exist.
  *
  * @set_default_key: set the default key on an interface
  *
@@ -994,7 +998,7 @@
 	 * know whether it points to a wiphy your driver has registered
 	 * or not. Assign this to something global to your driver to
 	 * help determine whether you own this wiphy or not. */
-	void *privid;
+	const void *privid;
 
 	struct ieee80211_supported_band *bands[IEEE80211_NUM_BANDS];
 
@@ -1070,7 +1074,7 @@
  * The returned pointer must be assigned to each netdev's
  * ieee80211_ptr for proper operation.
  */
-struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv);
+struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv);
 
 /**
  * wiphy_register - register a wiphy with cfg80211
@@ -1240,6 +1244,53 @@
 extern int ieee80211_radiotap_iterator_next(
    struct ieee80211_radiotap_iterator *iterator);
 
+extern const unsigned char rfc1042_header[6];
+extern const unsigned char bridge_tunnel_header[6];
+
+/**
+ * ieee80211_get_hdrlen_from_skb - get header length from data
+ *
+ * Given an skb with a raw 802.11 header at the data pointer this function
+ * returns the 802.11 header length in bytes (not including encryption
+ * headers). If the data in the sk_buff is too short to contain a valid 802.11
+ * header the function returns 0.
+ *
+ * @skb: the frame
+ */
+unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb);
+
+/**
+ * ieee80211_hdrlen - get header length in bytes from frame control
+ * @fc: frame control field in little-endian format
+ */
+unsigned int ieee80211_hdrlen(__le16 fc);
+
+/**
+ * ieee80211_data_to_8023 - convert an 802.11 data frame to 802.3
+ * @skb: the 802.11 data frame
+ * @addr: the device MAC address
+ * @iftype: the virtual interface type
+ */
+int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr,
+			   enum nl80211_iftype iftype);
+
+/**
+ * ieee80211_data_from_8023 - convert an 802.3 frame to 802.11
+ * @skb: the 802.3 frame
+ * @addr: the device MAC address
+ * @iftype: the virtual interface type
+ * @bssid: the network bssid (used only for iftype STATION and ADHOC)
+ * @qos: build 802.11 QoS data frame
+ */
+int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr,
+			     enum nl80211_iftype iftype, u8 *bssid, bool qos);
+
+/**
+ * cfg80211_classify8021d - determine the 802.1p/1d tag for a data frame
+ * @skb: the data frame
+ */
+unsigned int cfg80211_classify8021d(struct sk_buff *skb);
+
 /*
  * Regulatory helper functions for wiphys
  */
@@ -1303,9 +1354,10 @@
  * freq_reg_info - get regulatory information for the given frequency
  * @wiphy: the wiphy for which we want to process this rule for
  * @center_freq: Frequency in KHz for which we want regulatory information for
- * @bandwidth: the bandwidth requirement you have in KHz, if you do not have one
- * 	you can set this to 0. If this frequency is allowed we then set
- * 	this value to the maximum allowed bandwidth.
+ * @desired_bw_khz: the desired max bandwidth you want to use per
+ *	channel. Note that this is still 20 MHz if you want to use HT40
+ *	as HT40 makes use of two channels for its 40 MHz width bandwidth.
+ *	If set to 0 we'll assume you want the standard 20 MHz.
  * @reg_rule: the regulatory rule which we have for this frequency
  *
  * Use this function to get the regulatory rule for a specific frequency on
@@ -1320,7 +1372,9 @@
  * freq_in_rule_band() for our current definition of a band -- this is purely
  * subjective and right now its 802.11 specific.
  */
-extern int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth,
+extern int freq_reg_info(struct wiphy *wiphy,
+			 u32 center_freq,
+			 u32 desired_bw_khz,
 			 const struct ieee80211_reg_rule **reg_rule);
 
 /*
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index d10ed17..d72346f 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -173,7 +173,6 @@
  * @timestamp: beacon timestamp
  * @beacon_int: beacon interval
  * @assoc_capability: capabilities taken from assoc resp
- * @ht: BSS's HT configuration
  * @basic_rates: bitmap of basic rates, each bit stands for an
  *	index into the rate table configured by the driver in
  *	the current band.
@@ -673,16 +672,6 @@
 };
 
 /**
- * enum ieee80211_key_len - key length
- * @LEN_WEP40: WEP 5-byte long key
- * @LEN_WEP104: WEP 13-byte long key
- */
-enum ieee80211_key_len {
-	LEN_WEP40 = 5,
-	LEN_WEP104 = 13,
-};
-
-/**
  * enum ieee80211_key_flags - key flags
  *
  * These flags are used for communication about keys between the driver
@@ -1813,24 +1802,6 @@
 ieee80211_get_buffered_bc(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
 
 /**
- * ieee80211_get_hdrlen_from_skb - get header length from data
- *
- * Given an skb with a raw 802.11 header at the data pointer this function
- * returns the 802.11 header length in bytes (not including encryption
- * headers). If the data in the sk_buff is too short to contain a valid 802.11
- * header the function returns 0.
- *
- * @skb: the frame
- */
-unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb);
-
-/**
- * ieee80211_hdrlen - get header length in bytes from frame control
- * @fc: frame control field in little-endian format
- */
-unsigned int ieee80211_hdrlen(__le16 fc);
-
-/**
  * ieee80211_get_tkip_key - get a TKIP rc4 for skb
  *
  * This function computes a TKIP rc4 key for an skb. It computes
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index e7682fe..11c7231 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -52,14 +52,6 @@
 
 DEBUGFS_READONLY_FILE(frequency, 20, "%d",
 		      local->hw.conf.channel->center_freq);
-DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d",
-		      local->hw.wiphy->rts_threshold);
-DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d",
-		      local->hw.wiphy->frag_threshold);
-DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d",
-		      local->hw.wiphy->retry_short);
-DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d",
-		      local->hw.wiphy->retry_long);
 DEBUGFS_READONLY_FILE(total_ps_buffered, 20, "%d",
 		      local->total_ps_buffered);
 DEBUGFS_READONLY_FILE(wep_iv, 20, "%#08x",
@@ -303,10 +295,6 @@
 	local->debugfs.keys = debugfs_create_dir("keys", phyd);
 
 	DEBUGFS_ADD(frequency);
-	DEBUGFS_ADD(rts_threshold);
-	DEBUGFS_ADD(fragmentation_threshold);
-	DEBUGFS_ADD(short_retry_limit);
-	DEBUGFS_ADD(long_retry_limit);
 	DEBUGFS_ADD(total_ps_buffered);
 	DEBUGFS_ADD(wep_iv);
 	DEBUGFS_ADD(tsf);
@@ -359,10 +347,6 @@
 void debugfs_hw_del(struct ieee80211_local *local)
 {
 	DEBUGFS_DEL(frequency);
-	DEBUGFS_DEL(rts_threshold);
-	DEBUGFS_DEL(fragmentation_threshold);
-	DEBUGFS_DEL(short_retry_limit);
-	DEBUGFS_DEL(long_retry_limit);
 	DEBUGFS_DEL(total_ps_buffered);
 	DEBUGFS_DEL(wep_iv);
 	DEBUGFS_DEL(tsf);
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index c236079..0b30277 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -535,9 +535,9 @@
 		bssid = ifibss->bssid;
 	bss = (void *)cfg80211_get_bss(local->hw.wiphy, chan, bssid,
 				       ifibss->ssid, ifibss->ssid_len,
-				       capability,
 				       WLAN_CAPABILITY_IBSS |
-				       WLAN_CAPABILITY_PRIVACY);
+				       WLAN_CAPABILITY_PRIVACY,
+				       capability);
 
 #ifdef CONFIG_MAC80211_IBSS_DEBUG
 	if (bss)
@@ -737,6 +737,9 @@
 	struct ieee80211_if_ibss *ifibss;
 	struct sk_buff *skb;
 
+	if (WARN_ON(local->suspended))
+		return;
+
 	if (!netif_running(sdata->dev))
 		return;
 
@@ -773,10 +776,36 @@
 	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
 	struct ieee80211_local *local = sdata->local;
 
+	if (local->quiescing) {
+		ifibss->timer_running = true;
+		return;
+	}
+
 	set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request);
 	queue_work(local->hw.workqueue, &ifibss->work);
 }
 
+#ifdef CONFIG_PM
+void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+
+	cancel_work_sync(&ifibss->work);
+	if (del_timer_sync(&ifibss->timer))
+		ifibss->timer_running = true;
+}
+
+void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+
+	if (ifibss->timer_running) {
+		add_timer(&ifibss->timer);
+		ifibss->timer_running = false;
+	}
+}
+#endif
+
 void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 9d15147..c088c46 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -293,6 +293,7 @@
 	int auth_tries; /* retries for auth req */
 	int assoc_tries; /* retries for assoc req */
 
+	unsigned long timers_running; /* used for quiesce/restart */
 	bool powersave; /* powersave requested for this iface */
 
 	unsigned long request;
@@ -333,6 +334,9 @@
 
 	unsigned long request;
 	unsigned long last_scan_completed;
+
+	bool timer_running;
+
 	bool fixed_bssid;
 	bool fixed_channel;
 
@@ -358,6 +362,8 @@
 	struct timer_list mesh_path_timer;
 	struct sk_buff_head skb_queue;
 
+	unsigned long timers_running;
+
 	bool housekeeping;
 
 	u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
@@ -609,6 +615,21 @@
 	unsigned int filter_flags; /* FIF_* */
 	struct iw_statistics wstats;
 	bool tim_in_locked_section; /* see ieee80211_beacon_get() */
+
+	/*
+	 * suspended is true if we finished all the suspend _and_ we have
+	 * not yet come up from resume. This is to be used by mac80211
+	 * to ensure driver sanity during suspend and mac80211's own
+	 * sanity. It can eventually be used for WoW as well.
+	 */
+	bool suspended;
+
+	/*
+	 * quiescing is true during the suspend process _only_ to
+	 * ease timer cancelling etc.
+	 */
+	bool quiescing;
+
 	int tx_headroom; /* required headroom for hardware/radiotap */
 
 	/* Tasklet and skb queue to process calls from IRQ mode. All frames
@@ -758,10 +779,6 @@
 		struct dentry *rcdir;
 		struct dentry *rcname;
 		struct dentry *frequency;
-		struct dentry *rts_threshold;
-		struct dentry *fragmentation_threshold;
-		struct dentry *short_retry_limit;
-		struct dentry *long_retry_limit;
 		struct dentry *total_ps_buffered;
 		struct dentry *wep_iv;
 		struct dentry *tsf;
@@ -938,6 +955,11 @@
 void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency);
 int ieee80211_max_network_latency(struct notifier_block *nb,
 				  unsigned long data, void *dummy);
+void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
+				      struct ieee80211_channel_sw_ie *sw_elem,
+				      struct ieee80211_bss *bss);
+void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata);
+void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
 
 /* IBSS code */
 void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
@@ -950,6 +972,8 @@
 int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
 			struct cfg80211_ibss_params *params);
 int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata);
+void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata);
+void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata);
 
 /* scan/BSS handling */
 void ieee80211_scan_work(struct work_struct *work);
@@ -960,6 +984,7 @@
 int ieee80211_scan_results(struct ieee80211_local *local,
 			   struct iw_request_info *info,
 			   char *buf, size_t len);
+void ieee80211_scan_cancel(struct ieee80211_local *local);
 ieee80211_rx_result
 ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata,
 		  struct sk_buff *skb,
@@ -1035,14 +1060,6 @@
 void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
 				       struct ieee80211_mgmt *mgmt,
 				       size_t len);
-void ieee80211_chswitch_timer(unsigned long data);
-void ieee80211_chswitch_work(struct work_struct *work);
-void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata,
-				  struct ieee80211_channel_sw_ie *sw_elem,
-				  struct ieee80211_bss *bss);
-void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
-				 u16 capab_info, u8 *pwr_constr_elem,
-				 u8 pwr_constr_elem_len);
 
 /* Suspend/resume and hw reconfiguration */
 int ieee80211_reconfig(struct ieee80211_local *local);
@@ -1068,8 +1085,6 @@
 
 /* utility functions/constants */
 extern void *mac80211_wiphy_privid; /* for wiphy privid */
-extern const unsigned char rfc1042_header[6];
-extern const unsigned char bridge_tunnel_header[6];
 u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
 			enum nl80211_iftype type);
 int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 827ea8e..ce26756 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -320,7 +320,7 @@
 	case ALG_TKIP:
 		key->conf.iv_len = TKIP_IV_LEN;
 		key->conf.icv_len = TKIP_ICV_LEN;
-		if (seq && seq_len == 6) {
+		if (seq) {
 			for (i = 0; i < NUM_RX_DATA_QUEUES; i++) {
 				key->u.tkip.rx[i].iv32 =
 					get_unaligned_le32(&seq[2]);
@@ -332,7 +332,7 @@
 	case ALG_CCMP:
 		key->conf.iv_len = CCMP_HDR_LEN;
 		key->conf.icv_len = CCMP_MIC_LEN;
-		if (seq && seq_len == CCMP_PN_LEN) {
+		if (seq) {
 			for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
 				for (j = 0; j < CCMP_PN_LEN; j++)
 					key->u.ccmp.rx_pn[i][j] =
@@ -342,7 +342,7 @@
 	case ALG_AES_CMAC:
 		key->conf.iv_len = 0;
 		key->conf.icv_len = sizeof(struct ieee80211_mmie);
-		if (seq && seq_len == 6)
+		if (seq)
 			for (j = 0; j < 6; j++)
 				key->u.aes_cmac.rx_pn[j] = seq[6 - j - 1];
 		break;
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 76df5ea..6b7e92e 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -219,18 +219,26 @@
 				      u32 changed)
 {
 	struct ieee80211_local *local = sdata->local;
+	static const u8 zero[ETH_ALEN] = { 0 };
 
 	if (!changed)
 		return;
 
-	if (sdata->vif.type == NL80211_IFTYPE_STATION)
-		sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
-	else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+	if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+		/*
+		 * While not associated, claim a BSSID of all-zeroes
+		 * so that drivers don't do any weird things with the
+		 * BSSID at that time.
+		 */
+		if (sdata->vif.bss_conf.assoc)
+			sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
+		else
+			sdata->vif.bss_conf.bssid = zero;
+	} else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
 		sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
 	else if (sdata->vif.type == NL80211_IFTYPE_AP)
 		sdata->vif.bss_conf.bssid = sdata->dev->dev_addr;
 	else if (ieee80211_vif_is_mesh(&sdata->vif)) {
-		static const u8 zero[ETH_ALEN] = { 0 };
 		sdata->vif.bss_conf.bssid = zero;
 	} else {
 		WARN_ON(1);
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 9000b01..fc712e6 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -21,6 +21,9 @@
 #define CAPAB_OFFSET 17
 #define ACCEPT_PLINKS 0x80
 
+#define TMR_RUNNING_HK	0
+#define TMR_RUNNING_MP	1
+
 int mesh_allocated;
 static struct kmem_cache *rm_cache;
 
@@ -45,6 +48,12 @@
 	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 
 	ifmsh->housekeeping = true;
+
+	if (local->quiescing) {
+		set_bit(TMR_RUNNING_HK, &ifmsh->timers_running);
+		return;
+	}
+
 	queue_work(local->hw.workqueue, &ifmsh->work);
 }
 
@@ -343,6 +352,11 @@
 	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 	struct ieee80211_local *local = sdata->local;
 
+	if (local->quiescing) {
+		set_bit(TMR_RUNNING_MP, &ifmsh->timers_running);
+		return;
+	}
+
 	queue_work(local->hw.workqueue, &ifmsh->work);
 }
 
@@ -424,6 +438,32 @@
 		  round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL));
 }
 
+#ifdef CONFIG_PM
+void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+
+	/* might restart the timer but that doesn't matter */
+	cancel_work_sync(&ifmsh->work);
+
+	/* use atomic bitops in case both timers fire at the same time */
+
+	if (del_timer_sync(&ifmsh->housekeeping_timer))
+		set_bit(TMR_RUNNING_HK, &ifmsh->timers_running);
+	if (del_timer_sync(&ifmsh->mesh_path_timer))
+		set_bit(TMR_RUNNING_MP, &ifmsh->timers_running);
+}
+
+void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+
+	if (test_and_clear_bit(TMR_RUNNING_HK, &ifmsh->timers_running))
+		add_timer(&ifmsh->housekeeping_timer);
+	if (test_and_clear_bit(TMR_RUNNING_MP, &ifmsh->timers_running))
+		add_timer(&ifmsh->mesh_path_timer);
+}
+#endif
 
 void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
 {
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index d891d7d..c7d7281 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -191,12 +191,8 @@
 #define PLINK_CATEGORY		30
 #define MESH_PATH_SEL_CATEGORY	32
 
-/* Mesh Header Flags */
-#define IEEE80211S_FLAGS_AE	0x3
-
 /* Public interfaces */
 /* Various */
-int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr);
 int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
 		struct ieee80211_sub_if_data *sdata);
 int mesh_rmc_check(u8 *addr, struct ieee80211s_hdr *mesh_hdr,
@@ -267,6 +263,8 @@
 void mesh_path_flush_by_nexthop(struct sta_info *sta);
 void mesh_path_discard_frame(struct sk_buff *skb,
 		struct ieee80211_sub_if_data *sdata);
+void mesh_path_quiesce(struct ieee80211_sub_if_data *sdata);
+void mesh_path_restart(struct ieee80211_sub_if_data *sdata);
 
 #ifdef CONFIG_MAC80211_MESH
 extern int mesh_allocated;
@@ -294,10 +292,20 @@
 
 void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local);
 
+void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata);
+void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata);
+void mesh_plink_quiesce(struct sta_info *sta);
+void mesh_plink_restart(struct sta_info *sta);
 #else
 #define mesh_allocated	0
 static inline void
 ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) {}
+static inline void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata)
+{}
+static inline void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
+{}
+static inline void mesh_plink_quiesce(struct sta_info *sta) {}
+static inline void mesh_plink_restart(struct sta_info *sta) {}
 #endif
 
 #endif /* IEEE80211S_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 60b35ac..003cb47 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -836,8 +836,14 @@
 	mpath = rcu_dereference(mpath);
 	if (!mpath)
 		goto endmpathtimer;
-	spin_lock_bh(&mpath->state_lock);
 	sdata = mpath->sdata;
+
+	if (sdata->local->quiescing) {
+		rcu_read_unlock();
+		return;
+	}
+
+	spin_lock_bh(&mpath->state_lock);
 	if (mpath->flags & MESH_PATH_RESOLVED ||
 			(!(mpath->flags & MESH_PATH_RESOLVING)))
 		mpath->flags &= ~(MESH_PATH_RESOLVING | MESH_PATH_RESOLVED);
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index a8bbdec..cb14253 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -266,6 +266,11 @@
 	 */
 	sta = (struct sta_info *) data;
 
+	if (sta->sdata->local->quiescing) {
+		sta->plink_timer_was_running = true;
+		return;
+	}
+
 	spin_lock_bh(&sta->lock);
 	if (sta->ignore_plink_timer) {
 		sta->ignore_plink_timer = false;
@@ -322,6 +327,22 @@
 	}
 }
 
+#ifdef CONFIG_PM
+void mesh_plink_quiesce(struct sta_info *sta)
+{
+	if (del_timer_sync(&sta->plink_timer))
+		sta->plink_timer_was_running = true;
+}
+
+void mesh_plink_restart(struct sta_info *sta)
+{
+	if (sta->plink_timer_was_running) {
+		add_timer(&sta->plink_timer);
+		sta->plink_timer_was_running = false;
+	}
+}
+#endif
+
 static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
 {
 	sta->plink_timer.expires = jiffies + (HZ * timeout / 1000);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index ae03068..509469c 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -33,10 +33,13 @@
 #define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
 #define IEEE80211_ASSOC_MAX_TRIES 3
 #define IEEE80211_MONITORING_INTERVAL (2 * HZ)
-#define IEEE80211_PROBE_WAIT (HZ / 20)
+#define IEEE80211_PROBE_WAIT (HZ / 5)
 #define IEEE80211_PROBE_IDLE_TIME (60 * HZ)
 #define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ)
 
+#define TMR_RUNNING_TIMER	0
+#define TMR_RUNNING_CHANSW	1
+
 /* utils */
 static int ecw2cw(int ecw)
 {
@@ -121,10 +124,14 @@
 		    (hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) {
 			switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
 			case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
-				channel_type = NL80211_CHAN_HT40PLUS;
+				if (!(local->hw.conf.channel->flags &
+				    IEEE80211_CHAN_NO_HT40PLUS))
+					channel_type = NL80211_CHAN_HT40PLUS;
 				break;
 			case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
-				channel_type = NL80211_CHAN_HT40MINUS;
+				if (!(local->hw.conf.channel->flags &
+				    IEEE80211_CHAN_NO_HT40MINUS))
+					channel_type = NL80211_CHAN_HT40MINUS;
 				break;
 			}
 		}
@@ -349,13 +356,13 @@
 
 		switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
 		case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
-			if (flags & IEEE80211_CHAN_NO_FAT_ABOVE) {
+			if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
 				cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
 				cap &= ~IEEE80211_HT_CAP_SGI_40;
 			}
 			break;
 		case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
-			if (flags & IEEE80211_CHAN_NO_FAT_BELOW) {
+			if (flags & IEEE80211_CHAN_NO_HT40MINUS) {
 				cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
 				cap &= ~IEEE80211_HT_CAP_SGI_40;
 			}
@@ -482,6 +489,108 @@
 	ieee80211_tx_skb(sdata, skb, 0);
 }
 
+/* spectrum management related things */
+static void ieee80211_chswitch_work(struct work_struct *work)
+{
+	struct ieee80211_sub_if_data *sdata =
+		container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
+	struct ieee80211_bss *bss;
+	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+	if (!netif_running(sdata->dev))
+		return;
+
+	bss = ieee80211_rx_bss_get(sdata->local, ifmgd->bssid,
+				   sdata->local->hw.conf.channel->center_freq,
+				   ifmgd->ssid, ifmgd->ssid_len);
+	if (!bss)
+		goto exit;
+
+	sdata->local->oper_channel = sdata->local->csa_channel;
+	/* XXX: shouldn't really modify cfg80211-owned data! */
+	if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL))
+		bss->cbss.channel = sdata->local->oper_channel;
+
+	ieee80211_rx_bss_put(sdata->local, bss);
+exit:
+	ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
+	ieee80211_wake_queues_by_reason(&sdata->local->hw,
+					IEEE80211_QUEUE_STOP_REASON_CSA);
+}
+
+static void ieee80211_chswitch_timer(unsigned long data)
+{
+	struct ieee80211_sub_if_data *sdata =
+		(struct ieee80211_sub_if_data *) data;
+	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+	if (sdata->local->quiescing) {
+		set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running);
+		return;
+	}
+
+	queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work);
+}
+
+void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
+				      struct ieee80211_channel_sw_ie *sw_elem,
+				      struct ieee80211_bss *bss)
+{
+	struct ieee80211_channel *new_ch;
+	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+	int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num);
+
+	if (ifmgd->state != IEEE80211_STA_MLME_ASSOCIATED)
+		return;
+
+	if (sdata->local->sw_scanning || sdata->local->hw_scanning)
+		return;
+
+	/* Disregard subsequent beacons if we are already running a timer
+	   processing a CSA */
+
+	if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
+		return;
+
+	new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
+	if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED)
+		return;
+
+	sdata->local->csa_channel = new_ch;
+
+	if (sw_elem->count <= 1) {
+		queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work);
+	} else {
+		ieee80211_stop_queues_by_reason(&sdata->local->hw,
+					IEEE80211_QUEUE_STOP_REASON_CSA);
+		ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
+		mod_timer(&ifmgd->chswitch_timer,
+			  jiffies +
+			  msecs_to_jiffies(sw_elem->count *
+					   bss->cbss.beacon_interval));
+	}
+}
+
+static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
+					u16 capab_info, u8 *pwr_constr_elem,
+					u8 pwr_constr_elem_len)
+{
+	struct ieee80211_conf *conf = &sdata->local->hw.conf;
+
+	if (!(capab_info & WLAN_CAPABILITY_SPECTRUM_MGMT))
+		return;
+
+	/* Power constraint IE length should be 1 octet */
+	if (pwr_constr_elem_len != 1)
+		return;
+
+	if ((*pwr_constr_elem <= conf->channel->max_power) &&
+	    (*pwr_constr_elem != sdata->local->power_constr_level)) {
+		sdata->local->power_constr_level = *pwr_constr_elem;
+		ieee80211_hw_config(sdata->local, 0);
+	}
+}
+
 /* powersave */
 static void ieee80211_enable_ps(struct ieee80211_local *local,
 				struct ieee80211_sub_if_data *sdata)
@@ -613,6 +722,9 @@
 {
 	struct ieee80211_local *local = (void *) data;
 
+	if (local->quiescing)
+		return;
+
 	queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
 }
 
@@ -865,6 +977,10 @@
 	 * changed or not.
 	 */
 	bss_info_changed |= BSS_CHANGED_BASIC_RATES;
+
+	/* And the BSSID changed - we're associated now */
+	bss_info_changed |= BSS_CHANGED_BSSID;
+
 	ieee80211_bss_info_change_notify(sdata, bss_info_changed);
 
 	/* will be same as sdata */
@@ -1064,6 +1180,9 @@
 	}
 
 	ieee80211_hw_config(local, config_changed);
+
+	/* And the BSSID changed -- not very interesting here */
+	changed |= BSS_CHANGED_BSSID;
 	ieee80211_bss_info_change_notify(sdata, changed);
 
 	rcu_read_lock();
@@ -1270,8 +1389,8 @@
 		ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL;
 		ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid,
 					 ifmgd->ssid_len, NULL, 0);
+		mod_timer(&ifmgd->timer, jiffies + IEEE80211_PROBE_WAIT);
 		goto unlock;
-
 	}
 
 	if (time_after(jiffies, sta->last_rx + IEEE80211_PROBE_IDLE_TIME)) {
@@ -1280,15 +1399,16 @@
 					 ifmgd->ssid_len, NULL, 0);
 	}
 
+	if (!disassoc)
+		mod_timer(&ifmgd->timer,
+			  jiffies + IEEE80211_MONITORING_INTERVAL);
+
  unlock:
 	rcu_read_unlock();
 
 	if (disassoc)
 		ieee80211_set_disassoc(sdata, true, true,
 					WLAN_REASON_PREV_AUTH_NOT_VALID);
-	else
-		mod_timer(&ifmgd->timer, jiffies +
-				      IEEE80211_MONITORING_INTERVAL);
 }
 
 
@@ -1732,7 +1852,7 @@
 	    (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN) == 0)) {
 		struct ieee80211_channel_sw_ie *sw_elem =
 			(struct ieee80211_channel_sw_ie *)elems->ch_switch_elem;
-		ieee80211_process_chanswitch(sdata, sw_elem, bss);
+		ieee80211_sta_process_chanswitch(sdata, sw_elem, bss);
 	}
 
 	ieee80211_rx_bss_put(local, bss);
@@ -1820,6 +1940,16 @@
 	    memcmp(ifmgd->bssid, mgmt->bssid, ETH_ALEN) != 0)
 		return;
 
+	if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+		if (net_ratelimit()) {
+			printk(KERN_DEBUG "%s: cancelling probereq poll due "
+			       "to a received beacon\n", sdata->dev->name);
+		}
+#endif
+		ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
+	}
+
 	ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
 	ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable,
 					  len - baselen, &elems,
@@ -1829,16 +1959,13 @@
 		directed_tim = ieee80211_check_tim(elems.tim, elems.tim_len,
 						   ifmgd->aid);
 
-	ncrc = crc32_be(ncrc, (void *)&directed_tim, sizeof(directed_tim));
+	if (ncrc != ifmgd->beacon_crc) {
+		ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems,
+				      true);
 
-	if (ncrc == ifmgd->beacon_crc)
-		return;
-	ifmgd->beacon_crc = ncrc;
-
-	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, true);
-
-	ieee80211_sta_wmm_params(local, ifmgd, elems.wmm_param,
-				 elems.wmm_param_len);
+		ieee80211_sta_wmm_params(local, ifmgd, elems.wmm_param,
+					 elems.wmm_param_len);
+	}
 
 	if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) {
 		if (directed_tim) {
@@ -1863,6 +1990,10 @@
 		}
 	}
 
+	if (ncrc == ifmgd->beacon_crc)
+		return;
+	ifmgd->beacon_crc = ncrc;
+
 	if (elems.erp_info && elems.erp_info_len >= 1) {
 		erp_valid = true;
 		erp_value = elems.erp_info[0];
@@ -1997,6 +2128,11 @@
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 	struct ieee80211_local *local = sdata->local;
 
+	if (local->quiescing) {
+		set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
+		return;
+	}
+
 	set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request);
 	queue_work(local->hw.workqueue, &ifmgd->work);
 }
@@ -2129,6 +2265,17 @@
 
 	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
 		return;
+
+	/*
+	 * Nothing should have been stuffed into the workqueue during
+	 * the suspend->resume cycle. If this WARN is seen then there
+	 * is a bug with either the driver suspend or something in
+	 * mac80211 stuffing into the workqueue which we haven't yet
+	 * cleared during mac80211's suspend cycle.
+	 */
+	if (WARN_ON(local->suspended))
+		return;
+
 	ifmgd = &sdata->u.mgd;
 
 	while ((skb = skb_dequeue(&ifmgd->skb_queue)))
@@ -2196,6 +2343,38 @@
 	}
 }
 
+#ifdef CONFIG_PM
+void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+	/*
+	 * we need to use atomic bitops for the running bits
+	 * only because both timers might fire at the same
+	 * time -- the code here is properly synchronised.
+	 */
+
+	cancel_work_sync(&ifmgd->work);
+	cancel_work_sync(&ifmgd->beacon_loss_work);
+	if (del_timer_sync(&ifmgd->timer))
+		set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
+
+	cancel_work_sync(&ifmgd->chswitch_work);
+	if (del_timer_sync(&ifmgd->chswitch_timer))
+		set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running);
+}
+
+void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+	if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running))
+		add_timer(&ifmgd->timer);
+	if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running))
+		add_timer(&ifmgd->chswitch_timer);
+}
+#endif
+
 /* interface setup */
 void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
 {
@@ -2310,9 +2489,6 @@
 		ifmgd->flags &= ~IEEE80211_STA_BSSID_SET;
 	}
 
-	if (netif_running(sdata->dev))
-		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
-
 	return ieee80211_sta_commit(sdata);
 }
 
@@ -2321,6 +2497,13 @@
 {
 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
+	if (len == 0 && ifmgd->extra_ie_len == 0)
+		return -EALREADY;
+
+	if (len == ifmgd->extra_ie_len && ifmgd->extra_ie &&
+	    memcmp(ifmgd->extra_ie, ie, len) == 0)
+		return -EALREADY;
+
 	kfree(ifmgd->extra_ie);
 	if (len == 0) {
 		ifmgd->extra_ie = NULL;
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 9d3d89a..7a549f9 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -2,6 +2,7 @@
 #include <net/rtnetlink.h>
 
 #include "ieee80211_i.h"
+#include "mesh.h"
 #include "driver-ops.h"
 #include "led.h"
 
@@ -13,11 +14,30 @@
 	struct sta_info *sta;
 	unsigned long flags;
 
+	ieee80211_scan_cancel(local);
+
 	ieee80211_stop_queues_by_reason(hw,
 			IEEE80211_QUEUE_STOP_REASON_SUSPEND);
 
+	/* flush out all packets */
+	synchronize_net();
+
+	local->quiescing = true;
+	/* make quiescing visible to timers everywhere */
+	mb();
+
 	flush_workqueue(local->hw.workqueue);
 
+	/* Don't try to run timers while suspended. */
+	del_timer_sync(&local->sta_cleanup);
+
+	 /*
+	 * Note that this particular timer doesn't need to be
+	 * restarted at resume.
+	 */
+	cancel_work_sync(&local->dynamic_ps_enable_work);
+	del_timer_sync(&local->dynamic_ps_timer);
+
 	/* disable keys */
 	list_for_each_entry(sdata, &local->interfaces, list)
 		ieee80211_disable_keys(sdata);
@@ -35,10 +55,20 @@
 
 	rcu_read_unlock();
 
+	/* flush again, in case driver queued work */
+	flush_workqueue(local->hw.workqueue);
+
+	/* stop hardware - this must stop RX */
+	if (local->open_count) {
+		ieee80211_led_radio(local, false);
+		drv_stop(local);
+	}
+
 	/* remove STAs */
-	if (local->ops->sta_notify) {
-		spin_lock_irqsave(&local->sta_lock, flags);
-		list_for_each_entry(sta, &local->sta_list, list) {
+	spin_lock_irqsave(&local->sta_lock, flags);
+	list_for_each_entry(sta, &local->sta_list, list) {
+		if (local->ops->sta_notify) {
+			sdata = sta->sdata;
 			if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
 				sdata = container_of(sdata->bss,
 					     struct ieee80211_sub_if_data,
@@ -47,29 +77,43 @@
 			drv_sta_notify(local, &sdata->vif, STA_NOTIFY_REMOVE,
 				       &sta->sta);
 		}
-		spin_unlock_irqrestore(&local->sta_lock, flags);
+
+		mesh_plink_quiesce(sta);
 	}
+	spin_unlock_irqrestore(&local->sta_lock, flags);
 
 	/* remove all interfaces */
 	list_for_each_entry(sdata, &local->interfaces, list) {
-		if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
-		    sdata->vif.type != NL80211_IFTYPE_MONITOR &&
-		    netif_running(sdata->dev)) {
-			conf.vif = &sdata->vif;
-			conf.type = sdata->vif.type;
-			conf.mac_addr = sdata->dev->dev_addr;
-			drv_remove_interface(local, &conf);
+		switch(sdata->vif.type) {
+		case NL80211_IFTYPE_STATION:
+			ieee80211_sta_quiesce(sdata);
+			break;
+		case NL80211_IFTYPE_ADHOC:
+			ieee80211_ibss_quiesce(sdata);
+			break;
+		case NL80211_IFTYPE_MESH_POINT:
+			ieee80211_mesh_quiesce(sdata);
+			break;
+		case NL80211_IFTYPE_AP_VLAN:
+		case NL80211_IFTYPE_MONITOR:
+			/* don't tell driver about this */
+			continue;
+		default:
+			break;
 		}
+
+		if (!netif_running(sdata->dev))
+			continue;
+
+		conf.vif = &sdata->vif;
+		conf.type = sdata->vif.type;
+		conf.mac_addr = sdata->dev->dev_addr;
+		drv_remove_interface(local, &conf);
 	}
 
-	/* flush again, in case driver queued work */
-	flush_workqueue(local->hw.workqueue);
+	local->suspended = true;
+	local->quiescing = false;
 
-	/* stop hardware */
-	if (local->open_count) {
-		ieee80211_led_radio(local, false);
-		drv_stop(local);
-	}
 	return 0;
 }
 
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index f962bd1..6a9b8e6 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1247,93 +1247,12 @@
 }
 
 static int
-ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
+__ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
 {
 	struct net_device *dev = rx->dev;
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
-	u16 hdrlen, ethertype;
-	u8 *payload;
-	u8 dst[ETH_ALEN];
-	u8 src[ETH_ALEN] __aligned(2);
-	struct sk_buff *skb = rx->skb;
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-	if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
-		return -1;
-
-	hdrlen = ieee80211_hdrlen(hdr->frame_control);
-
-	/* convert IEEE 802.11 header + possible LLC headers into Ethernet
-	 * header
-	 * IEEE 802.11 address fields:
-	 * ToDS FromDS Addr1 Addr2 Addr3 Addr4
-	 *   0     0   DA    SA    BSSID n/a
-	 *   0     1   DA    BSSID SA    n/a
-	 *   1     0   BSSID SA    DA    n/a
-	 *   1     1   RA    TA    DA    SA
-	 */
-	memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN);
-	memcpy(src, ieee80211_get_SA(hdr), ETH_ALEN);
-
-	switch (hdr->frame_control &
-		cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
-	case cpu_to_le16(IEEE80211_FCTL_TODS):
-		if (unlikely(sdata->vif.type != NL80211_IFTYPE_AP &&
-			     sdata->vif.type != NL80211_IFTYPE_AP_VLAN))
-			return -1;
-		break;
-	case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
-		if (unlikely(sdata->vif.type != NL80211_IFTYPE_WDS &&
-			     sdata->vif.type != NL80211_IFTYPE_MESH_POINT))
-			return -1;
-		if (ieee80211_vif_is_mesh(&sdata->vif)) {
-			struct ieee80211s_hdr *meshdr = (struct ieee80211s_hdr *)
-				(skb->data + hdrlen);
-			hdrlen += ieee80211_get_mesh_hdrlen(meshdr);
-			if (meshdr->flags & MESH_FLAGS_AE_A5_A6) {
-				memcpy(dst, meshdr->eaddr1, ETH_ALEN);
-				memcpy(src, meshdr->eaddr2, ETH_ALEN);
-			}
-		}
-		break;
-	case cpu_to_le16(IEEE80211_FCTL_FROMDS):
-		if (sdata->vif.type != NL80211_IFTYPE_STATION ||
-		    (is_multicast_ether_addr(dst) &&
-		     !compare_ether_addr(src, dev->dev_addr)))
-			return -1;
-		break;
-	case cpu_to_le16(0):
-		if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
-			return -1;
-		break;
-	}
-
-	if (unlikely(skb->len - hdrlen < 8))
-		return -1;
-
-	payload = skb->data + hdrlen;
-	ethertype = (payload[6] << 8) | payload[7];
-
-	if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
-		    ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
-		   compare_ether_addr(payload, bridge_tunnel_header) == 0)) {
-		/* remove RFC1042 or Bridge-Tunnel encapsulation and
-		 * replace EtherType */
-		skb_pull(skb, hdrlen + 6);
-		memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
-		memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
-	} else {
-		struct ethhdr *ehdr;
-		__be16 len;
-
-		skb_pull(skb, hdrlen);
-		len = htons(skb->len);
-		ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
-		memcpy(ehdr->h_dest, dst, ETH_ALEN);
-		memcpy(ehdr->h_source, src, ETH_ALEN);
-		ehdr->h_proto = len;
-	}
-	return 0;
+	return ieee80211_data_to_8023(rx->skb, dev->dev_addr, sdata->vif.type);
 }
 
 /*
@@ -1472,7 +1391,7 @@
 	if (!(rx->flags & IEEE80211_RX_AMSDU))
 		return RX_CONTINUE;
 
-	err = ieee80211_data_to_8023(rx);
+	err = __ieee80211_data_to_8023(rx);
 	if (unlikely(err))
 		return RX_DROP_UNUSABLE;
 
@@ -1658,7 +1577,7 @@
 	if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
 		return RX_DROP_MONITOR;
 
-	err = ieee80211_data_to_8023(rx);
+	err = __ieee80211_data_to_8023(rx);
 	if (unlikely(err))
 		return RX_DROP_UNUSABLE;
 
@@ -1846,6 +1765,9 @@
 				   sizeof(mgmt->u.action.u.chan_switch)))
 				return RX_DROP_MONITOR;
 
+			if (sdata->vif.type != NL80211_IFTYPE_STATION)
+				return RX_DROP_MONITOR;
+
 			if (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN))
 				return RX_DROP_MONITOR;
 
@@ -1856,7 +1778,7 @@
 			if (!bss)
 				return RX_DROP_MONITOR;
 
-			ieee80211_process_chanswitch(sdata,
+			ieee80211_sta_process_chanswitch(sdata,
 				     &mgmt->u.action.u.chan_switch.sw_elem, bss);
 			ieee80211_rx_bss_put(local, bss);
 			break;
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index e65d74b..2a8d09a 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -631,3 +631,21 @@
 	mutex_unlock(&local->scan_mtx);
 	return ret;
 }
+
+void ieee80211_scan_cancel(struct ieee80211_local *local)
+{
+	bool swscan;
+
+	cancel_delayed_work_sync(&local->scan_work);
+
+	/*
+	 * Only call this function when a scan can't be
+	 * queued -- mostly at suspend under RTNL.
+	 */
+	mutex_lock(&local->scan_mtx);
+	swscan = local->sw_scanning;
+	mutex_unlock(&local->scan_mtx);
+
+	if (swscan)
+		ieee80211_scan_completed(&local->hw, true);
+}
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
index 48bf78e..6895303 100644
--- a/net/mac80211/spectmgmt.c
+++ b/net/mac80211/spectmgmt.c
@@ -84,104 +84,3 @@
 			mgmt->sa, mgmt->bssid,
 			mgmt->u.action.u.measurement.dialog_token);
 }
-
-void ieee80211_chswitch_work(struct work_struct *work)
-{
-	struct ieee80211_sub_if_data *sdata =
-		container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
-	struct ieee80211_bss *bss;
-	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-
-	if (!netif_running(sdata->dev))
-		return;
-
-	bss = ieee80211_rx_bss_get(sdata->local, ifmgd->bssid,
-				   sdata->local->hw.conf.channel->center_freq,
-				   ifmgd->ssid, ifmgd->ssid_len);
-	if (!bss)
-		goto exit;
-
-	sdata->local->oper_channel = sdata->local->csa_channel;
-	/* XXX: shouldn't really modify cfg80211-owned data! */
-	if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL))
-		bss->cbss.channel = sdata->local->oper_channel;
-
-	ieee80211_rx_bss_put(sdata->local, bss);
-exit:
-	ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
-	ieee80211_wake_queues_by_reason(&sdata->local->hw,
-					IEEE80211_QUEUE_STOP_REASON_CSA);
-}
-
-void ieee80211_chswitch_timer(unsigned long data)
-{
-	struct ieee80211_sub_if_data *sdata =
-		(struct ieee80211_sub_if_data *) data;
-	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-
-	queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work);
-}
-
-void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata,
-				  struct ieee80211_channel_sw_ie *sw_elem,
-				  struct ieee80211_bss *bss)
-{
-	struct ieee80211_channel *new_ch;
-	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-	int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num);
-
-	/* FIXME: Handle ADHOC later */
-	if (sdata->vif.type != NL80211_IFTYPE_STATION)
-		return;
-
-	if (ifmgd->state != IEEE80211_STA_MLME_ASSOCIATED)
-		return;
-
-	if (sdata->local->sw_scanning || sdata->local->hw_scanning)
-		return;
-
-	/* Disregard subsequent beacons if we are already running a timer
-	   processing a CSA */
-
-	if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
-		return;
-
-	new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
-	if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED)
-		return;
-
-	sdata->local->csa_channel = new_ch;
-
-	if (sw_elem->count <= 1) {
-		queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work);
-	} else {
-		ieee80211_stop_queues_by_reason(&sdata->local->hw,
-						IEEE80211_QUEUE_STOP_REASON_CSA);
-		ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
-		mod_timer(&ifmgd->chswitch_timer,
-			  jiffies +
-			  msecs_to_jiffies(sw_elem->count *
-					   bss->cbss.beacon_interval));
-	}
-}
-
-void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
-				 u16 capab_info, u8 *pwr_constr_elem,
-				 u8 pwr_constr_elem_len)
-{
-	struct ieee80211_conf *conf = &sdata->local->hw.conf;
-
-	if (!(capab_info & WLAN_CAPABILITY_SPECTRUM_MGMT))
-		return;
-
-	/* Power constraint IE length should be 1 octet */
-	if (pwr_constr_elem_len != 1)
-		return;
-
-	if ((*pwr_constr_elem <= conf->channel->max_power) &&
-	    (*pwr_constr_elem != sdata->local->power_constr_level)) {
-		sdata->local->power_constr_level = *pwr_constr_elem;
-		ieee80211_hw_config(sdata->local, 0);
-	}
-}
-
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index a98ea27..d5611d8 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -293,6 +293,9 @@
 	skb_queue_head_init(&sta->ps_tx_buf);
 	skb_queue_head_init(&sta->tx_filtered);
 
+	for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
+		sta->last_seq_ctrl[i] = cpu_to_le16(USHORT_MAX);
+
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
 	printk(KERN_DEBUG "%s: Allocated STA %pM\n",
 	       wiphy_name(local->hw.wiphy), sta->sta.addr);
@@ -608,6 +611,9 @@
 		sta_info_cleanup_expire_buffered(local, sta);
 	rcu_read_unlock();
 
+	if (local->quiescing)
+		return;
+
 	local->sta_cleanup.expires =
 		round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
 	add_timer(&local->sta_cleanup);
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 164b16c..49a1a1f 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -216,6 +216,7 @@
  * @plink_state: peer link state
  * @plink_timeout: timeout of peer link
  * @plink_timer: peer link watch timer
+ * @plink_timer_was_running: used by suspend/resume to restore timers
  * @debugfs: debug filesystem info
  * @sta: station information we share with the driver
  */
@@ -293,6 +294,7 @@
 	__le16 reason;
 	u8 plink_retries;
 	bool ignore_plink_timer;
+	bool plink_timer_was_running;
 	enum plink_state plink_state;
 	u32 plink_timeout;
 	struct timer_list plink_timer;
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 8f68bf9..a910148 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -872,6 +872,8 @@
 
 	do {
 		hdr = (void *) skb->data;
+		if (unlikely(ieee80211_is_pspoll(hdr->frame_control)))
+			break; /* must not overwrite AID */
 		next_len = skb->next ? skb->next->len : 0;
 		group_addr = is_multicast_ether_addr(hdr->addr1);
 
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 0689a8f..949d857 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -35,15 +35,6 @@
 /* privid for wiphys to determine whether they belong to us or not */
 void *mac80211_wiphy_privid = &mac80211_wiphy_privid;
 
-/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
-/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
-const unsigned char rfc1042_header[] __aligned(2) =
-	{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
-
-/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
-const unsigned char bridge_tunnel_header[] __aligned(2) =
-	{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
-
 struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy)
 {
 	struct ieee80211_local *local;
@@ -103,70 +94,6 @@
 	return NULL;
 }
 
-unsigned int ieee80211_hdrlen(__le16 fc)
-{
-	unsigned int hdrlen = 24;
-
-	if (ieee80211_is_data(fc)) {
-		if (ieee80211_has_a4(fc))
-			hdrlen = 30;
-		if (ieee80211_is_data_qos(fc))
-			hdrlen += IEEE80211_QOS_CTL_LEN;
-		goto out;
-	}
-
-	if (ieee80211_is_ctl(fc)) {
-		/*
-		 * ACK and CTS are 10 bytes, all others 16. To see how
-		 * to get this condition consider
-		 *   subtype mask:   0b0000000011110000 (0x00F0)
-		 *   ACK subtype:    0b0000000011010000 (0x00D0)
-		 *   CTS subtype:    0b0000000011000000 (0x00C0)
-		 *   bits that matter:         ^^^      (0x00E0)
-		 *   value of those: 0b0000000011000000 (0x00C0)
-		 */
-		if ((fc & cpu_to_le16(0x00E0)) == cpu_to_le16(0x00C0))
-			hdrlen = 10;
-		else
-			hdrlen = 16;
-	}
-out:
-	return hdrlen;
-}
-EXPORT_SYMBOL(ieee80211_hdrlen);
-
-unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
-{
-	const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *)skb->data;
-	unsigned int hdrlen;
-
-	if (unlikely(skb->len < 10))
-		return 0;
-	hdrlen = ieee80211_hdrlen(hdr->frame_control);
-	if (unlikely(hdrlen > skb->len))
-		return 0;
-	return hdrlen;
-}
-EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
-
-int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
-{
-	int ae = meshhdr->flags & IEEE80211S_FLAGS_AE;
-	/* 7.1.3.5a.2 */
-	switch (ae) {
-	case 0:
-		return 6;
-	case 1:
-		return 12;
-	case 2:
-		return 18;
-	case 3:
-		return 24;
-	default:
-		return 6;
-	}
-}
-
 void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
 {
 	struct sk_buff *skb = tx->skb;
@@ -1034,6 +961,13 @@
 	struct sta_info *sta;
 	unsigned long flags;
 	int res;
+	bool from_suspend = local->suspended;
+
+	/*
+	 * We're going to start the hardware, at that point
+	 * we are no longer suspended and can RX frames.
+	 */
+	local->suspended = false;
 
 	/* restart hardware */
 	if (local->open_count) {
@@ -1058,6 +992,7 @@
 	if (local->ops->sta_notify) {
 		spin_lock_irqsave(&local->sta_lock, flags);
 		list_for_each_entry(sta, &local->sta_list, list) {
+			sdata = sta->sdata;
 			if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
 				sdata = container_of(sdata->bss,
 					     struct ieee80211_sub_if_data,
@@ -1128,5 +1063,40 @@
 	ieee80211_wake_queues_by_reason(hw,
 			IEEE80211_QUEUE_STOP_REASON_SUSPEND);
 
+	/*
+	 * If this is for hw restart things are still running.
+	 * We may want to change that later, however.
+	 */
+	if (!from_suspend)
+		return 0;
+
+#ifdef CONFIG_PM
+	local->suspended = false;
+
+	list_for_each_entry(sdata, &local->interfaces, list) {
+		switch(sdata->vif.type) {
+		case NL80211_IFTYPE_STATION:
+			ieee80211_sta_restart(sdata);
+			break;
+		case NL80211_IFTYPE_ADHOC:
+			ieee80211_ibss_restart(sdata);
+			break;
+		case NL80211_IFTYPE_MESH_POINT:
+			ieee80211_mesh_restart(sdata);
+			break;
+		default:
+			break;
+		}
+	}
+
+	add_timer(&local->sta_cleanup);
+
+	spin_lock_irqsave(&local->sta_lock, flags);
+	list_for_each_entry(sta, &local->sta_list, list)
+		mesh_plink_restart(sta);
+	spin_unlock_irqrestore(&local->sta_lock, flags);
+#else
+	WARN_ON(1);
+#endif
 	return 0;
 }
diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c
index c143947..a01154e 100644
--- a/net/mac80211/wext.c
+++ b/net/mac80211/wext.c
@@ -37,12 +37,13 @@
 
 	if (sdata->vif.type == NL80211_IFTYPE_STATION) {
 		int ret = ieee80211_sta_set_extra_ie(sdata, extra, data->length);
-		if (ret)
+		if (ret && ret != -EALREADY)
 			return ret;
 		sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
 		sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME;
 		sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT;
-		ieee80211_sta_req_auth(sdata);
+		if (ret != -EALREADY)
+			ieee80211_sta_req_auth(sdata);
 		return 0;
 	}
 
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 45b74f3..694343b 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -23,34 +23,6 @@
  */
 const int ieee802_1d_to_ac[8] = { 2, 3, 3, 2, 1, 1, 0, 0 };
 
-static const char llc_ip_hdr[8] = {0xAA, 0xAA, 0x3, 0, 0, 0, 0x08, 0};
-
-/* Given a data frame determine the 802.1p/1d tag to use.  */
-static unsigned int classify_1d(struct sk_buff *skb)
-{
-	unsigned int dscp;
-
-	/* skb->priority values from 256->263 are magic values to
-	 * directly indicate a specific 802.1d priority.  This is used
-	 * to allow 802.1d priority to be passed directly in from VLAN
-	 * tags, etc.
-	 */
-	if (skb->priority >= 256 && skb->priority <= 263)
-		return skb->priority - 256;
-
-	switch (skb->protocol) {
-	case htons(ETH_P_IP):
-		dscp = ip_hdr(skb)->tos & 0xfc;
-		break;
-
-	default:
-		return 0;
-	}
-
-	return dscp >> 5;
-}
-
-
 static int wme_downgrade_ac(struct sk_buff *skb)
 {
 	switch (skb->priority) {
@@ -94,7 +66,7 @@
 
 	/* use the data classifier to determine what 802.1d tag the
 	 * data frame has */
-	skb->priority = classify_1d(skb);
+	skb->priority = cfg80211_classify8021d(skb);
 
 	/* in case we are a client verify acm is not set for this ac */
 	while (unlikely(local->wmm_acm & BIT(skb->priority))) {
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index 3c3bc9e..4500549 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -10,6 +10,14 @@
 
 	  If unsure, say N.
 
+config CFG80211_DEBUGFS
+	bool "cfg80211 DebugFS entries"
+	depends on CFG80211 && DEBUG_FS
+	---help---
+	  You can enable this if you want to debugfs entries for cfg80211.
+
+	  If unsure, say N.
+
 config WIRELESS_OLD_REGULATORY
 	bool "Old wireless static regulatory definitions"
 	default n
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index 14ea01c..f78c483 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -6,6 +6,7 @@
 obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o
 
 cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o
+cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
 cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o
 
 ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 47c20eb..a5dbea1 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -17,6 +17,7 @@
 #include "nl80211.h"
 #include "core.h"
 #include "sysfs.h"
+#include "debugfs.h"
 
 /* name for sysfs, %d is appended */
 #define PHY_NAME "phy"
@@ -228,7 +229,7 @@
 
 /* exported functions */
 
-struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv)
+struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
 {
 	static int wiphy_counter;
 
@@ -375,6 +376,8 @@
 		nl80211_send_reg_change_event(&request);
 	}
 
+	cfg80211_debugfs_drv_add(drv);
+
 	res = 0;
 out_unlock:
 	mutex_unlock(&cfg80211_mutex);
@@ -405,6 +408,8 @@
 	/* unlock again before freeing */
 	mutex_unlock(&drv->mtx);
 
+	cfg80211_debugfs_drv_del(drv);
+
 	/* If this device got a regulatory hint tell core its
 	 * free to listen now to a new shiny device regulatory hint */
 	reg_device_remove(wiphy);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index f14b6c5..ab512bc 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -10,12 +10,13 @@
 #include <linux/netdevice.h>
 #include <linux/kref.h>
 #include <linux/rbtree.h>
+#include <linux/debugfs.h>
 #include <net/genetlink.h>
 #include <net/cfg80211.h>
 #include "reg.h"
 
 struct cfg80211_registered_device {
-	struct cfg80211_ops *ops;
+	const struct cfg80211_ops *ops;
 	struct list_head list;
 	/* we hold this mutex during any call so that
 	 * we cannot do multiple calls at once, and also
@@ -50,6 +51,17 @@
 	struct cfg80211_scan_request *scan_req; /* protected by RTNL */
 	unsigned long suspend_at;
 
+#ifdef CONFIG_CFG80211_DEBUGFS
+	/* Debugfs entries */
+	struct wiphy_debugfsdentries {
+		struct dentry *rts_threshold;
+		struct dentry *fragmentation_threshold;
+		struct dentry *short_retry_limit;
+		struct dentry *long_retry_limit;
+		struct dentry *ht40allow_map;
+	} debugfs;
+#endif
+
 	/* must be last because of the way we do wiphy_priv(),
 	 * and it should at least be aligned to NETDEV_ALIGN */
 	struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN)));
diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c
new file mode 100644
index 0000000..679ddfc
--- /dev/null
+++ b/net/wireless/debugfs.c
@@ -0,0 +1,131 @@
+/*
+ * cfg80211 debugfs
+ *
+ * Copyright 2009	Luis R. Rodriguez <lrodriguez@atheros.com>
+ * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "core.h"
+#include "debugfs.h"
+
+static int cfg80211_open_file_generic(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+#define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...)		\
+static ssize_t name## _read(struct file *file, char __user *userbuf,	\
+			    size_t count, loff_t *ppos)			\
+{									\
+	struct wiphy *wiphy= file->private_data;		\
+	char buf[buflen];						\
+	int res;							\
+									\
+	res = scnprintf(buf, buflen, fmt "\n", ##value);		\
+	return simple_read_from_buffer(userbuf, count, ppos, buf, res);	\
+}									\
+									\
+static const struct file_operations name## _ops = {			\
+	.read = name## _read,						\
+	.open = cfg80211_open_file_generic,				\
+};
+
+DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d",
+		      wiphy->rts_threshold)
+DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d",
+		      wiphy->frag_threshold);
+DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d",
+		      wiphy->retry_short)
+DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d",
+		      wiphy->retry_long);
+
+static int ht_print_chan(struct ieee80211_channel *chan,
+			 char *buf, int buf_size, int offset)
+{
+	if (WARN_ON(offset > buf_size))
+		return 0;
+
+	if (chan->flags & IEEE80211_CHAN_DISABLED)
+		return snprintf(buf + offset,
+				buf_size - offset,
+				"%d Disabled\n",
+				chan->center_freq);
+
+	return snprintf(buf + offset,
+			buf_size - offset,
+			"%d HT40 %c%c\n",
+			chan->center_freq,
+			(chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ? ' ' : '-',
+			(chan->flags & IEEE80211_CHAN_NO_HT40PLUS)  ? ' ' : '+');
+}
+
+static ssize_t ht40allow_map_read(struct file *file,
+				  char __user *user_buf,
+				  size_t count, loff_t *ppos)
+{
+	struct wiphy *wiphy = file->private_data;
+	char *buf;
+	unsigned int offset = 0, buf_size = PAGE_SIZE, i, r;
+	enum ieee80211_band band;
+	struct ieee80211_supported_band *sband;
+
+	buf = kzalloc(buf_size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	mutex_lock(&cfg80211_mutex);
+
+	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+		sband = wiphy->bands[band];
+		if (!sband)
+			continue;
+		for (i = 0; i < sband->n_channels; i++)
+			offset += ht_print_chan(&sband->channels[i],
+						buf, buf_size, offset);
+	}
+
+	mutex_unlock(&cfg80211_mutex);
+
+	r = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
+
+	kfree(buf);
+
+	return r;
+}
+
+static const struct file_operations ht40allow_map_ops = {
+	.read = ht40allow_map_read,
+	.open = cfg80211_open_file_generic,
+};
+
+#define DEBUGFS_ADD(name)						\
+	drv->debugfs.name = debugfs_create_file(#name, S_IRUGO, phyd,	\
+						  &drv->wiphy, &name## _ops);
+#define DEBUGFS_DEL(name)						\
+	debugfs_remove(drv->debugfs.name);				\
+	drv->debugfs.name = NULL;
+
+void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv)
+{
+	struct dentry *phyd = drv->wiphy.debugfsdir;
+
+	DEBUGFS_ADD(rts_threshold);
+	DEBUGFS_ADD(fragmentation_threshold);
+	DEBUGFS_ADD(short_retry_limit);
+	DEBUGFS_ADD(long_retry_limit);
+	DEBUGFS_ADD(ht40allow_map);
+}
+
+void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv)
+{
+	DEBUGFS_DEL(rts_threshold);
+	DEBUGFS_DEL(fragmentation_threshold);
+	DEBUGFS_DEL(short_retry_limit);
+	DEBUGFS_DEL(long_retry_limit);
+	DEBUGFS_DEL(ht40allow_map);
+}
diff --git a/net/wireless/debugfs.h b/net/wireless/debugfs.h
new file mode 100644
index 0000000..c226983
--- /dev/null
+++ b/net/wireless/debugfs.h
@@ -0,0 +1,14 @@
+#ifndef __CFG80211_DEBUGFS_H
+#define __CFG80211_DEBUGFS_H
+
+#ifdef CONFIG_CFG80211_DEBUGFS
+void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv);
+void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv);
+#else
+static inline
+void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv) {}
+static inline
+void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv) {}
+#endif
+
+#endif /* __CFG80211_DEBUGFS_H */
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index a3a152f..56d729c 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -77,6 +77,7 @@
 	[NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
 	[NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
 	[NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
+	[NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
 
 	[NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
 	[NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
@@ -492,7 +493,7 @@
 		enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
 		struct ieee80211_channel *chan;
 		struct ieee80211_sta_ht_cap *ht_cap;
-		u32 freq, sec_freq;
+		u32 freq;
 
 		if (!rdev->ops->set_channel) {
 			result = -EOPNOTSUPP;
@@ -518,33 +519,28 @@
 		if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
 			goto bad_res;
 
-		if (channel_type == NL80211_CHAN_HT40MINUS)
-			sec_freq = freq - 20;
-		else if (channel_type == NL80211_CHAN_HT40PLUS)
-			sec_freq = freq + 20;
-		else
-			sec_freq = 0;
+		if (channel_type == NL80211_CHAN_HT40MINUS &&
+		    (chan->flags & IEEE80211_CHAN_NO_HT40MINUS))
+			goto bad_res;
+		else if (channel_type == NL80211_CHAN_HT40PLUS &&
+			 (chan->flags & IEEE80211_CHAN_NO_HT40PLUS))
+			goto bad_res;
+
+		/*
+		 * At this point we know if that if HT40 was requested
+		 * we are allowed to use it and the extension channel
+		 * exists.
+		 */
 
 		ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
 
-		/* no HT capabilities */
-		if (channel_type != NL80211_CHAN_NO_HT &&
-		    !ht_cap->ht_supported)
-			goto bad_res;
-
-		if (sec_freq) {
-			struct ieee80211_channel *schan;
-
-			/* no 40 MHz capabilities */
+		/* no HT capabilities or intolerant */
+		if (channel_type != NL80211_CHAN_NO_HT) {
+			if (!ht_cap->ht_supported)
+				goto bad_res;
 			if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
 			    (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
 				goto bad_res;
-
-			schan = ieee80211_get_channel(&rdev->wiphy, sec_freq);
-
-			/* Secondary channel not allowed */
-			if (!schan || schan->flags & IEEE80211_CHAN_DISABLED)
-				goto bad_res;
 		}
 
 		result = rdev->ops->set_channel(&rdev->wiphy, chan,
@@ -2571,18 +2567,24 @@
 			rem_reg_rules) {
 		num_rules++;
 		if (num_rules > NL80211_MAX_SUPP_REG_RULES)
-			goto bad_reg;
+			return -EINVAL;
 	}
 
-	if (!reg_is_valid_request(alpha2))
-		return -EINVAL;
+	mutex_lock(&cfg80211_mutex);
+
+	if (!reg_is_valid_request(alpha2)) {
+		r = -EINVAL;
+		goto bad_reg;
+	}
 
 	size_of_regd = sizeof(struct ieee80211_regdomain) +
 		(num_rules * sizeof(struct ieee80211_reg_rule));
 
 	rd = kzalloc(size_of_regd, GFP_KERNEL);
-	if (!rd)
-		return -ENOMEM;
+	if (!rd) {
+		r = -ENOMEM;
+		goto bad_reg;
+	}
 
 	rd->n_reg_rules = num_rules;
 	rd->alpha2[0] = alpha2[0];
@@ -2599,20 +2601,24 @@
 
 		rule_idx++;
 
-		if (rule_idx > NL80211_MAX_SUPP_REG_RULES)
+		if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
+			r = -EINVAL;
 			goto bad_reg;
+		}
 	}
 
 	BUG_ON(rule_idx != num_rules);
 
-	mutex_lock(&cfg80211_mutex);
 	r = set_regdom(rd);
+
 	mutex_unlock(&cfg80211_mutex);
+
 	return r;
 
  bad_reg:
+	mutex_unlock(&cfg80211_mutex);
 	kfree(rd);
-	return -EINVAL;
+	return r;
 }
 
 static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 537af62..df0ced9 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -48,12 +48,6 @@
 /* To trigger userspace events */
 static struct platform_device *reg_pdev;
 
-/* Keep the ordering from large to small */
-static u32 supported_bandwidths[] = {
-	MHZ_TO_KHZ(40),
-	MHZ_TO_KHZ(20),
-};
-
 /*
  * Central wireless core regulatory domains, we only need two,
  * the current one and a world regulatory domain in case we have no
@@ -388,6 +382,8 @@
 /* Used by nl80211 before kmalloc'ing our regulatory domain */
 bool reg_is_valid_request(const char *alpha2)
 {
+	assert_cfg80211_lock();
+
 	if (!last_request)
 		return false;
 
@@ -435,19 +431,20 @@
 	return true;
 }
 
-/* Returns value in KHz */
-static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range,
-	u32 freq)
+static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range,
+			    u32 center_freq_khz,
+			    u32 bw_khz)
 {
-	unsigned int i;
-	for (i = 0; i < ARRAY_SIZE(supported_bandwidths); i++) {
-		u32 start_freq_khz = freq - supported_bandwidths[i]/2;
-		u32 end_freq_khz = freq + supported_bandwidths[i]/2;
-		if (start_freq_khz >= freq_range->start_freq_khz &&
-			end_freq_khz <= freq_range->end_freq_khz)
-			return supported_bandwidths[i];
-	}
-	return 0;
+	u32 start_freq_khz, end_freq_khz;
+
+	start_freq_khz = center_freq_khz - (bw_khz/2);
+	end_freq_khz = center_freq_khz + (bw_khz/2);
+
+	if (start_freq_khz >= freq_range->start_freq_khz &&
+	    end_freq_khz <= freq_range->end_freq_khz)
+		return true;
+
+	return false;
 }
 
 /**
@@ -847,14 +844,17 @@
 
 static int freq_reg_info_regd(struct wiphy *wiphy,
 			      u32 center_freq,
-			      u32 *bandwidth,
+			      u32 desired_bw_khz,
 			      const struct ieee80211_reg_rule **reg_rule,
 			      const struct ieee80211_regdomain *custom_regd)
 {
 	int i;
 	bool band_rule_found = false;
 	const struct ieee80211_regdomain *regd;
-	u32 max_bandwidth = 0;
+	bool bw_fits = false;
+
+	if (!desired_bw_khz)
+		desired_bw_khz = MHZ_TO_KHZ(20);
 
 	regd = custom_regd ? custom_regd : cfg80211_regdomain;
 
@@ -887,38 +887,54 @@
 		if (!band_rule_found)
 			band_rule_found = freq_in_rule_band(fr, center_freq);
 
-		max_bandwidth = freq_max_bandwidth(fr, center_freq);
+		bw_fits = reg_does_bw_fit(fr,
+					  center_freq,
+					  desired_bw_khz);
 
-		if (max_bandwidth && *bandwidth <= max_bandwidth) {
+		if (band_rule_found && bw_fits) {
 			*reg_rule = rr;
-			*bandwidth = max_bandwidth;
-			break;
+			return 0;
 		}
 	}
 
 	if (!band_rule_found)
 		return -ERANGE;
 
-	return !max_bandwidth;
+	return -EINVAL;
 }
 EXPORT_SYMBOL(freq_reg_info);
 
-int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth,
-			 const struct ieee80211_reg_rule **reg_rule)
+int freq_reg_info(struct wiphy *wiphy,
+		  u32 center_freq,
+		  u32 desired_bw_khz,
+		  const struct ieee80211_reg_rule **reg_rule)
 {
 	assert_cfg80211_lock();
-	return freq_reg_info_regd(wiphy, center_freq,
-		bandwidth, reg_rule, NULL);
+	return freq_reg_info_regd(wiphy,
+				  center_freq,
+				  desired_bw_khz,
+				  reg_rule,
+				  NULL);
 }
 
+/*
+ * Note that right now we assume the desired channel bandwidth
+ * is always 20 MHz for each individual channel (HT40 uses 20 MHz
+ * per channel, the primary and the extension channel). To support
+ * smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a
+ * new ieee80211_channel.target_bw and re run the regulatory check
+ * on the wiphy with the target_bw specified. Then we can simply use
+ * that below for the desired_bw_khz below.
+ */
 static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
 			   unsigned int chan_idx)
 {
 	int r;
-	u32 flags;
-	u32 max_bandwidth = 0;
+	u32 flags, bw_flags = 0;
+	u32 desired_bw_khz = MHZ_TO_KHZ(20);
 	const struct ieee80211_reg_rule *reg_rule = NULL;
 	const struct ieee80211_power_rule *power_rule = NULL;
+	const struct ieee80211_freq_range *freq_range = NULL;
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_channel *chan;
 	struct wiphy *request_wiphy = NULL;
@@ -933,8 +949,10 @@
 
 	flags = chan->orig_flags;
 
-	r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq),
-		&max_bandwidth, &reg_rule);
+	r = freq_reg_info(wiphy,
+			  MHZ_TO_KHZ(chan->center_freq),
+			  desired_bw_khz,
+			  &reg_rule);
 
 	if (r) {
 		/*
@@ -977,6 +995,10 @@
 	}
 
 	power_rule = &reg_rule->power_rule;
+	freq_range = &reg_rule->freq_range;
+
+	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
+		bw_flags = IEEE80211_CHAN_NO_HT40;
 
 	if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
 	    request_wiphy && request_wiphy == wiphy &&
@@ -987,19 +1009,19 @@
 		 * settings
 		 */
 		chan->flags = chan->orig_flags =
-			map_regdom_flags(reg_rule->flags);
+			map_regdom_flags(reg_rule->flags) | bw_flags;
 		chan->max_antenna_gain = chan->orig_mag =
 			(int) MBI_TO_DBI(power_rule->max_antenna_gain);
-		chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth);
+		chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz);
 		chan->max_power = chan->orig_mpwr =
 			(int) MBM_TO_DBM(power_rule->max_eirp);
 		return;
 	}
 
-	chan->flags = flags | map_regdom_flags(reg_rule->flags);
+	chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags);
 	chan->max_antenna_gain = min(chan->orig_mag,
 		(int) MBI_TO_DBI(power_rule->max_antenna_gain));
-	chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth);
+	chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz);
 	if (chan->orig_mpwr)
 		chan->max_power = min(chan->orig_mpwr,
 			(int) MBM_TO_DBM(power_rule->max_eirp));
@@ -1156,6 +1178,93 @@
 	wiphy_update_beacon_reg(wiphy);
 }
 
+static bool is_ht40_not_allowed(struct ieee80211_channel *chan)
+{
+	if (!chan)
+		return true;
+	if (chan->flags & IEEE80211_CHAN_DISABLED)
+		return true;
+	/* This would happen when regulatory rules disallow HT40 completely */
+	if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40)))
+		return true;
+	return false;
+}
+
+static void reg_process_ht_flags_channel(struct wiphy *wiphy,
+					 enum ieee80211_band band,
+					 unsigned int chan_idx)
+{
+	struct ieee80211_supported_band *sband;
+	struct ieee80211_channel *channel;
+	struct ieee80211_channel *channel_before = NULL, *channel_after = NULL;
+	unsigned int i;
+
+	assert_cfg80211_lock();
+
+	sband = wiphy->bands[band];
+	BUG_ON(chan_idx >= sband->n_channels);
+	channel = &sband->channels[chan_idx];
+
+	if (is_ht40_not_allowed(channel)) {
+		channel->flags |= IEEE80211_CHAN_NO_HT40;
+		return;
+	}
+
+	/*
+	 * We need to ensure the extension channels exist to
+	 * be able to use HT40- or HT40+, this finds them (or not)
+	 */
+	for (i = 0; i < sband->n_channels; i++) {
+		struct ieee80211_channel *c = &sband->channels[i];
+		if (c->center_freq == (channel->center_freq - 20))
+			channel_before = c;
+		if (c->center_freq == (channel->center_freq + 20))
+			channel_after = c;
+	}
+
+	/*
+	 * Please note that this assumes target bandwidth is 20 MHz,
+	 * if that ever changes we also need to change the below logic
+	 * to include that as well.
+	 */
+	if (is_ht40_not_allowed(channel_before))
+		channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
+	else
+		channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
+
+	if (is_ht40_not_allowed(channel_after))
+		channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
+	else
+		channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
+}
+
+static void reg_process_ht_flags_band(struct wiphy *wiphy,
+				      enum ieee80211_band band)
+{
+	unsigned int i;
+	struct ieee80211_supported_band *sband;
+
+	BUG_ON(!wiphy->bands[band]);
+	sband = wiphy->bands[band];
+
+	for (i = 0; i < sband->n_channels; i++)
+		reg_process_ht_flags_channel(wiphy, band, i);
+}
+
+static void reg_process_ht_flags(struct wiphy *wiphy)
+{
+	enum ieee80211_band band;
+
+	if (!wiphy)
+		return;
+
+	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+		if (wiphy->bands[band])
+			reg_process_ht_flags_band(wiphy, band);
+	}
+
+}
+
 void wiphy_update_regulatory(struct wiphy *wiphy,
 			     enum nl80211_reg_initiator initiator)
 {
@@ -1169,6 +1278,7 @@
 	}
 out:
 	reg_process_beacons(wiphy);
+	reg_process_ht_flags(wiphy);
 	if (wiphy->reg_notifier)
 		wiphy->reg_notifier(wiphy, last_request);
 }
@@ -1179,9 +1289,11 @@
 				  const struct ieee80211_regdomain *regd)
 {
 	int r;
-	u32 max_bandwidth = 0;
+	u32 desired_bw_khz = MHZ_TO_KHZ(20);
+	u32 bw_flags = 0;
 	const struct ieee80211_reg_rule *reg_rule = NULL;
 	const struct ieee80211_power_rule *power_rule = NULL;
+	const struct ieee80211_freq_range *freq_range = NULL;
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_channel *chan;
 
@@ -1191,8 +1303,11 @@
 	BUG_ON(chan_idx >= sband->n_channels);
 	chan = &sband->channels[chan_idx];
 
-	r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq),
-		&max_bandwidth, &reg_rule, regd);
+	r = freq_reg_info_regd(wiphy,
+			       MHZ_TO_KHZ(chan->center_freq),
+			       desired_bw_khz,
+			       &reg_rule,
+			       regd);
 
 	if (r) {
 		chan->flags = IEEE80211_CHAN_DISABLED;
@@ -1200,10 +1315,14 @@
 	}
 
 	power_rule = &reg_rule->power_rule;
+	freq_range = &reg_rule->freq_range;
 
-	chan->flags |= map_regdom_flags(reg_rule->flags);
+	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
+		bw_flags = IEEE80211_CHAN_NO_HT40;
+
+	chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
 	chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain);
-	chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth);
+	chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz);
 	chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp);
 }
 
@@ -1225,13 +1344,22 @@
 				   const struct ieee80211_regdomain *regd)
 {
 	enum ieee80211_band band;
+	unsigned int bands_set = 0;
 
 	mutex_lock(&cfg80211_mutex);
 	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
-		if (wiphy->bands[band])
-			handle_band_custom(wiphy, band, regd);
+		if (!wiphy->bands[band])
+			continue;
+		handle_band_custom(wiphy, band, regd);
+		bands_set++;
 	}
 	mutex_unlock(&cfg80211_mutex);
+
+	/*
+	 * no point in calling this if it won't have any effect
+	 * on your device's supportd bands.
+	 */
+	WARN_ON(!bands_set);
 }
 EXPORT_SYMBOL(wiphy_apply_custom_regulatory);
 
diff --git a/net/wireless/util.c b/net/wireless/util.c
index beb226e..d072bff 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -4,7 +4,9 @@
  * Copyright 2007-2009	Johannes Berg <johannes@sipsolutions.net>
  */
 #include <linux/bitops.h>
+#include <linux/etherdevice.h>
 #include <net/cfg80211.h>
+#include <net/ip.h>
 #include "core.h"
 
 struct ieee80211_rate *
@@ -181,5 +183,323 @@
 		return -EINVAL;
 	}
 
+	if (params->seq) {
+		switch (params->cipher) {
+		case WLAN_CIPHER_SUITE_WEP40:
+		case WLAN_CIPHER_SUITE_WEP104:
+			/* These ciphers do not use key sequence */
+			return -EINVAL;
+		case WLAN_CIPHER_SUITE_TKIP:
+		case WLAN_CIPHER_SUITE_CCMP:
+		case WLAN_CIPHER_SUITE_AES_CMAC:
+			if (params->seq_len != 6)
+				return -EINVAL;
+			break;
+		}
+	}
+
 	return 0;
 }
+
+/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
+/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
+const unsigned char rfc1042_header[] __aligned(2) =
+	{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+EXPORT_SYMBOL(rfc1042_header);
+
+/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
+const unsigned char bridge_tunnel_header[] __aligned(2) =
+	{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
+EXPORT_SYMBOL(bridge_tunnel_header);
+
+unsigned int ieee80211_hdrlen(__le16 fc)
+{
+	unsigned int hdrlen = 24;
+
+	if (ieee80211_is_data(fc)) {
+		if (ieee80211_has_a4(fc))
+			hdrlen = 30;
+		if (ieee80211_is_data_qos(fc))
+			hdrlen += IEEE80211_QOS_CTL_LEN;
+		goto out;
+	}
+
+	if (ieee80211_is_ctl(fc)) {
+		/*
+		 * ACK and CTS are 10 bytes, all others 16. To see how
+		 * to get this condition consider
+		 *   subtype mask:   0b0000000011110000 (0x00F0)
+		 *   ACK subtype:    0b0000000011010000 (0x00D0)
+		 *   CTS subtype:    0b0000000011000000 (0x00C0)
+		 *   bits that matter:         ^^^      (0x00E0)
+		 *   value of those: 0b0000000011000000 (0x00C0)
+		 */
+		if ((fc & cpu_to_le16(0x00E0)) == cpu_to_le16(0x00C0))
+			hdrlen = 10;
+		else
+			hdrlen = 16;
+	}
+out:
+	return hdrlen;
+}
+EXPORT_SYMBOL(ieee80211_hdrlen);
+
+unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
+{
+	const struct ieee80211_hdr *hdr =
+			(const struct ieee80211_hdr *)skb->data;
+	unsigned int hdrlen;
+
+	if (unlikely(skb->len < 10))
+		return 0;
+	hdrlen = ieee80211_hdrlen(hdr->frame_control);
+	if (unlikely(hdrlen > skb->len))
+		return 0;
+	return hdrlen;
+}
+EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
+
+int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
+{
+	int ae = meshhdr->flags & MESH_FLAGS_AE;
+	/* 7.1.3.5a.2 */
+	switch (ae) {
+	case 0:
+		return 6;
+	case 1:
+		return 12;
+	case 2:
+		return 18;
+	case 3:
+		return 24;
+	default:
+		return 6;
+	}
+}
+
+int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr,
+			   enum nl80211_iftype iftype)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+	u16 hdrlen, ethertype;
+	u8 *payload;
+	u8 dst[ETH_ALEN];
+	u8 src[ETH_ALEN] __aligned(2);
+
+	if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
+		return -1;
+
+	hdrlen = ieee80211_hdrlen(hdr->frame_control);
+
+	/* convert IEEE 802.11 header + possible LLC headers into Ethernet
+	 * header
+	 * IEEE 802.11 address fields:
+	 * ToDS FromDS Addr1 Addr2 Addr3 Addr4
+	 *   0     0   DA    SA    BSSID n/a
+	 *   0     1   DA    BSSID SA    n/a
+	 *   1     0   BSSID SA    DA    n/a
+	 *   1     1   RA    TA    DA    SA
+	 */
+	memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN);
+	memcpy(src, ieee80211_get_SA(hdr), ETH_ALEN);
+
+	switch (hdr->frame_control &
+		cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
+	case cpu_to_le16(IEEE80211_FCTL_TODS):
+		if (unlikely(iftype != NL80211_IFTYPE_AP &&
+			     iftype != NL80211_IFTYPE_AP_VLAN))
+			return -1;
+		break;
+	case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
+		if (unlikely(iftype != NL80211_IFTYPE_WDS &&
+			     iftype != NL80211_IFTYPE_MESH_POINT))
+			return -1;
+		if (iftype == NL80211_IFTYPE_MESH_POINT) {
+			struct ieee80211s_hdr *meshdr =
+				(struct ieee80211s_hdr *) (skb->data + hdrlen);
+			hdrlen += ieee80211_get_mesh_hdrlen(meshdr);
+			if (meshdr->flags & MESH_FLAGS_AE_A5_A6) {
+				memcpy(dst, meshdr->eaddr1, ETH_ALEN);
+				memcpy(src, meshdr->eaddr2, ETH_ALEN);
+			}
+		}
+		break;
+	case cpu_to_le16(IEEE80211_FCTL_FROMDS):
+		if (iftype != NL80211_IFTYPE_STATION ||
+		    (is_multicast_ether_addr(dst) &&
+		     !compare_ether_addr(src, addr)))
+			return -1;
+		break;
+	case cpu_to_le16(0):
+		if (iftype != NL80211_IFTYPE_ADHOC)
+			return -1;
+		break;
+	}
+
+	if (unlikely(skb->len - hdrlen < 8))
+		return -1;
+
+	payload = skb->data + hdrlen;
+	ethertype = (payload[6] << 8) | payload[7];
+
+	if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
+		    ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
+		   compare_ether_addr(payload, bridge_tunnel_header) == 0)) {
+		/* remove RFC1042 or Bridge-Tunnel encapsulation and
+		 * replace EtherType */
+		skb_pull(skb, hdrlen + 6);
+		memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
+		memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
+	} else {
+		struct ethhdr *ehdr;
+		__be16 len;
+
+		skb_pull(skb, hdrlen);
+		len = htons(skb->len);
+		ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
+		memcpy(ehdr->h_dest, dst, ETH_ALEN);
+		memcpy(ehdr->h_source, src, ETH_ALEN);
+		ehdr->h_proto = len;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_data_to_8023);
+
+int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr,
+			     enum nl80211_iftype iftype, u8 *bssid, bool qos)
+{
+	struct ieee80211_hdr hdr;
+	u16 hdrlen, ethertype;
+	__le16 fc;
+	const u8 *encaps_data;
+	int encaps_len, skip_header_bytes;
+	int nh_pos, h_pos;
+	int head_need;
+
+	if (unlikely(skb->len < ETH_HLEN))
+		return -EINVAL;
+
+	nh_pos = skb_network_header(skb) - skb->data;
+	h_pos = skb_transport_header(skb) - skb->data;
+
+	/* convert Ethernet header to proper 802.11 header (based on
+	 * operation mode) */
+	ethertype = (skb->data[12] << 8) | skb->data[13];
+	fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA);
+
+	switch (iftype) {
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_AP_VLAN:
+		fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
+		/* DA BSSID SA */
+		memcpy(hdr.addr1, skb->data, ETH_ALEN);
+		memcpy(hdr.addr2, addr, ETH_ALEN);
+		memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
+		hdrlen = 24;
+		break;
+	case NL80211_IFTYPE_STATION:
+		fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
+		/* BSSID SA DA */
+		memcpy(hdr.addr1, bssid, ETH_ALEN);
+		memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+		memcpy(hdr.addr3, skb->data, ETH_ALEN);
+		hdrlen = 24;
+		break;
+	case NL80211_IFTYPE_ADHOC:
+		/* DA SA BSSID */
+		memcpy(hdr.addr1, skb->data, ETH_ALEN);
+		memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+		memcpy(hdr.addr3, bssid, ETH_ALEN);
+		hdrlen = 24;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	if (qos) {
+		fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
+		hdrlen += 2;
+	}
+
+	hdr.frame_control = fc;
+	hdr.duration_id = 0;
+	hdr.seq_ctrl = 0;
+
+	skip_header_bytes = ETH_HLEN;
+	if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) {
+		encaps_data = bridge_tunnel_header;
+		encaps_len = sizeof(bridge_tunnel_header);
+		skip_header_bytes -= 2;
+	} else if (ethertype > 0x600) {
+		encaps_data = rfc1042_header;
+		encaps_len = sizeof(rfc1042_header);
+		skip_header_bytes -= 2;
+	} else {
+		encaps_data = NULL;
+		encaps_len = 0;
+	}
+
+	skb_pull(skb, skip_header_bytes);
+	nh_pos -= skip_header_bytes;
+	h_pos -= skip_header_bytes;
+
+	head_need = hdrlen + encaps_len - skb_headroom(skb);
+
+	if (head_need > 0 || skb_cloned(skb)) {
+		head_need = max(head_need, 0);
+		if (head_need)
+			skb_orphan(skb);
+
+		if (pskb_expand_head(skb, head_need, 0, GFP_ATOMIC)) {
+			printk(KERN_ERR "failed to reallocate Tx buffer\n");
+			return -ENOMEM;
+		}
+		skb->truesize += head_need;
+	}
+
+	if (encaps_data) {
+		memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len);
+		nh_pos += encaps_len;
+		h_pos += encaps_len;
+	}
+
+	memcpy(skb_push(skb, hdrlen), &hdr, hdrlen);
+
+	nh_pos += hdrlen;
+	h_pos += hdrlen;
+
+	/* Update skb pointers to various headers since this modified frame
+	 * is going to go through Linux networking code that may potentially
+	 * need things like pointer to IP header. */
+	skb_set_mac_header(skb, 0);
+	skb_set_network_header(skb, nh_pos);
+	skb_set_transport_header(skb, h_pos);
+
+	return 0;
+}
+EXPORT_SYMBOL(ieee80211_data_from_8023);
+
+/* Given a data frame determine the 802.1p/1d tag to use. */
+unsigned int cfg80211_classify8021d(struct sk_buff *skb)
+{
+	unsigned int dscp;
+
+	/* skb->priority values from 256->263 are magic values to
+	 * directly indicate a specific 802.1d priority.  This is used
+	 * to allow 802.1d priority to be passed directly in from VLAN
+	 * tags, etc.
+	 */
+	if (skb->priority >= 256 && skb->priority <= 263)
+		return skb->priority - 256;
+
+	switch (skb->protocol) {
+	case htons(ETH_P_IP):
+		dscp = ip_hdr(skb)->tos & 0xfc;
+		break;
+	default:
+		return 0;
+	}
+
+	return dscp >> 5;
+}
+EXPORT_SYMBOL(cfg80211_classify8021d);
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index f98090b..711e00a 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -504,6 +504,13 @@
 			else if (idx == wdev->wext.default_mgmt_key)
 				wdev->wext.default_mgmt_key = -1;
 		}
+		/*
+		 * Applications using wireless extensions expect to be
+		 * able to delete keys that don't exist, so allow that.
+		 */
+		if (err == -ENOENT)
+			return 0;
+
 		return err;
 	} else {
 		if (addr)
diff --git a/net/wireless/wext.c b/net/wireless/wext.c
index d3bbef7..22378da 100644
--- a/net/wireless/wext.c
+++ b/net/wireless/wext.c
@@ -636,8 +636,10 @@
 /*
  * Print info for /proc/net/wireless (print all entries)
  */
-static int wireless_seq_show(struct seq_file *seq, void *v)
+static int wireless_dev_seq_show(struct seq_file *seq, void *v)
 {
+	might_sleep();
+
 	if (v == SEQ_START_TOKEN)
 		seq_printf(seq, "Inter-| sta-|   Quality        |   Discarded "
 				"packets               | Missed | WE\n"
@@ -651,21 +653,41 @@
 
 static void *wireless_dev_seq_start(struct seq_file *seq, loff_t *pos)
 {
+	struct net *net = seq_file_net(seq);
+	loff_t off;
+	struct net_device *dev;
+
 	rtnl_lock();
-	return dev_seq_start(seq, pos);
+	if (!*pos)
+		return SEQ_START_TOKEN;
+
+	off = 1;
+	for_each_netdev(net, dev)
+		if (off++ == *pos)
+			return dev;
+	return NULL;
+}
+
+static void *wireless_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct net *net = seq_file_net(seq);
+
+	++*pos;
+
+	return v == SEQ_START_TOKEN ?
+		first_net_device(net) : next_net_device(v);
 }
 
 static void wireless_dev_seq_stop(struct seq_file *seq, void *v)
 {
-	dev_seq_stop(seq, v);
 	rtnl_unlock();
 }
 
 static const struct seq_operations wireless_seq_ops = {
 	.start = wireless_dev_seq_start,
-	.next  = dev_seq_next,
+	.next  = wireless_dev_seq_next,
 	.stop  = wireless_dev_seq_stop,
-	.show  = wireless_seq_show,
+	.show  = wireless_dev_seq_show,
 };
 
 static int seq_open_wireless(struct inode *inode, struct file *file)