diff -ruN a/1-2-v3-ar9170-cleanup-of-bss_info_changed-and-beacon-config.patch b/1-2-v3-ar9170-cleanup-of-bss_info_changed-and-beacon-config.patch
--- a/1-2-v3-ar9170-cleanup-of-bss_info_changed-and-beacon-config.patch	1970-01-01 01:00:00.000000000 +0100
+++ b/1-2-v3-ar9170-cleanup-of-bss_info_changed-and-beacon-config.patch	2009-09-01 14:52:56.000000000 +0200
@@ -0,0 +1,90 @@
+diff --git drivers/net/wireless/ar9170/ar9170.h drivers/net/wireless/ar9170/ar9170.h
+index ce40724..914e471 100644
+--- drivers/net/wireless/ar9170/ar9170.h
++++ drivers/net/wireless/ar9170/ar9170.h
+@@ -178,6 +178,7 @@ struct ar9170 {
+ 	/* beaconing */
+ 	struct sk_buff *beacon;
+ 	struct work_struct beacon_work;
++	bool enable_beacon;
+ 
+ 	/* cryptographic engine */
+ 	u64 usedkeys;
+diff --git drivers/net/wireless/ar9170/mac.c drivers/net/wireless/ar9170/mac.c
+index 6004936..614e321 100644
+--- drivers/net/wireless/ar9170/mac.c
++++ drivers/net/wireless/ar9170/mac.c
+@@ -383,24 +383,26 @@ int ar9170_set_beacon_timers(struct ar9170 *ar)
+ 	if (ar->vif) {
+ 		v |= ar->vif->bss_conf.beacon_int;
+ 
+-		switch (ar->vif->type) {
+-		case NL80211_IFTYPE_MESH_POINT:
+-		case NL80211_IFTYPE_ADHOC:
+-			v |= BIT(25);
+-			break;
+-		case NL80211_IFTYPE_AP:
+-			v |= BIT(24);
+-			pretbtt = (ar->vif->bss_conf.beacon_int - 6) << 16;
+-			break;
+-		default:
++		if (ar->enable_beacon) {
++			switch (ar->vif->type) {
++			case NL80211_IFTYPE_MESH_POINT:
++			case NL80211_IFTYPE_ADHOC:
++				v |= BIT(25);
++				break;
++			case NL80211_IFTYPE_AP:
++				v |= BIT(24);
++				pretbtt = (ar->vif->bss_conf.beacon_int - 6) <<
++					  16;
++				break;
++			default:
+ 			break;
++			}
+ 		}
+ 
+ 		v |= ar->vif->bss_conf.dtim_period << 16;
+ 	}
+ 
+ 	ar9170_regwrite_begin(ar);
+-
+ 	ar9170_regwrite(AR9170_MAC_REG_PRETBTT, pretbtt);
+ 	ar9170_regwrite(AR9170_MAC_REG_BCN_PERIOD, v);
+ 	ar9170_regwrite_finish();
+diff --git drivers/net/wireless/ar9170/main.c drivers/net/wireless/ar9170/main.c
+index 658b323..c0fc355 100644
+--- drivers/net/wireless/ar9170/main.c
++++ drivers/net/wireless/ar9170/main.c
+@@ -2148,11 +2148,17 @@ static void ar9170_op_bss_info_changed(struct ieee80211_hw *hw,
+ 			goto out;
+ 	}
+ 
+-	if (changed & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED)) {
++	if (changed & BSS_CHANGED_BEACON_ENABLED)
++		ar->enable_beacon = bss_conf->enable_beacon;
++
++	if (changed & BSS_CHANGED_BEACON) {
+ 		err = ar9170_update_beacon(ar);
+ 		if (err)
+ 			goto out;
++	}
+ 
++	if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON |
++		       BSS_CHANGED_BEACON_INT)) {
+ 		err = ar9170_set_beacon_timers(ar);
+ 		if (err)
+ 			goto out;
+@@ -2165,12 +2171,6 @@ static void ar9170_op_bss_info_changed(struct ieee80211_hw *hw,
+ #endif /* CONFIG_AR9170_LEDS */
+ 	}
+ 
+-	if (changed & BSS_CHANGED_BEACON_INT) {
+-		err = ar9170_set_beacon_timers(ar);
+-		if (err)
+-			goto out;
+-	}
+-
+ 	if (changed & BSS_CHANGED_HT) {
+ 		/* TODO */
+ 		err = 0;
diff -ruN a/drivers/net/wireless/ar9170/ar9170.h b/drivers/net/wireless/ar9170/ar9170.h
--- a/drivers/net/wireless/ar9170/ar9170.h	2009-08-16 23:19:38.000000000 +0200
+++ b/drivers/net/wireless/ar9170/ar9170.h	2009-09-02 16:37:31.000000000 +0200
@@ -40,7 +40,7 @@
 
 #include <linux/completion.h>
 #include <linux/spinlock.h>
-#include <net/wireless.h>
+#include <net/cfg80211.h>
 #include <net/mac80211.h>
 #ifdef CONFIG_AR9170_LEDS
 #include <linux/leds.h>
@@ -48,6 +48,8 @@
 #include "eeprom.h"
 #include "hw.h"
 
+#include "regd.h"
+
 #define PAYLOAD_MAX	(AR9170_MAX_CMD_LEN/4 - 1)
 
 enum ar9170_bw {
@@ -58,6 +60,21 @@
 	__AR9170_NUM_BW,
 };
 
+static inline enum ar9170_bw nl80211_to_ar9170(enum nl80211_channel_type type)
+{
+	switch (type) {
+	case NL80211_CHAN_NO_HT:
+	case NL80211_CHAN_HT20:
+		return AR9170_BW_20;
+	case NL80211_CHAN_HT40MINUS:
+		return AR9170_BW_40_BELOW;
+	case NL80211_CHAN_HT40PLUS:
+		return AR9170_BW_40_ABOVE;
+	default:
+		BUG();
+	}
+}
+
 enum ar9170_rf_init_mode {
 	AR9170_RFI_NONE,
 	AR9170_RFI_WARM,
@@ -74,6 +91,7 @@
 	struct led_classdev l;
 	char name[32];
 	unsigned int toggled;
+	bool last_state;
 	bool registered;
 };
 
@@ -84,20 +102,31 @@
 	AR9170_STOPPED,
 	AR9170_IDLE,
 	AR9170_STARTED,
-	AR9170_ASSOCIATED,
 };
 
+struct ar9170_rxstream_mpdu_merge {
+	struct ar9170_rx_head plcp;
+	bool has_plcp;
+};
+
+#define AR9170_QUEUE_TIMEOUT		64
+#define AR9170_TX_TIMEOUT		8
+#define AR9170_JANITOR_DELAY		128
+#define AR9170_TX_INVALID_RATE		0xffffffff
+
 struct ar9170 {
 	struct ieee80211_hw *hw;
 	struct mutex mutex;
 	enum ar9170_device_state state;
+	unsigned long bad_hw_nagger;
 
 	int (*open)(struct ar9170 *);
 	void (*stop)(struct ar9170 *);
-	int (*tx)(struct ar9170 *, struct sk_buff *, bool, unsigned int);
+	int (*tx)(struct ar9170 *, struct sk_buff *);
 	int (*exec_cmd)(struct ar9170 *, enum ar9170_cmd, u32 ,
 			void *, u32 , void *);
 	void (*callback_cmd)(struct ar9170 *, u32 , void *);
+	int (*flush)(struct ar9170 *);
 
 	/* interface mode settings */
 	struct ieee80211_vif *vif;
@@ -117,7 +146,8 @@
 	struct work_struct filter_config_work;
 	u64 cur_mc_hash, want_mc_hash;
 	u32 cur_filter, want_filter;
-	unsigned int filter_changed;
+	unsigned long filter_changed;
+	unsigned int filter_state;
 	bool sniffer_enabled;
 
 	/* PHY */
@@ -151,21 +181,35 @@
 
 	/* EEPROM */
 	struct ar9170_eeprom eeprom;
+	struct ath_regulatory regulatory;
 
-	/* global tx status for unregistered Stations. */
-	struct sk_buff_head global_tx_status;
-	struct sk_buff_head global_tx_status_waste;
-	struct delayed_work tx_status_janitor;
+	/* tx queues - as seen by hw - */
+	struct sk_buff_head tx_pending[__AR9170_NUM_TXQ];
+	struct sk_buff_head tx_status[__AR9170_NUM_TXQ];
+	struct delayed_work tx_janitor;
+
+	/* rxstream mpdu merge */
+	struct ar9170_rxstream_mpdu_merge rx_mpdu;
+	struct sk_buff *rx_failover;
+	int rx_failover_missing;
 };
 
 struct ar9170_sta_info {
-	struct sk_buff_head tx_status[__AR9170_NUM_TXQ];
 };
 
-#define IS_STARTED(a)		(a->state >= AR9170_STARTED)
-#define IS_ACCEPTING_CMD(a)	(a->state >= AR9170_IDLE)
+#define AR9170_TX_FLAG_WAIT_FOR_ACK	BIT(0)
+#define AR9170_TX_FLAG_NO_ACK		BIT(1)
+#define AR9170_TX_FLAG_BLOCK_ACK	BIT(2)
+
+struct ar9170_tx_info {
+	unsigned long timeout;
+	unsigned int flags;
+};
+
+#define IS_STARTED(a)		(((struct ar9170 *)a)->state >= AR9170_STARTED)
+#define IS_ACCEPTING_CMD(a)	(((struct ar9170 *)a)->state >= AR9170_IDLE)
 
-#define AR9170_FILTER_CHANGED_PROMISC		BIT(0)
+#define AR9170_FILTER_CHANGED_MODE		BIT(0)
 #define AR9170_FILTER_CHANGED_MULTICAST		BIT(1)
 #define AR9170_FILTER_CHANGED_FRAMEFILTER	BIT(2)
 
@@ -174,8 +218,9 @@
 int ar9170_register(struct ar9170 *ar, struct device *pdev);
 void ar9170_rx(struct ar9170 *ar, struct sk_buff *skb);
 void ar9170_unregister(struct ar9170 *ar);
-void ar9170_handle_tx_status(struct ar9170 *ar, struct sk_buff *skb,
-			     bool update_statistics, u16 tx_status);
+void ar9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb);
+void ar9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len);
+int ar9170_nag_limiter(struct ar9170 *ar);
 
 /* MAC */
 int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
@@ -185,6 +230,9 @@
 int ar9170_update_frame_filter(struct ar9170 *ar);
 int ar9170_set_operating_mode(struct ar9170 *ar);
 int ar9170_set_beacon_timers(struct ar9170 *ar);
+int ar9170_set_dyn_sifs_ack(struct ar9170 *ar);
+int ar9170_set_slot_time(struct ar9170 *ar);
+int ar9170_set_basic_rates(struct ar9170 *ar);
 int ar9170_set_hwretry_limit(struct ar9170 *ar, u32 max_retry);
 int ar9170_update_beacon(struct ar9170 *ar);
 void ar9170_new_beacon(struct work_struct *work);
diff -ruN a/drivers/net/wireless/ar9170/hw.h b/drivers/net/wireless/ar9170/hw.h
--- a/drivers/net/wireless/ar9170/hw.h	2009-08-16 23:19:38.000000000 +0200
+++ b/drivers/net/wireless/ar9170/hw.h	2009-08-28 02:59:04.000000000 +0200
@@ -207,6 +207,9 @@
 #define AR9170_MAC_REG_AC1_AC0_TXOP		(AR9170_MAC_REG_BASE + 0xB44)
 #define AR9170_MAC_REG_AC3_AC2_TXOP		(AR9170_MAC_REG_BASE + 0xB48)
 
+#define AR9170_MAC_REG_AMPDU_FACTOR		(AR9170_MAC_REG_BASE + 0xB9C)
+#define AR9170_MAC_REG_AMPDU_DENSITY		(AR9170_MAC_REG_BASE + 0xBA0)
+
 #define AR9170_MAC_REG_ACK_TABLE		(AR9170_MAC_REG_BASE + 0xC00)
 #define AR9170_MAC_REG_AMPDU_RX_THRESH		(AR9170_MAC_REG_BASE + 0xC50)
 
@@ -312,7 +315,7 @@
 	u8 plcp[12];
 } __packed;
 
-struct ar9170_rx_tail {
+struct ar9170_rx_phystatus {
 	union {
 		struct {
 			u8 rssi_ant0, rssi_ant1, rssi_ant2,
@@ -324,6 +327,9 @@
 
 	u8 evm_stream0[6], evm_stream1[6];
 	u8 phy_err;
+} __packed;
+
+struct ar9170_rx_macstatus {
 	u8 SAidx, DAidx;
 	u8 error;
 	u8 status;
@@ -339,7 +345,7 @@
 
 #define AR9170_RX_ENC_SOFTWARE			0x8
 
-static inline u8 ar9170_get_decrypt_type(struct ar9170_rx_tail *t)
+static inline u8 ar9170_get_decrypt_type(struct ar9170_rx_macstatus *t)
 {
 	return (t->SAidx & 0xc0) >> 4 |
 	       (t->DAidx & 0xc0) >> 6;
@@ -357,10 +363,9 @@
 
 #define AR9170_RX_STATUS_MPDU_MASK		0x30
 #define AR9170_RX_STATUS_MPDU_SINGLE		0x00
-#define AR9170_RX_STATUS_MPDU_FIRST		0x10
-#define AR9170_RX_STATUS_MPDU_MIDDLE		0x20
-#define AR9170_RX_STATUS_MPDU_LAST		0x30
-
+#define AR9170_RX_STATUS_MPDU_FIRST		0x20
+#define AR9170_RX_STATUS_MPDU_MIDDLE		0x30
+#define AR9170_RX_STATUS_MPDU_LAST		0x10
 
 #define AR9170_RX_ERROR_RXTO			0x01
 #define AR9170_RX_ERROR_OVERRUN			0x02
@@ -369,9 +374,9 @@
 #define AR9170_RX_ERROR_WRONG_RA		0x10
 #define AR9170_RX_ERROR_PLCP			0x20
 #define AR9170_RX_ERROR_MMIC			0x40
+#define AR9170_RX_ERROR_FATAL			0x80
 
 struct ar9170_cmd_tx_status {
-	__le16 unkn;
 	u8 dst[ETH_ALEN];
 	__le32 rate;
 	__le16 status;
@@ -389,6 +394,7 @@
 struct ar9170_cmd_response {
 	u8 flag;
 	u8 type;
+	__le16 padding;
 
 	union {
 		struct ar9170_cmd_tx_status		tx_status;
@@ -414,4 +420,7 @@
 	__AR9170_NUM_TXQ,
 };
 
+#define AR9170_TXQ_DEPTH	32
+#define AR9170_TX_MAX_PENDING	128
+
 #endif /* __AR9170_HW_H */
diff -ruN a/drivers/net/wireless/ar9170/Kconfig b/drivers/net/wireless/ar9170/Kconfig
--- a/drivers/net/wireless/ar9170/Kconfig	2009-08-16 23:19:38.000000000 +0200
+++ b/drivers/net/wireless/ar9170/Kconfig	2009-08-28 02:59:04.000000000 +0200
@@ -2,6 +2,7 @@
 	tristate "Atheros AR9170 802.11n USB support"
 	depends on USB && MAC80211 && WLAN_80211 && EXPERIMENTAL
 	select FW_LOADER
+	select ATH_COMMON
 	help
 	  This is a driver for the Atheros "otus" 802.11n USB devices.
 
diff -ruN a/drivers/net/wireless/ar9170/led.c b/drivers/net/wireless/ar9170/led.c
--- a/drivers/net/wireless/ar9170/led.c	2009-08-16 23:19:38.000000000 +0200
+++ b/drivers/net/wireless/ar9170/led.c	2009-08-28 02:59:04.000000000 +0200
@@ -74,7 +74,7 @@
 
 	mutex_lock(&ar->mutex);
 	for (i = 0; i < AR9170_NUM_LEDS; i++)
-		if (ar->leds[i].toggled) {
+		if (ar->leds[i].registered && ar->leds[i].toggled) {
 			led_val |= 1 << i;
 
 			tmp = 70 + 200 / (ar->leds[i].toggled);
@@ -101,9 +101,15 @@
 	struct ar9170_led *arl = container_of(led, struct ar9170_led, l);
 	struct ar9170 *ar = arl->ar;
 
-	arl->toggled++;
+	if (unlikely(!arl->registered))
+		return ;
 
-	if (likely(IS_ACCEPTING_CMD(ar) && brightness))
+	if (arl->last_state != !!brightness) {
+		arl->toggled++;
+		arl->last_state = !!brightness;
+	}
+
+	if (likely(IS_ACCEPTING_CMD(ar) && arl->toggled))
 		queue_delayed_work(ar->hw->workqueue, &ar->led_work, HZ/10);
 }
 
@@ -136,13 +142,14 @@
 {
 	int i;
 
-	cancel_delayed_work_sync(&ar->led_work);
-
 	for (i = 0; i < AR9170_NUM_LEDS; i++)
 		if (ar->leds[i].registered) {
 			led_classdev_unregister(&ar->leds[i].l);
 			ar->leds[i].registered = false;
+			ar->leds[i].toggled = 0;
 		}
+
+	cancel_delayed_work_sync(&ar->led_work);
 }
 
 int ar9170_register_leds(struct ar9170 *ar)
diff -ruN a/drivers/net/wireless/ar9170/mac.c b/drivers/net/wireless/ar9170/mac.c
--- a/drivers/net/wireless/ar9170/mac.c	2009-08-16 23:19:38.000000000 +0200
+++ b/drivers/net/wireless/ar9170/mac.c	2009-08-28 02:59:04.000000000 +0200
@@ -38,6 +38,55 @@
 #include "ar9170.h"
 #include "cmd.h"
 
+int ar9170_set_dyn_sifs_ack(struct ar9170 *ar)
+{
+	u32 val;
+
+	if (conf_is_ht40(&ar->hw->conf))
+		val = 0x010a;
+	else {
+		if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ)
+			val = 0x105;
+		else
+			val = 0x104;
+	}
+
+	return ar9170_write_reg(ar, AR9170_MAC_REG_DYNAMIC_SIFS_ACK, val);
+}
+
+int ar9170_set_slot_time(struct ar9170 *ar)
+{
+	u32 slottime = 20;
+
+	if (!ar->vif)
+		return 0;
+
+	if ((ar->hw->conf.channel->band == IEEE80211_BAND_5GHZ) ||
+	    ar->vif->bss_conf.use_short_slot)
+		slottime = 9;
+
+	return ar9170_write_reg(ar, AR9170_MAC_REG_SLOT_TIME, slottime << 10);
+}
+
+int ar9170_set_basic_rates(struct ar9170 *ar)
+{
+	u8 cck, ofdm;
+
+	if (!ar->vif)
+		return 0;
+
+	ofdm = ar->vif->bss_conf.basic_rates >> 4;
+
+	/* FIXME: is still necessary? */
+	if (ar->hw->conf.channel->band == IEEE80211_BAND_5GHZ)
+		cck = 0;
+	else
+		cck = ar->vif->bss_conf.basic_rates & 0xf;
+
+	return ar9170_write_reg(ar, AR9170_MAC_REG_BASIC_RATE,
+				ofdm << 8 | cck);
+}
+
 int ar9170_set_qos(struct ar9170 *ar)
 {
 	ar9170_regwrite_begin(ar);
@@ -72,6 +121,24 @@
 	return ar9170_regwrite_result();
 }
 
+static int ar9170_set_ampdu_density(struct ar9170 *ar, u8 mpdudensity)
+{
+	u32 val;
+
+	/* don't allow AMPDU density > 8us */
+	if (mpdudensity > 6)
+		return -EINVAL;
+
+	/* Watch out! Otus uses slightly different density values. */
+	val = 0x140a00 | (mpdudensity ? (mpdudensity + 1) : 0);
+
+	ar9170_regwrite_begin(ar);
+	ar9170_regwrite(AR9170_MAC_REG_AMPDU_DENSITY, val);
+	ar9170_regwrite_finish();
+
+	return ar9170_regwrite_result();
+}
+
 int ar9170_init_mac(struct ar9170 *ar)
 {
 	ar9170_regwrite_begin(ar);
@@ -265,9 +332,9 @@
 		case NL80211_IFTYPE_ADHOC:
 			pm_mode |= AR9170_MAC_REG_POWERMGT_IBSS;
 			break;
-/*		case NL80211_IFTYPE_AP:
+		case NL80211_IFTYPE_AP:
 			pm_mode |= AR9170_MAC_REG_POWERMGT_AP;
-			break;*/
+			break;
 		case NL80211_IFTYPE_WDS:
 			pm_mode |= AR9170_MAC_REG_POWERMGT_AP_WDS;
 			break;
@@ -296,6 +363,11 @@
 	if (err)
 		return err;
 
+	/* set AMPDU density to 8us. */
+	err = ar9170_set_ampdu_density(ar, 6);
+	if (err)
+		return err;
+
 	ar9170_regwrite_begin(ar);
 
 	ar9170_regwrite(AR9170_MAC_REG_POWERMANAGEMENT, pm_mode);
@@ -316,9 +388,9 @@
 	u32 v = 0;
 	u32 pretbtt = 0;
 
-	v |= ar->hw->conf.beacon_int;
-
 	if (ar->vif) {
+		v |= ar->vif->bss_conf.beacon_int;
+
 		switch (ar->vif->type) {
 		case NL80211_IFTYPE_MESH_POINT:
 		case NL80211_IFTYPE_ADHOC:
@@ -326,7 +398,7 @@
 			break;
 		case NL80211_IFTYPE_AP:
 			v |= BIT(24);
-			pretbtt = (ar->hw->conf.beacon_int - 6) << 16;
+			pretbtt = (ar->vif->bss_conf.beacon_int - 6) << 16;
 			break;
 		default:
 			break;
@@ -375,10 +447,10 @@
 	/* XXX: use skb->cb info */
 	if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ)
 		ar9170_regwrite(AR9170_MAC_REG_BCN_PLCP,
-				((skb->len + 4) << (3+16)) + 0x0400);
+				((skb->len + 4) << (3 + 16)) + 0x0400);
 	else
 		ar9170_regwrite(AR9170_MAC_REG_BCN_PLCP,
-				((skb->len + 4) << (3+16)) + 0x0400);
+				((skb->len + 4) << 16) + 0x001b);
 
 	ar9170_regwrite(AR9170_MAC_REG_BCN_LENGTH, skb->len + 4);
 	ar9170_regwrite(AR9170_MAC_REG_BCN_ADDR, AR9170_BEACON_BUFFER_ADDRESS);
diff -ruN a/drivers/net/wireless/ar9170/main.c b/drivers/net/wireless/ar9170/main.c
--- a/drivers/net/wireless/ar9170/main.c	2009-08-16 23:19:38.000000000 +0200
+++ b/drivers/net/wireless/ar9170/main.c	2009-09-02 16:45:34.000000000 +0200
@@ -142,73 +142,153 @@
 };
 #undef CHAN
 
+#define AR9170_HT_CAP							\
+{									\
+	.ht_supported	= true,						\
+	.cap		= IEEE80211_HT_CAP_MAX_AMSDU |			\
+			  IEEE80211_HT_CAP_SUP_WIDTH_20_40 |		\
+			  IEEE80211_HT_CAP_SGI_40 |			\
+			  IEEE80211_HT_CAP_DSSSCCK40 |			\
+			  IEEE80211_HT_CAP_SM_PS,			\
+	.ampdu_factor	= 3,						\
+	.ampdu_density	= 6,						\
+	.mcs		= {						\
+		.rx_mask = { 0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, },	\
+	},								\
+}
+
 static struct ieee80211_supported_band ar9170_band_2GHz = {
 	.channels	= ar9170_2ghz_chantable,
 	.n_channels	= ARRAY_SIZE(ar9170_2ghz_chantable),
 	.bitrates	= ar9170_g_ratetable,
 	.n_bitrates	= ar9170_g_ratetable_size,
+	.ht_cap		= AR9170_HT_CAP,
 };
 
-#ifdef AR9170_QUEUE_DEBUG
-/*
- * In case some wants works with AR9170's crazy tx_status queueing techniques.
- * He might need this rather useful probing function.
- *
- * NOTE: caller must hold the queue's spinlock!
- */
+static struct ieee80211_supported_band ar9170_band_5GHz = {
+	.channels	= ar9170_5ghz_chantable,
+	.n_channels	= ARRAY_SIZE(ar9170_5ghz_chantable),
+	.bitrates	= ar9170_a_ratetable,
+	.n_bitrates	= ar9170_a_ratetable_size,
+	.ht_cap		= AR9170_HT_CAP,
+};
+
+static void ar9170_tx(struct ar9170 *ar);
 
+#ifdef AR9170_QUEUE_DEBUG
 static void ar9170_print_txheader(struct ar9170 *ar, struct sk_buff *skb)
 {
 	struct ar9170_tx_control *txc = (void *) skb->data;
-	struct ieee80211_hdr *hdr = (void *)txc->frame_data;
+	struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb);
+	struct ar9170_tx_info *arinfo = (void *) txinfo->rate_driver_data;
+	struct ieee80211_hdr *hdr = (void *) txc->frame_data;
 
-	printk(KERN_DEBUG "%s: => FRAME [skb:%p, queue:%d, DA:[%pM] "
-			  "mac_control:%04x, phy_control:%08x]\n",
+	printk(KERN_DEBUG "%s: => FRAME [skb:%p, q:%d, DA:[%pM] flags:%x "
+			  "mac_ctrl:%04x, phy_ctrl:%08x, timeout:[%d ms]]\n",
 	       wiphy_name(ar->hw->wiphy), skb, skb_get_queue_mapping(skb),
-	       ieee80211_get_DA(hdr), le16_to_cpu(txc->mac_control),
-	       le32_to_cpu(txc->phy_control));
+	       ieee80211_get_DA(hdr), arinfo->flags,
+	       le16_to_cpu(txc->mac_control), le32_to_cpu(txc->phy_control),
+	       jiffies_to_msecs(arinfo->timeout - jiffies));
 }
 
-static void ar9170_dump_station_tx_status_queue(struct ar9170 *ar,
-						struct sk_buff_head *queue)
+static void __ar9170_dump_txqueue(struct ar9170 *ar,
+				struct sk_buff_head *queue)
 {
 	struct sk_buff *skb;
 	int i = 0;
 
 	printk(KERN_DEBUG "---[ cut here ]---\n");
-	printk(KERN_DEBUG "%s: %d entries in tx_status queue.\n",
+	printk(KERN_DEBUG "%s: %d entries in queue.\n",
 	       wiphy_name(ar->hw->wiphy), skb_queue_len(queue));
 
 	skb_queue_walk(queue, skb) {
-		struct ar9170_tx_control *txc = (void *) skb->data;
-		struct ieee80211_hdr *hdr = (void *)txc->frame_data;
-
-		printk(KERN_DEBUG "index:%d => \n", i);
+		printk(KERN_DEBUG "index:%d => \n", i++);
 		ar9170_print_txheader(ar, skb);
 	}
+	if (i != skb_queue_len(queue))
+		printk(KERN_DEBUG "WARNING: queue frame counter "
+		       "mismatch %d != %d\n", skb_queue_len(queue), i);
 	printk(KERN_DEBUG "---[ end ]---\n");
 }
-#endif /* AR9170_QUEUE_DEBUG */
 
-static struct ieee80211_supported_band ar9170_band_5GHz = {
-	.channels	= ar9170_5ghz_chantable,
-	.n_channels	= ARRAY_SIZE(ar9170_5ghz_chantable),
-	.bitrates	= ar9170_a_ratetable,
-	.n_bitrates	= ar9170_a_ratetable_size,
-};
+static void ar9170_dump_txqueue(struct ar9170 *ar,
+				struct sk_buff_head *queue)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&queue->lock, flags);
+	__ar9170_dump_txqueue(ar, queue);
+	spin_unlock_irqrestore(&queue->lock, flags);
+}
 
-void ar9170_handle_tx_status(struct ar9170 *ar, struct sk_buff *skb,
-			     bool valid_status, u16 tx_status)
+static void __ar9170_dump_txstats(struct ar9170 *ar)
+{
+	int i;
+
+	printk(KERN_DEBUG "%s: QoS queue stats\n",
+	       wiphy_name(ar->hw->wiphy));
+
+	for (i = 0; i < __AR9170_NUM_TXQ; i++)
+		printk(KERN_DEBUG "%s: queue:%d limit:%d len:%d waitack:%d\n",
+		       wiphy_name(ar->hw->wiphy), i, ar->tx_stats[i].limit,
+		       ar->tx_stats[i].len, skb_queue_len(&ar->tx_status[i]));
+}
+
+static void ar9170_dump_txstats(struct ar9170 *ar)
 {
-	struct ieee80211_tx_info *txinfo;
-	unsigned int retries = 0, queue = skb_get_queue_mapping(skb);
 	unsigned long flags;
 
 	spin_lock_irqsave(&ar->tx_stats_lock, flags);
-	ar->tx_stats[queue].len--;
-	if (ieee80211_queue_stopped(ar->hw, queue))
-		ieee80211_wake_queue(ar->hw, queue);
+	__ar9170_dump_txstats(ar);
 	spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
+}
+#endif /* AR9170_QUEUE_DEBUG */
+
+/* caller must guarantee exclusive access for _bin_ queue. */
+static void ar9170_recycle_expired(struct ar9170 *ar,
+				   struct sk_buff_head *queue,
+				   struct sk_buff_head *bin)
+{
+	struct sk_buff *skb, *old = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&queue->lock, flags);
+	while ((skb = skb_peek(queue))) {
+		struct ieee80211_tx_info *txinfo;
+		struct ar9170_tx_info *arinfo;
+
+		txinfo = IEEE80211_SKB_CB(skb);
+		arinfo = (void *) txinfo->rate_driver_data;
+
+		if (time_is_before_jiffies(arinfo->timeout)) {
+#ifdef AR9170_QUEUE_DEBUG
+			printk(KERN_DEBUG "%s: [%ld > %ld] frame expired => "
+			       "recycle \n", wiphy_name(ar->hw->wiphy),
+			       jiffies, arinfo->timeout);
+			ar9170_print_txheader(ar, skb);
+#endif /* AR9170_QUEUE_DEBUG */
+			__skb_unlink(skb, queue);
+			__skb_queue_tail(bin, skb);
+		} else {
+			break;
+		}
+
+		if (unlikely(old == skb)) {
+			/* bail out - queue is shot. */
+
+			WARN_ON(1);
+			break;
+		}
+		old = skb;
+	}
+	spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+static void ar9170_tx_status(struct ar9170 *ar, struct sk_buff *skb,
+				    u16 tx_status)
+{
+	struct ieee80211_tx_info *txinfo;
+	unsigned int retries = 0;
 
 	txinfo = IEEE80211_SKB_CB(skb);
 	ieee80211_tx_info_clear_status(txinfo);
@@ -230,45 +310,61 @@
 		break;
 	}
 
-	if (valid_status)
-		txinfo->status.rates[0].count = retries + 1;
-
+	txinfo->status.rates[0].count = retries + 1;
 	skb_pull(skb, sizeof(struct ar9170_tx_control));
 	ieee80211_tx_status_irqsafe(ar->hw, skb);
 }
 
-static struct sk_buff *ar9170_find_skb_in_queue(struct ar9170 *ar,
-						const u8 *mac,
-						const u32 queue,
-						struct sk_buff_head *q)
+void ar9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb)
 {
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ar9170_tx_info *arinfo = (void *) info->rate_driver_data;
+	unsigned int queue = skb_get_queue_mapping(skb);
 	unsigned long flags;
-	struct sk_buff *skb;
 
-	spin_lock_irqsave(&q->lock, flags);
-	skb_queue_walk(q, skb) {
-		struct ar9170_tx_control *txc = (void *) skb->data;
-		struct ieee80211_hdr *hdr = (void *) txc->frame_data;
-		u32 txc_queue = (le32_to_cpu(txc->phy_control) &
-				AR9170_TX_PHY_QOS_MASK) >>
-				AR9170_TX_PHY_QOS_SHIFT;
+	spin_lock_irqsave(&ar->tx_stats_lock, flags);
+	ar->tx_stats[queue].len--;
 
-		if  ((queue != txc_queue) ||
-		     (compare_ether_addr(ieee80211_get_DA(hdr), mac)))
-			continue;
+	if (skb_queue_empty(&ar->tx_pending[queue])) {
+#ifdef AR9170_QUEUE_STOP_DEBUG
+		printk(KERN_DEBUG "%s: wake queue %d\n",
+		       wiphy_name(ar->hw->wiphy), queue);
+		__ar9170_dump_txstats(ar);
+#endif /* AR9170_QUEUE_STOP_DEBUG */
+		ieee80211_wake_queue(ar->hw, queue);
+	}
+	spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
 
-		__skb_unlink(skb, q);
-		spin_unlock_irqrestore(&q->lock, flags);
-		return skb;
+	if (arinfo->flags & AR9170_TX_FLAG_BLOCK_ACK) {
+		dev_kfree_skb_any(skb);
+	} else if (arinfo->flags & AR9170_TX_FLAG_WAIT_FOR_ACK) {
+		arinfo->timeout = jiffies +
+				  msecs_to_jiffies(AR9170_TX_TIMEOUT);
+
+		skb_queue_tail(&ar->tx_status[queue], skb);
+	} else if (arinfo->flags & AR9170_TX_FLAG_NO_ACK) {
+		ar9170_tx_status(ar, skb, AR9170_TX_STATUS_FAILED);
+	} else {
+#ifdef AR9170_QUEUE_DEBUG
+		printk(KERN_DEBUG "%s: unsupported frame flags!\n",
+		       wiphy_name(ar->hw->wiphy));
+		ar9170_print_txheader(ar, skb);
+#endif /* AR9170_QUEUE_DEBUG */
+		dev_kfree_skb_any(skb);
+	}
+
+	if (!ar->tx_stats[queue].len &&
+	    !skb_queue_empty(&ar->tx_pending[queue])) {
+		ar9170_tx(ar);
 	}
-	spin_unlock_irqrestore(&q->lock, flags);
-	return NULL;
 }
 
-static struct sk_buff *ar9170_find_queued_skb(struct ar9170 *ar, const u8 *mac,
-					      const u32 queue)
+static struct sk_buff *ar9170_get_queued_skb(struct ar9170 *ar,
+					     const u8 *mac,
+					     struct sk_buff_head *queue,
+					     const u32 rate)
 {
-	struct ieee80211_sta *sta;
+	unsigned long flags;
 	struct sk_buff *skb;
 
 	/*
@@ -279,85 +375,94 @@
 	 * the firmware provided (-> destination MAC, and phy_control) -
 	 * and hope that we picked the right one...
 	 */
-	rcu_read_lock();
-	sta = ieee80211_find_sta(ar->hw, mac);
 
-	if (likely(sta)) {
-		struct ar9170_sta_info *sta_priv = (void *) sta->drv_priv;
-		skb = skb_dequeue(&sta_priv->tx_status[queue]);
-		rcu_read_unlock();
-		if (likely(skb))
-			return skb;
-	} else
-		rcu_read_unlock();
-
-	/* scan the waste queue for candidates */
-	skb = ar9170_find_skb_in_queue(ar, mac, queue,
-				       &ar->global_tx_status_waste);
-	if (!skb) {
-		/* so it still _must_ be in the global list. */
-		skb = ar9170_find_skb_in_queue(ar, mac, queue,
-					       &ar->global_tx_status);
-	}
+	spin_lock_irqsave(&queue->lock, flags);
+	skb_queue_walk(queue, skb) {
+		struct ar9170_tx_control *txc = (void *) skb->data;
+		struct ieee80211_hdr *hdr = (void *) txc->frame_data;
+		u32 r;
+
+		if (mac && compare_ether_addr(ieee80211_get_DA(hdr), mac)) {
+#ifdef AR9170_QUEUE_DEBUG
+			printk(KERN_DEBUG "%s: skip frame => DA %pM != %pM\n",
+			       wiphy_name(ar->hw->wiphy), mac,
+			       ieee80211_get_DA(hdr));
+			ar9170_print_txheader(ar, skb);
+#endif /* AR9170_QUEUE_DEBUG */
+			continue;
+		}
 
+		r = (le32_to_cpu(txc->phy_control) & AR9170_TX_PHY_MCS_MASK) >>
+		    AR9170_TX_PHY_MCS_SHIFT;
+
+		if ((rate != AR9170_TX_INVALID_RATE) && (r != rate)) {
 #ifdef AR9170_QUEUE_DEBUG
-	if (unlikely((!skb) && net_ratelimit())) {
-		printk(KERN_ERR "%s: ESS:[%pM] does not have any "
-				"outstanding frames in this queue (%d).\n",
-				wiphy_name(ar->hw->wiphy), mac, queue);
+			printk(KERN_DEBUG "%s: skip frame => rate %d != %d\n",
+			       wiphy_name(ar->hw->wiphy), rate, r);
+			ar9170_print_txheader(ar, skb);
+#endif /* AR9170_QUEUE_DEBUG */
+			continue;
+		}
+
+		__skb_unlink(skb, queue);
+		spin_unlock_irqrestore(&queue->lock, flags);
+		return skb;
 	}
+
+#ifdef AR9170_QUEUE_DEBUG
+	printk(KERN_ERR "%s: ESS:[%pM] does not have any "
+			"outstanding frames in queue.\n",
+			wiphy_name(ar->hw->wiphy), mac);
+	__ar9170_dump_txqueue(ar, queue);
 #endif /* AR9170_QUEUE_DEBUG */
-	return skb;
+	spin_unlock_irqrestore(&queue->lock, flags);
+
+	return NULL;
 }
 
 /*
- * This worker tries to keep the global tx_status queue empty.
- * So we can guarantee that incoming tx_status reports for
- * unregistered stations are always synced with the actual
- * frame - which we think - belongs to.
+ * This worker tries to keeps an maintain tx_status queues.
+ * So we can guarantee that incoming tx_status reports are
+ * actually for a pending frame.
  */
 
-static void ar9170_tx_status_janitor(struct work_struct *work)
+static void ar9170_tx_janitor(struct work_struct *work)
 {
 	struct ar9170 *ar = container_of(work, struct ar9170,
-					 tx_status_janitor.work);
-	struct sk_buff *skb;
+					 tx_janitor.work);
+	struct sk_buff_head waste;
+	unsigned int i;
+	bool resched = false;
 
 	if (unlikely(!IS_STARTED(ar)))
 		return ;
 
-	mutex_lock(&ar->mutex);
-	/* recycle the garbage back to mac80211... one by one. */
-	while ((skb = skb_dequeue(&ar->global_tx_status_waste))) {
+	skb_queue_head_init(&waste);
+
+	for (i = 0; i < __AR9170_NUM_TXQ; i++) {
 #ifdef AR9170_QUEUE_DEBUG
-		printk(KERN_DEBUG "%s: dispose queued frame =>\n",
-		       wiphy_name(ar->hw->wiphy));
-		ar9170_print_txheader(ar, skb);
+		printk(KERN_DEBUG "%s: garbage collector scans queue:%d\n",
+		       wiphy_name(ar->hw->wiphy), i);
+		ar9170_dump_txqueue(ar, &ar->tx_pending[i]);
+		ar9170_dump_txqueue(ar, &ar->tx_status[i]);
 #endif /* AR9170_QUEUE_DEBUG */
-		ar9170_handle_tx_status(ar, skb, false,
-					AR9170_TX_STATUS_FAILED);
-	}
 
-	while ((skb = skb_dequeue(&ar->global_tx_status))) {
-#ifdef AR9170_QUEUE_DEBUG
-		printk(KERN_DEBUG "%s: moving frame into waste queue =>\n",
-		       wiphy_name(ar->hw->wiphy));
+		ar9170_recycle_expired(ar, &ar->tx_status[i], &waste);
+		ar9170_recycle_expired(ar, &ar->tx_pending[i], &waste);
+		skb_queue_purge(&waste);
 
-		ar9170_print_txheader(ar, skb);
-#endif /* AR9170_QUEUE_DEBUG */
-		skb_queue_tail(&ar->global_tx_status_waste, skb);
+		if (!skb_queue_empty(&ar->tx_status[i]) ||
+		    !skb_queue_empty(&ar->tx_pending[i]))
+			resched = true;
 	}
 
-	/* recall the janitor in 100ms - if there's garbage in the can. */
-	if (skb_queue_len(&ar->global_tx_status_waste) > 0)
-		queue_delayed_work(ar->hw->workqueue, &ar->tx_status_janitor,
-				   msecs_to_jiffies(100));
-
-	mutex_unlock(&ar->mutex);
+	if (resched)
+		queue_delayed_work(ar->hw->workqueue,
+				   &ar->tx_janitor,
+				   msecs_to_jiffies(AR9170_JANITOR_DELAY));
 }
 
-static void ar9170_handle_command_response(struct ar9170 *ar,
-					   void *buf, u32 len)
+void ar9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len)
 {
 	struct ar9170_cmd_response *cmd = (void *) buf;
 
@@ -381,15 +486,21 @@
 		 */
 
 		struct sk_buff *skb;
-		u32 queue = (le32_to_cpu(cmd->tx_status.rate) &
-			    AR9170_TX_PHY_QOS_MASK) >> AR9170_TX_PHY_QOS_SHIFT;
+		u32 phy = le32_to_cpu(cmd->tx_status.rate);
+		u32 q = (phy & AR9170_TX_PHY_QOS_MASK) >>
+			AR9170_TX_PHY_QOS_SHIFT;
+#ifdef AR9170_QUEUE_DEBUG
+		printk(KERN_DEBUG "%s: recv tx_status for %pM, p:%08x, q:%d\n",
+		       wiphy_name(ar->hw->wiphy), cmd->tx_status.dst, phy, q);
+#endif /* AR9170_QUEUE_DEBUG */
 
-		skb = ar9170_find_queued_skb(ar, cmd->tx_status.dst, queue);
+		skb = ar9170_get_queued_skb(ar, cmd->tx_status.dst,
+					    &ar->tx_status[q],
+					    AR9170_TX_INVALID_RATE);
 		if (unlikely(!skb))
 			return ;
 
-		ar9170_handle_tx_status(ar, skb, true,
-					le16_to_cpu(cmd->tx_status.status));
+		ar9170_tx_status(ar, skb, le16_to_cpu(cmd->tx_status.status));
 		break;
 		}
 
@@ -429,6 +540,38 @@
 		/* retransmission issue / SIFS/EIFS collision ?! */
 		break;
 
+	/* firmware debug */
+	case 0xca:
+		printk(KERN_DEBUG "ar9170 FW: %.*s\n", len - 4, (char *)buf + 4);
+		break;
+	case 0xcb:
+		len -= 4;
+
+		switch (len) {
+		case 1:
+			printk(KERN_DEBUG "ar9170 FW: u8: %#.2x\n",
+				*((char *)buf + 4));
+			break;
+		case 2:
+			printk(KERN_DEBUG "ar9170 FW: u8: %#.4x\n",
+				le16_to_cpup((__le16 *)((char *)buf + 4)));
+			break;
+		case 4:
+			printk(KERN_DEBUG "ar9170 FW: u8: %#.8x\n",
+				le32_to_cpup((__le32 *)((char *)buf + 4)));
+			break;
+		case 8:
+			printk(KERN_DEBUG "ar9170 FW: u8: %#.16lx\n",
+				(unsigned long)le64_to_cpup(
+						(__le64 *)((char *)buf + 4)));
+			break;
+		}
+		break;
+	case 0xcc:
+		print_hex_dump_bytes("ar9170 FW:", DUMP_PREFIX_NONE,
+				     (char *)buf + 4, len - 4);
+		break;
+
 	default:
 		printk(KERN_INFO "received unhandled event %x\n", cmd->type);
 		print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE, buf, len);
@@ -436,214 +579,430 @@
 	}
 }
 
-/*
- * If the frame alignment is right (or the kernel has
- * CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS), and there
- * is only a single MPDU in the USB frame, then we can
- * submit to mac80211 the SKB directly. However, since
- * there may be multiple packets in one SKB in stream
- * mode, and we need to observe the proper ordering,
- * this is non-trivial.
- */
-static void ar9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len)
+static void ar9170_rx_reset_rx_mpdu(struct ar9170 *ar)
 {
-	struct sk_buff *skb;
-	struct ar9170_rx_head *head = (void *)buf;
-	struct ar9170_rx_tail *tail;
-	struct ieee80211_rx_status status;
-	int mpdu_len, i;
-	u8 error, antennas = 0, decrypt;
-	__le16 fc;
-	int reserved;
+	memset(&ar->rx_mpdu.plcp, 0, sizeof(struct ar9170_rx_head));
+	ar->rx_mpdu.has_plcp = false;
+}
 
-	if (unlikely(!IS_STARTED(ar)))
-		return ;
+int ar9170_nag_limiter(struct ar9170 *ar)
+{
+	bool print_message;
+
+	/*
+	 * we expect all sorts of errors in promiscuous mode.
+	 * don't bother with it, it's OK!
+	 */
+	if (ar->sniffer_enabled)
+		return false;
+
+	/*
+	 * only go for frequent errors! The hardware tends to
+	 * do some stupid thing once in a while under load, in
+	 * noisy environments or just for fun!
+	 */
+	if (time_before(jiffies, ar->bad_hw_nagger) && net_ratelimit())
+		print_message = true;
+	else
+		print_message = false;
+
+	/* reset threshold for "once in a while" */
+	ar->bad_hw_nagger = jiffies + HZ / 4;
+	return print_message;
+}
+
+static int ar9170_rx_mac_status(struct ar9170 *ar,
+				struct ar9170_rx_head *head,
+				struct ar9170_rx_macstatus *mac,
+				struct ieee80211_rx_status *status)
+{
+	u8 error, decrypt;
 
-	/* Received MPDU */
-	mpdu_len = len;
-	mpdu_len -= sizeof(struct ar9170_rx_head);
-	mpdu_len -= sizeof(struct ar9170_rx_tail);
 	BUILD_BUG_ON(sizeof(struct ar9170_rx_head) != 12);
-	BUILD_BUG_ON(sizeof(struct ar9170_rx_tail) != 24);
+	BUILD_BUG_ON(sizeof(struct ar9170_rx_macstatus) != 4);
 
-	if (mpdu_len <= FCS_LEN)
-		return;
+	error = mac->error;
+	if (error & AR9170_RX_ERROR_MMIC) {
+		status->flag |= RX_FLAG_MMIC_ERROR;
+		error &= ~AR9170_RX_ERROR_MMIC;
+	}
 
-	tail = (void *)(buf + sizeof(struct ar9170_rx_head) + mpdu_len);
+	if (error & AR9170_RX_ERROR_PLCP) {
+		status->flag |= RX_FLAG_FAILED_PLCP_CRC;
+		error &= ~AR9170_RX_ERROR_PLCP;
 
-	for (i = 0; i < 3; i++)
-		if (tail->rssi[i] != 0x80)
-			antennas |= BIT(i);
+		if (!(ar->filter_state & FIF_PLCPFAIL))
+			return -EINVAL;
+	}
 
-	/* post-process RSSI */
-	for (i = 0; i < 7; i++)
-		if (tail->rssi[i] & 0x80)
-			tail->rssi[i] = ((tail->rssi[i] & 0x7f) + 1) & 0x7f;
+	if (error & AR9170_RX_ERROR_FCS) {
+		status->flag |= RX_FLAG_FAILED_FCS_CRC;
+		error &= ~AR9170_RX_ERROR_FCS;
 
-	memset(&status, 0, sizeof(status));
+		if (!(ar->filter_state & FIF_FCSFAIL))
+			return -EINVAL;
+	}
 
-	status.band = ar->channel->band;
-	status.freq = ar->channel->center_freq;
-	status.signal = ar->noise[0] + tail->rssi_combined;
-	status.noise = ar->noise[0];
-	status.antenna = antennas;
+	decrypt = ar9170_get_decrypt_type(mac);
+	if (!(decrypt & AR9170_RX_ENC_SOFTWARE) &&
+	    decrypt != AR9170_ENC_ALG_NONE)
+		status->flag |= RX_FLAG_DECRYPTED;
+
+	/* ignore wrong RA errors */
+	error &= ~AR9170_RX_ERROR_WRONG_RA;
 
-	switch (tail->status & AR9170_RX_STATUS_MODULATION_MASK) {
+	if (error & AR9170_RX_ERROR_DECRYPT) {
+		error &= ~AR9170_RX_ERROR_DECRYPT;
+		/*
+		 * Rx decryption is done in place,
+		 * the original data is lost anyway.
+		 */
+
+		return -EINVAL;
+	}
+
+	/* drop any other error frames */
+	if (unlikely(error)) {
+		/* TODO: update netdevice's RX dropped/errors statistics */
+
+		if (ar9170_nag_limiter(ar))
+			printk(KERN_DEBUG "%s: received frame with "
+			       "suspicious error code (%#x).\n",
+			       wiphy_name(ar->hw->wiphy), error);
+
+		return -EINVAL;
+	}
+
+	status->band = ar->channel->band;
+	status->freq = ar->channel->center_freq;
+
+	switch (mac->status & AR9170_RX_STATUS_MODULATION_MASK) {
 	case AR9170_RX_STATUS_MODULATION_CCK:
-		if (tail->status & AR9170_RX_STATUS_SHORT_PREAMBLE)
-			status.flag |= RX_FLAG_SHORTPRE;
+		if (mac->status & AR9170_RX_STATUS_SHORT_PREAMBLE)
+			status->flag |= RX_FLAG_SHORTPRE;
 		switch (head->plcp[0]) {
 		case 0x0a:
-			status.rate_idx = 0;
+			status->rate_idx = 0;
 			break;
 		case 0x14:
-			status.rate_idx = 1;
+			status->rate_idx = 1;
 			break;
 		case 0x37:
-			status.rate_idx = 2;
+			status->rate_idx = 2;
 			break;
 		case 0x6e:
-			status.rate_idx = 3;
+			status->rate_idx = 3;
 			break;
 		default:
-			if ((!ar->sniffer_enabled) && (net_ratelimit()))
+			if (ar9170_nag_limiter(ar))
 				printk(KERN_ERR "%s: invalid plcp cck rate "
 				       "(%x).\n", wiphy_name(ar->hw->wiphy),
 				       head->plcp[0]);
-			return;
+			return -EINVAL;
 		}
 		break;
+
 	case AR9170_RX_STATUS_MODULATION_OFDM:
-		switch (head->plcp[0] & 0xF) {
-		case 0xB:
-			status.rate_idx = 0;
+		switch (head->plcp[0] & 0xf) {
+		case 0xb:
+			status->rate_idx = 0;
 			break;
-		case 0xF:
-			status.rate_idx = 1;
+		case 0xf:
+			status->rate_idx = 1;
 			break;
-		case 0xA:
-			status.rate_idx = 2;
+		case 0xa:
+			status->rate_idx = 2;
 			break;
-		case 0xE:
-			status.rate_idx = 3;
+		case 0xe:
+			status->rate_idx = 3;
 			break;
 		case 0x9:
-			status.rate_idx = 4;
+			status->rate_idx = 4;
 			break;
-		case 0xD:
-			status.rate_idx = 5;
+		case 0xd:
+			status->rate_idx = 5;
 			break;
 		case 0x8:
-			status.rate_idx = 6;
+			status->rate_idx = 6;
 			break;
-		case 0xC:
-			status.rate_idx = 7;
+		case 0xc:
+			status->rate_idx = 7;
 			break;
 		default:
-			if ((!ar->sniffer_enabled) && (net_ratelimit()))
+			if (ar9170_nag_limiter(ar))
 				printk(KERN_ERR "%s: invalid plcp ofdm rate "
 				       "(%x).\n", wiphy_name(ar->hw->wiphy),
 				       head->plcp[0]);
-			return;
+			return -EINVAL;
 		}
-		if (status.band == IEEE80211_BAND_2GHZ)
-			status.rate_idx += 4;
+		if (status->band == IEEE80211_BAND_2GHZ)
+			status->rate_idx += 4;
 		break;
+
 	case AR9170_RX_STATUS_MODULATION_HT:
+		if (head->plcp[3] & 0x80)
+			status->flag |= RX_FLAG_40MHZ;
+		if (head->plcp[6] & 0x80)
+			status->flag |= RX_FLAG_SHORT_GI;
+
+		status->rate_idx = clamp(0, 75, head->plcp[6] & 0x7f);
+		status->flag |= RX_FLAG_HT;
+		break;
+
 	case AR9170_RX_STATUS_MODULATION_DUPOFDM:
 		/* XXX */
-
-		if (net_ratelimit())
+		if (ar9170_nag_limiter(ar))
 			printk(KERN_ERR "%s: invalid modulation\n",
 			       wiphy_name(ar->hw->wiphy));
-		return;
+		return -EINVAL;
 	}
 
-	error = tail->error;
+	return 0;
+}
 
-	if (error & AR9170_RX_ERROR_MMIC) {
-		status.flag |= RX_FLAG_MMIC_ERROR;
-		error &= ~AR9170_RX_ERROR_MMIC;
-	}
+static void ar9170_rx_phy_status(struct ar9170 *ar,
+				 struct ar9170_rx_phystatus *phy,
+				 struct ieee80211_rx_status *status)
+{
+	int i;
 
-	if (error & AR9170_RX_ERROR_PLCP) {
-		status.flag |= RX_FLAG_FAILED_PLCP_CRC;
-		error &= ~AR9170_RX_ERROR_PLCP;
+	BUILD_BUG_ON(sizeof(struct ar9170_rx_phystatus) != 20);
+
+	for (i = 0; i < 3; i++)
+		if (phy->rssi[i] != 0x80)
+			status->antenna |= BIT(i);
+
+	/* post-process RSSI */
+	for (i = 0; i < 7; i++)
+		if (phy->rssi[i] & 0x80)
+			phy->rssi[i] = ((phy->rssi[i] & 0x7f) + 1) & 0x7f;
+
+	/* 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)
+{
+	struct sk_buff *skb;
+	int reserved = 0;
+	struct ieee80211_hdr *hdr = (void *) buf;
+
+	if (ieee80211_is_data_qos(hdr->frame_control)) {
+		u8 *qc = ieee80211_get_qos_ctl(hdr);
+		reserved += NET_IP_ALIGN;
+
+		if (*qc & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT)
+			reserved += NET_IP_ALIGN;
 	}
 
-	if (error & AR9170_RX_ERROR_FCS) {
-		status.flag |= RX_FLAG_FAILED_FCS_CRC;
-		error &= ~AR9170_RX_ERROR_FCS;
+	if (ieee80211_has_a4(hdr->frame_control))
+		reserved += NET_IP_ALIGN;
+
+	reserved = 32 + (reserved & NET_IP_ALIGN);
+
+	skb = dev_alloc_skb(len + reserved);
+	if (likely(skb)) {
+		skb_reserve(skb, reserved);
+		memcpy(skb_put(skb, len), buf, len);
 	}
 
-	decrypt = ar9170_get_decrypt_type(tail);
-	if (!(decrypt & AR9170_RX_ENC_SOFTWARE) &&
-	    decrypt != AR9170_ENC_ALG_NONE)
-		status.flag |= RX_FLAG_DECRYPTED;
+	return skb;
+}
 
-	/* ignore wrong RA errors */
-	error &= ~AR9170_RX_ERROR_WRONG_RA;
+/*
+ * If the frame alignment is right (or the kernel has
+ * CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS), and there
+ * is only a single MPDU in the USB frame, then we could
+ * submit to mac80211 the SKB directly. However, since
+ * there may be multiple packets in one SKB in stream
+ * mode, and we need to observe the proper ordering,
+ * this is non-trivial.
+ */
 
-	if (error & AR9170_RX_ERROR_DECRYPT) {
-		error &= ~AR9170_RX_ERROR_DECRYPT;
+static void ar9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len)
+{
+	struct ar9170_rx_head *head;
+	struct ar9170_rx_macstatus *mac;
+	struct ar9170_rx_phystatus *phy = NULL;
+	struct ieee80211_rx_status status;
+	struct sk_buff *skb;
+	int mpdu_len;
+
+	if (unlikely(!IS_STARTED(ar) || len < (sizeof(*mac))))
+		return ;
+
+	/* Received MPDU */
+	mpdu_len = len - sizeof(*mac);
+
+	mac = (void *)(buf + mpdu_len);
+	if (unlikely(mac->error & AR9170_RX_ERROR_FATAL)) {
+		/* this frame is too damaged and can't be used - drop it */
 
-		/*
-		 * Rx decryption is done in place,
-		 * the original data is lost anyway.
-		 */
 		return ;
 	}
 
-	/* drop any other error frames */
-	if ((error) && (net_ratelimit())) {
-		printk(KERN_DEBUG "%s: errors: %#x\n",
-		       wiphy_name(ar->hw->wiphy), error);
-		return;
+	switch (mac->status & AR9170_RX_STATUS_MPDU_MASK) {
+	case AR9170_RX_STATUS_MPDU_FIRST:
+		/* first mpdu packet has the plcp header */
+		if (likely(mpdu_len >= sizeof(struct ar9170_rx_head))) {
+			head = (void *) buf;
+			memcpy(&ar->rx_mpdu.plcp, (void *) buf,
+			       sizeof(struct ar9170_rx_head));
+
+			mpdu_len -= sizeof(struct ar9170_rx_head);
+			buf += sizeof(struct ar9170_rx_head);
+			ar->rx_mpdu.has_plcp = true;
+		} else {
+			if (ar9170_nag_limiter(ar))
+				printk(KERN_ERR "%s: plcp info is clipped.\n",
+				       wiphy_name(ar->hw->wiphy));
+			return ;
+		}
+		break;
+
+	case AR9170_RX_STATUS_MPDU_LAST:
+		/* last mpdu has a extra tail with phy status information */
+
+		if (likely(mpdu_len >= sizeof(struct ar9170_rx_phystatus))) {
+			mpdu_len -= sizeof(struct ar9170_rx_phystatus);
+			phy = (void *)(buf + mpdu_len);
+		} else {
+			if (ar9170_nag_limiter(ar))
+				printk(KERN_ERR "%s: frame tail is clipped.\n",
+				       wiphy_name(ar->hw->wiphy));
+			return ;
+		}
+
+	case AR9170_RX_STATUS_MPDU_MIDDLE:
+		/* middle mpdus are just data */
+		if (unlikely(!ar->rx_mpdu.has_plcp)) {
+			if (!ar9170_nag_limiter(ar))
+				return ;
+
+			printk(KERN_ERR "%s: rx stream did not start "
+					"with a first_mpdu frame tag.\n",
+			       wiphy_name(ar->hw->wiphy));
+
+			return ;
+		}
+
+		head = &ar->rx_mpdu.plcp;
+		break;
+
+	case AR9170_RX_STATUS_MPDU_SINGLE:
+		/* single mpdu - has plcp (head) and phy status (tail) */
+		head = (void *) buf;
+
+		mpdu_len -= sizeof(struct ar9170_rx_head);
+		mpdu_len -= sizeof(struct ar9170_rx_phystatus);
+
+		buf += sizeof(struct ar9170_rx_head);
+		phy = (void *)(buf + mpdu_len);
+		break;
+
+	default:
+		BUG_ON(1);
+		break;
 	}
 
-	buf += sizeof(struct ar9170_rx_head);
-	fc = *(__le16 *)buf;
+	if (unlikely(mpdu_len < FCS_LEN))
+		return ;
 
-	if (ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc))
-		reserved = 32 + 2;
-	else
-		reserved = 32;
+	memset(&status, 0, sizeof(status));
+	if (unlikely(ar9170_rx_mac_status(ar, head, mac, &status)))
+		return ;
 
-	skb = dev_alloc_skb(mpdu_len + reserved);
-	if (!skb)
-		return;
+	if (phy)
+		ar9170_rx_phy_status(ar, phy, &status);
 
-	skb_reserve(skb, reserved);
-	memcpy(skb_put(skb, mpdu_len), buf, mpdu_len);
-	ieee80211_rx_irqsafe(ar->hw, skb, &status);
+	skb = ar9170_rx_copy_data(buf, mpdu_len);
+	if (likely(skb))
+		ieee80211_rx_irqsafe(ar->hw, skb, &status);
 }
 
 void ar9170_rx(struct ar9170 *ar, struct sk_buff *skb)
 {
-	unsigned int i, tlen, resplen;
+	unsigned int i, tlen, resplen, wlen = 0, clen = 0;
 	u8 *tbuf, *respbuf;
 
 	tbuf = skb->data;
 	tlen = skb->len;
 
 	while (tlen >= 4) {
-		int clen = tbuf[1] << 8 | tbuf[0];
-		int wlen = (clen + 3) & ~3;
+		clen = tbuf[1] << 8 | tbuf[0];
+		wlen = ALIGN(clen, 4);
 
-		/*
-		 * parse stream (if any)
-		 */
+		/* check if this is stream has a valid tag.*/
 		if (tbuf[2] != 0 || tbuf[3] != 0x4e) {
-			printk(KERN_ERR "%s: missing tag!\n",
-			       wiphy_name(ar->hw->wiphy));
+			/*
+			 * TODO: handle the highly unlikely event that the
+			 * corrupted stream has the TAG at the right position.
+			 */
+
+			/* check if the frame can be repaired. */
+			if (!ar->rx_failover_missing) {
+				/* this is no "short read". */
+				if (ar9170_nag_limiter(ar)) {
+					printk(KERN_ERR "%s: missing tag!\n",
+					       wiphy_name(ar->hw->wiphy));
+					goto err_telluser;
+				} else
+					goto err_silent;
+			}
+
+			if (ar->rx_failover_missing > tlen) {
+				if (ar9170_nag_limiter(ar)) {
+					printk(KERN_ERR "%s: possible multi "
+					       "stream corruption!\n",
+					       wiphy_name(ar->hw->wiphy));
+					goto err_telluser;
+				} else
+					goto err_silent;
+			}
+
+			memcpy(skb_put(ar->rx_failover, tlen), tbuf, tlen);
+			ar->rx_failover_missing -= tlen;
+
+			if (ar->rx_failover_missing <= 0) {
+				/*
+				 * nested ar9170_rx call!
+				 * termination is guranteed, even when the
+				 * combined frame also have a element with
+				 * a bad tag.
+				 */
+
+				ar->rx_failover_missing = 0;
+				ar9170_rx(ar, ar->rx_failover);
+
+				skb_reset_tail_pointer(ar->rx_failover);
+				skb_trim(ar->rx_failover, 0);
+			}
+
 			return ;
 		}
+
+		/* check if stream is clipped */
 		if (wlen > tlen - 4) {
-			printk(KERN_ERR "%s: invalid RX (%d, %d, %d)\n",
-			       wiphy_name(ar->hw->wiphy), clen, wlen, tlen);
-			print_hex_dump(KERN_DEBUG, "data: ",
-				       DUMP_PREFIX_OFFSET,
-				       16, 1, tbuf, tlen, true);
+			if (ar->rx_failover_missing) {
+				/* TODO: handle double stream corruption. */
+				if (ar9170_nag_limiter(ar)) {
+					printk(KERN_ERR "%s: double rx stream "
+					       "corruption!\n",
+						wiphy_name(ar->hw->wiphy));
+					goto err_telluser;
+				} else
+					goto err_silent;
+			}
+
+			/*
+			 * save incomplete data set.
+			 * the firmware will resend the missing bits when
+			 * the rx - descriptor comes round again.
+			 */
+
+			memcpy(skb_put(ar->rx_failover, tlen), tbuf, tlen);
+			ar->rx_failover_missing = clen - tlen;
 			return ;
 		}
 		resplen = clen;
@@ -668,12 +1027,44 @@
 		if (i == 12)
 			ar9170_handle_command_response(ar, respbuf, resplen);
 		else
-			ar9170_handle_mpdu(ar, respbuf, resplen);
+			ar9170_handle_mpdu(ar, respbuf, clen);
 	}
 
-	if (tlen)
-		printk(KERN_ERR "%s: buffer remains!\n",
-		       wiphy_name(ar->hw->wiphy));
+	if (tlen) {
+		if (net_ratelimit())
+			printk(KERN_ERR "%s: %d bytes of unprocessed "
+					"data left in rx stream!\n",
+			       wiphy_name(ar->hw->wiphy), tlen);
+
+		goto err_telluser;
+	}
+
+	return ;
+
+err_telluser:
+	printk(KERN_ERR "%s: damaged RX stream data [want:%d, "
+			"data:%d, rx:%d, pending:%d ]\n",
+	       wiphy_name(ar->hw->wiphy), clen, wlen, tlen,
+	       ar->rx_failover_missing);
+
+	if (ar->rx_failover_missing)
+		print_hex_dump_bytes("rxbuf:", DUMP_PREFIX_OFFSET,
+				     ar->rx_failover->data,
+				     ar->rx_failover->len);
+
+	print_hex_dump_bytes("stream:", DUMP_PREFIX_OFFSET,
+			     skb->data, skb->len);
+
+	printk(KERN_ERR "%s: please check your hardware and cables, if "
+			"you see this message frequently.\n",
+	       wiphy_name(ar->hw->wiphy));
+
+err_silent:
+	if (ar->rx_failover_missing) {
+		skb_reset_tail_pointer(ar->rx_failover);
+		skb_trim(ar->rx_failover, 0);
+		ar->rx_failover_missing = 0;
+	}
 }
 
 #define AR9170_FILL_QUEUE(queue, ai_fs, cwmin, cwmax, _txop)		\
@@ -691,10 +1082,12 @@
 
 	mutex_lock(&ar->mutex);
 
+	ar->filter_changed = 0;
+
 	/* reinitialize queues statistics */
 	memset(&ar->tx_stats, 0, sizeof(ar->tx_stats));
-	for (i = 0; i < ARRAY_SIZE(ar->tx_stats); i++)
-		ar->tx_stats[i].limit = 8;
+	for (i = 0; i < __AR9170_NUM_TXQ; i++)
+		ar->tx_stats[i].limit = AR9170_TXQ_DEPTH;
 
 	/* reset QoS defaults */
 	AR9170_FILL_QUEUE(ar->edcf[0], 3, 15, 1023,  0); /* BEST EFFORT*/
@@ -703,6 +1096,8 @@
 	AR9170_FILL_QUEUE(ar->edcf[3], 2, 3,     7, 47); /* VOICE */
 	AR9170_FILL_QUEUE(ar->edcf[4], 2, 3,     7,  0); /* SPECIAL */
 
+	ar->bad_hw_nagger = jiffies;
+
 	err = ar->open(ar);
 	if (err)
 		goto out;
@@ -738,17 +1133,17 @@
 static void ar9170_op_stop(struct ieee80211_hw *hw)
 {
 	struct ar9170 *ar = hw->priv;
+	unsigned int i;
 
 	if (IS_STARTED(ar))
 		ar->state = AR9170_IDLE;
 
-	mutex_lock(&ar->mutex);
+	flush_workqueue(ar->hw->workqueue);
 
-	cancel_delayed_work_sync(&ar->tx_status_janitor);
+	cancel_delayed_work_sync(&ar->tx_janitor);
 	cancel_work_sync(&ar->filter_config_work);
 	cancel_work_sync(&ar->beacon_work);
-	skb_queue_purge(&ar->global_tx_status_waste);
-	skb_queue_purge(&ar->global_tx_status);
+	mutex_lock(&ar->mutex);
 
 	if (IS_ACCEPTING_CMD(ar)) {
 		ar9170_set_leds_state(ar, 0);
@@ -758,51 +1153,32 @@
 		ar->stop(ar);
 	}
 
+	for (i = 0; i < __AR9170_NUM_TXQ; i++) {
+		skb_queue_purge(&ar->tx_pending[i]);
+		skb_queue_purge(&ar->tx_status[i]);
+	}
 	mutex_unlock(&ar->mutex);
 }
 
-int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+static int ar9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb)
 {
-	struct ar9170 *ar = hw->priv;
 	struct ieee80211_hdr *hdr;
 	struct ar9170_tx_control *txc;
 	struct ieee80211_tx_info *info;
-	struct ieee80211_rate *rate = NULL;
 	struct ieee80211_tx_rate *txrate;
+	struct ar9170_tx_info *arinfo;
 	unsigned int queue = skb_get_queue_mapping(skb);
-	unsigned long flags = 0;
-	struct ar9170_sta_info *sta_info = NULL;
-	u32 power, chains;
 	u16 keytype = 0;
 	u16 len, icv = 0;
-	int err;
-	bool tx_status;
 
-	if (unlikely(!IS_STARTED(ar)))
-		goto err_free;
+	BUILD_BUG_ON(sizeof(*arinfo) > sizeof(info->rate_driver_data));
 
 	hdr = (void *)skb->data;
 	info = IEEE80211_SKB_CB(skb);
 	len = skb->len;
 
-	spin_lock_irqsave(&ar->tx_stats_lock, flags);
-	if (ar->tx_stats[queue].limit < ar->tx_stats[queue].len) {
-		spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
-		return NETDEV_TX_OK;
-	}
-
-	ar->tx_stats[queue].len++;
-	ar->tx_stats[queue].count++;
-	if (ar->tx_stats[queue].limit == ar->tx_stats[queue].len)
-		ieee80211_stop_queue(hw, queue);
-
-	spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
-
 	txc = (void *)skb_push(skb, sizeof(*txc));
 
-	tx_status = (((info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) != 0) ||
-		    ((info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) != 0));
-
 	if (info->control.hw_key) {
 		icv = info->control.hw_key->icv_len;
 
@@ -818,7 +1194,7 @@
 			break;
 		default:
 			WARN_ON(1);
-			goto err_dequeue;
+			goto err_out;
 		}
 	}
 
@@ -835,16 +1211,65 @@
 	if (info->flags & IEEE80211_TX_CTL_NO_ACK)
 		txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_NO_ACK);
 
-	if (info->flags & IEEE80211_TX_CTL_AMPDU)
-		txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_AGGR);
-
 	txrate = &info->control.rates[0];
-
 	if (txrate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT)
 		txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS);
 	else if (txrate->flags & IEEE80211_TX_RC_USE_RTS_CTS)
 		txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS);
 
+	arinfo = (void *)info->rate_driver_data;
+	arinfo->timeout = jiffies + msecs_to_jiffies(AR9170_QUEUE_TIMEOUT);
+
+	if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) &&
+	     (is_valid_ether_addr(ieee80211_get_DA(hdr)))) {
+		if (info->flags & IEEE80211_TX_CTL_AMPDU) {
+			if (unlikely(!info->control.sta))
+				goto err_out;
+
+			txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_AGGR);
+			arinfo->flags = AR9170_TX_FLAG_BLOCK_ACK;
+			goto out;
+		}
+
+		txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_RATE_PROBE);
+		/*
+		 * WARNING:
+		 * Putting the QoS queue bits into an unexplored territory is
+		 * certainly not elegant.
+		 *
+		 * In my defense: This idea provides a reasonable way to
+		 * smuggle valuable information to the tx_status callback.
+		 * Also, the idea behind this bit-abuse came straight from
+		 * the original driver code.
+		 */
+
+		txc->phy_control |=
+			cpu_to_le32(queue << AR9170_TX_PHY_QOS_SHIFT);
+		arinfo->flags = AR9170_TX_FLAG_WAIT_FOR_ACK;
+	} else {
+		arinfo->flags = AR9170_TX_FLAG_NO_ACK;
+	}
+
+out:
+	return 0;
+
+err_out:
+	skb_pull(skb, sizeof(*txc));
+	return -EINVAL;
+}
+
+static void ar9170_tx_prepare_phy(struct ar9170 *ar, struct sk_buff *skb)
+{
+	struct ar9170_tx_control *txc;
+	struct ieee80211_tx_info *info;
+	struct ieee80211_rate *rate = NULL;
+	struct ieee80211_tx_rate *txrate;
+	u32 power, chains;
+
+	txc = (void *) skb->data;
+	info = IEEE80211_SKB_CB(skb);
+	txrate = &info->control.rates[0];
+
 	if (txrate->flags & IEEE80211_TX_RC_GREEN_FIELD)
 		txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_GREENFIELD);
 
@@ -864,9 +1289,12 @@
 		u32 r = txrate->idx;
 		u8 *txpower;
 
+		/* heavy clip control */
+		txc->phy_control |= cpu_to_le32((r & 0x7) << 7);
+
 		r <<= AR9170_TX_PHY_MCS_SHIFT;
-		if (WARN_ON(r & ~AR9170_TX_PHY_MCS_MASK))
-			goto err_dequeue;
+		BUG_ON(r & ~AR9170_TX_PHY_MCS_MASK);
+
 		txc->phy_control |= cpu_to_le32(r & AR9170_TX_PHY_MCS_MASK);
 		txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_MOD_HT);
 
@@ -928,53 +1356,154 @@
 			chains = AR9170_TX_PHY_TXCHAIN_1;
 	}
 	txc->phy_control |= cpu_to_le32(chains << AR9170_TX_PHY_TXCHAIN_SHIFT);
+}
 
-	if (tx_status) {
-		txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_RATE_PROBE);
-		/*
-		 * WARNING:
-		 * Putting the QoS queue bits into an unexplored territory is
-		 * certainly not elegant.
-		 *
-		 * In my defense: This idea provides a reasonable way to
-		 * smuggle valuable information to the tx_status callback.
-		 * Also, the idea behind this bit-abuse came straight from
-		 * the original driver code.
-		 */
+static void ar9170_tx(struct ar9170 *ar)
+{
+	struct sk_buff *skb;
+	unsigned long flags;
+	struct ieee80211_tx_info *info;
+	struct ar9170_tx_info *arinfo;
+	unsigned int i, frames, frames_failed, remaining_space;
+	int err;
+	bool schedule_garbagecollector = false;
 
-		txc->phy_control |=
-			cpu_to_le32(queue << AR9170_TX_PHY_QOS_SHIFT);
+	BUILD_BUG_ON(sizeof(*arinfo) > sizeof(info->rate_driver_data));
 
-		if (info->control.sta) {
-			sta_info = (void *) info->control.sta->drv_priv;
-			skb_queue_tail(&sta_info->tx_status[queue], skb);
-		} else {
-			skb_queue_tail(&ar->global_tx_status, skb);
+	if (unlikely(!IS_STARTED(ar)))
+		return ;
+
+	remaining_space = AR9170_TX_MAX_PENDING;
+
+	for (i = 0; i < __AR9170_NUM_TXQ; i++) {
+		spin_lock_irqsave(&ar->tx_stats_lock, flags);
+		if (ar->tx_stats[i].len >= ar->tx_stats[i].limit) {
+#ifdef AR9170_QUEUE_DEBUG
+			printk(KERN_DEBUG "%s: queue %d full\n",
+			       wiphy_name(ar->hw->wiphy), i);
+
+			__ar9170_dump_txstats(ar);
+			printk(KERN_DEBUG "stuck frames: ===> \n");
+			ar9170_dump_txqueue(ar, &ar->tx_pending[i]);
+			ar9170_dump_txqueue(ar, &ar->tx_status[i]);
+#endif /* AR9170_QUEUE_DEBUG */
+			ieee80211_stop_queue(ar->hw, i);
+			spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
+			continue;
+		}
+
+		frames = min(ar->tx_stats[i].limit - ar->tx_stats[i].len,
+			     skb_queue_len(&ar->tx_pending[i]));
+
+		if (remaining_space < frames) {
+#ifdef AR9170_QUEUE_DEBUG
+			printk(KERN_DEBUG "%s: tx quota reached queue:%d, "
+			       "remaining slots:%d, needed:%d\n",
+			       wiphy_name(ar->hw->wiphy), i, remaining_space,
+			       frames);
+
+			ar9170_dump_txstats(ar);
+#endif /* AR9170_QUEUE_DEBUG */
+			frames = remaining_space;
+		}
+
+		ar->tx_stats[i].len += frames;
+		ar->tx_stats[i].count += frames;
+		spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
 
-			queue_delayed_work(ar->hw->workqueue,
-					   &ar->tx_status_janitor,
-					   msecs_to_jiffies(100));
+		if (!frames)
+			continue;
+
+		frames_failed = 0;
+		while (frames) {
+			skb = skb_dequeue(&ar->tx_pending[i]);
+			if (unlikely(!skb)) {
+				frames_failed += frames;
+				frames = 0;
+				break;
+			}
+
+			info = IEEE80211_SKB_CB(skb);
+			arinfo = (void *) info->rate_driver_data;
+
+			/* TODO: cancel stuck frames */
+			arinfo->timeout = jiffies +
+					  msecs_to_jiffies(AR9170_TX_TIMEOUT);
+
+#ifdef AR9170_QUEUE_DEBUG
+			printk(KERN_DEBUG "%s: send frame q:%d =>\n",
+			       wiphy_name(ar->hw->wiphy), i);
+			ar9170_print_txheader(ar, skb);
+#endif /* AR9170_QUEUE_DEBUG */
+
+			err = ar->tx(ar, skb);
+			if (unlikely(err)) {
+				frames_failed++;
+				dev_kfree_skb_any(skb);
+			} else {
+				remaining_space--;
+				schedule_garbagecollector = true;
+			}
+
+			frames--;
+		}
+
+#ifdef AR9170_QUEUE_DEBUG
+		printk(KERN_DEBUG "%s: ar9170_tx report for queue %d\n",
+		       wiphy_name(ar->hw->wiphy), i);
+
+		printk(KERN_DEBUG "%s: unprocessed pending frames left:\n",
+		       wiphy_name(ar->hw->wiphy));
+		ar9170_dump_txqueue(ar, &ar->tx_pending[i]);
+#endif /* AR9170_QUEUE_DEBUG */
+
+		if (unlikely(frames_failed)) {
+#ifdef AR9170_QUEUE_DEBUG
+			printk(KERN_DEBUG "%s: frames failed =>\n",
+			       wiphy_name(ar->hw->wiphy), frames_failed);
+#endif /* AR9170_QUEUE_DEBUG */
+
+			spin_lock_irqsave(&ar->tx_stats_lock, flags);
+			ar->tx_stats[i].len -= frames_failed;
+			ar->tx_stats[i].count -= frames_failed;
+			ieee80211_wake_queue(ar->hw, i);
+			spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
 		}
 	}
 
-	err = ar->tx(ar, skb, tx_status, 0);
-	if (unlikely(tx_status && err)) {
-		if (info->control.sta)
-			skb_unlink(skb, &sta_info->tx_status[queue]);
-		else
-			skb_unlink(skb, &ar->global_tx_status);
+	if (schedule_garbagecollector)
+		queue_delayed_work(ar->hw->workqueue,
+				   &ar->tx_janitor,
+				   msecs_to_jiffies(AR9170_JANITOR_DELAY));
+}
+
+int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+	struct ar9170 *ar = hw->priv;
+	struct ieee80211_tx_info *info;
+
+	if (unlikely(!IS_STARTED(ar)))
+		goto err_free;
+
+	if (unlikely(ar9170_tx_prepare(ar, skb)))
+		goto err_free;
+
+	info = IEEE80211_SKB_CB(skb);
+	if (info->flags & IEEE80211_TX_CTL_AMPDU) {
+		/* drop frame, we do not allow TX A-MPDU aggregation yet. */
+		goto err_free;
+	} else {
+		unsigned int queue = skb_get_queue_mapping(skb);
+
+		ar9170_tx_prepare_phy(ar, skb);
+		skb_queue_tail(&ar->tx_pending[queue], skb);
 	}
 
+	ar9170_tx(ar);
 	return NETDEV_TX_OK;
 
-err_dequeue:
-	spin_lock_irqsave(&ar->tx_stats_lock, flags);
-	ar->tx_stats[queue].len--;
-	ar->tx_stats[queue].count--;
-	spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
-
 err_free:
-	dev_kfree_skb(skb);
+	dev_kfree_skb_any(skb);
 	return NETDEV_TX_OK;
 }
 
@@ -1037,11 +1566,6 @@
 
 	mutex_lock(&ar->mutex);
 
-	if (changed & IEEE80211_CONF_CHANGE_RADIO_ENABLED) {
-		/* TODO */
-		err = 0;
-	}
-
 	if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) {
 		/* TODO */
 		err = 0;
@@ -1068,48 +1592,28 @@
 			goto out;
 	}
 
-	if (changed & IEEE80211_CONF_CHANGE_BEACON_INTERVAL) {
+	if (changed & BSS_CHANGED_BEACON_INT) {
 		err = ar9170_set_beacon_timers(ar);
 		if (err)
 			goto out;
 	}
 
 	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
-		err = ar9170_set_channel(ar, hw->conf.channel,
-					 AR9170_RFI_NONE, AR9170_BW_20);
+
+		/* adjust slot time for 5 GHz */
+		err = ar9170_set_slot_time(ar);
 		if (err)
 			goto out;
-		/* adjust slot time for 5 GHz */
-		if (hw->conf.channel->band == IEEE80211_BAND_5GHZ)
-			err = ar9170_write_reg(ar, AR9170_MAC_REG_SLOT_TIME,
-					       9 << 10);
-	}
-
-out:
-	mutex_unlock(&ar->mutex);
-	return err;
-}
-
-static int ar9170_op_config_interface(struct ieee80211_hw *hw,
-				      struct ieee80211_vif *vif,
-				      struct ieee80211_if_conf *conf)
-{
-	struct ar9170 *ar = hw->priv;
-	int err = 0;
-
-	mutex_lock(&ar->mutex);
-
-	if (conf->changed & IEEE80211_IFCC_BSSID) {
-		memcpy(ar->bssid, conf->bssid, ETH_ALEN);
-		err = ar9170_set_operating_mode(ar);
-	}
 
-	if (conf->changed & IEEE80211_IFCC_BEACON) {
-		err = ar9170_update_beacon(ar);
+		err = ar9170_set_dyn_sifs_ack(ar);
+		if (err)
+			goto out;
 
+		err = ar9170_set_channel(ar, hw->conf.channel,
+				AR9170_RFI_NONE,
+				nl80211_to_ar9170(hw->conf.channel_type));
 		if (err)
 			goto out;
-		err = ar9170_set_beacon_timers(ar);
 	}
 
 out:
@@ -1123,24 +1627,30 @@
 					 filter_config_work);
 	int err;
 
-	mutex_lock(&ar->mutex);
 	if (unlikely(!IS_STARTED(ar)))
-		goto unlock;
+		return ;
 
-	if (ar->filter_changed & AR9170_FILTER_CHANGED_PROMISC) {
+	mutex_lock(&ar->mutex);
+	if (test_and_clear_bit(AR9170_FILTER_CHANGED_MODE,
+			       &ar->filter_changed)) {
 		err = ar9170_set_operating_mode(ar);
 		if (err)
 			goto unlock;
 	}
 
-	if (ar->filter_changed & AR9170_FILTER_CHANGED_MULTICAST) {
+	if (test_and_clear_bit(AR9170_FILTER_CHANGED_MULTICAST,
+			       &ar->filter_changed)) {
 		err = ar9170_update_multicast(ar);
 		if (err)
 			goto unlock;
 	}
 
-	if (ar->filter_changed & AR9170_FILTER_CHANGED_FRAMEFILTER)
+	if (test_and_clear_bit(AR9170_FILTER_CHANGED_FRAMEFILTER,
+			       &ar->filter_changed)) {
 		err = ar9170_update_frame_filter(ar);
+		if (err)
+			goto unlock;
+	}
 
 unlock:
 	mutex_unlock(&ar->mutex);
@@ -1155,8 +1665,8 @@
 
 	/* mask supported flags */
 	*new_flags &= FIF_ALLMULTI | FIF_CONTROL | FIF_BCN_PRBRESP_PROMISC |
-		      FIF_PROMISC_IN_BSS;
-
+		      FIF_PROMISC_IN_BSS | FIF_FCSFAIL | FIF_PLCPFAIL;
+	ar->filter_state = *new_flags;
 	/*
 	 * We can support more by setting the sniffer bit and
 	 * then checking the error flags, later.
@@ -1170,7 +1680,7 @@
 			int i;
 
 			/* always get broadcast frames */
-			mchash = 1ULL << (0xff>>2);
+			mchash = 1ULL << (0xff >> 2);
 
 			for (i = 0; i < mc_count; i++) {
 				if (WARN_ON(!mclist))
@@ -1180,7 +1690,7 @@
 			}
 		ar->want_mc_hash = mchash;
 		}
-		ar->filter_changed |= AR9170_FILTER_CHANGED_MULTICAST;
+		set_bit(AR9170_FILTER_CHANGED_MULTICAST, &ar->filter_changed);
 	}
 
 	if (changed_flags & FIF_CONTROL) {
@@ -1196,12 +1706,14 @@
 		else
 			ar->want_filter = ar->cur_filter & ~filter;
 
-		ar->filter_changed |= AR9170_FILTER_CHANGED_FRAMEFILTER;
+		set_bit(AR9170_FILTER_CHANGED_FRAMEFILTER,
+			&ar->filter_changed);
 	}
 
 	if (changed_flags & FIF_PROMISC_IN_BSS) {
 		ar->sniffer_enabled = ((*new_flags) & FIF_PROMISC_IN_BSS) != 0;
-		ar->filter_changed |= AR9170_FILTER_CHANGED_PROMISC;
+		set_bit(AR9170_FILTER_CHANGED_MODE,
+			&ar->filter_changed);
 	}
 
 	if (likely(IS_STARTED(ar)))
@@ -1218,48 +1730,54 @@
 
 	mutex_lock(&ar->mutex);
 
-	ar9170_regwrite_begin(ar);
+	if (changed & BSS_CHANGED_BSSID) {
+		memcpy(ar->bssid, bss_conf->bssid, ETH_ALEN);
+		err = ar9170_set_operating_mode(ar);
+		if (err)
+			goto out;
+	}
 
-	if (changed & BSS_CHANGED_ASSOC) {
-		ar->state = bss_conf->assoc ? AR9170_ASSOCIATED : ar->state;
+	if (changed & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED)) {
+		err = ar9170_update_beacon(ar);
+		if (err)
+			goto out;
 
+		err = ar9170_set_beacon_timers(ar);
+		if (err)
+			goto out;
+	}
+
+	if (changed & BSS_CHANGED_ASSOC) {
 #ifndef CONFIG_AR9170_LEDS
 		/* enable assoc LED. */
 		err = ar9170_set_leds_state(ar, bss_conf->assoc ? 2 : 0);
 #endif /* CONFIG_AR9170_LEDS */
 	}
 
+	if (changed & BSS_CHANGED_BEACON_INT) {
+		err = ar9170_set_beacon_timers(ar);
+		if (err)
+			goto out;
+	}
+
 	if (changed & BSS_CHANGED_HT) {
 		/* TODO */
 		err = 0;
 	}
 
 	if (changed & BSS_CHANGED_ERP_SLOT) {
-		u32 slottime = 20;
-
-		if (bss_conf->use_short_slot)
-			slottime = 9;
-
-		ar9170_regwrite(AR9170_MAC_REG_SLOT_TIME, slottime << 10);
+		err = ar9170_set_slot_time(ar);
+		if (err)
+			goto out;
 	}
 
 	if (changed & BSS_CHANGED_BASIC_RATES) {
-		u32 cck, ofdm;
-
-		if (hw->conf.channel->band == IEEE80211_BAND_5GHZ) {
-			ofdm = bss_conf->basic_rates;
-			cck = 0;
-		} else {
-			/* four cck rates */
-			cck = bss_conf->basic_rates & 0xf;
-			ofdm = bss_conf->basic_rates >> 4;
-		}
-		ar9170_regwrite(AR9170_MAC_REG_BASIC_RATE,
-				ofdm << 8 | cck);
+		err = ar9170_set_basic_rates(ar);
+		if (err)
+			goto out;
 	}
 
-	ar9170_regwrite_finish();
-	err = ar9170_regwrite_result();
+out:
 	mutex_unlock(&ar->mutex);
 }
 
@@ -1298,7 +1816,7 @@
 
 	switch (key->alg) {
 	case ALG_WEP:
-		if (key->keylen == LEN_WEP40)
+		if (key->keylen == 5)
 			ktype = AR9170_ENC_ALG_WEP64;
 		else
 			ktype = AR9170_ENC_ALG_WEP128;
@@ -1411,43 +1929,6 @@
 			      enum sta_notify_cmd cmd,
 			      struct ieee80211_sta *sta)
 {
-	struct ar9170 *ar = hw->priv;
-	struct ar9170_sta_info *info = (void *) sta->drv_priv;
-	struct sk_buff *skb;
-	unsigned int i;
-
-	switch (cmd) {
-	case STA_NOTIFY_ADD:
-		for (i = 0; i < ar->hw->queues; i++)
-			skb_queue_head_init(&info->tx_status[i]);
-		break;
-
-	case STA_NOTIFY_REMOVE:
-
-		/*
-		 * transfer all outstanding frames that need a tx_status
-		 * reports to the global tx_status queue
-		 */
-
-		for (i = 0; i < ar->hw->queues; i++) {
-			while ((skb = skb_dequeue(&info->tx_status[i]))) {
-#ifdef AR9170_QUEUE_DEBUG
-				printk(KERN_DEBUG "%s: queueing frame in "
-					  "global tx_status queue =>\n",
-				       wiphy_name(ar->hw->wiphy));
-
-				ar9170_print_txheader(ar, skb);
-#endif /* AR9170_QUEUE_DEBUG */
-				skb_queue_tail(&ar->global_tx_status, skb);
-			}
-		}
-		queue_delayed_work(ar->hw->workqueue, &ar->tx_status_janitor,
-				   msecs_to_jiffies(100));
-		break;
-
-	default:
-		break;
-	}
 }
 
 static int ar9170_get_stats(struct ieee80211_hw *hw,
@@ -1486,18 +1967,37 @@
 	int ret;
 
 	mutex_lock(&ar->mutex);
-	if ((param) && !(queue > ar->hw->queues)) {
+	if (queue < __AR9170_NUM_TXQ) {
 		memcpy(&ar->edcf[ar9170_qos_hwmap[queue]],
 		       param, sizeof(*param));
 
 		ret = ar9170_set_qos(ar);
-	} else
+	} else {
 		ret = -EINVAL;
+	}
 
 	mutex_unlock(&ar->mutex);
 	return ret;
 }
 
+static int ar9170_ampdu_action(struct ieee80211_hw *hw,
+			       enum ieee80211_ampdu_mlme_action action,
+			       struct ieee80211_sta *sta, u16 tid, u16 *ssn)
+{
+	switch (action) {
+	case IEEE80211_AMPDU_RX_START:
+	case IEEE80211_AMPDU_RX_STOP:
+		/*
+		 * Something goes wrong -- RX locks up
+		 * after a while of receiving aggregated
+		 * frames -- not enabling for now.
+		 */
+		return -EOPNOTSUPP;
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
 static const struct ieee80211_ops ar9170_ops = {
 	.start			= ar9170_op_start,
 	.stop			= ar9170_op_stop,
@@ -1505,7 +2005,6 @@
 	.add_interface		= ar9170_op_add_interface,
 	.remove_interface	= ar9170_op_remove_interface,
 	.config			= ar9170_op_config,
-	.config_interface	= ar9170_op_config_interface,
 	.configure_filter	= ar9170_op_configure_filter,
 	.conf_tx		= ar9170_conf_tx,
 	.bss_info_changed	= ar9170_op_bss_info_changed,
@@ -1514,29 +2013,45 @@
 	.sta_notify		= ar9170_sta_notify,
 	.get_stats		= ar9170_get_stats,
 	.get_tx_stats		= ar9170_get_tx_stats,
+	.ampdu_action		= ar9170_ampdu_action,
 };
 
 void *ar9170_alloc(size_t priv_size)
 {
 	struct ieee80211_hw *hw;
 	struct ar9170 *ar;
+	struct sk_buff *skb;
 	int i;
 
+	/*
+	 * this buffer is used for rx stream reconstruction.
+	 * Under heavy load this device (or the transport layer?)
+	 * tends to split the streams into seperate rx descriptors.
+	 */
+
+	skb = __dev_alloc_skb(AR9170_MAX_RX_BUFFER_SIZE, GFP_KERNEL);
+	if (!skb)
+		goto err_nomem;
+
 	hw = ieee80211_alloc_hw(priv_size, &ar9170_ops);
 	if (!hw)
-		return ERR_PTR(-ENOMEM);
+		goto err_nomem;
 
 	ar = hw->priv;
 	ar->hw = hw;
+	ar->rx_failover = skb;
 
 	mutex_init(&ar->mutex);
 	spin_lock_init(&ar->cmdlock);
 	spin_lock_init(&ar->tx_stats_lock);
-	skb_queue_head_init(&ar->global_tx_status);
-	skb_queue_head_init(&ar->global_tx_status_waste);
+	for (i = 0; i < __AR9170_NUM_TXQ; i++) {
+		skb_queue_head_init(&ar->tx_status[i]);
+		skb_queue_head_init(&ar->tx_pending[i]);
+	}
+	ar9170_rx_reset_rx_mpdu(ar);
 	INIT_WORK(&ar->filter_config_work, ar9170_set_filters);
 	INIT_WORK(&ar->beacon_work, ar9170_new_beacon);
-	INIT_DELAYED_WORK(&ar->tx_status_janitor, ar9170_tx_status_janitor);
+	INIT_DELAYED_WORK(&ar->tx_janitor, ar9170_tx_janitor);
 
 	/* all hw supports 2.4 GHz, so set channel to 1 by default */
 	ar->channel = &ar9170_2ghz_chantable[0];
@@ -1561,6 +2076,10 @@
 		ar->noise[i] = -95; /* ATH_DEFAULT_NOISE_FLOOR */
 
 	return ar;
+
+err_nomem:
+	kfree_skb(skb);
+	return ERR_PTR(-ENOMEM);
 }
 
 static int ar9170_read_eeprom(struct ar9170 *ar)
@@ -1619,12 +2138,24 @@
 	else
 		ar->hw->channel_change_time = 80 * 1000;
 
+	ar->regulatory.current_rd = le16_to_cpu(ar->eeprom.reg_domain[0]);
+	ar->regulatory.current_rd_ext = le16_to_cpu(ar->eeprom.reg_domain[1]);
+
 	/* second part of wiphy init */
 	SET_IEEE80211_PERM_ADDR(ar->hw, addr);
 
 	return bands ? 0 : -EINVAL;
 }
 
+static int ar9170_reg_notifier(struct wiphy *wiphy,
+			struct regulatory_request *request)
+{
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct ar9170 *ar = hw->priv;
+
+	return ath_reg_notifier_apply(wiphy, request, &ar->regulatory);
+}
+
 int ar9170_register(struct ar9170 *ar, struct device *pdev)
 {
 	int err;
@@ -1634,10 +2165,18 @@
 	if (err)
 		goto err_out;
 
+	err = ath_regd_init(&ar->regulatory, ar->hw->wiphy,
+			    ar9170_reg_notifier);
+	if (err)
+		goto err_out;
+
 	err = ieee80211_register_hw(ar->hw);
 	if (err)
 		goto err_out;
 
+	if (!ath_is_world_regd(&ar->regulatory))
+		regulatory_hint(ar->hw->wiphy, ar->regulatory.alpha2);
+
 	err = ar9170_init_leds(ar);
 	if (err)
 		goto err_unreg;
@@ -1666,6 +2205,7 @@
 	ar9170_unregister_leds(ar);
 #endif /* CONFIG_AR9170_LEDS */
 
+	kfree_skb(ar->rx_failover);
 	ieee80211_unregister_hw(ar->hw);
 	mutex_destroy(&ar->mutex);
 }
diff -ruN a/drivers/net/wireless/ar9170/Makefile b/drivers/net/wireless/ar9170/Makefile
--- a/drivers/net/wireless/ar9170/Makefile	2009-08-16 23:19:38.000000000 +0200
+++ b/drivers/net/wireless/ar9170/Makefile	2009-09-02 16:52:17.000000000 +0200
@@ -1,3 +1,3 @@
-ar9170usb-objs := usb.o main.o cmd.o mac.o phy.o led.o
+ar9170usb-objs := usb.o main.o cmd.o mac.o phy.o led.o regd.o
 
 obj-$(CONFIG_AR9170_USB) += ar9170usb.o
diff -ruN a/drivers/net/wireless/ar9170/phy.c b/drivers/net/wireless/ar9170/phy.c
--- a/drivers/net/wireless/ar9170/phy.c	2009-08-16 23:19:38.000000000 +0200
+++ b/drivers/net/wireless/ar9170/phy.c	2009-08-28 02:59:04.000000000 +0200
@@ -401,7 +401,7 @@
 	int i, err;
 	u32 val;
 	bool is_2ghz = band == IEEE80211_BAND_2GHZ;
-	bool is_40mhz = false; /* XXX: for now */
+	bool is_40mhz = conf_is_ht40(&ar->hw->conf);
 
 	ar9170_regwrite_begin(ar);
 
@@ -1200,7 +1200,7 @@
 		return -ENOSYS;
 	}
 
-	if (0 /* 2 streams capable */)
+	if (ar->eeprom.tx_mask != 1)
 		tmp |= 0x100;
 
 	err = ar9170_write_reg(ar, 0x1c5804, tmp);
@@ -1214,7 +1214,7 @@
 	freqpar = ar9170_get_hw_dyn_params(channel, bw);
 
 	vals[0] = cpu_to_le32(channel->center_freq * 1000);
-	vals[1] = cpu_to_le32(bw == AR9170_BW_20 ? 0 : 1);
+	vals[1] = cpu_to_le32(conf_is_ht40(&ar->hw->conf));
 	vals[2] = cpu_to_le32(offs << 2 | 1);
 	vals[3] = cpu_to_le32(freqpar->coeff_exp);
 	vals[4] = cpu_to_le32(freqpar->coeff_man);
diff -ruN a/drivers/net/wireless/ar9170/regd.c b/drivers/net/wireless/ar9170/regd.c
--- a/drivers/net/wireless/ar9170/regd.c	1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/net/wireless/ar9170/regd.c	2009-09-03 08:07:02.000000000 +0200
@@ -0,0 +1,592 @@
+/*
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <net/cfg80211.h>
+#include <net/mac80211.h>
+#include "regd.h"
+#include "regd_common.h"
+
+/*
+ * This is a set of common rules used by our world regulatory domains.
+ * We have 12 world regulatory domains. To save space we consolidate
+ * the regulatory domains in 5 structures by frequency and change
+ * the flags on our reg_notifier() on a case by case basis.
+ */
+
+/* Only these channels all allow active scan on all world regulatory domains */
+#define ATH9K_2GHZ_CH01_11	REG_RULE(2412-10, 2462+10, 40, 0, 20, 0)
+
+/* We enable active scan on these a case by case basis by regulatory domain */
+#define ATH9K_2GHZ_CH12_13	REG_RULE(2467-10, 2472+10, 40, 0, 20,\
+					NL80211_RRF_PASSIVE_SCAN)
+#define ATH9K_2GHZ_CH14		REG_RULE(2484-10, 2484+10, 40, 0, 20,\
+				NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_OFDM)
+
+/* We allow IBSS on these on a case by case basis by regulatory domain */
+#define ATH9K_5GHZ_5150_5350	REG_RULE(5150-10, 5350+10, 40, 0, 30,\
+				NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
+#define ATH9K_5GHZ_5470_5850	REG_RULE(5470-10, 5850+10, 40, 0, 30,\
+				NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
+#define ATH9K_5GHZ_5725_5850	REG_RULE(5725-10, 5850+10, 40, 0, 30,\
+				NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
+
+#define ATH9K_2GHZ_ALL		ATH9K_2GHZ_CH01_11, \
+				ATH9K_2GHZ_CH12_13, \
+				ATH9K_2GHZ_CH14
+
+#define ATH9K_5GHZ_ALL		ATH9K_5GHZ_5150_5350, \
+				ATH9K_5GHZ_5470_5850
+/* This one skips what we call "mid band" */
+#define ATH9K_5GHZ_NO_MIDBAND	ATH9K_5GHZ_5150_5350, \
+				ATH9K_5GHZ_5725_5850
+
+/* Can be used for:
+ * 0x60, 0x61, 0x62 */
+static const struct ieee80211_regdomain ath_world_regdom_60_61_62 = {
+	.n_reg_rules = 5,
+	.alpha2 =  "99",
+	.reg_rules = {
+		ATH9K_2GHZ_ALL,
+		ATH9K_5GHZ_ALL,
+	}
+};
+
+/* Can be used by 0x63 and 0x65 */
+static const struct ieee80211_regdomain ath_world_regdom_63_65 = {
+	.n_reg_rules = 4,
+	.alpha2 =  "99",
+	.reg_rules = {
+		ATH9K_2GHZ_CH01_11,
+		ATH9K_2GHZ_CH12_13,
+		ATH9K_5GHZ_NO_MIDBAND,
+	}
+};
+
+/* Can be used by 0x64 only */
+static const struct ieee80211_regdomain ath_world_regdom_64 = {
+	.n_reg_rules = 3,
+	.alpha2 =  "99",
+	.reg_rules = {
+		ATH9K_2GHZ_CH01_11,
+		ATH9K_5GHZ_NO_MIDBAND,
+	}
+};
+
+/* Can be used by 0x66 and 0x69 */
+static const struct ieee80211_regdomain ath_world_regdom_66_69 = {
+	.n_reg_rules = 3,
+	.alpha2 =  "99",
+	.reg_rules = {
+		ATH9K_2GHZ_CH01_11,
+		ATH9K_5GHZ_ALL,
+	}
+};
+
+/* Can be used by 0x67, 0x6A and 0x68 */
+static const struct ieee80211_regdomain ath_world_regdom_67_68_6A = {
+	.n_reg_rules = 4,
+	.alpha2 =  "99",
+	.reg_rules = {
+		ATH9K_2GHZ_CH01_11,
+		ATH9K_2GHZ_CH12_13,
+		ATH9K_5GHZ_ALL,
+	}
+};
+
+static inline bool is_wwr_sku(u16 regd)
+{
+	return ((regd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) ||
+		(regd == WORLD);
+}
+
+static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg)
+{
+	return reg->current_rd & ~WORLDWIDE_ROAMING_FLAG;
+}
+
+bool ath_is_world_regd(struct ath_regulatory *reg)
+{
+	return is_wwr_sku(ath_regd_get_eepromRD(reg));
+}
+EXPORT_SYMBOL(ath_is_world_regd);
+
+static const struct ieee80211_regdomain *ath_default_world_regdomain(void)
+{
+	/* this is the most restrictive */
+	return &ath_world_regdom_64;
+}
+
+static const struct
+ieee80211_regdomain *ath_world_regdomain(struct ath_regulatory *reg)
+{
+	switch (reg->regpair->regDmnEnum) {
+	case 0x60:
+	case 0x61:
+	case 0x62:
+		return &ath_world_regdom_60_61_62;
+	case 0x63:
+	case 0x65:
+		return &ath_world_regdom_63_65;
+	case 0x64:
+		return &ath_world_regdom_64;
+	case 0x66:
+	case 0x69:
+		return &ath_world_regdom_66_69;
+	case 0x67:
+	case 0x68:
+	case 0x6A:
+		return &ath_world_regdom_67_68_6A;
+	default:
+		WARN_ON(1);
+		return ath_default_world_regdomain();
+	}
+}
+
+/* Frequency is one where radar detection is required */
+static bool ath_is_radar_freq(u16 center_freq)
+{
+	return (center_freq >= 5260 && center_freq <= 5700);
+}
+
+/*
+ * N.B: These exception rules do not apply radar freqs.
+ *
+ * - We enable adhoc (or beaconing) if allowed by 11d
+ * - We enable active scan if the channel is allowed by 11d
+ * - If no country IE has been processed and a we determine we have
+ *   received a beacon on a channel we can enable active scan and
+ *   adhoc (or beaconing).
+ */
+static void
+ath_reg_apply_beaconing_flags(struct wiphy *wiphy,
+			      enum nl80211_reg_initiator initiator)
+{
+	enum ieee80211_band band;
+	struct ieee80211_supported_band *sband;
+	const struct ieee80211_reg_rule *reg_rule;
+	struct ieee80211_channel *ch;
+	unsigned int i;
+	u32 bandwidth = 0;
+	int r;
+
+	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+
+		if (!wiphy->bands[band])
+			continue;
+
+		sband = wiphy->bands[band];
+
+		for (i = 0; i < sband->n_channels; i++) {
+
+			ch = &sband->channels[i];
+
+			if (ath_is_radar_freq(ch->center_freq) ||
+			    (ch->flags & IEEE80211_CHAN_RADAR))
+				continue;
+
+			if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
+				r = freq_reg_info(wiphy,
+						  ch->center_freq,
+						  bandwidth,
+						  &reg_rule);
+				if (r)
+					continue;
+				/*
+				 * If 11d had a rule for this channel ensure
+				 * we enable adhoc/beaconing if it allows us to
+				 * use it. Note that we would have disabled it
+				 * by applying our static world regdomain by
+				 * default during init, prior to calling our
+				 * regulatory_hint().
+				 */
+				if (!(reg_rule->flags &
+				    NL80211_RRF_NO_IBSS))
+					ch->flags &=
+					  ~IEEE80211_CHAN_NO_IBSS;
+				if (!(reg_rule->flags &
+				    NL80211_RRF_PASSIVE_SCAN))
+					ch->flags &=
+					  ~IEEE80211_CHAN_PASSIVE_SCAN;
+			} else {
+				if (ch->beacon_found)
+					ch->flags &= ~(IEEE80211_CHAN_NO_IBSS |
+					  IEEE80211_CHAN_PASSIVE_SCAN);
+			}
+		}
+	}
+
+}
+
+/* Allows active scan scan on Ch 12 and 13 */
+static void
+ath_reg_apply_active_scan_flags(struct wiphy *wiphy,
+				enum nl80211_reg_initiator initiator)
+{
+	struct ieee80211_supported_band *sband;
+	struct ieee80211_channel *ch;
+	const struct ieee80211_reg_rule *reg_rule;
+	u32 bandwidth = 0;
+	int r;
+
+	sband = wiphy->bands[IEEE80211_BAND_2GHZ];
+
+	/*
+	 * If no country IE has been received always enable active scan
+	 * on these channels. This is only done for specific regulatory SKUs
+	 */
+	if (initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
+		ch = &sband->channels[11]; /* CH 12 */
+		if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
+			ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
+		ch = &sband->channels[12]; /* CH 13 */
+		if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
+			ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
+		return;
+	}
+
+	/*
+	 * If a country IE has been recieved check its rule for this
+	 * channel first before enabling active scan. The passive scan
+	 * would have been enforced by the initial processing of our
+	 * custom regulatory domain.
+	 */
+
+	ch = &sband->channels[11]; /* CH 12 */
+	r = freq_reg_info(wiphy, ch->center_freq, bandwidth, &reg_rule);
+	if (!r) {
+		if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
+			if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
+				ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
+	}
+
+	ch = &sband->channels[12]; /* CH 13 */
+	r = freq_reg_info(wiphy, ch->center_freq, bandwidth, &reg_rule);
+	if (!r) {
+		if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
+			if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
+				ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
+	}
+}
+
+/* Always apply Radar/DFS rules on freq range 5260 MHz - 5700 MHz */
+static void ath_reg_apply_radar_flags(struct wiphy *wiphy)
+{
+	struct ieee80211_supported_band *sband;
+	struct ieee80211_channel *ch;
+	unsigned int i;
+
+	if (!wiphy->bands[IEEE80211_BAND_5GHZ])
+		return;
+
+	sband = wiphy->bands[IEEE80211_BAND_5GHZ];
+
+	for (i = 0; i < sband->n_channels; i++) {
+		ch = &sband->channels[i];
+		if (!ath_is_radar_freq(ch->center_freq))
+			continue;
+		/* We always enable radar detection/DFS on this
+		 * frequency range. Additionally we also apply on
+		 * this frequency range:
+		 * - If STA mode does not yet have DFS supports disable
+		 *   active scanning
+		 * - If adhoc mode does not support DFS yet then
+		 *   disable adhoc in the frequency.
+		 * - If AP mode does not yet support radar detection/DFS
+		 *   do not allow AP mode
+		 */
+		if (!(ch->flags & IEEE80211_CHAN_DISABLED))
+			ch->flags |= IEEE80211_CHAN_RADAR |
+				     IEEE80211_CHAN_NO_IBSS |
+				     IEEE80211_CHAN_PASSIVE_SCAN;
+	}
+}
+
+static void ath_reg_apply_world_flags(struct wiphy *wiphy,
+				      enum nl80211_reg_initiator initiator,
+				      struct ath_regulatory *reg)
+{
+	switch (reg->regpair->regDmnEnum) {
+	case 0x60:
+	case 0x63:
+	case 0x66:
+	case 0x67:
+		ath_reg_apply_beaconing_flags(wiphy, initiator);
+		break;
+	case 0x68:
+		ath_reg_apply_beaconing_flags(wiphy, initiator);
+		ath_reg_apply_active_scan_flags(wiphy, initiator);
+		break;
+	}
+	return;
+}
+
+int ath_reg_notifier_apply(struct wiphy *wiphy,
+			   struct regulatory_request *request,
+			   struct ath_regulatory *reg)
+{
+	/* We always apply this */
+	ath_reg_apply_radar_flags(wiphy);
+
+	switch (request->initiator) {
+	case NL80211_REGDOM_SET_BY_DRIVER:
+	case NL80211_REGDOM_SET_BY_CORE:
+	case NL80211_REGDOM_SET_BY_USER:
+		break;
+	case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+		if (ath_is_world_regd(reg))
+			ath_reg_apply_world_flags(wiphy, request->initiator,
+						  reg);
+		break;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(ath_reg_notifier_apply);
+
+static bool ath_regd_is_eeprom_valid(struct ath_regulatory *reg)
+{
+	 u16 rd = ath_regd_get_eepromRD(reg);
+	int i;
+
+	if (rd & COUNTRY_ERD_FLAG) {
+		/* EEPROM value is a country code */
+		u16 cc = rd & ~COUNTRY_ERD_FLAG;
+		printk(KERN_DEBUG
+		       "ath: EEPROM indicates we should expect "
+			"a country code\n");
+		for (i = 0; i < ARRAY_SIZE(allCountries); i++)
+			if (allCountries[i].countryCode == cc)
+				return true;
+	} else {
+		/* EEPROM value is a regpair value */
+		if (rd != CTRY_DEFAULT)
+			printk(KERN_DEBUG "ath: EEPROM indicates we "
+			       "should expect a direct regpair map\n");
+		for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++)
+			if (regDomainPairs[i].regDmnEnum == rd)
+				return true;
+	}
+	printk(KERN_DEBUG
+		 "ath: invalid regulatory domain/country code 0x%x\n", rd);
+	return false;
+}
+
+/* EEPROM country code to regpair mapping */
+static struct country_code_to_enum_rd*
+ath_regd_find_country(u16 countryCode)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
+		if (allCountries[i].countryCode == countryCode)
+			return &allCountries[i];
+	}
+	return NULL;
+}
+
+/* EEPROM rd code to regpair mapping */
+static struct country_code_to_enum_rd*
+ath_regd_find_country_by_rd(int regdmn)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
+		if (allCountries[i].regDmnEnum == regdmn)
+			return &allCountries[i];
+	}
+	return NULL;
+}
+
+/* Returns the map of the EEPROM set RD to a country code */
+static u16 ath_regd_get_default_country(u16 rd)
+{
+	if (rd & COUNTRY_ERD_FLAG) {
+		struct country_code_to_enum_rd *country = NULL;
+		u16 cc = rd & ~COUNTRY_ERD_FLAG;
+
+		country = ath_regd_find_country(cc);
+		if (country != NULL)
+			return cc;
+	}
+
+	return CTRY_DEFAULT;
+}
+
+static struct reg_dmn_pair_mapping*
+ath_get_regpair(int regdmn)
+{
+	int i;
+
+	if (regdmn == NO_ENUMRD)
+		return NULL;
+	for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
+		if (regDomainPairs[i].regDmnEnum == regdmn)
+			return &regDomainPairs[i];
+	}
+	return NULL;
+}
+
+static int
+ath_regd_init_wiphy(struct ath_regulatory *reg,
+		    struct wiphy *wiphy,
+		    int (*reg_notifier)(struct wiphy *wiphy,
+					struct regulatory_request *request))
+{
+	const struct ieee80211_regdomain *regd;
+
+	wiphy->reg_notifier = reg_notifier;
+	wiphy->strict_regulatory = true;
+
+	if (ath_is_world_regd(reg)) {
+		/*
+		 * Anything applied here (prior to wiphy registration) gets
+		 * saved on the wiphy orig_* parameters
+		 */
+		regd = ath_world_regdomain(reg);
+		wiphy->custom_regulatory = true;
+		wiphy->strict_regulatory = false;
+	} else {
+		/*
+		 * This gets applied in the case of the absense of CRDA,
+		 * it's our own custom world regulatory domain, similar to
+		 * cfg80211's but we enable passive scanning.
+		 */
+		regd = ath_default_world_regdomain();
+	}
+	wiphy_apply_custom_regulatory(wiphy, regd);
+	ath_reg_apply_radar_flags(wiphy);
+	ath_reg_apply_world_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER, reg);
+	return 0;
+}
+
+/*
+ * Some users have reported their EEPROM programmed with
+ * 0x8000 set, this is not a supported regulatory domain
+ * but since we have more than one user with it we need
+ * a solution for them. We default to 0x64, which is the
+ * default Atheros world regulatory domain.
+ */
+static void ath_regd_sanitize(struct ath_regulatory *reg)
+{
+	if (reg->current_rd != COUNTRY_ERD_FLAG)
+		return;
+	printk(KERN_DEBUG "ath: EEPROM regdomain sanitized\n");
+	reg->current_rd = 0x64;
+}
+
+int
+ath_regd_init(struct ath_regulatory *reg,
+	      struct wiphy *wiphy,
+	      int (*reg_notifier)(struct wiphy *wiphy,
+				  struct regulatory_request *request))
+{
+	struct country_code_to_enum_rd *country = NULL;
+	u16 regdmn;
+
+	if (!reg)
+		return -EINVAL;
+
+	ath_regd_sanitize(reg);
+
+	printk(KERN_DEBUG "ath: EEPROM regdomain: 0x%0x\n", reg->current_rd);
+
+	if (!ath_regd_is_eeprom_valid(reg)) {
+		printk(KERN_ERR "ath: Invalid EEPROM contents\n");
+		return -EINVAL;
+	}
+
+	regdmn = ath_regd_get_eepromRD(reg);
+	reg->country_code = ath_regd_get_default_country(regdmn);
+
+	if (reg->country_code == CTRY_DEFAULT &&
+	    regdmn == CTRY_DEFAULT) {
+		printk(KERN_DEBUG "ath: EEPROM indicates default "
+		       "country code should be used\n");
+		reg->country_code = CTRY_UNITED_STATES;
+	}
+
+	if (reg->country_code == CTRY_DEFAULT) {
+		country = NULL;
+	} else {
+		printk(KERN_DEBUG "ath: doing EEPROM country->regdmn "
+		       "map search\n");
+		country = ath_regd_find_country(reg->country_code);
+		if (country == NULL) {
+			printk(KERN_DEBUG
+				"ath: no valid country maps found for "
+				"country code: 0x%0x\n",
+				reg->country_code);
+			return -EINVAL;
+		} else {
+			regdmn = country->regDmnEnum;
+			printk(KERN_DEBUG "ath: country maps to "
+			       "regdmn code: 0x%0x\n",
+			       regdmn);
+		}
+	}
+
+	reg->regpair = ath_get_regpair(regdmn);
+
+	if (!reg->regpair) {
+		printk(KERN_DEBUG "ath: "
+			"No regulatory domain pair found, cannot continue\n");
+		return -EINVAL;
+	}
+
+	if (!country)
+		country = ath_regd_find_country_by_rd(regdmn);
+
+	if (country) {
+		reg->alpha2[0] = country->isoName[0];
+		reg->alpha2[1] = country->isoName[1];
+	} else {
+		reg->alpha2[0] = '0';
+		reg->alpha2[1] = '0';
+	}
+
+	printk(KERN_DEBUG "ath: Country alpha2 being used: %c%c\n",
+		reg->alpha2[0], reg->alpha2[1]);
+	printk(KERN_DEBUG "ath: Regpair used: 0x%0x\n",
+		reg->regpair->regDmnEnum);
+
+	ath_regd_init_wiphy(reg, wiphy, reg_notifier);
+	return 0;
+}
+EXPORT_SYMBOL(ath_regd_init);
+
+u32 ath_regd_get_band_ctl(struct ath_regulatory *reg,
+			  enum ieee80211_band band)
+{
+	if (!reg->regpair ||
+	    (reg->country_code == CTRY_DEFAULT &&
+	     is_wwr_sku(ath_regd_get_eepromRD(reg)))) {
+		return SD_NO_CTL;
+	}
+
+	switch (band) {
+	case IEEE80211_BAND_2GHZ:
+		return reg->regpair->reg_2ghz_ctl;
+	case IEEE80211_BAND_5GHZ:
+		return reg->regpair->reg_5ghz_ctl;
+	default:
+		return NO_CTL;
+	}
+
+	return NO_CTL;
+}
+EXPORT_SYMBOL(ath_regd_get_band_ctl);
diff -ruN a/drivers/net/wireless/ar9170/regd_common.h b/drivers/net/wireless/ar9170/regd_common.h
--- a/drivers/net/wireless/ar9170/regd_common.h	1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/net/wireless/ar9170/regd_common.h	2009-09-03 08:07:14.000000000 +0200
@@ -0,0 +1,473 @@
+/*
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef REGD_COMMON_H
+#define REGD_COMMON_H
+
+enum EnumRd {
+	NO_ENUMRD = 0x00,
+	NULL1_WORLD = 0x03,
+	NULL1_ETSIB = 0x07,
+	NULL1_ETSIC = 0x08,
+	FCC1_FCCA = 0x10,
+	FCC1_WORLD = 0x11,
+	FCC4_FCCA = 0x12,
+	FCC5_FCCA = 0x13,
+	FCC6_FCCA = 0x14,
+
+	FCC2_FCCA = 0x20,
+	FCC2_WORLD = 0x21,
+	FCC2_ETSIC = 0x22,
+	FCC6_WORLD = 0x23,
+	FRANCE_RES = 0x31,
+	FCC3_FCCA = 0x3A,
+	FCC3_WORLD = 0x3B,
+
+	ETSI1_WORLD = 0x37,
+	ETSI3_ETSIA = 0x32,
+	ETSI2_WORLD = 0x35,
+	ETSI3_WORLD = 0x36,
+	ETSI4_WORLD = 0x30,
+	ETSI4_ETSIC = 0x38,
+	ETSI5_WORLD = 0x39,
+	ETSI6_WORLD = 0x34,
+	ETSI_RESERVED = 0x33,
+
+	MKK1_MKKA = 0x40,
+	MKK1_MKKB = 0x41,
+	APL4_WORLD = 0x42,
+	MKK2_MKKA = 0x43,
+	APL_RESERVED = 0x44,
+	APL2_WORLD = 0x45,
+	APL2_APLC = 0x46,
+	APL3_WORLD = 0x47,
+	MKK1_FCCA = 0x48,
+	APL2_APLD = 0x49,
+	MKK1_MKKA1 = 0x4A,
+	MKK1_MKKA2 = 0x4B,
+	MKK1_MKKC = 0x4C,
+
+	APL3_FCCA = 0x50,
+	APL1_WORLD = 0x52,
+	APL1_FCCA = 0x53,
+	APL1_APLA = 0x54,
+	APL1_ETSIC = 0x55,
+	APL2_ETSIC = 0x56,
+	APL5_WORLD = 0x58,
+	APL6_WORLD = 0x5B,
+	APL7_FCCA = 0x5C,
+	APL8_WORLD = 0x5D,
+	APL9_WORLD = 0x5E,
+
+	WOR0_WORLD = 0x60,
+	WOR1_WORLD = 0x61,
+	WOR2_WORLD = 0x62,
+	WOR3_WORLD = 0x63,
+	WOR4_WORLD = 0x64,
+	WOR5_ETSIC = 0x65,
+
+	WOR01_WORLD = 0x66,
+	WOR02_WORLD = 0x67,
+	EU1_WORLD = 0x68,
+
+	WOR9_WORLD = 0x69,
+	WORA_WORLD = 0x6A,
+	WORB_WORLD = 0x6B,
+
+	MKK3_MKKB = 0x80,
+	MKK3_MKKA2 = 0x81,
+	MKK3_MKKC = 0x82,
+
+	MKK4_MKKB = 0x83,
+	MKK4_MKKA2 = 0x84,
+	MKK4_MKKC = 0x85,
+
+	MKK5_MKKB = 0x86,
+	MKK5_MKKA2 = 0x87,
+	MKK5_MKKC = 0x88,
+
+	MKK6_MKKB = 0x89,
+	MKK6_MKKA2 = 0x8A,
+	MKK6_MKKC = 0x8B,
+
+	MKK7_MKKB = 0x8C,
+	MKK7_MKKA2 = 0x8D,
+	MKK7_MKKC = 0x8E,
+
+	MKK8_MKKB = 0x8F,
+	MKK8_MKKA2 = 0x90,
+	MKK8_MKKC = 0x91,
+
+	MKK14_MKKA1 = 0x92,
+	MKK15_MKKA1 = 0x93,
+
+	MKK10_FCCA = 0xD0,
+	MKK10_MKKA1 = 0xD1,
+	MKK10_MKKC = 0xD2,
+	MKK10_MKKA2 = 0xD3,
+
+	MKK11_MKKA = 0xD4,
+	MKK11_FCCA = 0xD5,
+	MKK11_MKKA1 = 0xD6,
+	MKK11_MKKC = 0xD7,
+	MKK11_MKKA2 = 0xD8,
+
+	MKK12_MKKA = 0xD9,
+	MKK12_FCCA = 0xDA,
+	MKK12_MKKA1 = 0xDB,
+	MKK12_MKKC = 0xDC,
+	MKK12_MKKA2 = 0xDD,
+
+	MKK13_MKKB = 0xDE,
+
+	MKK3_MKKA = 0xF0,
+	MKK3_MKKA1 = 0xF1,
+	MKK3_FCCA = 0xF2,
+	MKK4_MKKA = 0xF3,
+	MKK4_MKKA1 = 0xF4,
+	MKK4_FCCA = 0xF5,
+	MKK9_MKKA = 0xF6,
+	MKK10_MKKA = 0xF7,
+	MKK6_MKKA1 = 0xF8,
+	MKK6_FCCA = 0xF9,
+	MKK7_MKKA1 = 0xFA,
+	MKK7_FCCA = 0xFB,
+	MKK9_FCCA = 0xFC,
+	MKK9_MKKA1 = 0xFD,
+	MKK9_MKKC = 0xFE,
+	MKK9_MKKA2 = 0xFF,
+
+	WORLD = 0x0199,
+	DEBUG_REG_DMN = 0x01ff,
+};
+
+enum ctl_group {
+	CTL_FCC = 0x10,
+	CTL_MKK = 0x40,
+	CTL_ETSI = 0x30,
+};
+
+/* Regpair to CTL band mapping */
+static struct reg_dmn_pair_mapping regDomainPairs[] = {
+	/* regpair, 5 GHz CTL, 2 GHz CTL */
+	{NO_ENUMRD, DEBUG_REG_DMN, DEBUG_REG_DMN},
+	{NULL1_WORLD, NO_CTL, CTL_ETSI},
+	{NULL1_ETSIB, NO_CTL, CTL_ETSI},
+	{NULL1_ETSIC, NO_CTL, CTL_ETSI},
+
+	{FCC2_FCCA, CTL_FCC, CTL_FCC},
+	{FCC2_WORLD, CTL_FCC, CTL_ETSI},
+	{FCC2_ETSIC, CTL_FCC, CTL_ETSI},
+	{FCC3_FCCA, CTL_FCC, CTL_FCC},
+	{FCC3_WORLD, CTL_FCC, CTL_ETSI},
+	{FCC4_FCCA, CTL_FCC, CTL_FCC},
+	{FCC5_FCCA, CTL_FCC, CTL_FCC},
+	{FCC6_FCCA, CTL_FCC, CTL_FCC},
+	{FCC6_WORLD, CTL_FCC, CTL_ETSI},
+
+	{ETSI1_WORLD, CTL_ETSI, CTL_ETSI},
+	{ETSI2_WORLD, CTL_ETSI, CTL_ETSI},
+	{ETSI3_WORLD, CTL_ETSI, CTL_ETSI},
+	{ETSI4_WORLD, CTL_ETSI, CTL_ETSI},
+	{ETSI5_WORLD, CTL_ETSI, CTL_ETSI},
+	{ETSI6_WORLD, CTL_ETSI, CTL_ETSI},
+
+	/* XXX: For ETSI3_ETSIA, Was NO_CTL meant for the 2 GHz band ? */
+	{ETSI3_ETSIA, CTL_ETSI, CTL_ETSI},
+	{FRANCE_RES, CTL_ETSI, CTL_ETSI},
+
+	{FCC1_WORLD, CTL_FCC, CTL_ETSI},
+	{FCC1_FCCA, CTL_FCC, CTL_FCC},
+	{APL1_WORLD, CTL_FCC, CTL_ETSI},
+	{APL2_WORLD, CTL_FCC, CTL_ETSI},
+	{APL3_WORLD, CTL_FCC, CTL_ETSI},
+	{APL4_WORLD, CTL_FCC, CTL_ETSI},
+	{APL5_WORLD, CTL_FCC, CTL_ETSI},
+	{APL6_WORLD, CTL_ETSI, CTL_ETSI},
+	{APL8_WORLD, CTL_ETSI, CTL_ETSI},
+	{APL9_WORLD, CTL_ETSI, CTL_ETSI},
+
+	{APL3_FCCA, CTL_FCC, CTL_FCC},
+	{APL1_ETSIC, CTL_FCC, CTL_ETSI},
+	{APL2_ETSIC, CTL_FCC, CTL_ETSI},
+	{APL2_APLD, CTL_FCC, NO_CTL},
+
+	{MKK1_MKKA, CTL_MKK, CTL_MKK},
+	{MKK1_MKKB, CTL_MKK, CTL_MKK},
+	{MKK1_FCCA, CTL_MKK, CTL_FCC},
+	{MKK1_MKKA1, CTL_MKK, CTL_MKK},
+	{MKK1_MKKA2, CTL_MKK, CTL_MKK},
+	{MKK1_MKKC, CTL_MKK, CTL_MKK},
+
+	{MKK2_MKKA, CTL_MKK, CTL_MKK},
+	{MKK3_MKKA, CTL_MKK, CTL_MKK},
+	{MKK3_MKKB, CTL_MKK, CTL_MKK},
+	{MKK3_MKKA1, CTL_MKK, CTL_MKK},
+	{MKK3_MKKA2, CTL_MKK, CTL_MKK},
+	{MKK3_MKKC, CTL_MKK, CTL_MKK},
+	{MKK3_FCCA, CTL_MKK, CTL_FCC},
+
+	{MKK4_MKKA, CTL_MKK, CTL_MKK},
+	{MKK4_MKKB, CTL_MKK, CTL_MKK},
+	{MKK4_MKKA1, CTL_MKK, CTL_MKK},
+	{MKK4_MKKA2, CTL_MKK, CTL_MKK},
+	{MKK4_MKKC, CTL_MKK, CTL_MKK},
+	{MKK4_FCCA, CTL_MKK, CTL_FCC},
+
+	{MKK5_MKKB, CTL_MKK, CTL_MKK},
+	{MKK5_MKKA2, CTL_MKK, CTL_MKK},
+	{MKK5_MKKC, CTL_MKK, CTL_MKK},
+
+	{MKK6_MKKB, CTL_MKK, CTL_MKK},
+	{MKK6_MKKA1, CTL_MKK, CTL_MKK},
+	{MKK6_MKKA2, CTL_MKK, CTL_MKK},
+	{MKK6_MKKC, CTL_MKK, CTL_MKK},
+	{MKK6_FCCA, CTL_MKK, CTL_FCC},
+
+	{MKK7_MKKB, CTL_MKK, CTL_MKK},
+	{MKK7_MKKA1, CTL_MKK, CTL_MKK},
+	{MKK7_MKKA2, CTL_MKK, CTL_MKK},
+	{MKK7_MKKC, CTL_MKK, CTL_MKK},
+	{MKK7_FCCA, CTL_MKK, CTL_FCC},
+
+	{MKK8_MKKB, CTL_MKK, CTL_MKK},
+	{MKK8_MKKA2, CTL_MKK, CTL_MKK},
+	{MKK8_MKKC, CTL_MKK, CTL_MKK},
+
+	{MKK9_MKKA, CTL_MKK, CTL_MKK},
+	{MKK9_FCCA, CTL_MKK, CTL_FCC},
+	{MKK9_MKKA1, CTL_MKK, CTL_MKK},
+	{MKK9_MKKA2, CTL_MKK, CTL_MKK},
+	{MKK9_MKKC, CTL_MKK, CTL_MKK},
+
+	{MKK10_MKKA, CTL_MKK, CTL_MKK},
+	{MKK10_FCCA, CTL_MKK, CTL_FCC},
+	{MKK10_MKKA1, CTL_MKK, CTL_MKK},
+	{MKK10_MKKA2, CTL_MKK, CTL_MKK},
+	{MKK10_MKKC, CTL_MKK, CTL_MKK},
+
+	{MKK11_MKKA, CTL_MKK, CTL_MKK},
+	{MKK11_FCCA, CTL_MKK, CTL_FCC},
+	{MKK11_MKKA1, CTL_MKK, CTL_MKK},
+	{MKK11_MKKA2, CTL_MKK, CTL_MKK},
+	{MKK11_MKKC, CTL_MKK, CTL_MKK},
+
+	{MKK12_MKKA, CTL_MKK, CTL_MKK},
+	{MKK12_FCCA, CTL_MKK, CTL_FCC},
+	{MKK12_MKKA1, CTL_MKK, CTL_MKK},
+	{MKK12_MKKA2, CTL_MKK, CTL_MKK},
+	{MKK12_MKKC, CTL_MKK, CTL_MKK},
+
+	{MKK13_MKKB, CTL_MKK, CTL_MKK},
+	{MKK14_MKKA1, CTL_MKK, CTL_MKK},
+	{MKK15_MKKA1, CTL_MKK, CTL_MKK},
+
+	{WOR0_WORLD, NO_CTL, NO_CTL},
+	{WOR1_WORLD, NO_CTL, NO_CTL},
+	{WOR2_WORLD, NO_CTL, NO_CTL},
+	{WOR3_WORLD, NO_CTL, NO_CTL},
+	{WOR4_WORLD, NO_CTL, NO_CTL},
+	{WOR5_ETSIC, NO_CTL, NO_CTL},
+	{WOR01_WORLD, NO_CTL, NO_CTL},
+	{WOR02_WORLD, NO_CTL, NO_CTL},
+	{EU1_WORLD, NO_CTL, NO_CTL},
+	{WOR9_WORLD, NO_CTL, NO_CTL},
+	{WORA_WORLD, NO_CTL, NO_CTL},
+	{WORB_WORLD, NO_CTL, NO_CTL},
+};
+
+static struct country_code_to_enum_rd allCountries[] = {
+	{CTRY_DEBUG, NO_ENUMRD, "DB"},
+	{CTRY_DEFAULT, FCC1_FCCA, "CO"},
+	{CTRY_ALBANIA, NULL1_WORLD, "AL"},
+	{CTRY_ALGERIA, NULL1_WORLD, "DZ"},
+	{CTRY_ARGENTINA, APL3_WORLD, "AR"},
+	{CTRY_ARMENIA, ETSI4_WORLD, "AM"},
+	{CTRY_AUSTRALIA, FCC2_WORLD, "AU"},
+	{CTRY_AUSTRALIA2, FCC6_WORLD, "AU"},
+	{CTRY_AUSTRIA, ETSI1_WORLD, "AT"},
+	{CTRY_AZERBAIJAN, ETSI4_WORLD, "AZ"},
+	{CTRY_BAHRAIN, APL6_WORLD, "BH"},
+	{CTRY_BELARUS, ETSI1_WORLD, "BY"},
+	{CTRY_BELGIUM, ETSI1_WORLD, "BE"},
+	{CTRY_BELGIUM2, ETSI4_WORLD, "BL"},
+	{CTRY_BELIZE, APL1_ETSIC, "BZ"},
+	{CTRY_BOLIVIA, APL1_ETSIC, "BO"},
+	{CTRY_BOSNIA_HERZ, ETSI1_WORLD, "BA"},
+	{CTRY_BRAZIL, FCC3_WORLD, "BR"},
+	{CTRY_BRUNEI_DARUSSALAM, APL1_WORLD, "BN"},
+	{CTRY_BULGARIA, ETSI6_WORLD, "BG"},
+	{CTRY_CANADA, FCC2_FCCA, "CA"},
+	{CTRY_CANADA2, FCC6_FCCA, "CA"},
+	{CTRY_CHILE, APL6_WORLD, "CL"},
+	{CTRY_CHINA, APL1_WORLD, "CN"},
+	{CTRY_COLOMBIA, FCC1_FCCA, "CO"},
+	{CTRY_COSTA_RICA, FCC1_WORLD, "CR"},
+	{CTRY_CROATIA, ETSI3_WORLD, "HR"},
+	{CTRY_CYPRUS, ETSI1_WORLD, "CY"},
+	{CTRY_CZECH, ETSI3_WORLD, "CZ"},
+	{CTRY_DENMARK, ETSI1_WORLD, "DK"},
+	{CTRY_DOMINICAN_REPUBLIC, FCC1_FCCA, "DO"},
+	{CTRY_ECUADOR, FCC1_WORLD, "EC"},
+	{CTRY_EGYPT, ETSI3_WORLD, "EG"},
+	{CTRY_EL_SALVADOR, FCC1_WORLD, "SV"},
+	{CTRY_ESTONIA, ETSI1_WORLD, "EE"},
+	{CTRY_FINLAND, ETSI1_WORLD, "FI"},
+	{CTRY_FRANCE, ETSI1_WORLD, "FR"},
+	{CTRY_GEORGIA, ETSI4_WORLD, "GE"},
+	{CTRY_GERMANY, ETSI1_WORLD, "DE"},
+	{CTRY_GREECE, ETSI1_WORLD, "GR"},
+	{CTRY_GUATEMALA, FCC1_FCCA, "GT"},
+	{CTRY_HONDURAS, NULL1_WORLD, "HN"},
+	{CTRY_HONG_KONG, FCC2_WORLD, "HK"},
+	{CTRY_HUNGARY, ETSI1_WORLD, "HU"},
+	{CTRY_ICELAND, ETSI1_WORLD, "IS"},
+	{CTRY_INDIA, APL6_WORLD, "IN"},
+	{CTRY_INDONESIA, APL1_WORLD, "ID"},
+	{CTRY_IRAN, APL1_WORLD, "IR"},
+	{CTRY_IRELAND, ETSI1_WORLD, "IE"},
+	{CTRY_ISRAEL, NULL1_WORLD, "IL"},
+	{CTRY_ITALY, ETSI1_WORLD, "IT"},
+	{CTRY_JAMAICA, ETSI1_WORLD, "JM"},
+
+	{CTRY_JAPAN, MKK1_MKKA, "JP"},
+	{CTRY_JAPAN1, MKK1_MKKB, "JP"},
+	{CTRY_JAPAN2, MKK1_FCCA, "JP"},
+	{CTRY_JAPAN3, MKK2_MKKA, "JP"},
+	{CTRY_JAPAN4, MKK1_MKKA1, "JP"},
+	{CTRY_JAPAN5, MKK1_MKKA2, "JP"},
+	{CTRY_JAPAN6, MKK1_MKKC, "JP"},
+	{CTRY_JAPAN7, MKK3_MKKB, "JP"},
+	{CTRY_JAPAN8, MKK3_MKKA2, "JP"},
+	{CTRY_JAPAN9, MKK3_MKKC, "JP"},
+	{CTRY_JAPAN10, MKK4_MKKB, "JP"},
+	{CTRY_JAPAN11, MKK4_MKKA2, "JP"},
+	{CTRY_JAPAN12, MKK4_MKKC, "JP"},
+	{CTRY_JAPAN13, MKK5_MKKB, "JP"},
+	{CTRY_JAPAN14, MKK5_MKKA2, "JP"},
+	{CTRY_JAPAN15, MKK5_MKKC, "JP"},
+	{CTRY_JAPAN16, MKK6_MKKB, "JP"},
+	{CTRY_JAPAN17, MKK6_MKKA2, "JP"},
+	{CTRY_JAPAN18, MKK6_MKKC, "JP"},
+	{CTRY_JAPAN19, MKK7_MKKB, "JP"},
+	{CTRY_JAPAN20, MKK7_MKKA2, "JP"},
+	{CTRY_JAPAN21, MKK7_MKKC, "JP"},
+	{CTRY_JAPAN22, MKK8_MKKB, "JP"},
+	{CTRY_JAPAN23, MKK8_MKKA2, "JP"},
+	{CTRY_JAPAN24, MKK8_MKKC, "JP"},
+	{CTRY_JAPAN25, MKK3_MKKA, "JP"},
+	{CTRY_JAPAN26, MKK3_MKKA1, "JP"},
+	{CTRY_JAPAN27, MKK3_FCCA, "JP"},
+	{CTRY_JAPAN28, MKK4_MKKA1, "JP"},
+	{CTRY_JAPAN29, MKK4_FCCA, "JP"},
+	{CTRY_JAPAN30, MKK6_MKKA1, "JP"},
+	{CTRY_JAPAN31, MKK6_FCCA, "JP"},
+	{CTRY_JAPAN32, MKK7_MKKA1, "JP"},
+	{CTRY_JAPAN33, MKK7_FCCA, "JP"},
+	{CTRY_JAPAN34, MKK9_MKKA, "JP"},
+	{CTRY_JAPAN35, MKK10_MKKA, "JP"},
+	{CTRY_JAPAN36, MKK4_MKKA, "JP"},
+	{CTRY_JAPAN37, MKK9_FCCA, "JP"},
+	{CTRY_JAPAN38, MKK9_MKKA1, "JP"},
+	{CTRY_JAPAN39, MKK9_MKKC, "JP"},
+	{CTRY_JAPAN40, MKK9_MKKA2, "JP"},
+	{CTRY_JAPAN41, MKK10_FCCA, "JP"},
+	{CTRY_JAPAN42, MKK10_MKKA1, "JP"},
+	{CTRY_JAPAN43, MKK10_MKKC, "JP"},
+	{CTRY_JAPAN44, MKK10_MKKA2, "JP"},
+	{CTRY_JAPAN45, MKK11_MKKA, "JP"},
+	{CTRY_JAPAN46, MKK11_FCCA, "JP"},
+	{CTRY_JAPAN47, MKK11_MKKA1, "JP"},
+	{CTRY_JAPAN48, MKK11_MKKC, "JP"},
+	{CTRY_JAPAN49, MKK11_MKKA2, "JP"},
+	{CTRY_JAPAN50, MKK12_MKKA, "JP"},
+	{CTRY_JAPAN51, MKK12_FCCA, "JP"},
+	{CTRY_JAPAN52, MKK12_MKKA1, "JP"},
+	{CTRY_JAPAN53, MKK12_MKKC, "JP"},
+	{CTRY_JAPAN54, MKK12_MKKA2, "JP"},
+	{CTRY_JAPAN57, MKK13_MKKB, "JP"},
+	{CTRY_JAPAN58, MKK14_MKKA1, "JP"},
+	{CTRY_JAPAN59, MKK15_MKKA1, "JP"},
+
+	{CTRY_JORDAN, ETSI2_WORLD, "JO"},
+	{CTRY_KAZAKHSTAN, NULL1_WORLD, "KZ"},
+	{CTRY_KOREA_NORTH, APL9_WORLD, "KP"},
+	{CTRY_KOREA_ROC, APL9_WORLD, "KR"},
+	{CTRY_KOREA_ROC2, APL2_WORLD, "K2"},
+	{CTRY_KOREA_ROC3, APL9_WORLD, "K3"},
+	{CTRY_KUWAIT, NULL1_WORLD, "KW"},
+	{CTRY_LATVIA, ETSI1_WORLD, "LV"},
+	{CTRY_LEBANON, NULL1_WORLD, "LB"},
+	{CTRY_LIECHTENSTEIN, ETSI1_WORLD, "LI"},
+	{CTRY_LITHUANIA, ETSI1_WORLD, "LT"},
+	{CTRY_LUXEMBOURG, ETSI1_WORLD, "LU"},
+	{CTRY_MACAU, FCC2_WORLD, "MO"},
+	{CTRY_MACEDONIA, NULL1_WORLD, "MK"},
+	{CTRY_MALAYSIA, APL8_WORLD, "MY"},
+	{CTRY_MALTA, ETSI1_WORLD, "MT"},
+	{CTRY_MEXICO, FCC1_FCCA, "MX"},
+	{CTRY_MONACO, ETSI4_WORLD, "MC"},
+	{CTRY_MOROCCO, NULL1_WORLD, "MA"},
+	{CTRY_NEPAL, APL1_WORLD, "NP"},
+	{CTRY_NETHERLANDS, ETSI1_WORLD, "NL"},
+	{CTRY_NETHERLANDS_ANTILLES, ETSI1_WORLD, "AN"},
+	{CTRY_NEW_ZEALAND, FCC2_ETSIC, "NZ"},
+	{CTRY_NORWAY, ETSI1_WORLD, "NO"},
+	{CTRY_OMAN, APL6_WORLD, "OM"},
+	{CTRY_PAKISTAN, NULL1_WORLD, "PK"},
+	{CTRY_PANAMA, FCC1_FCCA, "PA"},
+	{CTRY_PAPUA_NEW_GUINEA, FCC1_WORLD, "PG"},
+	{CTRY_PERU, APL1_WORLD, "PE"},
+	{CTRY_PHILIPPINES, APL1_WORLD, "PH"},
+	{CTRY_POLAND, ETSI1_WORLD, "PL"},
+	{CTRY_PORTUGAL, ETSI1_WORLD, "PT"},
+	{CTRY_PUERTO_RICO, FCC1_FCCA, "PR"},
+	{CTRY_QATAR, NULL1_WORLD, "QA"},
+	{CTRY_ROMANIA, NULL1_WORLD, "RO"},
+	{CTRY_RUSSIA, NULL1_WORLD, "RU"},
+	{CTRY_SAUDI_ARABIA, NULL1_WORLD, "SA"},
+	{CTRY_SERBIA_MONTENEGRO, ETSI1_WORLD, "CS"},
+	{CTRY_SINGAPORE, APL6_WORLD, "SG"},
+	{CTRY_SLOVAKIA, ETSI1_WORLD, "SK"},
+	{CTRY_SLOVENIA, ETSI1_WORLD, "SI"},
+	{CTRY_SOUTH_AFRICA, FCC3_WORLD, "ZA"},
+	{CTRY_SPAIN, ETSI1_WORLD, "ES"},
+	{CTRY_SRI_LANKA, FCC3_WORLD, "LK"},
+	{CTRY_SWEDEN, ETSI1_WORLD, "SE"},
+	{CTRY_SWITZERLAND, ETSI1_WORLD, "CH"},
+	{CTRY_SYRIA, NULL1_WORLD, "SY"},
+	{CTRY_TAIWAN, APL3_FCCA, "TW"},
+	{CTRY_THAILAND, NULL1_WORLD, "TH"},
+	{CTRY_TRINIDAD_Y_TOBAGO, ETSI4_WORLD, "TT"},
+	{CTRY_TUNISIA, ETSI3_WORLD, "TN"},
+	{CTRY_TURKEY, ETSI3_WORLD, "TR"},
+	{CTRY_UKRAINE, NULL1_WORLD, "UA"},
+	{CTRY_UAE, NULL1_WORLD, "AE"},
+	{CTRY_UNITED_KINGDOM, ETSI1_WORLD, "GB"},
+	{CTRY_UNITED_STATES, FCC3_FCCA, "US"},
+	/* This "PS" is for US public safety actually... to support this we
+	 * would need to assign new special alpha2 to CRDA db as with the world
+	 * regdomain and use another alpha2 */
+	{CTRY_UNITED_STATES_FCC49, FCC4_FCCA, "PS"},
+	{CTRY_URUGUAY, APL2_WORLD, "UY"},
+	{CTRY_UZBEKISTAN, FCC3_FCCA, "UZ"},
+	{CTRY_VENEZUELA, APL2_ETSIC, "VE"},
+	{CTRY_VIET_NAM, NULL1_WORLD, "VN"},
+	{CTRY_YEMEN, NULL1_WORLD, "YE"},
+	{CTRY_ZIMBABWE, NULL1_WORLD, "ZW"},
+};
+
+#endif
diff -ruN a/drivers/net/wireless/ar9170/regd.h b/drivers/net/wireless/ar9170/regd.h
--- a/drivers/net/wireless/ar9170/regd.h	1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/net/wireless/ar9170/regd.h	2009-09-02 16:50:45.000000000 +0200
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef REGD_H
+#define REGD_H
+
+#include <linux/nl80211.h>
+
+#include <net/cfg80211.h>
+
+#define NO_CTL 0xff
+#define SD_NO_CTL               0xE0
+#define NO_CTL                  0xff
+#define CTL_MODE_M              7
+#define CTL_11A                 0
+#define CTL_11B                 1
+#define CTL_11G                 2
+#define CTL_2GHT20              5
+#define CTL_5GHT20              6
+#define CTL_2GHT40              7
+#define CTL_5GHT40              8
+
+#define CTRY_DEBUG 0x1ff
+#define CTRY_DEFAULT 0
+
+#define COUNTRY_ERD_FLAG        0x8000
+#define WORLDWIDE_ROAMING_FLAG  0x4000
+
+#define MULTI_DOMAIN_MASK 0xFF00
+
+#define WORLD_SKU_MASK          0x00F0
+#define WORLD_SKU_PREFIX        0x0060
+
+#define CHANNEL_HALF_BW         10
+#define CHANNEL_QUARTER_BW      5
+
+struct reg_dmn_pair_mapping {
+	u16 regDmnEnum;
+	u16 reg_5ghz_ctl;
+	u16 reg_2ghz_ctl;
+};
+
+struct country_code_to_enum_rd {
+	u16 countryCode;
+	u16 regDmnEnum;
+	const char *isoName;
+};
+
+struct ath_regulatory {
+	char alpha2[2];
+	u16 country_code;
+	u16 max_power_level;
+	u32 tp_scale;
+	u16 current_rd;
+	u16 current_rd_ext;
+	int16_t power_limit;
+	struct reg_dmn_pair_mapping *regpair;
+};
+
+enum CountryCode {
+	CTRY_ALBANIA = 8,
+	CTRY_ALGERIA = 12,
+	CTRY_ARGENTINA = 32,
+	CTRY_ARMENIA = 51,
+	CTRY_AUSTRALIA = 36,
+	CTRY_AUSTRIA = 40,
+	CTRY_AZERBAIJAN = 31,
+	CTRY_BAHRAIN = 48,
+	CTRY_BELARUS = 112,
+	CTRY_BELGIUM = 56,
+	CTRY_BELIZE = 84,
+	CTRY_BOLIVIA = 68,
+	CTRY_BOSNIA_HERZ = 70,
+	CTRY_BRAZIL = 76,
+	CTRY_BRUNEI_DARUSSALAM = 96,
+	CTRY_BULGARIA = 100,
+	CTRY_CANADA = 124,
+	CTRY_CHILE = 152,
+	CTRY_CHINA = 156,
+	CTRY_COLOMBIA = 170,
+	CTRY_COSTA_RICA = 188,
+	CTRY_CROATIA = 191,
+	CTRY_CYPRUS = 196,
+	CTRY_CZECH = 203,
+	CTRY_DENMARK = 208,
+	CTRY_DOMINICAN_REPUBLIC = 214,
+	CTRY_ECUADOR = 218,
+	CTRY_EGYPT = 818,
+	CTRY_EL_SALVADOR = 222,
+	CTRY_ESTONIA = 233,
+	CTRY_FAEROE_ISLANDS = 234,
+	CTRY_FINLAND = 246,
+	CTRY_FRANCE = 250,
+	CTRY_GEORGIA = 268,
+	CTRY_GERMANY = 276,
+	CTRY_GREECE = 300,
+	CTRY_GUATEMALA = 320,
+	CTRY_HONDURAS = 340,
+	CTRY_HONG_KONG = 344,
+	CTRY_HUNGARY = 348,
+	CTRY_ICELAND = 352,
+	CTRY_INDIA = 356,
+	CTRY_INDONESIA = 360,
+	CTRY_IRAN = 364,
+	CTRY_IRAQ = 368,
+	CTRY_IRELAND = 372,
+	CTRY_ISRAEL = 376,
+	CTRY_ITALY = 380,
+	CTRY_JAMAICA = 388,
+	CTRY_JAPAN = 392,
+	CTRY_JORDAN = 400,
+	CTRY_KAZAKHSTAN = 398,
+	CTRY_KENYA = 404,
+	CTRY_KOREA_NORTH = 408,
+	CTRY_KOREA_ROC = 410,
+	CTRY_KOREA_ROC2 = 411,
+	CTRY_KOREA_ROC3 = 412,
+	CTRY_KUWAIT = 414,
+	CTRY_LATVIA = 428,
+	CTRY_LEBANON = 422,
+	CTRY_LIBYA = 434,
+	CTRY_LIECHTENSTEIN = 438,
+	CTRY_LITHUANIA = 440,
+	CTRY_LUXEMBOURG = 442,
+	CTRY_MACAU = 446,
+	CTRY_MACEDONIA = 807,
+	CTRY_MALAYSIA = 458,
+	CTRY_MALTA = 470,
+	CTRY_MEXICO = 484,
+	CTRY_MONACO = 492,
+	CTRY_MOROCCO = 504,
+	CTRY_NEPAL = 524,
+	CTRY_NETHERLANDS = 528,
+	CTRY_NETHERLANDS_ANTILLES = 530,
+	CTRY_NEW_ZEALAND = 554,
+	CTRY_NICARAGUA = 558,
+	CTRY_NORWAY = 578,
+	CTRY_OMAN = 512,
+	CTRY_PAKISTAN = 586,
+	CTRY_PANAMA = 591,
+	CTRY_PAPUA_NEW_GUINEA = 598,
+	CTRY_PARAGUAY = 600,
+	CTRY_PERU = 604,
+	CTRY_PHILIPPINES = 608,
+	CTRY_POLAND = 616,
+	CTRY_PORTUGAL = 620,
+	CTRY_PUERTO_RICO = 630,
+	CTRY_QATAR = 634,
+	CTRY_ROMANIA = 642,
+	CTRY_RUSSIA = 643,
+	CTRY_SAUDI_ARABIA = 682,
+	CTRY_SERBIA_MONTENEGRO = 891,
+	CTRY_SINGAPORE = 702,
+	CTRY_SLOVAKIA = 703,
+	CTRY_SLOVENIA = 705,
+	CTRY_SOUTH_AFRICA = 710,
+	CTRY_SPAIN = 724,
+	CTRY_SRI_LANKA = 144,
+	CTRY_SWEDEN = 752,
+	CTRY_SWITZERLAND = 756,
+	CTRY_SYRIA = 760,
+	CTRY_TAIWAN = 158,
+	CTRY_THAILAND = 764,
+	CTRY_TRINIDAD_Y_TOBAGO = 780,
+	CTRY_TUNISIA = 788,
+	CTRY_TURKEY = 792,
+	CTRY_UAE = 784,
+	CTRY_UKRAINE = 804,
+	CTRY_UNITED_KINGDOM = 826,
+	CTRY_UNITED_STATES = 840,
+	CTRY_UNITED_STATES_FCC49 = 842,
+	CTRY_URUGUAY = 858,
+	CTRY_UZBEKISTAN = 860,
+	CTRY_VENEZUELA = 862,
+	CTRY_VIET_NAM = 704,
+	CTRY_YEMEN = 887,
+	CTRY_ZIMBABWE = 716,
+	CTRY_JAPAN1 = 393,
+	CTRY_JAPAN2 = 394,
+	CTRY_JAPAN3 = 395,
+	CTRY_JAPAN4 = 396,
+	CTRY_JAPAN5 = 397,
+	CTRY_JAPAN6 = 4006,
+	CTRY_JAPAN7 = 4007,
+	CTRY_JAPAN8 = 4008,
+	CTRY_JAPAN9 = 4009,
+	CTRY_JAPAN10 = 4010,
+	CTRY_JAPAN11 = 4011,
+	CTRY_JAPAN12 = 4012,
+	CTRY_JAPAN13 = 4013,
+	CTRY_JAPAN14 = 4014,
+	CTRY_JAPAN15 = 4015,
+	CTRY_JAPAN16 = 4016,
+	CTRY_JAPAN17 = 4017,
+	CTRY_JAPAN18 = 4018,
+	CTRY_JAPAN19 = 4019,
+	CTRY_JAPAN20 = 4020,
+	CTRY_JAPAN21 = 4021,
+	CTRY_JAPAN22 = 4022,
+	CTRY_JAPAN23 = 4023,
+	CTRY_JAPAN24 = 4024,
+	CTRY_JAPAN25 = 4025,
+	CTRY_JAPAN26 = 4026,
+	CTRY_JAPAN27 = 4027,
+	CTRY_JAPAN28 = 4028,
+	CTRY_JAPAN29 = 4029,
+	CTRY_JAPAN30 = 4030,
+	CTRY_JAPAN31 = 4031,
+	CTRY_JAPAN32 = 4032,
+	CTRY_JAPAN33 = 4033,
+	CTRY_JAPAN34 = 4034,
+	CTRY_JAPAN35 = 4035,
+	CTRY_JAPAN36 = 4036,
+	CTRY_JAPAN37 = 4037,
+	CTRY_JAPAN38 = 4038,
+	CTRY_JAPAN39 = 4039,
+	CTRY_JAPAN40 = 4040,
+	CTRY_JAPAN41 = 4041,
+	CTRY_JAPAN42 = 4042,
+	CTRY_JAPAN43 = 4043,
+	CTRY_JAPAN44 = 4044,
+	CTRY_JAPAN45 = 4045,
+	CTRY_JAPAN46 = 4046,
+	CTRY_JAPAN47 = 4047,
+	CTRY_JAPAN48 = 4048,
+	CTRY_JAPAN49 = 4049,
+	CTRY_JAPAN50 = 4050,
+	CTRY_JAPAN51 = 4051,
+	CTRY_JAPAN52 = 4052,
+	CTRY_JAPAN53 = 4053,
+	CTRY_JAPAN54 = 4054,
+	CTRY_JAPAN55 = 4055,
+	CTRY_JAPAN56 = 4056,
+	CTRY_JAPAN57 = 4057,
+	CTRY_JAPAN58 = 4058,
+	CTRY_JAPAN59 = 4059,
+	CTRY_AUSTRALIA2 = 5000,
+	CTRY_CANADA2 = 5001,
+	CTRY_BELGIUM2 = 5002
+};
+
+bool ath_is_world_regd(struct ath_regulatory *reg);
+int ath_regd_init(struct ath_regulatory *reg, struct wiphy *wiphy,
+		  int (*reg_notifier)(struct wiphy *wiphy,
+		  struct regulatory_request *request));
+u32 ath_regd_get_band_ctl(struct ath_regulatory *reg,
+			  enum ieee80211_band band);
+int ath_reg_notifier_apply(struct wiphy *wiphy,
+			   struct regulatory_request *request,
+			   struct ath_regulatory *reg);
+
+#endif
diff -ruN a/drivers/net/wireless/ar9170/usb.c b/drivers/net/wireless/ar9170/usb.c
--- a/drivers/net/wireless/ar9170/usb.c	2009-08-16 23:19:38.000000000 +0200
+++ b/drivers/net/wireless/ar9170/usb.c	2009-08-28 02:59:04.000000000 +0200
@@ -51,9 +51,14 @@
 MODULE_AUTHOR("Christian Lamparter <chunkeey@web.de>");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Atheros AR9170 802.11n USB wireless");
+MODULE_FIRMWARE("ar9170.fw");
 MODULE_FIRMWARE("ar9170-1.fw");
 MODULE_FIRMWARE("ar9170-2.fw");
 
+enum ar9170_requirements {
+	AR9170_REQ_FW1_ONLY = 1,
+};
+
 static struct usb_device_id ar9170_usb_ids[] = {
 	/* Atheros 9170 */
 	{ USB_DEVICE(0x0cf3, 0x9170) },
@@ -81,25 +86,74 @@
 	{ USB_DEVICE(0x2019, 0x5304) },
 	/* IO-Data WNGDNUS2 */
 	{ USB_DEVICE(0x04bb, 0x093f) },
+	/* AVM FRITZ!WLAN USB Stick N */
+	{ USB_DEVICE(0x057C, 0x8401) },
+	/* AVM FRITZ!WLAN USB Stick N 2.4 */
+	{ USB_DEVICE(0x057C, 0x8402), .driver_info = AR9170_REQ_FW1_ONLY },
 
 	/* terminate */
 	{}
 };
 MODULE_DEVICE_TABLE(usb, ar9170_usb_ids);
 
-static void ar9170_usb_tx_urb_complete_free(struct urb *urb)
+static void ar9170_usb_submit_urb(struct ar9170_usb *aru)
+{
+	struct urb *urb;
+	unsigned long flags;
+	int err;
+
+	if (unlikely(!IS_STARTED(&aru->common)))
+		return ;
+
+	spin_lock_irqsave(&aru->tx_urb_lock, flags);
+	if (aru->tx_submitted_urbs >= AR9170_NUM_TX_URBS) {
+		spin_unlock_irqrestore(&aru->tx_urb_lock, flags);
+		return ;
+	}
+	aru->tx_submitted_urbs++;
+
+	urb = usb_get_from_anchor(&aru->tx_pending);
+	if (!urb) {
+		aru->tx_submitted_urbs--;
+		spin_unlock_irqrestore(&aru->tx_urb_lock, flags);
+
+		return ;
+	}
+	spin_unlock_irqrestore(&aru->tx_urb_lock, flags);
+
+	aru->tx_pending_urbs--;
+	usb_anchor_urb(urb, &aru->tx_submitted);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (unlikely(err)) {
+		if (ar9170_nag_limiter(&aru->common))
+			dev_err(&aru->udev->dev, "submit_urb failed (%d).\n",
+				err);
+
+		usb_unanchor_urb(urb);
+		aru->tx_submitted_urbs--;
+		ar9170_tx_callback(&aru->common, urb->context);
+	}
+
+	usb_free_urb(urb);
+}
+
+static void ar9170_usb_tx_urb_complete_frame(struct urb *urb)
 {
 	struct sk_buff *skb = urb->context;
 	struct ar9170_usb *aru = (struct ar9170_usb *)
 	      usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
 
-	if (!aru) {
+	if (unlikely(!aru)) {
 		dev_kfree_skb_irq(skb);
 		return ;
 	}
 
-	ar9170_handle_tx_status(&aru->common, skb, false,
-				AR9170_TX_STATUS_COMPLETE);
+	aru->tx_submitted_urbs--;
+
+	ar9170_tx_callback(&aru->common, skb);
+
+	ar9170_usb_submit_urb(aru);
 }
 
 static void ar9170_usb_tx_urb_complete(struct urb *urb)
@@ -126,8 +180,8 @@
 		goto resubmit;
 	}
 
-	print_hex_dump_bytes("ar9170 irq: ", DUMP_PREFIX_OFFSET,
-			     urb->transfer_buffer, urb->actual_length);
+	ar9170_handle_command_response(&aru->common, urb->transfer_buffer,
+				       urb->actual_length);
 
 resubmit:
 	usb_anchor_urb(urb, &aru->rx_submitted);
@@ -177,16 +231,15 @@
 
 	usb_anchor_urb(urb, &aru->rx_submitted);
 	err = usb_submit_urb(urb, GFP_ATOMIC);
-	if (err) {
+	if (unlikely(err)) {
 		usb_unanchor_urb(urb);
-		dev_kfree_skb_irq(skb);
+		goto free;
 	}
 
 	return ;
 
 free:
 	dev_kfree_skb_irq(skb);
-	return;
 }
 
 static int ar9170_usb_prep_rx_urb(struct ar9170_usb *aru,
@@ -282,21 +335,47 @@
 	return err;
 }
 
-static void ar9170_usb_cancel_urbs(struct ar9170_usb *aru)
+static int ar9170_usb_flush(struct ar9170 *ar)
 {
-	int ret;
+	struct ar9170_usb *aru = (void *) ar;
+	struct urb *urb;
+	int ret, err = 0;
 
-	aru->common.state = AR9170_UNKNOWN_STATE;
+	if (IS_STARTED(ar))
+		aru->common.state = AR9170_IDLE;
 
-	usb_unlink_anchored_urbs(&aru->tx_submitted);
+	usb_wait_anchor_empty_timeout(&aru->tx_pending,
+					    msecs_to_jiffies(800));
+	while ((urb = usb_get_from_anchor(&aru->tx_pending))) {
+		ar9170_tx_callback(&aru->common, (void *) urb->context);
+		usb_free_urb(urb);
+	}
 
-	/* give the LED OFF command and the deauth frame a chance to air. */
+	/* lets wait a while until the tx - queues are dried out */
 	ret = usb_wait_anchor_empty_timeout(&aru->tx_submitted,
 					    msecs_to_jiffies(100));
 	if (ret == 0)
-		dev_err(&aru->udev->dev, "kill pending tx urbs.\n");
-	usb_poison_anchored_urbs(&aru->tx_submitted);
+		err = -ETIMEDOUT;
+
+	usb_kill_anchored_urbs(&aru->tx_submitted);
 
+	if (IS_ACCEPTING_CMD(ar))
+		aru->common.state = AR9170_STARTED;
+
+	return err;
+}
+
+static void ar9170_usb_cancel_urbs(struct ar9170_usb *aru)
+{
+	int err;
+
+	aru->common.state = AR9170_UNKNOWN_STATE;
+
+	err = ar9170_usb_flush(&aru->common);
+	if (err)
+		dev_err(&aru->udev->dev, "stuck tx urbs!\n");
+
+	usb_poison_anchored_urbs(&aru->tx_submitted);
 	usb_poison_anchored_urbs(&aru->rx_submitted);
 }
 
@@ -337,7 +416,7 @@
 
 	usb_anchor_urb(urb, &aru->tx_submitted);
 	err = usb_submit_urb(urb, GFP_ATOMIC);
-	if (err) {
+	if (unlikely(err)) {
 		usb_unanchor_urb(urb);
 		usb_free_urb(urb);
 		goto err_unbuf;
@@ -350,7 +429,7 @@
 		goto err_unbuf;
 	}
 
-	if (outlen >= 0 && aru->readlen != outlen) {
+	if (aru->readlen != outlen) {
 		err = -EMSGSIZE;
 		goto err_unbuf;
 	}
@@ -380,12 +459,10 @@
 	return err;
 }
 
-static int ar9170_usb_tx(struct ar9170 *ar, struct sk_buff *skb,
-			 bool txstatus_needed, unsigned int extra_len)
+static int ar9170_usb_tx(struct ar9170 *ar, struct sk_buff *skb)
 {
 	struct ar9170_usb *aru = (struct ar9170_usb *) ar;
 	struct urb *urb;
-	int err;
 
 	if (unlikely(!IS_STARTED(ar))) {
 		/* Seriously, what were you drink... err... thinking!? */
@@ -398,18 +475,17 @@
 
 	usb_fill_bulk_urb(urb, aru->udev,
 			  usb_sndbulkpipe(aru->udev, AR9170_EP_TX),
-			  skb->data, skb->len + extra_len, (txstatus_needed ?
-			  ar9170_usb_tx_urb_complete :
-			  ar9170_usb_tx_urb_complete_free), skb);
+			  skb->data, skb->len,
+			  ar9170_usb_tx_urb_complete_frame, skb);
 	urb->transfer_flags |= URB_ZERO_PACKET;
 
-	usb_anchor_urb(urb, &aru->tx_submitted);
-	err = usb_submit_urb(urb, GFP_ATOMIC);
-	if (unlikely(err))
-		usb_unanchor_urb(urb);
+	usb_anchor_urb(urb, &aru->tx_pending);
+	aru->tx_pending_urbs++;
 
 	usb_free_urb(urb);
-	return err;
+
+	ar9170_usb_submit_urb(aru);
+	return 0;
 }
 
 static void ar9170_usb_callback_cmd(struct ar9170 *ar, u32 len , void *buffer)
@@ -418,7 +494,7 @@
 	unsigned long flags;
 	u32 in, out;
 
-	if (!buffer)
+	if (unlikely(!buffer))
 		return ;
 
 	in = le32_to_cpup((__le32 *)buffer);
@@ -504,6 +580,22 @@
 {
 	int err = 0;
 
+	err = request_firmware(&aru->firmware, "ar9170.fw",
+			       &aru->udev->dev);
+	if (!err) {
+		aru->init_values = NULL;
+		return 0;
+	}
+
+	if (aru->req_one_stage_fw) {
+		dev_err(&aru->udev->dev, "ar9170.fw firmware file "
+			"not found and is required for this device\n");
+		return -EINVAL;
+	}
+
+	dev_err(&aru->udev->dev, "ar9170.fw firmware file "
+		"not found, trying old firmware...\n");
+
 	err = request_firmware(&aru->init_values, "ar9170-1.fw",
 			       &aru->udev->dev);
 	if (err) {
@@ -548,6 +640,9 @@
 {
 	int err;
 
+	if (!aru->init_values)
+		goto upload_fw_start;
+
 	/* First, upload initial values to device RAM */
 	err = ar9170_usb_upload(aru, aru->init_values->data,
 				aru->init_values->size, 0x102800, false);
@@ -557,6 +652,8 @@
 		return err;
 	}
 
+upload_fw_start:
+
 	/* Then, upload the firmware itself and start it */
 	return ar9170_usb_upload(aru, aru->firmware->data, aru->firmware->size,
 				0x200000, true);
@@ -592,10 +689,8 @@
 	if (IS_ACCEPTING_CMD(ar))
 		aru->common.state = AR9170_STOPPED;
 
-	/* lets wait a while until the tx - queues are dried out */
-	ret = usb_wait_anchor_empty_timeout(&aru->tx_submitted,
-					    msecs_to_jiffies(1000));
-	if (ret == 0)
+	ret = ar9170_usb_flush(ar);
+	if (ret)
 		dev_err(&aru->udev->dev, "kill pending tx urbs.\n");
 
 	usb_poison_anchored_urbs(&aru->tx_submitted);
@@ -656,6 +751,15 @@
 	return err;
 }
 
+static bool ar9170_requires_one_stage(const struct usb_device_id *id)
+{
+	if (!id->driver_info)
+		return false;
+	if (id->driver_info == AR9170_REQ_FW1_ONLY)
+		return true;
+	return false;
+}
+
 static int ar9170_usb_probe(struct usb_interface *intf,
 			const struct usb_device_id *id)
 {
@@ -676,19 +780,30 @@
 	aru->intf = intf;
 	ar = &aru->common;
 
+	aru->req_one_stage_fw = ar9170_requires_one_stage(id);
+
 	usb_set_intfdata(intf, aru);
 	SET_IEEE80211_DEV(ar->hw, &udev->dev);
 
 	init_usb_anchor(&aru->rx_submitted);
+	init_usb_anchor(&aru->tx_pending);
 	init_usb_anchor(&aru->tx_submitted);
 	init_completion(&aru->cmd_wait);
+	spin_lock_init(&aru->tx_urb_lock);
+
+	aru->tx_pending_urbs = 0;
+	aru->tx_submitted_urbs = 0;
 
 	aru->common.stop = ar9170_usb_stop;
+	aru->common.flush = ar9170_usb_flush;
 	aru->common.open = ar9170_usb_open;
 	aru->common.tx = ar9170_usb_tx;
 	aru->common.exec_cmd = ar9170_usb_exec_cmd;
 	aru->common.callback_cmd = ar9170_usb_callback_cmd;
 
+#ifdef CONFIG_PM
+	udev->reset_resume = 1;
+#endif /* CONFIG_PM */
 	err = ar9170_usb_reset(aru);
 	if (err)
 		goto err_freehw;
@@ -773,11 +888,6 @@
 	usb_unpoison_anchored_urbs(&aru->rx_submitted);
 	usb_unpoison_anchored_urbs(&aru->tx_submitted);
 
-	/*
-	 * FIXME: firmware upload will fail on resume.
-	 * but this is better than a hang!
-	 */
-
 	err = ar9170_usb_init_device(aru);
 	if (err)
 		goto err_unrx;
@@ -805,6 +915,7 @@
 #ifdef CONFIG_PM
 	.suspend = ar9170_suspend,
 	.resume = ar9170_resume,
+	.reset_resume = ar9170_resume,
 #endif /* CONFIG_PM */
 };
 
diff -ruN a/drivers/net/wireless/ar9170/usb.h b/drivers/net/wireless/ar9170/usb.h
--- a/drivers/net/wireless/ar9170/usb.h	2009-08-16 23:19:38.000000000 +0200
+++ b/drivers/net/wireless/ar9170/usb.h	2009-08-28 02:59:04.000000000 +0200
@@ -43,7 +43,7 @@
 #include <linux/completion.h>
 #include <linux/spinlock.h>
 #include <linux/leds.h>
-#include <net/wireless.h>
+#include <net/cfg80211.h>
 #include <net/mac80211.h>
 #include <linux/firmware.h>
 #include "eeprom.h"
@@ -51,6 +51,7 @@
 #include "ar9170.h"
 
 #define AR9170_NUM_RX_URBS			16
+#define AR9170_NUM_TX_URBS			8
 
 struct firmware;
 
@@ -60,9 +61,15 @@
 	struct usb_interface *intf;
 
 	struct usb_anchor rx_submitted;
+	struct usb_anchor tx_pending;
 	struct usb_anchor tx_submitted;
 
-	spinlock_t cmdlock;
+	bool req_one_stage_fw;
+
+	spinlock_t tx_urb_lock;
+	unsigned int tx_submitted_urbs;
+	unsigned int tx_pending_urbs;
+
 	struct completion cmd_wait;
 	int readlen;
 	u8 *readbuf;
diff -ruN a/include/net/mac80211.h b/include/net/mac80211.h
--- a/include/net/mac80211.h	2009-08-16 23:19:38.000000000 +0200
+++ b/include/net/mac80211.h	2009-09-02 16:43:39.000000000 +0200
@@ -158,6 +158,11 @@
 	BSS_CHANGED_ERP_SLOT		= 1<<3,
 	BSS_CHANGED_HT                  = 1<<4,
 	BSS_CHANGED_BASIC_RATES		= 1<<5,
+  BSS_CHANGED_BEACON_INT    = 1<<6,
+  BSS_CHANGED_BSSID   = 1<<7,
+  BSS_CHANGED_BEACON    = 1<<8,
+  BSS_CHANGED_BEACON_ENABLED  = 1<<9,
+
 };
 
 /**
@@ -193,6 +198,7 @@
  *	the current band.
  */
 struct ieee80211_bss_conf {
+  const u8 *bssid;
 	/* association related data */
 	bool assoc;
 	u16 aid;
@@ -200,11 +206,13 @@
 	bool use_cts_prot;
 	bool use_short_preamble;
 	bool use_short_slot;
+  bool enable_beacon;
 	u8 dtim_period;
 	u16 beacon_int;
 	u16 assoc_capability;
 	u64 timestamp;
 	u32 basic_rates;
+  u16 ht_operation_mode;
 	struct ieee80211_bss_ht_conf ht;
 };
 
