Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6 into for-davem

Conflicts:
	drivers/net/wireless/libertas_tf/cmd.c
	drivers/net/wireless/libertas_tf/main.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 405aea0..a73b9b3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3956,6 +3956,7 @@
 F:	net/wireless/
 F:	include/net/ieee80211*
 F:	include/linux/wireless.h
+F:	include/linux/iw_handler.h
 F:	drivers/net/wireless/
 
 NETWORKING DRIVERS
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 77500cb..2fbe9b45 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -38,6 +38,12 @@
 	---help---
 	  A library for Marvell Libertas 8xxx devices using thinfirm.
 
+config LIBERTAS_THINFIRM_DEBUG
+	bool "Enable full debugging output in the Libertas thin firmware module."
+	depends on LIBERTAS_THINFIRM
+	---help---
+	  Debugging support.
+
 config LIBERTAS_THINFIRM_USB
 	tristate "Marvell Libertas 8388 USB 802.11b/g cards with thin firmware"
 	depends on LIBERTAS_THINFIRM && USB
diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c
index 0fb4199..7a626d4 100644
--- a/drivers/net/wireless/at76c50x-usb.c
+++ b/drivers/net/wireless/at76c50x-usb.c
@@ -1889,6 +1889,7 @@
 }
 
 static int at76_hw_scan(struct ieee80211_hw *hw,
+			struct ieee80211_vif *vif,
 			struct cfg80211_scan_request *req)
 {
 	struct at76_priv *priv = hw->priv;
diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c
index 0312cee..2e9b330 100644
--- a/drivers/net/wireless/ath/ar9170/main.c
+++ b/drivers/net/wireless/ath/ar9170/main.c
@@ -927,7 +927,6 @@
 
 	/* TODO: we could do something with phy_errors */
 	status->signal = ar->noise[0] + phy->rssi_combined;
-	status->noise = ar->noise[0];
 }
 
 static struct sk_buff *ar9170_rx_copy_data(u8 *buf, int len)
@@ -2548,8 +2547,7 @@
 					 BIT(NL80211_IFTYPE_ADHOC);
 	ar->hw->flags |= IEEE80211_HW_RX_INCLUDES_FCS |
 			 IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
-			 IEEE80211_HW_SIGNAL_DBM |
-			 IEEE80211_HW_NOISE_DBM;
+			 IEEE80211_HW_SIGNAL_DBM;
 
 	if (modparam_ht) {
 		ar->hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index c274979a..7f5953f 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -548,8 +548,7 @@
 	SET_IEEE80211_DEV(hw, &pdev->dev);
 	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
 		    IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
-		    IEEE80211_HW_SIGNAL_DBM |
-		    IEEE80211_HW_NOISE_DBM;
+		    IEEE80211_HW_SIGNAL_DBM;
 
 	hw->wiphy->interface_modes =
 		BIT(NL80211_IFTYPE_AP) |
@@ -2030,8 +2029,7 @@
 		rxs->freq = sc->curchan->center_freq;
 		rxs->band = sc->curband->band;
 
-		rxs->noise = sc->ah->ah_noise_floor;
-		rxs->signal = rxs->noise + rs.rs_rssi;
+		rxs->signal = sc->ah->ah_noise_floor + rs.rs_rssi;
 
 		rxs->antenna = rs.rs_antenna;
 
diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c
index cec62d3..ba8b20f 100644
--- a/drivers/net/wireless/ath/ath9k/ani.c
+++ b/drivers/net/wireless/ath/ath9k/ani.c
@@ -657,12 +657,3 @@
 	if (ah->config.enable_ani)
 		ah->proc_phyerr |= HAL_PROCESS_ANI;
 }
-
-void ath9k_hw_ani_disable(struct ath_hw *ah)
-{
-	ath_print(ath9k_hw_common(ah), ATH_DBG_ANI, "Disabling ANI\n");
-
-	ath9k_hw_disable_mib_counters(ah);
-	REG_WRITE(ah, AR_PHY_ERR_1, 0);
-	REG_WRITE(ah, AR_PHY_ERR_2, 0);
-}
diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h
index 4e1ab94..3356762 100644
--- a/drivers/net/wireless/ath/ath9k/ani.h
+++ b/drivers/net/wireless/ath/ath9k/ani.h
@@ -118,6 +118,5 @@
 void ath9k_hw_procmibevent(struct ath_hw *ah);
 void ath9k_hw_ani_setup(struct ath_hw *ah);
 void ath9k_hw_ani_init(struct ath_hw *ah);
-void ath9k_hw_ani_disable(struct ath_hw *ah);
 
 #endif /* ANI_H */
diff --git a/drivers/net/wireless/ath/ath9k/ar5008_initvals.h b/drivers/net/wireless/ath/ath9k/ar5008_initvals.h
index cd953f6..025c31ac 100644
--- a/drivers/net/wireless/ath/ath9k/ar5008_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar5008_initvals.h
@@ -249,7 +249,7 @@
     { 0x00008258, 0x00000000 },
     { 0x0000825c, 0x400000ff },
     { 0x00008260, 0x00080922 },
-    { 0x00008264, 0xa8000010 },
+    { 0x00008264, 0x88000010 },
     { 0x00008270, 0x00000000 },
     { 0x00008274, 0x40000000 },
     { 0x00008278, 0x003e4180 },
diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
index de8ce12..b2c17c9 100644
--- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
@@ -850,7 +850,7 @@
 
 	REG_WRITE_ARRAY(&ah->iniBB_RfGain, freqIndex, regWrites);
 
-	if (AR_SREV_9280_20(ah) && IS_CHAN_A_5MHZ_SPACED(chan)) {
+	if (IS_CHAN_A_FAST_CLOCK(ah, chan)) {
 		REG_WRITE_ARRAY(&ah->iniModesAdditional, modesIndex,
 				regWrites);
 	}
@@ -892,8 +892,7 @@
 		rfMode |= (IS_CHAN_5GHZ(chan)) ?
 			AR_PHY_MODE_RF5GHZ : AR_PHY_MODE_RF2GHZ;
 
-	if ((AR_SREV_9280_20(ah) || AR_SREV_9300_20_OR_LATER(ah))
-	    && IS_CHAN_A_5MHZ_SPACED(chan))
+	if (IS_CHAN_A_FAST_CLOCK(ah, chan))
 		rfMode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE);
 
 	REG_WRITE(ah, AR_PHY_MODE, rfMode);
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
index adb33b3..a8a8cdc 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
@@ -408,6 +408,8 @@
 			REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
 
 		} else {
+			ENABLE_REGWRITE_BUFFER(ah);
+
 			REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00);
 			REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924);
 
@@ -428,6 +430,9 @@
 
 			/* Load the new settings */
 			REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
+
+			REGWRITE_BUFFER_FLUSH(ah);
+			DISABLE_REGWRITE_BUFFER(ah);
 		}
 
 		udelay(1000);
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_initvals.h b/drivers/net/wireless/ath/ath9k/ar9002_initvals.h
index f06313d..dae7f33 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar9002_initvals.h
@@ -793,7 +793,7 @@
     { 0x00008258, 0x00000000 },
     { 0x0000825c, 0x400000ff },
     { 0x00008260, 0x00080922 },
-    { 0x00008264, 0xa8a00010 },
+    { 0x00008264, 0x88a00010 },
     { 0x00008270, 0x00000000 },
     { 0x00008274, 0x40000000 },
     { 0x00008278, 0x003e4180 },
@@ -1963,7 +1963,7 @@
     { 0x00008258, 0x00000000 },
     { 0x0000825c, 0x400000ff },
     { 0x00008260, 0x00080922 },
-    { 0x00008264, 0xa8a00010 },
+    { 0x00008264, 0x88a00010 },
     { 0x00008270, 0x00000000 },
     { 0x00008274, 0x40000000 },
     { 0x00008278, 0x003e4180 },
@@ -3185,7 +3185,7 @@
     { 0x00008258, 0x00000000 },
     { 0x0000825c, 0x400000ff },
     { 0x00008260, 0x00080922 },
-    { 0x00008264, 0xa8a00010 },
+    { 0x00008264, 0x88a00010 },
     { 0x00008270, 0x00000000 },
     { 0x00008274, 0x40000000 },
     { 0x00008278, 0x003e4180 },
@@ -4973,7 +4973,7 @@
     { 0x00008258, 0x00000000 },
     { 0x0000825c, 0x400000ff },
     { 0x00008260, 0x00080922 },
-    { 0x00008264, 0xa8a00010 },
+    { 0x00008264, 0x88a00010 },
     { 0x00008270, 0x00000000 },
     { 0x00008274, 0x40000000 },
     { 0x00008278, 0x003e4180 },
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c
index 18cfe1a..ed314e8 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c
@@ -455,16 +455,12 @@
 		pll |= SM(0x2, AR_RTC_9160_PLL_CLKSEL);
 
 	if (chan && IS_CHAN_5GHZ(chan)) {
-		pll |= SM(0x28, AR_RTC_9160_PLL_DIV);
-
-
-		if (AR_SREV_9280_20(ah)) {
-			if (((chan->channel % 20) == 0)
-			    || ((chan->channel % 10) == 0))
-				pll = 0x2850;
-			else
-				pll = 0x142c;
-		}
+		if (IS_CHAN_A_FAST_CLOCK(ah, chan))
+			pll = 0x142c;
+		else if (AR_SREV_9280_20(ah))
+			pll = 0x2850;
+		else
+			pll |= SM(0x28, AR_RTC_9160_PLL_DIV);
 	} else {
 		pll |= SM(0x2c, AR_RTC_9160_PLL_DIV);
 	}
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
index 5e20b48..5fcafb4 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
@@ -755,7 +755,8 @@
 	}
 
 	/* Do Tx IQ Calibration */
-	ar9003_hw_tx_iq_cal(ah);
+	if (ah->config.tx_iq_calibration)
+		ar9003_hw_tx_iq_cal(ah);
 
 	/* Revert chainmasks to their original values before NF cal */
 	ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index 5d92be4..8a79550 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -659,6 +659,9 @@
 	word = swab16(eep->baseEepHeader.regDmn[1]);
 	eep->baseEepHeader.regDmn[1] = word;
 
+	dword = swab32(eep->baseEepHeader.swreg);
+	eep->baseEepHeader.swreg = dword;
+
 	dword = swab32(eep->modalHeader2G.antCtrlCommon);
 	eep->modalHeader2G.antCtrlCommon = dword;
 
@@ -1200,7 +1203,7 @@
 	u8 *pFreqBin;
 
 	if (is2GHz) {
-		numPiers = AR9300_NUM_5G_20_TARGET_POWERS;
+		numPiers = AR9300_NUM_2G_20_TARGET_POWERS;
 		pEepromTargetPwr = eep->calTargetPower2G;
 		pFreqBin = eep->calTarget_freqbin_2G;
 	} else {
@@ -1236,7 +1239,7 @@
 	u8 *pFreqBin;
 
 	if (is2GHz) {
-		numPiers = AR9300_NUM_5G_20_TARGET_POWERS;
+		numPiers = AR9300_NUM_2G_20_TARGET_POWERS;
 		pEepromTargetPwr = eep->calTargetPower2GHT20;
 		pFreqBin = eep->calTarget_freqbin_2GHT20;
 	} else {
@@ -1817,6 +1820,7 @@
 					u8 twiceMaxRegulatoryPower,
 					u8 powerLimit)
 {
+	ah->txpower_limit = powerLimit;
 	ar9003_hw_set_target_power_eeprom(ah, chan->channel);
 	ar9003_hw_calibration_apply(ah, chan->channel);
 }
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
index 5fe335e..d8c0318 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
@@ -265,7 +265,7 @@
 } __packed;
 
 struct cal_ctl_data_2g {
-	struct cal_ctl_edge_pwr ctlEdges[AR9300_NUM_BAND_EDGES_5G];
+	struct cal_ctl_edge_pwr ctlEdges[AR9300_NUM_BAND_EDGES_2G];
 } __packed;
 
 struct cal_ctl_data_5g {
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_initvals.h b/drivers/net/wireless/ath/ath9k/ar9003_initvals.h
index e0391b1..a131cd1 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_initvals.h
@@ -31,7 +31,7 @@
 
 static const u32 ar9300Modes_lowest_ob_db_tx_gain_table_2p0[][5] = {
 	/* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
-	{0x0000a410, 0x000050da, 0x000050da, 0x000050da, 0x000050da},
+	{0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
 	{0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
 	{0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002},
 	{0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004},
@@ -545,7 +545,7 @@
 };
 
 static const u32 ar9200_merlin_2p0_radio_core[][2] = {
-	/* Addr      common    */
+	/* Addr      allmodes  */
 	{0x00007800, 0x00040000},
 	{0x00007804, 0xdb005012},
 	{0x00007808, 0x04924914},
@@ -835,71 +835,71 @@
 
 static const u32 ar9300Modes_high_power_tx_gain_table_2p0[][5] = {
 	/* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
-	{0x0000a410, 0x000050d9, 0x000050d9, 0x000050da, 0x000050da},
+	{0x0000a410, 0x000050db, 0x000050db, 0x000050d9, 0x000050d9},
 	{0x0000a500, 0x00020220, 0x00020220, 0x00000000, 0x00000000},
 	{0x0000a504, 0x06020223, 0x06020223, 0x04000002, 0x04000002},
-	{0x0000a508, 0x0b022220, 0x0b022220, 0x08000004, 0x08000004},
-	{0x0000a50c, 0x10022223, 0x10022223, 0x0b000200, 0x0b000200},
-	{0x0000a510, 0x17022620, 0x17022620, 0x0f000202, 0x0f000202},
-	{0x0000a514, 0x1b022622, 0x1b022622, 0x11000400, 0x11000400},
-	{0x0000a518, 0x1f022822, 0x1f022822, 0x15000402, 0x15000402},
-	{0x0000a51c, 0x24022842, 0x24022842, 0x19000404, 0x19000404},
-	{0x0000a520, 0x28042840, 0x28042840, 0x1b000603, 0x1b000603},
-	{0x0000a524, 0x2c042842, 0x2c042842, 0x1f000a02, 0x1f000a02},
-	{0x0000a528, 0x30042844, 0x30042844, 0x23000a04, 0x23000a04},
-	{0x0000a52c, 0x34042846, 0x34042846, 0x26000a20, 0x26000a20},
-	{0x0000a530, 0x39042869, 0x39042869, 0x2a000e20, 0x2a000e20},
-	{0x0000a534, 0x3d062869, 0x3d062869, 0x2e000e22, 0x2e000e22},
-	{0x0000a538, 0x44062c69, 0x44062c69, 0x31000e24, 0x31000e24},
-	{0x0000a53c, 0x48063069, 0x48063069, 0x34001640, 0x34001640},
-	{0x0000a540, 0x4c0a3065, 0x4c0a3065, 0x38001660, 0x38001660},
-	{0x0000a544, 0x500a3069, 0x500a3069, 0x3b001861, 0x3b001861},
-	{0x0000a548, 0x530a3469, 0x530a3469, 0x3e001a81, 0x3e001a81},
-	{0x0000a54c, 0x590a7464, 0x590a7464, 0x42001a83, 0x42001a83},
-	{0x0000a550, 0x5e0a7865, 0x5e0a7865, 0x44001c84, 0x44001c84},
-	{0x0000a554, 0x630a7e66, 0x630a7e66, 0x48001ce3, 0x48001ce3},
-	{0x0000a558, 0x680a7e89, 0x680a7e89, 0x4c001ce5, 0x4c001ce5},
-	{0x0000a55c, 0x6e0a7e8c, 0x6e0a7e8c, 0x50001ce9, 0x50001ce9},
-	{0x0000a560, 0x730e7e8c, 0x730e7e8c, 0x54001ceb, 0x54001ceb},
-	{0x0000a564, 0x730e7e8c, 0x730e7e8c, 0x56001eec, 0x56001eec},
-	{0x0000a568, 0x730e7e8c, 0x730e7e8c, 0x56001eec, 0x56001eec},
-	{0x0000a56c, 0x730e7e8c, 0x730e7e8c, 0x56001eec, 0x56001eec},
-	{0x0000a570, 0x730e7e8c, 0x730e7e8c, 0x56001eec, 0x56001eec},
-	{0x0000a574, 0x730e7e8c, 0x730e7e8c, 0x56001eec, 0x56001eec},
-	{0x0000a578, 0x730e7e8c, 0x730e7e8c, 0x56001eec, 0x56001eec},
-	{0x0000a57c, 0x730e7e8c, 0x730e7e8c, 0x56001eec, 0x56001eec},
+	{0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004},
+	{0x0000a50c, 0x0f022223, 0x0f022223, 0x0b000200, 0x0b000200},
+	{0x0000a510, 0x14022620, 0x14022620, 0x0f000202, 0x0f000202},
+	{0x0000a514, 0x18022622, 0x18022622, 0x11000400, 0x11000400},
+	{0x0000a518, 0x1b022822, 0x1b022822, 0x15000402, 0x15000402},
+	{0x0000a51c, 0x20022842, 0x20022842, 0x19000404, 0x19000404},
+	{0x0000a520, 0x22022c41, 0x22022c41, 0x1b000603, 0x1b000603},
+	{0x0000a524, 0x28023042, 0x28023042, 0x1f000a02, 0x1f000a02},
+	{0x0000a528, 0x2c023044, 0x2c023044, 0x23000a04, 0x23000a04},
+	{0x0000a52c, 0x2f023644, 0x2f023644, 0x26000a20, 0x26000a20},
+	{0x0000a530, 0x34043643, 0x34043643, 0x2a000e20, 0x2a000e20},
+	{0x0000a534, 0x38043a44, 0x38043a44, 0x2e000e22, 0x2e000e22},
+	{0x0000a538, 0x3b043e45, 0x3b043e45, 0x31000e24, 0x31000e24},
+	{0x0000a53c, 0x40063e46, 0x40063e46, 0x34001640, 0x34001640},
+	{0x0000a540, 0x44083e46, 0x44083e46, 0x38001660, 0x38001660},
+	{0x0000a544, 0x46083e66, 0x46083e66, 0x3b001861, 0x3b001861},
+	{0x0000a548, 0x4b0a3e69, 0x4b0a3e69, 0x3e001a81, 0x3e001a81},
+	{0x0000a54c, 0x4f0a5e66, 0x4f0a5e66, 0x42001a83, 0x42001a83},
+	{0x0000a550, 0x540a7e66, 0x540a7e66, 0x44001c84, 0x44001c84},
+	{0x0000a554, 0x570a7e89, 0x570a7e89, 0x48001ce3, 0x48001ce3},
+	{0x0000a558, 0x5c0e7e8a, 0x5c0e7e8a, 0x4c001ce5, 0x4c001ce5},
+	{0x0000a55c, 0x60127e8b, 0x60127e8b, 0x50001ce9, 0x50001ce9},
+	{0x0000a560, 0x65127ecc, 0x65127ecc, 0x54001ceb, 0x54001ceb},
+	{0x0000a564, 0x6b169ecd, 0x6b169ecd, 0x56001eec, 0x56001eec},
+	{0x0000a568, 0x70169f0e, 0x70169f0e, 0x56001eec, 0x56001eec},
+	{0x0000a56c, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
+	{0x0000a570, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
+	{0x0000a574, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
+	{0x0000a578, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
+	{0x0000a57c, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
 	{0x0000a580, 0x00820220, 0x00820220, 0x00800000, 0x00800000},
 	{0x0000a584, 0x06820223, 0x06820223, 0x04800002, 0x04800002},
-	{0x0000a588, 0x0b822220, 0x0b822220, 0x08800004, 0x08800004},
-	{0x0000a58c, 0x10822223, 0x10822223, 0x0b800200, 0x0b800200},
-	{0x0000a590, 0x17822620, 0x17822620, 0x0f800202, 0x0f800202},
-	{0x0000a594, 0x1b822622, 0x1b822622, 0x11800400, 0x11800400},
-	{0x0000a598, 0x1f822822, 0x1f822822, 0x15800402, 0x15800402},
-	{0x0000a59c, 0x24822842, 0x24822842, 0x19800404, 0x19800404},
-	{0x0000a5a0, 0x28842840, 0x28842840, 0x1b800603, 0x1b800603},
-	{0x0000a5a4, 0x2c842842, 0x2c842842, 0x1f800a02, 0x1f800a02},
-	{0x0000a5a8, 0x30842844, 0x30842844, 0x23800a04, 0x23800a04},
-	{0x0000a5ac, 0x34842846, 0x34842846, 0x26800a20, 0x26800a20},
-	{0x0000a5b0, 0x39842869, 0x39842869, 0x2a800e20, 0x2a800e20},
-	{0x0000a5b4, 0x3d862869, 0x3d862869, 0x2e800e22, 0x2e800e22},
-	{0x0000a5b8, 0x44862c69, 0x44862c69, 0x31800e24, 0x31800e24},
-	{0x0000a5bc, 0x48863069, 0x48863069, 0x34801640, 0x34801640},
-	{0x0000a5c0, 0x4c8a3065, 0x4c8a3065, 0x38801660, 0x38801660},
-	{0x0000a5c4, 0x508a3069, 0x508a3069, 0x3b801861, 0x3b801861},
-	{0x0000a5c8, 0x538a3469, 0x538a3469, 0x3e801a81, 0x3e801a81},
-	{0x0000a5cc, 0x598a7464, 0x598a7464, 0x42801a83, 0x42801a83},
-	{0x0000a5d0, 0x5e8a7865, 0x5e8a7865, 0x44801c84, 0x44801c84},
-	{0x0000a5d4, 0x638a7e66, 0x638a7e66, 0x48801ce3, 0x48801ce3},
-	{0x0000a5d8, 0x688a7e89, 0x688a7e89, 0x4c801ce5, 0x4c801ce5},
-	{0x0000a5dc, 0x6e8a7e8c, 0x6e8a7e8c, 0x50801ce9, 0x50801ce9},
-	{0x0000a5e0, 0x738e7e8c, 0x738e7e8c, 0x54801ceb, 0x54801ceb},
-	{0x0000a5e4, 0x738e7e8c, 0x738e7e8c, 0x56801eec, 0x56801eec},
-	{0x0000a5e8, 0x738e7e8c, 0x738e7e8c, 0x56801eec, 0x56801eec},
-	{0x0000a5ec, 0x738e7e8c, 0x738e7e8c, 0x56801eec, 0x56801eec},
-	{0x0000a5f0, 0x738e7e8c, 0x738e7e8c, 0x56801eec, 0x56801eec},
-	{0x0000a5f4, 0x738e7e8c, 0x738e7e8c, 0x56801eec, 0x56801eec},
-	{0x0000a5f8, 0x738e7e8c, 0x738e7e8c, 0x56801eec, 0x56801eec},
-	{0x0000a5fc, 0x738e7e8c, 0x738e7e8c, 0x56801eec, 0x56801eec},
+	{0x0000a588, 0x0a822220, 0x0a822220, 0x08800004, 0x08800004},
+	{0x0000a58c, 0x0f822223, 0x0f822223, 0x0b800200, 0x0b800200},
+	{0x0000a590, 0x14822620, 0x14822620, 0x0f800202, 0x0f800202},
+	{0x0000a594, 0x18822622, 0x18822622, 0x11800400, 0x11800400},
+	{0x0000a598, 0x1b822822, 0x1b822822, 0x15800402, 0x15800402},
+	{0x0000a59c, 0x20822842, 0x20822842, 0x19800404, 0x19800404},
+	{0x0000a5a0, 0x22822c41, 0x22822c41, 0x1b800603, 0x1b800603},
+	{0x0000a5a4, 0x28823042, 0x28823042, 0x1f800a02, 0x1f800a02},
+	{0x0000a5a8, 0x2c823044, 0x2c823044, 0x23800a04, 0x23800a04},
+	{0x0000a5ac, 0x2f823644, 0x2f823644, 0x26800a20, 0x26800a20},
+	{0x0000a5b0, 0x34843643, 0x34843643, 0x2a800e20, 0x2a800e20},
+	{0x0000a5b4, 0x38843a44, 0x38843a44, 0x2e800e22, 0x2e800e22},
+	{0x0000a5b8, 0x3b843e45, 0x3b843e45, 0x31800e24, 0x31800e24},
+	{0x0000a5bc, 0x40863e46, 0x40863e46, 0x34801640, 0x34801640},
+	{0x0000a5c0, 0x4c8a3065, 0x44883e46, 0x44883e46, 0x38801660},
+	{0x0000a5c4, 0x46883e66, 0x46883e66, 0x3b801861, 0x3b801861},
+	{0x0000a5c8, 0x4b8a3e69, 0x4b8a3e69, 0x3e801a81, 0x3e801a81},
+	{0x0000a5cc, 0x4f8a5e66, 0x4f8a5e66, 0x42801a83, 0x42801a83},
+	{0x0000a5d0, 0x548a7e66, 0x548a7e66, 0x44801c84, 0x44801c84},
+	{0x0000a5d4, 0x578a7e89, 0x578a7e89, 0x48801ce3, 0x48801ce3},
+	{0x0000a5d8, 0x5c8e7e8a, 0x5c8e7e8a, 0x4c801ce5, 0x4c801ce5},
+	{0x0000a5dc, 0x60927e8b, 0x60927e8b, 0x50801ce9, 0x50801ce9},
+	{0x0000a5e0, 0x65927ecc, 0x65927ecc, 0x54801ceb, 0x54801ceb},
+	{0x0000a5e4, 0x6b969ecd, 0x6b969ecd, 0x56801eec, 0x56801eec},
+	{0x0000a5e8, 0x70969f0e, 0x70969f0e, 0x56801eec, 0x56801eec},
+	{0x0000a5ec, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
+	{0x0000a5f0, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
+	{0x0000a5f4, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
+	{0x0000a5f8, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
+	{0x0000a5fc, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
 	{0x00016044, 0x056db2e6, 0x056db2e6, 0x056db2e6, 0x056db2e6},
 	{0x00016048, 0xad241a61, 0xad241a61, 0xad241a61, 0xad241a61},
 	{0x00016068, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c},
@@ -913,71 +913,71 @@
 
 static const u32 ar9300Modes_high_ob_db_tx_gain_table_2p0[][5] = {
 	/* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
-	{0x0000a410, 0x000050d9, 0x000050d9, 0x000050da, 0x000050da},
+	{0x0000a410, 0x000050db, 0x000050db, 0x000050d9, 0x000050d9},
 	{0x0000a500, 0x00020220, 0x00020220, 0x00000000, 0x00000000},
 	{0x0000a504, 0x06020223, 0x06020223, 0x04000002, 0x04000002},
-	{0x0000a508, 0x0b022220, 0x0b022220, 0x08000004, 0x08000004},
-	{0x0000a50c, 0x10022223, 0x10022223, 0x0b000200, 0x0b000200},
-	{0x0000a510, 0x17022620, 0x17022620, 0x0f000202, 0x0f000202},
-	{0x0000a514, 0x1b022622, 0x1b022622, 0x11000400, 0x11000400},
-	{0x0000a518, 0x1f022822, 0x1f022822, 0x15000402, 0x15000402},
-	{0x0000a51c, 0x24022842, 0x24022842, 0x19000404, 0x19000404},
-	{0x0000a520, 0x28042840, 0x28042840, 0x1b000603, 0x1b000603},
-	{0x0000a524, 0x2c042842, 0x2c042842, 0x1f000a02, 0x1f000a02},
-	{0x0000a528, 0x30042844, 0x30042844, 0x23000a04, 0x23000a04},
-	{0x0000a52c, 0x34042846, 0x34042846, 0x26000a20, 0x26000a20},
-	{0x0000a530, 0x39042869, 0x39042869, 0x2a000e20, 0x2a000e20},
-	{0x0000a534, 0x3d062869, 0x3d062869, 0x2e000e22, 0x2e000e22},
-	{0x0000a538, 0x44062c69, 0x44062c69, 0x31000e24, 0x31000e24},
-	{0x0000a53c, 0x48063069, 0x48063069, 0x34001640, 0x34001640},
-	{0x0000a540, 0x4c0a3065, 0x4c0a3065, 0x38001660, 0x38001660},
-	{0x0000a544, 0x500a3069, 0x500a3069, 0x3b001861, 0x3b001861},
-	{0x0000a548, 0x530a3469, 0x530a3469, 0x3e001a81, 0x3e001a81},
-	{0x0000a54c, 0x590a7464, 0x590a7464, 0x42001a83, 0x42001a83},
-	{0x0000a550, 0x5e0a7865, 0x5e0a7865, 0x44001c84, 0x44001c84},
-	{0x0000a554, 0x630a7e66, 0x630a7e66, 0x48001ce3, 0x48001ce3},
-	{0x0000a558, 0x680a7e89, 0x680a7e89, 0x4c001ce5, 0x4c001ce5},
-	{0x0000a55c, 0x6e0a7e8c, 0x6e0a7e8c, 0x50001ce9, 0x50001ce9},
-	{0x0000a560, 0x730e7e8c, 0x730e7e8c, 0x54001ceb, 0x54001ceb},
-	{0x0000a564, 0x730e7e8c, 0x730e7e8c, 0x56001eec, 0x56001eec},
-	{0x0000a568, 0x730e7e8c, 0x730e7e8c, 0x56001eec, 0x56001eec},
-	{0x0000a56c, 0x730e7e8c, 0x730e7e8c, 0x56001eec, 0x56001eec},
-	{0x0000a570, 0x730e7e8c, 0x730e7e8c, 0x56001eec, 0x56001eec},
-	{0x0000a574, 0x730e7e8c, 0x730e7e8c, 0x56001eec, 0x56001eec},
-	{0x0000a578, 0x730e7e8c, 0x730e7e8c, 0x56001eec, 0x56001eec},
-	{0x0000a57c, 0x730e7e8c, 0x730e7e8c, 0x56001eec, 0x56001eec},
+	{0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004},
+	{0x0000a50c, 0x0f022223, 0x0f022223, 0x0b000200, 0x0b000200},
+	{0x0000a510, 0x14022620, 0x14022620, 0x0f000202, 0x0f000202},
+	{0x0000a514, 0x18022622, 0x18022622, 0x11000400, 0x11000400},
+	{0x0000a518, 0x1b022822, 0x1b022822, 0x15000402, 0x15000402},
+	{0x0000a51c, 0x20022842, 0x20022842, 0x19000404, 0x19000404},
+	{0x0000a520, 0x22022c41, 0x22022c41, 0x1b000603, 0x1b000603},
+	{0x0000a524, 0x28023042, 0x28023042, 0x1f000a02, 0x1f000a02},
+	{0x0000a528, 0x2c023044, 0x2c023044, 0x23000a04, 0x23000a04},
+	{0x0000a52c, 0x2f023644, 0x2f023644, 0x26000a20, 0x26000a20},
+	{0x0000a530, 0x34043643, 0x34043643, 0x2a000e20, 0x2a000e20},
+	{0x0000a534, 0x38043a44, 0x38043a44, 0x2e000e22, 0x2e000e22},
+	{0x0000a538, 0x3b043e45, 0x3b043e45, 0x31000e24, 0x31000e24},
+	{0x0000a53c, 0x40063e46, 0x40063e46, 0x34001640, 0x34001640},
+	{0x0000a540, 0x44083e46, 0x44083e46, 0x38001660, 0x38001660},
+	{0x0000a544, 0x46083e66, 0x46083e66, 0x3b001861, 0x3b001861},
+	{0x0000a548, 0x4b0a3e69, 0x4b0a3e69, 0x3e001a81, 0x3e001a81},
+	{0x0000a54c, 0x4f0a5e66, 0x4f0a5e66, 0x42001a83, 0x42001a83},
+	{0x0000a550, 0x540a7e66, 0x540a7e66, 0x44001c84, 0x44001c84},
+	{0x0000a554, 0x570a7e89, 0x570a7e89, 0x48001ce3, 0x48001ce3},
+	{0x0000a558, 0x5c0e7e8a, 0x5c0e7e8a, 0x4c001ce5, 0x4c001ce5},
+	{0x0000a55c, 0x60127e8b, 0x60127e8b, 0x50001ce9, 0x50001ce9},
+	{0x0000a560, 0x65127ecc, 0x65127ecc, 0x54001ceb, 0x54001ceb},
+	{0x0000a564, 0x6b169ecd, 0x6b169ecd, 0x56001eec, 0x56001eec},
+	{0x0000a568, 0x70169f0e, 0x70169f0e, 0x56001eec, 0x56001eec},
+	{0x0000a56c, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
+	{0x0000a570, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
+	{0x0000a574, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
+	{0x0000a578, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
+	{0x0000a57c, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
 	{0x0000a580, 0x00820220, 0x00820220, 0x00800000, 0x00800000},
 	{0x0000a584, 0x06820223, 0x06820223, 0x04800002, 0x04800002},
-	{0x0000a588, 0x0b822220, 0x0b822220, 0x08800004, 0x08800004},
-	{0x0000a58c, 0x10822223, 0x10822223, 0x0b800200, 0x0b800200},
-	{0x0000a590, 0x17822620, 0x17822620, 0x0f800202, 0x0f800202},
-	{0x0000a594, 0x1b822622, 0x1b822622, 0x11800400, 0x11800400},
-	{0x0000a598, 0x1f822822, 0x1f822822, 0x15800402, 0x15800402},
-	{0x0000a59c, 0x24822842, 0x24822842, 0x19800404, 0x19800404},
-	{0x0000a5a0, 0x28842840, 0x28842840, 0x1b800603, 0x1b800603},
-	{0x0000a5a4, 0x2c842842, 0x2c842842, 0x1f800a02, 0x1f800a02},
-	{0x0000a5a8, 0x30842844, 0x30842844, 0x23800a04, 0x23800a04},
-	{0x0000a5ac, 0x34842846, 0x34842846, 0x26800a20, 0x26800a20},
-	{0x0000a5b0, 0x39842869, 0x39842869, 0x2a800e20, 0x2a800e20},
-	{0x0000a5b4, 0x3d862869, 0x3d862869, 0x2e800e22, 0x2e800e22},
-	{0x0000a5b8, 0x44862c69, 0x44862c69, 0x31800e24, 0x31800e24},
-	{0x0000a5bc, 0x48863069, 0x48863069, 0x34801640, 0x34801640},
-	{0x0000a5c0, 0x4c8a3065, 0x4c8a3065, 0x38801660, 0x38801660},
-	{0x0000a5c4, 0x508a3069, 0x508a3069, 0x3b801861, 0x3b801861},
-	{0x0000a5c8, 0x538a3469, 0x538a3469, 0x3e801a81, 0x3e801a81},
-	{0x0000a5cc, 0x598a7464, 0x598a7464, 0x42801a83, 0x42801a83},
-	{0x0000a5d0, 0x5e8a7865, 0x5e8a7865, 0x44801c84, 0x44801c84},
-	{0x0000a5d4, 0x638a7e66, 0x638a7e66, 0x48801ce3, 0x48801ce3},
-	{0x0000a5d8, 0x688a7e89, 0x688a7e89, 0x4c801ce5, 0x4c801ce5},
-	{0x0000a5dc, 0x6e8a7e8c, 0x6e8a7e8c, 0x50801ce9, 0x50801ce9},
-	{0x0000a5e0, 0x738e7e8c, 0x738e7e8c, 0x54801ceb, 0x54801ceb},
-	{0x0000a5e4, 0x738e7e8c, 0x738e7e8c, 0x56801eec, 0x56801eec},
-	{0x0000a5e8, 0x738e7e8c, 0x738e7e8c, 0x56801eec, 0x56801eec},
-	{0x0000a5ec, 0x738e7e8c, 0x738e7e8c, 0x56801eec, 0x56801eec},
-	{0x0000a5f0, 0x738e7e8c, 0x738e7e8c, 0x56801eec, 0x56801eec},
-	{0x0000a5f4, 0x738e7e8c, 0x738e7e8c, 0x56801eec, 0x56801eec},
-	{0x0000a5f8, 0x738e7e8c, 0x738e7e8c, 0x56801eec, 0x56801eec},
-	{0x0000a5fc, 0x738e7e8c, 0x738e7e8c, 0x56801eec, 0x56801eec},
+	{0x0000a588, 0x0a822220, 0x0a822220, 0x08800004, 0x08800004},
+	{0x0000a58c, 0x0f822223, 0x0f822223, 0x0b800200, 0x0b800200},
+	{0x0000a590, 0x14822620, 0x14822620, 0x0f800202, 0x0f800202},
+	{0x0000a594, 0x18822622, 0x18822622, 0x11800400, 0x11800400},
+	{0x0000a598, 0x1b822822, 0x1b822822, 0x15800402, 0x15800402},
+	{0x0000a59c, 0x20822842, 0x20822842, 0x19800404, 0x19800404},
+	{0x0000a5a0, 0x22822c41, 0x22822c41, 0x1b800603, 0x1b800603},
+	{0x0000a5a4, 0x28823042, 0x28823042, 0x1f800a02, 0x1f800a02},
+	{0x0000a5a8, 0x2c823044, 0x2c823044, 0x23800a04, 0x23800a04},
+	{0x0000a5ac, 0x2f823644, 0x2f823644, 0x26800a20, 0x26800a20},
+	{0x0000a5b0, 0x34843643, 0x34843643, 0x2a800e20, 0x2a800e20},
+	{0x0000a5b4, 0x38843a44, 0x38843a44, 0x2e800e22, 0x2e800e22},
+	{0x0000a5b8, 0x3b843e45, 0x3b843e45, 0x31800e24, 0x31800e24},
+	{0x0000a5bc, 0x40863e46, 0x40863e46, 0x34801640, 0x34801640},
+	{0x0000a5c0, 0x44883e46, 0x44883e46, 0x38801660, 0x38801660},
+	{0x0000a5c4, 0x46883e66, 0x46883e66, 0x3b801861, 0x3b801861},
+	{0x0000a5c8, 0x4b8a3e69, 0x4b8a3e69, 0x3e801a81, 0x3e801a81},
+	{0x0000a5cc, 0x4f8a5e66, 0x4f8a5e66, 0x42801a83, 0x42801a83},
+	{0x0000a5d0, 0x548a7e66, 0x548a7e66, 0x44801c84, 0x44801c84},
+	{0x0000a5d4, 0x578a7e89, 0x578a7e89, 0x48801ce3, 0x48801ce3},
+	{0x0000a5d8, 0x5c8e7e8a, 0x5c8e7e8a, 0x4c801ce5, 0x4c801ce5},
+	{0x0000a5dc, 0x60927e8b, 0x60927e8b, 0x50801ce9, 0x50801ce9},
+	{0x0000a5e0, 0x65927ecc, 0x65927ecc, 0x54801ceb, 0x54801ceb},
+	{0x0000a5e4, 0x6b969ecd, 0x6b969ecd, 0x56801eec, 0x56801eec},
+	{0x0000a5e8, 0x70969f0e, 0x70969f0e, 0x56801eec, 0x56801eec},
+	{0x0000a5ec, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
+	{0x0000a5f0, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
+	{0x0000a5f4, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
+	{0x0000a5f8, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
+	{0x0000a5fc, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
 	{0x00016044, 0x056db2e4, 0x056db2e4, 0x056db2e4, 0x056db2e4},
 	{0x00016048, 0x8c001a61, 0x8c001a61, 0x8c001a61, 0x8c001a61},
 	{0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
@@ -1251,7 +1251,7 @@
 
 static const u32 ar9300Modes_low_ob_db_tx_gain_table_2p0[][5] = {
 	/* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
-	{0x0000a410, 0x000050da, 0x000050da, 0x000050da, 0x000050da},
+	{0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
 	{0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
 	{0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002},
 	{0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004},
@@ -1760,31 +1760,22 @@
 	{0x00007038, 0x000004c2},
 };
 
-/*
- * PCIE-PHY programming array, to be used prior to entering
- * full sleep (holding RTC in reset, PLL is ON in L1 mode)
- */
 static const u32 ar9300PciePhy_pll_on_clkreq_disable_L1_2p0[][2] = {
+	/* Addr      allmodes  */
 	{0x00004040, 0x08212e5e},
 	{0x00004040, 0x0008003b},
 	{0x00004044, 0x00000000},
 };
 
-/*
- * PCIE-PHY programming array, to be used when not in
- * full sleep (holding RTC in reset)
- */
 static const u32 ar9300PciePhy_clkreq_enable_L1_2p0[][2] = {
+	/* Addr      allmodes  */
 	{0x00004040, 0x08253e5e},
 	{0x00004040, 0x0008003b},
 	{0x00004044, 0x00000000},
 };
 
-/*
- * PCIE-PHY programming array, to be used prior to entering
- * full sleep (holding RTC in reset)
- */
 static const u32 ar9300PciePhy_clkreq_disable_L1_2p0[][2] = {
+	/* Addr      allmodes  */
 	{0x00004040, 0x08213e5e},
 	{0x00004040, 0x0008003b},
 	{0x00004044, 0x00000000},
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
index 7d111fb..37ba374 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
@@ -311,6 +311,9 @@
 {
 	struct ar9003_txc *ads = (struct ar9003_txc *) ds;
 
+	if (txpower > ah->txpower_limit)
+		txpower = ah->txpower_limit;
+
 	txpower += ah->txpower_indexoffset;
 	if (txpower > 63)
 		txpower = 63;
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
index 137543b..80431a2 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -375,16 +375,7 @@
 	else if (chan && IS_CHAN_QUARTER_RATE(chan))
 		pll |= SM(0x2, AR_RTC_9300_PLL_CLKSEL);
 
-	if (chan && IS_CHAN_5GHZ(chan)) {
-		pll |= SM(0x28, AR_RTC_9300_PLL_DIV);
-
-		/*
-		 * When doing fast clock, set PLL to 0x142c
-		 */
-		if (IS_CHAN_A_5MHZ_SPACED(chan))
-			pll = 0x142c;
-	} else
-		pll |= SM(0x2c, AR_RTC_9300_PLL_DIV);
+	pll |= SM(0x2c, AR_RTC_9300_PLL_DIV);
 
 	return pll;
 }
@@ -592,7 +583,7 @@
 	 * For 5GHz channels requiring Fast Clock, apply
 	 * different modal values.
 	 */
-	if (IS_CHAN_A_5MHZ_SPACED(chan))
+	if (IS_CHAN_A_FAST_CLOCK(ah, chan))
 		REG_WRITE_ARRAY(&ah->iniModesAdditional,
 				modesIndex, regWrites);
 
@@ -622,7 +613,7 @@
 	rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan))
 		? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM;
 
-	if (IS_CHAN_A_5MHZ_SPACED(chan))
+	if (IS_CHAN_A_FAST_CLOCK(ah, chan))
 		rfMode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE);
 
 	REG_WRITE(ah, AR_PHY_MODE, rfMode);
@@ -1102,6 +1093,7 @@
 		ath_print(common, ATH_DBG_ANY, "Timeout while waiting for nf "
 			  "to load: AR_PHY_AGC_CONTROL=0x%x\n",
 			  REG_READ(ah, AR_PHY_AGC_CONTROL));
+		return;
 	}
 
 	/*
diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c
index 09effde..b4424a6 100644
--- a/drivers/net/wireless/ath/ath9k/common.c
+++ b/drivers/net/wireless/ath/ath9k/common.c
@@ -212,7 +212,6 @@
 	rx_status->mactime = ath9k_hw_extend_tsf(ah, rx_stats->rs_tstamp);
 	rx_status->band = hw->conf.channel->band;
 	rx_status->freq = hw->conf.channel->center_freq;
-	rx_status->noise = common->ani.noise_floor;
 	rx_status->signal = ATH_DEFAULT_NOISE_FLOOR + rx_stats->rs_rssi;
 	rx_status->antenna = rx_stats->rs_antenna;
 	rx_status->flag |= RX_FLAG_TSFT;
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h
index fb9c8c9..21354c15 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.h
+++ b/drivers/net/wireless/ath/ath9k/eeprom.h
@@ -300,7 +300,8 @@
 	u32 binBuildNumber;
 	u8 deviceType;
 	u8 pwdclkind;
-	u8 futureBase_1[2];
+	u8 fastClk5g;
+	u8 divChain;
 	u8 rxGainType;
 	u8 dacHiPwrMode_5G;
 	u8 openLoopPwrCntl;
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c
index cf59799..e591ad6 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_def.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c
@@ -274,6 +274,8 @@
 		return pBase->txMask;
 	case EEP_RX_MASK:
 		return pBase->rxMask;
+	case EEP_FSTCLK_5G:
+		return pBase->fastClk5g;
 	case EEP_RXGAIN_TYPE:
 		return pBase->rxGainType;
 	case EEP_TXGAIN_TYPE:
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
index 3091bb3..74872ca 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
@@ -93,63 +93,6 @@
 	return ret;
 }
 
-static void hif_usb_tx_cb(struct urb *urb)
-{
-	struct tx_buf *tx_buf = (struct tx_buf *) urb->context;
-	struct hif_device_usb *hif_dev = tx_buf->hif_dev;
-	struct sk_buff *skb;
-	bool drop, flush;
-
-	if (!hif_dev)
-		return;
-
-	switch (urb->status) {
-	case 0:
-		break;
-	case -ENOENT:
-	case -ECONNRESET:
-		break;
-	case -ENODEV:
-	case -ESHUTDOWN:
-		return;
-	default:
-		break;
-	}
-
-	if (tx_buf) {
-		spin_lock(&hif_dev->tx.tx_lock);
-		drop = !!(hif_dev->tx.flags & HIF_USB_TX_STOP);
-		flush = !!(hif_dev->tx.flags & HIF_USB_TX_FLUSH);
-		spin_unlock(&hif_dev->tx.tx_lock);
-
-		while ((skb = __skb_dequeue(&tx_buf->skb_queue)) != NULL) {
-			if (!drop && !flush) {
-				ath9k_htc_txcompletion_cb(hif_dev->htc_handle,
-							  skb, 1);
-				TX_STAT_INC(skb_completed);
-			} else {
-				dev_kfree_skb_any(skb);
-				TX_STAT_INC(skb_dropped);
-			}
-		}
-
-		if (flush)
-			return;
-
-		tx_buf->len = tx_buf->offset = 0;
-		__skb_queue_head_init(&tx_buf->skb_queue);
-
-		spin_lock(&hif_dev->tx.tx_lock);
-		list_del(&tx_buf->list);
-		list_add_tail(&tx_buf->list, &hif_dev->tx.tx_buf);
-		hif_dev->tx.tx_buf_cnt++;
-		if (!drop)
-			__hif_usb_tx(hif_dev); /* Check for pending SKBs */
-		TX_STAT_INC(buf_completed);
-		spin_unlock(&hif_dev->tx.tx_lock);
-	}
-}
-
 static inline void ath9k_skb_queue_purge(struct hif_device_usb *hif_dev,
 					 struct sk_buff_head *list)
 {
@@ -161,6 +104,63 @@
 	}
 }
 
+static void hif_usb_tx_cb(struct urb *urb)
+{
+	struct tx_buf *tx_buf = (struct tx_buf *) urb->context;
+	struct hif_device_usb *hif_dev = tx_buf->hif_dev;
+	struct sk_buff *skb;
+
+	if (!hif_dev || !tx_buf)
+		return;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ENODEV:
+	case -ESHUTDOWN:
+		/*
+		 * The URB has been killed, free the SKBs
+		 * and return.
+		 */
+		ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue);
+		return;
+	default:
+		break;
+	}
+
+	/* Check if TX has been stopped */
+	spin_lock(&hif_dev->tx.tx_lock);
+	if (hif_dev->tx.flags & HIF_USB_TX_STOP) {
+		spin_unlock(&hif_dev->tx.tx_lock);
+		ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue);
+		goto add_free;
+	}
+	spin_unlock(&hif_dev->tx.tx_lock);
+
+	/* Complete the queued SKBs. */
+	while ((skb = __skb_dequeue(&tx_buf->skb_queue)) != NULL) {
+		ath9k_htc_txcompletion_cb(hif_dev->htc_handle,
+					  skb, 1);
+		TX_STAT_INC(skb_completed);
+	}
+
+add_free:
+	/* Re-initialize the SKB queue */
+	tx_buf->len = tx_buf->offset = 0;
+	__skb_queue_head_init(&tx_buf->skb_queue);
+
+	/* Add this TX buffer to the free list */
+	spin_lock(&hif_dev->tx.tx_lock);
+	list_move_tail(&tx_buf->list, &hif_dev->tx.tx_buf);
+	hif_dev->tx.tx_buf_cnt++;
+	if (!(hif_dev->tx.flags & HIF_USB_TX_STOP))
+		__hif_usb_tx(hif_dev); /* Check for pending SKBs */
+	TX_STAT_INC(buf_completed);
+	spin_unlock(&hif_dev->tx.tx_lock);
+}
+
 /* TX lock has to be taken */
 static int __hif_usb_tx(struct hif_device_usb *hif_dev)
 {
@@ -178,8 +178,7 @@
 		return 0;
 
 	tx_buf = list_first_entry(&hif_dev->tx.tx_buf, struct tx_buf, list);
-	list_del(&tx_buf->list);
-	list_add_tail(&tx_buf->list, &hif_dev->tx.tx_pending);
+	list_move_tail(&tx_buf->list, &hif_dev->tx.tx_pending);
 	hif_dev->tx.tx_buf_cnt--;
 
 	tx_skb_cnt = min_t(u16, hif_dev->tx.tx_skb_cnt, MAX_TX_AGGR_NUM);
@@ -511,9 +510,18 @@
 	if (likely(urb->actual_length != 0)) {
 		skb_put(skb, urb->actual_length);
 
+		/* Process the command first */
+		ath9k_htc_rx_msg(hif_dev->htc_handle, skb,
+				 skb->len, USB_REG_IN_PIPE);
+
+
 		nskb = alloc_skb(MAX_REG_IN_BUF_SIZE, GFP_ATOMIC);
-		if (!nskb)
-			goto resubmit;
+		if (!nskb) {
+			dev_err(&hif_dev->udev->dev,
+				"ath9k_htc: REG_IN memory allocation failure\n");
+			urb->context = NULL;
+			return;
+		}
 
 		usb_fill_int_urb(urb, hif_dev->udev,
 				 usb_rcvintpipe(hif_dev->udev, USB_REG_IN_PIPE),
@@ -523,12 +531,9 @@
 		ret = usb_submit_urb(urb, GFP_ATOMIC);
 		if (ret) {
 			kfree_skb(nskb);
-			goto free;
+			urb->context = NULL;
 		}
 
-		ath9k_htc_rx_msg(hif_dev->htc_handle, skb,
-				 skb->len, USB_REG_IN_PIPE);
-
 		return;
 	}
 
@@ -548,20 +553,17 @@
 
 static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev)
 {
-	unsigned long flags;
 	struct tx_buf *tx_buf = NULL, *tx_buf_tmp = NULL;
 
-	list_for_each_entry_safe(tx_buf, tx_buf_tmp, &hif_dev->tx.tx_buf, list) {
+	list_for_each_entry_safe(tx_buf, tx_buf_tmp,
+				 &hif_dev->tx.tx_buf, list) {
+		usb_kill_urb(tx_buf->urb);
 		list_del(&tx_buf->list);
 		usb_free_urb(tx_buf->urb);
 		kfree(tx_buf->buf);
 		kfree(tx_buf);
 	}
 
-	spin_lock_irqsave(&hif_dev->tx.tx_lock, flags);
-	hif_dev->tx.flags |= HIF_USB_TX_FLUSH;
-	spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
-
 	list_for_each_entry_safe(tx_buf, tx_buf_tmp,
 				 &hif_dev->tx.tx_pending, list) {
 		usb_kill_urb(tx_buf->urb);
@@ -570,10 +572,6 @@
 		kfree(tx_buf->buf);
 		kfree(tx_buf);
 	}
-
-	spin_lock_irqsave(&hif_dev->tx.tx_lock, flags);
-	hif_dev->tx.flags &= ~HIF_USB_TX_FLUSH;
-	spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
 }
 
 static int ath9k_hif_usb_alloc_tx_urbs(struct hif_device_usb *hif_dev)
@@ -897,6 +895,26 @@
 	return ret;
 }
 
+static void ath9k_hif_usb_reboot(struct usb_device *udev)
+{
+	u32 reboot_cmd = 0xffffffff;
+	void *buf;
+	int ret;
+
+	buf = kmalloc(4, GFP_KERNEL);
+	if (!buf)
+		return;
+
+	memcpy(buf, &reboot_cmd, 4);
+
+	ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, USB_REG_OUT_PIPE),
+			   buf, 4, NULL, HZ);
+	if (ret)
+		dev_err(&udev->dev, "ath9k_htc: USB reboot failed\n");
+
+	kfree(buf);
+}
+
 static void ath9k_hif_usb_disconnect(struct usb_interface *interface)
 {
 	struct usb_device *udev = interface_to_usbdev(interface);
@@ -904,14 +922,15 @@
 		(struct hif_device_usb *) usb_get_intfdata(interface);
 
 	if (hif_dev) {
-		ath9k_htc_hw_deinit(hif_dev->htc_handle, true);
+		ath9k_htc_hw_deinit(hif_dev->htc_handle,
+		    (udev->state == USB_STATE_NOTATTACHED) ? true : false);
 		ath9k_htc_hw_free(hif_dev->htc_handle);
 		ath9k_hif_usb_dev_deinit(hif_dev);
 		usb_set_intfdata(interface, NULL);
 	}
 
 	if (hif_dev->flags & HIF_USB_START)
-		usb_reset_device(udev);
+		ath9k_hif_usb_reboot(udev);
 
 	kfree(hif_dev);
 	dev_info(&udev->dev, "ath9k_htc: USB layer deinitialized\n");
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h
index 7d49a8a..0aca49b6 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.h
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.h
@@ -61,7 +61,6 @@
 };
 
 #define HIF_USB_TX_STOP  BIT(0)
-#define HIF_USB_TX_FLUSH BIT(1)
 
 struct hif_usb_tx {
 	u8 flags;
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index c765ff4..1ae18bb 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -329,6 +329,7 @@
 #define OP_ASSOCIATED     BIT(8)
 #define OP_ENABLE_BEACON  BIT(9)
 #define OP_LED_DEINIT     BIT(10)
+#define OP_UNPLUGGED      BIT(11)
 
 struct ath9k_htc_priv {
 	struct device *dev;
@@ -378,6 +379,7 @@
 	struct mutex htc_pm_lock;
 	unsigned long ps_usecount;
 	bool ps_enabled;
+	bool ps_idle;
 
 	struct ath_led radio_led;
 	struct ath_led assoc_led;
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
index a861896..701f2ef 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
@@ -744,6 +744,9 @@
 	if (ret)
 		goto err_init;
 
+	/* The device may have been unplugged earlier. */
+	priv->op_flags &= ~OP_UNPLUGGED;
+
 	ret = ath9k_init_device(priv, devid);
 	if (ret)
 		goto err_init;
@@ -760,6 +763,11 @@
 void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug)
 {
 	if (htc_handle->drv_priv) {
+
+		/* Check if the device has been yanked out. */
+		if (hotunplug)
+			htc_handle->drv_priv->op_flags |= OP_UNPLUGGED;
+
 		ath9k_deinit_device(htc_handle->drv_priv);
 		ath9k_deinit_wmi(htc_handle->drv_priv);
 		ieee80211_free_hw(htc_handle->drv_priv->hw);
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index ec7bcc8..ca7f3a7 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -94,8 +94,11 @@
 	if (--priv->ps_usecount != 0)
 		goto unlock;
 
-	if (priv->ps_enabled)
+	if (priv->ps_idle)
+		ath9k_hw_setpower(priv->ah, ATH9K_PM_FULL_SLEEP);
+	else if (priv->ps_enabled)
 		ath9k_hw_setpower(priv->ah, ATH9K_PM_NETWORK_SLEEP);
+
 unlock:
 	mutex_unlock(&priv->htc_pm_lock);
 }
@@ -153,7 +156,6 @@
 		ath_print(common, ATH_DBG_FATAL,
 			  "Unable to reset channel (%u Mhz) "
 			  "reset status %d\n", channel->center_freq, ret);
-		ath9k_htc_ps_restore(priv);
 		goto err;
 	}
 
@@ -1097,7 +1099,7 @@
 	return 0;
 }
 
-static int ath9k_htc_start(struct ieee80211_hw *hw)
+static int ath9k_htc_radio_enable(struct ieee80211_hw *hw)
 {
 	struct ath9k_htc_priv *priv = hw->priv;
 	struct ath_hw *ah = priv->ah;
@@ -1113,8 +1115,6 @@
 		  "Starting driver with initial channel: %d MHz\n",
 		  curchan->center_freq);
 
-	mutex_lock(&priv->mutex);
-
 	/* setup initial channel */
 	init_channel = ath9k_cmn_get_curchannel(hw, ah);
 
@@ -1127,7 +1127,7 @@
 		ath_print(common, ATH_DBG_FATAL,
 			  "Unable to reset hardware; reset status %d "
 			  "(freq %u MHz)\n", ret, curchan->center_freq);
-		goto mutex_unlock;
+		return ret;
 	}
 
 	ath_update_txpow(priv);
@@ -1135,16 +1135,8 @@
 	mode = ath9k_htc_get_curmode(priv, init_channel);
 	htc_mode = cpu_to_be16(mode);
 	WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
-	if (ret)
-		goto mutex_unlock;
-
 	WMI_CMD(WMI_ATH_INIT_CMDID);
-	if (ret)
-		goto mutex_unlock;
-
 	WMI_CMD(WMI_START_RECV_CMDID);
-	if (ret)
-		goto mutex_unlock;
 
 	ath9k_host_rx_init(priv);
 
@@ -1157,12 +1149,22 @@
 
 	ieee80211_wake_queues(hw);
 
-mutex_unlock:
-	mutex_unlock(&priv->mutex);
 	return ret;
 }
 
-static void ath9k_htc_stop(struct ieee80211_hw *hw)
+static int ath9k_htc_start(struct ieee80211_hw *hw)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+	int ret = 0;
+
+	mutex_lock(&priv->mutex);
+	ret = ath9k_htc_radio_enable(hw);
+	mutex_unlock(&priv->mutex);
+
+	return ret;
+}
+
+static void ath9k_htc_radio_disable(struct ieee80211_hw *hw)
 {
 	struct ath9k_htc_priv *priv = hw->priv;
 	struct ath_hw *ah = priv->ah;
@@ -1170,14 +1172,18 @@
 	int ret = 0;
 	u8 cmd_rsp;
 
-	mutex_lock(&priv->mutex);
-
 	if (priv->op_flags & OP_INVALID) {
 		ath_print(common, ATH_DBG_ANY, "Device not present\n");
-		mutex_unlock(&priv->mutex);
 		return;
 	}
 
+	/* Cancel all the running timers/work .. */
+	cancel_work_sync(&priv->ps_work);
+	cancel_delayed_work_sync(&priv->ath9k_ani_work);
+	cancel_delayed_work_sync(&priv->ath9k_aggr_work);
+	cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
+	ath9k_led_stop_brightness(priv);
+
 	ath9k_htc_ps_wakeup(priv);
 	htc_stop(priv->htc);
 	WMI_CMD(WMI_DISABLE_INTR_CMDID);
@@ -1189,11 +1195,6 @@
 	ath9k_htc_ps_restore(priv);
 	ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
 
-	cancel_work_sync(&priv->ps_work);
-	cancel_delayed_work_sync(&priv->ath9k_ani_work);
-	cancel_delayed_work_sync(&priv->ath9k_aggr_work);
-	cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
-	ath9k_led_stop_brightness(priv);
 	skb_queue_purge(&priv->tx_queue);
 
 	/* Remove monitor interface here */
@@ -1207,11 +1208,20 @@
 	}
 
 	priv->op_flags |= OP_INVALID;
-	mutex_unlock(&priv->mutex);
 
 	ath_print(common, ATH_DBG_CONFIG, "Driver halt\n");
 }
 
+static void ath9k_htc_stop(struct ieee80211_hw *hw)
+{
+	struct ath9k_htc_priv *priv = hw->priv;
+
+	mutex_lock(&priv->mutex);
+	ath9k_htc_radio_disable(hw);
+	mutex_unlock(&priv->mutex);
+}
+
+
 static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
 				   struct ieee80211_vif *vif)
 {
@@ -1325,6 +1335,23 @@
 
 	mutex_lock(&priv->mutex);
 
+	if (changed & IEEE80211_CONF_CHANGE_IDLE) {
+		bool enable_radio = false;
+		bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
+
+		if (!idle && priv->ps_idle)
+			enable_radio = true;
+
+		priv->ps_idle = idle;
+
+		if (enable_radio) {
+			ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
+			ath9k_htc_radio_enable(hw);
+			ath_print(common, ATH_DBG_CONFIG,
+				  "not-idle: enabling radio\n");
+		}
+	}
+
 	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
 		struct ieee80211_channel *curchan = hw->conf.channel;
 		int pos = curchan->hw_value;
@@ -1368,6 +1395,13 @@
 		}
 	}
 
+	if (priv->ps_idle) {
+		ath_print(common, ATH_DBG_CONFIG,
+			  "idle: disabling radio\n");
+		ath9k_htc_radio_disable(hw);
+	}
+
+
 	mutex_unlock(&priv->mutex);
 
 	return 0;
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index 2c3c510..28abc7d 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
@@ -244,16 +244,25 @@
 		    enum htc_endpoint_id ep_id, bool txok)
 {
 	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) drv_priv;
+	struct ath_common *common = ath9k_hw_common(priv->ah);
 	struct ieee80211_tx_info *tx_info;
 
 	if (!skb)
 		return;
 
-	if (ep_id == priv->mgmt_ep)
+	if (ep_id == priv->mgmt_ep) {
 		skb_pull(skb, sizeof(struct tx_mgmt_hdr));
-	else
-		/* TODO: Check for cab/uapsd/data */
+	} else if ((ep_id == priv->data_bk_ep) ||
+		   (ep_id == priv->data_be_ep) ||
+		   (ep_id == priv->data_vi_ep) ||
+		   (ep_id == priv->data_vo_ep)) {
 		skb_pull(skb, sizeof(struct tx_frame_hdr));
+	} else {
+		ath_print(common, ATH_DBG_FATAL,
+			  "Unsupported TX EPID: %d\n", ep_id);
+		dev_kfree_skb_any(skb);
+		return;
+	}
 
 	tx_info = IEEE80211_SKB_CB(skb);
 
@@ -439,10 +448,32 @@
 	struct ieee80211_hw *hw = priv->hw;
 	struct sk_buff *skb = rxbuf->skb;
 	struct ath_common *common = ath9k_hw_common(priv->ah);
+	struct ath_htc_rx_status *rxstatus;
 	int hdrlen, padpos, padsize;
 	int last_rssi = ATH_RSSI_DUMMY_MARKER;
 	__le16 fc;
 
+	if (skb->len <= HTC_RX_FRAME_HEADER_SIZE) {
+		ath_print(common, ATH_DBG_FATAL,
+			  "Corrupted RX frame, dropping\n");
+		goto rx_next;
+	}
+
+	rxstatus = (struct ath_htc_rx_status *)skb->data;
+
+	if (be16_to_cpu(rxstatus->rs_datalen) -
+	    (skb->len - HTC_RX_FRAME_HEADER_SIZE) != 0) {
+		ath_print(common, ATH_DBG_FATAL,
+			  "Corrupted RX data len, dropping "
+			  "(dlen: %d, skblen: %d)\n",
+			  rxstatus->rs_datalen, skb->len);
+		goto rx_next;
+	}
+
+	/* Get the RX status information */
+	memcpy(&rxbuf->rxstatus, rxstatus, HTC_RX_FRAME_HEADER_SIZE);
+	skb_pull(skb, HTC_RX_FRAME_HEADER_SIZE);
+
 	hdr = (struct ieee80211_hdr *)skb->data;
 	fc = hdr->frame_control;
 	hdrlen = ieee80211_get_hdrlen_from_skb(skb);
@@ -607,8 +638,6 @@
 	struct ath_hw *ah = priv->ah;
 	struct ath_common *common = ath9k_hw_common(ah);
 	struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL;
-	struct ath_htc_rx_status *rxstatus;
-	u32 len = 0;
 
 	spin_lock(&priv->rx.rxbuflock);
 	list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) {
@@ -625,27 +654,7 @@
 		goto err;
 	}
 
-	len = skb->len;
-	if (len <= HTC_RX_FRAME_HEADER_SIZE) {
-		ath_print(common, ATH_DBG_FATAL,
-			  "Corrupted RX frame, dropping\n");
-		goto err;
-	}
-
-	rxstatus = (struct ath_htc_rx_status *)skb->data;
-
-	if (be16_to_cpu(rxstatus->rs_datalen) -
-	    (len - HTC_RX_FRAME_HEADER_SIZE) != 0) {
-		ath_print(common, ATH_DBG_FATAL,
-			  "Corrupted RX data len, dropping "
-			  "(epid: %d, dlen: %d, skblen: %d)\n",
-			  ep_id, rxstatus->rs_datalen, len);
-		goto err;
-	}
-
 	spin_lock(&priv->rx.rxbuflock);
-	memcpy(&rxbuf->rxstatus, rxstatus, HTC_RX_FRAME_HEADER_SIZE);
-	skb_pull(skb, HTC_RX_FRAME_HEADER_SIZE);
 	rxbuf->skb = skb;
 	rxbuf->in_process = true;
 	spin_unlock(&priv->rx.rxbuflock);
diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c
index f2dca25..7bf6ce1 100644
--- a/drivers/net/wireless/ath/ath9k/htc_hst.c
+++ b/drivers/net/wireless/ath/ath9k/htc_hst.c
@@ -341,8 +341,9 @@
 		skb_pull(skb, sizeof(struct htc_frame_hdr));
 
 		if (endpoint->ep_callbacks.tx) {
-			endpoint->ep_callbacks.tx(htc_handle->drv_priv, skb,
-						  htc_hdr->endpoint_id, txok);
+			endpoint->ep_callbacks.tx(endpoint->ep_callbacks.priv,
+						  skb, htc_hdr->endpoint_id,
+						  txok);
 		}
 	}
 
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 5a29048..5590192 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -26,6 +26,7 @@
 #define ATH9K_CLOCK_RATE_CCK		22
 #define ATH9K_CLOCK_RATE_5GHZ_OFDM	40
 #define ATH9K_CLOCK_RATE_2GHZ_OFDM	44
+#define ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM 44
 
 static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type);
 
@@ -91,7 +92,11 @@
 		return usecs *ATH9K_CLOCK_RATE_CCK;
 	if (conf->channel->band == IEEE80211_BAND_2GHZ)
 		return usecs *ATH9K_CLOCK_RATE_2GHZ_OFDM;
-	return usecs *ATH9K_CLOCK_RATE_5GHZ_OFDM;
+
+	if (ah->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK)
+		return usecs * ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM;
+	else
+		return usecs * ATH9K_CLOCK_RATE_5GHZ_OFDM;
 }
 
 static u32 ath9k_hw_mac_to_clks(struct ath_hw *ah, u32 usecs)
@@ -387,6 +392,12 @@
 	ah->config.rx_intr_mitigation = true;
 
 	/*
+	 * Tx IQ Calibration (ah->config.tx_iq_calibration) is only
+	 * used by AR9003, but it is showing reliability issues.
+	 * It will take a while to fix so this is currently disabled.
+	 */
+
+	/*
 	 * We need this for PCI devices only (Cardbus, PCI, miniPCI)
 	 * _and_ if on non-uniprocessor systems (Multiprocessor/HT).
 	 * This means we use it for all AR5416 devices, and the few
@@ -819,9 +830,6 @@
 	if (common->state < ATH_HW_INITIALIZED)
 		goto free_hw;
 
-	if (!AR_SREV_9100(ah))
-		ath9k_hw_ani_disable(ah);
-
 	ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP);
 
 free_hw:
@@ -1221,8 +1229,7 @@
 	    (chan->channel != ah->curchan->channel) &&
 	    ((chan->channelFlags & CHANNEL_ALL) ==
 	     (ah->curchan->channelFlags & CHANNEL_ALL)) &&
-	     !(AR_SREV_9280(ah) || IS_CHAN_A_5MHZ_SPACED(chan) ||
-	     IS_CHAN_A_5MHZ_SPACED(ah->curchan))) {
+	    !AR_SREV_9280(ah)) {
 
 		if (ath9k_hw_channel_change(ah, chan)) {
 			ath9k_hw_loadnf(ah, ah->curchan);
@@ -2186,7 +2193,8 @@
 	}
 
 	if (AR_SREV_9300_20_OR_LATER(ah)) {
-		pCap->hw_caps |= ATH9K_HW_CAP_EDMA | ATH9K_HW_CAP_LDPC;
+		pCap->hw_caps |= ATH9K_HW_CAP_EDMA | ATH9K_HW_CAP_LDPC |
+				 ATH9K_HW_CAP_FASTCLOCK;
 		pCap->rx_hp_qdepth = ATH9K_HW_RX_HP_QDEPTH;
 		pCap->rx_lp_qdepth = ATH9K_HW_RX_LP_QDEPTH;
 		pCap->rx_status_len = sizeof(struct ar9003_rxs);
@@ -2194,6 +2202,11 @@
 		pCap->txs_len = sizeof(struct ar9003_txs);
 	} else {
 		pCap->tx_desc_len = sizeof(struct ath_desc);
+		if (AR_SREV_9280_20(ah) &&
+		    ((ah->eep_ops->get_eeprom(ah, EEP_MINOR_REV) <=
+		      AR5416_EEP_MINOR_VER_16) ||
+		     ah->eep_ops->get_eeprom(ah, EEP_FSTCLK_5G)))
+			pCap->hw_caps |= ATH9K_HW_CAP_FASTCLOCK;
 	}
 
 	if (AR_SREV_9300_20_OR_LATER(ah))
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index a78e09b..77245df 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -198,6 +198,7 @@
 	ATH9K_HW_CAP_EDMA			= BIT(17),
 	ATH9K_HW_CAP_RAC_SUPPORTED		= BIT(18),
 	ATH9K_HW_CAP_LDPC			= BIT(19),
+	ATH9K_HW_CAP_FASTCLOCK			= BIT(20),
 };
 
 enum ath9k_capability_type {
@@ -261,6 +262,7 @@
 #define AR_BASE_FREQ_5GHZ   	4900
 #define AR_SPUR_FEEQ_BOUND_HT40 19
 #define AR_SPUR_FEEQ_BOUND_HT20 10
+	bool tx_iq_calibration; /* Only available for >= AR9003 */
 	int spurmode;
 	u16 spurchans[AR_EEPROM_MODAL_SPURS][2];
 	u8 max_txtrig_level;
@@ -367,10 +369,9 @@
 #define IS_CHAN_2GHZ(_c) (((_c)->channelFlags & CHANNEL_2GHZ) != 0)
 #define IS_CHAN_HALF_RATE(_c) (((_c)->channelFlags & CHANNEL_HALF) != 0)
 #define IS_CHAN_QUARTER_RATE(_c) (((_c)->channelFlags & CHANNEL_QUARTER) != 0)
-#define IS_CHAN_A_5MHZ_SPACED(_c)			\
+#define IS_CHAN_A_FAST_CLOCK(_ah, _c)			\
 	((((_c)->channelFlags & CHANNEL_5GHZ) != 0) &&	\
-	 (((_c)->channel % 20) != 0) &&			\
-	 (((_c)->channel % 10) != 0))
+	 ((_ah)->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK))
 
 /* These macros check chanmode and not channelFlags */
 #define IS_CHAN_B(_c) ((_c)->chanmode == CHANNEL_B)
@@ -718,6 +719,7 @@
 	u32 *addac5416_21;
 	u32 *bank6Temp;
 
+	u8 txpower_limit;
 	int16_t txpower_indexoffset;
 	int coverage_class;
 	u32 beacon_interval;
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index 7bbf502..0e425cb 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -878,10 +878,12 @@
 	if (ints & ATH9K_INT_TX) {
 		if (ah->config.tx_intr_mitigation)
 			mask |= AR_IMR_TXMINTR | AR_IMR_TXINTM;
-		if (ah->txok_interrupt_mask)
-			mask |= AR_IMR_TXOK;
-		if (ah->txdesc_interrupt_mask)
-			mask |= AR_IMR_TXDESC;
+		else {
+			if (ah->txok_interrupt_mask)
+				mask |= AR_IMR_TXOK;
+			if (ah->txdesc_interrupt_mask)
+				mask |= AR_IMR_TXDESC;
+		}
 		if (ah->txerr_interrupt_mask)
 			mask |= AR_IMR_TXERR;
 		if (ah->txeol_interrupt_mask)
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index aad370a..893b552 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -2044,6 +2044,25 @@
 	return ret;
 }
 
+static int ath9k_get_survey(struct ieee80211_hw *hw, int idx,
+			     struct survey_info *survey)
+{
+	struct ath_wiphy *aphy = hw->priv;
+	struct ath_softc *sc = aphy->sc;
+	struct ath_hw *ah = sc->sc_ah;
+	struct ath_common *common = ath9k_hw_common(ah);
+	struct ieee80211_conf *conf = &hw->conf;
+
+	 if (idx != 0)
+		return -ENOENT;
+
+	survey->channel = conf->channel;
+	survey->filled = SURVEY_INFO_NOISE_DBM;
+	survey->noise = common->ani.noise_floor;
+
+	return 0;
+}
+
 static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
 {
 	struct ath_wiphy *aphy = hw->priv;
@@ -2115,6 +2134,7 @@
 	.set_tsf 	    = ath9k_set_tsf,
 	.reset_tsf 	    = ath9k_reset_tsf,
 	.ampdu_action       = ath9k_ampdu_action,
+	.get_survey	    = ath9k_get_survey,
 	.sw_scan_start      = ath9k_sw_scan_start,
 	.sw_scan_complete   = ath9k_sw_scan_complete,
 	.rfkill_poll        = ath9k_rfkill_poll_state,
diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c
index dc6c6fc..e23172c 100644
--- a/drivers/net/wireless/ath/ath9k/wmi.c
+++ b/drivers/net/wireless/ath/ath9k/wmi.c
@@ -276,6 +276,9 @@
 	int time_left, ret = 0;
 	unsigned long flags;
 
+	if (wmi->drv_priv->op_flags & OP_UNPLUGGED)
+		return 0;
+
 	if (!wmi)
 		return -EINVAL;
 
@@ -302,14 +305,14 @@
 	wmi->cmd_rsp_buf = rsp_buf;
 	wmi->cmd_rsp_len = rsp_len;
 
-	ret = ath9k_wmi_cmd_issue(wmi, skb, cmd_id, cmd_len);
-	if (ret)
-		goto out;
-
 	spin_lock_irqsave(&wmi->wmi_lock, flags);
 	wmi->last_cmd_id = cmd_id;
 	spin_unlock_irqrestore(&wmi->wmi_lock, flags);
 
+	ret = ath9k_wmi_cmd_issue(wmi, skb, cmd_id, cmd_len);
+	if (ret)
+		goto out;
+
 	time_left = wait_for_completion_timeout(&wmi->cmd_wait, timeout);
 	if (!time_left) {
 		ath_print(common, ATH_DBG_WMI,
diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h
index 167e15c..765db5f 100644
--- a/drivers/net/wireless/ath/ath9k/wmi.h
+++ b/drivers/net/wireless/ath/ath9k/wmi.h
@@ -126,14 +126,14 @@
 	do {								\
 		ret = ath9k_wmi_cmd(priv->wmi, _wmi_cmd, NULL, 0,	\
 				    (u8 *) &cmd_rsp,			\
-				    sizeof(cmd_rsp), HZ);		\
+				    sizeof(cmd_rsp), HZ*2);		\
 	} while (0)
 
 #define WMI_CMD_BUF(_wmi_cmd, _buf)					\
 	do {								\
 		ret = ath9k_wmi_cmd(priv->wmi, _wmi_cmd,		\
 				    (u8 *) _buf, sizeof(*_buf),		\
-				    &cmd_rsp, sizeof(cmd_rsp), HZ);	\
+				    &cmd_rsp, sizeof(cmd_rsp), HZ*2);	\
 	} while (0)
 
 #endif /* WMI_H */
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index b0d345a..3db1917 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -2290,6 +2290,8 @@
 			ath_tx_complete_buf(sc, bf, txq, &bf_head,
 					    &txs, txok, 0);
 
+		ath_wake_mac80211_queue(sc, txq);
+
 		spin_lock_bh(&txq->axq_lock);
 		if (!list_empty(&txq->txq_fifo_pending)) {
 			INIT_LIST_HEAD(&bf_head);
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index 997303b..7965b70 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -4571,6 +4571,23 @@
 	mutex_unlock(&wl->mutex);
 }
 
+static int b43_op_get_survey(struct ieee80211_hw *hw, int idx,
+			     struct survey_info *survey)
+{
+	struct b43_wl *wl = hw_to_b43_wl(hw);
+	struct b43_wldev *dev = wl->current_dev;
+	struct ieee80211_conf *conf = &hw->conf;
+
+	if (idx != 0)
+		return -ENOENT;
+
+	survey->channel = conf->channel;
+	survey->filled = SURVEY_INFO_NOISE_DBM;
+	survey->noise = dev->stats.link_noise;
+
+	return 0;
+}
+
 static const struct ieee80211_ops b43_hw_ops = {
 	.tx			= b43_op_tx,
 	.conf_tx		= b43_op_conf_tx,
@@ -4590,6 +4607,7 @@
 	.sta_notify		= b43_op_sta_notify,
 	.sw_scan_start		= b43_op_sw_scan_start_notifier,
 	.sw_scan_complete	= b43_op_sw_scan_complete_notifier,
+	.get_survey		= b43_op_get_survey,
 	.rfkill_poll		= b43_rfkill_poll,
 };
 
@@ -4905,8 +4923,7 @@
 
 	/* fill hw info */
 	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
-		    IEEE80211_HW_SIGNAL_DBM |
-		    IEEE80211_HW_NOISE_DBM;
+		    IEEE80211_HW_SIGNAL_DBM;
 
 	hw->wiphy->interface_modes =
 		BIT(NL80211_IFTYPE_AP) |
diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c
index eda0652..e6b0528 100644
--- a/drivers/net/wireless/b43/xmit.c
+++ b/drivers/net/wireless/b43/xmit.c
@@ -610,7 +610,6 @@
 	}
 
 	/* Link quality statistics */
-	status.noise = dev->stats.link_noise;
 	if ((chanstat & B43_RX_CHAN_PHYTYPE) == B43_PHYTYPE_N) {
 //		s8 rssi = max(rxhdr->power0, rxhdr->power1);
 		//TODO: Find out what the rssi value is (dBm or percentage?)
diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c
index bb2dd93..1713f5f 100644
--- a/drivers/net/wireless/b43legacy/main.c
+++ b/drivers/net/wireless/b43legacy/main.c
@@ -3482,6 +3482,23 @@
 	return 0;
 }
 
+static int b43legacy_op_get_survey(struct ieee80211_hw *hw, int idx,
+				   struct survey_info *survey)
+{
+	struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
+	struct b43legacy_wldev *dev = wl->current_dev;
+	struct ieee80211_conf *conf = &hw->conf;
+
+	if (idx != 0)
+		return -ENOENT;
+
+	survey->channel = conf->channel;
+	survey->filled = SURVEY_INFO_NOISE_DBM;
+	survey->noise = dev->stats.link_noise;
+
+	return 0;
+}
+
 static const struct ieee80211_ops b43legacy_hw_ops = {
 	.tx			= b43legacy_op_tx,
 	.conf_tx		= b43legacy_op_conf_tx,
@@ -3494,6 +3511,7 @@
 	.start			= b43legacy_op_start,
 	.stop			= b43legacy_op_stop,
 	.set_tim		= b43legacy_op_beacon_set_tim,
+	.get_survey		= b43legacy_op_get_survey,
 	.rfkill_poll		= b43legacy_rfkill_poll,
 };
 
@@ -3769,8 +3787,7 @@
 
 	/* fill hw info */
 	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
-		    IEEE80211_HW_SIGNAL_DBM |
-		    IEEE80211_HW_NOISE_DBM;
+		    IEEE80211_HW_SIGNAL_DBM;
 	hw->wiphy->interface_modes =
 		BIT(NL80211_IFTYPE_AP) |
 		BIT(NL80211_IFTYPE_STATION) |
diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c
index 9c8882d..7d177d9 100644
--- a/drivers/net/wireless/b43legacy/xmit.c
+++ b/drivers/net/wireless/b43legacy/xmit.c
@@ -548,7 +548,6 @@
 				      (phystat0 & B43legacy_RX_PHYST0_OFDM),
 				      (phystat0 & B43legacy_RX_PHYST0_GAINCTL),
 				      (phystat3 & B43legacy_RX_PHYST3_TRSTATE));
-	status.noise = dev->stats.link_noise;
 	/* change to support A PHY */
 	if (phystat0 & B43legacy_RX_PHYST0_OFDM)
 		status.rate_idx = b43legacy_plcp_get_bitrate_idx_ofdm(plcp, false);
diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile
index 5ed2dcb..7c72353 100644
--- a/drivers/net/wireless/iwlwifi/Makefile
+++ b/drivers/net/wireless/iwlwifi/Makefile
@@ -22,5 +22,6 @@
 # 3945
 obj-$(CONFIG_IWL3945)	+= iwl3945.o
 iwl3945-objs		:= iwl3945-base.o iwl-3945.o iwl-3945-rs.o iwl-3945-led.o
+iwl3945-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-3945-debugfs.o
 
 ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c
index 8431ffc..fb59af2 100644
--- a/drivers/net/wireless/iwlwifi/iwl-1000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-1000.c
@@ -282,7 +282,6 @@
 	.use_bsm = false,
 	.max_ll_items = OTP_MAX_LL_ITEMS_1000,
 	.shadow_ram_support = false,
-	.ht_greenfield_support = true,
 	.led_compensation = 51,
 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
 	.support_ct_kill_exit = true,
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-3945-debugfs.c
new file mode 100644
index 0000000..6a9c64a
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-debugfs.c
@@ -0,0 +1,500 @@
+/******************************************************************************
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *****************************************************************************/
+
+#include "iwl-3945-debugfs.h"
+
+ssize_t iwl3945_ucode_rx_stats_read(struct file *file,
+				    char __user *user_buf,
+				    size_t count, loff_t *ppos)
+{
+	struct iwl_priv *priv = file->private_data;
+	int pos = 0;
+	char *buf;
+	int bufsz = sizeof(struct iwl39_statistics_rx_phy) * 40 +
+		    sizeof(struct iwl39_statistics_rx_non_phy) * 40 + 400;
+	ssize_t ret;
+	struct iwl39_statistics_rx_phy *ofdm, *accum_ofdm, *delta_ofdm, *max_ofdm;
+	struct iwl39_statistics_rx_phy *cck, *accum_cck, *delta_cck, *max_cck;
+	struct iwl39_statistics_rx_non_phy *general, *accum_general;
+	struct iwl39_statistics_rx_non_phy *delta_general, *max_general;
+
+	if (!iwl_is_alive(priv))
+		return -EAGAIN;
+
+	buf = kzalloc(bufsz, GFP_KERNEL);
+	if (!buf) {
+		IWL_ERR(priv, "Can not allocate Buffer\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * The statistic information display here is based on
+	 * the last statistics notification from uCode
+	 * might not reflect the current uCode activity
+	 */
+	ofdm = &priv->_3945.statistics.rx.ofdm;
+	cck = &priv->_3945.statistics.rx.cck;
+	general = &priv->_3945.statistics.rx.general;
+	accum_ofdm = &priv->_3945.accum_statistics.rx.ofdm;
+	accum_cck = &priv->_3945.accum_statistics.rx.cck;
+	accum_general = &priv->_3945.accum_statistics.rx.general;
+	delta_ofdm = &priv->_3945.delta_statistics.rx.ofdm;
+	delta_cck = &priv->_3945.delta_statistics.rx.cck;
+	delta_general = &priv->_3945.delta_statistics.rx.general;
+	max_ofdm = &priv->_3945.max_delta.rx.ofdm;
+	max_cck = &priv->_3945.max_delta.rx.cck;
+	max_general = &priv->_3945.max_delta.rx.general;
+
+	pos += iwl_dbgfs_statistics_flag(priv, buf, bufsz);
+	pos += scnprintf(buf + pos, bufsz - pos, "%-32s     current"
+			 "acumulative       delta         max\n",
+			 "Statistics_Rx - OFDM:");
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "ina_cnt:", le32_to_cpu(ofdm->ina_cnt),
+			 accum_ofdm->ina_cnt,
+			 delta_ofdm->ina_cnt, max_ofdm->ina_cnt);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "fina_cnt:",
+			 le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt,
+			 delta_ofdm->fina_cnt, max_ofdm->fina_cnt);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n", "plcp_err:",
+			 le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err,
+			 delta_ofdm->plcp_err, max_ofdm->plcp_err);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",  "crc32_err:",
+			 le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err,
+			 delta_ofdm->crc32_err, max_ofdm->crc32_err);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n", "overrun_err:",
+			 le32_to_cpu(ofdm->overrun_err),
+			 accum_ofdm->overrun_err, delta_ofdm->overrun_err,
+			 max_ofdm->overrun_err);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "early_overrun_err:",
+			 le32_to_cpu(ofdm->early_overrun_err),
+			 accum_ofdm->early_overrun_err,
+			 delta_ofdm->early_overrun_err,
+			 max_ofdm->early_overrun_err);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "crc32_good:", le32_to_cpu(ofdm->crc32_good),
+			 accum_ofdm->crc32_good, delta_ofdm->crc32_good,
+			 max_ofdm->crc32_good);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n", "false_alarm_cnt:",
+			 le32_to_cpu(ofdm->false_alarm_cnt),
+			 accum_ofdm->false_alarm_cnt,
+			 delta_ofdm->false_alarm_cnt,
+			 max_ofdm->false_alarm_cnt);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "fina_sync_err_cnt:",
+			 le32_to_cpu(ofdm->fina_sync_err_cnt),
+			 accum_ofdm->fina_sync_err_cnt,
+			 delta_ofdm->fina_sync_err_cnt,
+			 max_ofdm->fina_sync_err_cnt);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "sfd_timeout:",
+			 le32_to_cpu(ofdm->sfd_timeout),
+			 accum_ofdm->sfd_timeout,
+			 delta_ofdm->sfd_timeout,
+			 max_ofdm->sfd_timeout);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "fina_timeout:",
+			 le32_to_cpu(ofdm->fina_timeout),
+			 accum_ofdm->fina_timeout,
+			 delta_ofdm->fina_timeout,
+			 max_ofdm->fina_timeout);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "unresponded_rts:",
+			 le32_to_cpu(ofdm->unresponded_rts),
+			 accum_ofdm->unresponded_rts,
+			 delta_ofdm->unresponded_rts,
+			 max_ofdm->unresponded_rts);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "rxe_frame_lmt_ovrun:",
+			 le32_to_cpu(ofdm->rxe_frame_limit_overrun),
+			 accum_ofdm->rxe_frame_limit_overrun,
+			 delta_ofdm->rxe_frame_limit_overrun,
+			 max_ofdm->rxe_frame_limit_overrun);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "sent_ack_cnt:",
+			 le32_to_cpu(ofdm->sent_ack_cnt),
+			 accum_ofdm->sent_ack_cnt,
+			 delta_ofdm->sent_ack_cnt,
+			 max_ofdm->sent_ack_cnt);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "sent_cts_cnt:",
+			 le32_to_cpu(ofdm->sent_cts_cnt),
+			 accum_ofdm->sent_cts_cnt,
+			 delta_ofdm->sent_cts_cnt, max_ofdm->sent_cts_cnt);
+
+	pos += scnprintf(buf + pos, bufsz - pos, "%-32s     current"
+			 "acumulative       delta         max\n",
+			 "Statistics_Rx - CCK:");
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "ina_cnt:",
+			 le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt,
+			 delta_cck->ina_cnt, max_cck->ina_cnt);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "fina_cnt:",
+			 le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt,
+			 delta_cck->fina_cnt, max_cck->fina_cnt);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "plcp_err:",
+			 le32_to_cpu(cck->plcp_err), accum_cck->plcp_err,
+			 delta_cck->plcp_err, max_cck->plcp_err);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "crc32_err:",
+			 le32_to_cpu(cck->crc32_err), accum_cck->crc32_err,
+			 delta_cck->crc32_err, max_cck->crc32_err);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "overrun_err:",
+			 le32_to_cpu(cck->overrun_err),
+			 accum_cck->overrun_err,
+			 delta_cck->overrun_err, max_cck->overrun_err);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "early_overrun_err:",
+			 le32_to_cpu(cck->early_overrun_err),
+			 accum_cck->early_overrun_err,
+			 delta_cck->early_overrun_err,
+			 max_cck->early_overrun_err);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "crc32_good:",
+			 le32_to_cpu(cck->crc32_good), accum_cck->crc32_good,
+			 delta_cck->crc32_good,
+			 max_cck->crc32_good);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "false_alarm_cnt:",
+			 le32_to_cpu(cck->false_alarm_cnt),
+			 accum_cck->false_alarm_cnt,
+			 delta_cck->false_alarm_cnt, max_cck->false_alarm_cnt);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "fina_sync_err_cnt:",
+			 le32_to_cpu(cck->fina_sync_err_cnt),
+			 accum_cck->fina_sync_err_cnt,
+			 delta_cck->fina_sync_err_cnt,
+			 max_cck->fina_sync_err_cnt);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "sfd_timeout:",
+			 le32_to_cpu(cck->sfd_timeout),
+			 accum_cck->sfd_timeout,
+			 delta_cck->sfd_timeout, max_cck->sfd_timeout);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "fina_timeout:",
+			 le32_to_cpu(cck->fina_timeout),
+			 accum_cck->fina_timeout,
+			 delta_cck->fina_timeout, max_cck->fina_timeout);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "unresponded_rts:",
+			 le32_to_cpu(cck->unresponded_rts),
+			 accum_cck->unresponded_rts,
+			 delta_cck->unresponded_rts,
+			 max_cck->unresponded_rts);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "rxe_frame_lmt_ovrun:",
+			 le32_to_cpu(cck->rxe_frame_limit_overrun),
+			 accum_cck->rxe_frame_limit_overrun,
+			 delta_cck->rxe_frame_limit_overrun,
+			 max_cck->rxe_frame_limit_overrun);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "sent_ack_cnt:",
+			 le32_to_cpu(cck->sent_ack_cnt),
+			 accum_cck->sent_ack_cnt,
+			 delta_cck->sent_ack_cnt,
+			 max_cck->sent_ack_cnt);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "sent_cts_cnt:",
+			 le32_to_cpu(cck->sent_cts_cnt),
+			 accum_cck->sent_cts_cnt,
+			 delta_cck->sent_cts_cnt,
+			 max_cck->sent_cts_cnt);
+
+	pos += scnprintf(buf + pos, bufsz - pos, "%-32s     current"
+			 "acumulative       delta         max\n",
+			 "Statistics_Rx - GENERAL:");
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "bogus_cts:",
+			 le32_to_cpu(general->bogus_cts),
+			 accum_general->bogus_cts,
+			 delta_general->bogus_cts, max_general->bogus_cts);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "bogus_ack:",
+			 le32_to_cpu(general->bogus_ack),
+			 accum_general->bogus_ack,
+			 delta_general->bogus_ack, max_general->bogus_ack);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "non_bssid_frames:",
+			 le32_to_cpu(general->non_bssid_frames),
+			 accum_general->non_bssid_frames,
+			 delta_general->non_bssid_frames,
+			 max_general->non_bssid_frames);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "filtered_frames:",
+			 le32_to_cpu(general->filtered_frames),
+			 accum_general->filtered_frames,
+			 delta_general->filtered_frames,
+			 max_general->filtered_frames);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "non_channel_beacons:",
+			 le32_to_cpu(general->non_channel_beacons),
+			 accum_general->non_channel_beacons,
+			 delta_general->non_channel_beacons,
+			 max_general->non_channel_beacons);
+
+	ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+	kfree(buf);
+	return ret;
+}
+
+ssize_t iwl3945_ucode_tx_stats_read(struct file *file,
+				    char __user *user_buf,
+				    size_t count, loff_t *ppos)
+{
+	struct iwl_priv *priv = file->private_data;
+	int pos = 0;
+	char *buf;
+	int bufsz = (sizeof(struct iwl39_statistics_tx) * 48) + 250;
+	ssize_t ret;
+	struct iwl39_statistics_tx *tx, *accum_tx, *delta_tx, *max_tx;
+
+	if (!iwl_is_alive(priv))
+		return -EAGAIN;
+
+	buf = kzalloc(bufsz, GFP_KERNEL);
+	if (!buf) {
+		IWL_ERR(priv, "Can not allocate Buffer\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * The statistic information display here is based on
+	 * the last statistics notification from uCode
+	 * might not reflect the current uCode activity
+	 */
+	tx = &priv->_3945.statistics.tx;
+	accum_tx = &priv->_3945.accum_statistics.tx;
+	delta_tx = &priv->_3945.delta_statistics.tx;
+	max_tx = &priv->_3945.max_delta.tx;
+	pos += iwl_dbgfs_statistics_flag(priv, buf, bufsz);
+	pos += scnprintf(buf + pos, bufsz - pos, "%-32s     current"
+			 "acumulative       delta         max\n",
+			 "Statistics_Tx:");
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "preamble:",
+			 le32_to_cpu(tx->preamble_cnt),
+			 accum_tx->preamble_cnt,
+			 delta_tx->preamble_cnt, max_tx->preamble_cnt);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "rx_detected_cnt:",
+			 le32_to_cpu(tx->rx_detected_cnt),
+			 accum_tx->rx_detected_cnt,
+			 delta_tx->rx_detected_cnt, max_tx->rx_detected_cnt);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "bt_prio_defer_cnt:",
+			 le32_to_cpu(tx->bt_prio_defer_cnt),
+			 accum_tx->bt_prio_defer_cnt,
+			 delta_tx->bt_prio_defer_cnt,
+			 max_tx->bt_prio_defer_cnt);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "bt_prio_kill_cnt:",
+			 le32_to_cpu(tx->bt_prio_kill_cnt),
+			 accum_tx->bt_prio_kill_cnt,
+			 delta_tx->bt_prio_kill_cnt,
+			 max_tx->bt_prio_kill_cnt);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "few_bytes_cnt:",
+			 le32_to_cpu(tx->few_bytes_cnt),
+			 accum_tx->few_bytes_cnt,
+			 delta_tx->few_bytes_cnt, max_tx->few_bytes_cnt);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "cts_timeout:",
+			 le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout,
+			 delta_tx->cts_timeout, max_tx->cts_timeout);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "ack_timeout:",
+			 le32_to_cpu(tx->ack_timeout),
+			 accum_tx->ack_timeout,
+			 delta_tx->ack_timeout, max_tx->ack_timeout);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "expected_ack_cnt:",
+			 le32_to_cpu(tx->expected_ack_cnt),
+			 accum_tx->expected_ack_cnt,
+			 delta_tx->expected_ack_cnt,
+			 max_tx->expected_ack_cnt);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "actual_ack_cnt:",
+			 le32_to_cpu(tx->actual_ack_cnt),
+			 accum_tx->actual_ack_cnt,
+			 delta_tx->actual_ack_cnt,
+			 max_tx->actual_ack_cnt);
+
+	ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+	kfree(buf);
+	return ret;
+}
+
+ssize_t iwl3945_ucode_general_stats_read(struct file *file,
+					 char __user *user_buf,
+					 size_t count, loff_t *ppos)
+{
+	struct iwl_priv *priv = file->private_data;
+	int pos = 0;
+	char *buf;
+	int bufsz = sizeof(struct iwl39_statistics_general) * 10 + 300;
+	ssize_t ret;
+	struct iwl39_statistics_general *general, *accum_general;
+	struct iwl39_statistics_general *delta_general, *max_general;
+	struct statistics_dbg *dbg, *accum_dbg, *delta_dbg, *max_dbg;
+	struct iwl39_statistics_div *div, *accum_div, *delta_div, *max_div;
+
+	if (!iwl_is_alive(priv))
+		return -EAGAIN;
+
+	buf = kzalloc(bufsz, GFP_KERNEL);
+	if (!buf) {
+		IWL_ERR(priv, "Can not allocate Buffer\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * The statistic information display here is based on
+	 * the last statistics notification from uCode
+	 * might not reflect the current uCode activity
+	 */
+	general = &priv->_3945.statistics.general;
+	dbg = &priv->_3945.statistics.general.dbg;
+	div = &priv->_3945.statistics.general.div;
+	accum_general = &priv->_3945.accum_statistics.general;
+	delta_general = &priv->_3945.delta_statistics.general;
+	max_general = &priv->_3945.max_delta.general;
+	accum_dbg = &priv->_3945.accum_statistics.general.dbg;
+	delta_dbg = &priv->_3945.delta_statistics.general.dbg;
+	max_dbg = &priv->_3945.max_delta.general.dbg;
+	accum_div = &priv->_3945.accum_statistics.general.div;
+	delta_div = &priv->_3945.delta_statistics.general.div;
+	max_div = &priv->_3945.max_delta.general.div;
+	pos += iwl_dbgfs_statistics_flag(priv, buf, bufsz);
+	pos += scnprintf(buf + pos, bufsz - pos, "%-32s     current"
+			 "acumulative       delta         max\n",
+			 "Statistics_General:");
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "burst_check:",
+			 le32_to_cpu(dbg->burst_check),
+			 accum_dbg->burst_check,
+			 delta_dbg->burst_check, max_dbg->burst_check);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "burst_count:",
+			 le32_to_cpu(dbg->burst_count),
+			 accum_dbg->burst_count,
+			 delta_dbg->burst_count, max_dbg->burst_count);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "sleep_time:",
+			 le32_to_cpu(general->sleep_time),
+			 accum_general->sleep_time,
+			 delta_general->sleep_time, max_general->sleep_time);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "slots_out:",
+			 le32_to_cpu(general->slots_out),
+			 accum_general->slots_out,
+			 delta_general->slots_out, max_general->slots_out);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "slots_idle:",
+			 le32_to_cpu(general->slots_idle),
+			 accum_general->slots_idle,
+			 delta_general->slots_idle, max_general->slots_idle);
+	pos += scnprintf(buf + pos, bufsz - pos, "ttl_timestamp:\t\t\t%u\n",
+			 le32_to_cpu(general->ttl_timestamp));
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "tx_on_a:",
+			 le32_to_cpu(div->tx_on_a), accum_div->tx_on_a,
+			 delta_div->tx_on_a, max_div->tx_on_a);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "tx_on_b:",
+			 le32_to_cpu(div->tx_on_b), accum_div->tx_on_b,
+			 delta_div->tx_on_b, max_div->tx_on_b);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "exec_time:",
+			 le32_to_cpu(div->exec_time), accum_div->exec_time,
+			 delta_div->exec_time, max_div->exec_time);
+	pos += scnprintf(buf + pos, bufsz - pos,
+			 "  %-30s %10u  %10u  %10u  %10u\n",
+			 "probe_time:",
+			 le32_to_cpu(div->probe_time), accum_div->probe_time,
+			 delta_div->probe_time, max_div->probe_time);
+	ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+	kfree(buf);
+	return ret;
+}
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-debugfs.h b/drivers/net/wireless/iwlwifi/iwl-3945-debugfs.h
new file mode 100644
index 0000000..70809c5
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-debugfs.h
@@ -0,0 +1,60 @@
+/******************************************************************************
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *****************************************************************************/
+
+#include "iwl-dev.h"
+#include "iwl-core.h"
+#include "iwl-debug.h"
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ssize_t iwl3945_ucode_rx_stats_read(struct file *file, char __user *user_buf,
+				    size_t count, loff_t *ppos);
+ssize_t iwl3945_ucode_tx_stats_read(struct file *file, char __user *user_buf,
+				    size_t count, loff_t *ppos);
+ssize_t iwl3945_ucode_general_stats_read(struct file *file,
+					 char __user *user_buf, size_t count,
+					 loff_t *ppos);
+#else
+static ssize_t iwl3945_ucode_rx_stats_read(struct file *file,
+					   char __user *user_buf, size_t count,
+					   loff_t *ppos)
+{
+	return 0;
+}
+static ssize_t iwl3945_ucode_tx_stats_read(struct file *file,
+					   char __user *user_buf, size_t count,
+					   loff_t *ppos)
+{
+	return 0;
+}
+static ssize_t iwl3945_ucode_general_stats_read(struct file *file,
+						char __user *user_buf,
+						size_t count, loff_t *ppos)
+{
+	return 0;
+}
+#endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c
index 59af259..17197a7 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.c
@@ -50,6 +50,7 @@
 #include "iwl-helpers.h"
 #include "iwl-led.h"
 #include "iwl-3945-led.h"
+#include "iwl-3945-debugfs.h"
 
 #define IWL_DECLARE_RATE_INFO(r, ip, in, rp, rn, pp, np)    \
 	[IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP,   \
@@ -293,7 +294,7 @@
  * iwl3945_rx_reply_tx - Handle Tx response
  */
 static void iwl3945_rx_reply_tx(struct iwl_priv *priv,
-			    struct iwl_rx_mem_buffer *rxb)
+				struct iwl_rx_mem_buffer *rxb)
 {
 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
 	u16 sequence = le16_to_cpu(pkt->hdr.sequence);
@@ -351,18 +352,81 @@
  *  RX handler implementations
  *
  *****************************************************************************/
+#ifdef CONFIG_IWLWIFI_DEBUG
+/*
+ *  based on the assumption of all statistics counter are in DWORD
+ *  FIXME: This function is for debugging, do not deal with
+ *  the case of counters roll-over.
+ */
+static void iwl3945_accumulative_statistics(struct iwl_priv *priv,
+					    __le32 *stats)
+{
+	int i;
+	__le32 *prev_stats;
+	u32 *accum_stats;
+	u32 *delta, *max_delta;
+
+	prev_stats = (__le32 *)&priv->_3945.statistics;
+	accum_stats = (u32 *)&priv->_3945.accum_statistics;
+	delta = (u32 *)&priv->_3945.delta_statistics;
+	max_delta = (u32 *)&priv->_3945.max_delta;
+
+	for (i = sizeof(__le32); i < sizeof(struct iwl3945_notif_statistics);
+	     i += sizeof(__le32), stats++, prev_stats++, delta++,
+	     max_delta++, accum_stats++) {
+		if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) {
+			*delta = (le32_to_cpu(*stats) -
+				le32_to_cpu(*prev_stats));
+			*accum_stats += *delta;
+			if (*delta > *max_delta)
+				*max_delta = *delta;
+		}
+	}
+
+	/* reset accumulative statistics for "no-counter" type statistics */
+	priv->_3945.accum_statistics.general.temperature =
+		priv->_3945.statistics.general.temperature;
+	priv->_3945.accum_statistics.general.ttl_timestamp =
+		priv->_3945.statistics.general.ttl_timestamp;
+}
+#endif
 
 void iwl3945_hw_rx_statistics(struct iwl_priv *priv,
 		struct iwl_rx_mem_buffer *rxb)
 {
 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
+
 	IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n",
 		     (int)sizeof(struct iwl3945_notif_statistics),
 		     le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK);
+#ifdef CONFIG_IWLWIFI_DEBUG
+	iwl3945_accumulative_statistics(priv, (__le32 *)&pkt->u.raw);
+#endif
 
 	memcpy(&priv->_3945.statistics, pkt->u.raw, sizeof(priv->_3945.statistics));
 }
 
+void iwl3945_reply_statistics(struct iwl_priv *priv,
+			      struct iwl_rx_mem_buffer *rxb)
+{
+	struct iwl_rx_packet *pkt = rxb_addr(rxb);
+	__le32 *flag = (__le32 *)&pkt->u.raw;
+
+	if (le32_to_cpu(*flag) & UCODE_STATISTICS_CLEAR_MSK) {
+#ifdef CONFIG_IWLWIFI_DEBUG
+		memset(&priv->_3945.accum_statistics, 0,
+			sizeof(struct iwl3945_notif_statistics));
+		memset(&priv->_3945.delta_statistics, 0,
+			sizeof(struct iwl3945_notif_statistics));
+		memset(&priv->_3945.max_delta, 0,
+			sizeof(struct iwl3945_notif_statistics));
+#endif
+		IWL_DEBUG_RX(priv, "Statistics have been cleared\n");
+	}
+	iwl3945_hw_rx_statistics(priv, rxb);
+}
+
+
 /******************************************************************************
  *
  * Misc. internal state and helper functions
@@ -2736,6 +2800,12 @@
 	.isr = iwl_isr_legacy,
 	.config_ap = iwl3945_config_ap,
 	.add_bcast_station = iwl3945_add_bcast_station,
+
+	.debugfs_ops = {
+		.rx_stats_read = iwl3945_ucode_rx_stats_read,
+		.tx_stats_read = iwl3945_ucode_tx_stats_read,
+		.general_stats_read = iwl3945_ucode_general_stats_read,
+	},
 };
 
 static struct iwl_hcmd_utils_ops iwl3945_hcmd_utils = {
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlwifi/iwl-3945.h
index e9674f0..643adb6 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.h
@@ -264,6 +264,8 @@
 extern int iwl3945_hw_reg_set_txpower(struct iwl_priv *priv, s8 power);
 extern void iwl3945_hw_rx_statistics(struct iwl_priv *priv,
 				 struct iwl_rx_mem_buffer *rxb);
+void iwl3945_reply_statistics(struct iwl_priv *priv,
+			      struct iwl_rx_mem_buffer *rxb);
 extern void iwl3945_disable_events(struct iwl_priv *priv);
 extern int iwl4965_get_temperature(const struct iwl_priv *priv);
 extern void iwl3945_post_associate(struct iwl_priv *priv);
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index e434936..115d3ea 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -200,26 +200,57 @@
 
 	/* Set initial sensitivity parameters */
 	/* Set initial calibration set */
-	switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) {
-	case CSR_HW_REV_TYPE_5150:
-		priv->hw_params.sens = &iwl5150_sensitivity;
-		priv->hw_params.calib_init_cfg =
-			BIT(IWL_CALIB_DC)		|
-			BIT(IWL_CALIB_LO)		|
-			BIT(IWL_CALIB_TX_IQ) 		|
-			BIT(IWL_CALIB_BASE_BAND);
+	priv->hw_params.sens = &iwl5000_sensitivity;
+	priv->hw_params.calib_init_cfg =
+		BIT(IWL_CALIB_XTAL)		|
+		BIT(IWL_CALIB_LO)		|
+		BIT(IWL_CALIB_TX_IQ)		|
+		BIT(IWL_CALIB_TX_IQ_PERD)	|
+		BIT(IWL_CALIB_BASE_BAND);
 
-		break;
-	default:
-		priv->hw_params.sens = &iwl5000_sensitivity;
-		priv->hw_params.calib_init_cfg =
-			BIT(IWL_CALIB_XTAL)		|
-			BIT(IWL_CALIB_LO)		|
-			BIT(IWL_CALIB_TX_IQ) 		|
-			BIT(IWL_CALIB_TX_IQ_PERD)	|
-			BIT(IWL_CALIB_BASE_BAND);
-		break;
-	}
+	return 0;
+}
+
+static int iwl5150_hw_set_hw_params(struct iwl_priv *priv)
+{
+	if (priv->cfg->mod_params->num_of_queues >= IWL_MIN_NUM_QUEUES &&
+	    priv->cfg->mod_params->num_of_queues <= IWLAGN_NUM_QUEUES)
+		priv->cfg->num_of_queues =
+			priv->cfg->mod_params->num_of_queues;
+
+	priv->hw_params.max_txq_num = priv->cfg->num_of_queues;
+	priv->hw_params.dma_chnl_num = FH50_TCSR_CHNL_NUM;
+	priv->hw_params.scd_bc_tbls_size =
+			priv->cfg->num_of_queues *
+			sizeof(struct iwlagn_scd_bc_tbl);
+	priv->hw_params.tfd_size = sizeof(struct iwl_tfd);
+	priv->hw_params.max_stations = IWL5000_STATION_COUNT;
+	priv->hw_params.bcast_sta_id = IWL5000_BROADCAST_ID;
+
+	priv->hw_params.max_data_size = IWLAGN_RTC_DATA_SIZE;
+	priv->hw_params.max_inst_size = IWLAGN_RTC_INST_SIZE;
+
+	priv->hw_params.max_bsm_size = 0;
+	priv->hw_params.ht40_channel =  BIT(IEEE80211_BAND_2GHZ) |
+					BIT(IEEE80211_BAND_5GHZ);
+	priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR;
+
+	priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant);
+	priv->hw_params.rx_chains_num = num_of_ant(priv->cfg->valid_rx_ant);
+	priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant;
+	priv->hw_params.valid_rx_ant = priv->cfg->valid_rx_ant;
+
+	if (priv->cfg->ops->lib->temp_ops.set_ct_kill)
+		priv->cfg->ops->lib->temp_ops.set_ct_kill(priv);
+
+	/* Set initial sensitivity parameters */
+	/* Set initial calibration set */
+	priv->hw_params.sens = &iwl5150_sensitivity;
+	priv->hw_params.calib_init_cfg =
+		BIT(IWL_CALIB_DC)		|
+		BIT(IWL_CALIB_LO)		|
+		BIT(IWL_CALIB_TX_IQ)		|
+		BIT(IWL_CALIB_BASE_BAND);
 
 	return 0;
 }
@@ -332,7 +363,7 @@
 };
 
 static struct iwl_lib_ops iwl5150_lib = {
-	.set_hw_params = iwl5000_hw_set_hw_params,
+	.set_hw_params = iwl5150_hw_set_hw_params,
 	.txq_update_byte_cnt_tbl = iwlagn_txq_update_byte_cnt_tbl,
 	.txq_inval_byte_cnt_tbl = iwlagn_txq_inval_byte_cnt_tbl,
 	.txq_set_sched = iwlagn_txq_set_sched,
diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c
index 7da23d3..7acef70 100644
--- a/drivers/net/wireless/iwlwifi/iwl-6000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-6000.c
@@ -176,24 +176,56 @@
 	/* Set initial sensitivity parameters */
 	/* Set initial calibration set */
 	priv->hw_params.sens = &iwl6000_sensitivity;
-	switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) {
-	case CSR_HW_REV_TYPE_6x50:
-		priv->hw_params.calib_init_cfg =
-			BIT(IWL_CALIB_XTAL)		|
-			BIT(IWL_CALIB_DC)		|
-			BIT(IWL_CALIB_LO)		|
-			BIT(IWL_CALIB_TX_IQ) 		|
-			BIT(IWL_CALIB_BASE_BAND);
+	priv->hw_params.calib_init_cfg =
+		BIT(IWL_CALIB_XTAL)		|
+		BIT(IWL_CALIB_LO)		|
+		BIT(IWL_CALIB_TX_IQ)		|
+		BIT(IWL_CALIB_BASE_BAND);
 
-		break;
-	default:
-		priv->hw_params.calib_init_cfg =
-			BIT(IWL_CALIB_XTAL)		|
-			BIT(IWL_CALIB_LO)		|
-			BIT(IWL_CALIB_TX_IQ) 		|
-			BIT(IWL_CALIB_BASE_BAND);
-		break;
-	}
+	return 0;
+}
+
+static int iwl6050_hw_set_hw_params(struct iwl_priv *priv)
+{
+	if (priv->cfg->mod_params->num_of_queues >= IWL_MIN_NUM_QUEUES &&
+	    priv->cfg->mod_params->num_of_queues <= IWLAGN_NUM_QUEUES)
+		priv->cfg->num_of_queues =
+			priv->cfg->mod_params->num_of_queues;
+
+	priv->hw_params.max_txq_num = priv->cfg->num_of_queues;
+	priv->hw_params.dma_chnl_num = FH50_TCSR_CHNL_NUM;
+	priv->hw_params.scd_bc_tbls_size =
+			priv->cfg->num_of_queues *
+			sizeof(struct iwlagn_scd_bc_tbl);
+	priv->hw_params.tfd_size = sizeof(struct iwl_tfd);
+	priv->hw_params.max_stations = IWL5000_STATION_COUNT;
+	priv->hw_params.bcast_sta_id = IWL5000_BROADCAST_ID;
+
+	priv->hw_params.max_data_size = IWL60_RTC_DATA_SIZE;
+	priv->hw_params.max_inst_size = IWL60_RTC_INST_SIZE;
+
+	priv->hw_params.max_bsm_size = 0;
+	priv->hw_params.ht40_channel =  BIT(IEEE80211_BAND_2GHZ) |
+					BIT(IEEE80211_BAND_5GHZ);
+	priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR;
+
+	priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant);
+	priv->hw_params.rx_chains_num = num_of_ant(priv->cfg->valid_rx_ant);
+	priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant;
+	priv->hw_params.valid_rx_ant = priv->cfg->valid_rx_ant;
+
+	if (priv->cfg->ops->lib->temp_ops.set_ct_kill)
+		priv->cfg->ops->lib->temp_ops.set_ct_kill(priv);
+
+	/* Set initial sensitivity parameters */
+	/* Set initial calibration set */
+	priv->hw_params.sens = &iwl6000_sensitivity;
+	priv->hw_params.calib_init_cfg =
+		BIT(IWL_CALIB_XTAL)		|
+		BIT(IWL_CALIB_DC)		|
+		BIT(IWL_CALIB_LO)		|
+		BIT(IWL_CALIB_TX_IQ)		|
+		BIT(IWL_CALIB_BASE_BAND);
 
 	return 0;
 }
@@ -304,7 +336,7 @@
 };
 
 static struct iwl_lib_ops iwl6050_lib = {
-	.set_hw_params = iwl6000_hw_set_hw_params,
+	.set_hw_params = iwl6050_hw_set_hw_params,
 	.txq_update_byte_cnt_tbl = iwlagn_txq_update_byte_cnt_tbl,
 	.txq_inval_byte_cnt_tbl = iwlagn_txq_inval_byte_cnt_tbl,
 	.txq_set_sched = iwlagn_txq_set_sched,
@@ -468,7 +500,6 @@
 	.pa_type = IWL_PA_INTERNAL,
 	.max_ll_items = OTP_MAX_LL_ITEMS_6x00,
 	.shadow_ram_support = true,
-	.ht_greenfield_support = true,
 	.led_compensation = 51,
 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
 	.supports_idle = true,
@@ -501,7 +532,6 @@
 	.pa_type = IWL_PA_INTERNAL,
 	.max_ll_items = OTP_MAX_LL_ITEMS_6x00,
 	.shadow_ram_support = true,
-	.ht_greenfield_support = true,
 	.led_compensation = 51,
 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
 	.supports_idle = true,
@@ -568,7 +598,6 @@
 	.pa_type = IWL_PA_SYSTEM,
 	.max_ll_items = OTP_MAX_LL_ITEMS_6x50,
 	.shadow_ram_support = true,
-	.ht_greenfield_support = true,
 	.led_compensation = 51,
 	.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
 	.supports_idle = true,
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
index 4bd0aec..a273474 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
@@ -1395,16 +1395,29 @@
 			rate = IWL_RATE_1M_PLCP;
 			rate_flags = RATE_MCS_CCK_MSK;
 		}
-		scan->good_CRC_th = 0;
+		scan->good_CRC_th = IWL_GOOD_CRC_TH_DISABLED;
 		break;
 	case IEEE80211_BAND_5GHZ:
 		rate = IWL_RATE_6M_PLCP;
 		/*
-		 * If active scaning is requested but a certain channel
-		 * is marked passive, we can do active scanning if we
-		 * detect transmissions.
+		 * If active scanning is requested but a certain channel is
+		 * marked passive, we can do active scanning if we detect
+		 * transmissions.
+		 *
+		 * There is an issue with some firmware versions that triggers
+		 * a sysassert on a "good CRC threshold" of zero (== disabled),
+		 * on a radar channel even though this means that we should NOT
+		 * send probes.
+		 *
+		 * The "good CRC threshold" is the number of frames that we
+		 * need to receive during our dwell time on a channel before
+		 * sending out probes -- setting this to a huge value will
+		 * mean we never reach it, but at the same time work around
+		 * the aforementioned issue. Thus use IWL_GOOD_CRC_TH_NEVER
+		 * here instead of IWL_GOOD_CRC_TH_DISABLED.
 		 */
-		scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH : 0;
+		scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT :
+						IWL_GOOD_CRC_TH_NEVER;
 		break;
 	default:
 		IWL_WARN(priv, "Invalid scan band count\n");
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
index 0e6161d..bfcac56 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
@@ -868,14 +868,14 @@
 		rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type,
 				&rs_index);
 		rs_collect_tx_data(curr_tbl, rs_index,
-				   info->status.ampdu_ack_len,
-				   info->status.ampdu_ack_map);
+				   info->status.ampdu_len,
+				   info->status.ampdu_ack_len);
 
 		/* Update success/fail counts if not searching for new mode */
 		if (lq_sta->stay_in_tbl) {
-			lq_sta->total_success += info->status.ampdu_ack_map;
-			lq_sta->total_failed += (info->status.ampdu_ack_len -
-					info->status.ampdu_ack_map);
+			lq_sta->total_success += info->status.ampdu_ack_len;
+			lq_sta->total_failed += (info->status.ampdu_len -
+					info->status.ampdu_ack_len);
 		}
 	} else {
 	/*
@@ -2078,10 +2078,12 @@
 	}
 	/* Else we have enough samples; calculate estimate of
 	 * actual average throughput */
-
-	/* Sanity-check TPT calculations */
-	BUG_ON(window->average_tpt != ((window->success_ratio *
-			tbl->expected_tpt[index] + 64) / 128));
+	if (window->average_tpt != ((window->success_ratio *
+			tbl->expected_tpt[index] + 64) / 128)) {
+		IWL_ERR(priv, "expected_tpt should have been calculated by now\n");
+		window->average_tpt = ((window->success_ratio *
+					tbl->expected_tpt[index] + 64) / 128);
+	}
 
 	/* If we are searching for better modulation mode, check success. */
 	if (lq_sta->search_better_tbl &&
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
index 11661fa..c2a5c85 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
@@ -83,6 +83,15 @@
 	return ac_to_fifo[ac];
 }
 
+static inline int get_ac_from_tid(u16 tid)
+{
+	if (likely(tid < ARRAY_SIZE(tid_to_ac)))
+		return tid_to_ac[tid];
+
+	/* no support for TIDs 8-15 yet */
+	return -EINVAL;
+}
+
 static inline int get_fifo_from_tid(u16 tid)
 {
 	if (likely(tid < ARRAY_SIZE(tid_to_ac)))
@@ -991,7 +1000,7 @@
 	tid_data = &priv->stations[sta_id].tid[tid];
 	*ssn = SEQ_TO_SN(tid_data->seq_number);
 	tid_data->agg.txq_id = txq_id;
-	priv->txq[txq_id].swq_id = iwl_virtual_agg_queue_num(tx_fifo, txq_id);
+	priv->txq[txq_id].swq_id = iwl_virtual_agg_queue_num(get_ac_from_tid(tid), txq_id);
 	spin_unlock_irqrestore(&priv->sta_lock, flags);
 
 	ret = priv->cfg->ops->lib->txq_agg_enable(priv, txq_id, tx_fifo,
@@ -1224,8 +1233,9 @@
 	memset(&info->status, 0, sizeof(info->status));
 	info->flags |= IEEE80211_TX_STAT_ACK;
 	info->flags |= IEEE80211_TX_STAT_AMPDU;
-	info->status.ampdu_ack_map = successes;
-	info->status.ampdu_ack_len = agg->frame_count;
+	info->status.ampdu_ack_len = successes;
+	info->status.ampdu_ack_map = bitmap;
+	info->status.ampdu_len = agg->frame_count;
 	iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags, info);
 
 	IWL_DEBUG_TX_REPLY(priv, "Bitmap %llx\n", (unsigned long long)bitmap);
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 00344d6..a672d33 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -2654,7 +2654,6 @@
 
 	/* Tell mac80211 our characteristics */
 	hw->flags = IEEE80211_HW_SIGNAL_DBM |
-		    IEEE80211_HW_NOISE_DBM |
 		    IEEE80211_HW_AMPDU_AGGREGATION |
 		    IEEE80211_HW_SPECTRUM_MGMT;
 
@@ -3002,18 +3001,6 @@
 	return 0;
 }
 
-static int iwl_mac_get_stats(struct ieee80211_hw *hw,
-			     struct ieee80211_low_level_stats *stats)
-{
-	struct iwl_priv *priv = hw->priv;
-
-	priv = hw->priv;
-	IWL_DEBUG_MAC80211(priv, "enter\n");
-	IWL_DEBUG_MAC80211(priv, "leave\n");
-
-	return 0;
-}
-
 static void iwl_mac_sta_notify(struct ieee80211_hw *hw,
 			       struct ieee80211_vif *vif,
 			       enum sta_notify_cmd cmd,
@@ -3391,7 +3378,6 @@
 	.configure_filter = iwl_configure_filter,
 	.set_key = iwl_mac_set_key,
 	.update_tkip_key = iwl_mac_update_tkip_key,
-	.get_stats = iwl_mac_get_stats,
 	.conf_tx = iwl_mac_conf_tx,
 	.reset_tsf = iwl_mac_reset_tsf,
 	.bss_info_changed = iwl_bss_info_changed,
diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h
index 67c723c..0086019 100644
--- a/drivers/net/wireless/iwlwifi/iwl-commands.h
+++ b/drivers/net/wireless/iwlwifi/iwl-commands.h
@@ -2663,7 +2663,9 @@
 #define PROBE_OPTION_MAX_3945		4
 #define PROBE_OPTION_MAX		20
 #define TX_CMD_LIFE_TIME_INFINITE	cpu_to_le32(0xFFFFFFFF)
-#define IWL_GOOD_CRC_TH			cpu_to_le16(1)
+#define IWL_GOOD_CRC_TH_DISABLED	0
+#define IWL_GOOD_CRC_TH_DEFAULT		cpu_to_le16(1)
+#define IWL_GOOD_CRC_TH_NEVER		cpu_to_le16(0xffff)
 #define IWL_MAX_SCAN_SIZE 1024
 #define IWL_MAX_CMD_SIZE 4096
 #define IWL_MAX_PROBE_REQUEST		200
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
index 56a9f17..4cdf4d3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -2117,10 +2117,6 @@
 
 /**
  * iwl_mac_config - mac80211 config callback
- *
- * We ignore conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME since it seems to
- * be set inappropriately and the driver currently sets the hardware up to
- * use it whenever needed.
  */
 int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
 {
@@ -2935,6 +2931,12 @@
 	struct iwl_priv *priv = pci_get_drvdata(pdev);
 	int ret;
 
+	/*
+	 * We disable the RETRY_TIMEOUT register (0x41) to keep
+	 * PCI Tx retries from interfering with C3 CPU state.
+	 */
+	pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
+
 	pci_set_power_state(pdev, PCI_D0);
 	ret = pci_enable_device(pdev);
 	if (ret)
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index a0cc11e..7273609 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -512,7 +512,9 @@
 void iwl_init_scan_params(struct iwl_priv *priv);
 int iwl_scan_cancel(struct iwl_priv *priv);
 int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms);
-int iwl_mac_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req);
+int iwl_mac_hw_scan(struct ieee80211_hw *hw,
+		    struct ieee80211_vif *vif,
+		    struct cfg80211_scan_request *req);
 void iwl_internal_short_hw_scan(struct iwl_priv *priv);
 int iwl_force_reset(struct iwl_priv *priv, int mode);
 u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame,
diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
index 61faf2d..4aabb54 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
@@ -1059,10 +1059,8 @@
 					size_t count, loff_t *ppos)
 {
 	struct iwl_priv *priv = file->private_data;
-	if (priv->cfg->ops->lib->debugfs_ops.rx_stats_read)
-		return priv->cfg->ops->lib->debugfs_ops.rx_stats_read(file,
-						user_buf, count, ppos);
-	return 0;
+	return priv->cfg->ops->lib->debugfs_ops.rx_stats_read(file,
+			user_buf, count, ppos);
 }
 
 static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file,
@@ -1070,10 +1068,8 @@
 					size_t count, loff_t *ppos)
 {
 	struct iwl_priv *priv = file->private_data;
-	if (priv->cfg->ops->lib->debugfs_ops.tx_stats_read)
-		return priv->cfg->ops->lib->debugfs_ops.tx_stats_read(file,
-						user_buf, count, ppos);
-	return 0;
+	return priv->cfg->ops->lib->debugfs_ops.tx_stats_read(file,
+			user_buf, count, ppos);
 }
 
 static ssize_t iwl_dbgfs_ucode_general_stats_read(struct file *file,
@@ -1081,10 +1077,8 @@
 					size_t count, loff_t *ppos)
 {
 	struct iwl_priv *priv = file->private_data;
-	if (priv->cfg->ops->lib->debugfs_ops.general_stats_read)
-		return priv->cfg->ops->lib->debugfs_ops.general_stats_read(file,
-						user_buf, count, ppos);
-	return 0;
+	return priv->cfg->ops->lib->debugfs_ops.general_stats_read(file,
+			user_buf, count, ppos);
 }
 
 static ssize_t iwl_dbgfs_sensitivity_read(struct file *file,
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
index bdc60aa..58c69a5 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -1202,6 +1202,11 @@
 			struct delayed_work rfkill_poll;
 
 			struct iwl3945_notif_statistics statistics;
+#ifdef CONFIG_IWLWIFI_DEBUG
+			struct iwl3945_notif_statistics accum_statistics;
+			struct iwl3945_notif_statistics delta_statistics;
+			struct iwl3945_notif_statistics max_delta;
+#endif
 
 			u32 sta_supp_rates;
 			int last_rx_rssi;	/* From Rx packet statistics */
diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c
index f4b8978..d12fd55 100644
--- a/drivers/net/wireless/iwlwifi/iwl-scan.c
+++ b/drivers/net/wireless/iwlwifi/iwl-scan.c
@@ -313,7 +313,8 @@
 }
 
 int iwl_mac_hw_scan(struct ieee80211_hw *hw,
-		     struct cfg80211_scan_request *req)
+		    struct ieee80211_vif *vif,
+		    struct cfg80211_scan_request *req)
 {
 	struct iwl_priv *priv = hw->priv;
 	int ret;
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index a41ba72..c7e1d7d 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -957,7 +957,7 @@
 	 * statistics request from the host as well as for the periodic
 	 * statistics notifications (after received beacons) from the uCode.
 	 */
-	priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl3945_hw_rx_statistics;
+	priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl3945_reply_statistics;
 	priv->rx_handlers[STATISTICS_NOTIFICATION] = iwl3945_hw_rx_statistics;
 
 	iwl_setup_rx_scan_handlers(priv);
@@ -2939,7 +2939,8 @@
 		 * is marked passive, we can do active scanning if we
 		 * detect transmissions.
 		 */
-		scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH : 0;
+		scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT :
+						IWL_GOOD_CRC_TH_DISABLED;
 		band = IEEE80211_BAND_5GHZ;
 		break;
 	default:
@@ -3873,7 +3874,6 @@
 
 	/* Tell mac80211 our characteristics */
 	hw->flags = IEEE80211_HW_SIGNAL_DBM |
-		    IEEE80211_HW_NOISE_DBM |
 		    IEEE80211_HW_SPECTRUM_MGMT;
 
 	if (!priv->cfg->broken_powersave)
diff --git a/drivers/net/wireless/iwmc3200wifi/bus.h b/drivers/net/wireless/iwmc3200wifi/bus.h
index 836663e..62edd58 100644
--- a/drivers/net/wireless/iwmc3200wifi/bus.h
+++ b/drivers/net/wireless/iwmc3200wifi/bus.h
@@ -31,7 +31,7 @@
 	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_init)(struct iwm_priv *iwm, struct dentry *parent_dir);
 	void (*debugfs_exit)(struct iwm_priv *iwm);
 
 	const char *umac_name;
diff --git a/drivers/net/wireless/iwmc3200wifi/debug.h b/drivers/net/wireless/iwmc3200wifi/debug.h
index e35c9b6..a0c13a4 100644
--- a/drivers/net/wireless/iwmc3200wifi/debug.h
+++ b/drivers/net/wireless/iwmc3200wifi/debug.h
@@ -113,13 +113,10 @@
 };
 
 #ifdef CONFIG_IWM_DEBUG
-int iwm_debugfs_init(struct iwm_priv *iwm);
+void 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_init(struct iwm_priv *iwm) {}
 static inline void iwm_debugfs_exit(struct iwm_priv *iwm) {}
 #endif
 
diff --git a/drivers/net/wireless/iwmc3200wifi/debugfs.c b/drivers/net/wireless/iwmc3200wifi/debugfs.c
index 7244413..53b0b77 100644
--- a/drivers/net/wireless/iwmc3200wifi/debugfs.c
+++ b/drivers/net/wireless/iwmc3200wifi/debugfs.c
@@ -48,12 +48,11 @@
 
 #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;        \
+	dbg.dbg_module_dentries[id] =			\
+		debugfs_create_x8(name, 0600,		\
+				dbg.dbgdir,		\
+				&(dbg.dbg_module[id]));	\
 } while (0)
 
 static int iwm_debugfs_u32_read(void *data, u64 *val)
@@ -423,89 +422,29 @@
 	.read =		iwm_debugfs_fw_err_read,
 };
 
-int iwm_debugfs_init(struct iwm_priv *iwm)
+void iwm_debugfs_init(struct iwm_priv *iwm)
 {
-	int i, result;
-	char devdir[16];
+	int i;
 
 	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.devdir = debugfs_create_dir(wiphy_name(iwm_to_wiphy(iwm)),
+					     iwm->dbg.rootdir);
 	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;
-		}
-	}
-
+	if (iwm->bus_ops->debugfs_init)
+		iwm->bus_ops->debugfs_init(iwm, iwm->dbg.busdir);
 
 	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,
@@ -514,44 +453,15 @@
 	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;
-	}
-
 	iwm->dbg.fw_err_dentry = debugfs_create_file("last_fw_err", 0200,
 						     iwm->dbg.dbgdir, iwm,
 						     &iwm_debugfs_fw_err_fops);
-	result = PTR_ERR(iwm->dbg.fw_err_dentry);
-	if (IS_ERR(iwm->dbg.fw_err_dentry) && (result != -ENODEV)) {
-		IWM_ERR(iwm, "Couldn't create last FW err: %d\n", result);
-		goto error;
-	}
-
-
-	return 0;
-
- error:
-	return result;
 }
 
 void iwm_debugfs_exit(struct iwm_priv *iwm)
diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.c b/drivers/net/wireless/iwmc3200wifi/sdio.c
index 1eafd6d..1acea37 100644
--- a/drivers/net/wireless/iwmc3200wifi/sdio.c
+++ b/drivers/net/wireless/iwmc3200wifi/sdio.c
@@ -366,21 +366,13 @@
 	.read =		iwm_debugfs_sdio_read,
 };
 
-static int if_sdio_debugfs_init(struct iwm_priv *iwm, struct dentry *parent_dir)
+static void 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)
@@ -440,11 +432,7 @@
 	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;
-	}
+	iwm_debugfs_init(iwm);
 
 	sdio_set_drvdata(func, hw);
 
@@ -473,7 +461,6 @@
 	destroy_workqueue(hw->isr_wq);
  debugfs_exit:
 	iwm_debugfs_exit(iwm);
- if_free:
 	iwm_if_free(iwm);
 	return ret;
 }
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
index 13dfeda..64dd345 100644
--- a/drivers/net/wireless/libertas/if_sdio.c
+++ b/drivers/net/wireless/libertas/if_sdio.c
@@ -324,7 +324,9 @@
 	timeout = jiffies + HZ;
 	while (1) {
 		status = sdio_readb(card->func, IF_SDIO_STATUS, &ret);
-		if (ret || (status & condition))
+		if (ret)
+			return ret;
+		if ((status & condition) == condition)
 			break;
 		if (time_after(jiffies, timeout))
 			return -ETIMEDOUT;
diff --git a/drivers/net/wireless/libertas_tf/cmd.c b/drivers/net/wireless/libertas_tf/cmd.c
index b620daf..8945afd 100644
--- a/drivers/net/wireless/libertas_tf/cmd.c
+++ b/drivers/net/wireless/libertas_tf/cmd.c
@@ -7,6 +7,8 @@
  *  the Free Software Foundation; either version 2 of the License, or (at
  *  your option) any later version.
  */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/slab.h>
 
 #include "libertas_tf.h"
@@ -82,6 +84,8 @@
 	int ret = -1;
 	u32 i;
 
+	lbtf_deb_enter(LBTF_DEB_CMD);
+
 	memset(&cmd, 0, sizeof(cmd));
 	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
 	memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN);
@@ -104,6 +108,8 @@
 		priv->fwrelease >>  8 & 0xff,
 		priv->fwrelease       & 0xff,
 		priv->fwcapinfo);
+	lbtf_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n",
+		    cmd.hwifversion, cmd.version);
 
 	/* Clamp region code to 8-bit since FW spec indicates that it should
 	 * only ever be 8-bit, even though the field size is 16-bit.  Some
@@ -118,8 +124,10 @@
 	}
 
 	/* if it's unidentified region code, use the default (USA) */
-	if (i >= MRVDRV_MAX_REGION_CODE)
+	if (i >= MRVDRV_MAX_REGION_CODE) {
 		priv->regioncode = 0x10;
+		pr_info("unidentified region code; using the default (USA)\n");
+	}
 
 	if (priv->current_addr[0] == 0xff)
 		memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN);
@@ -128,6 +136,7 @@
 
 	lbtf_geo_init(priv);
 out:
+	lbtf_deb_leave(LBTF_DEB_CMD);
 	return ret;
 }
 
@@ -141,13 +150,18 @@
  */
 int lbtf_set_channel(struct lbtf_private *priv, u8 channel)
 {
+	int ret = 0;
 	struct cmd_ds_802_11_rf_channel cmd;
 
+	lbtf_deb_enter(LBTF_DEB_CMD);
+
 	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
 	cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET);
 	cmd.channel = cpu_to_le16(channel);
 
-	return lbtf_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd);
+	ret = lbtf_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd);
+	lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret);
+	return ret;
 }
 
 int lbtf_beacon_set(struct lbtf_private *priv, struct sk_buff *beacon)
@@ -155,20 +169,28 @@
 	struct cmd_ds_802_11_beacon_set cmd;
 	int size;
 
-	if (beacon->len > MRVL_MAX_BCN_SIZE)
+	lbtf_deb_enter(LBTF_DEB_CMD);
+
+	if (beacon->len > MRVL_MAX_BCN_SIZE) {
+		lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", -1);
 		return -1;
+	}
 	size =  sizeof(cmd) - sizeof(cmd.beacon) + beacon->len;
 	cmd.hdr.size = cpu_to_le16(size);
 	cmd.len = cpu_to_le16(beacon->len);
 	memcpy(cmd.beacon, (u8 *) beacon->data, beacon->len);
 
 	lbtf_cmd_async(priv, CMD_802_11_BEACON_SET, &cmd.hdr, size);
+
+	lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", 0);
 	return 0;
 }
 
 int lbtf_beacon_ctrl(struct lbtf_private *priv, bool beacon_enable,
-		     int beacon_int) {
+		     int beacon_int)
+{
 	struct cmd_ds_802_11_beacon_control cmd;
+	lbtf_deb_enter(LBTF_DEB_CMD);
 
 	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
 	cmd.action = cpu_to_le16(CMD_ACT_SET);
@@ -176,6 +198,8 @@
 	cmd.beacon_period = cpu_to_le16(beacon_int);
 
 	lbtf_cmd_async(priv, CMD_802_11_BEACON_CTRL, &cmd.hdr, sizeof(cmd));
+
+	lbtf_deb_leave(LBTF_DEB_CMD);
 	return 0;
 }
 
@@ -183,17 +207,28 @@
 			  struct cmd_ctrl_node *cmdnode)
 {
 	unsigned long flags;
+	lbtf_deb_enter(LBTF_DEB_HOST);
 
-	if (!cmdnode)
-		return;
+	if (!cmdnode) {
+		lbtf_deb_host("QUEUE_CMD: cmdnode is NULL\n");
+		goto qcmd_done;
+	}
 
-	if (!cmdnode->cmdbuf->size)
-		return;
+	if (!cmdnode->cmdbuf->size) {
+		lbtf_deb_host("DNLD_CMD: cmd size is zero\n");
+		goto qcmd_done;
+	}
 
 	cmdnode->result = 0;
 	spin_lock_irqsave(&priv->driver_lock, flags);
 	list_add_tail(&cmdnode->list, &priv->cmdpendingq);
 	spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+	lbtf_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n",
+		     le16_to_cpu(cmdnode->cmdbuf->command));
+
+qcmd_done:
+	lbtf_deb_leave(LBTF_DEB_HOST);
 }
 
 static void lbtf_submit_command(struct lbtf_private *priv,
@@ -206,22 +241,33 @@
 	int timeo = 5 * HZ;
 	int ret;
 
+	lbtf_deb_enter(LBTF_DEB_HOST);
+
 	cmd = cmdnode->cmdbuf;
 
 	spin_lock_irqsave(&priv->driver_lock, flags);
 	priv->cur_cmd = cmdnode;
 	cmdsize = le16_to_cpu(cmd->size);
 	command = le16_to_cpu(cmd->command);
+
+	lbtf_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n",
+		     command, le16_to_cpu(cmd->seqnum), cmdsize);
+	lbtf_deb_hex(LBTF_DEB_CMD, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize);
+
 	ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize);
 	spin_unlock_irqrestore(&priv->driver_lock, flags);
 
-	if (ret)
+	if (ret) {
+		pr_info("DNLD_CMD: hw_host_to_card failed: %d\n", ret);
 		/* Let the timer kick in and retry, and potentially reset
 		   the whole thing if the condition persists */
 		timeo = HZ;
+	}
 
 	/* Setup the timer after transmit command */
 	mod_timer(&priv->command_timer, jiffies + timeo);
+
+	lbtf_deb_leave(LBTF_DEB_HOST);
 }
 
 /**
@@ -231,8 +277,10 @@
 static void __lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv,
 					 struct cmd_ctrl_node *cmdnode)
 {
+	lbtf_deb_enter(LBTF_DEB_HOST);
+
 	if (!cmdnode)
-		return;
+		goto cl_ins_out;
 
 	cmdnode->callback = NULL;
 	cmdnode->callback_arg = 0;
@@ -240,6 +288,9 @@
 	memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE);
 
 	list_add_tail(&cmdnode->list, &priv->cmdfreeq);
+
+cl_ins_out:
+	lbtf_deb_leave(LBTF_DEB_HOST);
 }
 
 static void lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv,
@@ -268,29 +319,41 @@
 {
 	struct cmd_ds_mac_multicast_addr cmd;
 
+	lbtf_deb_enter(LBTF_DEB_CMD);
+
 	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
 	cmd.action = cpu_to_le16(CMD_ACT_SET);
 
 	cmd.nr_of_adrs = cpu_to_le16((u16) priv->nr_of_multicastmacaddr);
+
+	lbtf_deb_cmd("MULTICAST_ADR: setting %d addresses\n", cmd.nr_of_adrs);
+
 	memcpy(cmd.maclist, priv->multicastlist,
 	       priv->nr_of_multicastmacaddr * ETH_ALEN);
 
 	lbtf_cmd_async(priv, CMD_MAC_MULTICAST_ADR, &cmd.hdr, sizeof(cmd));
+
+	lbtf_deb_leave(LBTF_DEB_CMD);
 	return 0;
 }
 
 void lbtf_set_mode(struct lbtf_private *priv, enum lbtf_mode mode)
 {
 	struct cmd_ds_set_mode cmd;
+	lbtf_deb_enter(LBTF_DEB_WEXT);
 
 	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
 	cmd.mode = cpu_to_le16(mode);
+	lbtf_deb_wext("Switching to mode: 0x%x\n", mode);
 	lbtf_cmd_async(priv, CMD_802_11_SET_MODE, &cmd.hdr, sizeof(cmd));
+
+	lbtf_deb_leave(LBTF_DEB_WEXT);
 }
 
 void lbtf_set_bssid(struct lbtf_private *priv, bool activate, const u8 *bssid)
 {
 	struct cmd_ds_set_bssid cmd;
+	lbtf_deb_enter(LBTF_DEB_CMD);
 
 	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
 	cmd.activate = activate ? 1 : 0;
@@ -298,11 +361,13 @@
 		memcpy(cmd.bssid, bssid, ETH_ALEN);
 
 	lbtf_cmd_async(priv, CMD_802_11_SET_BSSID, &cmd.hdr, sizeof(cmd));
+	lbtf_deb_leave(LBTF_DEB_CMD);
 }
 
 int lbtf_set_mac_address(struct lbtf_private *priv, uint8_t *mac_addr)
 {
 	struct cmd_ds_802_11_mac_address cmd;
+	lbtf_deb_enter(LBTF_DEB_CMD);
 
 	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
 	cmd.action = cpu_to_le16(CMD_ACT_SET);
@@ -310,6 +375,7 @@
 	memcpy(cmd.macadd, mac_addr, ETH_ALEN);
 
 	lbtf_cmd_async(priv, CMD_802_11_MAC_ADDRESS, &cmd.hdr, sizeof(cmd));
+	lbtf_deb_leave(LBTF_DEB_CMD);
 	return 0;
 }
 
@@ -318,6 +384,8 @@
 	int ret = 0;
 	struct cmd_ds_802_11_radio_control cmd;
 
+	lbtf_deb_enter(LBTF_DEB_CMD);
+
 	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
 	cmd.action = cpu_to_le16(CMD_ACT_SET);
 
@@ -341,19 +409,28 @@
 	else
 		cmd.control &= cpu_to_le16(~TURN_ON_RF);
 
+	lbtf_deb_cmd("RADIO_SET: radio %d, preamble %d\n", priv->radioon,
+		    priv->preamble);
+
 	ret = lbtf_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd);
+
+	lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret);
 	return ret;
 }
 
 void lbtf_set_mac_control(struct lbtf_private *priv)
 {
 	struct cmd_ds_mac_control cmd;
+	lbtf_deb_enter(LBTF_DEB_CMD);
+
 	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
 	cmd.action = cpu_to_le16(priv->mac_control);
 	cmd.reserved = 0;
 
 	lbtf_cmd_async(priv, CMD_MAC_CONTROL,
 		&cmd.hdr, sizeof(cmd));
+
+	lbtf_deb_leave(LBTF_DEB_CMD);
 }
 
 /**
@@ -365,29 +442,43 @@
  */
 int lbtf_allocate_cmd_buffer(struct lbtf_private *priv)
 {
+	int ret = 0;
 	u32 bufsize;
 	u32 i;
 	struct cmd_ctrl_node *cmdarray;
 
+	lbtf_deb_enter(LBTF_DEB_HOST);
+
 	/* Allocate and initialize the command array */
 	bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS;
 	cmdarray = kzalloc(bufsize, GFP_KERNEL);
-	if (!cmdarray)
-		return -1;
+	if (!cmdarray) {
+		lbtf_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n");
+		ret = -1;
+		goto done;
+	}
 	priv->cmd_array = cmdarray;
 
 	/* Allocate and initialize each command buffer in the command array */
 	for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
 		cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL);
-		if (!cmdarray[i].cmdbuf)
-			return -1;
+		if (!cmdarray[i].cmdbuf) {
+			lbtf_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n");
+			ret = -1;
+			goto done;
+		}
 	}
 
 	for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
 		init_waitqueue_head(&cmdarray[i].cmdwait_q);
 		lbtf_cleanup_and_insert_cmd(priv, &cmdarray[i]);
 	}
-	return 0;
+
+	ret = 0;
+
+done:
+	lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %d", ret);
+	return ret;
 }
 
 /**
@@ -402,9 +493,13 @@
 	struct cmd_ctrl_node *cmdarray;
 	unsigned int i;
 
+	lbtf_deb_enter(LBTF_DEB_HOST);
+
 	/* need to check if cmd array is allocated or not */
-	if (priv->cmd_array == NULL)
-		return 0;
+	if (priv->cmd_array == NULL) {
+		lbtf_deb_host("FREE_CMD_BUF: cmd_array is NULL\n");
+		goto done;
+	}
 
 	cmdarray = priv->cmd_array;
 
@@ -418,6 +513,8 @@
 	kfree(priv->cmd_array);
 	priv->cmd_array = NULL;
 
+done:
+	lbtf_deb_leave(LBTF_DEB_HOST);
 	return 0;
 }
 
@@ -433,6 +530,8 @@
 	struct cmd_ctrl_node *tempnode;
 	unsigned long flags;
 
+	lbtf_deb_enter(LBTF_DEB_HOST);
+
 	if (!priv)
 		return NULL;
 
@@ -442,11 +541,14 @@
 		tempnode = list_first_entry(&priv->cmdfreeq,
 					    struct cmd_ctrl_node, list);
 		list_del(&tempnode->list);
-	} else
+	} else {
+		lbtf_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n");
 		tempnode = NULL;
+	}
 
 	spin_unlock_irqrestore(&priv->driver_lock, flags);
 
+	lbtf_deb_leave(LBTF_DEB_HOST);
 	return tempnode;
 }
 
@@ -462,16 +564,20 @@
 	struct cmd_ctrl_node *cmdnode = NULL;
 	struct cmd_header *cmd;
 	unsigned long flags;
+	int ret = 0;
 
-	/* Debug group is LBS_DEB_THREAD and not LBS_DEB_HOST, because the
+	/* Debug group is lbtf_deb_THREAD and not lbtf_deb_HOST, because the
 	 * only caller to us is lbtf_thread() and we get even when a
 	 * data packet is received */
+	lbtf_deb_enter(LBTF_DEB_THREAD);
 
 	spin_lock_irqsave(&priv->driver_lock, flags);
 
 	if (priv->cur_cmd) {
+		pr_alert("EXEC_NEXT_CMD: already processing command!\n");
 		spin_unlock_irqrestore(&priv->driver_lock, flags);
-		return -1;
+		ret = -1;
+		goto done;
 	}
 
 	if (!list_empty(&priv->cmdpendingq)) {
@@ -483,11 +589,17 @@
 		cmd = cmdnode->cmdbuf;
 
 		list_del(&cmdnode->list);
+		lbtf_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n",
+			    le16_to_cpu(cmd->command));
 		spin_unlock_irqrestore(&priv->driver_lock, flags);
 		lbtf_submit_command(priv, cmdnode);
 	} else
 		spin_unlock_irqrestore(&priv->driver_lock, flags);
-	return 0;
+
+	ret = 0;
+done:
+	lbtf_deb_leave(LBTF_DEB_THREAD);
+	return ret;
 }
 
 static struct cmd_ctrl_node *__lbtf_cmd_async(struct lbtf_private *priv,
@@ -498,14 +610,22 @@
 {
 	struct cmd_ctrl_node *cmdnode;
 
-	if (priv->surpriseremoved)
-		return ERR_PTR(-ENOENT);
+	lbtf_deb_enter(LBTF_DEB_HOST);
+
+	if (priv->surpriseremoved) {
+		lbtf_deb_host("PREP_CMD: card removed\n");
+		cmdnode = ERR_PTR(-ENOENT);
+		goto done;
+	}
 
 	cmdnode = lbtf_get_cmd_ctrl_node(priv);
 	if (cmdnode == NULL) {
+		lbtf_deb_host("PREP_CMD: cmdnode is NULL\n");
+
 		/* Wake up main thread to execute next command */
 		queue_work(lbtf_wq, &priv->cmd_work);
-		return ERR_PTR(-ENOBUFS);
+		cmdnode = ERR_PTR(-ENOBUFS);
+		goto done;
 	}
 
 	cmdnode->callback = callback;
@@ -520,17 +640,24 @@
 	cmdnode->cmdbuf->size    = cpu_to_le16(in_cmd_size);
 	cmdnode->cmdbuf->seqnum  = cpu_to_le16(priv->seqnum);
 	cmdnode->cmdbuf->result  = 0;
+
+	lbtf_deb_host("PREP_CMD: command 0x%04x\n", command);
+
 	cmdnode->cmdwaitqwoken = 0;
 	lbtf_queue_cmd(priv, cmdnode);
 	queue_work(lbtf_wq, &priv->cmd_work);
 
+ done:
+	lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %p", cmdnode);
 	return cmdnode;
 }
 
 void lbtf_cmd_async(struct lbtf_private *priv, uint16_t command,
 	struct cmd_header *in_cmd, int in_cmd_size)
 {
+	lbtf_deb_enter(LBTF_DEB_CMD);
 	__lbtf_cmd_async(priv, command, in_cmd, in_cmd_size, NULL, 0);
+	lbtf_deb_leave(LBTF_DEB_CMD);
 }
 
 int __lbtf_cmd(struct lbtf_private *priv, uint16_t command,
@@ -543,30 +670,35 @@
 	unsigned long flags;
 	int ret = 0;
 
+	lbtf_deb_enter(LBTF_DEB_HOST);
+
 	cmdnode = __lbtf_cmd_async(priv, command, in_cmd, in_cmd_size,
 				  callback, callback_arg);
-	if (IS_ERR(cmdnode))
-		return PTR_ERR(cmdnode);
+	if (IS_ERR(cmdnode)) {
+		ret = PTR_ERR(cmdnode);
+		goto done;
+	}
 
 	might_sleep();
 	ret = wait_event_interruptible(cmdnode->cmdwait_q,
 				       cmdnode->cmdwaitqwoken);
-       if (ret)	{
-		printk(KERN_DEBUG
-		       "libertastf: command 0x%04x interrupted by signal",
-		       command);
-		return ret;
+	if (ret) {
+		pr_info("PREP_CMD: command 0x%04x interrupted by signal: %d\n",
+			    command, ret);
+		goto done;
 	}
 
 	spin_lock_irqsave(&priv->driver_lock, flags);
 	ret = cmdnode->result;
 	if (ret)
-		printk(KERN_DEBUG "libertastf: command 0x%04x failed: %d\n",
+		pr_info("PREP_CMD: command 0x%04x failed: %d\n",
 			    command, ret);
 
 	__lbtf_cleanup_and_insert_cmd(priv, cmdnode);
 	spin_unlock_irqrestore(&priv->driver_lock, flags);
 
+done:
+	lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %d", ret);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(__lbtf_cmd);
@@ -587,6 +719,8 @@
 	unsigned long flags;
 	uint16_t result;
 
+	lbtf_deb_enter(LBTF_DEB_CMD);
+
 	mutex_lock(&priv->lock);
 	spin_lock_irqsave(&priv->driver_lock, flags);
 
@@ -602,7 +736,7 @@
 	result = le16_to_cpu(resp->result);
 
 	if (net_ratelimit())
-		printk(KERN_DEBUG "libertastf: cmd response 0x%04x, seq %d, size %d\n",
+		pr_info("libertastf: cmd response 0x%04x, seq %d, size %d\n",
 			respcmd, le16_to_cpu(resp->seqnum),
 			le16_to_cpu(resp->size));
 
@@ -639,7 +773,7 @@
 		switch (respcmd) {
 		case CMD_RET(CMD_GET_HW_SPEC):
 		case CMD_RET(CMD_802_11_RESET):
-			printk(KERN_DEBUG "libertastf: reset failed\n");
+			pr_info("libertastf: reset failed\n");
 			break;
 
 		}
@@ -666,5 +800,6 @@
 
 done:
 	mutex_unlock(&priv->lock);
+	lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret);
 	return ret;
 }
diff --git a/drivers/net/wireless/libertas_tf/deb_defs.h b/drivers/net/wireless/libertas_tf/deb_defs.h
new file mode 100644
index 0000000..ae75396
--- /dev/null
+++ b/drivers/net/wireless/libertas_tf/deb_defs.h
@@ -0,0 +1,104 @@
+/**
+  * This header file contains global constant/enum definitions,
+  * global variable declaration.
+  */
+#ifndef _LBS_DEB_DEFS_H_
+#define _LBS_DEB_EFS_H_
+
+#ifndef DRV_NAME
+#define DRV_NAME "libertas_tf"
+#endif
+
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_LIBERTAS_THINFIRM_DEBUG
+#define DEBUG
+#define PROC_DEBUG
+#endif
+
+#define LBTF_DEB_ENTER	0x00000001
+#define LBTF_DEB_LEAVE	0x00000002
+#define LBTF_DEB_MAIN	0x00000004
+#define LBTF_DEB_NET	0x00000008
+#define LBTF_DEB_MESH	0x00000010
+#define LBTF_DEB_WEXT	0x00000020
+#define LBTF_DEB_IOCTL	0x00000040
+#define LBTF_DEB_SCAN	0x00000080
+#define LBTF_DEB_ASSOC	0x00000100
+#define LBTF_DEB_JOIN	0x00000200
+#define LBTF_DEB_11D	0x00000400
+#define LBTF_DEB_DEBUGFS	0x00000800
+#define LBTF_DEB_ETHTOOL	0x00001000
+#define LBTF_DEB_HOST	0x00002000
+#define LBTF_DEB_CMD	0x00004000
+#define LBTF_DEB_RX	0x00008000
+#define LBTF_DEB_TX	0x00010000
+#define LBTF_DEB_USB	0x00020000
+#define LBTF_DEB_CS	0x00040000
+#define LBTF_DEB_FW	0x00080000
+#define LBTF_DEB_THREAD	0x00100000
+#define LBTF_DEB_HEX	0x00200000
+#define LBTF_DEB_SDIO	0x00400000
+#define LBTF_DEB_MACOPS	0x00800000
+
+extern unsigned int lbtf_debug;
+
+
+#ifdef DEBUG
+#define LBTF_DEB_LL(grp, grpnam, fmt, args...) \
+do { if ((lbtf_debug & (grp)) == (grp)) \
+  printk(KERN_DEBUG DRV_NAME grpnam "%s: " fmt, \
+         in_interrupt() ? " (INT)" : "", ## args); } while (0)
+#else
+#define LBTF_DEB_LL(grp, grpnam, fmt, args...) do {} while (0)
+#endif
+
+#define lbtf_deb_enter(grp) \
+  LBTF_DEB_LL(grp | LBTF_DEB_ENTER, " enter", "%s()\n", __func__);
+#define lbtf_deb_enter_args(grp, fmt, args...) \
+  LBTF_DEB_LL(grp | LBTF_DEB_ENTER, " enter", "%s(" fmt ")\n", __func__, ## args);
+#define lbtf_deb_leave(grp) \
+  LBTF_DEB_LL(grp | LBTF_DEB_LEAVE, " leave", "%s()\n", __func__);
+#define lbtf_deb_leave_args(grp, fmt, args...) \
+  LBTF_DEB_LL(grp | LBTF_DEB_LEAVE, " leave", "%s(), " fmt "\n", \
+  __func__, ##args);
+#define lbtf_deb_main(fmt, args...)      LBTF_DEB_LL(LBTF_DEB_MAIN, " main", fmt, ##args)
+#define lbtf_deb_net(fmt, args...)       LBTF_DEB_LL(LBTF_DEB_NET, " net", fmt, ##args)
+#define lbtf_deb_mesh(fmt, args...)      LBTF_DEB_LL(LBTF_DEB_MESH, " mesh", fmt, ##args)
+#define lbtf_deb_wext(fmt, args...)      LBTF_DEB_LL(LBTF_DEB_WEXT, " wext", fmt, ##args)
+#define lbtf_deb_ioctl(fmt, args...)     LBTF_DEB_LL(LBTF_DEB_IOCTL, " ioctl", fmt, ##args)
+#define lbtf_deb_scan(fmt, args...)      LBTF_DEB_LL(LBTF_DEB_SCAN, " scan", fmt, ##args)
+#define lbtf_deb_assoc(fmt, args...)     LBTF_DEB_LL(LBTF_DEB_ASSOC, " assoc", fmt, ##args)
+#define lbtf_deb_join(fmt, args...)      LBTF_DEB_LL(LBTF_DEB_JOIN, " join", fmt, ##args)
+#define lbtf_deb_11d(fmt, args...)       LBTF_DEB_LL(LBTF_DEB_11D, " 11d", fmt, ##args)
+#define lbtf_deb_debugfs(fmt, args...)   LBTF_DEB_LL(LBTF_DEB_DEBUGFS, " debugfs", fmt, ##args)
+#define lbtf_deb_ethtool(fmt, args...)   LBTF_DEB_LL(LBTF_DEB_ETHTOOL, " ethtool", fmt, ##args)
+#define lbtf_deb_host(fmt, args...)      LBTF_DEB_LL(LBTF_DEB_HOST, " host", fmt, ##args)
+#define lbtf_deb_cmd(fmt, args...)       LBTF_DEB_LL(LBTF_DEB_CMD, " cmd", fmt, ##args)
+#define lbtf_deb_rx(fmt, args...)        LBTF_DEB_LL(LBTF_DEB_RX, " rx", fmt, ##args)
+#define lbtf_deb_tx(fmt, args...)        LBTF_DEB_LL(LBTF_DEB_TX, " tx", fmt, ##args)
+#define lbtf_deb_fw(fmt, args...)        LBTF_DEB_LL(LBTF_DEB_FW, " fw", fmt, ##args)
+#define lbtf_deb_usb(fmt, args...)       LBTF_DEB_LL(LBTF_DEB_USB, " usb", fmt, ##args)
+#define lbtf_deb_usbd(dev, fmt, args...) LBTF_DEB_LL(LBTF_DEB_USB, " usbd", "%s:" fmt, dev_name(dev), ##args)
+#define lbtf_deb_cs(fmt, args...)        LBTF_DEB_LL(LBTF_DEB_CS, " cs", fmt, ##args)
+#define lbtf_deb_thread(fmt, args...)    LBTF_DEB_LL(LBTF_DEB_THREAD, " thread", fmt, ##args)
+#define lbtf_deb_sdio(fmt, args...)      LBTF_DEB_LL(LBTF_DEB_SDIO, " thread", fmt, ##args)
+#define lbtf_deb_macops(fmt, args...)      LBTF_DEB_LL(LBTF_DEB_MACOPS, " thread", fmt, ##args)
+
+#ifdef DEBUG
+static inline void lbtf_deb_hex(unsigned int grp, const char *prompt, u8 *buf, int len)
+{
+	char newprompt[32];
+
+	if (len &&
+	    (lbtf_debug & LBTF_DEB_HEX) &&
+	    (lbtf_debug & grp))	{
+		snprintf(newprompt, sizeof(newprompt), DRV_NAME " %s: ", prompt);
+		print_hex_dump_bytes(prompt, DUMP_PREFIX_NONE, buf, len);
+	}
+}
+#else
+#define lbtf_deb_hex(grp, prompt, buf, len)	do {} while (0)
+#endif
+
+#endif
diff --git a/drivers/net/wireless/libertas_tf/if_usb.c b/drivers/net/wireless/libertas_tf/if_usb.c
index 8cc9db6..4412c27 100644
--- a/drivers/net/wireless/libertas_tf/if_usb.c
+++ b/drivers/net/wireless/libertas_tf/if_usb.c
@@ -7,6 +7,13 @@
  *  the Free Software Foundation; either version 2 of the License, or (at
  *  your option) any later version.
  */
+#define DRV_NAME "lbtf_usb"
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "libertas_tf.h"
+#include "if_usb.h"
+
 #include <linux/delay.h>
 #include <linux/moduleparam.h>
 #include <linux/firmware.h>
@@ -14,10 +21,8 @@
 #include <linux/slab.h>
 #include <linux/usb.h>
 
-#define DRV_NAME "lbtf_usb"
-
-#include "libertas_tf.h"
-#include "if_usb.h"
+#define INSANEDEBUG	0
+#define lbtf_deb_usb2(...) do { if (INSANEDEBUG) lbtf_deb_usbd(__VA_ARGS__); } while (0)
 
 #define MESSAGE_HEADER_LEN	4
 
@@ -53,9 +58,14 @@
  */
 static void if_usb_write_bulk_callback(struct urb *urb)
 {
-	if (urb->status != 0)
-		printk(KERN_INFO "libertastf: URB in failure status: %d\n",
-		       urb->status);
+	if (urb->status != 0) {
+		/* print the failure status number for debug */
+		pr_info("URB in failure status: %d\n", urb->status);
+	} else {
+		lbtf_deb_usb2(&urb->dev->dev, "URB status is successful\n");
+		lbtf_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n",
+			     urb->actual_length);
+	}
 }
 
 /**
@@ -65,6 +75,8 @@
  */
 static void if_usb_free(struct if_usb_card *cardp)
 {
+	lbtf_deb_enter(LBTF_DEB_USB);
+
 	/* Unlink tx & rx urb */
 	usb_kill_urb(cardp->tx_urb);
 	usb_kill_urb(cardp->rx_urb);
@@ -81,6 +93,8 @@
 
 	kfree(cardp->ep_out_buf);
 	cardp->ep_out_buf = NULL;
+
+	lbtf_deb_leave(LBTF_DEB_USB);
 }
 
 static void if_usb_setup_firmware(struct lbtf_private *priv)
@@ -88,23 +102,33 @@
 	struct if_usb_card *cardp = priv->card;
 	struct cmd_ds_set_boot2_ver b2_cmd;
 
+	lbtf_deb_enter(LBTF_DEB_USB);
+
 	if_usb_submit_rx_urb(cardp);
 	b2_cmd.hdr.size = cpu_to_le16(sizeof(b2_cmd));
 	b2_cmd.action = 0;
 	b2_cmd.version = cardp->boot2_version;
 
 	if (lbtf_cmd_with_response(priv, CMD_SET_BOOT2_VER, &b2_cmd))
-		printk(KERN_INFO "libertastf: setting boot2 version failed\n");
+		lbtf_deb_usb("Setting boot2 version failed\n");
+
+	lbtf_deb_leave(LBTF_DEB_USB);
 }
 
 static void if_usb_fw_timeo(unsigned long priv)
 {
 	struct if_usb_card *cardp = (void *)priv;
 
-	if (!cardp->fwdnldover)
+	lbtf_deb_enter(LBTF_DEB_USB);
+	if (!cardp->fwdnldover) {
 		/* Download timed out */
 		cardp->priv->surpriseremoved = 1;
+		pr_err("Download timed out\n");
+	} else {
+		lbtf_deb_usb("Download complete, no event. Assuming success\n");
+	}
 	wake_up(&cardp->fw_wq);
+	lbtf_deb_leave(LBTF_DEB_USB);
 }
 
 /**
@@ -125,11 +149,14 @@
 	struct if_usb_card *cardp;
 	int i;
 
+	lbtf_deb_enter(LBTF_DEB_USB);
 	udev = interface_to_usbdev(intf);
 
 	cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL);
-	if (!cardp)
+	if (!cardp) {
+		pr_err("Out of memory allocating private data.\n");
 		goto error;
+	}
 
 	setup_timer(&cardp->fw_timeout, if_usb_fw_timeo, (unsigned long)cardp);
 	init_waitqueue_head(&cardp->fw_wq);
@@ -137,38 +164,62 @@
 	cardp->udev = udev;
 	iface_desc = intf->cur_altsetting;
 
+	lbtf_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X"
+		     " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n",
+		     le16_to_cpu(udev->descriptor.bcdUSB),
+		     udev->descriptor.bDeviceClass,
+		     udev->descriptor.bDeviceSubClass,
+		     udev->descriptor.bDeviceProtocol);
+
 	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
 		endpoint = &iface_desc->endpoint[i].desc;
 		if (usb_endpoint_is_bulk_in(endpoint)) {
 			cardp->ep_in_size =
 				le16_to_cpu(endpoint->wMaxPacketSize);
 			cardp->ep_in = usb_endpoint_num(endpoint);
+
+			lbtf_deb_usbd(&udev->dev, "in_endpoint = %d\n", cardp->ep_in);
+			lbtf_deb_usbd(&udev->dev, "Bulk in size is %d\n", cardp->ep_in_size);
 		} else if (usb_endpoint_is_bulk_out(endpoint)) {
 			cardp->ep_out_size =
 				le16_to_cpu(endpoint->wMaxPacketSize);
 			cardp->ep_out = usb_endpoint_num(endpoint);
+
+			lbtf_deb_usbd(&udev->dev, "out_endpoint = %d\n", cardp->ep_out);
+			lbtf_deb_usbd(&udev->dev, "Bulk out size is %d\n",
+			              cardp->ep_out_size);
 		}
 	}
-	if (!cardp->ep_out_size || !cardp->ep_in_size)
+	if (!cardp->ep_out_size || !cardp->ep_in_size) {
+		lbtf_deb_usbd(&udev->dev, "Endpoints not found\n");
 		/* Endpoints not found */
 		goto dealloc;
+	}
 
 	cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
-	if (!cardp->rx_urb)
+	if (!cardp->rx_urb) {
+		lbtf_deb_usbd(&udev->dev, "Rx URB allocation failed\n");
 		goto dealloc;
+	}
 
 	cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
-	if (!cardp->tx_urb)
+	if (!cardp->tx_urb) {
+		lbtf_deb_usbd(&udev->dev, "Tx URB allocation failed\n");
 		goto dealloc;
+	}
 
 	cardp->cmd_urb = usb_alloc_urb(0, GFP_KERNEL);
-	if (!cardp->cmd_urb)
+	if (!cardp->cmd_urb) {
+		lbtf_deb_usbd(&udev->dev, "Cmd URB allocation failed\n");
 		goto dealloc;
+	}
 
 	cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE,
 				    GFP_KERNEL);
-	if (!cardp->ep_out_buf)
+	if (!cardp->ep_out_buf) {
+		lbtf_deb_usbd(&udev->dev, "Could not allocate buffer\n");
 		goto dealloc;
+	}
 
 	priv = lbtf_add_card(cardp, &udev->dev);
 	if (!priv)
@@ -189,6 +240,7 @@
 dealloc:
 	if_usb_free(cardp);
 error:
+lbtf_deb_leave(LBTF_DEB_MAIN);
 	return -ENOMEM;
 }
 
@@ -202,6 +254,8 @@
 	struct if_usb_card *cardp = usb_get_intfdata(intf);
 	struct lbtf_private *priv = (struct lbtf_private *) cardp->priv;
 
+	lbtf_deb_enter(LBTF_DEB_MAIN);
+
 	if_usb_reset_device(cardp);
 
 	if (priv)
@@ -212,6 +266,8 @@
 
 	usb_set_intfdata(intf, NULL);
 	usb_put_dev(interface_to_usbdev(intf));
+
+	lbtf_deb_leave(LBTF_DEB_MAIN);
 }
 
 /**
@@ -226,6 +282,8 @@
 	struct fwdata *fwdata = cardp->ep_out_buf;
 	u8 *firmware = (u8 *) cardp->fw->data;
 
+	lbtf_deb_enter(LBTF_DEB_FW);
+
 	/* If we got a CRC failure on the last block, back
 	   up and retry it */
 	if (!cardp->CRC_OK) {
@@ -233,6 +291,9 @@
 		cardp->fwseqnum--;
 	}
 
+	lbtf_deb_usb2(&cardp->udev->dev, "totalbytes = %d\n",
+		     cardp->totalbytes);
+
 	/* struct fwdata (which we sent to the card) has an
 	   extra __le32 field in between the header and the data,
 	   which is not in the struct fwheader in the actual
@@ -246,18 +307,33 @@
 	memcpy(fwdata->data, &firmware[cardp->totalbytes],
 	       le32_to_cpu(fwdata->hdr.datalength));
 
+	lbtf_deb_usb2(&cardp->udev->dev, "Data length = %d\n",
+		     le32_to_cpu(fwdata->hdr.datalength));
+
 	fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum);
 	cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength);
 
 	usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) +
 		     le32_to_cpu(fwdata->hdr.datalength), 0);
 
-	if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK))
+	if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) {
+		lbtf_deb_usb2(&cardp->udev->dev, "There are data to follow\n");
+		lbtf_deb_usb2(&cardp->udev->dev, "seqnum = %d totalbytes = %d\n",
+			     cardp->fwseqnum, cardp->totalbytes);
+	} else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) {
+		lbtf_deb_usb2(&cardp->udev->dev, "Host has finished FW downloading\n");
+		lbtf_deb_usb2(&cardp->udev->dev, "Donwloading FW JUMP BLOCK\n");
+
 		/* Host has finished FW downloading
 		 * Donwloading FW JUMP BLOCK
 		 */
 		cardp->fwfinalblk = 1;
+	}
 
+	lbtf_deb_usb2(&cardp->udev->dev, "Firmware download done; size %d\n",
+		     cardp->totalbytes);
+
+	lbtf_deb_leave(LBTF_DEB_FW);
 	return 0;
 }
 
@@ -266,6 +342,8 @@
 	struct cmd_ds_802_11_reset *cmd = cardp->ep_out_buf + 4;
 	int ret;
 
+	lbtf_deb_enter(LBTF_DEB_USB);
+
 	*(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST);
 
 	cmd->hdr.command = cpu_to_le16(CMD_802_11_RESET);
@@ -280,6 +358,8 @@
 	ret = usb_reset_device(cardp->udev);
 	msleep(100);
 
+	lbtf_deb_leave_args(LBTF_DEB_USB, "ret %d", ret);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(if_usb_reset_device);
@@ -297,11 +377,15 @@
 static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload,
 			uint16_t nb, u8 data)
 {
+	int ret = -1;
 	struct urb *urb;
 
+	lbtf_deb_enter(LBTF_DEB_USB);
 	/* check if device is removed */
-	if (cardp->priv->surpriseremoved)
-		return -1;
+	if (cardp->priv->surpriseremoved) {
+		lbtf_deb_usbd(&cardp->udev->dev, "Device removed\n");
+		goto tx_ret;
+	}
 
 	if (data)
 		urb = cardp->tx_urb;
@@ -315,19 +399,34 @@
 
 	urb->transfer_flags |= URB_ZERO_PACKET;
 
-	if (usb_submit_urb(urb, GFP_ATOMIC))
-		return -1;
-	return 0;
+	if (usb_submit_urb(urb, GFP_ATOMIC)) {
+		lbtf_deb_usbd(&cardp->udev->dev, "usb_submit_urb failed: %d\n", ret);
+		goto tx_ret;
+	}
+
+	lbtf_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n");
+
+	ret = 0;
+
+tx_ret:
+	lbtf_deb_leave(LBTF_DEB_USB);
+	return ret;
 }
 
 static int __if_usb_submit_rx_urb(struct if_usb_card *cardp,
 				  void (*callbackfn)(struct urb *urb))
 {
 	struct sk_buff *skb;
+	int ret = -1;
+
+	lbtf_deb_enter(LBTF_DEB_USB);
 
 	skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE);
-	if (!skb)
+	if (!skb) {
+		pr_err("No free skb\n");
+		lbtf_deb_leave(LBTF_DEB_USB);
 		return -1;
+	}
 
 	cardp->rx_skb = skb;
 
@@ -339,12 +438,19 @@
 
 	cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET;
 
-	if (usb_submit_urb(cardp->rx_urb, GFP_ATOMIC)) {
+	lbtf_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb);
+	ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC);
+	if (ret) {
+		lbtf_deb_usbd(&cardp->udev->dev, "Submit Rx URB failed: %d\n", ret);
 		kfree_skb(skb);
 		cardp->rx_skb = NULL;
+		lbtf_deb_leave(LBTF_DEB_USB);
 		return -1;
-	} else
+	} else {
+		lbtf_deb_usb2(&cardp->udev->dev, "Submit Rx URB success\n");
+		lbtf_deb_leave(LBTF_DEB_USB);
 		return 0;
+	}
 }
 
 static int if_usb_submit_rx_urb_fwload(struct if_usb_card *cardp)
@@ -364,8 +470,12 @@
 	struct fwsyncheader *syncfwheader;
 	struct bootcmdresp bcmdresp;
 
+	lbtf_deb_enter(LBTF_DEB_USB);
 	if (urb->status) {
+		lbtf_deb_usbd(&cardp->udev->dev,
+			     "URB status is failed during fw load\n");
 		kfree_skb(skb);
+		lbtf_deb_leave(LBTF_DEB_USB);
 		return;
 	}
 
@@ -373,12 +483,17 @@
 		__le32 *tmp = (__le32 *)(skb->data);
 
 		if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) &&
-		    tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY))
+		    tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) {
 			/* Firmware ready event received */
+			pr_info("Firmware ready event received\n");
 			wake_up(&cardp->fw_wq);
-		else
+		} else {
+			lbtf_deb_usb("Waiting for confirmation; got %x %x\n",
+				    le32_to_cpu(tmp[0]), le32_to_cpu(tmp[1]));
 			if_usb_submit_rx_urb_fwload(cardp);
+		}
 		kfree_skb(skb);
+		lbtf_deb_leave(LBTF_DEB_USB);
 		return;
 	}
 	if (cardp->bootcmdresp <= 0) {
@@ -389,34 +504,60 @@
 			if_usb_submit_rx_urb_fwload(cardp);
 			cardp->bootcmdresp = 1;
 			/* Received valid boot command response */
+			lbtf_deb_usbd(&cardp->udev->dev,
+				     "Received valid boot command response\n");
+			lbtf_deb_leave(LBTF_DEB_USB);
 			return;
 		}
 		if (bcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) {
 			if (bcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) ||
 			    bcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) ||
-			    bcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION))
+			    bcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) {
+				if (!cardp->bootcmdresp)
+					pr_info("Firmware already seems alive; resetting\n");
 				cardp->bootcmdresp = -1;
-		} else if (bcmdresp.cmd == BOOT_CMD_FW_BY_USB &&
-			   bcmdresp.result == BOOT_CMD_RESP_OK)
+			} else {
+				pr_info("boot cmd response wrong magic number (0x%x)\n",
+					    le32_to_cpu(bcmdresp.magic));
+			}
+		} else if (bcmdresp.cmd != BOOT_CMD_FW_BY_USB) {
+			pr_info("boot cmd response cmd_tag error (%d)\n",
+				    bcmdresp.cmd);
+		} else if (bcmdresp.result != BOOT_CMD_RESP_OK) {
+			pr_info("boot cmd response result error (%d)\n",
+				    bcmdresp.result);
+		} else {
 			cardp->bootcmdresp = 1;
+			lbtf_deb_usbd(&cardp->udev->dev,
+				     "Received valid boot command response\n");
+		}
 
 		kfree_skb(skb);
 		if_usb_submit_rx_urb_fwload(cardp);
+		lbtf_deb_leave(LBTF_DEB_USB);
 		return;
 	}
 
 	syncfwheader = kmalloc(sizeof(struct fwsyncheader), GFP_ATOMIC);
 	if (!syncfwheader) {
+		lbtf_deb_usbd(&cardp->udev->dev, "Failure to allocate syncfwheader\n");
 		kfree_skb(skb);
+		lbtf_deb_leave(LBTF_DEB_USB);
 		return;
 	}
 
 	memcpy(syncfwheader, skb->data, sizeof(struct fwsyncheader));
 
-	if (!syncfwheader->cmd)
+	if (!syncfwheader->cmd) {
+		lbtf_deb_usb2(&cardp->udev->dev, "FW received Blk with correct CRC\n");
+		lbtf_deb_usb2(&cardp->udev->dev, "FW received Blk seqnum = %d\n",
+			     le32_to_cpu(syncfwheader->seqnum));
 		cardp->CRC_OK = 1;
-	else
+	} else {
+		lbtf_deb_usbd(&cardp->udev->dev, "FW received Blk with CRC error\n");
 		cardp->CRC_OK = 0;
+	}
+
 	kfree_skb(skb);
 
 	/* reschedule timer for 200ms hence */
@@ -434,6 +575,7 @@
 
 	kfree(syncfwheader);
 
+	lbtf_deb_leave(LBTF_DEB_USB);
 	return;
 }
 
@@ -445,6 +587,7 @@
 {
 	if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + MESSAGE_HEADER_LEN
 	    || recvlength < MRVDRV_MIN_PKT_LEN) {
+		lbtf_deb_usbd(&cardp->udev->dev, "Packet length is Invalid\n");
 		kfree_skb(skb);
 		return;
 	}
@@ -460,6 +603,8 @@
 				      struct lbtf_private *priv)
 {
 	if (recvlength > LBS_CMD_BUFFER_SIZE) {
+		lbtf_deb_usbd(&cardp->udev->dev,
+			     "The receive buffer is too large\n");
 		kfree_skb(skb);
 		return;
 	}
@@ -489,16 +634,24 @@
 	uint32_t recvtype = 0;
 	__le32 *pkt = (__le32 *) skb->data;
 
+	lbtf_deb_enter(LBTF_DEB_USB);
+
 	if (recvlength) {
 		if (urb->status) {
+			lbtf_deb_usbd(&cardp->udev->dev, "RX URB failed: %d\n",
+				     urb->status);
 			kfree_skb(skb);
 			goto setup_for_next;
 		}
 
 		recvbuff = skb->data;
 		recvtype = le32_to_cpu(pkt[0]);
+		lbtf_deb_usbd(&cardp->udev->dev,
+			    "Recv length = 0x%x, Recv type = 0x%X\n",
+			    recvlength, recvtype);
 	} else if (urb->status) {
 		kfree_skb(skb);
+		lbtf_deb_leave(LBTF_DEB_USB);
 		return;
 	}
 
@@ -515,6 +668,7 @@
 	{
 		/* Event cause handling */
 		u32 event_cause = le32_to_cpu(pkt[1]);
+		lbtf_deb_usbd(&cardp->udev->dev, "**EVENT** 0x%X\n", event_cause);
 
 		/* Icky undocumented magic special case */
 		if (event_cause & 0xffff0000) {
@@ -529,21 +683,22 @@
 		} else if (event_cause == LBTF_EVENT_BCN_SENT)
 			lbtf_bcn_sent(priv);
 		else
-			printk(KERN_DEBUG
+			lbtf_deb_usbd(&cardp->udev->dev,
 			       "Unsupported notification %d received\n",
 			       event_cause);
 		kfree_skb(skb);
 		break;
 	}
 	default:
-		printk(KERN_DEBUG "libertastf: unknown command type 0x%X\n",
-			     recvtype);
+		lbtf_deb_usbd(&cardp->udev->dev,
+		         "libertastf: unknown command type 0x%X\n", recvtype);
 		kfree_skb(skb);
 		break;
 	}
 
 setup_for_next:
 	if_usb_submit_rx_urb(cardp);
+	lbtf_deb_leave(LBTF_DEB_USB);
 }
 
 /**
@@ -562,6 +717,9 @@
 	struct if_usb_card *cardp = priv->card;
 	u8 data = 0;
 
+	lbtf_deb_usbd(&cardp->udev->dev, "*** type = %u\n", type);
+	lbtf_deb_usbd(&cardp->udev->dev, "size after = %d\n", nb);
+
 	if (type == MVMS_CMD) {
 		*(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST);
 	} else {
@@ -639,8 +797,10 @@
 	} while (!exit);
 
 	if (ret)
-		printk(KERN_INFO
-		       "libertastf: firmware file format check failed\n");
+		pr_err("firmware file format check FAIL\n");
+	else
+		lbtf_deb_fw("firmware file format check PASS\n");
+
 	return ret;
 }
 
@@ -651,10 +811,12 @@
 	static int reset_count = 10;
 	int ret = 0;
 
+	lbtf_deb_enter(LBTF_DEB_USB);
+
 	ret = request_firmware(&cardp->fw, lbtf_fw_name, &cardp->udev->dev);
 	if (ret < 0) {
-		printk(KERN_INFO "libertastf: firmware %s not found\n",
-		       lbtf_fw_name);
+		pr_err("request_firmware() failed with %#x\n", ret);
+		pr_err("firmware %s not found\n", lbtf_fw_name);
 		goto done;
 	}
 
@@ -663,6 +825,7 @@
 
 restart:
 	if (if_usb_submit_rx_urb_fwload(cardp) < 0) {
+		lbtf_deb_usbd(&cardp->udev->dev, "URB submission is failed\n");
 		ret = -1;
 		goto release_fw;
 	}
@@ -709,14 +872,13 @@
 	usb_kill_urb(cardp->rx_urb);
 
 	if (!cardp->fwdnldover) {
-		printk(KERN_INFO "libertastf: failed to load fw,"
-				 " resetting device!\n");
+		pr_info("failed to load fw, resetting device!\n");
 		if (--reset_count >= 0) {
 			if_usb_reset_device(cardp);
 			goto restart;
 		}
 
-		printk(KERN_INFO "libertastf: fw download failure\n");
+		pr_info("FW download failure, time = %d ms\n", i * 100);
 		ret = -1;
 		goto release_fw;
 	}
@@ -730,6 +892,7 @@
 	if_usb_setup_firmware(cardp->priv);
 
  done:
+	lbtf_deb_leave_args(LBTF_DEB_USB, "ret %d", ret);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(if_usb_prog_firmware);
@@ -751,13 +914,19 @@
 {
 	int ret = 0;
 
+	lbtf_deb_enter(LBTF_DEB_MAIN);
+
 	ret = usb_register(&if_usb_driver);
+
+	lbtf_deb_leave_args(LBTF_DEB_MAIN, "ret %d", ret);
 	return ret;
 }
 
 static void __exit if_usb_exit_module(void)
 {
+	lbtf_deb_enter(LBTF_DEB_MAIN);
 	usb_deregister(&if_usb_driver);
+	lbtf_deb_leave(LBTF_DEB_MAIN);
 }
 
 module_init(if_usb_init_module);
diff --git a/drivers/net/wireless/libertas_tf/libertas_tf.h b/drivers/net/wireless/libertas_tf/libertas_tf.h
index 4cc42dd..fbbaaae 100644
--- a/drivers/net/wireless/libertas_tf/libertas_tf.h
+++ b/drivers/net/wireless/libertas_tf/libertas_tf.h
@@ -13,6 +13,8 @@
 #include <linux/kthread.h>
 #include <net/mac80211.h>
 
+#include "deb_defs.h"
+
 #ifndef DRV_NAME
 #define DRV_NAME "libertas_tf"
 #endif
diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c
index 7533a23..60787de 100644
--- a/drivers/net/wireless/libertas_tf/main.c
+++ b/drivers/net/wireless/libertas_tf/main.c
@@ -7,10 +7,12 @@
  *  the Free Software Foundation; either version 2 of the License, or (at
  *  your option) any later version.
  */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/slab.h>
 
+#include <linux/etherdevice.h>
 #include "libertas_tf.h"
-#include "linux/etherdevice.h"
 
 #define DRIVER_RELEASE_VERSION "004.p0"
 /* thinfirm version: 5.132.X.pX */
@@ -18,7 +20,17 @@
 #define LBTF_FW_VER_MAX		0x0584ffff
 #define QOS_CONTROL_LEN		2
 
-static const char lbtf_driver_version[] = "THINFIRM-USB8388-" DRIVER_RELEASE_VERSION;
+/* Module parameters */
+unsigned int lbtf_debug;
+EXPORT_SYMBOL_GPL(lbtf_debug);
+module_param_named(libertas_tf_debug, lbtf_debug, int, 0644);
+
+static const char lbtf_driver_version[] = "THINFIRM-USB8388-" DRIVER_RELEASE_VERSION
+#ifdef DEBUG
+	"-dbg"
+#endif
+	"";
+
 struct workqueue_struct *lbtf_wq;
 
 static const struct ieee80211_channel lbtf_channels[] = {
@@ -81,6 +93,9 @@
 {
 	struct lbtf_private *priv = container_of(work, struct lbtf_private,
 					 cmd_work);
+
+	lbtf_deb_enter(LBTF_DEB_CMD);
+
 	spin_lock_irq(&priv->driver_lock);
 	/* command response? */
 	if (priv->cmd_response_rxed) {
@@ -108,11 +123,16 @@
 	priv->cmd_timed_out = 0;
 	spin_unlock_irq(&priv->driver_lock);
 
-	if (!priv->fw_ready)
+	if (!priv->fw_ready) {
+		lbtf_deb_leave_args(LBTF_DEB_CMD, "fw not ready");
 		return;
+	}
+
 	/* Execute the next command */
 	if (!priv->cur_cmd)
 		lbtf_execute_next_command(priv);
+
+	lbtf_deb_leave(LBTF_DEB_CMD);
 }
 
 /**
@@ -126,6 +146,7 @@
 {
 	int ret = -1;
 
+	lbtf_deb_enter(LBTF_DEB_FW);
 	/*
 	 * Read priv address from HW
 	 */
@@ -141,6 +162,7 @@
 
 	ret = 0;
 done:
+	lbtf_deb_leave_args(LBTF_DEB_FW, "ret: %d", ret);
 	return ret;
 }
 
@@ -152,6 +174,7 @@
 {
 	struct lbtf_private *priv = (struct lbtf_private *)data;
 	unsigned long flags;
+	lbtf_deb_enter(LBTF_DEB_CMD);
 
 	spin_lock_irqsave(&priv->driver_lock, flags);
 
@@ -168,10 +191,12 @@
 	queue_work(lbtf_wq, &priv->cmd_work);
 out:
 	spin_unlock_irqrestore(&priv->driver_lock, flags);
+	lbtf_deb_leave(LBTF_DEB_CMD);
 }
 
 static int lbtf_init_adapter(struct lbtf_private *priv)
 {
+	lbtf_deb_enter(LBTF_DEB_MAIN);
 	memset(priv->current_addr, 0xff, ETH_ALEN);
 	mutex_init(&priv->lock);
 
@@ -188,13 +213,16 @@
 	if (lbtf_allocate_cmd_buffer(priv))
 		return -1;
 
+	lbtf_deb_leave(LBTF_DEB_MAIN);
 	return 0;
 }
 
 static void lbtf_free_adapter(struct lbtf_private *priv)
 {
+	lbtf_deb_enter(LBTF_DEB_MAIN);
 	lbtf_free_cmd_buffer(priv);
 	del_timer(&priv->command_timer);
+	lbtf_deb_leave(LBTF_DEB_MAIN);
 }
 
 static int lbtf_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
@@ -221,14 +249,18 @@
 	struct sk_buff *skb = NULL;
 	int err;
 
+	lbtf_deb_enter(LBTF_DEB_MACOPS | LBTF_DEB_TX);
+
 	if ((priv->vif->type == NL80211_IFTYPE_AP) &&
 	    (!skb_queue_empty(&priv->bc_ps_buf)))
 		skb = skb_dequeue(&priv->bc_ps_buf);
 	else if (priv->skb_to_tx) {
 		skb = priv->skb_to_tx;
 		priv->skb_to_tx = NULL;
-	} else
+	} else {
+		lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX);
 		return;
+	}
 
 	len = skb->len;
 	info  = IEEE80211_SKB_CB(skb);
@@ -236,6 +268,7 @@
 
 	if (priv->surpriseremoved) {
 		dev_kfree_skb_any(skb);
+		lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX);
 		return;
 	}
 
@@ -249,6 +282,7 @@
 		ETH_ALEN);
 	txpd->tx_packet_length = cpu_to_le16(len);
 	txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd));
+	lbtf_deb_hex(LBTF_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100));
 	BUG_ON(priv->tx_skb);
 	spin_lock_irq(&priv->driver_lock);
 	priv->tx_skb = skb;
@@ -257,7 +291,9 @@
 	if (err) {
 		dev_kfree_skb_any(skb);
 		priv->tx_skb = NULL;
+		pr_err("TX error: %d", err);
 	}
+	lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX);
 }
 
 static int lbtf_op_start(struct ieee80211_hw *hw)
@@ -266,6 +302,8 @@
 	void *card = priv->card;
 	int ret = -1;
 
+	lbtf_deb_enter(LBTF_DEB_MACOPS);
+
 	if (!priv->fw_ready)
 		/* Upload firmware */
 		if (priv->hw_prog_firmware(card))
@@ -286,10 +324,12 @@
 	}
 
 	printk(KERN_INFO "libertastf: Marvell WLAN 802.11 thinfirm adapter\n");
+	lbtf_deb_leave(LBTF_DEB_MACOPS);
 	return 0;
 
 err_prog_firmware:
 	priv->hw_reset_device(card);
+	lbtf_deb_leave_args(LBTF_DEB_MACOPS, "error programing fw; ret=%d", ret);
 	return ret;
 }
 
@@ -300,6 +340,9 @@
 	struct sk_buff *skb;
 
 	struct cmd_ctrl_node *cmdnode;
+
+	lbtf_deb_enter(LBTF_DEB_MACOPS);
+
 	/* Flush pending command nodes */
 	spin_lock_irqsave(&priv->driver_lock, flags);
 	list_for_each_entry(cmdnode, &priv->cmdpendingq, list) {
@@ -316,6 +359,7 @@
 	priv->radioon = RADIO_OFF;
 	lbtf_set_radio_control(priv);
 
+	lbtf_deb_leave(LBTF_DEB_MACOPS);
 	return;
 }
 
@@ -323,6 +367,7 @@
 			struct ieee80211_vif *vif)
 {
 	struct lbtf_private *priv = hw->priv;
+	lbtf_deb_enter(LBTF_DEB_MACOPS);
 	if (priv->vif != NULL)
 		return -EOPNOTSUPP;
 
@@ -340,6 +385,7 @@
 		return -EOPNOTSUPP;
 	}
 	lbtf_set_mac_address(priv, (u8 *) vif->addr);
+	lbtf_deb_leave(LBTF_DEB_MACOPS);
 	return 0;
 }
 
@@ -347,6 +393,7 @@
 			struct ieee80211_vif *vif)
 {
 	struct lbtf_private *priv = hw->priv;
+	lbtf_deb_enter(LBTF_DEB_MACOPS);
 
 	if (priv->vif->type == NL80211_IFTYPE_AP ||
 	    priv->vif->type == NL80211_IFTYPE_MESH_POINT)
@@ -354,17 +401,20 @@
 	lbtf_set_mode(priv, LBTF_PASSIVE_MODE);
 	lbtf_set_bssid(priv, 0, NULL);
 	priv->vif = NULL;
+	lbtf_deb_leave(LBTF_DEB_MACOPS);
 }
 
 static int lbtf_op_config(struct ieee80211_hw *hw, u32 changed)
 {
 	struct lbtf_private *priv = hw->priv;
 	struct ieee80211_conf *conf = &hw->conf;
+	lbtf_deb_enter(LBTF_DEB_MACOPS);
 
 	if (conf->channel->center_freq != priv->cur_freq) {
 		priv->cur_freq = conf->channel->center_freq;
 		lbtf_set_channel(priv, conf->channel->hw_value);
 	}
+	lbtf_deb_leave(LBTF_DEB_MACOPS);
 	return 0;
 }
 
@@ -395,11 +445,16 @@
 {
 	struct lbtf_private *priv = hw->priv;
 	int old_mac_control = priv->mac_control;
+
+	lbtf_deb_enter(LBTF_DEB_MACOPS);
+
 	changed_flags &= SUPPORTED_FIF_FLAGS;
 	*new_flags &= SUPPORTED_FIF_FLAGS;
 
-	if (!changed_flags)
+	if (!changed_flags) {
+		lbtf_deb_leave(LBTF_DEB_MACOPS);
 		return;
+	}
 
 	if (*new_flags & (FIF_PROMISC_IN_BSS))
 		priv->mac_control |= CMD_ACT_MAC_PROMISCUOUS_ENABLE;
@@ -425,6 +480,8 @@
 
 	if (priv->mac_control != old_mac_control)
 		lbtf_set_mac_control(priv);
+
+	lbtf_deb_leave(LBTF_DEB_MACOPS);
 }
 
 static void lbtf_op_bss_info_changed(struct ieee80211_hw *hw,
@@ -434,6 +491,7 @@
 {
 	struct lbtf_private *priv = hw->priv;
 	struct sk_buff *beacon;
+	lbtf_deb_enter(LBTF_DEB_MACOPS);
 
 	if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_INT)) {
 		switch (priv->vif->type) {
@@ -464,6 +522,8 @@
 			priv->preamble = CMD_TYPE_LONG_PREAMBLE;
 		lbtf_set_radio_control(priv);
 	}
+
+	lbtf_deb_leave(LBTF_DEB_MACOPS);
 }
 
 static const struct ieee80211_ops lbtf_ops = {
@@ -486,6 +546,8 @@
 	unsigned int flags;
 	struct ieee80211_hdr *hdr;
 
+	lbtf_deb_enter(LBTF_DEB_RX);
+
 	prxpd = (struct rxpd *) skb->data;
 
 	stats.flag = 0;
@@ -494,7 +556,6 @@
 	stats.freq = priv->cur_freq;
 	stats.band = IEEE80211_BAND_2GHZ;
 	stats.signal = prxpd->snr;
-	stats.noise = prxpd->nf;
 	/* Marvell rate index has a hole at value 4 */
 	if (prxpd->rx_rate > 4)
 		--prxpd->rx_rate;
@@ -516,7 +577,15 @@
 	}
 
 	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+
+	lbtf_deb_rx("rx data: skb->len-sizeof(RxPd) = %d-%zd = %zd\n",
+	       skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd));
+	lbtf_deb_hex(LBTF_DEB_RX, "RX Data", skb->data,
+	             min_t(unsigned int, skb->len, 100));
+
 	ieee80211_rx_irqsafe(priv->hw, skb);
+
+	lbtf_deb_leave(LBTF_DEB_RX);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(lbtf_rx);
@@ -533,6 +602,8 @@
 	struct ieee80211_hw *hw;
 	struct lbtf_private *priv = NULL;
 
+	lbtf_deb_enter(LBTF_DEB_MAIN);
+
 	hw = ieee80211_alloc_hw(sizeof(struct lbtf_private), &lbtf_ops);
 	if (!hw)
 		goto done;
@@ -575,6 +646,7 @@
 	priv = NULL;
 
 done:
+	lbtf_deb_leave_args(LBTF_DEB_MAIN, "priv %p", priv);
 	return priv;
 }
 EXPORT_SYMBOL_GPL(lbtf_add_card);
@@ -584,6 +656,8 @@
 {
 	struct ieee80211_hw *hw = priv->hw;
 
+	lbtf_deb_enter(LBTF_DEB_MAIN);
+
 	priv->surpriseremoved = 1;
 	del_timer(&priv->command_timer);
 	lbtf_free_adapter(priv);
@@ -591,6 +665,7 @@
 	ieee80211_unregister_hw(hw);
 	ieee80211_free_hw(hw);
 
+    lbtf_deb_leave(LBTF_DEB_MAIN);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(lbtf_remove_card);
@@ -649,17 +724,21 @@
 
 static int __init lbtf_init_module(void)
 {
+	lbtf_deb_enter(LBTF_DEB_MAIN);
 	lbtf_wq = create_workqueue("libertastf");
 	if (lbtf_wq == NULL) {
 		printk(KERN_ERR "libertastf: couldn't create workqueue\n");
 		return -ENOMEM;
 	}
+	lbtf_deb_leave(LBTF_DEB_MAIN);
 	return 0;
 }
 
 static void __exit lbtf_exit_module(void)
 {
+	lbtf_deb_enter(LBTF_DEB_MAIN);
 	destroy_workqueue(lbtf_wq);
+	lbtf_deb_leave(LBTF_DEB_MAIN);
 }
 
 module_init(lbtf_init_module);
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 6f0d8c9..9fd2bead 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -974,6 +974,7 @@
 }
 
 static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
 				  struct cfg80211_scan_request *req)
 {
 	struct hw_scan_done *hsd = kzalloc(sizeof(*hsd), GFP_KERNEL);
@@ -1020,7 +1021,7 @@
 	mutex_lock(&hwsim->mutex);
 
 	printk(KERN_DEBUG "hwsim sw_scan_complete\n");
-	hwsim->scanning = true;
+	hwsim->scanning = false;
 
 	mutex_unlock(&hwsim->mutex);
 }
@@ -1299,7 +1300,8 @@
 		hw->flags = IEEE80211_HW_MFP_CAPABLE |
 			    IEEE80211_HW_SIGNAL_DBM |
 			    IEEE80211_HW_SUPPORTS_STATIC_SMPS |
-			    IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS;
+			    IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
+			    IEEE80211_HW_AMPDU_AGGREGATION;
 
 		/* ask mac80211 to reserve space for magic */
 		hw->vif_data_size = sizeof(struct hwsim_vif_priv);
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index 73bbd08..808adb9 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -750,7 +750,6 @@
 	memset(status, 0, sizeof(*status));
 
 	status->signal = -rxd->rssi;
-	status->noise = -rxd->noise_floor;
 
 	if (rxd->rate & MWL8K_8366_AP_RATE_INFO_MCS_FORMAT) {
 		status->flag |= RX_FLAG_HT;
@@ -852,7 +851,6 @@
 	memset(status, 0, sizeof(*status));
 
 	status->signal = -rxd->rssi;
-	status->noise = -rxd->noise_level;
 	status->antenna = MWL8K_STA_RATE_INFO_ANTSELECT(rate_info);
 	status->rate_idx = MWL8K_STA_RATE_INFO_RATEID(rate_info);
 
@@ -3984,8 +3982,8 @@
 
 	hw->queues = MWL8K_TX_QUEUES;
 
-	/* Set rssi and noise values to dBm */
-	hw->flags |= IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM;
+	/* Set rssi values to dBm */
+	hw->flags |= IEEE80211_HW_SIGNAL_DBM;
 	hw->vif_data_size = sizeof(struct mwl8k_vif);
 	hw->sta_data_size = sizeof(struct mwl8k_sta);
 
diff --git a/drivers/net/wireless/orinoco/Kconfig b/drivers/net/wireless/orinoco/Kconfig
index 6116b54..60819bc 100644
--- a/drivers/net/wireless/orinoco/Kconfig
+++ b/drivers/net/wireless/orinoco/Kconfig
@@ -132,3 +132,10 @@
 	  This driver requires firmware download on startup.  Utilities
 	  for downloading Symbol firmware are available at
 	  <http://sourceforge.net/projects/orinoco/>
+
+config ORINOCO_USB
+	tristate "Agere Orinoco USB support"
+	depends on USB && HERMES
+	select FW_LOADER
+	---help---
+	  This driver is for USB versions of the Agere Orinoco card.
diff --git a/drivers/net/wireless/orinoco/Makefile b/drivers/net/wireless/orinoco/Makefile
index e645269..bfdefb85 100644
--- a/drivers/net/wireless/orinoco/Makefile
+++ b/drivers/net/wireless/orinoco/Makefile
@@ -11,6 +11,7 @@
 obj-$(CONFIG_TMD_HERMES)	+= orinoco_tmd.o
 obj-$(CONFIG_NORTEL_HERMES)	+= orinoco_nortel.o
 obj-$(CONFIG_PCMCIA_SPECTRUM)	+= spectrum_cs.o
+obj-$(CONFIG_ORINOCO_USB)	+= orinoco_usb.o
 
 # Orinoco should be endian clean.
 ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/orinoco/airport.c b/drivers/net/wireless/orinoco/airport.c
index c60df2c..9bcee10 100644
--- a/drivers/net/wireless/orinoco/airport.c
+++ b/drivers/net/wireless/orinoco/airport.c
@@ -77,9 +77,9 @@
 
 	enable_irq(card->irq);
 
-	spin_lock_irqsave(&priv->lock, flags);
+	priv->hw.ops->lock_irqsave(&priv->lock, &flags);
 	err = orinoco_up(priv);
-	spin_unlock_irqrestore(&priv->lock, flags);
+	priv->hw.ops->unlock_irqrestore(&priv->lock, &flags);
 
 	return err;
 }
@@ -195,7 +195,7 @@
 	ssleep(1);
 
 	/* Reset it before we get the interrupt */
-	hermes_init(hw);
+	hw->ops->init(hw);
 
 	if (request_irq(card->irq, orinoco_interrupt, 0, DRIVER_NAME, priv)) {
 		printk(KERN_ERR PFX "Couldn't get IRQ %d\n", card->irq);
@@ -210,7 +210,7 @@
 	}
 
 	/* Register an interface with the stack */
-	if (orinoco_if_add(priv, phys_addr, card->irq) != 0) {
+	if (orinoco_if_add(priv, phys_addr, card->irq, NULL) != 0) {
 		printk(KERN_ERR PFX "orinoco_if_add() failed\n");
 		goto failed;
 	}
diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/orinoco/cfg.c
index 90dd4d0..81d228d 100644
--- a/drivers/net/wireless/orinoco/cfg.c
+++ b/drivers/net/wireless/orinoco/cfg.c
@@ -189,7 +189,7 @@
 	if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
 		/* Fast channel change - no commit if successful */
 		hermes_t *hw = &priv->hw;
-		err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
+		err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
 					    HERMES_TEST_SET_CHANNEL,
 					channel, NULL);
 	}
diff --git a/drivers/net/wireless/orinoco/fw.c b/drivers/net/wireless/orinoco/fw.c
index 5ea0f7c..3e1947d 100644
--- a/drivers/net/wireless/orinoco/fw.c
+++ b/drivers/net/wireless/orinoco/fw.c
@@ -122,7 +122,7 @@
 	dev_dbg(dev, "Attempting to download firmware %s\n", firmware);
 
 	/* Read current plug data */
-	err = hermes_read_pda(hw, pda, fw->pda_addr, fw->pda_size, 0);
+	err = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size);
 	dev_dbg(dev, "Read PDA returned %d\n", err);
 	if (err)
 		goto free;
@@ -149,7 +149,7 @@
 	}
 
 	/* Enable aux port to allow programming */
-	err = hermesi_program_init(hw, le32_to_cpu(hdr->entry_point));
+	err = hw->ops->program_init(hw, le32_to_cpu(hdr->entry_point));
 	dev_dbg(dev, "Program init returned %d\n", err);
 	if (err != 0)
 		goto abort;
@@ -177,7 +177,7 @@
 		goto abort;
 
 	/* Tell card we've finished */
-	err = hermesi_program_end(hw);
+	err = hw->ops->program_end(hw);
 	dev_dbg(dev, "Program end returned %d\n", err);
 	if (err != 0)
 		goto abort;
@@ -224,7 +224,7 @@
 		if (!pda)
 			return -ENOMEM;
 
-		ret = hermes_read_pda(hw, pda, fw->pda_addr, fw->pda_size, 1);
+		ret = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size);
 		if (ret)
 			goto free;
 	}
@@ -260,7 +260,7 @@
 	}
 
 	/* Reset hermes chip and make sure it responds */
-	ret = hermes_init(hw);
+	ret = hw->ops->init(hw);
 
 	/* hermes_reset() should return 0 with the secondary firmware */
 	if (secondary && ret != 0)
diff --git a/drivers/net/wireless/orinoco/hermes.c b/drivers/net/wireless/orinoco/hermes.c
index 1a2fca7..6c6a23e 100644
--- a/drivers/net/wireless/orinoco/hermes.c
+++ b/drivers/net/wireless/orinoco/hermes.c
@@ -52,6 +52,26 @@
 #define ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us */
 
 /*
+ * AUX port access.  To unlock the AUX port write the access keys to the
+ * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL
+ * register.  Then read it and make sure it's HERMES_AUX_ENABLED.
+ */
+#define HERMES_AUX_ENABLE	0x8000	/* Enable auxiliary port access */
+#define HERMES_AUX_DISABLE	0x4000	/* Disable to auxiliary port access */
+#define HERMES_AUX_ENABLED	0xC000	/* Auxiliary port is open */
+#define HERMES_AUX_DISABLED	0x0000	/* Auxiliary port is closed */
+
+#define HERMES_AUX_PW0	0xFE01
+#define HERMES_AUX_PW1	0xDC23
+#define HERMES_AUX_PW2	0xBA45
+
+/* HERMES_CMD_DOWNLD */
+#define HERMES_PROGRAM_DISABLE             (0x0000 | HERMES_CMD_DOWNLD)
+#define HERMES_PROGRAM_ENABLE_VOLATILE     (0x0100 | HERMES_CMD_DOWNLD)
+#define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD)
+#define HERMES_PROGRAM_NON_VOLATILE        (0x0300 | HERMES_CMD_DOWNLD)
+
+/*
  * Debugging helpers
  */
 
@@ -70,6 +90,7 @@
 
 #endif /* ! HERMES_DEBUG */
 
+static const struct hermes_ops hermes_ops_local;
 
 /*
  * Internal functions
@@ -111,9 +132,9 @@
  */
 
 /* For doing cmds that wipe the magic constant in SWSUPPORT0 */
-int hermes_doicmd_wait(hermes_t *hw, u16 cmd,
-		       u16 parm0, u16 parm1, u16 parm2,
-		       struct hermes_response *resp)
+static int hermes_doicmd_wait(hermes_t *hw, u16 cmd,
+			      u16 parm0, u16 parm1, u16 parm2,
+			      struct hermes_response *resp)
 {
 	int err = 0;
 	int k;
@@ -163,17 +184,18 @@
 out:
 	return err;
 }
-EXPORT_SYMBOL(hermes_doicmd_wait);
 
 void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing)
 {
 	hw->iobase = address;
 	hw->reg_spacing = reg_spacing;
 	hw->inten = 0x0;
+	hw->eeprom_pda = false;
+	hw->ops = &hermes_ops_local;
 }
 EXPORT_SYMBOL(hermes_struct_init);
 
-int hermes_init(hermes_t *hw)
+static int hermes_init(hermes_t *hw)
 {
 	u16 reg;
 	int err = 0;
@@ -217,7 +239,6 @@
 
 	return err;
 }
-EXPORT_SYMBOL(hermes_init);
 
 /* Issue a command to the chip, and (busy!) wait for it to
  * complete.
@@ -228,8 +249,8 @@
  *     > 0 on error returned by the firmware
  *
  * Callable from any context, but locking is your problem. */
-int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0,
-		      struct hermes_response *resp)
+static int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0,
+			     struct hermes_response *resp)
 {
 	int err;
 	int k;
@@ -291,9 +312,8 @@
  out:
 	return err;
 }
-EXPORT_SYMBOL(hermes_docmd_wait);
 
-int hermes_allocate(hermes_t *hw, u16 size, u16 *fid)
+static int hermes_allocate(hermes_t *hw, u16 size, u16 *fid)
 {
 	int err = 0;
 	int k;
@@ -333,7 +353,6 @@
 
 	return 0;
 }
-EXPORT_SYMBOL(hermes_allocate);
 
 /* Set up a BAP to read a particular chunk of data from card's internal buffer.
  *
@@ -403,8 +422,8 @@
  *       0 on success
  *     > 0 on error from firmware
  */
-int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len,
-		     u16 id, u16 offset)
+static int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len,
+			    u16 id, u16 offset)
 {
 	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
 	int err = 0;
@@ -422,7 +441,6 @@
  out:
 	return err;
 }
-EXPORT_SYMBOL(hermes_bap_pread);
 
 /* Write a block of data to the chip's buffer, via the
  * BAP. Synchronization/serialization is the caller's problem.
@@ -432,8 +450,8 @@
  *       0 on success
  *     > 0 on error from firmware
  */
-int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len,
-		      u16 id, u16 offset)
+static int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len,
+			     u16 id, u16 offset)
 {
 	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
 	int err = 0;
@@ -451,7 +469,6 @@
  out:
 	return err;
 }
-EXPORT_SYMBOL(hermes_bap_pwrite);
 
 /* Read a Length-Type-Value record from the card.
  *
@@ -461,8 +478,8 @@
  * practice.
  *
  * Callable from user or bh context.  */
-int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize,
-		    u16 *length, void *buf)
+static int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize,
+			   u16 *length, void *buf)
 {
 	int err = 0;
 	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
@@ -505,10 +522,9 @@
 
 	return 0;
 }
-EXPORT_SYMBOL(hermes_read_ltv);
 
-int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
-		     u16 length, const void *value)
+static int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
+			    u16 length, const void *value)
 {
 	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
 	int err = 0;
@@ -533,4 +549,228 @@
 
 	return err;
 }
-EXPORT_SYMBOL(hermes_write_ltv);
+
+/*** Hermes AUX control ***/
+
+static inline void
+hermes_aux_setaddr(hermes_t *hw, u32 addr)
+{
+	hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7));
+	hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F));
+}
+
+static inline int
+hermes_aux_control(hermes_t *hw, int enabled)
+{
+	int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED;
+	int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE;
+	int i;
+
+	/* Already open? */
+	if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state)
+		return 0;
+
+	hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0);
+	hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1);
+	hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2);
+	hermes_write_reg(hw, HERMES_CONTROL, action);
+
+	for (i = 0; i < 20; i++) {
+		udelay(10);
+		if (hermes_read_reg(hw, HERMES_CONTROL) ==
+		    desired_state)
+			return 0;
+	}
+
+	return -EBUSY;
+}
+
+/*** Hermes programming ***/
+
+/* About to start programming data (Hermes I)
+ * offset is the entry point
+ *
+ * Spectrum_cs' Symbol fw does not require this
+ * wl_lkm Agere fw does
+ * Don't know about intersil
+ */
+static int hermesi_program_init(hermes_t *hw, u32 offset)
+{
+	int err;
+
+	/* Disable interrupts?*/
+	/*hw->inten = 0x0;*/
+	/*hermes_write_regn(hw, INTEN, 0);*/
+	/*hermes_set_irqmask(hw, 0);*/
+
+	/* Acknowledge any outstanding command */
+	hermes_write_regn(hw, EVACK, 0xFFFF);
+
+	/* Using init_cmd_wait rather than cmd_wait */
+	err = hw->ops->init_cmd_wait(hw,
+				     0x0100 | HERMES_CMD_INIT,
+				     0, 0, 0, NULL);
+	if (err)
+		return err;
+
+	err = hw->ops->init_cmd_wait(hw,
+				     0x0000 | HERMES_CMD_INIT,
+				     0, 0, 0, NULL);
+	if (err)
+		return err;
+
+	err = hermes_aux_control(hw, 1);
+	pr_debug("AUX enable returned %d\n", err);
+
+	if (err)
+		return err;
+
+	pr_debug("Enabling volatile, EP 0x%08x\n", offset);
+	err = hw->ops->init_cmd_wait(hw,
+				     HERMES_PROGRAM_ENABLE_VOLATILE,
+				     offset & 0xFFFFu,
+				     offset >> 16,
+				     0,
+				     NULL);
+	pr_debug("PROGRAM_ENABLE returned %d\n", err);
+
+	return err;
+}
+
+/* Done programming data (Hermes I)
+ *
+ * Spectrum_cs' Symbol fw does not require this
+ * wl_lkm Agere fw does
+ * Don't know about intersil
+ */
+static int hermesi_program_end(hermes_t *hw)
+{
+	struct hermes_response resp;
+	int rc = 0;
+	int err;
+
+	rc = hw->ops->cmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp);
+
+	pr_debug("PROGRAM_DISABLE returned %d, "
+		 "r0 0x%04x, r1 0x%04x, r2 0x%04x\n",
+		 rc, resp.resp0, resp.resp1, resp.resp2);
+
+	if ((rc == 0) &&
+	    ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD))
+		rc = -EIO;
+
+	err = hermes_aux_control(hw, 0);
+	pr_debug("AUX disable returned %d\n", err);
+
+	/* Acknowledge any outstanding command */
+	hermes_write_regn(hw, EVACK, 0xFFFF);
+
+	/* Reinitialise, ignoring return */
+	(void) hw->ops->init_cmd_wait(hw, 0x0000 | HERMES_CMD_INIT,
+				      0, 0, 0, NULL);
+
+	return rc ? rc : err;
+}
+
+static int hermes_program_bytes(struct hermes *hw, const char *data,
+				u32 addr, u32 len)
+{
+	/* wl lkm splits the programming into chunks of 2000 bytes.
+	 * This restriction appears to come from USB. The PCMCIA
+	 * adapters can program the whole lot in one go */
+	hermes_aux_setaddr(hw, addr);
+	hermes_write_bytes(hw, HERMES_AUXDATA, data, len);
+	return 0;
+}
+
+/* Read PDA from the adapter */
+static int hermes_read_pda(hermes_t *hw, __le16 *pda, u32 pda_addr, u16 pda_len)
+{
+	int ret;
+	u16 pda_size;
+	u16 data_len = pda_len;
+	__le16 *data = pda;
+
+	if (hw->eeprom_pda) {
+		/* PDA of spectrum symbol is in eeprom */
+
+		/* Issue command to read EEPROM */
+		ret = hw->ops->cmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
+		if (ret)
+			return ret;
+	} else {
+		/* wl_lkm does not include PDA size in the PDA area.
+		 * We will pad the information into pda, so other routines
+		 * don't have to be modified */
+		pda[0] = cpu_to_le16(pda_len - 2);
+			/* Includes CFG_PROD_DATA but not itself */
+		pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */
+		data_len = pda_len - 4;
+		data = pda + 2;
+	}
+
+	/* Open auxiliary port */
+	ret = hermes_aux_control(hw, 1);
+	pr_debug("AUX enable returned %d\n", ret);
+	if (ret)
+		return ret;
+
+	/* Read PDA */
+	hermes_aux_setaddr(hw, pda_addr);
+	hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2);
+
+	/* Close aux port */
+	ret = hermes_aux_control(hw, 0);
+	pr_debug("AUX disable returned %d\n", ret);
+
+	/* Check PDA length */
+	pda_size = le16_to_cpu(pda[0]);
+	pr_debug("Actual PDA length %d, Max allowed %d\n",
+		 pda_size, pda_len);
+	if (pda_size > pda_len)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void hermes_lock_irqsave(spinlock_t *lock,
+				unsigned long *flags) __acquires(lock)
+{
+	spin_lock_irqsave(lock, *flags);
+}
+
+static void hermes_unlock_irqrestore(spinlock_t *lock,
+				     unsigned long *flags) __releases(lock)
+{
+	spin_unlock_irqrestore(lock, *flags);
+}
+
+static void hermes_lock_irq(spinlock_t *lock) __acquires(lock)
+{
+	spin_lock_irq(lock);
+}
+
+static void hermes_unlock_irq(spinlock_t *lock) __releases(lock)
+{
+	spin_unlock_irq(lock);
+}
+
+/* Hermes operations for local buses */
+static const struct hermes_ops hermes_ops_local = {
+	.init = hermes_init,
+	.cmd_wait = hermes_docmd_wait,
+	.init_cmd_wait = hermes_doicmd_wait,
+	.allocate = hermes_allocate,
+	.read_ltv = hermes_read_ltv,
+	.write_ltv = hermes_write_ltv,
+	.bap_pread = hermes_bap_pread,
+	.bap_pwrite = hermes_bap_pwrite,
+	.read_pda = hermes_read_pda,
+	.program_init = hermesi_program_init,
+	.program_end = hermesi_program_end,
+	.program = hermes_program_bytes,
+	.lock_irqsave = hermes_lock_irqsave,
+	.unlock_irqrestore = hermes_unlock_irqrestore,
+	.lock_irq = hermes_lock_irq,
+	.unlock_irq = hermes_unlock_irq,
+};
diff --git a/drivers/net/wireless/orinoco/hermes.h b/drivers/net/wireless/orinoco/hermes.h
index 2dddbb5..9ca34e7 100644
--- a/drivers/net/wireless/orinoco/hermes.h
+++ b/drivers/net/wireless/orinoco/hermes.h
@@ -374,6 +374,37 @@
 /* Timeouts */
 #define HERMES_BAP_BUSY_TIMEOUT (10000) /* In iterations of ~1us */
 
+struct hermes;
+
+/* Functions to access hardware */
+struct hermes_ops {
+	int (*init)(struct hermes *hw);
+	int (*cmd_wait)(struct hermes *hw, u16 cmd, u16 parm0,
+			struct hermes_response *resp);
+	int (*init_cmd_wait)(struct hermes *hw, u16 cmd,
+			     u16 parm0, u16 parm1, u16 parm2,
+			     struct hermes_response *resp);
+	int (*allocate)(struct hermes *hw, u16 size, u16 *fid);
+	int (*read_ltv)(struct hermes *hw, int bap, u16 rid, unsigned buflen,
+			u16 *length, void *buf);
+	int (*write_ltv)(struct hermes *hw, int bap, u16 rid,
+			 u16 length, const void *value);
+	int (*bap_pread)(struct hermes *hw, int bap, void *buf, int len,
+			 u16 id, u16 offset);
+	int (*bap_pwrite)(struct hermes *hw, int bap, const void *buf,
+			  int len, u16 id, u16 offset);
+	int (*read_pda)(struct hermes *hw, __le16 *pda,
+			u32 pda_addr, u16 pda_len);
+	int (*program_init)(struct hermes *hw, u32 entry_point);
+	int (*program_end)(struct hermes *hw);
+	int (*program)(struct hermes *hw, const char *buf,
+		       u32 addr, u32 len);
+	void (*lock_irqsave)(spinlock_t *lock, unsigned long *flags);
+	void (*unlock_irqrestore)(spinlock_t *lock, unsigned long *flags);
+	void (*lock_irq)(spinlock_t *lock);
+	void (*unlock_irq)(spinlock_t *lock);
+};
+
 /* Basic control structure */
 typedef struct hermes {
 	void __iomem *iobase;
@@ -381,6 +412,9 @@
 #define HERMES_16BIT_REGSPACING	0
 #define HERMES_32BIT_REGSPACING	1
 	u16 inten; /* Which interrupts should be enabled? */
+	bool eeprom_pda;
+	const struct hermes_ops *ops;
+	void *priv;
 } hermes_t;
 
 /* Register access convenience macros */
@@ -394,22 +428,6 @@
 
 /* Function prototypes */
 void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing);
-int hermes_init(hermes_t *hw);
-int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0,
-		      struct hermes_response *resp);
-int hermes_doicmd_wait(hermes_t *hw, u16 cmd,
-		       u16 parm0, u16 parm1, u16 parm2,
-		       struct hermes_response *resp);
-int hermes_allocate(hermes_t *hw, u16 size, u16 *fid);
-
-int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len,
-		       u16 id, u16 offset);
-int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len,
-			u16 id, u16 offset);
-int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned buflen,
-		    u16 *length, void *buf);
-int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
-		      u16 length, const void *value);
 
 /* Inline functions */
 
@@ -426,13 +444,13 @@
 
 static inline int hermes_enable_port(hermes_t *hw, int port)
 {
-	return hermes_docmd_wait(hw, HERMES_CMD_ENABLE | (port << 8),
+	return hw->ops->cmd_wait(hw, HERMES_CMD_ENABLE | (port << 8),
 				 0, NULL);
 }
 
 static inline int hermes_disable_port(hermes_t *hw, int port)
 {
-	return hermes_docmd_wait(hw, HERMES_CMD_DISABLE | (port << 8),
+	return hw->ops->cmd_wait(hw, HERMES_CMD_DISABLE | (port << 8),
 				 0, NULL);
 }
 
@@ -440,7 +458,7 @@
  * information frame in __orinoco_ev_info() */
 static inline int hermes_inquire(hermes_t *hw, u16 rid)
 {
-	return hermes_docmd_wait(hw, HERMES_CMD_INQUIRE, rid, NULL);
+	return hw->ops->cmd_wait(hw, HERMES_CMD_INQUIRE, rid, NULL);
 }
 
 #define HERMES_BYTES_TO_RECLEN(n) ((((n)+1)/2) + 1)
@@ -475,10 +493,10 @@
 }
 
 #define HERMES_READ_RECORD(hw, bap, rid, buf) \
-	(hermes_read_ltv((hw), (bap), (rid), sizeof(*buf), NULL, (buf)))
+	(hw->ops->read_ltv((hw), (bap), (rid), sizeof(*buf), NULL, (buf)))
 #define HERMES_WRITE_RECORD(hw, bap, rid, buf) \
-	(hermes_write_ltv((hw), (bap), (rid), \
-			  HERMES_BYTES_TO_RECLEN(sizeof(*buf)), (buf)))
+	(hw->ops->write_ltv((hw), (bap), (rid), \
+			    HERMES_BYTES_TO_RECLEN(sizeof(*buf)), (buf)))
 
 static inline int hermes_read_wordrec(hermes_t *hw, int bap, u16 rid, u16 *word)
 {
diff --git a/drivers/net/wireless/orinoco/hermes_dld.c b/drivers/net/wireless/orinoco/hermes_dld.c
index fb157eb..6da85e7 100644
--- a/drivers/net/wireless/orinoco/hermes_dld.c
+++ b/drivers/net/wireless/orinoco/hermes_dld.c
@@ -46,37 +46,11 @@
 
 #define PFX "hermes_dld: "
 
-/*
- * AUX port access.  To unlock the AUX port write the access keys to the
- * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL
- * register.  Then read it and make sure it's HERMES_AUX_ENABLED.
- */
-#define HERMES_AUX_ENABLE	0x8000	/* Enable auxiliary port access */
-#define HERMES_AUX_DISABLE	0x4000	/* Disable to auxiliary port access */
-#define HERMES_AUX_ENABLED	0xC000	/* Auxiliary port is open */
-#define HERMES_AUX_DISABLED	0x0000	/* Auxiliary port is closed */
-
-#define HERMES_AUX_PW0	0xFE01
-#define HERMES_AUX_PW1	0xDC23
-#define HERMES_AUX_PW2	0xBA45
-
-/* HERMES_CMD_DOWNLD */
-#define HERMES_PROGRAM_DISABLE             (0x0000 | HERMES_CMD_DOWNLD)
-#define HERMES_PROGRAM_ENABLE_VOLATILE     (0x0100 | HERMES_CMD_DOWNLD)
-#define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD)
-#define HERMES_PROGRAM_NON_VOLATILE        (0x0300 | HERMES_CMD_DOWNLD)
-
 /* End markers used in dblocks */
 #define PDI_END		0x00000000	/* End of PDA */
 #define BLOCK_END	0xFFFFFFFF	/* Last image block */
 #define TEXT_END	0x1A		/* End of text header */
 
-/* Limit the amout we try to download in a single shot.
- * Size is in bytes.
- */
-#define MAX_DL_SIZE 1024
-#define LIMIT_PROGRAM_SIZE 0
-
 /*
  * The following structures have little-endian fields denoted by
  * the leading underscore.  Don't access them directly - use inline
@@ -165,41 +139,6 @@
 	return 2 * (le16_to_cpu(pdi->len) - 1);
 }
 
-/*** Hermes AUX control ***/
-
-static inline void
-hermes_aux_setaddr(hermes_t *hw, u32 addr)
-{
-	hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7));
-	hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F));
-}
-
-static inline int
-hermes_aux_control(hermes_t *hw, int enabled)
-{
-	int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED;
-	int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE;
-	int i;
-
-	/* Already open? */
-	if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state)
-		return 0;
-
-	hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0);
-	hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1);
-	hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2);
-	hermes_write_reg(hw, HERMES_CONTROL, action);
-
-	for (i = 0; i < 20; i++) {
-		udelay(10);
-		if (hermes_read_reg(hw, HERMES_CONTROL) ==
-		    desired_state)
-			return 0;
-	}
-
-	return -EBUSY;
-}
-
 /*** Plug Data Functions ***/
 
 /*
@@ -271,62 +210,7 @@
 		return -EINVAL;
 
 	/* do the actual plugging */
-	hermes_aux_setaddr(hw, pdr_addr(pdr));
-	hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi));
-
-	return 0;
-}
-
-/* Read PDA from the adapter */
-int hermes_read_pda(hermes_t *hw,
-		    __le16 *pda,
-		    u32 pda_addr,
-		    u16 pda_len,
-		    int use_eeprom) /* can we get this into hw? */
-{
-	int ret;
-	u16 pda_size;
-	u16 data_len = pda_len;
-	__le16 *data = pda;
-
-	if (use_eeprom) {
-		/* PDA of spectrum symbol is in eeprom */
-
-		/* Issue command to read EEPROM */
-		ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
-		if (ret)
-			return ret;
-	} else {
-		/* wl_lkm does not include PDA size in the PDA area.
-		 * We will pad the information into pda, so other routines
-		 * don't have to be modified */
-		pda[0] = cpu_to_le16(pda_len - 2);
-			/* Includes CFG_PROD_DATA but not itself */
-		pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */
-		data_len = pda_len - 4;
-		data = pda + 2;
-	}
-
-	/* Open auxiliary port */
-	ret = hermes_aux_control(hw, 1);
-	pr_debug(PFX "AUX enable returned %d\n", ret);
-	if (ret)
-		return ret;
-
-	/* read PDA from EEPROM */
-	hermes_aux_setaddr(hw, pda_addr);
-	hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2);
-
-	/* Close aux port */
-	ret = hermes_aux_control(hw, 0);
-	pr_debug(PFX "AUX disable returned %d\n", ret);
-
-	/* Check PDA length */
-	pda_size = le16_to_cpu(pda[0]);
-	pr_debug(PFX "Actual PDA length %d, Max allowed %d\n",
-		 pda_size, pda_len);
-	if (pda_size > pda_len)
-		return -EINVAL;
+	hw->ops->program(hw, pdi->data, pdr_addr(pdr), pdi_len(pdi));
 
 	return 0;
 }
@@ -389,101 +273,13 @@
 
 /*** Hermes programming ***/
 
-/* About to start programming data (Hermes I)
- * offset is the entry point
- *
- * Spectrum_cs' Symbol fw does not require this
- * wl_lkm Agere fw does
- * Don't know about intersil
- */
-int hermesi_program_init(hermes_t *hw, u32 offset)
-{
-	int err;
-
-	/* Disable interrupts?*/
-	/*hw->inten = 0x0;*/
-	/*hermes_write_regn(hw, INTEN, 0);*/
-	/*hermes_set_irqmask(hw, 0);*/
-
-	/* Acknowledge any outstanding command */
-	hermes_write_regn(hw, EVACK, 0xFFFF);
-
-	/* Using doicmd_wait rather than docmd_wait */
-	err = hermes_doicmd_wait(hw,
-				 0x0100 | HERMES_CMD_INIT,
-				 0, 0, 0, NULL);
-	if (err)
-		return err;
-
-	err = hermes_doicmd_wait(hw,
-				 0x0000 | HERMES_CMD_INIT,
-				 0, 0, 0, NULL);
-	if (err)
-		return err;
-
-	err = hermes_aux_control(hw, 1);
-	pr_debug(PFX "AUX enable returned %d\n", err);
-
-	if (err)
-		return err;
-
-	pr_debug(PFX "Enabling volatile, EP 0x%08x\n", offset);
-	err = hermes_doicmd_wait(hw,
-				 HERMES_PROGRAM_ENABLE_VOLATILE,
-				 offset & 0xFFFFu,
-				 offset >> 16,
-				 0,
-				 NULL);
-	pr_debug(PFX "PROGRAM_ENABLE returned %d\n", err);
-
-	return err;
-}
-
-/* Done programming data (Hermes I)
- *
- * Spectrum_cs' Symbol fw does not require this
- * wl_lkm Agere fw does
- * Don't know about intersil
- */
-int hermesi_program_end(hermes_t *hw)
-{
-	struct hermes_response resp;
-	int rc = 0;
-	int err;
-
-	rc = hermes_docmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp);
-
-	pr_debug(PFX "PROGRAM_DISABLE returned %d, "
-		 "r0 0x%04x, r1 0x%04x, r2 0x%04x\n",
-		 rc, resp.resp0, resp.resp1, resp.resp2);
-
-	if ((rc == 0) &&
-	    ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD))
-		rc = -EIO;
-
-	err = hermes_aux_control(hw, 0);
-	pr_debug(PFX "AUX disable returned %d\n", err);
-
-	/* Acknowledge any outstanding command */
-	hermes_write_regn(hw, EVACK, 0xFFFF);
-
-	/* Reinitialise, ignoring return */
-	(void) hermes_doicmd_wait(hw, 0x0000 | HERMES_CMD_INIT,
-				  0, 0, 0, NULL);
-
-	return rc ? rc : err;
-}
-
 /* Program the data blocks */
 int hermes_program(hermes_t *hw, const char *first_block, const void *end)
 {
 	const struct dblock *blk;
 	u32 blkaddr;
 	u32 blklen;
-#if LIMIT_PROGRAM_SIZE
-	u32 addr;
-	u32 len;
-#endif
+	int err = 0;
 
 	blk = (const struct dblock *) first_block;
 
@@ -498,30 +294,10 @@
 		pr_debug(PFX "Programming block of length %d "
 			 "to address 0x%08x\n", blklen, blkaddr);
 
-#if !LIMIT_PROGRAM_SIZE
-		/* wl_lkm driver splits this into writes of 2000 bytes */
-		hermes_aux_setaddr(hw, blkaddr);
-		hermes_write_bytes(hw, HERMES_AUXDATA, blk->data,
-				   blklen);
-#else
-		len = (blklen < MAX_DL_SIZE) ? blklen : MAX_DL_SIZE;
-		addr = blkaddr;
+		err = hw->ops->program(hw, blk->data, blkaddr, blklen);
+		if (err)
+			break;
 
-		while (addr < (blkaddr + blklen)) {
-			pr_debug(PFX "Programming subblock of length %d "
-				 "to address 0x%08x. Data @ %p\n",
-				 len, addr, &blk->data[addr - blkaddr]);
-
-			hermes_aux_setaddr(hw, addr);
-			hermes_write_bytes(hw, HERMES_AUXDATA,
-					   &blk->data[addr - blkaddr],
-					   len);
-
-			addr += len;
-			len = ((blkaddr + blklen - addr) < MAX_DL_SIZE) ?
-				(blkaddr + blklen - addr) : MAX_DL_SIZE;
-		}
-#endif
 		blk = (const struct dblock *) &blk->data[blklen];
 
 		if ((void *) blk > (end - sizeof(*blk)))
@@ -530,7 +306,7 @@
 		blkaddr = dblock_addr(blk);
 		blklen = dblock_len(blk);
 	}
-	return 0;
+	return err;
 }
 
 /*** Default plugging data for Hermes I ***/
@@ -690,9 +466,8 @@
 			if ((pdi_len(pdi) == pdr_len(pdr)) &&
 			    ((void *) pdi->data + pdi_len(pdi) < pda_end)) {
 				/* do the actual plugging */
-				hermes_aux_setaddr(hw, pdr_addr(pdr));
-				hermes_write_bytes(hw, HERMES_AUXDATA,
-						   pdi->data, pdi_len(pdi));
+				hw->ops->program(hw, pdi->data, pdr_addr(pdr),
+						 pdi_len(pdi));
 			}
 		}
 
diff --git a/drivers/net/wireless/orinoco/hw.c b/drivers/net/wireless/orinoco/hw.c
index 741f8ce..6fbd788 100644
--- a/drivers/net/wireless/orinoco/hw.c
+++ b/drivers/net/wireless/orinoco/hw.c
@@ -177,9 +177,9 @@
 		/* 3Com MAC : 00:50:DA:* */
 		memset(tmp, 0, sizeof(tmp));
 		/* Get the Symbol firmware version */
-		err = hermes_read_ltv(hw, USER_BAP,
-				      HERMES_RID_SECONDARYVERSION_SYMBOL,
-				      SYMBOL_MAX_VER_LEN, NULL, &tmp);
+		err = hw->ops->read_ltv(hw, USER_BAP,
+					HERMES_RID_SECONDARYVERSION_SYMBOL,
+					SYMBOL_MAX_VER_LEN, NULL, &tmp);
 		if (err) {
 			dev_warn(dev, "Error %d reading Symbol firmware info. "
 				 "Wildly guessing capabilities...\n", err);
@@ -286,8 +286,8 @@
 	u16 reclen;
 
 	/* Get the MAC address */
-	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
-			      ETH_ALEN, NULL, dev_addr);
+	err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
+				ETH_ALEN, NULL, dev_addr);
 	if (err) {
 		dev_warn(dev, "Failed to read MAC address!\n");
 		goto out;
@@ -296,8 +296,8 @@
 	dev_dbg(dev, "MAC address %pM\n", dev_addr);
 
 	/* Get the station name */
-	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
-			      sizeof(nickbuf), &reclen, &nickbuf);
+	err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
+				sizeof(nickbuf), &reclen, &nickbuf);
 	if (err) {
 		dev_err(dev, "failed to read station name\n");
 		goto out;
@@ -413,11 +413,11 @@
 	struct hermes *hw = &priv->hw;
 	int err;
 
-	err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
+	err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid);
 	if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) {
 		/* Try workaround for old Symbol firmware bug */
 		priv->nicbuf_size = TX_NICBUF_SIZE_BUG;
-		err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
+		err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid);
 
 		dev_warn(dev, "Firmware ALLOC bug detected "
 			 "(old Symbol firmware?). Work around %s\n",
@@ -463,8 +463,9 @@
 	struct hermes_idstring idbuf;
 
 	/* Set the MAC address */
-	err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
-			       HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr);
+	err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
+				 HERMES_BYTES_TO_RECLEN(ETH_ALEN),
+				 dev->dev_addr);
 	if (err) {
 		printk(KERN_ERR "%s: Error %d setting MAC address\n",
 		       dev->name, err);
@@ -527,7 +528,7 @@
 	idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
 	memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
 	/* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */
-	err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID,
+	err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID,
 			HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
 			&idbuf);
 	if (err) {
@@ -535,7 +536,7 @@
 		       dev->name, err);
 		return err;
 	}
-	err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
+	err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
 			HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
 			&idbuf);
 	if (err) {
@@ -547,9 +548,9 @@
 	/* Set the station name */
 	idbuf.len = cpu_to_le16(strlen(priv->nick));
 	memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val));
-	err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
-			       HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2),
-			       &idbuf);
+	err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
+				 HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2),
+				 &idbuf);
 	if (err) {
 		printk(KERN_ERR "%s: Error %d setting nickname\n",
 		       dev->name, err);
@@ -664,12 +665,12 @@
 	if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
 		/* Enable monitor mode */
 		dev->type = ARPHRD_IEEE80211;
-		err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
+		err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
 					    HERMES_TEST_MONITOR, 0, NULL);
 	} else {
 		/* Disable monitor mode */
 		dev->type = ARPHRD_ETHER;
-		err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
+		err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
 					    HERMES_TEST_STOP, 0, NULL);
 	}
 	if (err)
@@ -695,8 +696,8 @@
 	if ((key < 0) || (key >= 4))
 		return -EINVAL;
 
-	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV,
-			      sizeof(tsc_arr), NULL, &tsc_arr);
+	err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV,
+				sizeof(tsc_arr), NULL, &tsc_arr);
 	if (!err)
 		memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0]));
 
@@ -875,7 +876,7 @@
 				memcpy(key, priv->keys[i].key,
 				       priv->keys[i].key_len);
 
-				err = hermes_write_ltv(hw, USER_BAP,
+				err = hw->ops->write_ltv(hw, USER_BAP,
 						HERMES_RID_CNFDEFAULTKEY0 + i,
 						HERMES_BYTES_TO_RECLEN(keylen),
 						key);
@@ -1092,7 +1093,7 @@
 			memcpy(mclist.addr[i++], ha->addr, ETH_ALEN);
 		}
 
-		err = hermes_write_ltv(hw, USER_BAP,
+		err = hw->ops->write_ltv(hw, USER_BAP,
 				   HERMES_RID_CNFGROUPADDRESSES,
 				   HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN),
 				   &mclist);
@@ -1134,15 +1135,15 @@
 		rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID :
 			HERMES_RID_CNFDESIREDSSID;
 
-		err = hermes_read_ltv(hw, USER_BAP, rid, sizeof(essidbuf),
-				      NULL, &essidbuf);
+		err = hw->ops->read_ltv(hw, USER_BAP, rid, sizeof(essidbuf),
+					NULL, &essidbuf);
 		if (err)
 			goto fail_unlock;
 	} else {
 		*active = 0;
 
-		err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID,
-				      sizeof(essidbuf), NULL, &essidbuf);
+		err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID,
+					sizeof(essidbuf), NULL, &essidbuf);
 		if (err)
 			goto fail_unlock;
 	}
@@ -1213,8 +1214,8 @@
 	if (orinoco_lock(priv, &flags) != 0)
 		return -EBUSY;
 
-	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES,
-			      sizeof(list), NULL, &list);
+	err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES,
+				sizeof(list), NULL, &list);
 	orinoco_unlock(priv, &flags);
 
 	if (err)
@@ -1281,7 +1282,7 @@
 				idbuf.len = cpu_to_le16(len);
 				memcpy(idbuf.val, ssid->ssid, len);
 
-				err = hermes_write_ltv(hw, USER_BAP,
+				err = hw->ops->write_ltv(hw, USER_BAP,
 					       HERMES_RID_CNFSCANSSID_AGERE,
 					       HERMES_BYTES_TO_RECLEN(len + 2),
 					       &idbuf);
@@ -1345,8 +1346,8 @@
 	hermes_t *hw = &priv->hw;
 	int err;
 
-	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
-			      ETH_ALEN, NULL, addr);
+	err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
+				ETH_ALEN, NULL, addr);
 
 	return err;
 }
diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/orinoco/main.c
index 413e9ab..1d60c7e 100644
--- a/drivers/net/wireless/orinoco/main.c
+++ b/drivers/net/wireless/orinoco/main.c
@@ -254,7 +254,7 @@
 /* Device methods                                                   */
 /********************************************************************/
 
-static int orinoco_open(struct net_device *dev)
+int orinoco_open(struct net_device *dev)
 {
 	struct orinoco_private *priv = ndev_priv(dev);
 	unsigned long flags;
@@ -272,8 +272,9 @@
 
 	return err;
 }
+EXPORT_SYMBOL(orinoco_open);
 
-static int orinoco_stop(struct net_device *dev)
+int orinoco_stop(struct net_device *dev)
 {
 	struct orinoco_private *priv = ndev_priv(dev);
 	int err = 0;
@@ -281,25 +282,27 @@
 	/* We mustn't use orinoco_lock() here, because we need to be
 	   able to close the interface even if hw_unavailable is set
 	   (e.g. as we're released after a PC Card removal) */
-	spin_lock_irq(&priv->lock);
+	orinoco_lock_irq(priv);
 
 	priv->open = 0;
 
 	err = __orinoco_down(priv);
 
-	spin_unlock_irq(&priv->lock);
+	orinoco_unlock_irq(priv);
 
 	return err;
 }
+EXPORT_SYMBOL(orinoco_stop);
 
-static struct net_device_stats *orinoco_get_stats(struct net_device *dev)
+struct net_device_stats *orinoco_get_stats(struct net_device *dev)
 {
 	struct orinoco_private *priv = ndev_priv(dev);
 
 	return &priv->stats;
 }
+EXPORT_SYMBOL(orinoco_get_stats);
 
-static void orinoco_set_multicast_list(struct net_device *dev)
+void orinoco_set_multicast_list(struct net_device *dev)
 {
 	struct orinoco_private *priv = ndev_priv(dev);
 	unsigned long flags;
@@ -313,8 +316,9 @@
 	__orinoco_set_multicast_list(dev);
 	orinoco_unlock(priv, &flags);
 }
+EXPORT_SYMBOL(orinoco_set_multicast_list);
 
-static int orinoco_change_mtu(struct net_device *dev, int new_mtu)
+int orinoco_change_mtu(struct net_device *dev, int new_mtu)
 {
 	struct orinoco_private *priv = ndev_priv(dev);
 
@@ -330,6 +334,7 @@
 
 	return 0;
 }
+EXPORT_SYMBOL(orinoco_change_mtu);
 
 /********************************************************************/
 /* Tx path                                                          */
@@ -400,8 +405,8 @@
 		memset(&desc, 0, sizeof(desc));
 
 		*txcntl = cpu_to_le16(tx_control);
-		err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
-					txfid, 0);
+		err = hw->ops->bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
+					  txfid, 0);
 		if (err) {
 			if (net_ratelimit())
 				printk(KERN_ERR "%s: Error %d writing Tx "
@@ -414,8 +419,8 @@
 		memset(&desc, 0, sizeof(desc));
 
 		desc.tx_control = cpu_to_le16(tx_control);
-		err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
-					txfid, 0);
+		err = hw->ops->bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
+					  txfid, 0);
 		if (err) {
 			if (net_ratelimit())
 				printk(KERN_ERR "%s: Error %d writing Tx "
@@ -458,8 +463,8 @@
 		memcpy(eh, &hdr, sizeof(hdr));
 	}
 
-	err = hermes_bap_pwrite(hw, USER_BAP, skb->data, skb->len,
-				txfid, HERMES_802_3_OFFSET);
+	err = hw->ops->bap_pwrite(hw, USER_BAP, skb->data, skb->len,
+				  txfid, HERMES_802_3_OFFSET);
 	if (err) {
 		printk(KERN_ERR "%s: Error %d writing packet to BAP\n",
 		       dev->name, err);
@@ -490,8 +495,8 @@
 			    skb->data + ETH_HLEN, skb->len - ETH_HLEN, mic);
 
 		/* Write the MIC */
-		err = hermes_bap_pwrite(hw, USER_BAP, &mic_buf[0], len,
-					txfid, HERMES_802_3_OFFSET + offset);
+		err = hw->ops->bap_pwrite(hw, USER_BAP, &mic_buf[0], len,
+					  txfid, HERMES_802_3_OFFSET + offset);
 		if (err) {
 			printk(KERN_ERR "%s: Error %d writing MIC to BAP\n",
 			       dev->name, err);
@@ -502,7 +507,7 @@
 	/* Finally, we actually initiate the send */
 	netif_stop_queue(dev);
 
-	err = hermes_docmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL,
+	err = hw->ops->cmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL,
 				txfid, NULL);
 	if (err) {
 		netif_start_queue(dev);
@@ -572,9 +577,9 @@
 		return; /* Nothing's really happened */
 
 	/* Read part of the frame header - we need status and addr1 */
-	err = hermes_bap_pread(hw, IRQ_BAP, &hdr,
-			       sizeof(struct hermes_txexc_data),
-			       fid, 0);
+	err = hw->ops->bap_pread(hw, IRQ_BAP, &hdr,
+				 sizeof(struct hermes_txexc_data),
+				 fid, 0);
 
 	hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
 	stats->tx_errors++;
@@ -615,7 +620,7 @@
 	netif_wake_queue(dev);
 }
 
-static void orinoco_tx_timeout(struct net_device *dev)
+void orinoco_tx_timeout(struct net_device *dev)
 {
 	struct orinoco_private *priv = ndev_priv(dev);
 	struct net_device_stats *stats = &priv->stats;
@@ -630,6 +635,7 @@
 
 	schedule_work(&priv->reset_work);
 }
+EXPORT_SYMBOL(orinoco_tx_timeout);
 
 /********************************************************************/
 /* Rx path (data frames)                                            */
@@ -764,9 +770,9 @@
 
 	/* If any, copy the data from the card to the skb */
 	if (datalen > 0) {
-		err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, datalen),
-				       ALIGN(datalen, 2), rxfid,
-				       HERMES_802_2_OFFSET);
+		err = hw->ops->bap_pread(hw, IRQ_BAP, skb_put(skb, datalen),
+					 ALIGN(datalen, 2), rxfid,
+					 HERMES_802_2_OFFSET);
 		if (err) {
 			printk(KERN_ERR "%s: error %d reading monitor frame\n",
 			       dev->name, err);
@@ -792,7 +798,7 @@
 	stats->rx_dropped++;
 }
 
-static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
+void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
 {
 	struct orinoco_private *priv = ndev_priv(dev);
 	struct net_device_stats *stats = &priv->stats;
@@ -814,8 +820,8 @@
 
 	rxfid = hermes_read_regn(hw, RXFID);
 
-	err = hermes_bap_pread(hw, IRQ_BAP, desc, sizeof(*desc),
-			       rxfid, 0);
+	err = hw->ops->bap_pread(hw, IRQ_BAP, desc, sizeof(*desc),
+				 rxfid, 0);
 	if (err) {
 		printk(KERN_ERR "%s: error %d reading Rx descriptor. "
 		       "Frame dropped.\n", dev->name, err);
@@ -882,9 +888,9 @@
 	   nothing is removed.  2 is for aligning the IP header.  */
 	skb_reserve(skb, ETH_HLEN + 2);
 
-	err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, length),
-			       ALIGN(length, 2), rxfid,
-			       HERMES_802_2_OFFSET);
+	err = hw->ops->bap_pread(hw, IRQ_BAP, skb_put(skb, length),
+				 ALIGN(length, 2), rxfid,
+				 HERMES_802_2_OFFSET);
 	if (err) {
 		printk(KERN_ERR "%s: error %d reading frame. "
 		       "Frame dropped.\n", dev->name, err);
@@ -913,6 +919,7 @@
 out:
 	kfree(desc);
 }
+EXPORT_SYMBOL(__orinoco_ev_rx);
 
 static void orinoco_rx(struct net_device *dev,
 		       struct hermes_rx_descriptor *desc,
@@ -1145,9 +1152,9 @@
 		goto out;
 
 	/* Read scan results from the firmware */
-	err = hermes_read_ltv(hw, USER_BAP,
-			      HERMES_RID_SCANRESULTSTABLE,
-			      MAX_SCAN_LEN, &len, buf);
+	err = hw->ops->read_ltv(hw, USER_BAP,
+				HERMES_RID_SCANRESULTSTABLE,
+				MAX_SCAN_LEN, &len, buf);
 	if (err) {
 		printk(KERN_ERR "%s: Cannot read scan results\n",
 		       dev->name);
@@ -1194,8 +1201,8 @@
 	union iwreq_data wrqu;
 	int err;
 
-	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
-			      ETH_ALEN, NULL, wrqu.ap_addr.sa_data);
+	err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
+				ETH_ALEN, NULL, wrqu.ap_addr.sa_data);
 	if (err != 0)
 		return;
 
@@ -1217,8 +1224,8 @@
 	if (!priv->has_wpa)
 		return;
 
-	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO,
-			      sizeof(buf), NULL, &buf);
+	err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO,
+				sizeof(buf), NULL, &buf);
 	if (err != 0)
 		return;
 
@@ -1247,8 +1254,9 @@
 	if (!priv->has_wpa)
 		return;
 
-	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_RESP_INFO,
-			      sizeof(buf), NULL, &buf);
+	err = hw->ops->read_ltv(hw, USER_BAP,
+				HERMES_RID_CURRENT_ASSOC_RESP_INFO,
+				sizeof(buf), NULL, &buf);
 	if (err != 0)
 		return;
 
@@ -1353,7 +1361,7 @@
 	spin_unlock_irqrestore(&priv->scan_lock, flags);
 }
 
-static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
+void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
 {
 	struct orinoco_private *priv = ndev_priv(dev);
 	u16 infofid;
@@ -1371,8 +1379,8 @@
 	infofid = hermes_read_regn(hw, INFOFID);
 
 	/* Read the info frame header - don't try too hard */
-	err = hermes_bap_pread(hw, IRQ_BAP, &info, sizeof(info),
-			       infofid, 0);
+	err = hw->ops->bap_pread(hw, IRQ_BAP, &info, sizeof(info),
+				 infofid, 0);
 	if (err) {
 		printk(KERN_ERR "%s: error %d reading info frame. "
 		       "Frame dropped.\n", dev->name, err);
@@ -1393,8 +1401,8 @@
 			len = sizeof(tallies);
 		}
 
-		err = hermes_bap_pread(hw, IRQ_BAP, &tallies, len,
-				       infofid, sizeof(info));
+		err = hw->ops->bap_pread(hw, IRQ_BAP, &tallies, len,
+					 infofid, sizeof(info));
 		if (err)
 			break;
 
@@ -1429,8 +1437,8 @@
 			break;
 		}
 
-		err = hermes_bap_pread(hw, IRQ_BAP, &linkstatus, len,
-				       infofid, sizeof(info));
+		err = hw->ops->bap_pread(hw, IRQ_BAP, &linkstatus, len,
+					 infofid, sizeof(info));
 		if (err)
 			break;
 		newstatus = le16_to_cpu(linkstatus.linkstatus);
@@ -1494,8 +1502,8 @@
 		}
 
 		/* Read scan data */
-		err = hermes_bap_pread(hw, IRQ_BAP, (void *) buf, len,
-				       infofid, sizeof(info));
+		err = hw->ops->bap_pread(hw, IRQ_BAP, (void *) buf, len,
+					 infofid, sizeof(info));
 		if (err) {
 			kfree(buf);
 			qabort_scan(priv);
@@ -1547,8 +1555,8 @@
 			break;
 
 		/* Read scan data */
-		err = hermes_bap_pread(hw, IRQ_BAP, (void *) bss, len,
-				       infofid, sizeof(info));
+		err = hw->ops->bap_pread(hw, IRQ_BAP, (void *) bss, len,
+					 infofid, sizeof(info));
 		if (err)
 			kfree(bss);
 		else
@@ -1571,6 +1579,7 @@
 
 	return;
 }
+EXPORT_SYMBOL(__orinoco_ev_info);
 
 static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw)
 {
@@ -1647,7 +1656,7 @@
 	struct hermes *hw = &priv->hw;
 	int err;
 
-	err = hermes_init(hw);
+	err = hw->ops->init(hw);
 	if (priv->do_fw_download && !err) {
 		err = orinoco_download(priv);
 		if (err)
@@ -1735,7 +1744,7 @@
 	}
 
 	/* This has to be called from user context */
-	spin_lock_irq(&priv->lock);
+	orinoco_lock_irq(priv);
 
 	priv->hw_unavailable--;
 
@@ -1750,7 +1759,7 @@
 			dev->trans_start = jiffies;
 	}
 
-	spin_unlock_irq(&priv->lock);
+	orinoco_unlock_irq(priv);
 
 	return;
  disable:
@@ -1984,7 +1993,7 @@
 	priv->nicbuf_size = IEEE80211_MAX_FRAME_LEN + ETH_HLEN;
 
 	/* Initialize the firmware */
-	err = hermes_init(hw);
+	err = hw->ops->init(hw);
 	if (err != 0) {
 		dev_err(dev, "Failed to initialize firmware (err = %d)\n",
 			err);
@@ -2067,9 +2076,9 @@
 
 	/* Make the hardware available, as long as it hasn't been
 	 * removed elsewhere (e.g. by PCMCIA hot unplug) */
-	spin_lock_irq(&priv->lock);
+	orinoco_lock_irq(priv);
 	priv->hw_unavailable--;
-	spin_unlock_irq(&priv->lock);
+	orinoco_unlock_irq(priv);
 
 	dev_dbg(dev, "Ready\n");
 
@@ -2192,7 +2201,8 @@
  */
 int orinoco_if_add(struct orinoco_private *priv,
 		   unsigned long base_addr,
-		   unsigned int irq)
+		   unsigned int irq,
+		   const struct net_device_ops *ops)
 {
 	struct wiphy *wiphy = priv_to_wiphy(priv);
 	struct wireless_dev *wdev;
@@ -2211,12 +2221,17 @@
 
 	/* Setup / override net_device fields */
 	dev->ieee80211_ptr = wdev;
-	dev->netdev_ops = &orinoco_netdev_ops;
 	dev->watchdog_timeo = HZ; /* 1 second timeout */
 	dev->wireless_handlers = &orinoco_handler_def;
 #ifdef WIRELESS_SPY
 	dev->wireless_data = &priv->wireless_data;
 #endif
+	/* Default to standard ops if not set */
+	if (ops)
+		dev->netdev_ops = ops;
+	else
+		dev->netdev_ops = &orinoco_netdev_ops;
+
 	/* we use the default eth_mac_addr for setting the MAC addr */
 
 	/* Reserve space in skb for the SNAP header */
@@ -2305,7 +2320,7 @@
 	unsigned long flags;
 	int err;
 
-	spin_lock_irqsave(&priv->lock, flags);
+	priv->hw.ops->lock_irqsave(&priv->lock, &flags);
 
 	err = orinoco_reinit_firmware(priv);
 	if (err) {
@@ -2325,7 +2340,7 @@
 	}
 
 exit:
-	spin_unlock_irqrestore(&priv->lock, flags);
+	priv->hw.ops->unlock_irqrestore(&priv->lock, &flags);
 
 	return 0;
 }
@@ -2337,7 +2352,7 @@
 	unsigned long flags;
 	int err;
 
-	spin_lock_irqsave(&priv->lock, flags);
+	priv->hw.ops->lock_irqsave(&priv->lock, &flags);
 	err = __orinoco_down(priv);
 	if (err)
 		printk(KERN_WARNING "%s: Error %d downing interface\n",
@@ -2345,7 +2360,7 @@
 
 	netif_device_detach(dev);
 	priv->hw_unavailable++;
-	spin_unlock_irqrestore(&priv->lock, flags);
+	priv->hw.ops->unlock_irqrestore(&priv->lock, &flags);
 }
 EXPORT_SYMBOL(orinoco_down);
 
diff --git a/drivers/net/wireless/orinoco/orinoco.h b/drivers/net/wireless/orinoco/orinoco.h
index ff6b7b1..e9f415a 100644
--- a/drivers/net/wireless/orinoco/orinoco.h
+++ b/drivers/net/wireless/orinoco/orinoco.h
@@ -190,12 +190,24 @@
 extern int orinoco_init(struct orinoco_private *priv);
 extern int orinoco_if_add(struct orinoco_private *priv,
 			  unsigned long base_addr,
-			  unsigned int irq);
+			  unsigned int irq,
+			  const struct net_device_ops *ops);
 extern void orinoco_if_del(struct orinoco_private *priv);
 extern int orinoco_up(struct orinoco_private *priv);
 extern void orinoco_down(struct orinoco_private *priv);
 extern irqreturn_t orinoco_interrupt(int irq, void *dev_id);
 
+extern void __orinoco_ev_info(struct net_device *dev, hermes_t *hw);
+extern void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw);
+
+/* Common ndo functions exported for reuse by orinoco_usb */
+int orinoco_open(struct net_device *dev);
+int orinoco_stop(struct net_device *dev);
+struct net_device_stats *orinoco_get_stats(struct net_device *dev);
+void orinoco_set_multicast_list(struct net_device *dev);
+int orinoco_change_mtu(struct net_device *dev, int new_mtu);
+void orinoco_tx_timeout(struct net_device *dev);
+
 /********************************************************************/
 /* Locking and synchronization functions                            */
 /********************************************************************/
@@ -203,11 +215,11 @@
 static inline int orinoco_lock(struct orinoco_private *priv,
 			       unsigned long *flags)
 {
-	spin_lock_irqsave(&priv->lock, *flags);
+	priv->hw.ops->lock_irqsave(&priv->lock, flags);
 	if (priv->hw_unavailable) {
 		DEBUG(1, "orinoco_lock() called with hw_unavailable (dev=%p)\n",
 		       priv->ndev);
-		spin_unlock_irqrestore(&priv->lock, *flags);
+		priv->hw.ops->unlock_irqrestore(&priv->lock, flags);
 		return -EBUSY;
 	}
 	return 0;
@@ -216,7 +228,17 @@
 static inline void orinoco_unlock(struct orinoco_private *priv,
 				  unsigned long *flags)
 {
-	spin_unlock_irqrestore(&priv->lock, *flags);
+	priv->hw.ops->unlock_irqrestore(&priv->lock, flags);
+}
+
+static inline void orinoco_lock_irq(struct orinoco_private *priv)
+{
+	priv->hw.ops->lock_irq(&priv->lock);
+}
+
+static inline void orinoco_unlock_irq(struct orinoco_private *priv)
+{
+	priv->hw.ops->unlock_irq(&priv->lock);
 }
 
 /*** Navigate from net_device to orinoco_private ***/
diff --git a/drivers/net/wireless/orinoco/orinoco_cs.c b/drivers/net/wireless/orinoco/orinoco_cs.c
index fdc9613..f99b13b 100644
--- a/drivers/net/wireless/orinoco/orinoco_cs.c
+++ b/drivers/net/wireless/orinoco/orinoco_cs.c
@@ -296,7 +296,7 @@
 
 	/* Register an interface with the stack */
 	if (orinoco_if_add(priv, link->io.BasePort1,
-			   link->irq.AssignedIRQ) != 0) {
+			   link->irq.AssignedIRQ, NULL) != 0) {
 		printk(KERN_ERR PFX "orinoco_if_add() failed\n");
 		goto failed;
 	}
@@ -327,9 +327,9 @@
 
 	/* We're committed to taking the device away now, so mark the
 	 * hardware as unavailable */
-	spin_lock_irqsave(&priv->lock, flags);
+	priv->hw.ops->lock_irqsave(&priv->lock, &flags);
 	priv->hw_unavailable++;
-	spin_unlock_irqrestore(&priv->lock, flags);
+	priv->hw.ops->unlock_irqrestore(&priv->lock, &flags);
 
 	pcmcia_disable_device(link);
 	if (priv->hw.iobase)
diff --git a/drivers/net/wireless/orinoco/orinoco_nortel.c b/drivers/net/wireless/orinoco/orinoco_nortel.c
index 075f446..bc3ea0b 100644
--- a/drivers/net/wireless/orinoco/orinoco_nortel.c
+++ b/drivers/net/wireless/orinoco/orinoco_nortel.c
@@ -220,7 +220,7 @@
 		goto fail;
 	}
 
-	err = orinoco_if_add(priv, 0, 0);
+	err = orinoco_if_add(priv, 0, 0, NULL);
 	if (err) {
 		printk(KERN_ERR PFX "orinoco_if_add() failed\n");
 		goto fail;
diff --git a/drivers/net/wireless/orinoco/orinoco_pci.c b/drivers/net/wireless/orinoco/orinoco_pci.c
index bda5317..468197f 100644
--- a/drivers/net/wireless/orinoco/orinoco_pci.c
+++ b/drivers/net/wireless/orinoco/orinoco_pci.c
@@ -170,7 +170,7 @@
 		goto fail;
 	}
 
-	err = orinoco_if_add(priv, 0, 0);
+	err = orinoco_if_add(priv, 0, 0, NULL);
 	if (err) {
 		printk(KERN_ERR PFX "orinoco_if_add() failed\n");
 		goto fail;
diff --git a/drivers/net/wireless/orinoco/orinoco_plx.c b/drivers/net/wireless/orinoco/orinoco_plx.c
index e0d5874..9358f4d 100644
--- a/drivers/net/wireless/orinoco/orinoco_plx.c
+++ b/drivers/net/wireless/orinoco/orinoco_plx.c
@@ -259,7 +259,7 @@
 		goto fail;
 	}
 
-	err = orinoco_if_add(priv, 0, 0);
+	err = orinoco_if_add(priv, 0, 0, NULL);
 	if (err) {
 		printk(KERN_ERR PFX "orinoco_if_add() failed\n");
 		goto fail;
diff --git a/drivers/net/wireless/orinoco/orinoco_tmd.c b/drivers/net/wireless/orinoco/orinoco_tmd.c
index 88cbc79..784605f 100644
--- a/drivers/net/wireless/orinoco/orinoco_tmd.c
+++ b/drivers/net/wireless/orinoco/orinoco_tmd.c
@@ -156,7 +156,7 @@
 		goto fail;
 	}
 
-	err = orinoco_if_add(priv, 0, 0);
+	err = orinoco_if_add(priv, 0, 0, NULL);
 	if (err) {
 		printk(KERN_ERR PFX "orinoco_if_add() failed\n");
 		goto fail;
diff --git a/drivers/net/wireless/orinoco/orinoco_usb.c b/drivers/net/wireless/orinoco/orinoco_usb.c
new file mode 100644
index 0000000..e220933
--- /dev/null
+++ b/drivers/net/wireless/orinoco/orinoco_usb.c
@@ -0,0 +1,1800 @@
+/*
+ * USB Orinoco driver
+ *
+ * Copyright (c) 2003 Manuel Estrada Sainz
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License
+ * at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above.  If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the MPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL.  If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the MPL or the GPL.
+ *
+ * Queueing code based on linux-wlan-ng 0.2.1-pre5
+ *
+ * Copyright (C) 1999 AbsoluteValue Systems, Inc.  All Rights Reserved.
+ *
+ *	The license is the same as above.
+ *
+ * Initialy based on USB Skeleton driver - 0.7
+ *
+ * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *	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 the Free Software Foundation; either version 2 of
+ *	the License, or (at your option) any later version.
+ *
+ * NOTE: The original USB Skeleton driver is GPL, but all that code is
+ * gone so MPL/GPL applies.
+ */
+
+#define DRIVER_NAME "orinoco_usb"
+#define PFX DRIVER_NAME ": "
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fcntl.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
+#include <linux/usb.h>
+#include <linux/timer.h>
+
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#include <linux/firmware.h>
+
+#include "orinoco.h"
+
+#ifndef URB_ASYNC_UNLINK
+#define URB_ASYNC_UNLINK 0
+#endif
+
+/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */
+static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
+#define ENCAPS_OVERHEAD		(sizeof(encaps_hdr) + 2)
+
+struct header_struct {
+	/* 802.3 */
+	u8 dest[ETH_ALEN];
+	u8 src[ETH_ALEN];
+	__be16 len;
+	/* 802.2 */
+	u8 dsap;
+	u8 ssap;
+	u8 ctrl;
+	/* SNAP */
+	u8 oui[3];
+	__be16 ethertype;
+} __attribute__ ((packed));
+
+struct ez_usb_fw {
+	u16 size;
+	const u8 *code;
+};
+
+static struct ez_usb_fw firmware = {
+	.size = 0,
+	.code = NULL,
+};
+
+#ifdef CONFIG_USB_DEBUG
+static int debug = 1;
+#else
+static int debug;
+#endif
+
+/* Debugging macros */
+#undef dbg
+#define dbg(format, arg...) \
+	do { if (debug) printk(KERN_DEBUG PFX "%s: " format "\n", \
+			       __func__ , ## arg); } while (0)
+#undef err
+#define err(format, arg...) \
+	do { printk(KERN_ERR PFX format "\n", ## arg); } while (0)
+
+/* Module paramaters */
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+MODULE_FIRMWARE("orinoco_ezusb_fw");
+
+/*
+ * Under some conditions, the card gets stuck and stops paying attention
+ * to the world (i.e. data communication stalls) until we do something to
+ * it.  Sending an INQ_TALLIES command seems to be enough and should be
+ * harmless otherwise.  This behaviour has been observed when using the
+ * driver on a systemimager client during installation.  In the past a
+ * timer was used to send INQ_TALLIES commands when there was no other
+ * activity, but it was troublesome and was removed.
+ */
+
+#define USB_COMPAQ_VENDOR_ID     0x049f /* Compaq Computer Corp. */
+#define USB_COMPAQ_WL215_ID      0x001f /* Compaq WL215 USB Adapter */
+#define USB_COMPAQ_W200_ID       0x0076 /* Compaq W200 USB Adapter */
+#define USB_HP_WL215_ID          0x0082 /* Compaq WL215 USB Adapter */
+
+#define USB_MELCO_VENDOR_ID      0x0411
+#define USB_BUFFALO_L11_ID       0x0006 /* BUFFALO WLI-USB-L11 */
+#define USB_BUFFALO_L11G_WR_ID   0x000B /* BUFFALO WLI-USB-L11G-WR */
+#define USB_BUFFALO_L11G_ID      0x000D /* BUFFALO WLI-USB-L11G */
+
+#define USB_LUCENT_VENDOR_ID     0x047E /* Lucent Technologies */
+#define USB_LUCENT_ORINOCO_ID    0x0300 /* Lucent/Agere Orinoco USB Client */
+
+#define USB_AVAYA8_VENDOR_ID     0x0D98
+#define USB_AVAYAE_VENDOR_ID     0x0D9E
+#define USB_AVAYA_WIRELESS_ID    0x0300 /* Avaya Wireless USB Card */
+
+#define USB_AGERE_VENDOR_ID      0x0D4E /* Agere Systems */
+#define USB_AGERE_MODEL0801_ID   0x1000 /* Wireless USB Card Model 0801 */
+#define USB_AGERE_MODEL0802_ID   0x1001 /* Wireless USB Card Model 0802 */
+#define USB_AGERE_REBRANDED_ID   0x047A /* WLAN USB Card */
+
+#define USB_ELSA_VENDOR_ID       0x05CC
+#define USB_ELSA_AIRLANCER_ID    0x3100 /* ELSA AirLancer USB-11 */
+
+#define USB_LEGEND_VENDOR_ID     0x0E7C
+#define USB_LEGEND_JOYNET_ID     0x0300 /* Joynet WLAN USB Card */
+
+#define USB_SAMSUNG_VENDOR_ID    0x04E8
+#define USB_SAMSUNG_SEW2001U1_ID 0x5002 /* Samsung SEW-2001u Card */
+#define USB_SAMSUNG_SEW2001U2_ID 0x5B11 /* Samsung SEW-2001u Card */
+#define USB_SAMSUNG_SEW2003U_ID  0x7011 /* Samsung SEW-2003U Card */
+
+#define USB_IGATE_VENDOR_ID      0x0681
+#define USB_IGATE_IGATE_11M_ID   0x0012 /* I-GATE 11M USB Card */
+
+#define USB_FUJITSU_VENDOR_ID    0x0BF8
+#define USB_FUJITSU_E1100_ID     0x1002 /* connect2AIR WLAN E-1100 USB */
+
+#define USB_2WIRE_VENDOR_ID      0x1630
+#define USB_2WIRE_WIRELESS_ID    0xff81 /* 2Wire Wireless USB adapter */
+
+
+#define EZUSB_REQUEST_FW_TRANS		0xA0
+#define EZUSB_REQUEST_TRIGER		0xAA
+#define EZUSB_REQUEST_TRIG_AC		0xAC
+#define EZUSB_CPUCS_REG			0x7F92
+
+#define EZUSB_RID_TX			0x0700
+#define EZUSB_RID_RX			0x0701
+#define EZUSB_RID_INIT1			0x0702
+#define EZUSB_RID_ACK			0x0710
+#define EZUSB_RID_READ_PDA		0x0800
+#define EZUSB_RID_PROG_INIT		0x0852
+#define EZUSB_RID_PROG_SET_ADDR		0x0853
+#define EZUSB_RID_PROG_BYTES		0x0854
+#define EZUSB_RID_PROG_END		0x0855
+#define EZUSB_RID_DOCMD			0x0860
+
+/* Recognize info frames */
+#define EZUSB_IS_INFO(id)		((id >= 0xF000) && (id <= 0xF2FF))
+
+#define EZUSB_MAGIC			0x0210
+
+#define EZUSB_FRAME_DATA		1
+#define EZUSB_FRAME_CONTROL		2
+
+#define DEF_TIMEOUT			(3*HZ)
+
+#define BULK_BUF_SIZE			2048
+
+#define MAX_DL_SIZE (BULK_BUF_SIZE - sizeof(struct ezusb_packet))
+
+#define FW_BUF_SIZE			64
+#define FW_VAR_OFFSET_PTR		0x359
+#define FW_VAR_VALUE			0
+#define FW_HOLE_START			0x100
+#define FW_HOLE_END			0x300
+
+struct ezusb_packet {
+	__le16 magic;		/* 0x0210 */
+	u8 req_reply_count;
+	u8 ans_reply_count;
+	__le16 frame_type;	/* 0x01 for data frames, 0x02 otherwise */
+	__le16 size;		/* transport size */
+	__le16 crc;		/* CRC up to here */
+	__le16 hermes_len;
+	__le16 hermes_rid;
+	u8 data[0];
+} __attribute__ ((packed));
+
+/* Table of devices that work or may work with this driver */
+static struct usb_device_id ezusb_table[] = {
+	{USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_COMPAQ_WL215_ID)},
+	{USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_HP_WL215_ID)},
+	{USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_COMPAQ_W200_ID)},
+	{USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11_ID)},
+	{USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11G_WR_ID)},
+	{USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11G_ID)},
+	{USB_DEVICE(USB_LUCENT_VENDOR_ID, USB_LUCENT_ORINOCO_ID)},
+	{USB_DEVICE(USB_AVAYA8_VENDOR_ID, USB_AVAYA_WIRELESS_ID)},
+	{USB_DEVICE(USB_AVAYAE_VENDOR_ID, USB_AVAYA_WIRELESS_ID)},
+	{USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_MODEL0801_ID)},
+	{USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_MODEL0802_ID)},
+	{USB_DEVICE(USB_ELSA_VENDOR_ID, USB_ELSA_AIRLANCER_ID)},
+	{USB_DEVICE(USB_LEGEND_VENDOR_ID, USB_LEGEND_JOYNET_ID)},
+	{USB_DEVICE_VER(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2001U1_ID,
+			0, 0)},
+	{USB_DEVICE(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2001U2_ID)},
+	{USB_DEVICE(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2003U_ID)},
+	{USB_DEVICE(USB_IGATE_VENDOR_ID, USB_IGATE_IGATE_11M_ID)},
+	{USB_DEVICE(USB_FUJITSU_VENDOR_ID, USB_FUJITSU_E1100_ID)},
+	{USB_DEVICE(USB_2WIRE_VENDOR_ID, USB_2WIRE_WIRELESS_ID)},
+	{USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_REBRANDED_ID)},
+	{}			/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ezusb_table);
+
+/* Structure to hold all of our device specific stuff */
+struct ezusb_priv {
+	struct usb_device *udev;
+	struct net_device *dev;
+	struct mutex mtx;
+	spinlock_t req_lock;
+	struct list_head req_pending;
+	struct list_head req_active;
+	spinlock_t reply_count_lock;
+	u16 hermes_reg_fake[0x40];
+	u8 *bap_buf;
+	struct urb *read_urb;
+	int read_pipe;
+	int write_pipe;
+	u8 reply_count;
+};
+
+enum ezusb_state {
+	EZUSB_CTX_START,
+	EZUSB_CTX_QUEUED,
+	EZUSB_CTX_REQ_SUBMITTED,
+	EZUSB_CTX_REQ_COMPLETE,
+	EZUSB_CTX_RESP_RECEIVED,
+	EZUSB_CTX_REQ_TIMEOUT,
+	EZUSB_CTX_REQ_FAILED,
+	EZUSB_CTX_RESP_TIMEOUT,
+	EZUSB_CTX_REQSUBMIT_FAIL,
+	EZUSB_CTX_COMPLETE,
+};
+
+struct request_context {
+	struct list_head list;
+	atomic_t refcount;
+	struct completion done;	/* Signals that CTX is dead */
+	int killed;
+	struct urb *outurb;	/* OUT for req pkt */
+	struct ezusb_priv *upriv;
+	struct ezusb_packet *buf;
+	int buf_length;
+	struct timer_list timer;	/* Timeout handling */
+	enum ezusb_state state;	/* Current state */
+	/* the RID that we will wait for */
+	u16 out_rid;
+	u16 in_rid;
+};
+
+
+/* Forward declarations */
+static void ezusb_ctx_complete(struct request_context *ctx);
+static void ezusb_req_queue_run(struct ezusb_priv *upriv);
+static void ezusb_bulk_in_callback(struct urb *urb);
+
+static inline u8 ezusb_reply_inc(u8 count)
+{
+	if (count < 0x7F)
+		return count + 1;
+	else
+		return 1;
+}
+
+static void ezusb_request_context_put(struct request_context *ctx)
+{
+	if (!atomic_dec_and_test(&ctx->refcount))
+		return;
+
+	WARN_ON(!ctx->done.done);
+	BUG_ON(ctx->outurb->status == -EINPROGRESS);
+	BUG_ON(timer_pending(&ctx->timer));
+	usb_free_urb(ctx->outurb);
+	kfree(ctx->buf);
+	kfree(ctx);
+}
+
+static inline void ezusb_mod_timer(struct ezusb_priv *upriv,
+				   struct timer_list *timer,
+				   unsigned long expire)
+{
+	if (!upriv->udev)
+		return;
+	mod_timer(timer, expire);
+}
+
+static void ezusb_request_timerfn(u_long _ctx)
+{
+	struct request_context *ctx = (void *) _ctx;
+
+	ctx->outurb->transfer_flags |= URB_ASYNC_UNLINK;
+	if (usb_unlink_urb(ctx->outurb) == -EINPROGRESS) {
+		ctx->state = EZUSB_CTX_REQ_TIMEOUT;
+	} else {
+		ctx->state = EZUSB_CTX_RESP_TIMEOUT;
+		dbg("couldn't unlink");
+		atomic_inc(&ctx->refcount);
+		ctx->killed = 1;
+		ezusb_ctx_complete(ctx);
+		ezusb_request_context_put(ctx);
+	}
+};
+
+static struct request_context *ezusb_alloc_ctx(struct ezusb_priv *upriv,
+					       u16 out_rid, u16 in_rid)
+{
+	struct request_context *ctx;
+
+	ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC);
+	if (!ctx)
+		return NULL;
+
+	memset(ctx, 0, sizeof(*ctx));
+
+	ctx->buf = kmalloc(BULK_BUF_SIZE, GFP_ATOMIC);
+	if (!ctx->buf) {
+		kfree(ctx);
+		return NULL;
+	}
+	ctx->outurb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!ctx->outurb) {
+		kfree(ctx->buf);
+		kfree(ctx);
+		return NULL;
+	}
+
+	ctx->upriv = upriv;
+	ctx->state = EZUSB_CTX_START;
+	ctx->out_rid = out_rid;
+	ctx->in_rid = in_rid;
+
+	atomic_set(&ctx->refcount, 1);
+	init_completion(&ctx->done);
+
+	init_timer(&ctx->timer);
+	ctx->timer.function = ezusb_request_timerfn;
+	ctx->timer.data = (u_long) ctx;
+	return ctx;
+}
+
+
+/* Hopefully the real complete_all will soon be exported, in the mean
+ * while this should work. */
+static inline void ezusb_complete_all(struct completion *comp)
+{
+	complete(comp);
+	complete(comp);
+	complete(comp);
+	complete(comp);
+}
+
+static void ezusb_ctx_complete(struct request_context *ctx)
+{
+	struct ezusb_priv *upriv = ctx->upriv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&upriv->req_lock, flags);
+
+	list_del_init(&ctx->list);
+	if (upriv->udev) {
+		spin_unlock_irqrestore(&upriv->req_lock, flags);
+		ezusb_req_queue_run(upriv);
+		spin_lock_irqsave(&upriv->req_lock, flags);
+	}
+
+	switch (ctx->state) {
+	case EZUSB_CTX_COMPLETE:
+	case EZUSB_CTX_REQSUBMIT_FAIL:
+	case EZUSB_CTX_REQ_FAILED:
+	case EZUSB_CTX_REQ_TIMEOUT:
+	case EZUSB_CTX_RESP_TIMEOUT:
+		spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+		if ((ctx->out_rid == EZUSB_RID_TX) && upriv->dev) {
+			struct net_device *dev = upriv->dev;
+			struct orinoco_private *priv = ndev_priv(dev);
+			struct net_device_stats *stats = &priv->stats;
+
+			if (ctx->state != EZUSB_CTX_COMPLETE)
+				stats->tx_errors++;
+			else
+				stats->tx_packets++;
+
+			netif_wake_queue(dev);
+		}
+		ezusb_complete_all(&ctx->done);
+		ezusb_request_context_put(ctx);
+		break;
+
+	default:
+		spin_unlock_irqrestore(&upriv->req_lock, flags);
+		if (!upriv->udev) {
+			/* This is normal, as all request contexts get flushed
+			 * when the device is disconnected */
+			err("Called, CTX not terminating, but device gone");
+			ezusb_complete_all(&ctx->done);
+			ezusb_request_context_put(ctx);
+			break;
+		}
+
+		err("Called, CTX not in terminating state.");
+		/* Things are really bad if this happens. Just leak
+		 * the CTX because it may still be linked to the
+		 * queue or the OUT urb may still be active.
+		 * Just leaking at least prevents an Oops or Panic.
+		 */
+		break;
+	}
+}
+
+/**
+ * ezusb_req_queue_run:
+ * Description:
+ *	Note: Only one active CTX at any one time, because there's no
+ *	other (reliable) way to match the response URB to the correct
+ *	CTX.
+ **/
+static void ezusb_req_queue_run(struct ezusb_priv *upriv)
+{
+	unsigned long flags;
+	struct request_context *ctx;
+	int result;
+
+	spin_lock_irqsave(&upriv->req_lock, flags);
+
+	if (!list_empty(&upriv->req_active))
+		goto unlock;
+
+	if (list_empty(&upriv->req_pending))
+		goto unlock;
+
+	ctx =
+	    list_entry(upriv->req_pending.next, struct request_context,
+		       list);
+
+	if (!ctx->upriv->udev)
+		goto unlock;
+
+	/* We need to split this off to avoid a race condition */
+	list_move_tail(&ctx->list, &upriv->req_active);
+
+	if (ctx->state == EZUSB_CTX_QUEUED) {
+		atomic_inc(&ctx->refcount);
+		result = usb_submit_urb(ctx->outurb, GFP_ATOMIC);
+		if (result) {
+			ctx->state = EZUSB_CTX_REQSUBMIT_FAIL;
+
+			spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+			err("Fatal, failed to submit command urb."
+			    " error=%d\n", result);
+
+			ezusb_ctx_complete(ctx);
+			ezusb_request_context_put(ctx);
+			goto done;
+		}
+
+		ctx->state = EZUSB_CTX_REQ_SUBMITTED;
+		ezusb_mod_timer(ctx->upriv, &ctx->timer,
+				jiffies + DEF_TIMEOUT);
+	}
+
+ unlock:
+	spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+ done:
+	return;
+}
+
+static void ezusb_req_enqueue_run(struct ezusb_priv *upriv,
+				  struct request_context *ctx)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&upriv->req_lock, flags);
+
+	if (!ctx->upriv->udev) {
+		spin_unlock_irqrestore(&upriv->req_lock, flags);
+		goto done;
+	}
+	atomic_inc(&ctx->refcount);
+	list_add_tail(&ctx->list, &upriv->req_pending);
+	spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+	ctx->state = EZUSB_CTX_QUEUED;
+	ezusb_req_queue_run(upriv);
+
+ done:
+	return;
+}
+
+static void ezusb_request_out_callback(struct urb *urb)
+{
+	unsigned long flags;
+	enum ezusb_state state;
+	struct request_context *ctx = urb->context;
+	struct ezusb_priv *upriv = ctx->upriv;
+
+	spin_lock_irqsave(&upriv->req_lock, flags);
+
+	del_timer(&ctx->timer);
+
+	if (ctx->killed) {
+		spin_unlock_irqrestore(&upriv->req_lock, flags);
+		pr_warning("interrupt called with dead ctx");
+		goto out;
+	}
+
+	state = ctx->state;
+
+	if (urb->status == 0) {
+		switch (state) {
+		case EZUSB_CTX_REQ_SUBMITTED:
+			if (ctx->in_rid) {
+				ctx->state = EZUSB_CTX_REQ_COMPLETE;
+				/* reply URB still pending */
+				ezusb_mod_timer(upriv, &ctx->timer,
+						jiffies + DEF_TIMEOUT);
+				spin_unlock_irqrestore(&upriv->req_lock,
+						       flags);
+				break;
+			}
+			/* fall through */
+		case EZUSB_CTX_RESP_RECEIVED:
+			/* IN already received before this OUT-ACK */
+			ctx->state = EZUSB_CTX_COMPLETE;
+			spin_unlock_irqrestore(&upriv->req_lock, flags);
+			ezusb_ctx_complete(ctx);
+			break;
+
+		default:
+			spin_unlock_irqrestore(&upriv->req_lock, flags);
+			err("Unexpected state(0x%x, %d) in OUT URB",
+			    state, urb->status);
+			break;
+		}
+	} else {
+		/* If someone cancels the OUT URB then its status
+		 * should be either -ECONNRESET or -ENOENT.
+		 */
+		switch (state) {
+		case EZUSB_CTX_REQ_SUBMITTED:
+		case EZUSB_CTX_RESP_RECEIVED:
+			ctx->state = EZUSB_CTX_REQ_FAILED;
+			/* fall through */
+
+		case EZUSB_CTX_REQ_FAILED:
+		case EZUSB_CTX_REQ_TIMEOUT:
+			spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+			ezusb_ctx_complete(ctx);
+			break;
+
+		default:
+			spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+			err("Unexpected state(0x%x, %d) in OUT URB",
+			    state, urb->status);
+			break;
+		}
+	}
+ out:
+	ezusb_request_context_put(ctx);
+}
+
+static void ezusb_request_in_callback(struct ezusb_priv *upriv,
+				      struct urb *urb)
+{
+	struct ezusb_packet *ans = urb->transfer_buffer;
+	struct request_context *ctx = NULL;
+	enum ezusb_state state;
+	unsigned long flags;
+
+	/* Find the CTX on the active queue that requested this URB */
+	spin_lock_irqsave(&upriv->req_lock, flags);
+	if (upriv->udev) {
+		struct list_head *item;
+
+		list_for_each(item, &upriv->req_active) {
+			struct request_context *c;
+			int reply_count;
+
+			c = list_entry(item, struct request_context, list);
+			reply_count =
+			    ezusb_reply_inc(c->buf->req_reply_count);
+			if ((ans->ans_reply_count == reply_count)
+			    && (le16_to_cpu(ans->hermes_rid) == c->in_rid)) {
+				ctx = c;
+				break;
+			}
+			dbg("Skipped (0x%x/0x%x) (%d/%d)",
+			    le16_to_cpu(ans->hermes_rid),
+			    c->in_rid, ans->ans_reply_count, reply_count);
+		}
+	}
+
+	if (ctx == NULL) {
+		spin_unlock_irqrestore(&upriv->req_lock, flags);
+		err("%s: got unexpected RID: 0x%04X", __func__,
+		    le16_to_cpu(ans->hermes_rid));
+		ezusb_req_queue_run(upriv);
+		return;
+	}
+
+	/* The data we want is in the in buffer, exchange */
+	urb->transfer_buffer = ctx->buf;
+	ctx->buf = (void *) ans;
+	ctx->buf_length = urb->actual_length;
+
+	state = ctx->state;
+	switch (state) {
+	case EZUSB_CTX_REQ_SUBMITTED:
+		/* We have received our response URB before
+		 * our request has been acknowledged. Do NOT
+		 * destroy our CTX yet, because our OUT URB
+		 * is still alive ...
+		 */
+		ctx->state = EZUSB_CTX_RESP_RECEIVED;
+		spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+		/* Let the machine continue running. */
+		break;
+
+	case EZUSB_CTX_REQ_COMPLETE:
+		/* This is the usual path: our request
+		 * has already been acknowledged, and
+		 * we have now received the reply.
+		 */
+		ctx->state = EZUSB_CTX_COMPLETE;
+
+		/* Stop the intimer */
+		del_timer(&ctx->timer);
+		spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+		/* Call the completion handler */
+		ezusb_ctx_complete(ctx);
+		break;
+
+	default:
+		spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+		pr_warning("Matched IN URB, unexpected context state(0x%x)",
+		     state);
+		/* Throw this CTX away and try submitting another */
+		del_timer(&ctx->timer);
+		ctx->outurb->transfer_flags |= URB_ASYNC_UNLINK;
+		usb_unlink_urb(ctx->outurb);
+		ezusb_req_queue_run(upriv);
+		break;
+	}			/* switch */
+}
+
+
+static void ezusb_req_ctx_wait(struct ezusb_priv *upriv,
+			       struct request_context *ctx)
+{
+	switch (ctx->state) {
+	case EZUSB_CTX_QUEUED:
+	case EZUSB_CTX_REQ_SUBMITTED:
+	case EZUSB_CTX_REQ_COMPLETE:
+	case EZUSB_CTX_RESP_RECEIVED:
+		if (in_softirq()) {
+			/* If we get called from a timer, timeout timers don't
+			 * get the chance to run themselves. So we make sure
+			 * that we don't sleep for ever */
+			int msecs = DEF_TIMEOUT * (1000 / HZ);
+			while (!ctx->done.done && msecs--)
+				udelay(1000);
+		} else {
+			wait_event_interruptible(ctx->done.wait,
+						 ctx->done.done);
+		}
+		break;
+	default:
+		/* Done or failed - nothing to wait for */
+		break;
+	}
+}
+
+static inline u16 build_crc(struct ezusb_packet *data)
+{
+	u16 crc = 0;
+	u8 *bytes = (u8 *)data;
+	int i;
+
+	for (i = 0; i < 8; i++)
+		crc = (crc << 1) + bytes[i];
+
+	return crc;
+}
+
+/**
+ * ezusb_fill_req:
+ *
+ * if data == NULL and length > 0 the data is assumed to be already in
+ * the target buffer and only the header is filled.
+ *
+ */
+static int ezusb_fill_req(struct ezusb_packet *req, u16 length, u16 rid,
+			  const void *data, u16 frame_type, u8 reply_count)
+{
+	int total_size = sizeof(*req) + length;
+
+	BUG_ON(total_size > BULK_BUF_SIZE);
+
+	req->magic = cpu_to_le16(EZUSB_MAGIC);
+	req->req_reply_count = reply_count;
+	req->ans_reply_count = 0;
+	req->frame_type = cpu_to_le16(frame_type);
+	req->size = cpu_to_le16(length + 4);
+	req->crc = cpu_to_le16(build_crc(req));
+	req->hermes_len = cpu_to_le16(HERMES_BYTES_TO_RECLEN(length));
+	req->hermes_rid = cpu_to_le16(rid);
+	if (data)
+		memcpy(req->data, data, length);
+	return total_size;
+}
+
+static int ezusb_submit_in_urb(struct ezusb_priv *upriv)
+{
+	int retval = 0;
+	void *cur_buf = upriv->read_urb->transfer_buffer;
+
+	if (upriv->read_urb->status == -EINPROGRESS) {
+		dbg("urb busy, not resubmiting");
+		retval = -EBUSY;
+		goto exit;
+	}
+	usb_fill_bulk_urb(upriv->read_urb, upriv->udev, upriv->read_pipe,
+			  cur_buf, BULK_BUF_SIZE,
+			  ezusb_bulk_in_callback, upriv);
+	upriv->read_urb->transfer_flags = 0;
+	retval = usb_submit_urb(upriv->read_urb, GFP_ATOMIC);
+	if (retval)
+		err("%s submit failed %d", __func__, retval);
+
+ exit:
+	return retval;
+}
+
+static inline int ezusb_8051_cpucs(struct ezusb_priv *upriv, int reset)
+{
+	u8 res_val = reset;	/* avoid argument promotion */
+
+	if (!upriv->udev) {
+		err("%s: !upriv->udev", __func__);
+		return -EFAULT;
+	}
+	return usb_control_msg(upriv->udev,
+			       usb_sndctrlpipe(upriv->udev, 0),
+			       EZUSB_REQUEST_FW_TRANS,
+			       USB_TYPE_VENDOR | USB_RECIP_DEVICE |
+			       USB_DIR_OUT, EZUSB_CPUCS_REG, 0, &res_val,
+			       sizeof(res_val), DEF_TIMEOUT);
+}
+
+static int ezusb_firmware_download(struct ezusb_priv *upriv,
+				   struct ez_usb_fw *fw)
+{
+	u8 fw_buffer[FW_BUF_SIZE];
+	int retval, addr;
+	int variant_offset;
+
+	/*
+	 * This byte is 1 and should be replaced with 0.  The offset is
+	 * 0x10AD in version 0.0.6.  The byte in question should follow
+	 * the end of the code pointed to by the jump in the beginning
+	 * of the firmware.  Also, it is read by code located at 0x358.
+	 */
+	variant_offset = be16_to_cpup((__be16 *) &fw->code[FW_VAR_OFFSET_PTR]);
+	if (variant_offset >= fw->size) {
+		printk(KERN_ERR PFX "Invalid firmware variant offset: "
+		       "0x%04x\n", variant_offset);
+		retval = -EINVAL;
+		goto fail;
+	}
+
+	retval = ezusb_8051_cpucs(upriv, 1);
+	if (retval < 0)
+		goto fail;
+	for (addr = 0; addr < fw->size; addr += FW_BUF_SIZE) {
+		/* 0x100-0x300 should be left alone, it contains card
+		 * specific data, like USB enumeration information */
+		if ((addr >= FW_HOLE_START) && (addr < FW_HOLE_END))
+			continue;
+
+		memcpy(fw_buffer, &fw->code[addr], FW_BUF_SIZE);
+		if (variant_offset >= addr &&
+		    variant_offset < addr + FW_BUF_SIZE) {
+			dbg("Patching card_variant byte at 0x%04X",
+			    variant_offset);
+			fw_buffer[variant_offset - addr] = FW_VAR_VALUE;
+		}
+		retval = usb_control_msg(upriv->udev,
+					 usb_sndctrlpipe(upriv->udev, 0),
+					 EZUSB_REQUEST_FW_TRANS,
+					 USB_TYPE_VENDOR | USB_RECIP_DEVICE
+					 | USB_DIR_OUT,
+					 addr, 0x0,
+					 fw_buffer, FW_BUF_SIZE,
+					 DEF_TIMEOUT);
+
+		if (retval < 0)
+			goto fail;
+	}
+	retval = ezusb_8051_cpucs(upriv, 0);
+	if (retval < 0)
+		goto fail;
+
+	goto exit;
+ fail:
+	printk(KERN_ERR PFX "Firmware download failed, error %d\n",
+	       retval);
+ exit:
+	return retval;
+}
+
+static int ezusb_access_ltv(struct ezusb_priv *upriv,
+			    struct request_context *ctx,
+			    u16 length, const void *data, u16 frame_type,
+			    void *ans_buff, int ans_size, u16 *ans_length)
+{
+	int req_size;
+	int retval = 0;
+	enum ezusb_state state;
+
+	BUG_ON(in_irq());
+
+	if (!upriv->udev) {
+		dbg("Device disconnected");
+		return -ENODEV;
+	}
+
+	if (upriv->read_urb->status != -EINPROGRESS)
+		err("%s: in urb not pending", __func__);
+
+	/* protect upriv->reply_count, guarantee sequential numbers */
+	spin_lock_bh(&upriv->reply_count_lock);
+	req_size = ezusb_fill_req(ctx->buf, length, ctx->out_rid, data,
+				  frame_type, upriv->reply_count);
+	usb_fill_bulk_urb(ctx->outurb, upriv->udev, upriv->write_pipe,
+			  ctx->buf, req_size,
+			  ezusb_request_out_callback, ctx);
+
+	if (ctx->in_rid)
+		upriv->reply_count = ezusb_reply_inc(upriv->reply_count);
+
+	ezusb_req_enqueue_run(upriv, ctx);
+
+	spin_unlock_bh(&upriv->reply_count_lock);
+
+	if (ctx->in_rid)
+		ezusb_req_ctx_wait(upriv, ctx);
+
+	state = ctx->state;
+	switch (state) {
+	case EZUSB_CTX_COMPLETE:
+		retval = ctx->outurb->status;
+		break;
+
+	case EZUSB_CTX_QUEUED:
+	case EZUSB_CTX_REQ_SUBMITTED:
+		if (!ctx->in_rid)
+			break;
+	default:
+		err("%s: Unexpected context state %d", __func__,
+		    state);
+		/* fall though */
+	case EZUSB_CTX_REQ_TIMEOUT:
+	case EZUSB_CTX_REQ_FAILED:
+	case EZUSB_CTX_RESP_TIMEOUT:
+	case EZUSB_CTX_REQSUBMIT_FAIL:
+		printk(KERN_ERR PFX "Access failed, resetting (state %d,"
+		       " reply_count %d)\n", state, upriv->reply_count);
+		upriv->reply_count = 0;
+		if (state == EZUSB_CTX_REQ_TIMEOUT
+		    || state == EZUSB_CTX_RESP_TIMEOUT) {
+			printk(KERN_ERR PFX "ctx timed out\n");
+			retval = -ETIMEDOUT;
+		} else {
+			printk(KERN_ERR PFX "ctx failed\n");
+			retval = -EFAULT;
+		}
+		goto exit;
+		break;
+	}
+	if (ctx->in_rid) {
+		struct ezusb_packet *ans = ctx->buf;
+		int exp_len;
+
+		if (ans->hermes_len != 0)
+			exp_len = le16_to_cpu(ans->hermes_len) * 2 + 12;
+		else
+			exp_len = 14;
+
+		if (exp_len != ctx->buf_length) {
+			err("%s: length mismatch for RID 0x%04x: "
+			    "expected %d, got %d", __func__,
+			    ctx->in_rid, exp_len, ctx->buf_length);
+			retval = -EIO;
+			goto exit;
+		}
+
+		if (ans_buff)
+			memcpy(ans_buff, ans->data,
+			       min_t(int, exp_len, ans_size));
+		if (ans_length)
+			*ans_length = le16_to_cpu(ans->hermes_len);
+	}
+ exit:
+	ezusb_request_context_put(ctx);
+	return retval;
+}
+
+static int ezusb_write_ltv(hermes_t *hw, int bap, u16 rid,
+			   u16 length, const void *data)
+{
+	struct ezusb_priv *upriv = hw->priv;
+	u16 frame_type;
+	struct request_context *ctx;
+
+	if (length == 0)
+		return -EINVAL;
+
+	length = HERMES_RECLEN_TO_BYTES(length);
+
+	/* On memory mapped devices HERMES_RID_CNFGROUPADDRESSES can be
+	 * set to be empty, but the USB bridge doesn't like it */
+	if (length == 0)
+		return 0;
+
+	ctx = ezusb_alloc_ctx(upriv, rid, EZUSB_RID_ACK);
+	if (!ctx)
+		return -ENOMEM;
+
+	if (rid == EZUSB_RID_TX)
+		frame_type = EZUSB_FRAME_DATA;
+	else
+		frame_type = EZUSB_FRAME_CONTROL;
+
+	return ezusb_access_ltv(upriv, ctx, length, data, frame_type,
+				NULL, 0, NULL);
+}
+
+static int ezusb_read_ltv(hermes_t *hw, int bap, u16 rid,
+			  unsigned bufsize, u16 *length, void *buf)
+{
+	struct ezusb_priv *upriv = hw->priv;
+	struct request_context *ctx;
+
+	if ((bufsize < 0) || (bufsize % 2))
+		return -EINVAL;
+
+	ctx = ezusb_alloc_ctx(upriv, rid, rid);
+	if (!ctx)
+		return -ENOMEM;
+
+	return ezusb_access_ltv(upriv, ctx, 0, NULL, EZUSB_FRAME_CONTROL,
+				buf, bufsize, length);
+}
+
+static int ezusb_doicmd_wait(hermes_t *hw, u16 cmd, u16 parm0, u16 parm1,
+			     u16 parm2, struct hermes_response *resp)
+{
+	struct ezusb_priv *upriv = hw->priv;
+	struct request_context *ctx;
+
+	__le16 data[4] = {
+		cpu_to_le16(cmd),
+		cpu_to_le16(parm0),
+		cpu_to_le16(parm1),
+		cpu_to_le16(parm2),
+	};
+	dbg("0x%04X, parm0 0x%04X, parm1 0x%04X, parm2 0x%04X",
+	    cmd, parm0, parm1, parm2);
+	ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_DOCMD, EZUSB_RID_ACK);
+	if (!ctx)
+		return -ENOMEM;
+
+	return ezusb_access_ltv(upriv, ctx, sizeof(data), &data,
+				EZUSB_FRAME_CONTROL, NULL, 0, NULL);
+}
+
+static int ezusb_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0,
+			    struct hermes_response *resp)
+{
+	struct ezusb_priv *upriv = hw->priv;
+	struct request_context *ctx;
+
+	__le16 data[4] = {
+		cpu_to_le16(cmd),
+		cpu_to_le16(parm0),
+		0,
+		0,
+	};
+	dbg("0x%04X, parm0 0x%04X", cmd, parm0);
+	ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_DOCMD, EZUSB_RID_ACK);
+	if (!ctx)
+		return -ENOMEM;
+
+	return ezusb_access_ltv(upriv, ctx, sizeof(data), &data,
+				EZUSB_FRAME_CONTROL, NULL, 0, NULL);
+}
+
+static int ezusb_bap_pread(struct hermes *hw, int bap,
+			   void *buf, int len, u16 id, u16 offset)
+{
+	struct ezusb_priv *upriv = hw->priv;
+	struct ezusb_packet *ans = (void *) upriv->read_urb->transfer_buffer;
+	int actual_length = upriv->read_urb->actual_length;
+
+	if (id == EZUSB_RID_RX) {
+		if ((sizeof(*ans) + offset + len) > actual_length) {
+			printk(KERN_ERR PFX "BAP read beyond buffer end "
+			       "in rx frame\n");
+			return -EINVAL;
+		}
+		memcpy(buf, ans->data + offset, len);
+		return 0;
+	}
+
+	if (EZUSB_IS_INFO(id)) {
+		/* Include 4 bytes for length/type */
+		if ((sizeof(*ans) + offset + len - 4) > actual_length) {
+			printk(KERN_ERR PFX "BAP read beyond buffer end "
+			       "in info frame\n");
+			return -EFAULT;
+		}
+		memcpy(buf, ans->data + offset - 4, len);
+	} else {
+		printk(KERN_ERR PFX "Unexpected fid 0x%04x\n", id);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ezusb_read_pda(struct hermes *hw, __le16 *pda,
+			  u32 pda_addr, u16 pda_len)
+{
+	struct ezusb_priv *upriv = hw->priv;
+	struct request_context *ctx;
+	__le16 data[] = {
+		cpu_to_le16(pda_addr & 0xffff),
+		cpu_to_le16(pda_len - 4)
+	};
+	ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_READ_PDA, EZUSB_RID_READ_PDA);
+	if (!ctx)
+		return -ENOMEM;
+
+	/* wl_lkm does not include PDA size in the PDA area.
+	 * We will pad the information into pda, so other routines
+	 * don't have to be modified */
+	pda[0] = cpu_to_le16(pda_len - 2);
+	/* Includes CFG_PROD_DATA but not itself */
+	pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */
+
+	return ezusb_access_ltv(upriv, ctx, sizeof(data), &data,
+				EZUSB_FRAME_CONTROL, &pda[2], pda_len - 4,
+				NULL);
+}
+
+static int ezusb_program_init(struct hermes *hw, u32 entry_point)
+{
+	struct ezusb_priv *upriv = hw->priv;
+	struct request_context *ctx;
+	__le32 data = cpu_to_le32(entry_point);
+
+	ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_INIT, EZUSB_RID_ACK);
+	if (!ctx)
+		return -ENOMEM;
+
+	return ezusb_access_ltv(upriv, ctx, sizeof(data), &data,
+				EZUSB_FRAME_CONTROL, NULL, 0, NULL);
+}
+
+static int ezusb_program_end(struct hermes *hw)
+{
+	struct ezusb_priv *upriv = hw->priv;
+	struct request_context *ctx;
+
+	ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_END, EZUSB_RID_ACK);
+	if (!ctx)
+		return -ENOMEM;
+
+	return ezusb_access_ltv(upriv, ctx, 0, NULL,
+				EZUSB_FRAME_CONTROL, NULL, 0, NULL);
+}
+
+static int ezusb_program_bytes(struct hermes *hw, const char *buf,
+			       u32 addr, u32 len)
+{
+	struct ezusb_priv *upriv = hw->priv;
+	struct request_context *ctx;
+	__le32 data = cpu_to_le32(addr);
+	int err;
+
+	ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_SET_ADDR, EZUSB_RID_ACK);
+	if (!ctx)
+		return -ENOMEM;
+
+	err = ezusb_access_ltv(upriv, ctx, sizeof(data), &data,
+			       EZUSB_FRAME_CONTROL, NULL, 0, NULL);
+	if (err)
+		return err;
+
+	ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_BYTES, EZUSB_RID_ACK);
+	if (!ctx)
+		return -ENOMEM;
+
+	return ezusb_access_ltv(upriv, ctx, len, buf,
+				EZUSB_FRAME_CONTROL, NULL, 0, NULL);
+}
+
+static int ezusb_program(struct hermes *hw, const char *buf,
+			 u32 addr, u32 len)
+{
+	u32 ch_addr;
+	u32 ch_len;
+	int err = 0;
+
+	/* We can only send 2048 bytes out of the bulk xmit at a time,
+	 * so we have to split any programming into chunks of <2048
+	 * bytes. */
+
+	ch_len = (len < MAX_DL_SIZE) ? len : MAX_DL_SIZE;
+	ch_addr = addr;
+
+	while (ch_addr < (addr + len)) {
+		pr_debug("Programming subblock of length %d "
+			 "to address 0x%08x. Data @ %p\n",
+			 ch_len, ch_addr, &buf[ch_addr - addr]);
+
+		err = ezusb_program_bytes(hw, &buf[ch_addr - addr],
+					  ch_addr, ch_len);
+		if (err)
+			break;
+
+		ch_addr += ch_len;
+		ch_len = ((addr + len - ch_addr) < MAX_DL_SIZE) ?
+			(addr + len - ch_addr) : MAX_DL_SIZE;
+	}
+
+	return err;
+}
+
+static netdev_tx_t ezusb_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct orinoco_private *priv = ndev_priv(dev);
+	struct net_device_stats *stats = &priv->stats;
+	struct ezusb_priv *upriv = priv->card;
+	int err = 0;
+	char *p;
+	struct ethhdr *eh;
+	int len, data_len, data_off;
+	__le16 tx_control;
+	unsigned long flags;
+	struct request_context *ctx;
+	u8 *buf;
+	int tx_size;
+
+	if (!netif_running(dev)) {
+		printk(KERN_ERR "%s: Tx on stopped device!\n",
+		       dev->name);
+		return NETDEV_TX_BUSY;
+	}
+
+	if (netif_queue_stopped(dev)) {
+		printk(KERN_DEBUG "%s: Tx while transmitter busy!\n",
+		       dev->name);
+		return NETDEV_TX_BUSY;
+	}
+
+	if (orinoco_lock(priv, &flags) != 0) {
+		printk(KERN_ERR
+		       "%s: orinoco_xmit() called while hw_unavailable\n",
+		       dev->name);
+		return NETDEV_TX_BUSY;
+	}
+
+	if (!netif_carrier_ok(dev) ||
+	    (priv->iw_mode == NL80211_IFTYPE_MONITOR)) {
+		/* Oops, the firmware hasn't established a connection,
+		   silently drop the packet (this seems to be the
+		   safest approach). */
+		stats->tx_errors++;
+		orinoco_unlock(priv, &flags);
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+
+	ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_TX, 0);
+	if (!ctx)
+		goto fail;
+
+	memset(ctx->buf, 0, BULK_BUF_SIZE);
+	buf = ctx->buf->data;
+
+	/* Length of the packet body */
+	/* FIXME: what if the skb is smaller than this? */
+	len = max_t(int, skb->len - ETH_HLEN, ETH_ZLEN - ETH_HLEN);
+
+	eh = (struct ethhdr *) skb->data;
+
+	tx_control = cpu_to_le16(0);
+	memcpy(buf, &tx_control, sizeof(tx_control));
+	buf += sizeof(tx_control);
+	/* Encapsulate Ethernet-II frames */
+	if (ntohs(eh->h_proto) > ETH_DATA_LEN) {	/* Ethernet-II frame */
+		struct header_struct *hdr = (void *) buf;
+		buf += sizeof(*hdr);
+		data_len = len;
+		data_off = sizeof(tx_control) + sizeof(*hdr);
+		p = skb->data + ETH_HLEN;
+
+		/* 802.3 header */
+		memcpy(hdr->dest, eh->h_dest, ETH_ALEN);
+		memcpy(hdr->src, eh->h_source, ETH_ALEN);
+		hdr->len = htons(data_len + ENCAPS_OVERHEAD);
+
+		/* 802.2 header */
+		memcpy(&hdr->dsap, &encaps_hdr, sizeof(encaps_hdr));
+
+		hdr->ethertype = eh->h_proto;
+	} else {		/* IEEE 802.3 frame */
+		data_len = len + ETH_HLEN;
+		data_off = sizeof(tx_control);
+		p = skb->data;
+	}
+
+	memcpy(buf, p, data_len);
+	buf += data_len;
+
+	/* Finally, we actually initiate the send */
+	netif_stop_queue(dev);
+
+	/* The card may behave better if we send evenly sized usb transfers */
+	tx_size = ALIGN(buf - ctx->buf->data, 2);
+
+	err = ezusb_access_ltv(upriv, ctx, tx_size, NULL,
+			       EZUSB_FRAME_DATA, NULL, 0, NULL);
+
+	if (err) {
+		netif_start_queue(dev);
+		if (net_ratelimit())
+			printk(KERN_ERR "%s: Error %d transmitting packet\n",
+				dev->name, err);
+		stats->tx_errors++;
+		goto fail;
+	}
+
+	dev->trans_start = jiffies;
+	stats->tx_bytes += data_off + data_len;
+
+	orinoco_unlock(priv, &flags);
+
+	dev_kfree_skb(skb);
+
+	return NETDEV_TX_OK;
+
+ fail:
+	orinoco_unlock(priv, &flags);
+	return NETDEV_TX_BUSY;
+}
+
+static int ezusb_allocate(struct hermes *hw, u16 size, u16 *fid)
+{
+	*fid = EZUSB_RID_TX;
+	return 0;
+}
+
+
+static int ezusb_hard_reset(struct orinoco_private *priv)
+{
+	struct ezusb_priv *upriv = priv->card;
+	int retval = ezusb_8051_cpucs(upriv, 1);
+
+	if (retval < 0) {
+		err("Failed to reset");
+		return retval;
+	}
+
+	retval = ezusb_8051_cpucs(upriv, 0);
+	if (retval < 0) {
+		err("Failed to unreset");
+		return retval;
+	}
+
+	dbg("sending control message");
+	retval = usb_control_msg(upriv->udev,
+				 usb_sndctrlpipe(upriv->udev, 0),
+				 EZUSB_REQUEST_TRIGER,
+				 USB_TYPE_VENDOR | USB_RECIP_DEVICE |
+				 USB_DIR_OUT, 0x0, 0x0, NULL, 0,
+				 DEF_TIMEOUT);
+	if (retval < 0) {
+		err("EZUSB_REQUEST_TRIGER failed retval %d", retval);
+		return retval;
+	}
+#if 0
+	dbg("Sending EZUSB_REQUEST_TRIG_AC");
+	retval = usb_control_msg(upriv->udev,
+				 usb_sndctrlpipe(upriv->udev, 0),
+				 EZUSB_REQUEST_TRIG_AC,
+				 USB_TYPE_VENDOR | USB_RECIP_DEVICE |
+				 USB_DIR_OUT, 0x00FA, 0x0, NULL, 0,
+				 DEF_TIMEOUT);
+	if (retval < 0) {
+		err("EZUSB_REQUEST_TRIG_AC failed retval %d", retval);
+		return retval;
+	}
+#endif
+
+	return 0;
+}
+
+
+static int ezusb_init(hermes_t *hw)
+{
+	struct ezusb_priv *upriv = hw->priv;
+	int retval;
+
+	BUG_ON(in_interrupt());
+	BUG_ON(!upriv);
+
+	upriv->reply_count = 0;
+	/* Write the MAGIC number on the simulated registers to keep
+	 * orinoco.c happy */
+	hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC);
+	hermes_write_regn(hw, RXFID, EZUSB_RID_RX);
+
+	usb_kill_urb(upriv->read_urb);
+	ezusb_submit_in_urb(upriv);
+
+	retval = ezusb_write_ltv(hw, 0, EZUSB_RID_INIT1,
+				 HERMES_BYTES_TO_RECLEN(2), "\x10\x00");
+	if (retval < 0) {
+		printk(KERN_ERR PFX "EZUSB_RID_INIT1 error %d\n", retval);
+		return retval;
+	}
+
+	retval = ezusb_docmd_wait(hw, HERMES_CMD_INIT, 0, NULL);
+	if (retval < 0) {
+		printk(KERN_ERR PFX "HERMES_CMD_INIT error %d\n", retval);
+		return retval;
+	}
+
+	return 0;
+}
+
+static void ezusb_bulk_in_callback(struct urb *urb)
+{
+	struct ezusb_priv *upriv = (struct ezusb_priv *) urb->context;
+	struct ezusb_packet *ans = urb->transfer_buffer;
+	u16 crc;
+	u16 hermes_rid;
+
+	if (upriv->udev == NULL) {
+		dbg("disconnected");
+		return;
+	}
+
+	if (urb->status == -ETIMEDOUT) {
+		/* When a device gets unplugged we get this every time
+		 * we resubmit, flooding the logs.  Since we don't use
+		 * USB timeouts, it shouldn't happen any other time*/
+		pr_warning("%s: urb timed out, not resubmiting", __func__);
+		return;
+	}
+	if (urb->status == -ECONNABORTED) {
+		pr_warning("%s: connection abort, resubmiting urb",
+		     __func__);
+		goto resubmit;
+	}
+	if ((urb->status == -EILSEQ)
+	    || (urb->status == -ENOENT)
+	    || (urb->status == -ECONNRESET)) {
+		dbg("status %d, not resubmiting", urb->status);
+		return;
+	}
+	if (urb->status)
+		dbg("status: %d length: %d",
+		    urb->status, urb->actual_length);
+	if (urb->actual_length < sizeof(*ans)) {
+		err("%s: short read, ignoring", __func__);
+		goto resubmit;
+	}
+	crc = build_crc(ans);
+	if (le16_to_cpu(ans->crc) != crc) {
+		err("CRC error, ignoring packet");
+		goto resubmit;
+	}
+
+	hermes_rid = le16_to_cpu(ans->hermes_rid);
+	if ((hermes_rid != EZUSB_RID_RX) && !EZUSB_IS_INFO(hermes_rid)) {
+		ezusb_request_in_callback(upriv, urb);
+	} else if (upriv->dev) {
+		struct net_device *dev = upriv->dev;
+		struct orinoco_private *priv = ndev_priv(dev);
+		hermes_t *hw = &priv->hw;
+
+		if (hermes_rid == EZUSB_RID_RX) {
+			__orinoco_ev_rx(dev, hw);
+		} else {
+			hermes_write_regn(hw, INFOFID,
+					  le16_to_cpu(ans->hermes_rid));
+			__orinoco_ev_info(dev, hw);
+		}
+	}
+
+ resubmit:
+	if (upriv->udev)
+		ezusb_submit_in_urb(upriv);
+}
+
+static inline void ezusb_delete(struct ezusb_priv *upriv)
+{
+	struct net_device *dev;
+	struct list_head *item;
+	struct list_head *tmp_item;
+	unsigned long flags;
+
+	BUG_ON(in_interrupt());
+	BUG_ON(!upriv);
+
+	dev = upriv->dev;
+	mutex_lock(&upriv->mtx);
+
+	upriv->udev = NULL;	/* No timer will be rearmed from here */
+
+	usb_kill_urb(upriv->read_urb);
+
+	spin_lock_irqsave(&upriv->req_lock, flags);
+	list_for_each_safe(item, tmp_item, &upriv->req_active) {
+		struct request_context *ctx;
+		int err;
+
+		ctx = list_entry(item, struct request_context, list);
+		atomic_inc(&ctx->refcount);
+
+		ctx->outurb->transfer_flags |= URB_ASYNC_UNLINK;
+		err = usb_unlink_urb(ctx->outurb);
+
+		spin_unlock_irqrestore(&upriv->req_lock, flags);
+		if (err == -EINPROGRESS)
+			wait_for_completion(&ctx->done);
+
+		del_timer_sync(&ctx->timer);
+		/* FIXME: there is an slight chance for the irq handler to
+		 * be running */
+		if (!list_empty(&ctx->list))
+			ezusb_ctx_complete(ctx);
+
+		ezusb_request_context_put(ctx);
+		spin_lock_irqsave(&upriv->req_lock, flags);
+	}
+	spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+	list_for_each_safe(item, tmp_item, &upriv->req_pending)
+	    ezusb_ctx_complete(list_entry(item,
+					  struct request_context, list));
+
+	if (upriv->read_urb->status == -EINPROGRESS)
+		printk(KERN_ERR PFX "Some URB in progress\n");
+
+	mutex_unlock(&upriv->mtx);
+
+	kfree(upriv->read_urb->transfer_buffer);
+	if (upriv->bap_buf != NULL)
+		kfree(upriv->bap_buf);
+	if (upriv->read_urb != NULL)
+		usb_free_urb(upriv->read_urb);
+	if (upriv->dev) {
+		struct orinoco_private *priv = ndev_priv(upriv->dev);
+		orinoco_if_del(priv);
+		free_orinocodev(priv);
+	}
+}
+
+static void ezusb_lock_irqsave(spinlock_t *lock,
+			       unsigned long *flags) __acquires(lock)
+{
+	spin_lock_bh(lock);
+}
+
+static void ezusb_unlock_irqrestore(spinlock_t *lock,
+				    unsigned long *flags) __releases(lock)
+{
+	spin_unlock_bh(lock);
+}
+
+static void ezusb_lock_irq(spinlock_t *lock) __acquires(lock)
+{
+	spin_lock_bh(lock);
+}
+
+static void ezusb_unlock_irq(spinlock_t *lock) __releases(lock)
+{
+	spin_unlock_bh(lock);
+}
+
+static const struct hermes_ops ezusb_ops = {
+	.init = ezusb_init,
+	.cmd_wait = ezusb_docmd_wait,
+	.init_cmd_wait = ezusb_doicmd_wait,
+	.allocate = ezusb_allocate,
+	.read_ltv = ezusb_read_ltv,
+	.write_ltv = ezusb_write_ltv,
+	.bap_pread = ezusb_bap_pread,
+	.read_pda = ezusb_read_pda,
+	.program_init = ezusb_program_init,
+	.program_end = ezusb_program_end,
+	.program = ezusb_program,
+	.lock_irqsave = ezusb_lock_irqsave,
+	.unlock_irqrestore = ezusb_unlock_irqrestore,
+	.lock_irq = ezusb_lock_irq,
+	.unlock_irq = ezusb_unlock_irq,
+};
+
+static const struct net_device_ops ezusb_netdev_ops = {
+	.ndo_open		= orinoco_open,
+	.ndo_stop		= orinoco_stop,
+	.ndo_start_xmit		= ezusb_xmit,
+	.ndo_set_multicast_list	= orinoco_set_multicast_list,
+	.ndo_change_mtu		= orinoco_change_mtu,
+	.ndo_set_mac_address	= eth_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_tx_timeout		= orinoco_tx_timeout,
+	.ndo_get_stats		= orinoco_get_stats,
+};
+
+static int ezusb_probe(struct usb_interface *interface,
+		       const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct orinoco_private *priv;
+	hermes_t *hw;
+	struct ezusb_priv *upriv = NULL;
+	struct usb_interface_descriptor *iface_desc;
+	struct usb_endpoint_descriptor *ep;
+	const struct firmware *fw_entry;
+	int retval = 0;
+	int i;
+
+	priv = alloc_orinocodev(sizeof(*upriv), &udev->dev,
+				ezusb_hard_reset, NULL);
+	if (!priv) {
+		err("Couldn't allocate orinocodev");
+		goto exit;
+	}
+
+	hw = &priv->hw;
+
+	upriv = priv->card;
+
+	mutex_init(&upriv->mtx);
+	spin_lock_init(&upriv->reply_count_lock);
+
+	spin_lock_init(&upriv->req_lock);
+	INIT_LIST_HEAD(&upriv->req_pending);
+	INIT_LIST_HEAD(&upriv->req_active);
+
+	upriv->udev = udev;
+
+	hw->iobase = (void __force __iomem *) &upriv->hermes_reg_fake;
+	hw->reg_spacing = HERMES_16BIT_REGSPACING;
+	hw->priv = upriv;
+	hw->ops = &ezusb_ops;
+
+	/* set up the endpoint information */
+	/* check out the endpoints */
+
+	iface_desc = &interface->altsetting[0].desc;
+	for (i = 0; i < iface_desc->bNumEndpoints; ++i) {
+		ep = &interface->altsetting[0].endpoint[i].desc;
+
+		if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+		     == USB_DIR_IN) &&
+		    ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+		     == USB_ENDPOINT_XFER_BULK)) {
+			/* we found a bulk in endpoint */
+			if (upriv->read_urb != NULL) {
+				pr_warning("Found a second bulk in ep, ignored");
+				continue;
+			}
+
+			upriv->read_urb = usb_alloc_urb(0, GFP_KERNEL);
+			if (!upriv->read_urb) {
+				err("No free urbs available");
+				goto error;
+			}
+			if (le16_to_cpu(ep->wMaxPacketSize) != 64)
+				pr_warning("bulk in: wMaxPacketSize!= 64");
+			if (ep->bEndpointAddress != (2 | USB_DIR_IN))
+				pr_warning("bulk in: bEndpointAddress: %d",
+				     ep->bEndpointAddress);
+			upriv->read_pipe = usb_rcvbulkpipe(udev,
+							 ep->
+							 bEndpointAddress);
+			upriv->read_urb->transfer_buffer =
+			    kmalloc(BULK_BUF_SIZE, GFP_KERNEL);
+			if (!upriv->read_urb->transfer_buffer) {
+				err("Couldn't allocate IN buffer");
+				goto error;
+			}
+		}
+
+		if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+		     == USB_DIR_OUT) &&
+		    ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+		     == USB_ENDPOINT_XFER_BULK)) {
+			/* we found a bulk out endpoint */
+			if (upriv->bap_buf != NULL) {
+				pr_warning("Found a second bulk out ep, ignored");
+				continue;
+			}
+
+			if (le16_to_cpu(ep->wMaxPacketSize) != 64)
+				pr_warning("bulk out: wMaxPacketSize != 64");
+			if (ep->bEndpointAddress != 2)
+				pr_warning("bulk out: bEndpointAddress: %d",
+				     ep->bEndpointAddress);
+			upriv->write_pipe = usb_sndbulkpipe(udev,
+							  ep->
+							  bEndpointAddress);
+			upriv->bap_buf = kmalloc(BULK_BUF_SIZE, GFP_KERNEL);
+			if (!upriv->bap_buf) {
+				err("Couldn't allocate bulk_out_buffer");
+				goto error;
+			}
+		}
+	}
+	if (!upriv->bap_buf || !upriv->read_urb) {
+		err("Didn't find the required bulk endpoints");
+		goto error;
+	}
+
+	if (request_firmware(&fw_entry, "orinoco_ezusb_fw",
+			     &interface->dev) == 0) {
+		firmware.size = fw_entry->size;
+		firmware.code = fw_entry->data;
+	}
+	if (firmware.size && firmware.code) {
+		ezusb_firmware_download(upriv, &firmware);
+	} else {
+		err("No firmware to download");
+		goto error;
+	}
+
+	if (ezusb_hard_reset(priv) < 0) {
+		err("Cannot reset the device");
+		goto error;
+	}
+
+	/* If the firmware is already downloaded orinoco.c will call
+	 * ezusb_init but if the firmware is not already there, that will make
+	 * the kernel very unstable, so we try initializing here and quit in
+	 * case of error */
+	if (ezusb_init(hw) < 0) {
+		err("Couldn't initialize the device");
+		err("Firmware may not be downloaded or may be wrong.");
+		goto error;
+	}
+
+	/* Initialise the main driver */
+	if (orinoco_init(priv) != 0) {
+		err("orinoco_init() failed\n");
+		goto error;
+	}
+
+	if (orinoco_if_add(priv, 0, 0, &ezusb_netdev_ops) != 0) {
+		upriv->dev = NULL;
+		err("%s: orinoco_if_add() failed", __func__);
+		goto error;
+	}
+	upriv->dev = priv->ndev;
+
+	goto exit;
+
+ error:
+	ezusb_delete(upriv);
+	if (upriv->dev) {
+		/* upriv->dev was 0, so ezusb_delete() didn't free it */
+		free_orinocodev(priv);
+	}
+	upriv = NULL;
+	retval = -EFAULT;
+ exit:
+	if (fw_entry) {
+		firmware.code = NULL;
+		firmware.size = 0;
+		release_firmware(fw_entry);
+	}
+	usb_set_intfdata(interface, upriv);
+	return retval;
+}
+
+
+static void ezusb_disconnect(struct usb_interface *intf)
+{
+	struct ezusb_priv *upriv = usb_get_intfdata(intf);
+	usb_set_intfdata(intf, NULL);
+	ezusb_delete(upriv);
+	printk(KERN_INFO PFX "Disconnected\n");
+}
+
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver orinoco_driver = {
+	.name = DRIVER_NAME,
+	.probe = ezusb_probe,
+	.disconnect = ezusb_disconnect,
+	.id_table = ezusb_table,
+};
+
+/* Can't be declared "const" or the whole __initdata section will
+ * become const */
+static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
+    " (Manuel Estrada Sainz)";
+
+static int __init ezusb_module_init(void)
+{
+	int err;
+
+	printk(KERN_DEBUG "%s\n", version);
+
+	/* register this driver with the USB subsystem */
+	err = usb_register(&orinoco_driver);
+	if (err < 0) {
+		printk(KERN_ERR PFX "usb_register failed, error %d\n",
+		       err);
+		return err;
+	}
+
+	return 0;
+}
+
+static void __exit ezusb_module_exit(void)
+{
+	/* deregister this driver with the USB subsystem */
+	usb_deregister(&orinoco_driver);
+}
+
+
+module_init(ezusb_module_init);
+module_exit(ezusb_module_exit);
+
+MODULE_AUTHOR("Manuel Estrada Sainz");
+MODULE_DESCRIPTION
+    ("Driver for Orinoco wireless LAN cards using EZUSB bridge");
+MODULE_LICENSE("Dual MPL/GPL");
diff --git a/drivers/net/wireless/orinoco/spectrum_cs.c b/drivers/net/wireless/orinoco/spectrum_cs.c
index 59bda24..9b1af49 100644
--- a/drivers/net/wireless/orinoco/spectrum_cs.c
+++ b/drivers/net/wireless/orinoco/spectrum_cs.c
@@ -349,6 +349,7 @@
 		goto failed;
 
 	hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING);
+	hw->eeprom_pda = true;
 
 	/*
 	 * This actually configures the PCMCIA socket -- setting up
@@ -374,7 +375,7 @@
 
 	/* Register an interface with the stack */
 	if (orinoco_if_add(priv, link->io.BasePort1,
-			   link->irq.AssignedIRQ) != 0) {
+			   link->irq.AssignedIRQ, NULL) != 0) {
 		printk(KERN_ERR PFX "orinoco_if_add() failed\n");
 		goto failed;
 	}
@@ -405,9 +406,9 @@
 
 	/* We're committed to taking the device away now, so mark the
 	 * hardware as unavailable */
-	spin_lock_irqsave(&priv->lock, flags);
+	priv->hw.ops->lock_irqsave(&priv->lock, &flags);
 	priv->hw_unavailable++;
-	spin_unlock_irqrestore(&priv->lock, flags);
+	priv->hw.ops->unlock_irqrestore(&priv->lock, &flags);
 
 	pcmcia_disable_device(link);
 	if (priv->hw.iobase)
diff --git a/drivers/net/wireless/orinoco/wext.c b/drivers/net/wireless/orinoco/wext.c
index a1006bf..5775124 100644
--- a/drivers/net/wireless/orinoco/wext.c
+++ b/drivers/net/wireless/orinoco/wext.c
@@ -458,7 +458,7 @@
 	if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
 		/* Fast channel change - no commit if successful */
 		hermes_t *hw = &priv->hw;
-		err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
+		err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
 					    HERMES_TEST_SET_CHANNEL,
 					chan, NULL);
 	}
@@ -1273,8 +1273,8 @@
 	if (orinoco_lock(priv, &flags) != 0)
 		return -EBUSY;
 
-	err = hermes_read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length,
-			      extra);
+	err = hw->ops->read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length,
+				extra);
 	if (err)
 		goto out;
 
diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c
index 7bbd9d3..c072f41 100644
--- a/drivers/net/wireless/p54/main.c
+++ b/drivers/net/wireless/p54/main.c
@@ -546,8 +546,7 @@
 		     IEEE80211_HW_SUPPORTS_PS |
 		     IEEE80211_HW_PS_NULLFUNC_STACK |
 		     IEEE80211_HW_BEACON_FILTER |
-		     IEEE80211_HW_REPORTS_TX_ACK_STATUS |
-		     IEEE80211_HW_NOISE_DBM;
+		     IEEE80211_HW_REPORTS_TX_ACK_STATUS;
 
 	dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
 				      BIT(NL80211_IFTYPE_ADHOC) |
diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c
index ca42ccb..07c4528 100644
--- a/drivers/net/wireless/p54/p54pci.c
+++ b/drivers/net/wireless/p54/p54pci.c
@@ -277,6 +277,14 @@
 	struct p54p_priv *priv = dev->priv;
 	struct p54p_ring_control *ring_control = priv->ring_control;
 
+	p54p_check_tx_ring(dev, &priv->tx_idx_mgmt, 3, ring_control->tx_mgmt,
+			   ARRAY_SIZE(ring_control->tx_mgmt),
+			   priv->tx_buf_mgmt);
+
+	p54p_check_tx_ring(dev, &priv->tx_idx_data, 1, ring_control->tx_data,
+			   ARRAY_SIZE(ring_control->tx_data),
+			   priv->tx_buf_data);
+
 	p54p_check_rx_ring(dev, &priv->rx_idx_mgmt, 2, ring_control->rx_mgmt,
 		ARRAY_SIZE(ring_control->rx_mgmt), priv->rx_buf_mgmt);
 
@@ -285,14 +293,6 @@
 
 	wmb();
 	P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE));
-
-	p54p_check_tx_ring(dev, &priv->tx_idx_mgmt, 3, ring_control->tx_mgmt,
-			   ARRAY_SIZE(ring_control->tx_mgmt),
-			   priv->tx_buf_mgmt);
-
-	p54p_check_tx_ring(dev, &priv->tx_idx_data, 1, ring_control->tx_data,
-			   ARRAY_SIZE(ring_control->tx_data),
-			   priv->tx_buf_data);
 }
 
 static irqreturn_t p54p_interrupt(int irq, void *dev_id)
diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c
index 2ceff54..4e68910 100644
--- a/drivers/net/wireless/p54/txrx.c
+++ b/drivers/net/wireless/p54/txrx.c
@@ -350,7 +350,6 @@
 		rx_status->flag |= RX_FLAG_MMIC_ERROR;
 
 	rx_status->signal = p54_rssi_to_dbm(priv, hdr->rssi);
-	rx_status->noise = priv->noise;
 	if (hdr->rate & 0x10)
 		rx_status->flag |= RX_FLAG_SHORTPRE;
 	if (priv->hw->conf.channel->band == IEEE80211_BAND_5GHZ)
diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig
index 5239e082..eea1ef2 100644
--- a/drivers/net/wireless/rt2x00/Kconfig
+++ b/drivers/net/wireless/rt2x00/Kconfig
@@ -87,7 +87,7 @@
 
 config RT2800PCI_RT30XX
 	bool "rt2800pci - Include support for rt30xx (PCI/PCIe/PCMCIA) devices"
-	default n
+	default y
 	---help---
 	  This adds support for rt30xx wireless chipset family to the
 	  rt2800pci driver.
@@ -156,7 +156,7 @@
 
 config RT2800USB_RT30XX
 	bool "rt2800usb - Include support for rt30xx (USB) devices"
-	default n
+	default y
 	---help---
 	  This adds support for rt30xx wireless chipset family to the
 	  rt2800usb driver.
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c
index cdbf591..06b92f8 100644
--- a/drivers/net/wireless/rt2x00/rt2400pci.c
+++ b/drivers/net/wireless/rt2x00/rt2400pci.c
@@ -1018,8 +1018,8 @@
 	rt2x00_desc_write(entry_priv->desc, 1, word);
 
 	rt2x00_desc_read(txd, 2, &word);
-	rt2x00_set_field32(&word, TXD_W2_BUFFER_LENGTH, skb->len);
-	rt2x00_set_field32(&word, TXD_W2_DATABYTE_COUNT, skb->len);
+	rt2x00_set_field32(&word, TXD_W2_BUFFER_LENGTH, txdesc->length);
+	rt2x00_set_field32(&word, TXD_W2_DATABYTE_COUNT, txdesc->length);
 	rt2x00_desc_write(txd, 2, word);
 
 	rt2x00_desc_read(txd, 3, &word);
diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c
index 89e986f..ae8e205 100644
--- a/drivers/net/wireless/rt2x00/rt2500pci.c
+++ b/drivers/net/wireless/rt2x00/rt2500pci.c
@@ -1209,7 +1209,7 @@
 	rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->ifs);
 	rt2x00_set_field32(&word, TXD_W0_RETRY_MODE,
 			   test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags));
-	rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, skb->len);
+	rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length);
 	rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, CIPHER_NONE);
 	rt2x00_desc_write(txd, 0, word);
 }
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c
index 7185cb0..41d9996 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/rt2x00/rt2500usb.c
@@ -1072,7 +1072,7 @@
 	rt2x00_set_field32(&word, TXD_W0_NEW_SEQ,
 			   test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags));
 	rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->ifs);
-	rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, skb->len);
+	rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length);
 	rt2x00_set_field32(&word, TXD_W0_CIPHER, !!txdesc->cipher);
 	rt2x00_set_field32(&word, TXD_W0_KEY_ID, txdesc->key_idx);
 	rt2x00_desc_write(txd, 0, word);
diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h
index ec89372..2aa0375 100644
--- a/drivers/net/wireless/rt2x00/rt2800.h
+++ b/drivers/net/wireless/rt2x00/rt2800.h
@@ -107,7 +107,7 @@
 /*
  * INT_SOURCE_CSR: Interrupt source register.
  * Write one to clear corresponding bit.
- * TX_FIFO_STATUS: FIFO Statistics is full, sw should read 0x171c
+ * TX_FIFO_STATUS: FIFO Statistics is full, sw should read TX_STA_FIFO
  */
 #define INT_SOURCE_CSR			0x0200
 #define INT_SOURCE_CSR_RXDELAYINT	FIELD32(0x00000001)
@@ -845,7 +845,7 @@
  * TX_BAND_CFG: 0x1 use upper 20MHz, 0x0 use lower 20MHz
  */
 #define TX_BAND_CFG			0x132c
-#define TX_BAND_CFG_HT40_PLUS		FIELD32(0x00000001)
+#define TX_BAND_CFG_HT40_MINUS		FIELD32(0x00000001)
 #define TX_BAND_CFG_A			FIELD32(0x00000002)
 #define TX_BAND_CFG_BG			FIELD32(0x00000004)
 
@@ -1519,7 +1519,7 @@
  * BBP 3: RX Antenna
  */
 #define BBP3_RX_ANTENNA			FIELD8(0x18)
-#define BBP3_HT40_PLUS			FIELD8(0x20)
+#define BBP3_HT40_MINUS			FIELD8(0x20)
 
 /*
  * BBP 4: Bandwidth
@@ -1566,6 +1566,11 @@
 #define RFCSR12_TX_POWER		FIELD8(0x1f)
 
 /*
+ * RFCSR 13:
+ */
+#define RFCSR13_TX_POWER		FIELD8(0x1f)
+
+/*
  * RFCSR 15:
  */
 #define RFCSR15_TX_LO2_EN		FIELD8(0x08)
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 2648f31..e37bbea 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -41,9 +41,6 @@
 #if defined(CONFIG_RT2X00_LIB_USB) || defined(CONFIG_RT2X00_LIB_USB_MODULE)
 #include "rt2x00usb.h"
 #endif
-#if defined(CONFIG_RT2X00_LIB_PCI) || defined(CONFIG_RT2X00_LIB_PCI_MODULE)
-#include "rt2x00pci.h"
-#endif
 #include "rt2800lib.h"
 #include "rt2800.h"
 #include "rt2800usb.h"
@@ -76,6 +73,23 @@
 	rt2800_regbusy_read((__dev), H2M_MAILBOX_CSR, \
 			    H2M_MAILBOX_CSR_OWNER, (__reg))
 
+static inline bool rt2800_is_305x_soc(struct rt2x00_dev *rt2x00dev)
+{
+	/* check for rt2872 on SoC */
+	if (!rt2x00_is_soc(rt2x00dev) ||
+	    !rt2x00_rt(rt2x00dev, RT2872))
+		return false;
+
+	/* we know for sure that these rf chipsets are used on rt305x boards */
+	if (rt2x00_rf(rt2x00dev, RF3020) ||
+	    rt2x00_rf(rt2x00dev, RF3021) ||
+	    rt2x00_rf(rt2x00dev, RF3022))
+		return true;
+
+	NOTICE(rt2x00dev, "Unknown RF chipset on rt305x\n");
+	return false;
+}
+
 static void rt2800_bbp_write(struct rt2x00_dev *rt2x00dev,
 			     const unsigned int word, const u8 value)
 {
@@ -794,6 +808,11 @@
 			  TXPOWER_G_TO_DEV(info->tx_power1));
 	rt2800_rfcsr_write(rt2x00dev, 12, rfcsr);
 
+	rt2800_rfcsr_read(rt2x00dev, 13, &rfcsr);
+	rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER,
+			  TXPOWER_G_TO_DEV(info->tx_power2));
+	rt2800_rfcsr_write(rt2x00dev, 13, rfcsr);
+
 	rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr);
 	rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset);
 	rt2800_rfcsr_write(rt2x00dev, 23, rfcsr);
@@ -849,7 +868,7 @@
 	}
 
 	rt2800_register_read(rt2x00dev, TX_BAND_CFG, &reg);
-	rt2x00_set_field32(&reg, TX_BAND_CFG_HT40_PLUS, conf_is_ht40_plus(conf));
+	rt2x00_set_field32(&reg, TX_BAND_CFG_HT40_MINUS, conf_is_ht40_minus(conf));
 	rt2x00_set_field32(&reg, TX_BAND_CFG_A, rf->channel > 14);
 	rt2x00_set_field32(&reg, TX_BAND_CFG_BG, rf->channel <= 14);
 	rt2800_register_write(rt2x00dev, TX_BAND_CFG, reg);
@@ -882,7 +901,7 @@
 	rt2800_bbp_write(rt2x00dev, 4, bbp);
 
 	rt2800_bbp_read(rt2x00dev, 3, &bbp);
-	rt2x00_set_field8(&bbp, BBP3_HT40_PLUS, conf_is_ht40_plus(conf));
+	rt2x00_set_field8(&bbp, BBP3_HT40_MINUS, conf_is_ht40_minus(conf));
 	rt2800_bbp_write(rt2x00dev, 3, bbp);
 
 	if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) {
@@ -1551,6 +1570,9 @@
 		     rt2800_wait_bbp_ready(rt2x00dev)))
 		return -EACCES;
 
+	if (rt2800_is_305x_soc(rt2x00dev))
+		rt2800_bbp_write(rt2x00dev, 31, 0x08);
+
 	rt2800_bbp_write(rt2x00dev, 65, 0x2c);
 	rt2800_bbp_write(rt2x00dev, 66, 0x38);
 
@@ -1571,6 +1593,9 @@
 		rt2800_bbp_write(rt2x00dev, 79, 0x13);
 		rt2800_bbp_write(rt2x00dev, 80, 0x05);
 		rt2800_bbp_write(rt2x00dev, 81, 0x33);
+	} else if (rt2800_is_305x_soc(rt2x00dev)) {
+		rt2800_bbp_write(rt2x00dev, 78, 0x0e);
+		rt2800_bbp_write(rt2x00dev, 80, 0x08);
 	} else {
 		rt2800_bbp_write(rt2x00dev, 81, 0x37);
 	}
@@ -1591,12 +1616,16 @@
 	if (rt2x00_rt_rev_gte(rt2x00dev, RT3070, REV_RT3070F) ||
 	    rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) ||
 	    rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E) ||
-	    rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E))
+	    rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E) ||
+	    rt2800_is_305x_soc(rt2x00dev))
 		rt2800_bbp_write(rt2x00dev, 103, 0xc0);
 	else
 		rt2800_bbp_write(rt2x00dev, 103, 0x00);
 
-	rt2800_bbp_write(rt2x00dev, 105, 0x05);
+	if (rt2800_is_305x_soc(rt2x00dev))
+		rt2800_bbp_write(rt2x00dev, 105, 0x01);
+	else
+		rt2800_bbp_write(rt2x00dev, 105, 0x05);
 	rt2800_bbp_write(rt2x00dev, 106, 0x35);
 
 	if (rt2x00_rt(rt2x00dev, RT3071) ||
@@ -1613,11 +1642,6 @@
 		rt2800_bbp_write(rt2x00dev, 138, value);
 	}
 
-	if (rt2x00_rt(rt2x00dev, RT2872)) {
-		rt2800_bbp_write(rt2x00dev, 31, 0x08);
-		rt2800_bbp_write(rt2x00dev, 78, 0x0e);
-		rt2800_bbp_write(rt2x00dev, 80, 0x08);
-	}
 
 	for (i = 0; i < EEPROM_BBP_SIZE; i++) {
 		rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom);
@@ -1703,7 +1727,8 @@
 	if (!rt2x00_rt(rt2x00dev, RT3070) &&
 	    !rt2x00_rt(rt2x00dev, RT3071) &&
 	    !rt2x00_rt(rt2x00dev, RT3090) &&
-	    !rt2x00_rt(rt2x00dev, RT3390))
+	    !rt2x00_rt(rt2x00dev, RT3390) &&
+	    !rt2800_is_305x_soc(rt2x00dev))
 		return 0;
 
 	/*
@@ -1771,6 +1796,40 @@
 		rt2800_rfcsr_write(rt2x00dev, 29, 0x8f);
 		rt2800_rfcsr_write(rt2x00dev, 30, 0x20);
 		rt2800_rfcsr_write(rt2x00dev, 31, 0x0f);
+	} else if (rt2800_is_305x_soc(rt2x00dev)) {
+		rt2800_rfcsr_write(rt2x00dev, 0, 0x50);
+		rt2800_rfcsr_write(rt2x00dev, 1, 0x01);
+		rt2800_rfcsr_write(rt2x00dev, 2, 0xf7);
+		rt2800_rfcsr_write(rt2x00dev, 3, 0x75);
+		rt2800_rfcsr_write(rt2x00dev, 4, 0x40);
+		rt2800_rfcsr_write(rt2x00dev, 5, 0x03);
+		rt2800_rfcsr_write(rt2x00dev, 6, 0x02);
+		rt2800_rfcsr_write(rt2x00dev, 7, 0x50);
+		rt2800_rfcsr_write(rt2x00dev, 8, 0x39);
+		rt2800_rfcsr_write(rt2x00dev, 9, 0x0f);
+		rt2800_rfcsr_write(rt2x00dev, 10, 0x60);
+		rt2800_rfcsr_write(rt2x00dev, 11, 0x21);
+		rt2800_rfcsr_write(rt2x00dev, 12, 0x75);
+		rt2800_rfcsr_write(rt2x00dev, 13, 0x75);
+		rt2800_rfcsr_write(rt2x00dev, 14, 0x90);
+		rt2800_rfcsr_write(rt2x00dev, 15, 0x58);
+		rt2800_rfcsr_write(rt2x00dev, 16, 0xb3);
+		rt2800_rfcsr_write(rt2x00dev, 17, 0x92);
+		rt2800_rfcsr_write(rt2x00dev, 18, 0x2c);
+		rt2800_rfcsr_write(rt2x00dev, 19, 0x02);
+		rt2800_rfcsr_write(rt2x00dev, 20, 0xba);
+		rt2800_rfcsr_write(rt2x00dev, 21, 0xdb);
+		rt2800_rfcsr_write(rt2x00dev, 22, 0x00);
+		rt2800_rfcsr_write(rt2x00dev, 23, 0x31);
+		rt2800_rfcsr_write(rt2x00dev, 24, 0x08);
+		rt2800_rfcsr_write(rt2x00dev, 25, 0x01);
+		rt2800_rfcsr_write(rt2x00dev, 26, 0x25);
+		rt2800_rfcsr_write(rt2x00dev, 27, 0x23);
+		rt2800_rfcsr_write(rt2x00dev, 28, 0x13);
+		rt2800_rfcsr_write(rt2x00dev, 29, 0x83);
+		rt2800_rfcsr_write(rt2x00dev, 30, 0x00);
+		rt2800_rfcsr_write(rt2x00dev, 31, 0x00);
+		return 0;
 	}
 
 	if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F)) {
@@ -1986,7 +2045,6 @@
 		EEPROM(rt2x00dev, "Antenna: 0x%04x\n", word);
 	} else if (rt2x00_rt(rt2x00dev, RT2860) ||
 		   rt2x00_rt(rt2x00dev, RT2870) ||
-		   rt2x00_rt(rt2x00dev, RT2872) ||
 		   rt2x00_rt(rt2x00dev, RT2872)) {
 		/*
 		 * There is a max of 2 RX streams for RT28x0 series
@@ -2318,8 +2376,11 @@
 	else
 		spec->ht.ht_supported = false;
 
+	/*
+	 * Don't set IEEE80211_HT_CAP_SUP_WIDTH_20_40 for now as it causes
+	 * reception problems with HT40 capable 11n APs
+	 */
 	spec->ht.cap =
-	    IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
 	    IEEE80211_HT_CAP_GRN_FLD |
 	    IEEE80211_HT_CAP_SGI_20 |
 	    IEEE80211_HT_CAP_SGI_40 |
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c
index 0e52f174..f08b6a3 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@ -663,7 +663,7 @@
 			   test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags) ?
 			   txdesc->key_idx : 0xff);
 	rt2x00_set_field32(&word, TXWI_W1_MPDU_TOTAL_BYTE_COUNT,
-			   skb->len - txdesc->l2pad);
+			   txdesc->length);
 	rt2x00_set_field32(&word, TXWI_W1_PACKETID,
 			   skbdesc->entry->queue->qid + 1);
 	rt2x00_desc_write(txwi, 1, word);
@@ -895,10 +895,6 @@
 	    (rt2x00_get_field32(rxwi2, RXWI_W2_RSSI0) +
 	     rt2x00_get_field32(rxwi2, RXWI_W2_RSSI1)) / 2;
 
-	rxdesc->noise =
-	    (rt2x00_get_field32(rxwi3, RXWI_W3_SNR0) +
-	     rt2x00_get_field32(rxwi3, RXWI_W3_SNR1)) / 2;
-
 	rxdesc->size = rt2x00_get_field32(rxwi0, RXWI_W0_MPDU_TOTAL_BYTE_COUNT);
 
 	/*
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index 95c8a61..e3f3a97 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -437,7 +437,7 @@
 			   test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags) ?
 			   txdesc->key_idx : 0xff);
 	rt2x00_set_field32(&word, TXWI_W1_MPDU_TOTAL_BYTE_COUNT,
-			   skb->len - txdesc->l2pad);
+			   txdesc->length);
 	rt2x00_set_field32(&word, TXWI_W1_PACKETID,
 			   skbdesc->entry->queue->qid + 1);
 	rt2x00_desc_write(txwi, 1, word);
@@ -645,10 +645,6 @@
 	    (rt2x00_get_field32(rxwi2, RXWI_W2_RSSI0) +
 	     rt2x00_get_field32(rxwi2, RXWI_W2_RSSI1)) / 2;
 
-	rxdesc->noise =
-	    (rt2x00_get_field32(rxwi3, RXWI_W3_SNR0) +
-	     rt2x00_get_field32(rxwi3, RXWI_W3_SNR1)) / 2;
-
 	rxdesc->size = rt2x00_get_field32(rxwi0, RXWI_W0_MPDU_TOTAL_BYTE_COUNT);
 
 	/*
@@ -1027,7 +1023,7 @@
 #ifdef CONFIG_RT2800USB_UNKNOWN
 	/*
 	 * Unclear what kind of devices these are (they aren't supported by the
-	 * vendor driver).
+	 * vendor linux driver).
 	 */
 	/* Amigo */
 	{ USB_DEVICE(0x0e0b, 0x9031), USB_DEVICE_DATA(&rt2800usb_ops) },
@@ -1040,6 +1036,7 @@
 	/* AzureWave */
 	{ USB_DEVICE(0x13d3, 0x3262), USB_DEVICE_DATA(&rt2800usb_ops) },
 	{ USB_DEVICE(0x13d3, 0x3284), USB_DEVICE_DATA(&rt2800usb_ops) },
+	{ USB_DEVICE(0x13d3, 0x3322), USB_DEVICE_DATA(&rt2800usb_ops) },
 	/* Belkin */
 	{ USB_DEVICE(0x050d, 0x825a), USB_DEVICE_DATA(&rt2800usb_ops) },
 	/* Buffalo */
@@ -1058,6 +1055,7 @@
 	{ USB_DEVICE(0x07d1, 0x3c0b), USB_DEVICE_DATA(&rt2800usb_ops) },
 	{ USB_DEVICE(0x07d1, 0x3c13), USB_DEVICE_DATA(&rt2800usb_ops) },
 	{ USB_DEVICE(0x07d1, 0x3c15), USB_DEVICE_DATA(&rt2800usb_ops) },
+	{ USB_DEVICE(0x07d1, 0x3c17), USB_DEVICE_DATA(&rt2800usb_ops) },
 	/* Encore */
 	{ USB_DEVICE(0x203d, 0x14a1), USB_DEVICE_DATA(&rt2800usb_ops) },
 	/* Gemtek */
@@ -1074,11 +1072,13 @@
 	/* Motorola */
 	{ USB_DEVICE(0x100d, 0x9032), USB_DEVICE_DATA(&rt2800usb_ops) },
 	/* Ovislink */
+	{ USB_DEVICE(0x1b75, 0x3071), USB_DEVICE_DATA(&rt2800usb_ops) },
 	{ USB_DEVICE(0x1b75, 0x3072), USB_DEVICE_DATA(&rt2800usb_ops) },
 	/* Pegatron */
 	{ USB_DEVICE(0x05a6, 0x0101), USB_DEVICE_DATA(&rt2800usb_ops) },
 	{ USB_DEVICE(0x1d4d, 0x0002), USB_DEVICE_DATA(&rt2800usb_ops) },
 	{ USB_DEVICE(0x1d4d, 0x0010), USB_DEVICE_DATA(&rt2800usb_ops) },
+	{ USB_DEVICE(0x1d4d, 0x0011), USB_DEVICE_DATA(&rt2800usb_ops) },
 	/* Planex */
 	{ USB_DEVICE(0x2019, 0xab24), USB_DEVICE_DATA(&rt2800usb_ops) },
 	/* Qcom */
@@ -1087,6 +1087,7 @@
 	{ USB_DEVICE(0x083a, 0xa512), USB_DEVICE_DATA(&rt2800usb_ops) },
 	{ USB_DEVICE(0x083a, 0xc522), USB_DEVICE_DATA(&rt2800usb_ops) },
 	{ USB_DEVICE(0x083a, 0xd522), USB_DEVICE_DATA(&rt2800usb_ops) },
+	{ USB_DEVICE(0x083a, 0xf511), USB_DEVICE_DATA(&rt2800usb_ops) },
 	/* Sweex */
 	{ USB_DEVICE(0x177f, 0x0153), USB_DEVICE_DATA(&rt2800usb_ops) },
 	{ USB_DEVICE(0x177f, 0x0313), USB_DEVICE_DATA(&rt2800usb_ops) },
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index eda73ba..3ae468c 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -435,7 +435,6 @@
 	rx_status->mactime = rxdesc.timestamp;
 	rx_status->rate_idx = rate_idx;
 	rx_status->signal = rxdesc.rssi;
-	rx_status->noise = rxdesc.noise;
 	rx_status->flag = rxdesc.flags;
 	rx_status->antenna = rt2x00dev->link.ant.active.rx;
 
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index 91b7fb9..e22029f 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -334,12 +334,10 @@
 	txdesc->aifs = entry->queue->aifs;
 
 	/*
-	 * Header and alignment information.
+	 * Header and frame information.
 	 */
+	txdesc->length = entry->skb->len;
 	txdesc->header_length = ieee80211_get_hdrlen_from_skb(entry->skb);
-	if (test_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags) &&
-	    (entry->skb->len > txdesc->header_length))
-		txdesc->l2pad = L2PAD_SIZE(txdesc->header_length);
 
 	/*
 	 * Check whether this frame is to be acked.
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h
index c1e482b..94a48c1 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.h
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.h
@@ -183,7 +183,6 @@
  * @timestamp: RX Timestamp
  * @signal: Signal of the received frame.
  * @rssi: RSSI of the received frame.
- * @noise: Measured noise during frame reception.
  * @size: Data size of the received frame.
  * @flags: MAC80211 receive flags (See &enum mac80211_rx_flags).
  * @dev_flags: Ralink receive flags (See &enum rxdone_entry_desc_flags).
@@ -197,7 +196,6 @@
 	u64 timestamp;
 	int signal;
 	int rssi;
-	int noise;
 	int size;
 	int flags;
 	int dev_flags;
@@ -287,8 +285,8 @@
  *
  * @flags: Descriptor flags (See &enum queue_entry_flags).
  * @queue: Queue identification (See &enum data_queue_qid).
+ * @length: Length of the entire frame.
  * @header_length: Length of 802.11 header.
- * @l2pad: Amount of padding to align 802.11 payload to 4-byte boundrary.
  * @length_high: PLCP length high word.
  * @length_low: PLCP length low word.
  * @signal: PLCP signal.
@@ -313,8 +311,8 @@
 
 	enum data_queue_qid queue;
 
+	u16 length;
 	u16 header_length;
-	u16 l2pad;
 
 	u16 length_high;
 	u16 length_low;
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c
index b988598..26ee791 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/rt2x00/rt61pci.c
@@ -1809,7 +1809,8 @@
 
 	if (skbdesc->desc_len > TXINFO_SIZE) {
 		rt2x00_desc_read(txd, 11, &word);
-		rt2x00_set_field32(&word, TXD_W11_BUFFER_LENGTH0, skb->len);
+		rt2x00_set_field32(&word, TXD_W11_BUFFER_LENGTH0,
+				   txdesc->length);
 		rt2x00_desc_write(txd, 11, word);
 	}
 
@@ -1832,7 +1833,7 @@
 	rt2x00_set_field32(&word, TXD_W0_KEY_TABLE,
 			   test_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags));
 	rt2x00_set_field32(&word, TXD_W0_KEY_INDEX, txdesc->key_idx);
-	rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, skb->len);
+	rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length);
 	rt2x00_set_field32(&word, TXD_W0_BURST,
 			   test_bit(ENTRY_TXD_BURST, &txdesc->flags));
 	rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, txdesc->cipher);
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index 576ea9d..39b3c6d 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -1495,7 +1495,7 @@
 	rt2x00_set_field32(&word, TXD_W0_KEY_TABLE,
 			   test_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags));
 	rt2x00_set_field32(&word, TXD_W0_KEY_INDEX, txdesc->key_idx);
-	rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, skb->len);
+	rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length);
 	rt2x00_set_field32(&word, TXD_W0_BURST2,
 			   test_bit(ENTRY_TXD_BURST, &txdesc->flags));
 	rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, txdesc->cipher);
diff --git a/drivers/net/wireless/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c
index 6b46329..21307f2 100644
--- a/drivers/net/wireless/rtl818x/rtl8180_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c
@@ -188,6 +188,7 @@
 			info->flags |= IEEE80211_TX_STAT_ACK;
 
 		info->status.rates[0].count = (flags & 0xFF) + 1;
+		info->status.rates[1].idx = -1;
 
 		ieee80211_tx_status_irqsafe(dev, skb);
 		if (ring->entries - skb_queue_len(&ring->queue) == 2)
@@ -297,7 +298,7 @@
 	entry->flags = cpu_to_le32(tx_flags);
 	__skb_queue_tail(&ring->queue, skb);
 	if (ring->entries - skb_queue_len(&ring->queue) < 2)
-		ieee80211_stop_queue(dev, skb_get_queue_mapping(skb));
+		ieee80211_stop_queue(dev, prio);
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, (1 << (prio + 4)));
@@ -827,6 +828,7 @@
 	const char *chip_name, *rf_name = NULL;
 	u32 reg;
 	u16 eeprom_val;
+	u8 mac_addr[ETH_ALEN];
 
 	err = pci_enable_device(pdev);
 	if (err) {
@@ -987,12 +989,13 @@
 		eeprom_93cx6_read(&eeprom, 0x19, &priv->rfparam);
 	}
 
-	eeprom_93cx6_multiread(&eeprom, 0x7, (__le16 *)dev->wiphy->perm_addr, 3);
-	if (!is_valid_ether_addr(dev->wiphy->perm_addr)) {
+	eeprom_93cx6_multiread(&eeprom, 0x7, (__le16 *)mac_addr, 3);
+	if (!is_valid_ether_addr(mac_addr)) {
 		printk(KERN_WARNING "%s (rtl8180): Invalid hwaddr! Using"
 		       " randomly generated MAC addr\n", pci_name(pdev));
-		random_ether_addr(dev->wiphy->perm_addr);
+		random_ether_addr(mac_addr);
 	}
+	SET_IEEE80211_PERM_ADDR(dev, mac_addr);
 
 	/* CCK TX power */
 	for (i = 0; i < 14; i += 2) {
@@ -1024,7 +1027,7 @@
 	}
 
 	printk(KERN_INFO "%s: hwaddr %pM, %s + %s\n",
-	       wiphy_name(dev->wiphy), dev->wiphy->perm_addr,
+	       wiphy_name(dev->wiphy), mac_addr,
 	       chip_name, priv->rf->name);
 
 	return 0;
diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c
index 738921f..891b849 100644
--- a/drivers/net/wireless/rtl818x/rtl8187_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c
@@ -1333,6 +1333,7 @@
 	u16 txpwr, reg;
 	u16 product_id = le16_to_cpu(udev->descriptor.idProduct);
 	int err, i;
+	u8 mac_addr[ETH_ALEN];
 
 	dev = ieee80211_alloc_hw(sizeof(*priv), &rtl8187_ops);
 	if (!dev) {
@@ -1390,12 +1391,13 @@
 	udelay(10);
 
 	eeprom_93cx6_multiread(&eeprom, RTL8187_EEPROM_MAC_ADDR,
-			       (__le16 __force *)dev->wiphy->perm_addr, 3);
-	if (!is_valid_ether_addr(dev->wiphy->perm_addr)) {
+			       (__le16 __force *)mac_addr, 3);
+	if (!is_valid_ether_addr(mac_addr)) {
 		printk(KERN_WARNING "rtl8187: Invalid hwaddr! Using randomly "
 		       "generated MAC address\n");
-		random_ether_addr(dev->wiphy->perm_addr);
+		random_ether_addr(mac_addr);
 	}
+	SET_IEEE80211_PERM_ADDR(dev, mac_addr);
 
 	channel = priv->channels;
 	for (i = 0; i < 3; i++) {
@@ -1526,7 +1528,7 @@
 	skb_queue_head_init(&priv->b_tx_status.queue);
 
 	printk(KERN_INFO "%s: hwaddr %pM, %s V%d + %s, rfkill mask %d\n",
-	       wiphy_name(dev->wiphy), dev->wiphy->perm_addr,
+	       wiphy_name(dev->wiphy), mac_addr,
 	       chip_name, priv->asic_rev, priv->rf->name, priv->rfkill_mask);
 
 #ifdef CONFIG_RTL8187_LEDS
diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c
index 51614d1..00b2428 100644
--- a/drivers/net/wireless/wl12xx/wl1251_main.c
+++ b/drivers/net/wireless/wl12xx/wl1251_main.c
@@ -857,6 +857,7 @@
 }
 
 static int wl1251_op_hw_scan(struct ieee80211_hw *hw,
+			     struct ieee80211_vif *vif,
 			     struct cfg80211_scan_request *req)
 {
 	struct wl1251 *wl = hw->priv;
@@ -1291,7 +1292,6 @@
 	wl->hw->channel_change_time = 10000;
 
 	wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
-		IEEE80211_HW_NOISE_DBM |
 		IEEE80211_HW_SUPPORTS_PS |
 		IEEE80211_HW_BEACON_FILTER |
 		IEEE80211_HW_SUPPORTS_UAPSD;
diff --git a/drivers/net/wireless/wl12xx/wl1251_rx.c b/drivers/net/wireless/wl12xx/wl1251_rx.c
index 6f229e0..af5c67b 100644
--- a/drivers/net/wireless/wl12xx/wl1251_rx.c
+++ b/drivers/net/wireless/wl12xx/wl1251_rx.c
@@ -74,12 +74,6 @@
 
 	status->signal = desc->rssi;
 
-	/*
-	 * FIXME: guessing that snr needs to be divided by two, otherwise
-	 * the values don't make any sense
-	 */
-	status->noise = desc->rssi - desc->snr / 2;
-
 	status->freq = ieee80211_channel_to_frequency(desc->channel);
 
 	status->flag |= RX_FLAG_TSFT;
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c
index 2ad086e..e19e2f8 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.c
@@ -590,7 +590,7 @@
 
 	/* BT-WLAN coext parameters */
 	for (i = 0; i < CONF_SG_PARAMS_MAX; i++)
-		param->params[i] = c->params[i];
+		param->params[i] = cpu_to_le32(c->params[i]);
 	param->param_idx = CONF_SG_PARAMS_ALL;
 
 	ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param));
diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c
index 8087dc1..acb1d9e 100644
--- a/drivers/net/wireless/wl12xx/wl1271_boot.c
+++ b/drivers/net/wireless/wl12xx/wl1271_boot.c
@@ -351,7 +351,7 @@
 static int wl1271_boot_run_firmware(struct wl1271 *wl)
 {
 	int loop, ret;
-	u32 chip_id, interrupt;
+	u32 chip_id, intr;
 
 	wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT);
 
@@ -368,15 +368,15 @@
 	loop = 0;
 	while (loop++ < INIT_LOOP) {
 		udelay(INIT_LOOP_DELAY);
-		interrupt = wl1271_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR);
+		intr = wl1271_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR);
 
-		if (interrupt == 0xffffffff) {
+		if (intr == 0xffffffff) {
 			wl1271_error("error reading hardware complete "
 				     "init indication");
 			return -EIO;
 		}
 		/* check that ACX_INTR_INIT_COMPLETE is enabled */
-		else if (interrupt & WL1271_ACX_INTR_INIT_COMPLETE) {
+		else if (intr & WL1271_ACX_INTR_INIT_COMPLETE) {
 			wl1271_write32(wl, ACX_REG_INTERRUPT_ACK,
 				       WL1271_ACX_INTR_INIT_COMPLETE);
 			break;
diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c
index 6b5ba8e..62c11af 100644
--- a/drivers/net/wireless/wl12xx/wl1271_cmd.c
+++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c
@@ -37,7 +37,7 @@
 #include "wl1271_cmd.h"
 #include "wl1271_event.h"
 
-#define WL1271_CMD_POLL_COUNT       5
+#define WL1271_CMD_FAST_POLL_COUNT       50
 
 /*
  * send command to firmware
@@ -77,11 +77,11 @@
 			goto out;
 		}
 
-		udelay(10);
 		poll_count++;
-		if (poll_count == WL1271_CMD_POLL_COUNT)
-			wl1271_info("cmd polling took over %d cycles",
-				    poll_count);
+		if (poll_count < WL1271_CMD_FAST_POLL_COUNT)
+			udelay(10);
+		else
+			msleep(1);
 
 		intr = wl1271_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR);
 	}
@@ -318,7 +318,7 @@
 	join->rx_config_options = cpu_to_le32(wl->rx_config);
 	join->rx_filter_options = cpu_to_le32(wl->rx_filter);
 	join->bss_type = bss_type;
-	join->basic_rate_set = wl->basic_rate_set;
+	join->basic_rate_set = cpu_to_le32(wl->basic_rate_set);
 
 	if (wl->band == IEEE80211_BAND_5GHZ)
 		join->bss_type |= WL1271_JOIN_CMD_BSS_TYPE_5GHZ;
@@ -615,7 +615,7 @@
 	params->params.scan_options = cpu_to_le16(scan_options);
 
 	params->params.num_probe_requests = probe_requests;
-	params->params.tx_rate = rate;
+	params->params.tx_rate = cpu_to_le32(rate);
 	params->params.tid_trigger = 0;
 	params->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
 
diff --git a/drivers/net/wireless/wl12xx/wl1271_conf.h b/drivers/net/wireless/wl12xx/wl1271_conf.h
index c44307c..d046d04 100644
--- a/drivers/net/wireless/wl12xx/wl1271_conf.h
+++ b/drivers/net/wireless/wl12xx/wl1271_conf.h
@@ -401,7 +401,7 @@
 };
 
 struct conf_sg_settings {
-	__le32 params[CONF_SG_PARAMS_MAX];
+	u32 params[CONF_SG_PARAMS_MAX];
 	u8 state;
 };
 
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index e47a58d..62e5440 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -1118,14 +1118,13 @@
 	}
 }
 
-static int wl1271_join_channel(struct wl1271 *wl, int channel)
+static int wl1271_dummy_join(struct wl1271 *wl)
 {
 	int ret = 0;
 	/* we need to use a dummy BSSID for now */
 	static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde,
 						  0xad, 0xbe, 0xef };
 
-	wl->channel = channel;
 	memcpy(wl->bssid, dummy_bssid, ETH_ALEN);
 
 	/* pass through frames from all BSS */
@@ -1141,7 +1140,47 @@
 	return ret;
 }
 
-static int wl1271_unjoin_channel(struct wl1271 *wl)
+static int wl1271_join(struct wl1271 *wl)
+{
+	int ret;
+
+	ret = wl1271_cmd_join(wl, wl->set_bss_type);
+	if (ret < 0)
+		goto out;
+
+	set_bit(WL1271_FLAG_JOINED, &wl->flags);
+
+	if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
+		goto out;
+
+	/*
+	 * The join command disable the keep-alive mode, shut down its process,
+	 * and also clear the template config, so we need to reset it all after
+	 * the join. The acx_aid starts the keep-alive process, and the order
+	 * of the commands below is relevant.
+	 */
+	ret = wl1271_acx_keep_alive_mode(wl, true);
+	if (ret < 0)
+		goto out;
+
+	ret = wl1271_acx_aid(wl, wl->aid);
+	if (ret < 0)
+		goto out;
+
+	ret = wl1271_cmd_build_klv_null_data(wl);
+	if (ret < 0)
+		goto out;
+
+	ret = wl1271_acx_keep_alive_config(wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
+					   ACX_KEEP_ALIVE_TPL_VALID);
+	if (ret < 0)
+		goto out;
+
+out:
+	return ret;
+}
+
+static int wl1271_unjoin(struct wl1271 *wl)
 {
 	int ret;
 
@@ -1231,7 +1270,7 @@
 				       "failed %d", ret);
 
 		if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
-			ret = wl1271_cmd_join(wl, wl->set_bss_type);
+			ret = wl1271_join(wl);
 			if (ret < 0)
 				wl1271_warning("cmd join to update channel "
 					       "failed %d", ret);
@@ -1241,9 +1280,9 @@
 	if (changed & IEEE80211_CONF_CHANGE_IDLE) {
 		if (conf->flags & IEEE80211_CONF_IDLE &&
 		    test_bit(WL1271_FLAG_JOINED, &wl->flags))
-			wl1271_unjoin_channel(wl);
+			wl1271_unjoin(wl);
 		else if (!(conf->flags & IEEE80211_CONF_IDLE))
-			wl1271_join_channel(wl, channel);
+			wl1271_dummy_join(wl);
 
 		if (conf->flags & IEEE80211_CONF_IDLE) {
 			wl->rate_set = wl1271_min_rate_get(wl);
@@ -1519,6 +1558,7 @@
 }
 
 static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
+			     struct ieee80211_vif *vif,
 			     struct cfg80211_scan_request *req)
 {
 	struct wl1271 *wl = hw->priv;
@@ -1607,7 +1647,6 @@
 	enum wl1271_cmd_ps_mode mode;
 	struct wl1271 *wl = hw->priv;
 	bool do_join = false;
-	bool do_keepalive = false;
 	int ret;
 
 	wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed");
@@ -1702,6 +1741,10 @@
 			if (ret < 0)
 				goto out_sleep;
 
+			ret = wl1271_build_qos_null_data(wl);
+			if (ret < 0)
+				goto out_sleep;
+
 			/* filter out all packets not from this BSSID */
 			wl1271_configure_filters(wl, 0);
 
@@ -1746,19 +1789,6 @@
 			ret = wl1271_cmd_build_probe_req(wl, NULL, 0,
 							 NULL, 0, wl->band);
 
-			/* Enable the keep-alive feature */
-			ret = wl1271_acx_keep_alive_mode(wl, true);
-			if (ret < 0)
-				goto out_sleep;
-
-			/*
-			 * This is awkward. The keep-alive configs must be done
-			 * *after* the join command, because otherwise it will
-			 * not work, but it must only be done *once* because
-			 * otherwise the firmware will start complaining.
-			 */
-			do_keepalive = true;
-
 			/* enable the connection monitoring feature */
 			ret = wl1271_acx_conn_monit_params(wl, true);
 			if (ret < 0)
@@ -1826,35 +1856,11 @@
 	}
 
 	if (do_join) {
-		ret = wl1271_cmd_join(wl, wl->set_bss_type);
+		ret = wl1271_join(wl);
 		if (ret < 0) {
 			wl1271_warning("cmd join failed %d", ret);
 			goto out_sleep;
 		}
-		set_bit(WL1271_FLAG_JOINED, &wl->flags);
-	}
-
-	/*
-	 * The JOIN operation shuts down the firmware keep-alive as a side
-	 * effect, and the ACX_AID will start the keep-alive as a side effect.
-	 * Hence, for non-IBSS, the ACX_AID must always happen *after* the
-	 * JOIN operation, and the template config after the ACX_AID.
-	 */
-	if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
-		ret = wl1271_acx_aid(wl, wl->aid);
-		if (ret < 0)
-			goto out_sleep;
-	}
-
-	if (do_keepalive) {
-		ret = wl1271_cmd_build_klv_null_data(wl);
-		if (ret < 0)
-			goto out_sleep;
-		ret = wl1271_acx_keep_alive_config(
-			wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
-			ACX_KEEP_ALIVE_TPL_VALID);
-		if (ret < 0)
-			goto out_sleep;
 	}
 
 out_sleep:
@@ -2265,7 +2271,6 @@
 	wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval;
 
 	wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
-		IEEE80211_HW_NOISE_DBM |
 		IEEE80211_HW_BEACON_FILTER |
 		IEEE80211_HW_SUPPORTS_PS |
 		IEEE80211_HW_SUPPORTS_UAPSD |
diff --git a/drivers/ssb/driver_chipcommon.c b/drivers/ssb/driver_chipcommon.c
index 59c3c0f..59ae76b 100644
--- a/drivers/ssb/driver_chipcommon.c
+++ b/drivers/ssb/driver_chipcommon.c
@@ -233,6 +233,8 @@
 {
 	if (!cc->dev)
 		return; /* We don't have a ChipCommon */
+	if (cc->dev->id.revision >= 11)
+		cc->status = chipco_read32(cc, SSB_CHIPCO_CHIPSTAT);
 	ssb_pmu_init(cc);
 	chipco_powercontrol_init(cc);
 	ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST);
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c
index 80ff7d9..7e5b8a8 100644
--- a/drivers/ssb/main.c
+++ b/drivers/ssb/main.c
@@ -834,6 +834,9 @@
 	if (!err) {
 		ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found on "
 			   "PCI device %s\n", dev_name(&host_pci->dev));
+	} else {
+		ssb_printk(KERN_ERR PFX "Failed to register PCI version"
+			   " of SSB with error %d\n", err);
 	}
 
 	return err;
diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c
index a8dbb06..989e275 100644
--- a/drivers/ssb/pci.c
+++ b/drivers/ssb/pci.c
@@ -168,7 +168,7 @@
 }
 
 /* Get the word-offset for a SSB_SPROM_XXX define. */
-#define SPOFF(offset)	(((offset) - SSB_SPROM_BASE) / sizeof(u16))
+#define SPOFF(offset)	((offset) / sizeof(u16))
 /* Helper to extract some _offset, which is one of the SSB_SPROM_XXX defines. */
 #define SPEX16(_outvar, _offset, _mask, _shift)	\
 	out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift))
@@ -254,7 +254,7 @@
 	int i;
 
 	for (i = 0; i < bus->sprom_size; i++)
-		sprom[i] = ioread16(bus->mmio + SSB_SPROM_BASE + (i * 2));
+		sprom[i] = ioread16(bus->mmio + bus->sprom_offset + (i * 2));
 
 	return 0;
 }
@@ -285,7 +285,7 @@
 			ssb_printk("75%%");
 		else if (i % 2)
 			ssb_printk(".");
-		writew(sprom[i], bus->mmio + SSB_SPROM_BASE + (i * 2));
+		writew(sprom[i], bus->mmio + bus->sprom_offset + (i * 2));
 		mmiowb();
 		msleep(20);
 	}
@@ -621,6 +621,14 @@
 	int err = -ENOMEM;
 	u16 *buf;
 
+	if (!ssb_is_sprom_available(bus)) {
+		ssb_printk(KERN_ERR PFX "No SPROM available!\n");
+		return -ENODEV;
+	}
+
+	bus->sprom_offset = (bus->chipco.dev->id.revision < 31) ?
+		SSB_SPROM_BASE1 : SSB_SPROM_BASE31;
+
 	buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL);
 	if (!buf)
 		goto out;
diff --git a/drivers/ssb/sprom.c b/drivers/ssb/sprom.c
index f2f920f..007bc3a 100644
--- a/drivers/ssb/sprom.c
+++ b/drivers/ssb/sprom.c
@@ -176,3 +176,17 @@
 {
 	return fallback_sprom;
 }
+
+/* http://bcm-v4.sipsolutions.net/802.11/IsSpromAvailable */
+bool ssb_is_sprom_available(struct ssb_bus *bus)
+{
+	/* status register only exists on chipcomon rev >= 11 and we need check
+	   for >= 31 only */
+	/* this routine differs from specs as we do not access SPROM directly
+	   on PCMCIA */
+	if (bus->bustype == SSB_BUSTYPE_PCI &&
+	    bus->chipco.dev->id.revision >= 31)
+		return bus->chipco.capabilities & SSB_CHIPCO_CAP_SPROM;
+
+	return true;
+}
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 2ea3ede..f8750f9 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -709,6 +709,9 @@
  *	NL80211_CMD_AUTHENTICATE, NL80211_CMD_DEAUTHENTICATE,
  *	NL80211_CMD_DISASSOCIATE.
  *
+ * @NL80211_ATTR_AP_ISOLATE: (AP mode) Do not forward traffic between stations
+ *	connected to this BSS.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -864,6 +867,8 @@
 
 	NL80211_ATTR_LOCAL_STATE_CHANGE,
 
+	NL80211_ATTR_AP_ISOLATE,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
index 24f9885..a2608bf 100644
--- a/include/linux/ssb/ssb.h
+++ b/include/linux/ssb/ssb.h
@@ -305,6 +305,7 @@
 	/* ID information about the Chip. */
 	u16 chip_id;
 	u16 chip_rev;
+	u16 sprom_offset;
 	u16 sprom_size;		/* number of words in sprom */
 	u8 chip_package;
 
@@ -394,6 +395,9 @@
 
 extern void ssb_bus_unregister(struct ssb_bus *bus);
 
+/* Does the device have an SPROM? */
+extern bool ssb_is_sprom_available(struct ssb_bus *bus);
+
 /* Set a fallback SPROM.
  * See kdoc at the function definition for complete documentation. */
 extern int ssb_arch_set_fallback_sprom(const struct ssb_sprom *sprom);
diff --git a/include/linux/ssb/ssb_driver_chipcommon.h b/include/linux/ssb/ssb_driver_chipcommon.h
index 4e27acf..2cdf249 100644
--- a/include/linux/ssb/ssb_driver_chipcommon.h
+++ b/include/linux/ssb/ssb_driver_chipcommon.h
@@ -53,6 +53,7 @@
 #define  SSB_CHIPCO_CAP_64BIT		0x08000000	/* 64-bit Backplane */
 #define  SSB_CHIPCO_CAP_PMU		0x10000000	/* PMU available (rev >= 20) */
 #define  SSB_CHIPCO_CAP_ECI		0x20000000	/* ECI available (rev >= 20) */
+#define  SSB_CHIPCO_CAP_SPROM		0x40000000	/* SPROM present */
 #define SSB_CHIPCO_CORECTL		0x0008
 #define  SSB_CHIPCO_CORECTL_UARTCLK0	0x00000001	/* Drive UART with internal clock */
 #define	 SSB_CHIPCO_CORECTL_SE		0x00000002	/* sync clk out enable (corerev >= 3) */
@@ -385,6 +386,7 @@
 
 
 /** Chip specific Chip-Status register contents. */
+#define SSB_CHIPCO_CHST_4322_SPROM_EXISTS	0x00000040 /* SPROM present */
 #define SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL	0x00000003
 #define SSB_CHIPCO_CHST_4325_DEFCIS_SEL		0 /* OTP is powered up, use def. CIS, no SPROM */
 #define SSB_CHIPCO_CHST_4325_SPROM_SEL		1 /* OTP is powered up, SPROM is present */
@@ -398,6 +400,18 @@
 #define SSB_CHIPCO_CHST_4325_RCAL_VALUE_SHIFT	4
 #define SSB_CHIPCO_CHST_4325_PMUTOP_2B 		0x00000200 /* 1 for 2b, 0 for to 2a */
 
+/** Macros to determine SPROM presence based on Chip-Status register. */
+#define SSB_CHIPCO_CHST_4312_SPROM_PRESENT(status) \
+	((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \
+		SSB_CHIPCO_CHST_4325_OTP_SEL)
+#define SSB_CHIPCO_CHST_4322_SPROM_PRESENT(status) \
+	(status & SSB_CHIPCO_CHST_4322_SPROM_EXISTS)
+#define SSB_CHIPCO_CHST_4325_SPROM_PRESENT(status) \
+	(((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \
+		SSB_CHIPCO_CHST_4325_DEFCIS_SEL) && \
+	 ((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \
+		SSB_CHIPCO_CHST_4325_OTP_SEL))
+
 
 
 /** Clockcontrol masks and values **/
@@ -564,6 +578,7 @@
 struct ssb_chipcommon {
 	struct ssb_device *dev;
 	u32 capabilities;
+	u32 status;
 	/* Fast Powerup Delay constant */
 	u16 fast_pwrup_delay;
 	struct ssb_chipcommon_pmu pmu;
diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h
index 9ae9082..a6d5225 100644
--- a/include/linux/ssb/ssb_regs.h
+++ b/include/linux/ssb/ssb_regs.h
@@ -170,26 +170,27 @@
 #define SSB_SPROMSIZE_WORDS_R4		220
 #define SSB_SPROMSIZE_BYTES_R123	(SSB_SPROMSIZE_WORDS_R123 * sizeof(u16))
 #define SSB_SPROMSIZE_BYTES_R4		(SSB_SPROMSIZE_WORDS_R4 * sizeof(u16))
-#define SSB_SPROM_BASE			0x1000
-#define SSB_SPROM_REVISION		0x107E
+#define SSB_SPROM_BASE1			0x1000
+#define SSB_SPROM_BASE31		0x0800
+#define SSB_SPROM_REVISION		0x007E
 #define  SSB_SPROM_REVISION_REV		0x00FF	/* SPROM Revision number */
 #define  SSB_SPROM_REVISION_CRC		0xFF00	/* SPROM CRC8 value */
 #define  SSB_SPROM_REVISION_CRC_SHIFT	8
 
 /* SPROM Revision 1 */
-#define SSB_SPROM1_SPID			0x1004	/* Subsystem Product ID for PCI */
-#define SSB_SPROM1_SVID			0x1006	/* Subsystem Vendor ID for PCI */
-#define SSB_SPROM1_PID			0x1008	/* Product ID for PCI */
-#define SSB_SPROM1_IL0MAC		0x1048	/* 6 bytes MAC address for 802.11b/g */
-#define SSB_SPROM1_ET0MAC		0x104E	/* 6 bytes MAC address for Ethernet */
-#define SSB_SPROM1_ET1MAC		0x1054	/* 6 bytes MAC address for 802.11a */
-#define SSB_SPROM1_ETHPHY		0x105A	/* Ethernet PHY settings */
+#define SSB_SPROM1_SPID			0x0004	/* Subsystem Product ID for PCI */
+#define SSB_SPROM1_SVID			0x0006	/* Subsystem Vendor ID for PCI */
+#define SSB_SPROM1_PID			0x0008	/* Product ID for PCI */
+#define SSB_SPROM1_IL0MAC		0x0048	/* 6 bytes MAC address for 802.11b/g */
+#define SSB_SPROM1_ET0MAC		0x004E	/* 6 bytes MAC address for Ethernet */
+#define SSB_SPROM1_ET1MAC		0x0054	/* 6 bytes MAC address for 802.11a */
+#define SSB_SPROM1_ETHPHY		0x005A	/* Ethernet PHY settings */
 #define  SSB_SPROM1_ETHPHY_ET0A		0x001F	/* MII Address for enet0 */
 #define  SSB_SPROM1_ETHPHY_ET1A		0x03E0	/* MII Address for enet1 */
 #define  SSB_SPROM1_ETHPHY_ET1A_SHIFT	5
 #define  SSB_SPROM1_ETHPHY_ET0M		(1<<14)	/* MDIO for enet0 */
 #define  SSB_SPROM1_ETHPHY_ET1M		(1<<15)	/* MDIO for enet1 */
-#define SSB_SPROM1_BINF			0x105C	/* Board info */
+#define SSB_SPROM1_BINF			0x005C	/* Board info */
 #define  SSB_SPROM1_BINF_BREV		0x00FF	/* Board Revision */
 #define  SSB_SPROM1_BINF_CCODE		0x0F00	/* Country Code */
 #define  SSB_SPROM1_BINF_CCODE_SHIFT	8
@@ -197,63 +198,63 @@
 #define  SSB_SPROM1_BINF_ANTBG_SHIFT	12
 #define  SSB_SPROM1_BINF_ANTA		0xC000	/* Available A-PHY antennas */
 #define  SSB_SPROM1_BINF_ANTA_SHIFT	14
-#define SSB_SPROM1_PA0B0		0x105E
-#define SSB_SPROM1_PA0B1		0x1060
-#define SSB_SPROM1_PA0B2		0x1062
-#define SSB_SPROM1_GPIOA		0x1064	/* General Purpose IO pins 0 and 1 */
+#define SSB_SPROM1_PA0B0		0x005E
+#define SSB_SPROM1_PA0B1		0x0060
+#define SSB_SPROM1_PA0B2		0x0062
+#define SSB_SPROM1_GPIOA		0x0064	/* General Purpose IO pins 0 and 1 */
 #define  SSB_SPROM1_GPIOA_P0		0x00FF	/* Pin 0 */
 #define  SSB_SPROM1_GPIOA_P1		0xFF00	/* Pin 1 */
 #define  SSB_SPROM1_GPIOA_P1_SHIFT	8
-#define SSB_SPROM1_GPIOB		0x1066	/* General Purpuse IO pins 2 and 3 */
+#define SSB_SPROM1_GPIOB		0x0066	/* General Purpuse IO pins 2 and 3 */
 #define  SSB_SPROM1_GPIOB_P2		0x00FF	/* Pin 2 */
 #define  SSB_SPROM1_GPIOB_P3		0xFF00	/* Pin 3 */
 #define  SSB_SPROM1_GPIOB_P3_SHIFT	8
-#define SSB_SPROM1_MAXPWR		0x1068	/* Power Amplifier Max Power */
+#define SSB_SPROM1_MAXPWR		0x0068	/* Power Amplifier Max Power */
 #define  SSB_SPROM1_MAXPWR_BG		0x00FF	/* B-PHY and G-PHY (in dBm Q5.2) */
 #define  SSB_SPROM1_MAXPWR_A		0xFF00	/* A-PHY (in dBm Q5.2) */
 #define  SSB_SPROM1_MAXPWR_A_SHIFT	8
-#define SSB_SPROM1_PA1B0		0x106A
-#define SSB_SPROM1_PA1B1		0x106C
-#define SSB_SPROM1_PA1B2		0x106E
-#define SSB_SPROM1_ITSSI		0x1070	/* Idle TSSI Target */
+#define SSB_SPROM1_PA1B0		0x006A
+#define SSB_SPROM1_PA1B1		0x006C
+#define SSB_SPROM1_PA1B2		0x006E
+#define SSB_SPROM1_ITSSI		0x0070	/* Idle TSSI Target */
 #define  SSB_SPROM1_ITSSI_BG		0x00FF	/* B-PHY and G-PHY*/
 #define  SSB_SPROM1_ITSSI_A		0xFF00	/* A-PHY */
 #define  SSB_SPROM1_ITSSI_A_SHIFT	8
-#define SSB_SPROM1_BFLLO		0x1072	/* Boardflags (low 16 bits) */
-#define SSB_SPROM1_AGAIN		0x1074	/* Antenna Gain (in dBm Q5.2) */
+#define SSB_SPROM1_BFLLO		0x0072	/* Boardflags (low 16 bits) */
+#define SSB_SPROM1_AGAIN		0x0074	/* Antenna Gain (in dBm Q5.2) */
 #define  SSB_SPROM1_AGAIN_BG		0x00FF	/* B-PHY and G-PHY */
 #define  SSB_SPROM1_AGAIN_BG_SHIFT	0
 #define  SSB_SPROM1_AGAIN_A		0xFF00	/* A-PHY */
 #define  SSB_SPROM1_AGAIN_A_SHIFT	8
 
 /* SPROM Revision 2 (inherits from rev 1) */
-#define SSB_SPROM2_BFLHI		0x1038	/* Boardflags (high 16 bits) */
-#define SSB_SPROM2_MAXP_A		0x103A	/* A-PHY Max Power */
+#define SSB_SPROM2_BFLHI		0x0038	/* Boardflags (high 16 bits) */
+#define SSB_SPROM2_MAXP_A		0x003A	/* A-PHY Max Power */
 #define  SSB_SPROM2_MAXP_A_HI		0x00FF	/* Max Power High */
 #define  SSB_SPROM2_MAXP_A_LO		0xFF00	/* Max Power Low */
 #define  SSB_SPROM2_MAXP_A_LO_SHIFT	8
-#define SSB_SPROM2_PA1LOB0		0x103C	/* A-PHY PowerAmplifier Low Settings */
-#define SSB_SPROM2_PA1LOB1		0x103E	/* A-PHY PowerAmplifier Low Settings */
-#define SSB_SPROM2_PA1LOB2		0x1040	/* A-PHY PowerAmplifier Low Settings */
-#define SSB_SPROM2_PA1HIB0		0x1042	/* A-PHY PowerAmplifier High Settings */
-#define SSB_SPROM2_PA1HIB1		0x1044	/* A-PHY PowerAmplifier High Settings */
-#define SSB_SPROM2_PA1HIB2		0x1046	/* A-PHY PowerAmplifier High Settings */
-#define SSB_SPROM2_OPO			0x1078	/* OFDM Power Offset from CCK Level */
+#define SSB_SPROM2_PA1LOB0		0x003C	/* A-PHY PowerAmplifier Low Settings */
+#define SSB_SPROM2_PA1LOB1		0x003E	/* A-PHY PowerAmplifier Low Settings */
+#define SSB_SPROM2_PA1LOB2		0x0040	/* A-PHY PowerAmplifier Low Settings */
+#define SSB_SPROM2_PA1HIB0		0x0042	/* A-PHY PowerAmplifier High Settings */
+#define SSB_SPROM2_PA1HIB1		0x0044	/* A-PHY PowerAmplifier High Settings */
+#define SSB_SPROM2_PA1HIB2		0x0046	/* A-PHY PowerAmplifier High Settings */
+#define SSB_SPROM2_OPO			0x0078	/* OFDM Power Offset from CCK Level */
 #define  SSB_SPROM2_OPO_VALUE		0x00FF
 #define  SSB_SPROM2_OPO_UNUSED		0xFF00
-#define SSB_SPROM2_CCODE		0x107C	/* Two char Country Code */
+#define SSB_SPROM2_CCODE		0x007C	/* Two char Country Code */
 
 /* SPROM Revision 3 (inherits most data from rev 2) */
-#define SSB_SPROM3_IL0MAC		0x104A	/* 6 bytes MAC address for 802.11b/g */
-#define SSB_SPROM3_OFDMAPO		0x102C	/* A-PHY OFDM Mid Power Offset (4 bytes, BigEndian) */
-#define SSB_SPROM3_OFDMALPO		0x1030	/* A-PHY OFDM Low Power Offset (4 bytes, BigEndian) */
-#define SSB_SPROM3_OFDMAHPO		0x1034	/* A-PHY OFDM High Power Offset (4 bytes, BigEndian) */
-#define SSB_SPROM3_GPIOLDC		0x1042	/* GPIO LED Powersave Duty Cycle (4 bytes, BigEndian) */
+#define SSB_SPROM3_OFDMAPO		0x002C	/* A-PHY OFDM Mid Power Offset (4 bytes, BigEndian) */
+#define SSB_SPROM3_OFDMALPO		0x0030	/* A-PHY OFDM Low Power Offset (4 bytes, BigEndian) */
+#define SSB_SPROM3_OFDMAHPO		0x0034	/* A-PHY OFDM High Power Offset (4 bytes, BigEndian) */
+#define SSB_SPROM3_GPIOLDC		0x0042	/* GPIO LED Powersave Duty Cycle (4 bytes, BigEndian) */
 #define  SSB_SPROM3_GPIOLDC_OFF		0x0000FF00	/* Off Count */
 #define  SSB_SPROM3_GPIOLDC_OFF_SHIFT	8
 #define  SSB_SPROM3_GPIOLDC_ON		0x00FF0000	/* On Count */
 #define  SSB_SPROM3_GPIOLDC_ON_SHIFT	16
-#define SSB_SPROM3_CCKPO		0x1078	/* CCK Power Offset */
+#define SSB_SPROM3_IL0MAC		0x004A	/* 6 bytes MAC address for 802.11b/g */
+#define SSB_SPROM3_CCKPO		0x0078	/* CCK Power Offset */
 #define  SSB_SPROM3_CCKPO_1M		0x000F	/* 1M Rate PO */
 #define  SSB_SPROM3_CCKPO_2M		0x00F0	/* 2M Rate PO */
 #define  SSB_SPROM3_CCKPO_2M_SHIFT	4
@@ -264,100 +265,100 @@
 #define  SSB_SPROM3_OFDMGPO		0x107A	/* G-PHY OFDM Power Offset (4 bytes, BigEndian) */
 
 /* SPROM Revision 4 */
-#define SSB_SPROM4_IL0MAC		0x104C	/* 6 byte MAC address for a/b/g/n */
-#define SSB_SPROM4_ETHPHY		0x105A	/* Ethernet PHY settings ?? */
+#define SSB_SPROM4_BFLLO		0x0044	/* Boardflags (low 16 bits) */
+#define SSB_SPROM4_BFLHI		0x0046  /* Board Flags Hi */
+#define SSB_SPROM4_IL0MAC		0x004C	/* 6 byte MAC address for a/b/g/n */
+#define SSB_SPROM4_CCODE		0x0052	/* Country Code (2 bytes) */
+#define SSB_SPROM4_GPIOA		0x0056	/* Gen. Purpose IO # 0 and 1 */
+#define  SSB_SPROM4_GPIOA_P0		0x00FF	/* Pin 0 */
+#define  SSB_SPROM4_GPIOA_P1		0xFF00	/* Pin 1 */
+#define  SSB_SPROM4_GPIOA_P1_SHIFT	8
+#define SSB_SPROM4_GPIOB		0x0058	/* Gen. Purpose IO # 2 and 3 */
+#define  SSB_SPROM4_GPIOB_P2		0x00FF	/* Pin 2 */
+#define  SSB_SPROM4_GPIOB_P3		0xFF00	/* Pin 3 */
+#define  SSB_SPROM4_GPIOB_P3_SHIFT	8
+#define SSB_SPROM4_ETHPHY		0x005A	/* Ethernet PHY settings ?? */
 #define  SSB_SPROM4_ETHPHY_ET0A		0x001F	/* MII Address for enet0 */
 #define  SSB_SPROM4_ETHPHY_ET1A		0x03E0	/* MII Address for enet1 */
 #define  SSB_SPROM4_ETHPHY_ET1A_SHIFT	5
 #define  SSB_SPROM4_ETHPHY_ET0M		(1<<14)	/* MDIO for enet0 */
 #define  SSB_SPROM4_ETHPHY_ET1M		(1<<15)	/* MDIO for enet1 */
-#define SSB_SPROM4_CCODE		0x1052	/* Country Code (2 bytes) */
-#define SSB_SPROM4_ANTAVAIL		0x105D  /* Antenna available bitfields */
-#define SSB_SPROM4_ANTAVAIL_A		0x00FF	/* A-PHY bitfield */
-#define SSB_SPROM4_ANTAVAIL_A_SHIFT	0
-#define SSB_SPROM4_ANTAVAIL_BG		0xFF00	/* B-PHY and G-PHY bitfield */
-#define SSB_SPROM4_ANTAVAIL_BG_SHIFT	8
-#define SSB_SPROM4_BFLLO		0x1044	/* Boardflags (low 16 bits) */
-#define SSB_SPROM4_AGAIN01		0x105E	/* Antenna Gain (in dBm Q5.2) */
+#define SSB_SPROM4_ANTAVAIL		0x005D  /* Antenna available bitfields */
+#define  SSB_SPROM4_ANTAVAIL_A		0x00FF	/* A-PHY bitfield */
+#define  SSB_SPROM4_ANTAVAIL_A_SHIFT	0
+#define  SSB_SPROM4_ANTAVAIL_BG		0xFF00	/* B-PHY and G-PHY bitfield */
+#define  SSB_SPROM4_ANTAVAIL_BG_SHIFT	8
+#define SSB_SPROM4_AGAIN01		0x005E	/* Antenna Gain (in dBm Q5.2) */
 #define  SSB_SPROM4_AGAIN0		0x00FF	/* Antenna 0 */
 #define  SSB_SPROM4_AGAIN0_SHIFT	0
 #define  SSB_SPROM4_AGAIN1		0xFF00	/* Antenna 1 */
 #define  SSB_SPROM4_AGAIN1_SHIFT	8
-#define SSB_SPROM4_AGAIN23		0x1060
+#define SSB_SPROM4_AGAIN23		0x0060
 #define  SSB_SPROM4_AGAIN2		0x00FF	/* Antenna 2 */
 #define  SSB_SPROM4_AGAIN2_SHIFT	0
 #define  SSB_SPROM4_AGAIN3		0xFF00	/* Antenna 3 */
 #define  SSB_SPROM4_AGAIN3_SHIFT	8
-#define SSB_SPROM4_BFLHI		0x1046  /* Board Flags Hi */
-#define SSB_SPROM4_MAXP_BG		0x1080  /* Max Power BG in path 1 */
+#define SSB_SPROM4_MAXP_BG		0x0080  /* Max Power BG in path 1 */
 #define  SSB_SPROM4_MAXP_BG_MASK	0x00FF  /* Mask for Max Power BG */
 #define  SSB_SPROM4_ITSSI_BG		0xFF00	/* Mask for path 1 itssi_bg */
 #define  SSB_SPROM4_ITSSI_BG_SHIFT	8
-#define SSB_SPROM4_MAXP_A		0x108A  /* Max Power A in path 1 */
+#define SSB_SPROM4_MAXP_A		0x008A  /* Max Power A in path 1 */
 #define  SSB_SPROM4_MAXP_A_MASK		0x00FF  /* Mask for Max Power A */
 #define  SSB_SPROM4_ITSSI_A		0xFF00	/* Mask for path 1 itssi_a */
 #define  SSB_SPROM4_ITSSI_A_SHIFT	8
-#define SSB_SPROM4_GPIOA		0x1056	/* Gen. Purpose IO # 0 and 1 */
-#define  SSB_SPROM4_GPIOA_P0		0x00FF	/* Pin 0 */
-#define  SSB_SPROM4_GPIOA_P1		0xFF00	/* Pin 1 */
-#define  SSB_SPROM4_GPIOA_P1_SHIFT	8
-#define SSB_SPROM4_GPIOB		0x1058	/* Gen. Purpose IO # 2 and 3 */
-#define  SSB_SPROM4_GPIOB_P2		0x00FF	/* Pin 2 */
-#define  SSB_SPROM4_GPIOB_P3		0xFF00	/* Pin 3 */
-#define  SSB_SPROM4_GPIOB_P3_SHIFT	8
-#define SSB_SPROM4_PA0B0		0x1082	/* The paXbY locations are */
-#define SSB_SPROM4_PA0B1		0x1084	/*   only guesses */
-#define SSB_SPROM4_PA0B2		0x1086
-#define SSB_SPROM4_PA1B0		0x108E
-#define SSB_SPROM4_PA1B1		0x1090
-#define SSB_SPROM4_PA1B2		0x1092
+#define SSB_SPROM4_PA0B0		0x0082	/* The paXbY locations are */
+#define SSB_SPROM4_PA0B1		0x0084	/*   only guesses */
+#define SSB_SPROM4_PA0B2		0x0086
+#define SSB_SPROM4_PA1B0		0x008E
+#define SSB_SPROM4_PA1B1		0x0090
+#define SSB_SPROM4_PA1B2		0x0092
 
 /* SPROM Revision 5 (inherits most data from rev 4) */
-#define SSB_SPROM5_BFLLO		0x104A	/* Boardflags (low 16 bits) */
-#define SSB_SPROM5_BFLHI		0x104C  /* Board Flags Hi */
-#define SSB_SPROM5_IL0MAC		0x1052	/* 6 byte MAC address for a/b/g/n */
-#define SSB_SPROM5_CCODE		0x1044	/* Country Code (2 bytes) */
-#define SSB_SPROM5_GPIOA		0x1076	/* Gen. Purpose IO # 0 and 1 */
+#define SSB_SPROM5_CCODE		0x0044	/* Country Code (2 bytes) */
+#define SSB_SPROM5_BFLLO		0x004A	/* Boardflags (low 16 bits) */
+#define SSB_SPROM5_BFLHI		0x004C  /* Board Flags Hi */
+#define SSB_SPROM5_IL0MAC		0x0052	/* 6 byte MAC address for a/b/g/n */
+#define SSB_SPROM5_GPIOA		0x0076	/* Gen. Purpose IO # 0 and 1 */
 #define  SSB_SPROM5_GPIOA_P0		0x00FF	/* Pin 0 */
 #define  SSB_SPROM5_GPIOA_P1		0xFF00	/* Pin 1 */
 #define  SSB_SPROM5_GPIOA_P1_SHIFT	8
-#define SSB_SPROM5_GPIOB		0x1078	/* Gen. Purpose IO # 2 and 3 */
+#define SSB_SPROM5_GPIOB		0x0078	/* Gen. Purpose IO # 2 and 3 */
 #define  SSB_SPROM5_GPIOB_P2		0x00FF	/* Pin 2 */
 #define  SSB_SPROM5_GPIOB_P3		0xFF00	/* Pin 3 */
 #define  SSB_SPROM5_GPIOB_P3_SHIFT	8
 
 /* SPROM Revision 8 */
-#define SSB_SPROM8_BOARDREV		0x1082	/* Board revision */
-#define SSB_SPROM8_BFLLO		0x1084	/* Board flags (bits 0-15) */
-#define SSB_SPROM8_BFLHI		0x1086	/* Board flags (bits 16-31) */
-#define SSB_SPROM8_BFL2LO		0x1088	/* Board flags (bits 32-47) */
-#define SSB_SPROM8_BFL2HI		0x108A	/* Board flags (bits 48-63) */
-#define SSB_SPROM8_IL0MAC		0x108C	/* 6 byte MAC address */
-#define SSB_SPROM8_CCODE		0x1092	/* 2 byte country code */
-#define SSB_SPROM8_ANTAVAIL		0x109C  /* Antenna available bitfields*/
-#define SSB_SPROM8_ANTAVAIL_A		0xFF00	/* A-PHY bitfield */
-#define SSB_SPROM8_ANTAVAIL_A_SHIFT	8
-#define SSB_SPROM8_ANTAVAIL_BG		0x00FF	/* B-PHY and G-PHY bitfield */
-#define SSB_SPROM8_ANTAVAIL_BG_SHIFT	0
-#define SSB_SPROM8_AGAIN01		0x109E	/* Antenna Gain (in dBm Q5.2) */
+#define SSB_SPROM8_BOARDREV		0x0082	/* Board revision */
+#define SSB_SPROM8_BFLLO		0x0084	/* Board flags (bits 0-15) */
+#define SSB_SPROM8_BFLHI		0x0086	/* Board flags (bits 16-31) */
+#define SSB_SPROM8_BFL2LO		0x0088	/* Board flags (bits 32-47) */
+#define SSB_SPROM8_BFL2HI		0x008A	/* Board flags (bits 48-63) */
+#define SSB_SPROM8_IL0MAC		0x008C	/* 6 byte MAC address */
+#define SSB_SPROM8_CCODE		0x0092	/* 2 byte country code */
+#define SSB_SPROM8_GPIOA		0x0096	/*Gen. Purpose IO # 0 and 1 */
+#define  SSB_SPROM8_GPIOA_P0		0x00FF	/* Pin 0 */
+#define  SSB_SPROM8_GPIOA_P1		0xFF00	/* Pin 1 */
+#define  SSB_SPROM8_GPIOA_P1_SHIFT	8
+#define SSB_SPROM8_GPIOB		0x0098	/* Gen. Purpose IO # 2 and 3 */
+#define  SSB_SPROM8_GPIOB_P2		0x00FF	/* Pin 2 */
+#define  SSB_SPROM8_GPIOB_P3		0xFF00	/* Pin 3 */
+#define  SSB_SPROM8_GPIOB_P3_SHIFT	8
+#define SSB_SPROM8_ANTAVAIL		0x009C  /* Antenna available bitfields*/
+#define  SSB_SPROM8_ANTAVAIL_A		0xFF00	/* A-PHY bitfield */
+#define  SSB_SPROM8_ANTAVAIL_A_SHIFT	8
+#define  SSB_SPROM8_ANTAVAIL_BG		0x00FF	/* B-PHY and G-PHY bitfield */
+#define  SSB_SPROM8_ANTAVAIL_BG_SHIFT	0
+#define SSB_SPROM8_AGAIN01		0x009E	/* Antenna Gain (in dBm Q5.2) */
 #define  SSB_SPROM8_AGAIN0		0x00FF	/* Antenna 0 */
 #define  SSB_SPROM8_AGAIN0_SHIFT	0
 #define  SSB_SPROM8_AGAIN1		0xFF00	/* Antenna 1 */
 #define  SSB_SPROM8_AGAIN1_SHIFT	8
-#define SSB_SPROM8_AGAIN23		0x10A0
+#define SSB_SPROM8_AGAIN23		0x00A0
 #define  SSB_SPROM8_AGAIN2		0x00FF	/* Antenna 2 */
 #define  SSB_SPROM8_AGAIN2_SHIFT	0
 #define  SSB_SPROM8_AGAIN3		0xFF00	/* Antenna 3 */
 #define  SSB_SPROM8_AGAIN3_SHIFT	8
-#define SSB_SPROM8_GPIOA		0x1096	/*Gen. Purpose IO # 0 and 1 */
-#define  SSB_SPROM8_GPIOA_P0		0x00FF	/* Pin 0 */
-#define  SSB_SPROM8_GPIOA_P1		0xFF00	/* Pin 1 */
-#define  SSB_SPROM8_GPIOA_P1_SHIFT	8
-#define SSB_SPROM8_GPIOB		0x1098	/* Gen. Purpose IO # 2 and 3 */
-#define  SSB_SPROM8_GPIOB_P2		0x00FF	/* Pin 2 */
-#define  SSB_SPROM8_GPIOB_P3		0xFF00	/* Pin 3 */
-#define  SSB_SPROM8_GPIOB_P3_SHIFT	8
-#define SSB_SPROM8_RSSIPARM2G		0x10A4	/* RSSI params for 2GHz */
+#define SSB_SPROM8_RSSIPARM2G		0x00A4	/* RSSI params for 2GHz */
 #define  SSB_SPROM8_RSSISMF2G		0x000F
 #define  SSB_SPROM8_RSSISMC2G		0x00F0
 #define  SSB_SPROM8_RSSISMC2G_SHIFT	4
@@ -365,7 +366,7 @@
 #define  SSB_SPROM8_RSSISAV2G_SHIFT	8
 #define  SSB_SPROM8_BXA2G		0x1800
 #define  SSB_SPROM8_BXA2G_SHIFT		11
-#define SSB_SPROM8_RSSIPARM5G		0x10A6	/* RSSI params for 5GHz */
+#define SSB_SPROM8_RSSIPARM5G		0x00A6	/* RSSI params for 5GHz */
 #define  SSB_SPROM8_RSSISMF5G		0x000F
 #define  SSB_SPROM8_RSSISMC5G		0x00F0
 #define  SSB_SPROM8_RSSISMC5G_SHIFT	4
@@ -373,47 +374,47 @@
 #define  SSB_SPROM8_RSSISAV5G_SHIFT	8
 #define  SSB_SPROM8_BXA5G		0x1800
 #define  SSB_SPROM8_BXA5G_SHIFT		11
-#define SSB_SPROM8_TRI25G		0x10A8	/* TX isolation 2.4&5.3GHz */
+#define SSB_SPROM8_TRI25G		0x00A8	/* TX isolation 2.4&5.3GHz */
 #define  SSB_SPROM8_TRI2G		0x00FF	/* TX isolation 2.4GHz */
 #define  SSB_SPROM8_TRI5G		0xFF00	/* TX isolation 5.3GHz */
 #define  SSB_SPROM8_TRI5G_SHIFT		8
-#define SSB_SPROM8_TRI5GHL		0x10AA	/* TX isolation 5.2/5.8GHz */
+#define SSB_SPROM8_TRI5GHL		0x00AA	/* TX isolation 5.2/5.8GHz */
 #define  SSB_SPROM8_TRI5GL		0x00FF	/* TX isolation 5.2GHz */
 #define  SSB_SPROM8_TRI5GH		0xFF00	/* TX isolation 5.8GHz */
 #define  SSB_SPROM8_TRI5GH_SHIFT	8
-#define SSB_SPROM8_RXPO			0x10AC  /* RX power offsets */
+#define SSB_SPROM8_RXPO			0x00AC  /* RX power offsets */
 #define  SSB_SPROM8_RXPO2G		0x00FF	/* 2GHz RX power offset */
 #define  SSB_SPROM8_RXPO5G		0xFF00	/* 5GHz RX power offset */
 #define  SSB_SPROM8_RXPO5G_SHIFT	8
-#define SSB_SPROM8_MAXP_BG		0x10C0  /* Max Power 2GHz in path 1 */
+#define SSB_SPROM8_MAXP_BG		0x00C0  /* Max Power 2GHz in path 1 */
 #define  SSB_SPROM8_MAXP_BG_MASK	0x00FF  /* Mask for Max Power 2GHz */
 #define  SSB_SPROM8_ITSSI_BG		0xFF00	/* Mask for path 1 itssi_bg */
 #define  SSB_SPROM8_ITSSI_BG_SHIFT	8
-#define SSB_SPROM8_PA0B0		0x10C2	/* 2GHz power amp settings */
-#define SSB_SPROM8_PA0B1		0x10C4
-#define SSB_SPROM8_PA0B2		0x10C6
-#define SSB_SPROM8_MAXP_A		0x10C8  /* Max Power 5.3GHz */
+#define SSB_SPROM8_PA0B0		0x00C2	/* 2GHz power amp settings */
+#define SSB_SPROM8_PA0B1		0x00C4
+#define SSB_SPROM8_PA0B2		0x00C6
+#define SSB_SPROM8_MAXP_A		0x00C8  /* Max Power 5.3GHz */
 #define  SSB_SPROM8_MAXP_A_MASK		0x00FF  /* Mask for Max Power 5.3GHz */
 #define  SSB_SPROM8_ITSSI_A		0xFF00	/* Mask for path 1 itssi_a */
 #define  SSB_SPROM8_ITSSI_A_SHIFT	8
-#define SSB_SPROM8_MAXP_AHL		0x10CA  /* Max Power 5.2/5.8GHz */
+#define SSB_SPROM8_MAXP_AHL		0x00CA  /* Max Power 5.2/5.8GHz */
 #define  SSB_SPROM8_MAXP_AH_MASK	0x00FF  /* Mask for Max Power 5.8GHz */
 #define  SSB_SPROM8_MAXP_AL_MASK	0xFF00  /* Mask for Max Power 5.2GHz */
 #define  SSB_SPROM8_MAXP_AL_SHIFT	8
-#define SSB_SPROM8_PA1B0		0x10CC	/* 5.3GHz power amp settings */
-#define SSB_SPROM8_PA1B1		0x10CE
-#define SSB_SPROM8_PA1B2		0x10D0
-#define SSB_SPROM8_PA1LOB0		0x10D2	/* 5.2GHz power amp settings */
-#define SSB_SPROM8_PA1LOB1		0x10D4
-#define SSB_SPROM8_PA1LOB2		0x10D6
-#define SSB_SPROM8_PA1HIB0		0x10D8	/* 5.8GHz power amp settings */
-#define SSB_SPROM8_PA1HIB1		0x10DA
-#define SSB_SPROM8_PA1HIB2		0x10DC
-#define SSB_SPROM8_CCK2GPO		0x1140	/* CCK power offset */
-#define SSB_SPROM8_OFDM2GPO		0x1142	/* 2.4GHz OFDM power offset */
-#define SSB_SPROM8_OFDM5GPO		0x1146	/* 5.3GHz OFDM power offset */
-#define SSB_SPROM8_OFDM5GLPO		0x114A	/* 5.2GHz OFDM power offset */
-#define SSB_SPROM8_OFDM5GHPO		0x114E	/* 5.8GHz OFDM power offset */
+#define SSB_SPROM8_PA1B0		0x00CC	/* 5.3GHz power amp settings */
+#define SSB_SPROM8_PA1B1		0x00CE
+#define SSB_SPROM8_PA1B2		0x00D0
+#define SSB_SPROM8_PA1LOB0		0x00D2	/* 5.2GHz power amp settings */
+#define SSB_SPROM8_PA1LOB1		0x00D4
+#define SSB_SPROM8_PA1LOB2		0x00D6
+#define SSB_SPROM8_PA1HIB0		0x00D8	/* 5.8GHz power amp settings */
+#define SSB_SPROM8_PA1HIB1		0x00DA
+#define SSB_SPROM8_PA1HIB2		0x00DC
+#define SSB_SPROM8_CCK2GPO		0x0140	/* CCK power offset */
+#define SSB_SPROM8_OFDM2GPO		0x0142	/* 2.4GHz OFDM power offset */
+#define SSB_SPROM8_OFDM5GPO		0x0146	/* 5.3GHz OFDM power offset */
+#define SSB_SPROM8_OFDM5GLPO		0x014A	/* 5.2GHz OFDM power offset */
+#define SSB_SPROM8_OFDM5GHPO		0x014E	/* 5.8GHz OFDM power offset */
 
 /* Values for SSB_SPROM1_BINF_CCODE */
 enum {
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 5a4efe5..7d10c01 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -511,6 +511,7 @@
  * @basic_rates: basic rates in IEEE 802.11 format
  *	(or NULL for no change)
  * @basic_rates_len: number of basic rates
+ * @ap_isolate: do not forward packets between connected stations
  */
 struct bss_parameters {
 	int use_cts_prot;
@@ -518,6 +519,7 @@
 	int use_short_slot_time;
 	u8 *basic_rates;
 	u8 basic_rates_len;
+	int ap_isolate;
 };
 
 struct mesh_config {
@@ -1018,6 +1020,8 @@
  *	RSN IE. It allows for faster roaming between WPA2 BSSIDs.
  * @del_pmksa: Delete a cached PMKID.
  * @flush_pmksa: Flush all cached PMKIDs.
+ * @set_power_mgmt: Configure WLAN power management. A timeout value of -1
+ *	allows the driver to adjust the dynamic ps timeout value.
  * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold.
  *
  */
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 78908b5..54aa16b 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -145,6 +145,7 @@
  * @BSS_CHANGED_BEACON_ENABLED: Beaconing should be
  *	enabled/disabled (beaconing modes)
  * @BSS_CHANGED_CQM: Connection quality monitor config changed
+ * @BSS_CHANGED_IBSS: IBSS join status changed
  */
 enum ieee80211_bss_change {
 	BSS_CHANGED_ASSOC		= 1<<0,
@@ -158,6 +159,7 @@
 	BSS_CHANGED_BEACON		= 1<<8,
 	BSS_CHANGED_BEACON_ENABLED	= 1<<9,
 	BSS_CHANGED_CQM			= 1<<10,
+	BSS_CHANGED_IBSS		= 1<<11,
 };
 
 /**
@@ -167,6 +169,8 @@
  * to that BSS) that can change during the lifetime of the BSS.
  *
  * @assoc: association status
+ * @ibss_joined: indicates whether this station is part of an IBSS
+ *	or not
  * @aid: association ID number, valid only when @assoc is true
  * @use_cts_prot: use CTS protection
  * @use_short_preamble: use 802.11b short preamble;
@@ -194,7 +198,7 @@
 struct ieee80211_bss_conf {
 	const u8 *bssid;
 	/* association related data */
-	bool assoc;
+	bool assoc, ibss_joined;
 	u16 aid;
 	/* erp related data */
 	bool use_cts_prot;
@@ -556,7 +560,6 @@
  * @signal: signal strength when receiving this frame, either in dBm, in dB or
  *	unspecified depending on the hardware capabilities flags
  *	@IEEE80211_HW_SIGNAL_*
- * @noise: noise when receiving this frame, in dBm (DEPRECATED).
  * @antenna: antenna used
  * @rate_idx: index of data rate into band's supported rates or MCS index if
  *	HT rates are use (RX_FLAG_HT)
@@ -567,7 +570,6 @@
 	enum ieee80211_band band;
 	int freq;
 	int signal;
-	int noise __deprecated;
 	int antenna;
 	int rate_idx;
 	int flag;
@@ -668,6 +670,9 @@
  * @dynamic_ps_timeout: The dynamic powersave timeout (in ms), see the
  *	powersave documentation below. This variable is valid only when
  *	the CONF_PS flag is set.
+ * @dynamic_ps_forced_timeout: The dynamic powersave timeout (in ms) configured
+ *	by cfg80211 (essentially, wext) If set, this value overrules the value
+ *	chosen by mac80211 based on ps qos network latency.
  *
  * @power_level: requested transmit power (in dBm)
  *
@@ -687,7 +692,7 @@
  */
 struct ieee80211_conf {
 	u32 flags;
-	int power_level, dynamic_ps_timeout;
+	int power_level, dynamic_ps_timeout, dynamic_ps_forced_timeout;
 	int max_sleep_period;
 
 	u16 listen_interval;
@@ -927,10 +932,6 @@
  *	one milliwatt. This is the preferred method since it is standardized
  *	between different devices. @max_signal does not need to be set.
  *
- * @IEEE80211_HW_NOISE_DBM:
- *	Hardware can provide noise (radio interference) values in units dBm,
- *      decibel difference from one milliwatt.
- *
  * @IEEE80211_HW_SPECTRUM_MGMT:
  * 	Hardware supports spectrum management defined in 802.11h
  * 	Measurement, Channel Switch, Quieting, TPC
@@ -994,7 +995,7 @@
 	IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE	= 1<<4,
 	IEEE80211_HW_SIGNAL_UNSPEC			= 1<<5,
 	IEEE80211_HW_SIGNAL_DBM				= 1<<6,
-	IEEE80211_HW_NOISE_DBM				= 1<<7,
+	/* use this hole */
 	IEEE80211_HW_SPECTRUM_MGMT			= 1<<8,
 	IEEE80211_HW_AMPDU_AGGREGATION			= 1<<9,
 	IEEE80211_HW_SUPPORTS_PS			= 1<<10,
@@ -1654,7 +1655,7 @@
 				struct ieee80211_key_conf *conf,
 				struct ieee80211_sta *sta,
 				u32 iv32, u16 *phase1key);
-	int (*hw_scan)(struct ieee80211_hw *hw,
+	int (*hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 		       struct cfg80211_scan_request *req);
 	void (*sw_scan_start)(struct ieee80211_hw *hw);
 	void (*sw_scan_complete)(struct ieee80211_hw *hw);
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 845a6e6..ae37270 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -97,9 +97,6 @@
 					    params->mesh_id_len,
 					    params->mesh_id);
 
-	if (sdata->vif.type != NL80211_IFTYPE_MONITOR || !flags)
-		return 0;
-
 	if (type == NL80211_IFTYPE_AP_VLAN &&
 	    params && params->use_4addr == 0)
 		rcu_assign_pointer(sdata->u.vlan.sta, NULL);
@@ -107,7 +104,9 @@
 		 params && params->use_4addr >= 0)
 		sdata->u.mgd.use_4addr = params->use_4addr;
 
-	sdata->u.mntr_flags = *flags;
+	if (sdata->vif.type == NL80211_IFTYPE_MONITOR && flags)
+		sdata->u.mntr_flags = *flags;
+
 	return 0;
 }
 
@@ -1115,6 +1114,13 @@
 		changed |= BSS_CHANGED_BASIC_RATES;
 	}
 
+	if (params->ap_isolate >= 0) {
+		if (params->ap_isolate)
+			sdata->flags |= IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
+		else
+			sdata->flags &= ~IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
+	}
+
 	ieee80211_bss_info_change_notify(sdata, changed);
 
 	return 0;
@@ -1399,11 +1405,11 @@
 		return -EOPNOTSUPP;
 
 	if (enabled == sdata->u.mgd.powersave &&
-	    timeout == conf->dynamic_ps_timeout)
+	    timeout == conf->dynamic_ps_forced_timeout)
 		return 0;
 
 	sdata->u.mgd.powersave = enabled;
-	conf->dynamic_ps_timeout = timeout;
+	conf->dynamic_ps_forced_timeout = timeout;
 
 	/* no change, but if automatic follow powersave */
 	mutex_lock(&sdata->u.mgd.mtx);
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 6bc9b07..e763f15 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -39,6 +39,13 @@
 	.open = mac80211_open_file_generic,				\
 }
 
+#define STA_OPS_RW(name)						\
+static const struct file_operations sta_ ##name## _ops = {		\
+	.read = sta_##name##_read,					\
+	.write = sta_##name##_write,					\
+	.open = mac80211_open_file_generic,				\
+}
+
 #define STA_FILE(name, field, format)					\
 		STA_READ_##format(name, field)				\
 		STA_OPS(name)
@@ -156,7 +163,63 @@
 
 	return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
 }
-STA_OPS(agg_status);
+
+static ssize_t sta_agg_status_write(struct file *file, const char __user *userbuf,
+				    size_t count, loff_t *ppos)
+{
+	char _buf[12], *buf = _buf;
+	struct sta_info *sta = file->private_data;
+	bool start, tx;
+	unsigned long tid;
+	int ret;
+
+	if (count > sizeof(_buf))
+		return -EINVAL;
+
+	if (copy_from_user(buf, userbuf, count))
+		return -EFAULT;
+
+	buf[sizeof(_buf) - 1] = '\0';
+
+	if (strncmp(buf, "tx ", 3) == 0) {
+		buf += 3;
+		tx = true;
+	} else if (strncmp(buf, "rx ", 3) == 0) {
+		buf += 3;
+		tx = false;
+	} else
+		return -EINVAL;
+
+	if (strncmp(buf, "start ", 6) == 0) {
+		buf += 6;
+		start = true;
+		if (!tx)
+			return -EINVAL;
+	} else if (strncmp(buf, "stop ", 5) == 0) {
+		buf += 5;
+		start = false;
+	} else
+		return -EINVAL;
+
+	tid = simple_strtoul(buf, NULL, 0);
+
+	if (tid >= STA_TID_NUM)
+		return -EINVAL;
+
+	if (tx) {
+		if (start)
+			ret = ieee80211_start_tx_ba_session(&sta->sta, tid);
+		else
+			ret = ieee80211_stop_tx_ba_session(&sta->sta, tid,
+							   WLAN_BACK_RECIPIENT);
+	} else {
+		__ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT, 3);
+		ret = 0;
+	}
+
+	return ret ?: count;
+}
+STA_OPS_RW(agg_status);
 
 static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf,
 				size_t count, loff_t *ppos)
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 35e1e58..ee8b63f 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -152,14 +152,15 @@
 }
 
 static inline int drv_hw_scan(struct ieee80211_local *local,
+			      struct ieee80211_sub_if_data *sdata,
 			      struct cfg80211_scan_request *req)
 {
 	int ret;
 
 	might_sleep();
 
-	ret = local->ops->hw_scan(&local->hw, req);
-	trace_drv_hw_scan(local, req, ret);
+	ret = local->ops->hw_scan(&local->hw, &sdata->vif, req);
+	trace_drv_hw_scan(local, sdata, req, ret);
 	return ret;
 }
 
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index e209cb82..ce734b5 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -363,23 +363,26 @@
 
 TRACE_EVENT(drv_hw_scan,
 	TP_PROTO(struct ieee80211_local *local,
+		 struct ieee80211_sub_if_data *sdata,
 		 struct cfg80211_scan_request *req, int ret),
 
-	TP_ARGS(local, req, ret),
+	TP_ARGS(local, sdata, req, ret),
 
 	TP_STRUCT__entry(
 		LOCAL_ENTRY
+		VIF_ENTRY
 		__field(int, ret)
 	),
 
 	TP_fast_assign(
 		LOCAL_ASSIGN;
+		VIF_ASSIGN;
 		__entry->ret = ret;
 	),
 
 	TP_printk(
-		LOCAL_PR_FMT " ret:%d",
-		LOCAL_PR_ARG, __entry->ret
+		LOCAL_PR_FMT VIF_PR_FMT " ret:%d",
+		LOCAL_PR_ARG,VIF_PR_ARG, __entry->ret
 	)
 );
 
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index e6f3b0c..b72ee64 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -92,6 +92,12 @@
 	if (memcmp(ifibss->bssid, bssid, ETH_ALEN))
 		sta_info_flush(sdata->local, sdata);
 
+	/* if merging, indicate to driver that we leave the old IBSS */
+	if (sdata->vif.bss_conf.ibss_joined) {
+		sdata->vif.bss_conf.ibss_joined = false;
+		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IBSS);
+	}
+
 	memcpy(ifibss->bssid, bssid, ETH_ALEN);
 
 	sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
@@ -171,6 +177,8 @@
 	bss_change |= BSS_CHANGED_BSSID;
 	bss_change |= BSS_CHANGED_BEACON;
 	bss_change |= BSS_CHANGED_BEACON_ENABLED;
+	bss_change |= BSS_CHANGED_IBSS;
+	sdata->vif.bss_conf.ibss_joined = true;
 	ieee80211_bss_info_change_notify(sdata, bss_change);
 
 	ieee80211_sta_def_wmm_params(sdata, sband->n_bitrates, supp_rates);
@@ -481,7 +489,9 @@
 	printk(KERN_DEBUG "%s: No active IBSS STAs - trying to scan for other "
 	       "IBSS networks with same SSID (merge)\n", sdata->name);
 
-	ieee80211_request_internal_scan(sdata, ifibss->ssid, ifibss->ssid_len);
+	ieee80211_request_internal_scan(sdata,
+			ifibss->ssid, ifibss->ssid_len,
+			ifibss->fixed_channel ? ifibss->channel : NULL);
 }
 
 static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
@@ -588,8 +598,9 @@
 		printk(KERN_DEBUG "%s: Trigger new scan to find an IBSS to "
 		       "join\n", sdata->name);
 
-		ieee80211_request_internal_scan(sdata, ifibss->ssid,
-						ifibss->ssid_len);
+		ieee80211_request_internal_scan(sdata,
+				ifibss->ssid, ifibss->ssid_len,
+				ifibss->fixed_channel ? ifibss->channel : NULL);
 	} else {
 		int interval = IEEE80211_SCAN_INTERVAL;
 
@@ -897,6 +908,12 @@
 	sdata->u.ibss.channel = params->channel;
 	sdata->u.ibss.fixed_channel = params->channel_fixed;
 
+	/* fix ourselves to that channel now already */
+	if (params->channel_fixed) {
+		sdata->local->oper_channel = params->channel;
+		sdata->local->oper_channel_type = NL80211_CHAN_NO_HT;
+	}
+
 	if (params->ie) {
 		sdata->u.ibss.ie = kmemdup(params->ie, params->ie_len,
 					   GFP_KERNEL);
@@ -951,7 +968,9 @@
 	kfree(sdata->u.ibss.ie);
 	skb = sdata->u.ibss.presp;
 	rcu_assign_pointer(sdata->u.ibss.presp, NULL);
-	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
+	sdata->vif.bss_conf.ibss_joined = false;
+	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
+						BSS_CHANGED_IBSS);
 	synchronize_rcu();
 	kfree_skb(skb);
 
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index c9712f3..cbaf4981 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1019,7 +1019,8 @@
 /* scan/BSS handling */
 void ieee80211_scan_work(struct work_struct *work);
 int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
-				    const u8 *ssid, u8 ssid_len);
+				    const u8 *ssid, u8 ssid_len,
+				    struct ieee80211_channel *chan);
 int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
 			   struct cfg80211_scan_request *req);
 void ieee80211_scan_cancel(struct ieee80211_local *local);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 011ee85..bd632e1 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -442,7 +442,7 @@
 	struct ieee80211_local *local = hw_to_local(hw);
 	int result;
 	enum ieee80211_band band;
-	int channels, i, j, max_bitrates;
+	int channels, max_bitrates;
 	bool supp_ht;
 	static const u32 cipher_suites[] = {
 		WLAN_CIPHER_SUITE_WEP40,
@@ -572,6 +572,8 @@
 
 	local->hw.conf.listen_interval = local->hw.max_listen_interval;
 
+	local->hw.conf.dynamic_ps_forced_timeout = -1;
+
 	result = sta_info_start(local);
 	if (result < 0)
 		goto fail_sta_info;
@@ -606,21 +608,6 @@
 
 	ieee80211_led_init(local);
 
-	/* alloc internal scan request */
-	i = 0;
-	local->int_scan_req->ssids = &local->scan_ssid;
-	local->int_scan_req->n_ssids = 1;
-	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
-		if (!hw->wiphy->bands[band])
-			continue;
-		for (j = 0; j < hw->wiphy->bands[band]->n_channels; j++) {
-			local->int_scan_req->channels[i] =
-				&hw->wiphy->bands[band]->channels[j];
-			i++;
-		}
-	}
-	local->int_scan_req->n_channels = i;
-
 	local->network_latency_notifier.notifier_call =
 		ieee80211_max_network_latency;
 	result = pm_qos_add_notifier(PM_QOS_NETWORK_LATENCY,
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 425f66c..358226f 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -478,6 +478,7 @@
 {
 	struct ieee80211_sub_if_data *sdata, *found = NULL;
 	int count = 0;
+	int timeout;
 
 	if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) {
 		local->ps_sdata = NULL;
@@ -511,6 +512,26 @@
 		beaconint_us = ieee80211_tu_to_usec(
 					found->vif.bss_conf.beacon_int);
 
+		timeout = local->hw.conf.dynamic_ps_forced_timeout;
+		if (timeout < 0) {
+			/*
+			 * The 2 second value is there for compatibility until
+			 * the PM_QOS_NETWORK_LATENCY is configured with real
+			 * values.
+			 */
+			if (latency == 2000000000)
+				timeout = 100;
+			else if (latency <= 50000)
+				timeout = 300;
+			else if (latency <= 100000)
+				timeout = 100;
+			else if (latency <= 500000)
+				timeout = 50;
+			else
+				timeout = 0;
+		}
+		local->hw.conf.dynamic_ps_timeout = timeout;
+
 		if (beaconint_us > latency) {
 			local->ps_sdata = NULL;
 		} else {
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 72efbd8..9a08f2c 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -81,8 +81,6 @@
 		len += 8;
 	if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
 		len += 1;
-	if (local->hw.flags & IEEE80211_HW_NOISE_DBM)
-		len += 1;
 
 	if (len & 1) /* padding for RX_FLAGS if necessary */
 		len++;
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index e1a3def..e14c441 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -85,7 +85,7 @@
 {
 	struct cfg80211_bss *cbss;
 	struct ieee80211_bss *bss;
-	int clen;
+	int clen, srlen;
 	s32 signal = 0;
 
 	if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
@@ -114,23 +114,24 @@
 		bss->dtim_period = tim_ie->dtim_period;
 	}
 
-	bss->supp_rates_len = 0;
+	/* replace old supported rates if we get new values */
+	srlen = 0;
 	if (elems->supp_rates) {
-		clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
+		clen = IEEE80211_MAX_SUPP_RATES;
 		if (clen > elems->supp_rates_len)
 			clen = elems->supp_rates_len;
-		memcpy(&bss->supp_rates[bss->supp_rates_len], elems->supp_rates,
-		       clen);
-		bss->supp_rates_len += clen;
+		memcpy(bss->supp_rates, elems->supp_rates, clen);
+		srlen += clen;
 	}
 	if (elems->ext_supp_rates) {
-		clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
+		clen = IEEE80211_MAX_SUPP_RATES - srlen;
 		if (clen > elems->ext_supp_rates_len)
 			clen = elems->ext_supp_rates_len;
-		memcpy(&bss->supp_rates[bss->supp_rates_len],
-		       elems->ext_supp_rates, clen);
-		bss->supp_rates_len += clen;
+		memcpy(bss->supp_rates + srlen, elems->ext_supp_rates, clen);
+		srlen += clen;
 	}
+	if (srlen)
+		bss->supp_rates_len = srlen;
 
 	bss->wmm_used = elems->wmm_param || elems->wmm_info;
 	bss->uapsd_supported = is_uapsd_supported(elems);
@@ -411,7 +412,7 @@
 
 	if (local->ops->hw_scan) {
 		WARN_ON(!ieee80211_prep_hw_scan(local));
-		rc = drv_hw_scan(local, local->hw_scan_req);
+		rc = drv_hw_scan(local, sdata, local->hw_scan_req);
 	} else
 		rc = ieee80211_start_sw_scan(local);
 
@@ -655,7 +656,7 @@
 	}
 
 	if (local->hw_scan_req) {
-		int rc = drv_hw_scan(local, local->hw_scan_req);
+		int rc = drv_hw_scan(local, sdata, local->hw_scan_req);
 		mutex_unlock(&local->scan_mtx);
 		if (rc)
 			ieee80211_scan_completed(&local->hw, true);
@@ -728,10 +729,12 @@
 }
 
 int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
-				    const u8 *ssid, u8 ssid_len)
+				    const u8 *ssid, u8 ssid_len,
+				    struct ieee80211_channel *chan)
 {
 	struct ieee80211_local *local = sdata->local;
 	int ret = -EBUSY;
+	enum nl80211_band band;
 
 	mutex_lock(&local->scan_mtx);
 
@@ -739,6 +742,30 @@
 	if (local->scan_req)
 		goto unlock;
 
+	/* fill internal scan request */
+	if (!chan) {
+		int i, nchan = 0;
+
+		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+			if (!local->hw.wiphy->bands[band])
+				continue;
+			for (i = 0;
+			     i < local->hw.wiphy->bands[band]->n_channels;
+			     i++) {
+				local->int_scan_req->channels[nchan] =
+				    &local->hw.wiphy->bands[band]->channels[i];
+				nchan++;
+			}
+		}
+
+		local->int_scan_req->n_channels = nchan;
+	} else {
+		local->int_scan_req->channels[0] = chan;
+		local->int_scan_req->n_channels = 1;
+	}
+
+	local->int_scan_req->ssids = &local->scan_ssid;
+	local->int_scan_req->n_ssids = 1;
 	memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN);
 	local->int_scan_req->ssids[0].ssid_len = ssid_len;
 
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 3de7a22..7301975 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -855,8 +855,12 @@
 	struct sta_info *sta, *nxt;
 
 	/* Just return a random station ... first in list ... */
-	for_each_sta_info(hw_to_local(hw), addr, sta, nxt)
+	for_each_sta_info(hw_to_local(hw), addr, sta, nxt) {
+		if (!sta->uploaded)
+			return NULL;
 		return &sta->sta;
+	}
+
 	return NULL;
 }
 EXPORT_SYMBOL_GPL(ieee80211_find_sta_by_hw);
@@ -864,14 +868,19 @@
 struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif,
 					 const u8 *addr)
 {
-	struct ieee80211_sub_if_data *sdata;
+	struct sta_info *sta;
 
 	if (!vif)
 		return NULL;
 
-	sdata = vif_to_sdata(vif);
+	sta = sta_info_get_bss(vif_to_sdata(vif), addr);
+	if (!sta)
+		return NULL;
 
-	return ieee80211_find_sta_by_hw(&sdata->local->hw, addr);
+	if (!sta->uploaded)
+		return NULL;
+
+	return &sta->sta;
 }
 EXPORT_SYMBOL(ieee80211_find_sta);
 
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 11805a3..94613af0 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -171,6 +171,7 @@
 	struct net_device *prev_dev = NULL;
 	struct sta_info *sta, *tmp;
 	int retry_count = -1, i;
+	int rates_idx = -1;
 	bool send_to_cooked;
 
 	for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
@@ -178,6 +179,8 @@
 		if (i >= hw->max_rates) {
 			info->status.rates[i].idx = -1;
 			info->status.rates[i].count = 0;
+		} else if (info->status.rates[i].idx >= 0) {
+			rates_idx = i;
 		}
 
 		retry_count += info->status.rates[i].count;
@@ -206,6 +209,10 @@
 			return;
 		}
 
+		if ((local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) &&
+		    (rates_idx != -1))
+			sta->last_tx_rate = info->status.rates[rates_idx];
+
 		if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) &&
 		    (ieee80211_is_data_qos(fc))) {
 			u16 tid, ssn;
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index e2aa972..f3841f4 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -593,7 +593,8 @@
 	struct ieee80211_hdr *hdr = (void *)tx->skb->data;
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_rate *rate;
-	int i, len;
+	int i;
+	u32 len;
 	bool inval = false, rts = false, short_preamble = false;
 	struct ieee80211_tx_rate_control txrc;
 	u32 sta_flags;
@@ -602,7 +603,7 @@
 
 	sband = tx->local->hw.wiphy->bands[tx->channel->band];
 
-	len = min_t(int, tx->skb->len + FCS_LEN,
+	len = min_t(u32, tx->skb->len + FCS_LEN,
 			 tx->local->hw.wiphy->frag_threshold);
 
 	/* set up the tx rate control struct we give the RC algo */
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
index bdb1d05..3dd0760 100644
--- a/net/mac80211/work.c
+++ b/net/mac80211/work.c
@@ -213,15 +213,25 @@
 
 	sband = local->hw.wiphy->bands[wk->chan->band];
 
-	/*
-	 * Get all rates supported by the device and the AP as
-	 * some APs don't like getting a superset of their rates
-	 * in the association request (e.g. D-Link DAP 1353 in
-	 * b-only mode)...
-	 */
-	rates_len = ieee80211_compatible_rates(wk->assoc.supp_rates,
-					       wk->assoc.supp_rates_len,
-					       sband, &rates);
+	if (wk->assoc.supp_rates_len) {
+		/*
+		 * Get all rates supported by the device and the AP as
+		 * some APs don't like getting a superset of their rates
+		 * in the association request (e.g. D-Link DAP 1353 in
+		 * b-only mode)...
+		 */
+		rates_len = ieee80211_compatible_rates(wk->assoc.supp_rates,
+						       wk->assoc.supp_rates_len,
+						       sband, &rates);
+	} else {
+		/*
+		 * In case AP not provide any supported rates information
+		 * before association, we send information element(s) with
+		 * all rates that we support.
+		 */
+		rates = ~0;
+		rates_len = sband->n_bitrates;
+	}
 
 	skb = alloc_skb(local->hw.extra_tx_headroom +
 			sizeof(*mgmt) + /* bit too much but doesn't matter */
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 6ac70c1..37d0e0a 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -705,7 +705,8 @@
 			wdev->ps = true;
 		else
 			wdev->ps = false;
-		wdev->ps_timeout = 100;
+		/* allow mac80211 to determine the timeout */
+		wdev->ps_timeout = -1;
 		if (rdev->ops->set_power_mgmt)
 			if (rdev->ops->set_power_mgmt(wdev->wiphy, dev,
 						      wdev->ps,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 356a84a..01da83dd 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -152,6 +152,7 @@
 	[NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
 	[NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
 	[NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
+	[NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 },
 };
 
 /* policy for the attributes */
@@ -2442,6 +2443,7 @@
 	params.use_cts_prot = -1;
 	params.use_short_preamble = -1;
 	params.use_short_slot_time = -1;
+	params.ap_isolate = -1;
 
 	if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
 		params.use_cts_prot =
@@ -2458,6 +2460,8 @@
 		params.basic_rates_len =
 			nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
 	}
+	if (info->attrs[NL80211_ATTR_AP_ISOLATE])
+		params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]);
 
 	rtnl_lock();