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

Conflicts:
	drivers/net/wireless/ath/ar9170/main.c
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 2fbe9b45..174e344 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -5,6 +5,7 @@
 menuconfig WLAN
 	bool "Wireless LAN"
 	depends on !S390
+	depends on NET
 	select WIRELESS
 	default y
 	---help---
diff --git a/drivers/net/wireless/ath/ar9170/ar9170.h b/drivers/net/wireless/ath/ar9170/ar9170.h
index dc662b7..4f845f8 100644
--- a/drivers/net/wireless/ath/ar9170/ar9170.h
+++ b/drivers/net/wireless/ath/ar9170/ar9170.h
@@ -109,41 +109,6 @@
 	bool has_plcp;
 };
 
-#define AR9170_NUM_TID	16
-#define WME_BA_BMP_SIZE         64
-#define AR9170_NUM_MAX_AGG_LEN	(2 * WME_BA_BMP_SIZE)
-
-#define WME_AC_BE   2
-#define WME_AC_BK   3
-#define WME_AC_VI   1
-#define WME_AC_VO   0
-
-#define TID_TO_WME_AC(_tid)				\
-	((((_tid) == 0) || ((_tid) == 3)) ? WME_AC_BE :	\
-	 (((_tid) == 1) || ((_tid) == 2)) ? WME_AC_BK :	\
-	 (((_tid) == 4) || ((_tid) == 5)) ? WME_AC_VI :	\
-	 WME_AC_VO)
-
-#define BAW_WITHIN(_start, _bawsz, _seqno) \
-	((((_seqno) - (_start)) & 0xfff) < (_bawsz))
-
-enum ar9170_tid_state {
-	AR9170_TID_STATE_INVALID,
-	AR9170_TID_STATE_SHUTDOWN,
-	AR9170_TID_STATE_PROGRESS,
-	AR9170_TID_STATE_COMPLETE,
-};
-
-struct ar9170_sta_tid {
-	struct list_head list;
-	struct sk_buff_head queue;
-	u8 addr[ETH_ALEN];
-	u16 ssn;
-	u16 tid;
-	enum ar9170_tid_state state;
-	bool active;
-};
-
 struct ar9170_tx_queue_stats {
 	unsigned int len;
 	unsigned int limit;
@@ -152,14 +117,11 @@
 
 #define AR9170_QUEUE_TIMEOUT		64
 #define AR9170_TX_TIMEOUT		8
-#define AR9170_BA_TIMEOUT		4
 #define AR9170_JANITOR_DELAY		128
 #define AR9170_TX_INVALID_RATE		0xffffffff
 
-#define AR9170_NUM_TX_STATUS		128
-#define AR9170_NUM_TX_AGG_MAX		30
-#define AR9170_NUM_TX_LIMIT_HARD       AR9170_TXQ_DEPTH
-#define AR9170_NUM_TX_LIMIT_SOFT       (AR9170_TXQ_DEPTH - 10)
+#define AR9170_NUM_TX_LIMIT_HARD	AR9170_TXQ_DEPTH
+#define AR9170_NUM_TX_LIMIT_SOFT	(AR9170_TXQ_DEPTH - 10)
 
 struct ar9170 {
 	struct ieee80211_hw *hw;
@@ -234,11 +196,6 @@
 	struct sk_buff_head tx_pending[__AR9170_NUM_TXQ];
 	struct sk_buff_head tx_status[__AR9170_NUM_TXQ];
 	struct delayed_work tx_janitor;
-	/* tx ampdu */
-	struct sk_buff_head tx_status_ampdu;
-	spinlock_t tx_ampdu_list_lock;
-	struct list_head tx_ampdu_list;
-	atomic_t tx_ampdu_pending;
 
 	/* rxstream mpdu merge */
 	struct ar9170_rxstream_mpdu_merge rx_mpdu;
@@ -250,11 +207,6 @@
 	u8 global_ampdu_factor;
 };
 
-struct ar9170_sta_info {
-	struct ar9170_sta_tid agg[AR9170_NUM_TID];
-	unsigned int ampdu_max_len;
-};
-
 struct ar9170_tx_info {
 	unsigned long timeout;
 };
diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c
index 2e9b330..2abc875 100644
--- a/drivers/net/wireless/ath/ar9170/main.c
+++ b/drivers/net/wireless/ath/ar9170/main.c
@@ -50,10 +50,6 @@
 module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
 MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
 
-static int modparam_ht;
-module_param_named(ht, modparam_ht, bool, S_IRUGO);
-MODULE_PARM_DESC(ht, "enable MPDU aggregation.");
-
 #define RATE(_bitrate, _hw_rate, _txpidx, _flags) {	\
 	.bitrate	= (_bitrate),			\
 	.flags		= (_flags),			\
@@ -182,7 +178,6 @@
 };
 
 static void ar9170_tx(struct ar9170 *ar);
-static bool ar9170_tx_ampdu(struct ar9170 *ar);
 
 static inline u16 ar9170_get_seq_h(struct ieee80211_hdr *hdr)
 {
@@ -195,21 +190,7 @@
 	return ar9170_get_seq_h((void *) txc->frame_data);
 }
 
-static inline u16 ar9170_get_tid_h(struct ieee80211_hdr *hdr)
-{
-	return (ieee80211_get_qos_ctl(hdr))[0] & IEEE80211_QOS_CTL_TID_MASK;
-}
-
-static inline u16 ar9170_get_tid(struct sk_buff *skb)
-{
-	struct ar9170_tx_control *txc = (void *) skb->data;
-	return ar9170_get_tid_h((struct ieee80211_hdr *) txc->frame_data);
-}
-
-#define GET_NEXT_SEQ(seq)	((seq + 1) & 0x0fff)
-#define GET_NEXT_SEQ_FROM_SKB(skb)	(GET_NEXT_SEQ(ar9170_get_seq(skb)))
-
-#if (defined AR9170_QUEUE_DEBUG) || (defined AR9170_TXAGG_DEBUG)
+#ifdef AR9170_QUEUE_DEBUG
 static void ar9170_print_txheader(struct ar9170 *ar, struct sk_buff *skb)
 {
 	struct ar9170_tx_control *txc = (void *) skb->data;
@@ -244,7 +225,7 @@
 		       "mismatch %d != %d\n", skb_queue_len(queue), i);
 	printk(KERN_DEBUG "---[ end ]---\n");
 }
-#endif /* AR9170_QUEUE_DEBUG || AR9170_TXAGG_DEBUG */
+#endif /* AR9170_QUEUE_DEBUG */
 
 #ifdef AR9170_QUEUE_DEBUG
 static void ar9170_dump_txqueue(struct ar9170 *ar,
@@ -275,20 +256,6 @@
 }
 #endif /* AR9170_QUEUE_STOP_DEBUG */
 
-#ifdef AR9170_TXAGG_DEBUG
-static void ar9170_dump_tx_status_ampdu(struct ar9170 *ar)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&ar->tx_status_ampdu.lock, flags);
-	printk(KERN_DEBUG "%s: A-MPDU tx_status queue =>\n",
-	       wiphy_name(ar->hw->wiphy));
-	__ar9170_dump_txqueue(ar, &ar->tx_status_ampdu);
-	spin_unlock_irqrestore(&ar->tx_status_ampdu.lock, flags);
-}
-
-#endif /* AR9170_TXAGG_DEBUG */
-
 /* caller must guarantee exclusive access for _bin_ queue. */
 static void ar9170_recycle_expired(struct ar9170 *ar,
 				   struct sk_buff_head *queue,
@@ -360,70 +327,6 @@
 	ieee80211_tx_status_irqsafe(ar->hw, skb);
 }
 
-static void ar9170_tx_fake_ampdu_status(struct ar9170 *ar)
-{
-	struct sk_buff_head success;
-	struct sk_buff *skb;
-	unsigned int i;
-	unsigned long queue_bitmap = 0;
-
-	skb_queue_head_init(&success);
-
-	while (skb_queue_len(&ar->tx_status_ampdu) > AR9170_NUM_TX_STATUS)
-		__skb_queue_tail(&success, skb_dequeue(&ar->tx_status_ampdu));
-
-	ar9170_recycle_expired(ar, &ar->tx_status_ampdu, &success);
-
-#ifdef AR9170_TXAGG_DEBUG
-	printk(KERN_DEBUG "%s: collected %d A-MPDU frames.\n",
-	       wiphy_name(ar->hw->wiphy), skb_queue_len(&success));
-	__ar9170_dump_txqueue(ar, &success);
-#endif /* AR9170_TXAGG_DEBUG */
-
-	while ((skb = __skb_dequeue(&success))) {
-		struct ieee80211_tx_info *txinfo;
-
-		queue_bitmap |= BIT(skb_get_queue_mapping(skb));
-
-		txinfo = IEEE80211_SKB_CB(skb);
-		ieee80211_tx_info_clear_status(txinfo);
-
-		txinfo->flags |= IEEE80211_TX_STAT_ACK;
-		txinfo->status.rates[0].count = 1;
-
-		skb_pull(skb, sizeof(struct ar9170_tx_control));
-		ieee80211_tx_status_irqsafe(ar->hw, skb);
-	}
-
-	for_each_set_bit(i, &queue_bitmap, BITS_PER_BYTE) {
-#ifdef AR9170_QUEUE_STOP_DEBUG
-		printk(KERN_DEBUG "%s: wake queue %d\n",
-		       wiphy_name(ar->hw->wiphy), i);
-		__ar9170_dump_txstats(ar);
-#endif /* AR9170_QUEUE_STOP_DEBUG */
-		ieee80211_wake_queue(ar->hw, i);
-	}
-
-	if (queue_bitmap)
-		ar9170_tx(ar);
-}
-
-static void ar9170_tx_ampdu_callback(struct ar9170 *ar, struct sk_buff *skb)
-{
-	struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb);
-	struct ar9170_tx_info *arinfo = (void *) txinfo->rate_driver_data;
-
-	arinfo->timeout = jiffies +
-			  msecs_to_jiffies(AR9170_BA_TIMEOUT);
-
-	skb_queue_tail(&ar->tx_status_ampdu, skb);
-	ar9170_tx_fake_ampdu_status(ar);
-
-	if (atomic_dec_and_test(&ar->tx_ampdu_pending) &&
-	    !list_empty(&ar->tx_ampdu_list))
-		ar9170_tx_ampdu(ar);
-}
-
 void ar9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb)
 {
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -447,14 +350,10 @@
 	if (info->flags & IEEE80211_TX_CTL_NO_ACK) {
 		ar9170_tx_status(ar, skb, AR9170_TX_STATUS_FAILED);
 	} else {
-		if (info->flags & IEEE80211_TX_CTL_AMPDU) {
-			ar9170_tx_ampdu_callback(ar, skb);
-		} else {
-			arinfo->timeout = jiffies +
-				  msecs_to_jiffies(AR9170_TX_TIMEOUT);
+		arinfo->timeout = jiffies +
+			  msecs_to_jiffies(AR9170_TX_TIMEOUT);
 
-			skb_queue_tail(&ar->tx_status[queue], skb);
-		}
+		skb_queue_tail(&ar->tx_status[queue], skb);
 	}
 
 	if (!ar->tx_stats[queue].len &&
@@ -524,38 +423,6 @@
 	return NULL;
 }
 
-static void ar9170_handle_block_ack(struct ar9170 *ar, u16 count, u16 r)
-{
-	struct sk_buff *skb;
-	struct ieee80211_tx_info *txinfo;
-
-	while (count) {
-		skb = ar9170_get_queued_skb(ar, NULL, &ar->tx_status_ampdu, r);
-		if (!skb)
-			break;
-
-		txinfo = IEEE80211_SKB_CB(skb);
-		ieee80211_tx_info_clear_status(txinfo);
-
-		/* FIXME: maybe more ? */
-		txinfo->status.rates[0].count = 1;
-
-		skb_pull(skb, sizeof(struct ar9170_tx_control));
-		ieee80211_tx_status_irqsafe(ar->hw, skb);
-		count--;
-	}
-
-#ifdef AR9170_TXAGG_DEBUG
-	if (count) {
-		printk(KERN_DEBUG "%s: got %d more failed mpdus, but no more "
-		       "suitable frames left in tx_status queue.\n",
-		       wiphy_name(ar->hw->wiphy), count);
-
-		ar9170_dump_tx_status_ampdu(ar);
-	}
-#endif /* AR9170_TXAGG_DEBUG */
-}
-
 /*
  * This worker tries to keeps an maintain tx_status queues.
  * So we can guarantee that incoming tx_status reports are
@@ -592,8 +459,6 @@
 			resched = true;
 	}
 
-	ar9170_tx_fake_ampdu_status(ar);
-
 	if (!resched)
 		return;
 
@@ -673,10 +538,6 @@
 
 	case 0xc5:
 		/* BlockACK events */
-		ar9170_handle_block_ack(ar,
-					le16_to_cpu(cmd->ba_fail_cnt.failed),
-					le16_to_cpu(cmd->ba_fail_cnt.rate));
-		ar9170_tx_fake_ampdu_status(ar);
 		break;
 
 	case 0xc6:
@@ -1247,7 +1108,6 @@
 	ar->global_ampdu_density = 6;
 	ar->global_ampdu_factor = 3;
 
-	atomic_set(&ar->tx_ampdu_pending, 0);
 	ar->bad_hw_nagger = jiffies;
 
 	err = ar->open(ar);
@@ -1310,40 +1170,10 @@
 		skb_queue_purge(&ar->tx_pending[i]);
 		skb_queue_purge(&ar->tx_status[i]);
 	}
-	skb_queue_purge(&ar->tx_status_ampdu);
 
 	mutex_unlock(&ar->mutex);
 }
 
-static void ar9170_tx_indicate_immba(struct ar9170 *ar, struct sk_buff *skb)
-{
-	struct ar9170_tx_control *txc = (void *) skb->data;
-
-	txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_IMM_AMPDU);
-}
-
-static void ar9170_tx_copy_phy(struct ar9170 *ar, struct sk_buff *dst,
-			       struct sk_buff *src)
-{
-	struct ar9170_tx_control *dst_txc, *src_txc;
-	struct ieee80211_tx_info *dst_info, *src_info;
-	struct ar9170_tx_info *dst_arinfo, *src_arinfo;
-
-	src_txc = (void *) src->data;
-	src_info = IEEE80211_SKB_CB(src);
-	src_arinfo = (void *) src_info->rate_driver_data;
-
-	dst_txc = (void *) dst->data;
-	dst_info = IEEE80211_SKB_CB(dst);
-	dst_arinfo = (void *) dst_info->rate_driver_data;
-
-	dst_txc->phy_control = src_txc->phy_control;
-
-	/* same MCS for the whole aggregate */
-	memcpy(dst_info->driver_rates, src_info->driver_rates,
-	       sizeof(dst_info->driver_rates));
-}
-
 static int ar9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb)
 {
 	struct ieee80211_hdr *hdr;
@@ -1420,14 +1250,7 @@
 		txc->phy_control |=
 			cpu_to_le32(queue << AR9170_TX_PHY_QOS_SHIFT);
 
-		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);
-		} else {
-			txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_RATE_PROBE);
-		}
+		txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_RATE_PROBE);
 	}
 
 	return 0;
@@ -1537,158 +1360,6 @@
 	txc->phy_control |= cpu_to_le32(chains << AR9170_TX_PHY_TXCHAIN_SHIFT);
 }
 
-static bool ar9170_tx_ampdu(struct ar9170 *ar)
-{
-	struct sk_buff_head agg;
-	struct ar9170_sta_tid *tid_info = NULL, *tmp;
-	struct sk_buff *skb, *first = NULL;
-	unsigned long flags, f2;
-	unsigned int i = 0;
-	u16 seq, queue, tmpssn;
-	bool run = false;
-
-	skb_queue_head_init(&agg);
-
-	spin_lock_irqsave(&ar->tx_ampdu_list_lock, flags);
-	if (list_empty(&ar->tx_ampdu_list)) {
-#ifdef AR9170_TXAGG_DEBUG
-		printk(KERN_DEBUG "%s: aggregation list is empty.\n",
-		       wiphy_name(ar->hw->wiphy));
-#endif /* AR9170_TXAGG_DEBUG */
-		goto out_unlock;
-	}
-
-	list_for_each_entry_safe(tid_info, tmp, &ar->tx_ampdu_list, list) {
-		if (tid_info->state != AR9170_TID_STATE_COMPLETE) {
-#ifdef AR9170_TXAGG_DEBUG
-			printk(KERN_DEBUG "%s: dangling aggregation entry!\n",
-			       wiphy_name(ar->hw->wiphy));
-#endif /* AR9170_TXAGG_DEBUG */
-			continue;
-		}
-
-		if (++i > 64) {
-#ifdef AR9170_TXAGG_DEBUG
-			printk(KERN_DEBUG "%s: enough frames aggregated.\n",
-			       wiphy_name(ar->hw->wiphy));
-#endif /* AR9170_TXAGG_DEBUG */
-			break;
-		}
-
-		queue = TID_TO_WME_AC(tid_info->tid);
-
-		if (skb_queue_len(&ar->tx_pending[queue]) >=
-		    AR9170_NUM_TX_AGG_MAX) {
-#ifdef AR9170_TXAGG_DEBUG
-			printk(KERN_DEBUG "%s: queue %d full.\n",
-			       wiphy_name(ar->hw->wiphy), queue);
-#endif /* AR9170_TXAGG_DEBUG */
-			continue;
-		}
-
-		list_del_init(&tid_info->list);
-
-		spin_lock_irqsave(&tid_info->queue.lock, f2);
-		tmpssn = seq = tid_info->ssn;
-		first = skb_peek(&tid_info->queue);
-
-		if (likely(first))
-			tmpssn = ar9170_get_seq(first);
-
-		if (unlikely(tmpssn != seq)) {
-#ifdef AR9170_TXAGG_DEBUG
-			printk(KERN_DEBUG "%s: ssn mismatch [%d != %d]\n.",
-			       wiphy_name(ar->hw->wiphy), seq, tmpssn);
-#endif /* AR9170_TXAGG_DEBUG */
-			tid_info->ssn = tmpssn;
-		}
-
-#ifdef AR9170_TXAGG_DEBUG
-		printk(KERN_DEBUG "%s: generate A-MPDU for tid:%d ssn:%d with "
-		       "%d queued frames.\n", wiphy_name(ar->hw->wiphy),
-		       tid_info->tid, tid_info->ssn,
-		       skb_queue_len(&tid_info->queue));
-		__ar9170_dump_txqueue(ar, &tid_info->queue);
-#endif /* AR9170_TXAGG_DEBUG */
-
-		while ((skb = skb_peek(&tid_info->queue))) {
-			if (unlikely(ar9170_get_seq(skb) != seq))
-				break;
-
-			__skb_unlink(skb, &tid_info->queue);
-			tid_info->ssn = seq = GET_NEXT_SEQ(seq);
-
-			if (unlikely(skb_get_queue_mapping(skb) != queue)) {
-#ifdef AR9170_TXAGG_DEBUG
-				printk(KERN_DEBUG "%s: tid:%d(q:%d) queue:%d "
-				       "!match.\n", wiphy_name(ar->hw->wiphy),
-				       tid_info->tid,
-				       TID_TO_WME_AC(tid_info->tid),
-				       skb_get_queue_mapping(skb));
-#endif /* AR9170_TXAGG_DEBUG */
-					dev_kfree_skb_any(skb);
-					continue;
-			}
-
-			if (unlikely(first == skb)) {
-				ar9170_tx_prepare_phy(ar, skb);
-				__skb_queue_tail(&agg, skb);
-				first = skb;
-			} else {
-				ar9170_tx_copy_phy(ar, skb, first);
-				__skb_queue_tail(&agg, skb);
-			}
-
-			if (unlikely(skb_queue_len(&agg) ==
-			    AR9170_NUM_TX_AGG_MAX))
-				break;
-		}
-
-		if (skb_queue_empty(&tid_info->queue))
-			tid_info->active = false;
-		else
-			list_add_tail(&tid_info->list,
-				      &ar->tx_ampdu_list);
-
-		spin_unlock_irqrestore(&tid_info->queue.lock, f2);
-
-		if (unlikely(skb_queue_empty(&agg))) {
-#ifdef AR9170_TXAGG_DEBUG
-			printk(KERN_DEBUG "%s: queued empty list!\n",
-			       wiphy_name(ar->hw->wiphy));
-#endif /* AR9170_TXAGG_DEBUG */
-			continue;
-		}
-
-		/*
-		 * tell the FW/HW that this is the last frame,
-		 * that way it will wait for the immediate block ack.
-		 */
-		ar9170_tx_indicate_immba(ar, skb_peek_tail(&agg));
-
-#ifdef AR9170_TXAGG_DEBUG
-		printk(KERN_DEBUG "%s: generated A-MPDU looks like this:\n",
-		       wiphy_name(ar->hw->wiphy));
-		__ar9170_dump_txqueue(ar, &agg);
-#endif /* AR9170_TXAGG_DEBUG */
-
-		spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags);
-
-		spin_lock_irqsave(&ar->tx_pending[queue].lock, flags);
-		skb_queue_splice_tail_init(&agg, &ar->tx_pending[queue]);
-		spin_unlock_irqrestore(&ar->tx_pending[queue].lock, flags);
-		run = true;
-
-		spin_lock_irqsave(&ar->tx_ampdu_list_lock, flags);
-	}
-
-out_unlock:
-	spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags);
-	__skb_queue_purge(&agg);
-
-	return run;
-}
-
 static void ar9170_tx(struct ar9170 *ar)
 {
 	struct sk_buff *skb;
@@ -1763,9 +1434,6 @@
 			arinfo->timeout = jiffies +
 					  msecs_to_jiffies(AR9170_TX_TIMEOUT);
 
-			if (info->flags & IEEE80211_TX_CTL_AMPDU)
-				atomic_inc(&ar->tx_ampdu_pending);
-
 #ifdef AR9170_QUEUE_DEBUG
 			printk(KERN_DEBUG "%s: send frame q:%d =>\n",
 			       wiphy_name(ar->hw->wiphy), i);
@@ -1774,9 +1442,6 @@
 
 			err = ar->tx(ar, skb);
 			if (unlikely(err)) {
-				if (info->flags & IEEE80211_TX_CTL_AMPDU)
-					atomic_dec(&ar->tx_ampdu_pending);
-
 				frames_failed++;
 				dev_kfree_skb_any(skb);
 			} else {
@@ -1823,94 +1488,11 @@
 				     msecs_to_jiffies(AR9170_JANITOR_DELAY));
 }
 
-static bool ar9170_tx_ampdu_queue(struct ar9170 *ar, struct sk_buff *skb)
-{
-	struct ieee80211_tx_info *txinfo;
-	struct ar9170_sta_info *sta_info;
-	struct ar9170_sta_tid *agg;
-	struct sk_buff *iter;
-	unsigned long flags, f2;
-	unsigned int max;
-	u16 tid, seq, qseq;
-	bool run = false, queue = false;
-
-	tid = ar9170_get_tid(skb);
-	seq = ar9170_get_seq(skb);
-	txinfo = IEEE80211_SKB_CB(skb);
-	sta_info = (void *) txinfo->control.sta->drv_priv;
-	agg = &sta_info->agg[tid];
-	max = sta_info->ampdu_max_len;
-
-	spin_lock_irqsave(&ar->tx_ampdu_list_lock, flags);
-
-	if (unlikely(agg->state != AR9170_TID_STATE_COMPLETE)) {
-#ifdef AR9170_TXAGG_DEBUG
-		printk(KERN_DEBUG "%s: BlockACK session not fully initialized "
-		       "for ESS:%pM tid:%d state:%d.\n",
-		       wiphy_name(ar->hw->wiphy), agg->addr, agg->tid,
-		       agg->state);
-#endif /* AR9170_TXAGG_DEBUG */
-		goto err_unlock;
-	}
-
-	if (!agg->active) {
-		agg->active = true;
-		agg->ssn = seq;
-		queue = true;
-	}
-
-	/* check if seq is within the BA window */
-	if (unlikely(!BAW_WITHIN(agg->ssn, max, seq))) {
-#ifdef AR9170_TXAGG_DEBUG
-		printk(KERN_DEBUG "%s: frame with tid:%d seq:%d does not "
-		       "fit into BA window (%d - %d)\n",
-		       wiphy_name(ar->hw->wiphy), tid, seq, agg->ssn,
-		       (agg->ssn + max) & 0xfff);
-#endif /* AR9170_TXAGG_DEBUG */
-		goto err_unlock;
-	}
-
-	spin_lock_irqsave(&agg->queue.lock, f2);
-
-	skb_queue_reverse_walk(&agg->queue, iter) {
-		qseq = ar9170_get_seq(iter);
-
-		if (GET_NEXT_SEQ(qseq) == seq) {
-			__skb_queue_after(&agg->queue, iter, skb);
-			goto queued;
-		}
-	}
-
-	__skb_queue_head(&agg->queue, skb);
-
-queued:
-	spin_unlock_irqrestore(&agg->queue.lock, f2);
-
-#ifdef AR9170_TXAGG_DEBUG
-	printk(KERN_DEBUG "%s: new aggregate %p queued.\n",
-	       wiphy_name(ar->hw->wiphy), skb);
-	__ar9170_dump_txqueue(ar, &agg->queue);
-#endif /* AR9170_TXAGG_DEBUG */
-
-	if (skb_queue_len(&agg->queue) >= AR9170_NUM_TX_AGG_MAX)
-		run = true;
-
-	if (queue)
-		list_add_tail(&agg->list, &ar->tx_ampdu_list);
-
-	spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags);
-	return run;
-
-err_unlock:
-	spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags);
-	dev_kfree_skb_irq(skb);
-	return false;
-}
-
 int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
 	struct ar9170 *ar = hw->priv;
 	struct ieee80211_tx_info *info;
+	unsigned int queue;
 
 	if (unlikely(!IS_STARTED(ar)))
 		goto err_free;
@@ -1918,18 +1500,10 @@
 	if (unlikely(ar9170_tx_prepare(ar, skb)))
 		goto err_free;
 
+	queue = skb_get_queue_mapping(skb);
 	info = IEEE80211_SKB_CB(skb);
-	if (info->flags & IEEE80211_TX_CTL_AMPDU) {
-		bool run = ar9170_tx_ampdu_queue(ar, skb);
-
-		if (run || !atomic_read(&ar->tx_ampdu_pending))
-			ar9170_tx_ampdu(ar);
-	} 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_prepare_phy(ar, skb);
+	skb_queue_tail(&ar->tx_pending[queue], skb);
 
 	ar9170_tx(ar);
 	return NETDEV_TX_OK;
@@ -2326,57 +1900,6 @@
 	return err;
 }
 
-static int ar9170_sta_add(struct ieee80211_hw *hw,
-			  struct ieee80211_vif *vif,
-			  struct ieee80211_sta *sta)
-{
-	struct ar9170 *ar = hw->priv;
-	struct ar9170_sta_info *sta_info = (void *) sta->drv_priv;
-	unsigned int i;
-
-	memset(sta_info, 0, sizeof(*sta_info));
-
-	if (!sta->ht_cap.ht_supported)
-		return 0;
-
-	if (sta->ht_cap.ampdu_density > ar->global_ampdu_density)
-		ar->global_ampdu_density = sta->ht_cap.ampdu_density;
-
-	if (sta->ht_cap.ampdu_factor < ar->global_ampdu_factor)
-		ar->global_ampdu_factor = sta->ht_cap.ampdu_factor;
-
-	for (i = 0; i < AR9170_NUM_TID; i++) {
-		sta_info->agg[i].state = AR9170_TID_STATE_SHUTDOWN;
-		sta_info->agg[i].active = false;
-		sta_info->agg[i].ssn = 0;
-		sta_info->agg[i].tid = i;
-		INIT_LIST_HEAD(&sta_info->agg[i].list);
-		skb_queue_head_init(&sta_info->agg[i].queue);
-	}
-
-	sta_info->ampdu_max_len = 1 << (3 + sta->ht_cap.ampdu_factor);
-
-	return 0;
-}
-
-static int ar9170_sta_remove(struct ieee80211_hw *hw,
-			     struct ieee80211_vif *vif,
-			     struct ieee80211_sta *sta)
-{
-	struct ar9170_sta_info *sta_info = (void *) sta->drv_priv;
-	unsigned int i;
-
-	if (!sta->ht_cap.ht_supported)
-		return 0;
-
-	for (i = 0; i < AR9170_NUM_TID; i++) {
-		sta_info->agg[i].state = AR9170_TID_STATE_INVALID;
-		skb_queue_purge(&sta_info->agg[i].queue);
-	}
-
-	return 0;
-}
-
 static int ar9170_get_stats(struct ieee80211_hw *hw,
 			    struct ieee80211_low_level_stats *stats)
 {
@@ -2419,55 +1942,7 @@
 			       enum ieee80211_ampdu_mlme_action action,
 			       struct ieee80211_sta *sta, u16 tid, u16 *ssn)
 {
-	struct ar9170 *ar = hw->priv;
-	struct ar9170_sta_info *sta_info = (void *) sta->drv_priv;
-	struct ar9170_sta_tid *tid_info = &sta_info->agg[tid];
-	unsigned long flags;
-
-	if (!modparam_ht)
-		return -EOPNOTSUPP;
-
 	switch (action) {
-	case IEEE80211_AMPDU_TX_START:
-		spin_lock_irqsave(&ar->tx_ampdu_list_lock, flags);
-		if (tid_info->state != AR9170_TID_STATE_SHUTDOWN ||
-		    !list_empty(&tid_info->list)) {
-			spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags);
-#ifdef AR9170_TXAGG_DEBUG
-			printk(KERN_INFO "%s: A-MPDU [ESS:[%pM] tid:[%d]] "
-			       "is in a very bad state!\n",
-			       wiphy_name(hw->wiphy), sta->addr, tid);
-#endif /* AR9170_TXAGG_DEBUG */
-			return -EBUSY;
-		}
-
-		*ssn = tid_info->ssn;
-		tid_info->state = AR9170_TID_STATE_PROGRESS;
-		tid_info->active = false;
-		spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags);
-		ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
-		break;
-
-	case IEEE80211_AMPDU_TX_STOP:
-		spin_lock_irqsave(&ar->tx_ampdu_list_lock, flags);
-		tid_info->state = AR9170_TID_STATE_SHUTDOWN;
-		list_del_init(&tid_info->list);
-		tid_info->active = false;
-		skb_queue_purge(&tid_info->queue);
-		spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags);
-		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
-		break;
-
-	case IEEE80211_AMPDU_TX_OPERATIONAL:
-#ifdef AR9170_TXAGG_DEBUG
-		printk(KERN_INFO "%s: A-MPDU for %pM [tid:%d] Operational.\n",
-		       wiphy_name(hw->wiphy), sta->addr, tid);
-#endif /* AR9170_TXAGG_DEBUG */
-		spin_lock_irqsave(&ar->tx_ampdu_list_lock, flags);
-		sta_info->agg[tid].state = AR9170_TID_STATE_COMPLETE;
-		spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags);
-		break;
-
 	case IEEE80211_AMPDU_RX_START:
 	case IEEE80211_AMPDU_RX_STOP:
 		/* Handled by firmware */
@@ -2493,8 +1968,6 @@
 	.bss_info_changed	= ar9170_op_bss_info_changed,
 	.get_tsf		= ar9170_op_get_tsf,
 	.set_key		= ar9170_set_key,
-	.sta_add		= ar9170_sta_add,
-	.sta_remove		= ar9170_sta_remove,
 	.get_stats		= ar9170_get_stats,
 	.ampdu_action		= ar9170_ampdu_action,
 };
@@ -2527,8 +2000,6 @@
 	mutex_init(&ar->mutex);
 	spin_lock_init(&ar->cmdlock);
 	spin_lock_init(&ar->tx_stats_lock);
-	spin_lock_init(&ar->tx_ampdu_list_lock);
-	skb_queue_head_init(&ar->tx_status_ampdu);
 	for (i = 0; i < __AR9170_NUM_TXQ; i++) {
 		skb_queue_head_init(&ar->tx_status[i]);
 		skb_queue_head_init(&ar->tx_pending[i]);
@@ -2536,7 +2007,6 @@
 	ar9170_rx_reset_rx_mpdu(ar);
 	INIT_WORK(&ar->beacon_work, ar9170_new_beacon);
 	INIT_DELAYED_WORK(&ar->tx_janitor, ar9170_tx_janitor);
-	INIT_LIST_HEAD(&ar->tx_ampdu_list);
 
 	/* all hw supports 2.4 GHz, so set channel to 1 by default */
 	ar->channel = &ar9170_2ghz_chantable[0];
@@ -2549,16 +2019,8 @@
 			 IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
 			 IEEE80211_HW_SIGNAL_DBM;
 
-	if (modparam_ht) {
-		ar->hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
-	} else {
-		ar9170_band_2GHz.ht_cap.ht_supported = false;
-		ar9170_band_5GHz.ht_cap.ht_supported = false;
-	}
-
 	ar->hw->queues = __AR9170_NUM_TXQ;
 	ar->hw->extra_tx_headroom = 8;
-	ar->hw->sta_data_size = sizeof(struct ar9170_sta_info);
 
 	ar->hw->max_rates = 1;
 	ar->hw->max_rate_tries = 3;
diff --git a/drivers/net/wireless/ath/ath5k/ani.c b/drivers/net/wireless/ath/ath5k/ani.c
index 584a3285..f2311ab 100644
--- a/drivers/net/wireless/ath/ath5k/ani.c
+++ b/drivers/net/wireless/ath/ath5k/ani.c
@@ -73,7 +73,7 @@
 	const s8 hi[] = { -14, -12 };
 	const s8 fr[] = { -78, -80 };
 #endif
-	if (level < 0 || level > ARRAY_SIZE(sz)) {
+	if (level < 0 || level >= ARRAY_SIZE(sz)) {
 		ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI,
 			"level out of range %d", level);
 		return;
@@ -104,7 +104,7 @@
 {
 	const int val[] = { 2, 4, 6, 8, 10, 12, 14, 16 };
 
-	if (level < 0 || level > ARRAY_SIZE(val) ||
+	if (level < 0 || level >= ARRAY_SIZE(val) ||
 	    level > ah->ah_sc->ani_state.max_spur_level) {
 		ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI,
 			"level out of range %d", level);
@@ -129,7 +129,7 @@
 {
 	const int val[] = { 0, 4, 8 };
 
-	if (level < 0 || level > ARRAY_SIZE(val)) {
+	if (level < 0 || level >= ARRAY_SIZE(val)) {
 		ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_ANI,
 			"level out of range %d", level);
 		return;
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_initvals.h b/drivers/net/wireless/ath/ath9k/ar9003_initvals.h
index a131cd1..ef6116e 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_initvals.h
@@ -541,7 +541,7 @@
 
 static const u32 ar9300_2p0_soc_postamble[][5] = {
 	/* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
-	{0x00007010, 0x00000023, 0x00000023, 0x00000022, 0x00000022},
+	{0x00007010, 0x00000023, 0x00000023, 0x00000023, 0x00000023},
 };
 
 static const u32 ar9200_merlin_2p0_radio_core[][2] = {
@@ -588,12 +588,12 @@
 
 static const u32 ar9300_2p0_baseband_postamble[][5] = {
 	/* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
-	{0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8005},
+	{0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a800b},
 	{0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e},
 	{0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
 	{0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881},
 	{0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
-	{0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x0000059c},
+	{0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x00000b9c},
 	{0x00009c00, 0x00000044, 0x000000c4, 0x000000c4, 0x00000044},
 	{0x00009e00, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0},
 	{0x00009e04, 0x00802020, 0x00802020, 0x00802020, 0x00802020},
@@ -744,7 +744,7 @@
 	{0x0000a408, 0x0e79e5c6},
 	{0x0000a40c, 0x00820820},
 	{0x0000a414, 0x1ce739ce},
-	{0x0000a418, 0x7d001dce},
+	{0x0000a418, 0x2d001dce},
 	{0x0000a41c, 0x1ce739ce},
 	{0x0000a420, 0x000001ce},
 	{0x0000a424, 0x1ce739ce},
@@ -756,7 +756,7 @@
 	{0x0000a43c, 0x00000000},
 	{0x0000a440, 0x00000000},
 	{0x0000a444, 0x00000000},
-	{0x0000a448, 0x07000080},
+	{0x0000a448, 0x04000080},
 	{0x0000a44c, 0x00000001},
 	{0x0000a450, 0x00010000},
 	{0x0000a458, 0x00000000},
@@ -777,7 +777,7 @@
 	{0x0000a638, 0x00000000},
 	{0x0000a63c, 0x00000000},
 	{0x0000a640, 0x00000000},
-	{0x0000a644, 0x3ffd9d74},
+	{0x0000a644, 0x3fad9d74},
 	{0x0000a648, 0x0048060a},
 	{0x0000a64c, 0x00000637},
 	{0x0000a670, 0x03020100},
@@ -835,9 +835,9 @@
 
 static const u32 ar9300Modes_high_power_tx_gain_table_2p0[][5] = {
 	/* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
-	{0x0000a410, 0x000050db, 0x000050db, 0x000050d9, 0x000050d9},
-	{0x0000a500, 0x00020220, 0x00020220, 0x00000000, 0x00000000},
-	{0x0000a504, 0x06020223, 0x06020223, 0x04000002, 0x04000002},
+	{0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+	{0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000},
+	{0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002},
 	{0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004},
 	{0x0000a50c, 0x0f022223, 0x0f022223, 0x0b000200, 0x0b000200},
 	{0x0000a510, 0x14022620, 0x14022620, 0x0f000202, 0x0f000202},
@@ -848,28 +848,28 @@
 	{0x0000a524, 0x28023042, 0x28023042, 0x1f000a02, 0x1f000a02},
 	{0x0000a528, 0x2c023044, 0x2c023044, 0x23000a04, 0x23000a04},
 	{0x0000a52c, 0x2f023644, 0x2f023644, 0x26000a20, 0x26000a20},
-	{0x0000a530, 0x34043643, 0x34043643, 0x2a000e20, 0x2a000e20},
-	{0x0000a534, 0x38043a44, 0x38043a44, 0x2e000e22, 0x2e000e22},
-	{0x0000a538, 0x3b043e45, 0x3b043e45, 0x31000e24, 0x31000e24},
-	{0x0000a53c, 0x40063e46, 0x40063e46, 0x34001640, 0x34001640},
-	{0x0000a540, 0x44083e46, 0x44083e46, 0x38001660, 0x38001660},
-	{0x0000a544, 0x46083e66, 0x46083e66, 0x3b001861, 0x3b001861},
-	{0x0000a548, 0x4b0a3e69, 0x4b0a3e69, 0x3e001a81, 0x3e001a81},
-	{0x0000a54c, 0x4f0a5e66, 0x4f0a5e66, 0x42001a83, 0x42001a83},
-	{0x0000a550, 0x540a7e66, 0x540a7e66, 0x44001c84, 0x44001c84},
-	{0x0000a554, 0x570a7e89, 0x570a7e89, 0x48001ce3, 0x48001ce3},
-	{0x0000a558, 0x5c0e7e8a, 0x5c0e7e8a, 0x4c001ce5, 0x4c001ce5},
-	{0x0000a55c, 0x60127e8b, 0x60127e8b, 0x50001ce9, 0x50001ce9},
-	{0x0000a560, 0x65127ecc, 0x65127ecc, 0x54001ceb, 0x54001ceb},
-	{0x0000a564, 0x6b169ecd, 0x6b169ecd, 0x56001eec, 0x56001eec},
-	{0x0000a568, 0x70169f0e, 0x70169f0e, 0x56001eec, 0x56001eec},
-	{0x0000a56c, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
-	{0x0000a570, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
-	{0x0000a574, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
-	{0x0000a578, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
-	{0x0000a57c, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
-	{0x0000a580, 0x00820220, 0x00820220, 0x00800000, 0x00800000},
-	{0x0000a584, 0x06820223, 0x06820223, 0x04800002, 0x04800002},
+	{0x0000a530, 0x34025643, 0x34025643, 0x2a000e20, 0x2a000e20},
+	{0x0000a534, 0x38025a44, 0x38025a44, 0x2e000e22, 0x2e000e22},
+	{0x0000a538, 0x3b025e45, 0x3b025e45, 0x31000e24, 0x31000e24},
+	{0x0000a53c, 0x41025e4a, 0x41025e4a, 0x34001640, 0x34001640},
+	{0x0000a540, 0x48025e6c, 0x48025e6c, 0x38001660, 0x38001660},
+	{0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3b001861, 0x3b001861},
+	{0x0000a548, 0x53025eb2, 0x53025eb2, 0x3e001a81, 0x3e001a81},
+	{0x0000a54c, 0x59025eb5, 0x59025eb5, 0x42001a83, 0x42001a83},
+	{0x0000a550, 0x5f025ef6, 0x5f025ef6, 0x44001c84, 0x44001c84},
+	{0x0000a554, 0x62025f56, 0x62025f56, 0x48001ce3, 0x48001ce3},
+	{0x0000a558, 0x66027f56, 0x66027f56, 0x4c001ce5, 0x4c001ce5},
+	{0x0000a55c, 0x6a029f56, 0x6a029f56, 0x50001ce9, 0x50001ce9},
+	{0x0000a560, 0x70049f56, 0x70049f56, 0x54001ceb, 0x54001ceb},
+	{0x0000a564, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+	{0x0000a568, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+	{0x0000a56c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+	{0x0000a570, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+	{0x0000a574, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+	{0x0000a578, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+	{0x0000a57c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+	{0x0000a580, 0x00802220, 0x00802220, 0x00800000, 0x00800000},
+	{0x0000a584, 0x06802223, 0x06802223, 0x04800002, 0x04800002},
 	{0x0000a588, 0x0a822220, 0x0a822220, 0x08800004, 0x08800004},
 	{0x0000a58c, 0x0f822223, 0x0f822223, 0x0b800200, 0x0b800200},
 	{0x0000a590, 0x14822620, 0x14822620, 0x0f800202, 0x0f800202},
@@ -880,42 +880,42 @@
 	{0x0000a5a4, 0x28823042, 0x28823042, 0x1f800a02, 0x1f800a02},
 	{0x0000a5a8, 0x2c823044, 0x2c823044, 0x23800a04, 0x23800a04},
 	{0x0000a5ac, 0x2f823644, 0x2f823644, 0x26800a20, 0x26800a20},
-	{0x0000a5b0, 0x34843643, 0x34843643, 0x2a800e20, 0x2a800e20},
-	{0x0000a5b4, 0x38843a44, 0x38843a44, 0x2e800e22, 0x2e800e22},
-	{0x0000a5b8, 0x3b843e45, 0x3b843e45, 0x31800e24, 0x31800e24},
-	{0x0000a5bc, 0x40863e46, 0x40863e46, 0x34801640, 0x34801640},
-	{0x0000a5c0, 0x4c8a3065, 0x44883e46, 0x44883e46, 0x38801660},
-	{0x0000a5c4, 0x46883e66, 0x46883e66, 0x3b801861, 0x3b801861},
-	{0x0000a5c8, 0x4b8a3e69, 0x4b8a3e69, 0x3e801a81, 0x3e801a81},
-	{0x0000a5cc, 0x4f8a5e66, 0x4f8a5e66, 0x42801a83, 0x42801a83},
-	{0x0000a5d0, 0x548a7e66, 0x548a7e66, 0x44801c84, 0x44801c84},
-	{0x0000a5d4, 0x578a7e89, 0x578a7e89, 0x48801ce3, 0x48801ce3},
-	{0x0000a5d8, 0x5c8e7e8a, 0x5c8e7e8a, 0x4c801ce5, 0x4c801ce5},
-	{0x0000a5dc, 0x60927e8b, 0x60927e8b, 0x50801ce9, 0x50801ce9},
-	{0x0000a5e0, 0x65927ecc, 0x65927ecc, 0x54801ceb, 0x54801ceb},
-	{0x0000a5e4, 0x6b969ecd, 0x6b969ecd, 0x56801eec, 0x56801eec},
-	{0x0000a5e8, 0x70969f0e, 0x70969f0e, 0x56801eec, 0x56801eec},
-	{0x0000a5ec, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
-	{0x0000a5f0, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
-	{0x0000a5f4, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
-	{0x0000a5f8, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
-	{0x0000a5fc, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
+	{0x0000a5b0, 0x34825643, 0x34825643, 0x2a800e20, 0x2a800e20},
+	{0x0000a5b4, 0x38825a44, 0x38825a44, 0x2e800e22, 0x2e800e22},
+	{0x0000a5b8, 0x3b825e45, 0x3b825e45, 0x31800e24, 0x31800e24},
+	{0x0000a5bc, 0x41825e4a, 0x41825e4a, 0x34801640, 0x34801640},
+	{0x0000a5c0, 0x48825e6c, 0x48825e6c, 0x38801660, 0x38801660},
+	{0x0000a5c4, 0x4e825e8e, 0x4e825e8e, 0x3b801861, 0x3b801861},
+	{0x0000a5c8, 0x53825eb2, 0x53825eb2, 0x3e801a81, 0x3e801a81},
+	{0x0000a5cc, 0x59825eb5, 0x59825eb5, 0x42801a83, 0x42801a83},
+	{0x0000a5d0, 0x5f825ef6, 0x5f825ef6, 0x44801c84, 0x44801c84},
+	{0x0000a5d4, 0x62825f56, 0x62825f56, 0x48801ce3, 0x48801ce3},
+	{0x0000a5d8, 0x66827f56, 0x66827f56, 0x4c801ce5, 0x4c801ce5},
+	{0x0000a5dc, 0x6a829f56, 0x6a829f56, 0x50801ce9, 0x50801ce9},
+	{0x0000a5e0, 0x70849f56, 0x70849f56, 0x54801ceb, 0x54801ceb},
+	{0x0000a5e4, 0x7584ff56, 0x7584ff56, 0x56801eec, 0x56801eec},
+	{0x0000a5e8, 0x7584ff56, 0x7584ff56, 0x56801eec, 0x56801eec},
+	{0x0000a5ec, 0x7584ff56, 0x7584ff56, 0x56801eec, 0x56801eec},
+	{0x0000a5f0, 0x7584ff56, 0x7584ff56, 0x56801eec, 0x56801eec},
+	{0x0000a5f4, 0x7584ff56, 0x7584ff56, 0x56801eec, 0x56801eec},
+	{0x0000a5f8, 0x7584ff56, 0x7584ff56, 0x56801eec, 0x56801eec},
+	{0x0000a5fc, 0x7584ff56, 0x7584ff56, 0x56801eec, 0x56801eec},
 	{0x00016044, 0x056db2e6, 0x056db2e6, 0x056db2e6, 0x056db2e6},
-	{0x00016048, 0xad241a61, 0xad241a61, 0xad241a61, 0xad241a61},
+	{0x00016048, 0xae481a61, 0xae481a61, 0xae481a61, 0xae481a61},
 	{0x00016068, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c},
 	{0x00016444, 0x056db2e6, 0x056db2e6, 0x056db2e6, 0x056db2e6},
-	{0x00016448, 0xad241a61, 0xad241a61, 0xad241a61, 0xad241a61},
+	{0x00016448, 0xae481a61, 0xae481a61, 0xae481a61, 0xae481a61},
 	{0x00016468, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c},
 	{0x00016844, 0x056db2e6, 0x056db2e6, 0x056db2e6, 0x056db2e6},
-	{0x00016848, 0xad241a61, 0xad241a61, 0xad241a61, 0xad241a61},
+	{0x00016848, 0xae481a61, 0xae481a61, 0xae481a61, 0xae481a61},
 	{0x00016868, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c},
 };
 
 static const u32 ar9300Modes_high_ob_db_tx_gain_table_2p0[][5] = {
 	/* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
-	{0x0000a410, 0x000050db, 0x000050db, 0x000050d9, 0x000050d9},
-	{0x0000a500, 0x00020220, 0x00020220, 0x00000000, 0x00000000},
-	{0x0000a504, 0x06020223, 0x06020223, 0x04000002, 0x04000002},
+	{0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+	{0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000},
+	{0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002},
 	{0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004},
 	{0x0000a50c, 0x0f022223, 0x0f022223, 0x0b000200, 0x0b000200},
 	{0x0000a510, 0x14022620, 0x14022620, 0x0f000202, 0x0f000202},
@@ -926,28 +926,28 @@
 	{0x0000a524, 0x28023042, 0x28023042, 0x1f000a02, 0x1f000a02},
 	{0x0000a528, 0x2c023044, 0x2c023044, 0x23000a04, 0x23000a04},
 	{0x0000a52c, 0x2f023644, 0x2f023644, 0x26000a20, 0x26000a20},
-	{0x0000a530, 0x34043643, 0x34043643, 0x2a000e20, 0x2a000e20},
-	{0x0000a534, 0x38043a44, 0x38043a44, 0x2e000e22, 0x2e000e22},
-	{0x0000a538, 0x3b043e45, 0x3b043e45, 0x31000e24, 0x31000e24},
-	{0x0000a53c, 0x40063e46, 0x40063e46, 0x34001640, 0x34001640},
-	{0x0000a540, 0x44083e46, 0x44083e46, 0x38001660, 0x38001660},
-	{0x0000a544, 0x46083e66, 0x46083e66, 0x3b001861, 0x3b001861},
-	{0x0000a548, 0x4b0a3e69, 0x4b0a3e69, 0x3e001a81, 0x3e001a81},
-	{0x0000a54c, 0x4f0a5e66, 0x4f0a5e66, 0x42001a83, 0x42001a83},
-	{0x0000a550, 0x540a7e66, 0x540a7e66, 0x44001c84, 0x44001c84},
-	{0x0000a554, 0x570a7e89, 0x570a7e89, 0x48001ce3, 0x48001ce3},
-	{0x0000a558, 0x5c0e7e8a, 0x5c0e7e8a, 0x4c001ce5, 0x4c001ce5},
-	{0x0000a55c, 0x60127e8b, 0x60127e8b, 0x50001ce9, 0x50001ce9},
-	{0x0000a560, 0x65127ecc, 0x65127ecc, 0x54001ceb, 0x54001ceb},
-	{0x0000a564, 0x6b169ecd, 0x6b169ecd, 0x56001eec, 0x56001eec},
-	{0x0000a568, 0x70169f0e, 0x70169f0e, 0x56001eec, 0x56001eec},
-	{0x0000a56c, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
-	{0x0000a570, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
-	{0x0000a574, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
-	{0x0000a578, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
-	{0x0000a57c, 0x75169f4f, 0x75169f4f, 0x56001eec, 0x56001eec},
-	{0x0000a580, 0x00820220, 0x00820220, 0x00800000, 0x00800000},
-	{0x0000a584, 0x06820223, 0x06820223, 0x04800002, 0x04800002},
+	{0x0000a530, 0x34025643, 0x34025643, 0x2a000e20, 0x2a000e20},
+	{0x0000a534, 0x38025a44, 0x38025a44, 0x2e000e22, 0x2e000e22},
+	{0x0000a538, 0x3b025e45, 0x3b025e45, 0x31000e24, 0x31000e24},
+	{0x0000a53c, 0x41025e4a, 0x41025e4a, 0x34001640, 0x34001640},
+	{0x0000a540, 0x48025e6c, 0x48025e6c, 0x38001660, 0x38001660},
+	{0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3b001861, 0x3b001861},
+	{0x0000a548, 0x53025eb2, 0x53025eb2, 0x3e001a81, 0x3e001a81},
+	{0x0000a54c, 0x59025eb5, 0x59025eb5, 0x42001a83, 0x42001a83},
+	{0x0000a550, 0x5f025ef6, 0x5f025ef6, 0x44001c84, 0x44001c84},
+	{0x0000a554, 0x62025f56, 0x62025f56, 0x48001ce3, 0x48001ce3},
+	{0x0000a558, 0x66027f56, 0x66027f56, 0x4c001ce5, 0x4c001ce5},
+	{0x0000a55c, 0x6a029f56, 0x6a029f56, 0x50001ce9, 0x50001ce9},
+	{0x0000a560, 0x70049f56, 0x70049f56, 0x54001ceb, 0x54001ceb},
+	{0x0000a564, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+	{0x0000a568, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+	{0x0000a56c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+	{0x0000a570, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+	{0x0000a574, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+	{0x0000a578, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+	{0x0000a57c, 0x7504ff56, 0x7504ff56, 0x56001eec, 0x56001eec},
+	{0x0000a580, 0x00802220, 0x00802220, 0x00800000, 0x00800000},
+	{0x0000a584, 0x06802223, 0x06802223, 0x04800002, 0x04800002},
 	{0x0000a588, 0x0a822220, 0x0a822220, 0x08800004, 0x08800004},
 	{0x0000a58c, 0x0f822223, 0x0f822223, 0x0b800200, 0x0b800200},
 	{0x0000a590, 0x14822620, 0x14822620, 0x0f800202, 0x0f800202},
@@ -958,34 +958,34 @@
 	{0x0000a5a4, 0x28823042, 0x28823042, 0x1f800a02, 0x1f800a02},
 	{0x0000a5a8, 0x2c823044, 0x2c823044, 0x23800a04, 0x23800a04},
 	{0x0000a5ac, 0x2f823644, 0x2f823644, 0x26800a20, 0x26800a20},
-	{0x0000a5b0, 0x34843643, 0x34843643, 0x2a800e20, 0x2a800e20},
-	{0x0000a5b4, 0x38843a44, 0x38843a44, 0x2e800e22, 0x2e800e22},
-	{0x0000a5b8, 0x3b843e45, 0x3b843e45, 0x31800e24, 0x31800e24},
-	{0x0000a5bc, 0x40863e46, 0x40863e46, 0x34801640, 0x34801640},
-	{0x0000a5c0, 0x44883e46, 0x44883e46, 0x38801660, 0x38801660},
-	{0x0000a5c4, 0x46883e66, 0x46883e66, 0x3b801861, 0x3b801861},
-	{0x0000a5c8, 0x4b8a3e69, 0x4b8a3e69, 0x3e801a81, 0x3e801a81},
-	{0x0000a5cc, 0x4f8a5e66, 0x4f8a5e66, 0x42801a83, 0x42801a83},
-	{0x0000a5d0, 0x548a7e66, 0x548a7e66, 0x44801c84, 0x44801c84},
-	{0x0000a5d4, 0x578a7e89, 0x578a7e89, 0x48801ce3, 0x48801ce3},
-	{0x0000a5d8, 0x5c8e7e8a, 0x5c8e7e8a, 0x4c801ce5, 0x4c801ce5},
-	{0x0000a5dc, 0x60927e8b, 0x60927e8b, 0x50801ce9, 0x50801ce9},
-	{0x0000a5e0, 0x65927ecc, 0x65927ecc, 0x54801ceb, 0x54801ceb},
-	{0x0000a5e4, 0x6b969ecd, 0x6b969ecd, 0x56801eec, 0x56801eec},
-	{0x0000a5e8, 0x70969f0e, 0x70969f0e, 0x56801eec, 0x56801eec},
-	{0x0000a5ec, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
-	{0x0000a5f0, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
-	{0x0000a5f4, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
-	{0x0000a5f8, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
-	{0x0000a5fc, 0x75969f4f, 0x75969f4f, 0x56801eec, 0x56801eec},
+	{0x0000a5b0, 0x34825643, 0x34825643, 0x2a800e20, 0x2a800e20},
+	{0x0000a5b4, 0x38825a44, 0x38825a44, 0x2e800e22, 0x2e800e22},
+	{0x0000a5b8, 0x3b825e45, 0x3b825e45, 0x31800e24, 0x31800e24},
+	{0x0000a5bc, 0x41825e4a, 0x41825e4a, 0x34801640, 0x34801640},
+	{0x0000a5c0, 0x48825e6c, 0x48825e6c, 0x38801660, 0x38801660},
+	{0x0000a5c4, 0x4e825e8e, 0x4e825e8e, 0x3b801861, 0x3b801861},
+	{0x0000a5c8, 0x53825eb2, 0x53825eb2, 0x3e801a81, 0x3e801a81},
+	{0x0000a5cc, 0x59825eb5, 0x59825eb5, 0x42801a83, 0x42801a83},
+	{0x0000a5d0, 0x5f825ef6, 0x5f825ef6, 0x44801c84, 0x44801c84},
+	{0x0000a5d4, 0x62825f56, 0x62825f56, 0x48801ce3, 0x48801ce3},
+	{0x0000a5d8, 0x66827f56, 0x66827f56, 0x4c801ce5, 0x4c801ce5},
+	{0x0000a5dc, 0x6a829f56, 0x6a829f56, 0x50801ce9, 0x50801ce9},
+	{0x0000a5e0, 0x70849f56, 0x70849f56, 0x54801ceb, 0x54801ceb},
+	{0x0000a5e4, 0x7584ff56, 0x7584ff56, 0x56801eec, 0x56801eec},
+	{0x0000a5e8, 0x7584ff56, 0x7584ff56, 0x56801eec, 0x56801eec},
+	{0x0000a5ec, 0x7584ff56, 0x7584ff56, 0x56801eec, 0x56801eec},
+	{0x0000a5f0, 0x7584ff56, 0x7584ff56, 0x56801eec, 0x56801eec},
+	{0x0000a5f4, 0x7584ff56, 0x7584ff56, 0x56801eec, 0x56801eec},
+	{0x0000a5f8, 0x7584ff56, 0x7584ff56, 0x56801eec, 0x56801eec},
+	{0x0000a5fc, 0x7584ff56, 0x7584ff56, 0x56801eec, 0x56801eec},
 	{0x00016044, 0x056db2e4, 0x056db2e4, 0x056db2e4, 0x056db2e4},
-	{0x00016048, 0x8c001a61, 0x8c001a61, 0x8c001a61, 0x8c001a61},
+	{0x00016048, 0x8e481a61, 0x8e481a61, 0x8e481a61, 0x8e481a61},
 	{0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
 	{0x00016444, 0x056db2e4, 0x056db2e4, 0x056db2e4, 0x056db2e4},
-	{0x00016448, 0x8c001a61, 0x8c001a61, 0x8c001a61, 0x8c001a61},
+	{0x00016448, 0x8e481a61, 0x8e481a61, 0x8e481a61, 0x8e481a61},
 	{0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
 	{0x00016844, 0x056db2e4, 0x056db2e4, 0x056db2e4, 0x056db2e4},
-	{0x00016848, 0x8c001a61, 0x8c001a61, 0x8c001a61, 0x8c001a61},
+	{0x00016848, 0x8e481a61, 0x8e481a61, 0x8e481a61, 0x8e481a61},
 	{0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
 };
 
diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c
index b4424a6..7707341 100644
--- a/drivers/net/wireless/ath/ath9k/common.c
+++ b/drivers/net/wireless/ath/ath9k/common.c
@@ -57,13 +57,19 @@
 	 * rs_more indicates chained descriptors which can be used
 	 * to link buffers together for a sort of scatter-gather
 	 * operation.
-	 *
+	 * reject the frame, we don't support scatter-gather yet and
+	 * the frame is probably corrupt anyway
+	 */
+	if (rx_stats->rs_more)
+		return false;
+
+	/*
 	 * The rx_stats->rs_status will not be set until the end of the
 	 * chained descriptors so it can be ignored if rs_more is set. The
 	 * rs_more will be false at the last element of the chained
 	 * descriptors.
 	 */
-	if (!rx_stats->rs_more && rx_stats->rs_status != 0) {
+	if (rx_stats->rs_status != 0) {
 		if (rx_stats->rs_status & ATH9K_RXERR_CRC)
 			rxs->flag |= RX_FLAG_FAILED_FCS_CRC;
 		if (rx_stats->rs_status & ATH9K_RXERR_PHY)
@@ -102,11 +108,11 @@
 	return true;
 }
 
-static u8 ath9k_process_rate(struct ath_common *common,
-			     struct ieee80211_hw *hw,
-			     struct ath_rx_status *rx_stats,
-			     struct ieee80211_rx_status *rxs,
-			     struct sk_buff *skb)
+static int ath9k_process_rate(struct ath_common *common,
+			      struct ieee80211_hw *hw,
+			      struct ath_rx_status *rx_stats,
+			      struct ieee80211_rx_status *rxs,
+			      struct sk_buff *skb)
 {
 	struct ieee80211_supported_band *sband;
 	enum ieee80211_band band;
@@ -122,25 +128,32 @@
 			rxs->flag |= RX_FLAG_40MHZ;
 		if (rx_stats->rs_flags & ATH9K_RX_GI)
 			rxs->flag |= RX_FLAG_SHORT_GI;
-		return rx_stats->rs_rate & 0x7f;
+		rxs->rate_idx = rx_stats->rs_rate & 0x7f;
+		return 0;
 	}
 
 	for (i = 0; i < sband->n_bitrates; i++) {
-		if (sband->bitrates[i].hw_value == rx_stats->rs_rate)
-			return i;
+		if (sband->bitrates[i].hw_value == rx_stats->rs_rate) {
+			rxs->rate_idx = i;
+			return 0;
+		}
 		if (sband->bitrates[i].hw_value_short == rx_stats->rs_rate) {
 			rxs->flag |= RX_FLAG_SHORTPRE;
-			return i;
+			rxs->rate_idx = i;
+			return 0;
 		}
 	}
 
-	/* No valid hardware bitrate found -- we should not get here */
+	/*
+	 * No valid hardware bitrate found -- we should not get here
+	 * because hardware has already validated this frame as OK.
+	 */
 	ath_print(common, ATH_DBG_XMIT, "unsupported hw bitrate detected "
 		  "0x%02x using 1 Mbit\n", rx_stats->rs_rate);
 	if ((common->debug_mask & ATH_DBG_XMIT))
 		print_hex_dump_bytes("", DUMP_PREFIX_NONE, skb->data, skb->len);
 
-        return 0;
+	return -EINVAL;
 }
 
 static void ath9k_process_rssi(struct ath_common *common,
@@ -202,13 +215,19 @@
 	struct ath_hw *ah = common->ah;
 
 	memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
+
+	/*
+	 * everything but the rate is checked here, the rate check is done
+	 * separately to avoid doing two lookups for a rate for each frame.
+	 */
 	if (!ath9k_rx_accept(common, skb, rx_status, rx_stats, decrypt_error))
 		return -EINVAL;
 
 	ath9k_process_rssi(common, hw, skb, rx_stats);
 
-	rx_status->rate_idx = ath9k_process_rate(common, hw,
-						 rx_stats, rx_status, skb);
+	if (ath9k_process_rate(common, hw, rx_stats, rx_status, skb))
+		return -EINVAL;
+
 	rx_status->mactime = ath9k_hw_extend_tsf(ah, rx_stats->rs_tstamp);
 	rx_status->band = hw->conf.channel->band;
 	rx_status->freq = hw->conf.channel->center_freq;
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index 1ae18bb..ad556aa 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -356,7 +356,6 @@
 	u16 seq_no;
 	u32 bmiss_cnt;
 
-	struct sk_buff *beacon;
 	spinlock_t beacon_lock;
 
 	bool tx_queues_stop;
@@ -408,13 +407,13 @@
 void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv,
 			     struct ieee80211_vif *vif);
 void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending);
-void ath9k_htc_beacon_update(struct ath9k_htc_priv *priv,
-			     struct ieee80211_vif *vif);
 
 void ath9k_htc_rxep(void *priv, struct sk_buff *skb,
 		    enum htc_endpoint_id ep_id);
 void ath9k_htc_txep(void *priv, struct sk_buff *skb, enum htc_endpoint_id ep_id,
 		    bool txok);
+void ath9k_htc_beaconep(void *drv_priv, struct sk_buff *skb,
+			enum htc_endpoint_id ep_id, bool txok);
 
 void ath9k_htc_station_work(struct work_struct *work);
 void ath9k_htc_aggr_work(struct work_struct *work);
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
index 7cb55f5..c10c7d0 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
@@ -165,22 +165,10 @@
 	WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask);
 }
 
-void ath9k_htc_beacon_update(struct ath9k_htc_priv *priv,
-			     struct ieee80211_vif *vif)
+void ath9k_htc_beaconep(void *drv_priv, struct sk_buff *skb,
+			enum htc_endpoint_id ep_id, bool txok)
 {
-	struct ath_common *common = ath9k_hw_common(priv->ah);
-
-	spin_lock_bh(&priv->beacon_lock);
-
-	if (priv->beacon)
-		dev_kfree_skb_any(priv->beacon);
-
-	priv->beacon = ieee80211_beacon_get(priv->hw, vif);
-	if (!priv->beacon)
-		ath_print(common, ATH_DBG_BEACON,
-			  "Unable to allocate beacon\n");
-
-	spin_unlock_bh(&priv->beacon_lock);
+	dev_kfree_skb_any(skb);
 }
 
 void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending)
@@ -189,6 +177,7 @@
 	struct tx_beacon_header beacon_hdr;
 	struct ath9k_htc_tx_ctl tx_ctl;
 	struct ieee80211_tx_info *info;
+	struct sk_buff *beacon;
 	u8 *tx_fhdr;
 
 	memset(&beacon_hdr, 0, sizeof(struct tx_beacon_header));
@@ -207,25 +196,17 @@
 		return;
 	}
 
-	if (unlikely(priv->beacon == NULL)) {
-		spin_unlock_bh(&priv->beacon_lock);
-		return;
-	}
-
-	/* Free the old SKB first */
-	dev_kfree_skb_any(priv->beacon);
-
 	/* Get a new beacon */
-	priv->beacon = ieee80211_beacon_get(priv->hw, priv->vif);
-	if (!priv->beacon) {
+	beacon = ieee80211_beacon_get(priv->hw, priv->vif);
+	if (!beacon) {
 		spin_unlock_bh(&priv->beacon_lock);
 		return;
 	}
 
-	info = IEEE80211_SKB_CB(priv->beacon);
+	info = IEEE80211_SKB_CB(beacon);
 	if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
 		struct ieee80211_hdr *hdr =
-			(struct ieee80211_hdr *) priv->beacon->data;
+			(struct ieee80211_hdr *) beacon->data;
 		priv->seq_no += 0x10;
 		hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
 		hdr->seq_ctrl |= cpu_to_le16(priv->seq_no);
@@ -233,10 +214,10 @@
 
 	tx_ctl.type = ATH9K_HTC_NORMAL;
 	beacon_hdr.vif_index = avp->index;
-	tx_fhdr = skb_push(priv->beacon, sizeof(beacon_hdr));
+	tx_fhdr = skb_push(beacon, sizeof(beacon_hdr));
 	memcpy(tx_fhdr, (u8 *) &beacon_hdr, sizeof(beacon_hdr));
 
-	htc_send(priv->htc, priv->beacon, priv->beacon_ep, &tx_ctl);
+	htc_send(priv->htc, beacon, priv->beacon_ep, &tx_ctl);
 
 	spin_unlock_bh(&priv->beacon_lock);
 }
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
index 701f2ef..17111fc 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
@@ -144,7 +144,7 @@
 		goto err;
 
 	/* Beacon */
-	ret = ath9k_htc_connect_svc(priv, WMI_BEACON_SVC, NULL,
+	ret = ath9k_htc_connect_svc(priv, WMI_BEACON_SVC, ath9k_htc_beaconep,
 				    &priv->beacon_ep);
 	if (ret)
 		goto err;
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index ca7f3a7..6c386dad 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -461,11 +461,11 @@
 	struct ath_common *common = ath9k_hw_common(priv->ah);
 	struct ath9k_htc_target_aggr aggr;
 	struct ieee80211_sta *sta = NULL;
-	struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
+	struct ath9k_htc_sta *ista;
 	int ret = 0;
 	u8 cmd_rsp;
 
-	if (tid > ATH9K_HTC_MAX_TID)
+	if (tid >= ATH9K_HTC_MAX_TID)
 		return -EINVAL;
 
 	memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
@@ -1099,7 +1099,7 @@
 	return 0;
 }
 
-static int ath9k_htc_radio_enable(struct ieee80211_hw *hw)
+static int ath9k_htc_radio_enable(struct ieee80211_hw *hw, bool led)
 {
 	struct ath9k_htc_priv *priv = hw->priv;
 	struct ath_hw *ah = priv->ah;
@@ -1147,6 +1147,13 @@
 	priv->tx_queues_stop = false;
 	spin_unlock_bh(&priv->tx_lock);
 
+	if (led) {
+		/* Enable LED */
+		ath9k_hw_cfg_output(ah, ah->led_pin,
+				    AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+		ath9k_hw_set_gpio(ah, ah->led_pin, 0);
+	}
+
 	ieee80211_wake_queues(hw);
 
 	return ret;
@@ -1158,13 +1165,13 @@
 	int ret = 0;
 
 	mutex_lock(&priv->mutex);
-	ret = ath9k_htc_radio_enable(hw);
+	ret = ath9k_htc_radio_enable(hw, false);
 	mutex_unlock(&priv->mutex);
 
 	return ret;
 }
 
-static void ath9k_htc_radio_disable(struct ieee80211_hw *hw)
+static void ath9k_htc_radio_disable(struct ieee80211_hw *hw, bool led)
 {
 	struct ath9k_htc_priv *priv = hw->priv;
 	struct ath_hw *ah = priv->ah;
@@ -1177,6 +1184,12 @@
 		return;
 	}
 
+	if (led) {
+		/* Disable LED */
+		ath9k_hw_set_gpio(ah, ah->led_pin, 1);
+		ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
+	}
+
 	/* Cancel all the running timers/work .. */
 	cancel_work_sync(&priv->ps_work);
 	cancel_delayed_work_sync(&priv->ath9k_ani_work);
@@ -1217,7 +1230,7 @@
 	struct ath9k_htc_priv *priv = hw->priv;
 
 	mutex_lock(&priv->mutex);
-	ath9k_htc_radio_disable(hw);
+	ath9k_htc_radio_disable(hw, false);
 	mutex_unlock(&priv->mutex);
 }
 
@@ -1313,15 +1326,6 @@
 	priv->nvifs--;
 
 	ath9k_htc_remove_station(priv, vif, NULL);
-
-	if (vif->type == NL80211_IFTYPE_ADHOC) {
-		spin_lock_bh(&priv->beacon_lock);
-		if (priv->beacon)
-			dev_kfree_skb_any(priv->beacon);
-		priv->beacon = NULL;
-		spin_unlock_bh(&priv->beacon_lock);
-	}
-
 	priv->vif = NULL;
 
 	mutex_unlock(&priv->mutex);
@@ -1346,7 +1350,7 @@
 
 		if (enable_radio) {
 			ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
-			ath9k_htc_radio_enable(hw);
+			ath9k_htc_radio_enable(hw, true);
 			ath_print(common, ATH_DBG_CONFIG,
 				  "not-idle: enabling radio\n");
 		}
@@ -1398,10 +1402,9 @@
 	if (priv->ps_idle) {
 		ath_print(common, ATH_DBG_CONFIG,
 			  "idle: disabling radio\n");
-		ath9k_htc_radio_disable(hw);
+		ath9k_htc_radio_disable(hw, true);
 	}
 
-
 	mutex_unlock(&priv->mutex);
 
 	return 0;
@@ -1590,9 +1593,6 @@
 		ath9k_htc_beacon_config(priv, vif);
 	}
 
-	if (changed & BSS_CHANGED_BEACON)
-		ath9k_htc_beacon_update(priv, vif);
-
 	if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
 	    !bss_conf->enable_beacon) {
 		priv->op_flags &= ~OP_ENABLE_BEACON;
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 5590192..c33f17d 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -574,6 +574,26 @@
 
 	ath9k_hw_init_mode_regs(ah);
 
+	/*
+	 * Configire PCIE after Ini init. SERDES values now come from ini file
+	 * This enables PCIe low power mode.
+	 */
+	if (AR_SREV_9300_20_OR_LATER(ah)) {
+		u32 regval;
+		unsigned int i;
+
+		/* Set Bits 16 and 17 in the AR_WA register. */
+		regval = REG_READ(ah, AR_WA);
+		regval |= 0x00030000;
+		REG_WRITE(ah, AR_WA, regval);
+
+		for (i = 0; i < ah->iniPcieSerdesLowPower.ia_rows; i++) {
+			REG_WRITE(ah,
+				  INI_RA(&ah->iniPcieSerdesLowPower, i, 0),
+				  INI_RA(&ah->iniPcieSerdesLowPower, i, 1));
+		}
+	}
+
 	if (ah->is_pciexpress)
 		ath9k_hw_configpcipowersave(ah, 0, 0);
 	else
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c
index 17197a7..99b876a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.c
@@ -2483,7 +2483,6 @@
 				   &priv->_3945.shared_phys, GFP_KERNEL);
 	if (!priv->_3945.shared_virt) {
 		IWL_ERR(priv, "failed to allocate pci memory\n");
-		mutex_unlock(&priv->mutex);
 		return -ENOMEM;
 	}
 
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index ce7bec4..9d5d3cc 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -79,6 +79,7 @@
 
 
 static int lbs_cfg_set_channel(struct wiphy *wiphy,
+	struct net_device *netdev,
 	struct ieee80211_channel *chan,
 	enum nl80211_channel_type channel_type)
 {
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 9fd2bead..6f8cb3e 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -652,17 +652,17 @@
 	add_timer(&data->beacon_timer);
 }
 
+static const char *hwsim_chantypes[] = {
+	[NL80211_CHAN_NO_HT] = "noht",
+	[NL80211_CHAN_HT20] = "ht20",
+	[NL80211_CHAN_HT40MINUS] = "ht40-",
+	[NL80211_CHAN_HT40PLUS] = "ht40+",
+};
 
 static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
 {
 	struct mac80211_hwsim_data *data = hw->priv;
 	struct ieee80211_conf *conf = &hw->conf;
-	static const char *chantypes[4] = {
-		[NL80211_CHAN_NO_HT] = "noht",
-		[NL80211_CHAN_HT20] = "ht20",
-		[NL80211_CHAN_HT40MINUS] = "ht40-",
-		[NL80211_CHAN_HT40PLUS] = "ht40+",
-	};
 	static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = {
 		[IEEE80211_SMPS_AUTOMATIC] = "auto",
 		[IEEE80211_SMPS_OFF] = "off",
@@ -673,7 +673,7 @@
 	printk(KERN_DEBUG "%s:%s (freq=%d/%s idle=%d ps=%d smps=%s)\n",
 	       wiphy_name(hw->wiphy), __func__,
 	       conf->channel->center_freq,
-	       chantypes[conf->channel_type],
+	       hwsim_chantypes[conf->channel_type],
 	       !!(conf->flags & IEEE80211_CONF_IDLE),
 	       !!(conf->flags & IEEE80211_CONF_PS),
 	       smps_modes[conf->smps_mode]);
@@ -761,9 +761,10 @@
 	}
 
 	if (changed & BSS_CHANGED_HT) {
-		printk(KERN_DEBUG "  %s: HT: op_mode=0x%x\n",
+		printk(KERN_DEBUG "  %s: HT: op_mode=0x%x, chantype=%s\n",
 		       wiphy_name(hw->wiphy),
-		       info->ht_operation_mode);
+		       info->ht_operation_mode,
+		       hwsim_chantypes[info->channel_type]);
 	}
 
 	if (changed & BSS_CHANGED_BASIC_RATES) {
diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/orinoco/cfg.c
index 81d228d..8c4169c 100644
--- a/drivers/net/wireless/orinoco/cfg.c
+++ b/drivers/net/wireless/orinoco/cfg.c
@@ -159,6 +159,7 @@
 }
 
 static int orinoco_set_channel(struct wiphy *wiphy,
+			struct net_device *netdev,
 			struct ieee80211_channel *chan,
 			enum nl80211_channel_type channel_type)
 {
diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/orinoco/main.c
index 884a777..97e954e 100644
--- a/drivers/net/wireless/orinoco/main.c
+++ b/drivers/net/wireless/orinoco/main.c
@@ -340,18 +340,109 @@
 /* Tx path                                                          */
 /********************************************************************/
 
+/* Add encapsulation and MIC to the existing SKB.
+ * The main xmit routine will then send the whole lot to the card.
+ * Need 8 bytes headroom
+ * Need 8 bytes tailroom
+ *
+ *                          With encapsulated ethernet II frame
+ *                          --------
+ *                          803.3 header (14 bytes)
+ *                           dst[6]
+ * --------                  src[6]
+ * 803.3 header (14 bytes)   len[2]
+ *  dst[6]                  803.2 header (8 bytes)
+ *  src[6]                   encaps[6]
+ *  len[2] <- leave alone -> len[2]
+ * --------                 -------- <-- 0
+ * Payload                  Payload
+ * ...                      ...
+ *
+ * --------                 --------
+ *                          MIC (8 bytes)
+ *                          --------
+ *
+ * returns 0 on success, -ENOMEM on error.
+ */
+int orinoco_process_xmit_skb(struct sk_buff *skb,
+			     struct net_device *dev,
+			     struct orinoco_private *priv,
+			     int *tx_control,
+			     u8 *mic_buf)
+{
+	struct orinoco_tkip_key *key;
+	struct ethhdr *eh;
+	int do_mic;
+
+	key = (struct orinoco_tkip_key *) priv->keys[priv->tx_key].key;
+
+	do_mic = ((priv->encode_alg == ORINOCO_ALG_TKIP) &&
+		  (key != NULL));
+
+	if (do_mic)
+		*tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) |
+			HERMES_TXCTRL_MIC;
+
+	eh = (struct ethhdr *)skb->data;
+
+	/* Encapsulate Ethernet-II frames */
+	if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */
+		struct header_struct {
+			struct ethhdr eth;	/* 802.3 header */
+			u8 encap[6];		/* 802.2 header */
+		} __attribute__ ((packed)) hdr;
+		int len = skb->len + sizeof(encaps_hdr) - (2 * ETH_ALEN);
+
+		if (skb_headroom(skb) < ENCAPS_OVERHEAD) {
+			if (net_ratelimit())
+				printk(KERN_ERR
+				       "%s: Not enough headroom for 802.2 headers %d\n",
+				       dev->name, skb_headroom(skb));
+			return -ENOMEM;
+		}
+
+		/* Fill in new header */
+		memcpy(&hdr.eth, eh, 2 * ETH_ALEN);
+		hdr.eth.h_proto = htons(len);
+		memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr));
+
+		/* Make room for the new header, and copy it in */
+		eh = (struct ethhdr *) skb_push(skb, ENCAPS_OVERHEAD);
+		memcpy(eh, &hdr, sizeof(hdr));
+	}
+
+	/* Calculate Michael MIC */
+	if (do_mic) {
+		size_t len = skb->len - ETH_HLEN;
+		u8 *mic = &mic_buf[0];
+
+		/* Have to write to an even address, so copy the spare
+		 * byte across */
+		if (skb->len % 2) {
+			*mic = skb->data[skb->len - 1];
+			mic++;
+		}
+
+		orinoco_mic(priv->tx_tfm_mic, key->tx_mic,
+			    eh->h_dest, eh->h_source, 0 /* priority */,
+			    skb->data + ETH_HLEN,
+			    len, mic);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(orinoco_process_xmit_skb);
+
 static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct orinoco_private *priv = ndev_priv(dev);
 	struct net_device_stats *stats = &priv->stats;
-	struct orinoco_tkip_key *key;
 	hermes_t *hw = &priv->hw;
 	int err = 0;
 	u16 txfid = priv->txfid;
-	struct ethhdr *eh;
 	int tx_control;
 	unsigned long flags;
-	int do_mic;
+	u8 mic_buf[MICHAEL_MIC_LEN+1];
 
 	if (!netif_running(dev)) {
 		printk(KERN_ERR "%s: Tx on stopped device!\n",
@@ -383,16 +474,12 @@
 	if (skb->len < ETH_HLEN)
 		goto drop;
 
-	key = (struct orinoco_tkip_key *) priv->keys[priv->tx_key].key;
-
-	do_mic = ((priv->encode_alg == ORINOCO_ALG_TKIP) &&
-		  (key != NULL));
-
 	tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX;
 
-	if (do_mic)
-		tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) |
-			HERMES_TXCTRL_MIC;
+	err = orinoco_process_xmit_skb(skb, dev, priv, &tx_control,
+				       &mic_buf[0]);
+	if (err)
+		goto drop;
 
 	if (priv->has_alt_txcntl) {
 		/* WPA enabled firmwares have tx_cntl at the end of
@@ -435,34 +522,6 @@
 				   HERMES_802_3_OFFSET - HERMES_802_11_OFFSET);
 	}
 
-	eh = (struct ethhdr *)skb->data;
-
-	/* Encapsulate Ethernet-II frames */
-	if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */
-		struct header_struct {
-			struct ethhdr eth;	/* 802.3 header */
-			u8 encap[6];		/* 802.2 header */
-		} __attribute__ ((packed)) hdr;
-
-		/* Strip destination and source from the data */
-		skb_pull(skb, 2 * ETH_ALEN);
-
-		/* And move them to a separate header */
-		memcpy(&hdr.eth, eh, 2 * ETH_ALEN);
-		hdr.eth.h_proto = htons(sizeof(encaps_hdr) + skb->len);
-		memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr));
-
-		/* Insert the SNAP header */
-		if (skb_headroom(skb) < sizeof(hdr)) {
-			printk(KERN_ERR
-			       "%s: Not enough headroom for 802.2 headers %d\n",
-			       dev->name, skb_headroom(skb));
-			goto drop;
-		}
-		eh = (struct ethhdr *) skb_push(skb, sizeof(hdr));
-		memcpy(eh, &hdr, sizeof(hdr));
-	}
-
 	err = hw->ops->bap_pwrite(hw, USER_BAP, skb->data, skb->len,
 				  txfid, HERMES_802_3_OFFSET);
 	if (err) {
@@ -471,32 +530,16 @@
 		goto busy;
 	}
 
-	/* Calculate Michael MIC */
-	if (do_mic) {
-		u8 mic_buf[MICHAEL_MIC_LEN + 1];
-		u8 *mic;
-		size_t offset;
-		size_t len;
+	if (tx_control & HERMES_TXCTRL_MIC) {
+		size_t offset = HERMES_802_3_OFFSET + skb->len;
+		size_t len = MICHAEL_MIC_LEN;
 
-		if (skb->len % 2) {
-			/* MIC start is on an odd boundary */
-			mic_buf[0] = skb->data[skb->len - 1];
-			mic = &mic_buf[1];
-			offset = skb->len - 1;
-			len = MICHAEL_MIC_LEN + 1;
-		} else {
-			mic = &mic_buf[0];
-			offset = skb->len;
-			len = MICHAEL_MIC_LEN;
+		if (offset % 2) {
+			offset--;
+			len++;
 		}
-
-		orinoco_mic(priv->tx_tfm_mic, key->tx_mic,
-			    eh->h_dest, eh->h_source, 0 /* priority */,
-			    skb->data + ETH_HLEN, skb->len - ETH_HLEN, mic);
-
-		/* Write the MIC */
 		err = hw->ops->bap_pwrite(hw, USER_BAP, &mic_buf[0], len,
-					  txfid, HERMES_802_3_OFFSET + offset);
+					  txfid, offset);
 		if (err) {
 			printk(KERN_ERR "%s: Error %d writing MIC to BAP\n",
 			       dev->name, err);
@@ -2234,7 +2277,7 @@
 	/* we use the default eth_mac_addr for setting the MAC addr */
 
 	/* Reserve space in skb for the SNAP header */
-	dev->hard_header_len += ENCAPS_OVERHEAD;
+	dev->needed_headroom = ENCAPS_OVERHEAD;
 
 	netif_carrier_off(dev);
 
diff --git a/drivers/net/wireless/orinoco/orinoco.h b/drivers/net/wireless/orinoco/orinoco.h
index e9f415a..a6da86e 100644
--- a/drivers/net/wireless/orinoco/orinoco.h
+++ b/drivers/net/wireless/orinoco/orinoco.h
@@ -200,6 +200,12 @@
 extern void __orinoco_ev_info(struct net_device *dev, hermes_t *hw);
 extern void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw);
 
+int orinoco_process_xmit_skb(struct sk_buff *skb,
+			     struct net_device *dev,
+			     struct orinoco_private *priv,
+			     int *tx_control,
+			     u8 *mic);
+
 /* Common ndo functions exported for reuse by orinoco_usb */
 int orinoco_open(struct net_device *dev);
 int orinoco_stop(struct net_device *dev);
diff --git a/drivers/net/wireless/orinoco/orinoco_usb.c b/drivers/net/wireless/orinoco/orinoco_usb.c
index e220933..78f089b 100644
--- a/drivers/net/wireless/orinoco/orinoco_usb.c
+++ b/drivers/net/wireless/orinoco/orinoco_usb.c
@@ -67,6 +67,7 @@
 #include <linux/wireless.h>
 #include <linux/firmware.h>
 
+#include "mic.h"
 #include "orinoco.h"
 
 #ifndef URB_ASYNC_UNLINK
@@ -1198,11 +1199,9 @@
 	struct orinoco_private *priv = ndev_priv(dev);
 	struct net_device_stats *stats = &priv->stats;
 	struct ezusb_priv *upriv = priv->card;
+	u8 mic[MICHAEL_MIC_LEN+1];
 	int err = 0;
-	char *p;
-	struct ethhdr *eh;
-	int len, data_len, data_off;
-	__le16 tx_control;
+	int tx_control;
 	unsigned long flags;
 	struct request_context *ctx;
 	u8 *buf;
@@ -1222,7 +1221,7 @@
 
 	if (orinoco_lock(priv, &flags) != 0) {
 		printk(KERN_ERR
-		       "%s: orinoco_xmit() called while hw_unavailable\n",
+		       "%s: ezusb_xmit() called while hw_unavailable\n",
 		       dev->name);
 		return NETDEV_TX_BUSY;
 	}
@@ -1232,53 +1231,46 @@
 		/* Oops, the firmware hasn't established a connection,
 		   silently drop the packet (this seems to be the
 		   safest approach). */
-		stats->tx_errors++;
-		orinoco_unlock(priv, &flags);
-		dev_kfree_skb(skb);
-		return NETDEV_TX_OK;
+		goto drop;
 	}
 
+	/* Check packet length */
+	if (skb->len < ETH_HLEN)
+		goto drop;
+
 	ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_TX, 0);
 	if (!ctx)
-		goto fail;
+		goto busy;
 
 	memset(ctx->buf, 0, BULK_BUF_SIZE);
 	buf = ctx->buf->data;
 
-	/* Length of the packet body */
-	/* FIXME: what if the skb is smaller than this? */
-	len = max_t(int, skb->len - ETH_HLEN, ETH_ZLEN - ETH_HLEN);
+	tx_control = 0;
 
-	eh = (struct ethhdr *) skb->data;
+	err = orinoco_process_xmit_skb(skb, dev, priv, &tx_control,
+				       &mic[0]);
+	if (err)
+		goto drop;
 
-	tx_control = cpu_to_le16(0);
-	memcpy(buf, &tx_control, sizeof(tx_control));
-	buf += sizeof(tx_control);
-	/* Encapsulate Ethernet-II frames */
-	if (ntohs(eh->h_proto) > ETH_DATA_LEN) {	/* Ethernet-II frame */
-		struct header_struct *hdr = (void *) buf;
-		buf += sizeof(*hdr);
-		data_len = len;
-		data_off = sizeof(tx_control) + sizeof(*hdr);
-		p = skb->data + ETH_HLEN;
-
-		/* 802.3 header */
-		memcpy(hdr->dest, eh->h_dest, ETH_ALEN);
-		memcpy(hdr->src, eh->h_source, ETH_ALEN);
-		hdr->len = htons(data_len + ENCAPS_OVERHEAD);
-
-		/* 802.2 header */
-		memcpy(&hdr->dsap, &encaps_hdr, sizeof(encaps_hdr));
-
-		hdr->ethertype = eh->h_proto;
-	} else {		/* IEEE 802.3 frame */
-		data_len = len + ETH_HLEN;
-		data_off = sizeof(tx_control);
-		p = skb->data;
+	{
+		__le16 *tx_cntl = (__le16 *)buf;
+		*tx_cntl = cpu_to_le16(tx_control);
+		buf += sizeof(*tx_cntl);
 	}
 
-	memcpy(buf, p, data_len);
-	buf += data_len;
+	memcpy(buf, skb->data, skb->len);
+	buf += skb->len;
+
+	if (tx_control & HERMES_TXCTRL_MIC) {
+		u8 *m = mic;
+		/* Mic has been offset so it can be copied to an even
+		 * address. We're copying eveything anyway, so we
+		 * don't need to copy that first byte. */
+		if (skb->len % 2)
+			m++;
+		memcpy(buf, m, MICHAEL_MIC_LEN);
+		buf += MICHAEL_MIC_LEN;
+	}
 
 	/* Finally, we actually initiate the send */
 	netif_stop_queue(dev);
@@ -1294,20 +1286,23 @@
 		if (net_ratelimit())
 			printk(KERN_ERR "%s: Error %d transmitting packet\n",
 				dev->name, err);
-		stats->tx_errors++;
-		goto fail;
+		goto busy;
 	}
 
 	dev->trans_start = jiffies;
-	stats->tx_bytes += data_off + data_len;
+	stats->tx_bytes += skb->len;
+	goto ok;
 
+ drop:
+	stats->tx_errors++;
+	stats->tx_dropped++;
+
+ ok:
 	orinoco_unlock(priv, &flags);
-
 	dev_kfree_skb(skb);
-
 	return NETDEV_TX_OK;
 
- fail:
+ busy:
 	orinoco_unlock(priv, &flags);
 	return NETDEV_TX_BUSY;
 }
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index babdcdf..2d28908 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -535,7 +535,7 @@
 
 static int rndis_leave_ibss(struct wiphy *wiphy, struct net_device *dev);
 
-static int rndis_set_channel(struct wiphy *wiphy,
+static int rndis_set_channel(struct wiphy *wiphy, struct net_device *dev,
 	struct ieee80211_channel *chan, enum nl80211_channel_type channel_type);
 
 static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev,
@@ -2291,7 +2291,7 @@
 	return deauthenticate(usbdev);
 }
 
-static int rndis_set_channel(struct wiphy *wiphy,
+static int rndis_set_channel(struct wiphy *wiphy, struct net_device *netdev,
 	struct ieee80211_channel *chan, enum nl80211_channel_type channel_type)
 {
 	struct rndis_wlan_private *priv = wiphy_priv(wiphy);
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c
index 06b92f8..6126c0a 100644
--- a/drivers/net/wireless/rt2x00/rt2400pci.c
+++ b/drivers/net/wireless/rt2x00/rt2400pci.c
@@ -1060,7 +1060,8 @@
 /*
  * TX data initialization
  */
-static void rt2400pci_write_beacon(struct queue_entry *entry)
+static void rt2400pci_write_beacon(struct queue_entry *entry,
+				   struct txentry_desc *txdesc)
 {
 	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
 	struct queue_entry_priv_pci *entry_priv = entry->priv_data;
@@ -1090,6 +1091,14 @@
 	rt2x00_desc_read(entry_priv->desc, 1, &word);
 	rt2x00_set_field32(&word, TXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma);
 	rt2x00_desc_write(entry_priv->desc, 1, word);
+
+	/*
+	 * Enable beaconing again.
+	 */
+	rt2x00_set_field32(&reg, CSR14_TSF_COUNT, 1);
+	rt2x00_set_field32(&reg, CSR14_TBCN, 1);
+	rt2x00_set_field32(&reg, CSR14_BEACON_GEN, 1);
+	rt2x00pci_register_write(rt2x00dev, CSR14, reg);
 }
 
 static void rt2400pci_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
@@ -1097,17 +1106,6 @@
 {
 	u32 reg;
 
-	if (queue == QID_BEACON) {
-		rt2x00pci_register_read(rt2x00dev, CSR14, &reg);
-		if (!rt2x00_get_field32(reg, CSR14_BEACON_GEN)) {
-			rt2x00_set_field32(&reg, CSR14_TSF_COUNT, 1);
-			rt2x00_set_field32(&reg, CSR14_TBCN, 1);
-			rt2x00_set_field32(&reg, CSR14_BEACON_GEN, 1);
-			rt2x00pci_register_write(rt2x00dev, CSR14, reg);
-		}
-		return;
-	}
-
 	rt2x00pci_register_read(rt2x00dev, TXCSR0, &reg);
 	rt2x00_set_field32(&reg, TXCSR0_KICK_PRIO, (queue == QID_AC_BE));
 	rt2x00_set_field32(&reg, TXCSR0_KICK_TX, (queue == QID_AC_BK));
diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c
index ae8e205..2e4f461 100644
--- a/drivers/net/wireless/rt2x00/rt2500pci.c
+++ b/drivers/net/wireless/rt2x00/rt2500pci.c
@@ -1217,7 +1217,8 @@
 /*
  * TX data initialization
  */
-static void rt2500pci_write_beacon(struct queue_entry *entry)
+static void rt2500pci_write_beacon(struct queue_entry *entry,
+				   struct txentry_desc *txdesc)
 {
 	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
 	struct queue_entry_priv_pci *entry_priv = entry->priv_data;
@@ -1247,6 +1248,14 @@
 	rt2x00_desc_read(entry_priv->desc, 1, &word);
 	rt2x00_set_field32(&word, TXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma);
 	rt2x00_desc_write(entry_priv->desc, 1, word);
+
+	/*
+	 * Enable beaconing again.
+	 */
+	rt2x00_set_field32(&reg, CSR14_TSF_COUNT, 1);
+	rt2x00_set_field32(&reg, CSR14_TBCN, 1);
+	rt2x00_set_field32(&reg, CSR14_BEACON_GEN, 1);
+	rt2x00pci_register_write(rt2x00dev, CSR14, reg);
 }
 
 static void rt2500pci_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
@@ -1254,17 +1263,6 @@
 {
 	u32 reg;
 
-	if (queue == QID_BEACON) {
-		rt2x00pci_register_read(rt2x00dev, CSR14, &reg);
-		if (!rt2x00_get_field32(reg, CSR14_BEACON_GEN)) {
-			rt2x00_set_field32(&reg, CSR14_TSF_COUNT, 1);
-			rt2x00_set_field32(&reg, CSR14_TBCN, 1);
-			rt2x00_set_field32(&reg, CSR14_BEACON_GEN, 1);
-			rt2x00pci_register_write(rt2x00dev, CSR14, reg);
-		}
-		return;
-	}
-
 	rt2x00pci_register_read(rt2x00dev, TXCSR0, &reg);
 	rt2x00_set_field32(&reg, TXCSR0_KICK_PRIO, (queue == QID_AC_BE));
 	rt2x00_set_field32(&reg, TXCSR0_KICK_TX, (queue == QID_AC_BK));
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c
index 41d9996..e88d703 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/rt2x00/rt2500usb.c
@@ -1083,7 +1083,8 @@
  */
 static void rt2500usb_beacondone(struct urb *urb);
 
-static void rt2500usb_write_beacon(struct queue_entry *entry)
+static void rt2500usb_write_beacon(struct queue_entry *entry,
+				   struct txentry_desc *txdesc)
 {
 	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
 	struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev);
@@ -1091,7 +1092,7 @@
 	struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
 	int pipe = usb_sndbulkpipe(usb_dev, entry->queue->usb_endpoint);
 	int length;
-	u16 reg;
+	u16 reg, reg0;
 
 	/*
 	 * Add the descriptor in front of the skb.
@@ -1133,6 +1134,26 @@
 	 * Send out the guardian byte.
 	 */
 	usb_submit_urb(bcn_priv->guardian_urb, GFP_ATOMIC);
+
+	/*
+	 * Enable beaconing again.
+	 */
+	rt2x00_set_field16(&reg, TXRX_CSR19_TSF_COUNT, 1);
+	rt2x00_set_field16(&reg, TXRX_CSR19_TBCN, 1);
+	reg0 = reg;
+	rt2x00_set_field16(&reg, TXRX_CSR19_BEACON_GEN, 1);
+	/*
+	 * Beacon generation will fail initially.
+	 * To prevent this we need to change the TXRX_CSR19
+	 * register several times (reg0 is the same as reg
+	 * except for TXRX_CSR19_BEACON_GEN, which is 0 in reg0
+	 * and 1 in reg).
+	 */
+	rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
+	rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg0);
+	rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
+	rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg0);
+	rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
 }
 
 static int rt2500usb_get_tx_data_len(struct queue_entry *entry)
@@ -1149,37 +1170,6 @@
 	return length;
 }
 
-static void rt2500usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
-				    const enum data_queue_qid queue)
-{
-	u16 reg, reg0;
-
-	if (queue != QID_BEACON) {
-		rt2x00usb_kick_tx_queue(rt2x00dev, queue);
-		return;
-	}
-
-	rt2500usb_register_read(rt2x00dev, TXRX_CSR19, &reg);
-	if (!rt2x00_get_field16(reg, TXRX_CSR19_BEACON_GEN)) {
-		rt2x00_set_field16(&reg, TXRX_CSR19_TSF_COUNT, 1);
-		rt2x00_set_field16(&reg, TXRX_CSR19_TBCN, 1);
-		reg0 = reg;
-		rt2x00_set_field16(&reg, TXRX_CSR19_BEACON_GEN, 1);
-		/*
-		 * Beacon generation will fail initially.
-		 * To prevent this we need to change the TXRX_CSR19
-		 * register several times (reg0 is the same as reg
-		 * except for TXRX_CSR19_BEACON_GEN, which is 0 in reg0
-		 * and 1 in reg).
-		 */
-		rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
-		rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg0);
-		rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
-		rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg0);
-		rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg);
-	}
-}
-
 /*
  * RX control handlers
  */
@@ -1214,11 +1204,9 @@
 	if (rt2x00_get_field32(word0, RXD_W0_PHYSICAL_ERROR))
 		rxdesc->flags |= RX_FLAG_FAILED_PLCP_CRC;
 
-	if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) {
-		rxdesc->cipher = rt2x00_get_field32(word0, RXD_W0_CIPHER);
-		if (rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR))
-			rxdesc->cipher_status = RX_CRYPTO_FAIL_KEY;
-	}
+	rxdesc->cipher = rt2x00_get_field32(word0, RXD_W0_CIPHER);
+	if (rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR))
+		rxdesc->cipher_status = RX_CRYPTO_FAIL_KEY;
 
 	if (rxdesc->cipher != CIPHER_NONE) {
 		_rt2x00_desc_read(rxd, 2, &rxdesc->iv[0]);
@@ -1780,7 +1768,7 @@
 	.write_tx_data		= rt2x00usb_write_tx_data,
 	.write_beacon		= rt2500usb_write_beacon,
 	.get_tx_data_len	= rt2500usb_get_tx_data_len,
-	.kick_tx_queue		= rt2500usb_kick_tx_queue,
+	.kick_tx_queue		= rt2x00usb_kick_tx_queue,
 	.kill_tx_queue		= rt2x00usb_kill_tx_queue,
 	.fill_rxdone		= rt2500usb_fill_rxdone,
 	.config_shared_key	= rt2500usb_config_key,
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index e37bbea..db4250d 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -282,6 +282,104 @@
 }
 EXPORT_SYMBOL_GPL(rt2800_wait_wpdma_ready);
 
+void rt2800_write_txwi(struct sk_buff *skb, struct txentry_desc *txdesc)
+{
+	__le32 *txwi = (__le32 *)(skb->data - TXWI_DESC_SIZE);
+	u32 word;
+
+	/*
+	 * Initialize TX Info descriptor
+	 */
+	rt2x00_desc_read(txwi, 0, &word);
+	rt2x00_set_field32(&word, TXWI_W0_FRAG,
+			   test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags));
+	rt2x00_set_field32(&word, TXWI_W0_MIMO_PS, 0);
+	rt2x00_set_field32(&word, TXWI_W0_CF_ACK, 0);
+	rt2x00_set_field32(&word, TXWI_W0_TS,
+			   test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags));
+	rt2x00_set_field32(&word, TXWI_W0_AMPDU,
+			   test_bit(ENTRY_TXD_HT_AMPDU, &txdesc->flags));
+	rt2x00_set_field32(&word, TXWI_W0_MPDU_DENSITY, txdesc->mpdu_density);
+	rt2x00_set_field32(&word, TXWI_W0_TX_OP, txdesc->txop);
+	rt2x00_set_field32(&word, TXWI_W0_MCS, txdesc->mcs);
+	rt2x00_set_field32(&word, TXWI_W0_BW,
+			   test_bit(ENTRY_TXD_HT_BW_40, &txdesc->flags));
+	rt2x00_set_field32(&word, TXWI_W0_SHORT_GI,
+			   test_bit(ENTRY_TXD_HT_SHORT_GI, &txdesc->flags));
+	rt2x00_set_field32(&word, TXWI_W0_STBC, txdesc->stbc);
+	rt2x00_set_field32(&word, TXWI_W0_PHYMODE, txdesc->rate_mode);
+	rt2x00_desc_write(txwi, 0, word);
+
+	rt2x00_desc_read(txwi, 1, &word);
+	rt2x00_set_field32(&word, TXWI_W1_ACK,
+			   test_bit(ENTRY_TXD_ACK, &txdesc->flags));
+	rt2x00_set_field32(&word, TXWI_W1_NSEQ,
+			   test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags));
+	rt2x00_set_field32(&word, TXWI_W1_BW_WIN_SIZE, txdesc->ba_size);
+	rt2x00_set_field32(&word, TXWI_W1_WIRELESS_CLI_ID,
+			   test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags) ?
+			   txdesc->key_idx : 0xff);
+	rt2x00_set_field32(&word, TXWI_W1_MPDU_TOTAL_BYTE_COUNT,
+			   txdesc->length);
+	rt2x00_set_field32(&word, TXWI_W1_PACKETID, txdesc->queue + 1);
+	rt2x00_desc_write(txwi, 1, word);
+
+	/*
+	 * Always write 0 to IV/EIV fields, hardware will insert the IV
+	 * from the IVEIV register when TXD_W3_WIV is set to 0.
+	 * When TXD_W3_WIV is set to 1 it will use the IV data
+	 * from the descriptor. The TXWI_W1_WIRELESS_CLI_ID indicates which
+	 * crypto entry in the registers should be used to encrypt the frame.
+	 */
+	_rt2x00_desc_write(txwi, 2, 0 /* skbdesc->iv[0] */);
+	_rt2x00_desc_write(txwi, 3, 0 /* skbdesc->iv[1] */);
+}
+EXPORT_SYMBOL_GPL(rt2800_write_txwi);
+
+void rt2800_process_rxwi(struct sk_buff *skb, struct rxdone_entry_desc *rxdesc)
+{
+	__le32 *rxwi = (__le32 *) skb->data;
+	u32 word;
+
+	rt2x00_desc_read(rxwi, 0, &word);
+
+	rxdesc->cipher = rt2x00_get_field32(word, RXWI_W0_UDF);
+	rxdesc->size = rt2x00_get_field32(word, RXWI_W0_MPDU_TOTAL_BYTE_COUNT);
+
+	rt2x00_desc_read(rxwi, 1, &word);
+
+	if (rt2x00_get_field32(word, RXWI_W1_SHORT_GI))
+		rxdesc->flags |= RX_FLAG_SHORT_GI;
+
+	if (rt2x00_get_field32(word, RXWI_W1_BW))
+		rxdesc->flags |= RX_FLAG_40MHZ;
+
+	/*
+	 * Detect RX rate, always use MCS as signal type.
+	 */
+	rxdesc->dev_flags |= RXDONE_SIGNAL_MCS;
+	rxdesc->signal = rt2x00_get_field32(word, RXWI_W1_MCS);
+	rxdesc->rate_mode = rt2x00_get_field32(word, RXWI_W1_PHYMODE);
+
+	/*
+	 * Mask of 0x8 bit to remove the short preamble flag.
+	 */
+	if (rxdesc->rate_mode == RATE_MODE_CCK)
+		rxdesc->signal &= ~0x8;
+
+	rt2x00_desc_read(rxwi, 2, &word);
+
+	rxdesc->rssi =
+	    (rt2x00_get_field32(word, RXWI_W2_RSSI0) +
+	     rt2x00_get_field32(word, RXWI_W2_RSSI1)) / 2;
+
+	/*
+	 * Remove RXWI descriptor from start of buffer.
+	 */
+	skb_pull(skb, RXWI_DESC_SIZE);
+}
+EXPORT_SYMBOL_GPL(rt2800_process_rxwi);
+
 #ifdef CONFIG_RT2X00_LIB_DEBUGFS
 const struct rt2x00debug rt2800_rt2x00debug = {
 	.owner	= THIS_MODULE,
@@ -640,8 +738,6 @@
 	rt2800_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg);
 
 	rt2800_register_read(rt2x00dev, XIFS_TIME_CFG, &reg);
-	rt2x00_set_field32(&reg, XIFS_TIME_CFG_CCKM_SIFS_TIME, erp->sifs);
-	rt2x00_set_field32(&reg, XIFS_TIME_CFG_OFDM_SIFS_TIME, erp->sifs);
 	rt2x00_set_field32(&reg, XIFS_TIME_CFG_EIFS, erp->eifs);
 	rt2800_register_write(rt2x00dev, XIFS_TIME_CFG, reg);
 
@@ -1415,9 +1511,16 @@
 
 	rt2800_register_write(rt2x00dev, EXP_ACK_TIME, 0x002400ca);
 
+	/*
+	 * Usually the CCK SIFS time should be set to 10 and the OFDM SIFS
+	 * time should be set to 16. However, the original Ralink driver uses
+	 * 16 for both and indeed using a value of 10 for CCK SIFS results in
+	 * connection problems with 11g + CTS protection. Hence, use the same
+	 * defaults as the Ralink driver: 16 for both, CCK and OFDM SIFS.
+	 */
 	rt2800_register_read(rt2x00dev, XIFS_TIME_CFG, &reg);
-	rt2x00_set_field32(&reg, XIFS_TIME_CFG_CCKM_SIFS_TIME, 32);
-	rt2x00_set_field32(&reg, XIFS_TIME_CFG_OFDM_SIFS_TIME, 32);
+	rt2x00_set_field32(&reg, XIFS_TIME_CFG_CCKM_SIFS_TIME, 16);
+	rt2x00_set_field32(&reg, XIFS_TIME_CFG_OFDM_SIFS_TIME, 16);
 	rt2x00_set_field32(&reg, XIFS_TIME_CFG_OFDM_XIFS_TIME, 4);
 	rt2x00_set_field32(&reg, XIFS_TIME_CFG_EIFS, 314);
 	rt2x00_set_field32(&reg, XIFS_TIME_CFG_BB_RXEND_ENABLE, 1);
@@ -2219,7 +2322,7 @@
 EXPORT_SYMBOL_GPL(rt2800_init_eeprom);
 
 /*
- * RF value list for rt28x0
+ * RF value list for rt28xx
  * Supports: 2.4 GHz (all) & 5.2 GHz (RF2850 & RF2750)
  */
 static const struct rf_channel rf_vals[] = {
@@ -2294,10 +2397,10 @@
 };
 
 /*
- * RF value list for rt3070
- * Supports: 2.4 GHz
+ * RF value list for rt3xxx
+ * Supports: 2.4 GHz (all) & 5.2 GHz (RF3052)
  */
-static const struct rf_channel rf_vals_302x[] = {
+static const struct rf_channel rf_vals_3x[] = {
 	{1,  241, 2, 2 },
 	{2,  241, 2, 7 },
 	{3,  242, 2, 2 },
@@ -2312,6 +2415,51 @@
 	{12, 246, 2, 7 },
 	{13, 247, 2, 2 },
 	{14, 248, 2, 4 },
+
+	/* 802.11 UNI / HyperLan 2 */
+	{36, 0x56, 0, 4},
+	{38, 0x56, 0, 6},
+	{40, 0x56, 0, 8},
+	{44, 0x57, 0, 0},
+	{46, 0x57, 0, 2},
+	{48, 0x57, 0, 4},
+	{52, 0x57, 0, 8},
+	{54, 0x57, 0, 10},
+	{56, 0x58, 0, 0},
+	{60, 0x58, 0, 4},
+	{62, 0x58, 0, 6},
+	{64, 0x58, 0, 8},
+
+	/* 802.11 HyperLan 2 */
+	{100, 0x5b, 0, 8},
+	{102, 0x5b, 0, 10},
+	{104, 0x5c, 0, 0},
+	{108, 0x5c, 0, 4},
+	{110, 0x5c, 0, 6},
+	{112, 0x5c, 0, 8},
+	{116, 0x5d, 0, 0},
+	{118, 0x5d, 0, 2},
+	{120, 0x5d, 0, 4},
+	{124, 0x5d, 0, 8},
+	{126, 0x5d, 0, 10},
+	{128, 0x5e, 0, 0},
+	{132, 0x5e, 0, 4},
+	{134, 0x5e, 0, 6},
+	{136, 0x5e, 0, 8},
+	{140, 0x5f, 0, 0},
+
+	/* 802.11 UNII */
+	{149, 0x5f, 0, 9},
+	{151, 0x5f, 0, 11},
+	{153, 0x60, 0, 1},
+	{157, 0x60, 0, 5},
+	{159, 0x60, 0, 7},
+	{161, 0x60, 0, 9},
+	{165, 0x61, 0, 1},
+	{167, 0x61, 0, 3},
+	{169, 0x61, 0, 5},
+	{171, 0x61, 0, 7},
+	{173, 0x61, 0, 9},
 };
 
 int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
@@ -2352,11 +2500,11 @@
 	spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM;
 
 	if (rt2x00_rf(rt2x00dev, RF2820) ||
-	    rt2x00_rf(rt2x00dev, RF2720) ||
-	    rt2x00_rf(rt2x00dev, RF3052)) {
+	    rt2x00_rf(rt2x00dev, RF2720)) {
 		spec->num_channels = 14;
 		spec->channels = rf_vals;
-	} else if (rt2x00_rf(rt2x00dev, RF2850) || rt2x00_rf(rt2x00dev, RF2750)) {
+	} else if (rt2x00_rf(rt2x00dev, RF2850) ||
+		   rt2x00_rf(rt2x00dev, RF2750)) {
 		spec->supported_bands |= SUPPORT_BAND_5GHZ;
 		spec->num_channels = ARRAY_SIZE(rf_vals);
 		spec->channels = rf_vals;
@@ -2364,8 +2512,12 @@
 		   rt2x00_rf(rt2x00dev, RF2020) ||
 		   rt2x00_rf(rt2x00dev, RF3021) ||
 		   rt2x00_rf(rt2x00dev, RF3022)) {
-		spec->num_channels = ARRAY_SIZE(rf_vals_302x);
-		spec->channels = rf_vals_302x;
+		spec->num_channels = 14;
+		spec->channels = rf_vals_3x;
+	} else if (rt2x00_rf(rt2x00dev, RF3052)) {
+		spec->supported_bands |= SUPPORT_BAND_5GHZ;
+		spec->num_channels = ARRAY_SIZE(rf_vals_3x);
+		spec->channels = rf_vals_3x;
 	}
 
 	/*
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h
index ebabeae..94de999 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.h
+++ b/drivers/net/wireless/rt2x00/rt2800lib.h
@@ -111,6 +111,9 @@
 			const u8 command, const u8 token,
 			const u8 arg0, const u8 arg1);
 
+void rt2800_write_txwi(struct sk_buff *skb, struct txentry_desc *txdesc);
+void rt2800_process_rxwi(struct sk_buff *skb, struct rxdone_entry_desc *txdesc);
+
 extern const struct rt2x00debug rt2800_rt2x00debug;
 
 int rt2800_rfkill_poll(struct rt2x00_dev *rt2x00dev);
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c
index f08b6a3..7d4778d 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@ -616,67 +616,13 @@
 static int rt2800pci_write_tx_data(struct queue_entry* entry,
 				   struct txentry_desc *txdesc)
 {
-	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
-	struct sk_buff *skb = entry->skb;
-	struct skb_frame_desc *skbdesc;
 	int ret;
-	__le32 *txwi;
-	u32 word;
 
 	ret = rt2x00pci_write_tx_data(entry, txdesc);
 	if (ret)
 		return ret;
 
-	skbdesc = get_skb_frame_desc(skb);
-	txwi = (__le32 *)(skb->data - rt2x00dev->ops->extra_tx_headroom);
-
-	/*
-	 * Initialize TX Info descriptor
-	 */
-	rt2x00_desc_read(txwi, 0, &word);
-	rt2x00_set_field32(&word, TXWI_W0_FRAG,
-			   test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags));
-	rt2x00_set_field32(&word, TXWI_W0_MIMO_PS, 0);
-	rt2x00_set_field32(&word, TXWI_W0_CF_ACK, 0);
-	rt2x00_set_field32(&word, TXWI_W0_TS,
-			   test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags));
-	rt2x00_set_field32(&word, TXWI_W0_AMPDU,
-			   test_bit(ENTRY_TXD_HT_AMPDU, &txdesc->flags));
-	rt2x00_set_field32(&word, TXWI_W0_MPDU_DENSITY, txdesc->mpdu_density);
-	rt2x00_set_field32(&word, TXWI_W0_TX_OP, txdesc->ifs);
-	rt2x00_set_field32(&word, TXWI_W0_MCS, txdesc->mcs);
-	rt2x00_set_field32(&word, TXWI_W0_BW,
-			   test_bit(ENTRY_TXD_HT_BW_40, &txdesc->flags));
-	rt2x00_set_field32(&word, TXWI_W0_SHORT_GI,
-			   test_bit(ENTRY_TXD_HT_SHORT_GI, &txdesc->flags));
-	rt2x00_set_field32(&word, TXWI_W0_STBC, txdesc->stbc);
-	rt2x00_set_field32(&word, TXWI_W0_PHYMODE, txdesc->rate_mode);
-	rt2x00_desc_write(txwi, 0, word);
-
-	rt2x00_desc_read(txwi, 1, &word);
-	rt2x00_set_field32(&word, TXWI_W1_ACK,
-			   test_bit(ENTRY_TXD_ACK, &txdesc->flags));
-	rt2x00_set_field32(&word, TXWI_W1_NSEQ,
-			   test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags));
-	rt2x00_set_field32(&word, TXWI_W1_BW_WIN_SIZE, txdesc->ba_size);
-	rt2x00_set_field32(&word, TXWI_W1_WIRELESS_CLI_ID,
-			   test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags) ?
-			   txdesc->key_idx : 0xff);
-	rt2x00_set_field32(&word, TXWI_W1_MPDU_TOTAL_BYTE_COUNT,
-			   txdesc->length);
-	rt2x00_set_field32(&word, TXWI_W1_PACKETID,
-			   skbdesc->entry->queue->qid + 1);
-	rt2x00_desc_write(txwi, 1, word);
-
-	/*
-	 * Always write 0 to IV/EIV fields, hardware will insert the IV
-	 * from the IVEIV register when TXD_W3_WIV is set to 0.
-	 * When TXD_W3_WIV is set to 1 it will use the IV data
-	 * from the descriptor. The TXWI_W1_WIRELESS_CLI_ID indicates which
-	 * crypto entry in the registers should be used to encrypt the frame.
-	 */
-	_rt2x00_desc_write(txwi, 2, 0 /* skbdesc->iv[0] */);
-	_rt2x00_desc_write(txwi, 3, 0 /* skbdesc->iv[1] */);
+	rt2800_write_txwi(entry->skb, txdesc);
 
 	return 0;
 }
@@ -732,10 +678,10 @@
 /*
  * TX data initialization
  */
-static void rt2800pci_write_beacon(struct queue_entry *entry)
+static void rt2800pci_write_beacon(struct queue_entry *entry,
+				   struct txentry_desc *txdesc)
 {
 	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
-	struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
 	unsigned int beacon_base;
 	u32 reg;
 
@@ -748,15 +694,25 @@
 	rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
 
 	/*
-	 * Write entire beacon with descriptor to register.
+	 * Add the TXWI for the beacon to the skb.
+	 */
+	rt2800_write_txwi(entry->skb, txdesc);
+	skb_push(entry->skb, TXWI_DESC_SIZE);
+
+	/*
+	 * Write entire beacon with TXWI to register.
 	 */
 	beacon_base = HW_BEACON_OFFSET(entry->entry_idx);
-	rt2800_register_multiwrite(rt2x00dev,
-				      beacon_base,
-				      skbdesc->desc, skbdesc->desc_len);
-	rt2800_register_multiwrite(rt2x00dev,
-				      beacon_base + skbdesc->desc_len,
-				      entry->skb->data, entry->skb->len);
+	rt2800_register_multiwrite(rt2x00dev, beacon_base,
+				   entry->skb->data, entry->skb->len);
+
+	/*
+	 * Enable beaconing again.
+	 */
+	rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 1);
+	rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 1);
+	rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 1);
+	rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
 
 	/*
 	 * Clean up beacon skb.
@@ -770,18 +726,6 @@
 {
 	struct data_queue *queue;
 	unsigned int idx, qidx = 0;
-	u32 reg;
-
-	if (queue_idx == QID_BEACON) {
-		rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
-		if (!rt2x00_get_field32(reg, BCN_TIME_CFG_BEACON_GEN)) {
-			rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 1);
-			rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 1);
-			rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 1);
-			rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
-		}
-		return;
-	}
 
 	if (queue_idx > QID_HCCA && queue_idx != QID_MGMT)
 		return;
@@ -824,34 +768,21 @@
 	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
 	struct queue_entry_priv_pci *entry_priv = entry->priv_data;
 	__le32 *rxd = entry_priv->desc;
-	__le32 *rxwi = (__le32 *)entry->skb->data;
-	u32 rxd3;
-	u32 rxwi0;
-	u32 rxwi1;
-	u32 rxwi2;
-	u32 rxwi3;
+	u32 word;
 
-	rt2x00_desc_read(rxd, 3, &rxd3);
-	rt2x00_desc_read(rxwi, 0, &rxwi0);
-	rt2x00_desc_read(rxwi, 1, &rxwi1);
-	rt2x00_desc_read(rxwi, 2, &rxwi2);
-	rt2x00_desc_read(rxwi, 3, &rxwi3);
+	rt2x00_desc_read(rxd, 3, &word);
 
-	if (rt2x00_get_field32(rxd3, RXD_W3_CRC_ERROR))
+	if (rt2x00_get_field32(word, RXD_W3_CRC_ERROR))
 		rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC;
 
-	if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) {
-		/*
-		 * Unfortunately we don't know the cipher type used during
-		 * decryption. This prevents us from correct providing
-		 * correct statistics through debugfs.
-		 */
-		rxdesc->cipher = rt2x00_get_field32(rxwi0, RXWI_W0_UDF);
-		rxdesc->cipher_status =
-		    rt2x00_get_field32(rxd3, RXD_W3_CIPHER_ERROR);
-	}
+	/*
+	 * Unfortunately we don't know the cipher type used during
+	 * decryption. This prevents us from correct providing
+	 * correct statistics through debugfs.
+	 */
+	rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W3_CIPHER_ERROR);
 
-	if (rt2x00_get_field32(rxd3, RXD_W3_DECRYPTED)) {
+	if (rt2x00_get_field32(word, RXD_W3_DECRYPTED)) {
 		/*
 		 * Hardware has stripped IV/EIV data from 802.11 frame during
 		 * decryption. Unfortunately the descriptor doesn't contain
@@ -866,47 +797,22 @@
 			rxdesc->flags |= RX_FLAG_MMIC_ERROR;
 	}
 
-	if (rt2x00_get_field32(rxd3, RXD_W3_MY_BSS))
+	if (rt2x00_get_field32(word, RXD_W3_MY_BSS))
 		rxdesc->dev_flags |= RXDONE_MY_BSS;
 
-	if (rt2x00_get_field32(rxd3, RXD_W3_L2PAD))
+	if (rt2x00_get_field32(word, RXD_W3_L2PAD))
 		rxdesc->dev_flags |= RXDONE_L2PAD;
 
-	if (rt2x00_get_field32(rxwi1, RXWI_W1_SHORT_GI))
-		rxdesc->flags |= RX_FLAG_SHORT_GI;
-
-	if (rt2x00_get_field32(rxwi1, RXWI_W1_BW))
-		rxdesc->flags |= RX_FLAG_40MHZ;
-
 	/*
-	 * Detect RX rate, always use MCS as signal type.
+	 * Process the RXWI structure that is at the start of the buffer.
 	 */
-	rxdesc->dev_flags |= RXDONE_SIGNAL_MCS;
-	rxdesc->rate_mode = rt2x00_get_field32(rxwi1, RXWI_W1_PHYMODE);
-	rxdesc->signal = rt2x00_get_field32(rxwi1, RXWI_W1_MCS);
-
-	/*
-	 * Mask of 0x8 bit to remove the short preamble flag.
-	 */
-	if (rxdesc->rate_mode == RATE_MODE_CCK)
-		rxdesc->signal &= ~0x8;
-
-	rxdesc->rssi =
-	    (rt2x00_get_field32(rxwi2, RXWI_W2_RSSI0) +
-	     rt2x00_get_field32(rxwi2, RXWI_W2_RSSI1)) / 2;
-
-	rxdesc->size = rt2x00_get_field32(rxwi0, RXWI_W0_MPDU_TOTAL_BYTE_COUNT);
+	rt2800_process_rxwi(entry->skb, rxdesc);
 
 	/*
 	 * Set RX IDX in register to inform hardware that we have handled
 	 * this entry and it is available for reuse again.
 	 */
 	rt2800_register_write(rt2x00dev, RX_CRX_IDX, entry->entry_idx);
-
-	/*
-	 * Remove TXWI descriptor from start of buffer.
-	 */
-	skb_pull(entry->skb, RXWI_DESC_SIZE);
 }
 
 /*
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index e3f3a97..8ad0669 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -401,59 +401,15 @@
 {
 	struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
 	__le32 *txi = skbdesc->desc;
-	__le32 *txwi = &txi[TXINFO_DESC_SIZE / sizeof(__le32)];
 	u32 word;
 
 	/*
-	 * Initialize TX Info descriptor
+	 * Initialize TXWI descriptor
 	 */
-	rt2x00_desc_read(txwi, 0, &word);
-	rt2x00_set_field32(&word, TXWI_W0_FRAG,
-			   test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags));
-	rt2x00_set_field32(&word, TXWI_W0_MIMO_PS, 0);
-	rt2x00_set_field32(&word, TXWI_W0_CF_ACK, 0);
-	rt2x00_set_field32(&word, TXWI_W0_TS,
-			   test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags));
-	rt2x00_set_field32(&word, TXWI_W0_AMPDU,
-			   test_bit(ENTRY_TXD_HT_AMPDU, &txdesc->flags));
-	rt2x00_set_field32(&word, TXWI_W0_MPDU_DENSITY, txdesc->mpdu_density);
-	rt2x00_set_field32(&word, TXWI_W0_TX_OP, txdesc->ifs);
-	rt2x00_set_field32(&word, TXWI_W0_MCS, txdesc->mcs);
-	rt2x00_set_field32(&word, TXWI_W0_BW,
-			   test_bit(ENTRY_TXD_HT_BW_40, &txdesc->flags));
-	rt2x00_set_field32(&word, TXWI_W0_SHORT_GI,
-			   test_bit(ENTRY_TXD_HT_SHORT_GI, &txdesc->flags));
-	rt2x00_set_field32(&word, TXWI_W0_STBC, txdesc->stbc);
-	rt2x00_set_field32(&word, TXWI_W0_PHYMODE, txdesc->rate_mode);
-	rt2x00_desc_write(txwi, 0, word);
-
-	rt2x00_desc_read(txwi, 1, &word);
-	rt2x00_set_field32(&word, TXWI_W1_ACK,
-			   test_bit(ENTRY_TXD_ACK, &txdesc->flags));
-	rt2x00_set_field32(&word, TXWI_W1_NSEQ,
-			   test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags));
-	rt2x00_set_field32(&word, TXWI_W1_BW_WIN_SIZE, txdesc->ba_size);
-	rt2x00_set_field32(&word, TXWI_W1_WIRELESS_CLI_ID,
-			   test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags) ?
-			   txdesc->key_idx : 0xff);
-	rt2x00_set_field32(&word, TXWI_W1_MPDU_TOTAL_BYTE_COUNT,
-			   txdesc->length);
-	rt2x00_set_field32(&word, TXWI_W1_PACKETID,
-			   skbdesc->entry->queue->qid + 1);
-	rt2x00_desc_write(txwi, 1, word);
+	rt2800_write_txwi(skb, txdesc);
 
 	/*
-	 * Always write 0 to IV/EIV fields, hardware will insert the IV
-	 * from the IVEIV register when TXINFO_W0_WIV is set to 0.
-	 * When TXINFO_W0_WIV is set to 1 it will use the IV data
-	 * from the descriptor. The TXWI_W1_WIRELESS_CLI_ID indicates which
-	 * crypto entry in the registers should be used to encrypt the frame.
-	 */
-	_rt2x00_desc_write(txwi, 2, 0 /* skbdesc->iv[0] */);
-	_rt2x00_desc_write(txwi, 3, 0 /* skbdesc->iv[1] */);
-
-	/*
-	 * Initialize TX descriptor
+	 * Initialize TXINFO descriptor
 	 */
 	rt2x00_desc_read(txi, 0, &word);
 	rt2x00_set_field32(&word, TXINFO_W0_USB_DMA_TX_PKT_LEN,
@@ -471,21 +427,14 @@
 /*
  * TX data initialization
  */
-static void rt2800usb_write_beacon(struct queue_entry *entry)
+static void rt2800usb_write_beacon(struct queue_entry *entry,
+				   struct txentry_desc *txdesc)
 {
 	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
-	struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
 	unsigned int beacon_base;
 	u32 reg;
 
 	/*
-	 * Add the descriptor in front of the skb.
-	 */
-	skb_push(entry->skb, entry->queue->desc_size);
-	memcpy(entry->skb->data, skbdesc->desc, skbdesc->desc_len);
-	skbdesc->desc = entry->skb->data;
-
-	/*
 	 * Disable beaconing while we are reloading the beacon data,
 	 * otherwise we might be sending out invalid data.
 	 */
@@ -494,6 +443,12 @@
 	rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
 
 	/*
+	 * Add the TXWI for the beacon to the skb.
+	 */
+	rt2800_write_txwi(entry->skb, txdesc);
+	skb_push(entry->skb, TXWI_DESC_SIZE);
+
+	/*
 	 * Write entire beacon with descriptor to register.
 	 */
 	beacon_base = HW_BEACON_OFFSET(entry->entry_idx);
@@ -503,6 +458,14 @@
 					    REGISTER_TIMEOUT32(entry->skb->len));
 
 	/*
+	 * Enable beaconing again.
+	 */
+	rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 1);
+	rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 1);
+	rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 1);
+	rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+
+	/*
 	 * Clean up the beacon skb.
 	 */
 	dev_kfree_skb(entry->skb);
@@ -524,84 +487,53 @@
 	return length;
 }
 
-static void rt2800usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
-				    const enum data_queue_qid queue)
-{
-	u32 reg;
-
-	if (queue != QID_BEACON) {
-		rt2x00usb_kick_tx_queue(rt2x00dev, queue);
-		return;
-	}
-
-	rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
-	if (!rt2x00_get_field32(reg, BCN_TIME_CFG_BEACON_GEN)) {
-		rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 1);
-		rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 1);
-		rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 1);
-		rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
-	}
-}
-
 /*
  * RX control handlers
  */
 static void rt2800usb_fill_rxdone(struct queue_entry *entry,
 				  struct rxdone_entry_desc *rxdesc)
 {
-	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
 	struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
 	__le32 *rxi = (__le32 *)entry->skb->data;
-	__le32 *rxwi;
 	__le32 *rxd;
-	u32 rxi0;
-	u32 rxwi0;
-	u32 rxwi1;
-	u32 rxwi2;
-	u32 rxwi3;
-	u32 rxd0;
+	u32 word;
 	int rx_pkt_len;
 
 	/*
-	 * RX frame format is :
-	 * | RXINFO | RXWI | header | L2 pad | payload | pad | RXD | USB pad |
-	 *          |<------------ rx_pkt_len -------------->|
-	 */
-	rt2x00_desc_read(rxi, 0, &rxi0);
-	rx_pkt_len = rt2x00_get_field32(rxi0, RXINFO_W0_USB_DMA_RX_PKT_LEN);
-
-	rxwi = (__le32 *)(entry->skb->data + RXINFO_DESC_SIZE);
-
-	/*
-	 * FIXME : we need to check for rx_pkt_len validity
-	 */
-	rxd = (__le32 *)(entry->skb->data + RXINFO_DESC_SIZE + rx_pkt_len);
-
-	/*
 	 * Copy descriptor to the skbdesc->desc buffer, making it safe from
 	 * moving of frame data in rt2x00usb.
 	 */
 	memcpy(skbdesc->desc, rxi, skbdesc->desc_len);
 
 	/*
+	 * RX frame format is :
+	 * | RXINFO | RXWI | header | L2 pad | payload | pad | RXD | USB pad |
+	 *          |<------------ rx_pkt_len -------------->|
+	 */
+	rt2x00_desc_read(rxi, 0, &word);
+	rx_pkt_len = rt2x00_get_field32(word, RXINFO_W0_USB_DMA_RX_PKT_LEN);
+
+	/*
+	 * Remove the RXINFO structure from the sbk.
+	 */
+	skb_pull(entry->skb, RXINFO_DESC_SIZE);
+
+	/*
+	 * FIXME: we need to check for rx_pkt_len validity
+	 */
+	rxd = (__le32 *)(entry->skb->data + rx_pkt_len);
+
+	/*
 	 * It is now safe to read the descriptor on all architectures.
 	 */
-	rt2x00_desc_read(rxwi, 0, &rxwi0);
-	rt2x00_desc_read(rxwi, 1, &rxwi1);
-	rt2x00_desc_read(rxwi, 2, &rxwi2);
-	rt2x00_desc_read(rxwi, 3, &rxwi3);
-	rt2x00_desc_read(rxd, 0, &rxd0);
+	rt2x00_desc_read(rxd, 0, &word);
 
-	if (rt2x00_get_field32(rxd0, RXD_W0_CRC_ERROR))
+	if (rt2x00_get_field32(word, RXD_W0_CRC_ERROR))
 		rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC;
 
-	if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) {
-		rxdesc->cipher = rt2x00_get_field32(rxwi0, RXWI_W0_UDF);
-		rxdesc->cipher_status =
-		    rt2x00_get_field32(rxd0, RXD_W0_CIPHER_ERROR);
-	}
+	rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W0_CIPHER_ERROR);
 
-	if (rt2x00_get_field32(rxd0, RXD_W0_DECRYPTED)) {
+	if (rt2x00_get_field32(word, RXD_W0_DECRYPTED)) {
 		/*
 		 * Hardware has stripped IV/EIV data from 802.11 frame during
 		 * decryption. Unfortunately the descriptor doesn't contain
@@ -616,41 +548,21 @@
 			rxdesc->flags |= RX_FLAG_MMIC_ERROR;
 	}
 
-	if (rt2x00_get_field32(rxd0, RXD_W0_MY_BSS))
+	if (rt2x00_get_field32(word, RXD_W0_MY_BSS))
 		rxdesc->dev_flags |= RXDONE_MY_BSS;
 
-	if (rt2x00_get_field32(rxd0, RXD_W0_L2PAD))
+	if (rt2x00_get_field32(word, RXD_W0_L2PAD))
 		rxdesc->dev_flags |= RXDONE_L2PAD;
 
-	if (rt2x00_get_field32(rxwi1, RXWI_W1_SHORT_GI))
-		rxdesc->flags |= RX_FLAG_SHORT_GI;
-
-	if (rt2x00_get_field32(rxwi1, RXWI_W1_BW))
-		rxdesc->flags |= RX_FLAG_40MHZ;
+	/*
+	 * Remove RXD descriptor from end of buffer.
+	 */
+	skb_trim(entry->skb, rx_pkt_len);
 
 	/*
-	 * Detect RX rate, always use MCS as signal type.
+	 * Process the RXWI structure.
 	 */
-	rxdesc->dev_flags |= RXDONE_SIGNAL_MCS;
-	rxdesc->rate_mode = rt2x00_get_field32(rxwi1, RXWI_W1_PHYMODE);
-	rxdesc->signal = rt2x00_get_field32(rxwi1, RXWI_W1_MCS);
-
-	/*
-	 * Mask of 0x8 bit to remove the short preamble flag.
-	 */
-	if (rxdesc->rate_mode == RATE_MODE_CCK)
-		rxdesc->signal &= ~0x8;
-
-	rxdesc->rssi =
-	    (rt2x00_get_field32(rxwi2, RXWI_W2_RSSI0) +
-	     rt2x00_get_field32(rxwi2, RXWI_W2_RSSI1)) / 2;
-
-	rxdesc->size = rt2x00_get_field32(rxwi0, RXWI_W0_MPDU_TOTAL_BYTE_COUNT);
-
-	/*
-	 * Remove RXWI descriptor from start of buffer.
-	 */
-	skb_pull(entry->skb, skbdesc->desc_len);
+	rt2800_process_rxwi(entry->skb, rxdesc);
 }
 
 /*
@@ -743,7 +655,7 @@
 	.write_tx_data		= rt2x00usb_write_tx_data,
 	.write_beacon		= rt2800usb_write_beacon,
 	.get_tx_data_len	= rt2800usb_get_tx_data_len,
-	.kick_tx_queue		= rt2800usb_kick_tx_queue,
+	.kick_tx_queue		= rt2x00usb_kick_tx_queue,
 	.kill_tx_queue		= rt2x00usb_kill_tx_queue,
 	.fill_rxdone		= rt2800usb_fill_rxdone,
 	.config_shared_key	= rt2800_config_shared_key,
@@ -841,7 +753,7 @@
 	{ USB_DEVICE(0x7392, 0x7717), USB_DEVICE_DATA(&rt2800usb_ops) },
 	{ USB_DEVICE(0x7392, 0x7718), USB_DEVICE_DATA(&rt2800usb_ops) },
 	/* EnGenius */
-	{ USB_DEVICE(0X1740, 0x9701), USB_DEVICE_DATA(&rt2800usb_ops) },
+	{ USB_DEVICE(0x1740, 0x9701), USB_DEVICE_DATA(&rt2800usb_ops) },
 	{ USB_DEVICE(0x1740, 0x9702), USB_DEVICE_DATA(&rt2800usb_ops) },
 	/* Gigabyte */
 	{ USB_DEVICE(0x1044, 0x800b), USB_DEVICE_DATA(&rt2800usb_ops) },
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.h b/drivers/net/wireless/rt2x00/rt2800usb.h
index d1d8ae9..2bca6a7 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.h
+++ b/drivers/net/wireless/rt2x00/rt2800usb.h
@@ -79,8 +79,6 @@
  */
 #define TXINFO_DESC_SIZE		( 1 * sizeof(__le32) )
 #define RXINFO_DESC_SIZE		( 1 * sizeof(__le32) )
-#define RXWI_DESC_SIZE			( 4 * sizeof(__le32) )
-#define RXD_DESC_SIZE			( 1 * sizeof(__le32) )
 
 /*
  * TX Info structure
@@ -113,44 +111,6 @@
 #define RXINFO_W0_USB_DMA_RX_PKT_LEN	FIELD32(0x0000ffff)
 
 /*
- * RX WI structure
- */
-
-/*
- * Word0
- */
-#define RXWI_W0_WIRELESS_CLI_ID		FIELD32(0x000000ff)
-#define RXWI_W0_KEY_INDEX		FIELD32(0x00000300)
-#define RXWI_W0_BSSID			FIELD32(0x00001c00)
-#define RXWI_W0_UDF			FIELD32(0x0000e000)
-#define RXWI_W0_MPDU_TOTAL_BYTE_COUNT	FIELD32(0x0fff0000)
-#define RXWI_W0_TID			FIELD32(0xf0000000)
-
-/*
- * Word1
- */
-#define RXWI_W1_FRAG			FIELD32(0x0000000f)
-#define RXWI_W1_SEQUENCE		FIELD32(0x0000fff0)
-#define RXWI_W1_MCS			FIELD32(0x007f0000)
-#define RXWI_W1_BW			FIELD32(0x00800000)
-#define RXWI_W1_SHORT_GI		FIELD32(0x01000000)
-#define RXWI_W1_STBC			FIELD32(0x06000000)
-#define RXWI_W1_PHYMODE			FIELD32(0xc0000000)
-
-/*
- * Word2
- */
-#define RXWI_W2_RSSI0			FIELD32(0x000000ff)
-#define RXWI_W2_RSSI1			FIELD32(0x0000ff00)
-#define RXWI_W2_RSSI2			FIELD32(0x00ff0000)
-
-/*
- * Word3
- */
-#define RXWI_W3_SNR0			FIELD32(0x000000ff)
-#define RXWI_W3_SNR1			FIELD32(0x0000ff00)
-
-/*
  * RX descriptor format for RX Ring.
  */
 
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 4f9b666..6c1ff4c 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -551,7 +551,8 @@
 			       struct txentry_desc *txdesc);
 	int (*write_tx_data) (struct queue_entry *entry,
 			      struct txentry_desc *txdesc);
-	void (*write_beacon) (struct queue_entry *entry);
+	void (*write_beacon) (struct queue_entry *entry,
+			      struct txentry_desc *txdesc);
 	int (*get_tx_data_len) (struct queue_entry *entry);
 	void (*kick_tx_queue) (struct rt2x00_dev *rt2x00dev,
 			       const enum data_queue_qid queue);
diff --git a/drivers/net/wireless/rt2x00/rt2x00crypto.c b/drivers/net/wireless/rt2x00/rt2x00crypto.c
index d291c78..583dacd 100644
--- a/drivers/net/wireless/rt2x00/rt2x00crypto.c
+++ b/drivers/net/wireless/rt2x00/rt2x00crypto.c
@@ -128,6 +128,7 @@
 
 	/* Pull buffer to correct size */
 	skb_pull(skb, txdesc->iv_len);
+	txdesc->length -= txdesc->iv_len;
 
 	/* IV/EIV data has officially been stripped */
 	skbdesc->flags |= SKBDESC_IV_STRIPPED;
diff --git a/drivers/net/wireless/rt2x00/rt2x00ht.c b/drivers/net/wireless/rt2x00/rt2x00ht.c
index 1056c92..5a40760 100644
--- a/drivers/net/wireless/rt2x00/rt2x00ht.c
+++ b/drivers/net/wireless/rt2x00/rt2x00ht.c
@@ -35,6 +35,7 @@
 {
 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb);
 	struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0];
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)entry->skb->data;
 
 	if (tx_info->control.sta)
 		txdesc->mpdu_density =
@@ -66,4 +67,20 @@
 		__set_bit(ENTRY_TXD_HT_BW_40, &txdesc->flags);
 	if (txrate->flags & IEEE80211_TX_RC_SHORT_GI)
 		__set_bit(ENTRY_TXD_HT_SHORT_GI, &txdesc->flags);
+
+	/*
+	 * Determine IFS values
+	 * - Use TXOP_BACKOFF for management frames
+	 * - Use TXOP_SIFS for fragment bursts
+	 * - Use TXOP_HTTXOP for everything else
+	 *
+	 * Note: rt2800 devices won't use CTS protection (if used)
+	 * for frames not transmitted with TXOP_HTTXOP
+	 */
+	if (ieee80211_is_mgmt(hdr->frame_control))
+		txdesc->txop = TXOP_BACKOFF;
+	else if (!(tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT))
+		txdesc->txop = TXOP_SIFS;
+	else
+		txdesc->txop = TXOP_HTTXOP;
 }
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index e22029f..089a12c 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -429,20 +429,23 @@
 	 * it is now ready to be dumped to userspace through debugfs.
 	 */
 	rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_TX, entry->skb);
+}
+
+static void rt2x00queue_kick_tx_queue(struct queue_entry *entry,
+				      struct txentry_desc *txdesc)
+{
+	struct data_queue *queue = entry->queue;
+	struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
 
 	/*
 	 * Check if we need to kick the queue, there are however a few rules
-	 *	1) Don't kick beacon queue
-	 *	2) Don't kick unless this is the last in frame in a burst.
+	 *	1) Don't kick unless this is the last in frame in a burst.
 	 *	   When the burst flag is set, this frame is always followed
 	 *	   by another frame which in some way are related to eachother.
 	 *	   This is true for fragments, RTS or CTS-to-self frames.
-	 *	3) Rule 2 can be broken when the available entries
+	 *	2) Rule 1 can be broken when the available entries
 	 *	   in the queue are less then a certain threshold.
 	 */
-	if (entry->queue->qid == QID_BEACON)
-		return;
-
 	if (rt2x00queue_threshold(queue) ||
 	    !test_bit(ENTRY_TXD_BURST, &txdesc->flags))
 		rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, queue->qid);
@@ -538,6 +541,7 @@
 
 	rt2x00queue_index_inc(queue, Q_INDEX);
 	rt2x00queue_write_tx_descriptor(entry, &txdesc);
+	rt2x00queue_kick_tx_queue(entry, &txdesc);
 
 	return 0;
 }
@@ -603,12 +607,9 @@
 	rt2x00queue_write_tx_descriptor(intf->beacon, &txdesc);
 
 	/*
-	 * Send beacon to hardware.
-	 * Also enable beacon generation, which might have been disabled
-	 * by the driver during the config_beacon() callback function.
+	 * Send beacon to hardware and enable beacon genaration..
 	 */
-	rt2x00dev->ops->lib->write_beacon(intf->beacon);
-	rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, QID_BEACON);
+	rt2x00dev->ops->lib->write_beacon(intf->beacon, &txdesc);
 
 	mutex_unlock(&intf->beacon_skb_mutex);
 
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h
index 94a48c1..36a957a 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.h
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.h
@@ -299,6 +299,7 @@
  * @retry_limit: Max number of retries.
  * @aifs: AIFS value.
  * @ifs: IFS value.
+ * @txop: IFS value for 11n capable chips.
  * @cw_min: cwmin value.
  * @cw_max: cwmax value.
  * @cipher: Cipher type used for encryption.
@@ -328,6 +329,7 @@
 	short retry_limit;
 	short aifs;
 	short ifs;
+	short txop;
 	short cw_min;
 	short cw_max;
 
diff --git a/drivers/net/wireless/rt2x00/rt2x00reg.h b/drivers/net/wireless/rt2x00/rt2x00reg.h
index 603bfc0..b9fe948 100644
--- a/drivers/net/wireless/rt2x00/rt2x00reg.h
+++ b/drivers/net/wireless/rt2x00/rt2x00reg.h
@@ -101,6 +101,16 @@
 };
 
 /*
+ * IFS backoff values for HT devices
+ */
+enum txop {
+	TXOP_HTTXOP = 0,
+	TXOP_PIFS = 1,
+	TXOP_SIFS = 2,
+	TXOP_BACKOFF = 3,
+};
+
+/*
  * Cipher types for hardware encryption
  */
 enum cipher {
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c
index 26ee791..86c75b9 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/rt2x00/rt61pci.c
@@ -1843,7 +1843,8 @@
 /*
  * TX data initialization
  */
-static void rt61pci_write_beacon(struct queue_entry *entry)
+static void rt61pci_write_beacon(struct queue_entry *entry,
+				 struct txentry_desc *txdesc)
 {
 	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
 	struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
@@ -1870,6 +1871,19 @@
 				      entry->skb->data, entry->skb->len);
 
 	/*
+	 * Enable beaconing again.
+	 *
+	 * For Wi-Fi faily generated beacons between participating
+	 * stations. Set TBTT phase adaptive adjustment step to 8us.
+	 */
+	rt2x00pci_register_write(rt2x00dev, TXRX_CSR10, 0x00001008);
+
+	rt2x00_set_field32(&reg, TXRX_CSR9_TSF_TICKING, 1);
+	rt2x00_set_field32(&reg, TXRX_CSR9_TBTT_ENABLE, 1);
+	rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_GEN, 1);
+	rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg);
+
+	/*
 	 * Clean up beacon skb.
 	 */
 	dev_kfree_skb_any(entry->skb);
@@ -1881,23 +1895,6 @@
 {
 	u32 reg;
 
-	if (queue == QID_BEACON) {
-		/*
-		 * For Wi-Fi faily generated beacons between participating
-		 * stations. Set TBTT phase adaptive adjustment step to 8us.
-		 */
-		rt2x00pci_register_write(rt2x00dev, TXRX_CSR10, 0x00001008);
-
-		rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, &reg);
-		if (!rt2x00_get_field32(reg, TXRX_CSR9_BEACON_GEN)) {
-			rt2x00_set_field32(&reg, TXRX_CSR9_TSF_TICKING, 1);
-			rt2x00_set_field32(&reg, TXRX_CSR9_TBTT_ENABLE, 1);
-			rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_GEN, 1);
-			rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg);
-		}
-		return;
-	}
-
 	rt2x00pci_register_read(rt2x00dev, TX_CNTL_CSR, &reg);
 	rt2x00_set_field32(&reg, TX_CNTL_CSR_KICK_TX_AC0, (queue == QID_AC_BE));
 	rt2x00_set_field32(&reg, TX_CNTL_CSR_KICK_TX_AC1, (queue == QID_AC_BK));
@@ -1969,12 +1966,8 @@
 	if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR))
 		rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC;
 
-	if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) {
-		rxdesc->cipher =
-		    rt2x00_get_field32(word0, RXD_W0_CIPHER_ALG);
-		rxdesc->cipher_status =
-		    rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR);
-	}
+	rxdesc->cipher = rt2x00_get_field32(word0, RXD_W0_CIPHER_ALG);
+	rxdesc->cipher_status = rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR);
 
 	if (rxdesc->cipher != CIPHER_NONE) {
 		_rt2x00_desc_read(entry_priv->desc, 2, &rxdesc->iv[0]);
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index 39b3c6d..11c1307 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -1505,7 +1505,8 @@
 /*
  * TX data initialization
  */
-static void rt73usb_write_beacon(struct queue_entry *entry)
+static void rt73usb_write_beacon(struct queue_entry *entry,
+				 struct txentry_desc *txdesc)
 {
 	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
 	struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
@@ -1537,6 +1538,19 @@
 					    REGISTER_TIMEOUT32(entry->skb->len));
 
 	/*
+	 * Enable beaconing again.
+	 *
+	 * For Wi-Fi faily generated beacons between participating stations.
+	 * Set TBTT phase adaptive adjustment step to 8us (default 16us)
+	 */
+	rt2x00usb_register_write(rt2x00dev, TXRX_CSR10, 0x00001008);
+
+	rt2x00_set_field32(&reg, TXRX_CSR9_TSF_TICKING, 1);
+	rt2x00_set_field32(&reg, TXRX_CSR9_TBTT_ENABLE, 1);
+	rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_GEN, 1);
+	rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg);
+
+	/*
 	 * Clean up the beacon skb.
 	 */
 	dev_kfree_skb(entry->skb);
@@ -1557,31 +1571,6 @@
 	return length;
 }
 
-static void rt73usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
-				  const enum data_queue_qid queue)
-{
-	u32 reg;
-
-	if (queue != QID_BEACON) {
-		rt2x00usb_kick_tx_queue(rt2x00dev, queue);
-		return;
-	}
-
-	/*
-	 * For Wi-Fi faily generated beacons between participating stations.
-	 * Set TBTT phase adaptive adjustment step to 8us (default 16us)
-	 */
-	rt2x00usb_register_write(rt2x00dev, TXRX_CSR10, 0x00001008);
-
-	rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &reg);
-	if (!rt2x00_get_field32(reg, TXRX_CSR9_BEACON_GEN)) {
-		rt2x00_set_field32(&reg, TXRX_CSR9_TSF_TICKING, 1);
-		rt2x00_set_field32(&reg, TXRX_CSR9_TBTT_ENABLE, 1);
-		rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_GEN, 1);
-		rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg);
-	}
-}
-
 /*
  * RX control handlers
  */
@@ -1645,12 +1634,8 @@
 	if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR))
 		rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC;
 
-	if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) {
-		rxdesc->cipher =
-		    rt2x00_get_field32(word0, RXD_W0_CIPHER_ALG);
-		rxdesc->cipher_status =
-		    rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR);
-	}
+	rxdesc->cipher = rt2x00_get_field32(word0, RXD_W0_CIPHER_ALG);
+	rxdesc->cipher_status = rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR);
 
 	if (rxdesc->cipher != CIPHER_NONE) {
 		_rt2x00_desc_read(rxd, 2, &rxdesc->iv[0]);
@@ -2266,7 +2251,7 @@
 	.write_tx_data		= rt2x00usb_write_tx_data,
 	.write_beacon		= rt73usb_write_beacon,
 	.get_tx_data_len	= rt73usb_get_tx_data_len,
-	.kick_tx_queue		= rt73usb_kick_tx_queue,
+	.kick_tx_queue		= rt2x00usb_kick_tx_queue,
 	.kill_tx_queue		= rt2x00usb_kill_tx_queue,
 	.fill_rxdone		= rt73usb_fill_rxdone,
 	.config_shared_key	= rt73usb_config_shared_key,
diff --git a/drivers/net/wireless/rtl818x/rtl8180.h b/drivers/net/wireless/rtl818x/rtl8180.h
index de3844f..4baf0cf 100644
--- a/drivers/net/wireless/rtl818x/rtl8180.h
+++ b/drivers/net/wireless/rtl818x/rtl8180.h
@@ -55,6 +55,14 @@
 	struct sk_buff_head queue;
 };
 
+struct rtl8180_vif {
+	struct ieee80211_hw *dev;
+
+	/* beaconing */
+	struct delayed_work beacon_work;
+	bool enable_beacon;
+};
+
 struct rtl8180_priv {
 	/* common between rtl818x drivers */
 	struct rtl818x_csr __iomem *map;
@@ -78,6 +86,9 @@
 	u32 anaparam;
 	u16 rfparam;
 	u8 csthreshold;
+
+	/* sequence # */
+	u16 seqno;
 };
 
 void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data);
diff --git a/drivers/net/wireless/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c
index 21307f2..515817d 100644
--- a/drivers/net/wireless/rtl818x/rtl8180_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c
@@ -234,6 +234,7 @@
 static int rtl8180_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
 {
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	struct rtl8180_priv *priv = dev->priv;
 	struct rtl8180_tx_ring *ring;
 	struct rtl8180_tx_desc *entry;
@@ -285,6 +286,14 @@
 	}
 
 	spin_lock_irqsave(&priv->lock, flags);
+
+	if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+		if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
+			priv->seqno += 0x10;
+		hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
+		hdr->seq_ctrl |= cpu_to_le16(priv->seqno);
+	}
+
 	idx = (ring->idx + skb_queue_len(&ring->queue)) % ring->entries;
 	entry = &ring->desc[idx];
 
@@ -299,6 +308,7 @@
 	__skb_queue_tail(&ring->queue, skb);
 	if (ring->entries - skb_queue_len(&ring->queue) < 2)
 		ieee80211_stop_queue(dev, prio);
+
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, (1 << (prio + 4)));
@@ -653,10 +663,59 @@
 		rtl8180_free_tx_ring(dev, i);
 }
 
+static u64 rtl8180_get_tsf(struct ieee80211_hw *dev)
+{
+	struct rtl8180_priv *priv = dev->priv;
+
+	return rtl818x_ioread32(priv, &priv->map->TSFT[0]) |
+	       (u64)(rtl818x_ioread32(priv, &priv->map->TSFT[1])) << 32;
+}
+
+void rtl8180_beacon_work(struct work_struct *work)
+{
+	struct rtl8180_vif *vif_priv =
+		container_of(work, struct rtl8180_vif, beacon_work.work);
+	struct ieee80211_vif *vif =
+		container_of((void *)vif_priv, struct ieee80211_vif, drv_priv);
+	struct ieee80211_hw *dev = vif_priv->dev;
+	struct ieee80211_mgmt *mgmt;
+	struct sk_buff *skb;
+	int err = 0;
+
+	/* don't overflow the tx ring */
+	if (ieee80211_queue_stopped(dev, 0))
+		goto resched;
+
+	/* grab a fresh beacon */
+	skb = ieee80211_beacon_get(dev, vif);
+
+	/*
+	 * update beacon timestamp w/ TSF value
+	 * TODO: make hardware update beacon timestamp
+	 */
+	mgmt = (struct ieee80211_mgmt *)skb->data;
+	mgmt->u.beacon.timestamp = cpu_to_le64(rtl8180_get_tsf(dev));
+
+	/* TODO: use actual beacon queue */
+	skb_set_queue_mapping(skb, 0);
+
+	err = rtl8180_tx(dev, skb);
+	WARN_ON(err);
+
+resched:
+	/*
+	 * schedule next beacon
+	 * TODO: use hardware support for beacon timing
+	 */
+	schedule_delayed_work(&vif_priv->beacon_work,
+			usecs_to_jiffies(1024 * vif->bss_conf.beacon_int));
+}
+
 static int rtl8180_add_interface(struct ieee80211_hw *dev,
 				 struct ieee80211_vif *vif)
 {
 	struct rtl8180_priv *priv = dev->priv;
+	struct rtl8180_vif *vif_priv;
 
 	/*
 	 * We only support one active interface at a time.
@@ -666,6 +725,7 @@
 
 	switch (vif->type) {
 	case NL80211_IFTYPE_STATION:
+	case NL80211_IFTYPE_ADHOC:
 		break;
 	default:
 		return -EOPNOTSUPP;
@@ -673,6 +733,12 @@
 
 	priv->vif = vif;
 
+	/* Initialize driver private area */
+	vif_priv = (struct rtl8180_vif *)&vif->drv_priv;
+	vif_priv->dev = dev;
+	INIT_DELAYED_WORK(&vif_priv->beacon_work, rtl8180_beacon_work);
+	vif_priv->enable_beacon = false;
+
 	rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
 	rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->MAC[0],
 			  le32_to_cpu(*(__le32 *)vif->addr));
@@ -706,8 +772,11 @@
 				     u32 changed)
 {
 	struct rtl8180_priv *priv = dev->priv;
+	struct rtl8180_vif *vif_priv;
 	int i;
 
+	vif_priv = (struct rtl8180_vif *)&vif->drv_priv;
+
 	if (changed & BSS_CHANGED_BSSID) {
 		for (i = 0; i < ETH_ALEN; i++)
 			rtl818x_iowrite8(priv, &priv->map->BSSID[i],
@@ -722,7 +791,16 @@
 	}
 
 	if (changed & BSS_CHANGED_ERP_SLOT && priv->rf->conf_erp)
-	        priv->rf->conf_erp(dev, info);
+		priv->rf->conf_erp(dev, info);
+
+	if (changed & BSS_CHANGED_BEACON_ENABLED)
+		vif_priv->enable_beacon = info->enable_beacon;
+
+	if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON)) {
+		cancel_delayed_work_sync(&vif_priv->beacon_work);
+		if (vif_priv->enable_beacon)
+			schedule_work(&vif_priv->beacon_work.work);
+	}
 }
 
 static u64 rtl8180_prepare_multicast(struct ieee80211_hw *dev,
@@ -763,14 +841,6 @@
 	rtl818x_iowrite32(priv, &priv->map->RX_CONF, priv->rx_conf);
 }
 
-static u64 rtl8180_get_tsf(struct ieee80211_hw *dev)
-{
-	struct rtl8180_priv *priv = dev->priv;
-
-	return rtl818x_ioread32(priv, &priv->map->TSFT[0]) |
-	       (u64)(rtl818x_ioread32(priv, &priv->map->TSFT[1])) << 32;
-}
-
 static const struct ieee80211_ops rtl8180_ops = {
 	.tx			= rtl8180_tx,
 	.start			= rtl8180_start,
@@ -857,8 +927,8 @@
 		goto err_free_reg;
 	}
 
-	if ((err = pci_set_dma_mask(pdev, 0xFFFFFF00ULL)) ||
-	    (err = pci_set_consistent_dma_mask(pdev, 0xFFFFFF00ULL))) {
+	if ((err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) ||
+	    (err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)))) {
 		printk(KERN_ERR "%s (rtl8180): No suitable DMA available\n",
 		       pci_name(pdev));
 		goto err_free_reg;
@@ -907,7 +977,9 @@
 	dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
 		     IEEE80211_HW_RX_INCLUDES_FCS |
 		     IEEE80211_HW_SIGNAL_UNSPEC;
-	dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+	dev->vif_data_size = sizeof(struct rtl8180_vif);
+	dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+					BIT(NL80211_IFTYPE_ADHOC);
 	dev->queues = 1;
 	dev->max_signal = 65;
 
diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index 75887e7..6f1b6b5 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -388,6 +388,8 @@
 	size_t fw_len;
 	struct wl1271_nvs_file *nvs;
 
+	s8 hw_pg_ver;
+
 	u8 bssid[ETH_ALEN];
 	u8 mac_addr[ETH_ALEN];
 	u8 bss_type;
@@ -479,7 +481,7 @@
 	struct wl1271_stats stats;
 	struct wl1271_debugfs debugfs;
 
-	u32 buffer_32;
+	__le32 buffer_32;
 	u32 buffer_cmd;
 	u32 buffer_busyword[WL1271_BUSY_WORD_CNT];
 
diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c
index acb1d9e..1a36d8a 100644
--- a/drivers/net/wireless/wl12xx/wl1271_boot.c
+++ b/drivers/net/wireless/wl12xx/wl1271_boot.c
@@ -441,11 +441,23 @@
 	return 0;
 }
 
+static void wl1271_boot_hw_version(struct wl1271 *wl)
+{
+	u32 fuse;
+
+	fuse = wl1271_top_reg_read(wl, REG_FUSE_DATA_2_1);
+	fuse = (fuse & PG_VER_MASK) >> PG_VER_OFFSET;
+
+	wl->hw_pg_ver = (s8)fuse;
+}
+
 int wl1271_boot(struct wl1271 *wl)
 {
 	int ret = 0;
 	u32 tmp, clk, pause;
 
+	wl1271_boot_hw_version(wl);
+
 	if (REF_CLOCK == 0 || REF_CLOCK == 2 || REF_CLOCK == 4)
 		/* ref clk: 19.2/38.4/38.4-XTAL */
 		clk = 0x3;
diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.h b/drivers/net/wireless/wl12xx/wl1271_boot.h
index 95ecc52..f829699 100644
--- a/drivers/net/wireless/wl12xx/wl1271_boot.h
+++ b/drivers/net/wireless/wl12xx/wl1271_boot.h
@@ -55,6 +55,9 @@
 #define OCP_REG_CLK_POLARITY 0x0cb2
 #define OCP_REG_CLK_PULL     0x0cb4
 
+#define REG_FUSE_DATA_2_1    0x050a
+#define PG_VER_MASK          0x3c
+#define PG_VER_OFFSET        2
 
 #define CMD_MBOX_ADDRESS     0x407B4
 
diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c
index 62c11af..19393e2 100644
--- a/drivers/net/wireless/wl12xx/wl1271_cmd.c
+++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c
@@ -516,7 +516,7 @@
 	ps_params->ps_mode = ps_mode;
 	ps_params->send_null_data = send;
 	ps_params->retries = 5;
-	ps_params->hang_over_period = 128;
+	ps_params->hang_over_period = 1;
 	ps_params->null_data_rate = cpu_to_le32(1); /* 1 Mbps */
 
 	ret = wl1271_cmd_send(wl, CMD_SET_PS_MODE, ps_params,
diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.h b/drivers/net/wireless/wl12xx/wl1271_cmd.h
index 00f78b7..f2820b4 100644
--- a/drivers/net/wireless/wl12xx/wl1271_cmd.h
+++ b/drivers/net/wireless/wl12xx/wl1271_cmd.h
@@ -129,7 +129,7 @@
 /* unit ms */
 #define WL1271_COMMAND_TIMEOUT     2000
 #define WL1271_CMD_TEMPL_MAX_SIZE  252
-#define WL1271_EVENT_TIMEOUT       100
+#define WL1271_EVENT_TIMEOUT       750
 
 struct wl1271_cmd_header {
 	__le16 id;
diff --git a/drivers/net/wireless/wl12xx/wl1271_io.h b/drivers/net/wireless/wl12xx/wl1271_io.h
index d8837ef..bc806c7 100644
--- a/drivers/net/wireless/wl12xx/wl1271_io.h
+++ b/drivers/net/wireless/wl12xx/wl1271_io.h
@@ -74,12 +74,12 @@
 	wl1271_raw_read(wl, addr, &wl->buffer_32,
 			    sizeof(wl->buffer_32), false);
 
-	return wl->buffer_32;
+	return le32_to_cpu(wl->buffer_32);
 }
 
 static inline void wl1271_raw_write32(struct wl1271 *wl, int addr, u32 val)
 {
-	wl->buffer_32 = val;
+	wl->buffer_32 = cpu_to_le32(val);
 	wl1271_raw_write(wl, addr, &wl->buffer_32,
 			     sizeof(wl->buffer_32), false);
 }
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index 62e5440..5bb9e3f 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -1140,10 +1140,25 @@
 	return ret;
 }
 
-static int wl1271_join(struct wl1271 *wl)
+static int wl1271_join(struct wl1271 *wl, bool set_assoc)
 {
 	int ret;
 
+	/*
+	 * One of the side effects of the JOIN command is that is clears
+	 * WPA/WPA2 keys from the chipset. Performing a JOIN while associated
+	 * to a WPA/WPA2 access point will therefore kill the data-path.
+	 * Currently there is no supported scenario for JOIN during
+	 * association - if it becomes a supported scenario, the WPA/WPA2 keys
+	 * must be handled somehow.
+	 *
+	 */
+	if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
+		wl1271_info("JOIN while associated.");
+
+	if (set_assoc)
+		set_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
+
 	ret = wl1271_cmd_join(wl, wl->set_bss_type);
 	if (ret < 0)
 		goto out;
@@ -1190,7 +1205,6 @@
 		goto out;
 
 	clear_bit(WL1271_FLAG_JOINED, &wl->flags);
-	wl->channel = 0;
 	memset(wl->bssid, 0, ETH_ALEN);
 
 	/* stop filterting packets based on bssid */
@@ -1250,7 +1264,9 @@
 		goto out;
 
 	/* if the channel changes while joined, join again */
-	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+	if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
+	    ((wl->band != conf->channel->band) ||
+	     (wl->channel != channel))) {
 		wl->band = conf->channel->band;
 		wl->channel = channel;
 
@@ -1270,7 +1286,7 @@
 				       "failed %d", ret);
 
 		if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
-			ret = wl1271_join(wl);
+			ret = wl1271_join(wl, false);
 			if (ret < 0)
 				wl1271_warning("cmd join to update channel "
 					       "failed %d", ret);
@@ -1647,6 +1663,7 @@
 	enum wl1271_cmd_ps_mode mode;
 	struct wl1271 *wl = hw->priv;
 	bool do_join = false;
+	bool set_assoc = false;
 	int ret;
 
 	wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed");
@@ -1756,7 +1773,7 @@
 		if (bss_conf->assoc) {
 			u32 rates;
 			wl->aid = bss_conf->aid;
-			set_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
+			set_assoc = true;
 
 			/*
 			 * use basic rates from AP, and determine lowest rate
@@ -1856,7 +1873,7 @@
 	}
 
 	if (do_join) {
-		ret = wl1271_join(wl);
+		ret = wl1271_join(wl, set_assoc);
 		if (ret < 0) {
 			wl1271_warning("cmd join failed %d", ret);
 			goto out_sleep;
@@ -2228,6 +2245,29 @@
 		   wl1271_sysfs_show_bt_coex_state,
 		   wl1271_sysfs_store_bt_coex_state);
 
+static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	struct wl1271 *wl = dev_get_drvdata(dev);
+	ssize_t len;
+
+	/* FIXME: what's the maximum length of buf? page size?*/
+	len = 500;
+
+	mutex_lock(&wl->mutex);
+	if (wl->hw_pg_ver >= 0)
+		len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
+	else
+		len = snprintf(buf, len, "n/a\n");
+	mutex_unlock(&wl->mutex);
+
+	return len;
+}
+
+static DEVICE_ATTR(hw_pg_ver, S_IRUGO | S_IWUSR,
+		   wl1271_sysfs_show_hw_pg_ver, NULL);
+
 int wl1271_register_hw(struct wl1271 *wl)
 {
 	int ret;
@@ -2347,6 +2387,7 @@
 	wl->vif = NULL;
 	wl->flags = 0;
 	wl->sg_enabled = true;
+	wl->hw_pg_ver = -1;
 
 	for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
 		wl->tx_frames[i] = NULL;
@@ -2376,8 +2417,18 @@
 		goto err_platform;
 	}
 
+	/* Create sysfs file to get HW PG version */
+	ret = device_create_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
+	if (ret < 0) {
+		wl1271_error("failed to create sysfs file hw_pg_ver");
+		goto err_bt_coex_state;
+	}
+
 	return hw;
 
+err_bt_coex_state:
+	device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
+
 err_platform:
 	platform_device_unregister(wl->plat_dev);
 
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index f8750f9..b7c77f9 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -52,6 +52,8 @@
  *	%NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT,
  *	%NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
  *	and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD.
+ *	However, for setting the channel, see %NL80211_CMD_SET_CHANNEL
+ *	instead, the support here is for backward compatibility only.
  * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request
  *	or rename notification. Has attributes %NL80211_ATTR_WIPHY and
  *	%NL80211_ATTR_WIPHY_NAME.
@@ -329,6 +331,15 @@
  * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This
  *	command is used as an event to indicate the that a trigger level was
  *	reached.
+ * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ
+ *	and %NL80211_ATTR_WIPHY_CHANNEL_TYPE) the given interface (identifed
+ *	by %NL80211_ATTR_IFINDEX) shall operate on.
+ *	In case multiple channels are supported by the device, the mechanism
+ *	with which it switches channels is implementation-defined.
+ *	When a monitor interface is given, it can only switch channel while
+ *	no other interfaces are operating to avoid disturbing the operation
+ *	of any other interfaces, and other interfaces will again take
+ *	precedence when they are used.
  *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
@@ -428,6 +439,8 @@
 	NL80211_CMD_SET_CQM,
 	NL80211_CMD_NOTIFY_CQM,
 
+	NL80211_CMD_SET_CHANNEL,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 7d10c01..b44a2e5 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -966,7 +966,11 @@
  *
  * @set_txq_params: Set TX queue parameters
  *
- * @set_channel: Set channel
+ * @set_channel: Set channel for a given wireless interface. Some devices
+ *	may support multi-channel operation (by channel hopping) so cfg80211
+ *	doesn't verify much. Note, however, that the passed netdev may be
+ *	%NULL as well if the user requested changing the channel for the
+ *	device itself, or for a monitor interface.
  *
  * @scan: Request to do a scan. If returning zero, the scan request is given
  *	the driver, and will be valid until passed to cfg80211_scan_done().
@@ -1095,7 +1099,7 @@
 	int	(*set_txq_params)(struct wiphy *wiphy,
 				  struct ieee80211_txq_params *params);
 
-	int	(*set_channel)(struct wiphy *wiphy,
+	int	(*set_channel)(struct wiphy *wiphy, struct net_device *dev,
 			       struct ieee80211_channel *chan,
 			       enum nl80211_channel_type channel_type);
 
@@ -1461,6 +1465,8 @@
  * @list: (private) Used to collect the interfaces
  * @netdev: (private) Used to reference back to the netdev
  * @current_bss: (private) Used by the internal configuration code
+ * @channel: (private) Used by the internal configuration code to track
+ *	user-set AP, monitor and WDS channels for wireless extensions
  * @bssid: (private) Used by the internal configuration code
  * @ssid: (private) Used by the internal configuration code
  * @ssid_len: (private) Used by the internal configuration code
@@ -1507,6 +1513,7 @@
 	struct cfg80211_internal_bss *authtry_bsses[MAX_AUTH_BSSES];
 	struct cfg80211_internal_bss *auth_bsses[MAX_AUTH_BSSES];
 	struct cfg80211_internal_bss *current_bss; /* associated / joined */
+	struct ieee80211_channel *channel;
 
 	bool ps;
 	int ps_timeout;
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 54aa16b..ac45c5b 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -160,6 +160,8 @@
 	BSS_CHANGED_BEACON_ENABLED	= 1<<9,
 	BSS_CHANGED_CQM			= 1<<10,
 	BSS_CHANGED_IBSS		= 1<<11,
+
+	/* when adding here, make sure to change ieee80211_reconfig */
 };
 
 /**
@@ -189,6 +191,9 @@
  *	the current band.
  * @bssid: The BSSID for this BSS
  * @enable_beacon: whether beaconing should be enabled or not
+ * @channel_type: Channel type for this BSS -- the hardware might be
+ *	configured for HT40+ while this BSS only uses no-HT, for
+ *	example.
  * @ht_operation_mode: HT operation mode (like in &struct ieee80211_ht_info).
  *	This field is only valid when the channel type is one of the HT types.
  * @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value
@@ -213,6 +218,7 @@
 	u16 ht_operation_mode;
 	s32 cqm_rssi_thold;
 	u32 cqm_rssi_hyst;
+	enum nl80211_channel_type channel_type;
 };
 
 /**
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 0442029..84b48ba 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -23,7 +23,8 @@
 	key.o \
 	util.o \
 	wme.o \
-	event.o
+	event.o \
+	chan.o
 
 mac80211-$(CONFIG_MAC80211_LEDS) += led.o
 mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index ae37270..c7000a6 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1162,15 +1162,39 @@
 }
 
 static int ieee80211_set_channel(struct wiphy *wiphy,
+				 struct net_device *netdev,
 				 struct ieee80211_channel *chan,
 				 enum nl80211_channel_type channel_type)
 {
 	struct ieee80211_local *local = wiphy_priv(wiphy);
+	struct ieee80211_sub_if_data *sdata = NULL;
+
+	if (netdev)
+		sdata = IEEE80211_DEV_TO_SUB_IF(netdev);
+
+	switch (ieee80211_get_channel_mode(local, NULL)) {
+	case CHAN_MODE_HOPPING:
+		return -EBUSY;
+	case CHAN_MODE_FIXED:
+		if (local->oper_channel != chan)
+			return -EBUSY;
+		if (!sdata && local->_oper_channel_type == channel_type)
+			return 0;
+		break;
+	case CHAN_MODE_UNDEFINED:
+		break;
+	}
 
 	local->oper_channel = chan;
-	local->oper_channel_type = channel_type;
 
-	return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+	if (!ieee80211_set_channel_type(local, sdata, channel_type))
+		return -EBUSY;
+
+	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+	if (sdata && sdata->vif.type != NL80211_IFTYPE_MONITOR)
+		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_HT);
+
+	return 0;
 }
 
 #ifdef CONFIG_PM
@@ -1214,6 +1238,20 @@
 static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev,
 			   struct cfg80211_assoc_request *req)
 {
+	struct ieee80211_local *local = wiphy_priv(wiphy);
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+	switch (ieee80211_get_channel_mode(local, sdata)) {
+	case CHAN_MODE_HOPPING:
+		return -EBUSY;
+	case CHAN_MODE_FIXED:
+		if (local->oper_channel == req->bss->channel)
+			break;
+		return -EBUSY;
+	case CHAN_MODE_UNDEFINED:
+		break;
+	}
+
 	return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req);
 }
 
@@ -1236,8 +1274,22 @@
 static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
 			       struct cfg80211_ibss_params *params)
 {
+	struct ieee80211_local *local = wiphy_priv(wiphy);
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
+	switch (ieee80211_get_channel_mode(local, sdata)) {
+	case CHAN_MODE_HOPPING:
+		return -EBUSY;
+	case CHAN_MODE_FIXED:
+		if (!params->channel_fixed)
+			return -EBUSY;
+		if (local->oper_channel == params->channel)
+			break;
+		return -EBUSY;
+	case CHAN_MODE_UNDEFINED:
+		break;
+	}
+
 	return ieee80211_ibss_join(sdata, params);
 }
 
@@ -1366,7 +1418,7 @@
 	 * association, there's no need to send an action frame.
 	 */
 	if (!sdata->u.mgd.associated ||
-	    sdata->local->oper_channel_type == NL80211_CHAN_NO_HT) {
+	    sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) {
 		mutex_lock(&sdata->local->iflist_mtx);
 		ieee80211_recalc_smps(sdata->local, sdata);
 		mutex_unlock(&sdata->local->iflist_mtx);
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
new file mode 100644
index 0000000..5d218c5
--- /dev/null
+++ b/net/mac80211/chan.c
@@ -0,0 +1,127 @@
+/*
+ * mac80211 - channel management
+ */
+
+#include <linux/nl80211.h>
+#include "ieee80211_i.h"
+
+enum ieee80211_chan_mode
+__ieee80211_get_channel_mode(struct ieee80211_local *local,
+			     struct ieee80211_sub_if_data *ignore)
+{
+	struct ieee80211_sub_if_data *sdata;
+
+	WARN_ON(!mutex_is_locked(&local->iflist_mtx));
+
+	list_for_each_entry(sdata, &local->interfaces, list) {
+		if (sdata == ignore)
+			continue;
+
+		if (!ieee80211_sdata_running(sdata))
+			continue;
+
+		if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
+			continue;
+
+		if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+		    !sdata->u.mgd.associated)
+			continue;
+
+		if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+			if (!sdata->u.ibss.ssid_len)
+				continue;
+			if (!sdata->u.ibss.fixed_channel)
+				return CHAN_MODE_HOPPING;
+		}
+
+		if (sdata->vif.type == NL80211_IFTYPE_AP &&
+		    !sdata->u.ap.beacon)
+			continue;
+
+		return CHAN_MODE_FIXED;
+	}
+
+	return CHAN_MODE_UNDEFINED;
+}
+
+enum ieee80211_chan_mode
+ieee80211_get_channel_mode(struct ieee80211_local *local,
+			   struct ieee80211_sub_if_data *ignore)
+{
+	enum ieee80211_chan_mode mode;
+
+	mutex_lock(&local->iflist_mtx);
+	mode = __ieee80211_get_channel_mode(local, ignore);
+	mutex_unlock(&local->iflist_mtx);
+
+	return mode;
+}
+
+bool ieee80211_set_channel_type(struct ieee80211_local *local,
+				struct ieee80211_sub_if_data *sdata,
+				enum nl80211_channel_type chantype)
+{
+	struct ieee80211_sub_if_data *tmp;
+	enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
+	bool result;
+
+	mutex_lock(&local->iflist_mtx);
+
+	list_for_each_entry(tmp, &local->interfaces, list) {
+		if (tmp == sdata)
+			continue;
+
+		if (!ieee80211_sdata_running(tmp))
+			continue;
+
+		switch (tmp->vif.bss_conf.channel_type) {
+		case NL80211_CHAN_NO_HT:
+		case NL80211_CHAN_HT20:
+			superchan = tmp->vif.bss_conf.channel_type;
+			break;
+		case NL80211_CHAN_HT40PLUS:
+			WARN_ON(superchan == NL80211_CHAN_HT40MINUS);
+			superchan = NL80211_CHAN_HT40PLUS;
+			break;
+		case NL80211_CHAN_HT40MINUS:
+			WARN_ON(superchan == NL80211_CHAN_HT40PLUS);
+			superchan = NL80211_CHAN_HT40MINUS;
+			break;
+		}
+	}
+
+	switch (superchan) {
+	case NL80211_CHAN_NO_HT:
+	case NL80211_CHAN_HT20:
+		/*
+		 * allow any change that doesn't go to no-HT
+		 * (if it already is no-HT no change is needed)
+		 */
+		if (chantype == NL80211_CHAN_NO_HT)
+			break;
+		superchan = chantype;
+		break;
+	case NL80211_CHAN_HT40PLUS:
+	case NL80211_CHAN_HT40MINUS:
+		/* allow smaller bandwidth and same */
+		if (chantype == NL80211_CHAN_NO_HT)
+			break;
+		if (chantype == NL80211_CHAN_HT20)
+			break;
+		if (superchan == chantype)
+			break;
+		result = false;
+		goto out;
+	}
+
+	local->_oper_channel_type = superchan;
+
+	if (sdata)
+		sdata->vif.bss_conf.channel_type = chantype;
+
+	result = true;
+ out:
+	mutex_unlock(&local->iflist_mtx);
+
+	return result;
+}
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index b72ee64..b2cc1fd 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -103,7 +103,7 @@
 	sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
 
 	local->oper_channel = chan;
-	local->oper_channel_type = NL80211_CHAN_NO_HT;
+	WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT));
 	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
 
 	sband = local->hw.wiphy->bands[chan->band];
@@ -911,7 +911,8 @@
 	/* fix ourselves to that channel now already */
 	if (params->channel_fixed) {
 		sdata->local->oper_channel = params->channel;
-		sdata->local->oper_channel_type = NL80211_CHAN_NO_HT;
+		WARN_ON(!ieee80211_set_channel_type(sdata->local, sdata,
+						    NL80211_CHAN_NO_HT));
 	}
 
 	if (params->ie) {
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index cbaf4981..7ef7798 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -767,7 +767,7 @@
 	enum mac80211_scan_state next_scan_state;
 	struct delayed_work scan_work;
 	struct ieee80211_sub_if_data *scan_sdata;
-	enum nl80211_channel_type oper_channel_type;
+	enum nl80211_channel_type _oper_channel_type;
 	struct ieee80211_channel *oper_channel, *csa_channel;
 
 	/* Temporary remain-on-channel for off-channel operations */
@@ -1228,6 +1228,20 @@
 int ieee80211_wk_cancel_remain_on_channel(
 	struct ieee80211_sub_if_data *sdata, u64 cookie);
 
+/* channel management */
+enum ieee80211_chan_mode {
+	CHAN_MODE_UNDEFINED,
+	CHAN_MODE_HOPPING,
+	CHAN_MODE_FIXED,
+};
+
+enum ieee80211_chan_mode
+ieee80211_get_channel_mode(struct ieee80211_local *local,
+			   struct ieee80211_sub_if_data *ignore);
+bool ieee80211_set_channel_type(struct ieee80211_local *local,
+				struct ieee80211_sub_if_data *sdata,
+				enum nl80211_channel_type chantype);
+
 #ifdef CONFIG_MAC80211_NOINLINE
 #define debug_noinline noinline
 #else
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index bd632e1..22a384d 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -111,7 +111,7 @@
 		channel_type = local->tmp_channel_type;
 	} else {
 		chan = local->oper_channel;
-		channel_type = local->oper_channel_type;
+		channel_type = local->_oper_channel_type;
 	}
 
 	if (chan != local->hw.conf.channel ||
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 358226f..1178319 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -137,11 +137,14 @@
 	struct sta_info *sta;
 	u32 changed = 0;
 	u16 ht_opmode;
-	bool enable_ht = true, ht_changed;
+	bool enable_ht = true;
+	enum nl80211_channel_type prev_chantype;
 	enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
 
 	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 
+	prev_chantype = sdata->vif.bss_conf.channel_type;
+
 	/* HT is not supported */
 	if (!sband->ht_cap.ht_supported)
 		enable_ht = false;
@@ -172,38 +175,37 @@
 		}
 	}
 
-	ht_changed = conf_is_ht(&local->hw.conf) != enable_ht ||
-		     channel_type != local->hw.conf.channel_type;
-
 	if (local->tmp_channel)
 		local->tmp_channel_type = channel_type;
-	local->oper_channel_type = channel_type;
 
-	if (ht_changed) {
-                /* channel_type change automatically detected */
-		ieee80211_hw_config(local, 0);
+	if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
+		/* can only fail due to HT40+/- mismatch */
+		channel_type = NL80211_CHAN_HT20;
+		WARN_ON(!ieee80211_set_channel_type(local, sdata, channel_type));
+	}
 
+	/* channel_type change automatically detected */
+	ieee80211_hw_config(local, 0);
+
+	if (prev_chantype != channel_type) {
 		rcu_read_lock();
 		sta = sta_info_get(sdata, bssid);
 		if (sta)
 			rate_control_rate_update(local, sband, sta,
 						 IEEE80211_RC_HT_CHANGED,
-						 local->oper_channel_type);
+						 channel_type);
 		rcu_read_unlock();
-        }
-
-	/* disable HT */
-	if (!enable_ht)
-		return 0;
+	}
 
 	ht_opmode = le16_to_cpu(hti->operation_mode);
 
 	/* if bss configuration changed store the new one */
-	if (!sdata->ht_opmode_valid ||
-	    sdata->vif.bss_conf.ht_operation_mode != ht_opmode) {
+	if (sdata->ht_opmode_valid != enable_ht ||
+	    sdata->vif.bss_conf.ht_operation_mode != ht_opmode ||
+	    prev_chantype != channel_type) {
 		changed |= BSS_CHANGED_HT;
 		sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
-		sdata->ht_opmode_valid = true;
+		sdata->ht_opmode_valid = enable_ht;
 	}
 
 	return changed;
@@ -866,7 +868,7 @@
 	ieee80211_set_wmm_default(sdata);
 
 	/* channel(_type) changes are handled by ieee80211_hw_config */
-	local->oper_channel_type = NL80211_CHAN_NO_HT;
+	WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT));
 
 	/* on the next assoc, re-program HT parameters */
 	sdata->ht_opmode_valid = false;
@@ -883,8 +885,8 @@
 
 	ieee80211_hw_config(local, config_changed);
 
-	/* And the BSSID changed -- not very interesting here */
-	changed |= BSS_CHANGED_BSSID;
+	/* The BSSID (not really interesting) and HT changed */
+	changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT;
 	ieee80211_bss_info_change_notify(sdata, changed);
 
 	if (remove_sta)
@@ -2266,7 +2268,7 @@
 	if ((chan != local->tmp_channel ||
 	     channel_type != local->tmp_channel_type) &&
 	    (chan != local->oper_channel ||
-	     channel_type != local->oper_channel_type))
+	     channel_type != local->_oper_channel_type))
 		return -EBUSY;
 
 	skb = dev_alloc_skb(local->hw.extra_tx_headroom + len);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index f3841f4..680bcb7 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2251,8 +2251,9 @@
 
 	info->control.vif = vif;
 
-	info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
-	info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
+	info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT |
+			IEEE80211_TX_CTL_ASSIGN_SEQ |
+			IEEE80211_TX_CTL_FIRST_FRAGMENT;
  out:
 	rcu_read_unlock();
 	return skb;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 2b75b4f..5b79d55 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1160,18 +1160,33 @@
 
 	/* Finally also reconfigure all the BSS information */
 	list_for_each_entry(sdata, &local->interfaces, list) {
-		u32 changed = ~0;
+		u32 changed;
+
 		if (!ieee80211_sdata_running(sdata))
 			continue;
+
+		/* common change flags for all interface types */
+		changed = BSS_CHANGED_ERP_CTS_PROT |
+			  BSS_CHANGED_ERP_PREAMBLE |
+			  BSS_CHANGED_ERP_SLOT |
+			  BSS_CHANGED_HT |
+			  BSS_CHANGED_BASIC_RATES |
+			  BSS_CHANGED_BEACON_INT |
+			  BSS_CHANGED_BSSID |
+			  BSS_CHANGED_CQM;
+
 		switch (sdata->vif.type) {
 		case NL80211_IFTYPE_STATION:
-			/* disable beacon change bits */
-			changed &= ~(BSS_CHANGED_BEACON |
-				     BSS_CHANGED_BEACON_ENABLED);
-			/* fall through */
+			changed |= BSS_CHANGED_ASSOC;
+			ieee80211_bss_info_change_notify(sdata, changed);
+			break;
 		case NL80211_IFTYPE_ADHOC:
+			changed |= BSS_CHANGED_IBSS;
+			/* fall through */
 		case NL80211_IFTYPE_AP:
 		case NL80211_IFTYPE_MESH_POINT:
+			changed |= BSS_CHANGED_BEACON |
+				   BSS_CHANGED_BEACON_ENABLED;
 			ieee80211_bss_info_change_notify(sdata, changed);
 			break;
 		case NL80211_IFTYPE_WDS:
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index bf1737f..d92d088 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -10,38 +10,6 @@
 #include "core.h"
 
 struct ieee80211_channel *
-rdev_fixed_channel(struct cfg80211_registered_device *rdev,
-		   struct wireless_dev *for_wdev)
-{
-	struct wireless_dev *wdev;
-	struct ieee80211_channel *result = NULL;
-
-	WARN_ON(!mutex_is_locked(&rdev->devlist_mtx));
-
-	list_for_each_entry(wdev, &rdev->netdev_list, list) {
-		if (wdev == for_wdev)
-			continue;
-
-		/*
-		 * Lock manually to tell lockdep about allowed
-		 * nesting here if for_wdev->mtx is held already.
-		 * This is ok as it's all under the rdev devlist
-		 * mutex and as such can only be done once at any
-		 * given time.
-		 */
-		mutex_lock_nested(&wdev->mtx, SINGLE_DEPTH_NESTING);
-		if (wdev->current_bss)
-			result = wdev->current_bss->pub.channel;
-		wdev_unlock(wdev);
-
-		if (result)
-			break;
-	}
-
-	return result;
-}
-
-struct ieee80211_channel *
 rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
 		  int freq, enum nl80211_channel_type channel_type)
 {
@@ -75,15 +43,22 @@
 	return chan;
 }
 
-int rdev_set_freq(struct cfg80211_registered_device *rdev,
-		  struct wireless_dev *for_wdev,
-		  int freq, enum nl80211_channel_type channel_type)
+int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
+		      struct wireless_dev *wdev, int freq,
+		      enum nl80211_channel_type channel_type)
 {
 	struct ieee80211_channel *chan;
 	int result;
 
-	if (rdev_fixed_channel(rdev, for_wdev))
-		return -EBUSY;
+	if (wdev->iftype == NL80211_IFTYPE_MONITOR)
+		wdev = NULL;
+
+	if (wdev) {
+		ASSERT_WDEV_LOCK(wdev);
+
+		if (!netif_running(wdev->netdev))
+			return -ENETDOWN;
+	}
 
 	if (!rdev->ops->set_channel)
 		return -EOPNOTSUPP;
@@ -92,11 +67,14 @@
 	if (!chan)
 		return -EINVAL;
 
-	result = rdev->ops->set_channel(&rdev->wiphy, chan, channel_type);
+	result = rdev->ops->set_channel(&rdev->wiphy,
+					wdev ? wdev->netdev : NULL,
+					chan, channel_type);
 	if (result)
 		return result;
 
-	rdev->channel = chan;
+	if (wdev)
+		wdev->channel = chan;
 
 	return 0;
 }
diff --git a/net/wireless/core.h b/net/wireless/core.h
index b2234b4..ae930ac 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -70,9 +70,6 @@
 	struct work_struct conn_work;
 	struct work_struct event_work;
 
-	/* current channel */
-	struct ieee80211_channel *channel;
-
 	/* must be last because of the way we do wiphy_priv(),
 	 * and it should at least be aligned to NETDEV_ALIGN */
 	struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN)));
@@ -388,14 +385,11 @@
 void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
 
 struct ieee80211_channel *
-rdev_fixed_channel(struct cfg80211_registered_device *rdev,
-		   struct wireless_dev *for_wdev);
-struct ieee80211_channel *
 rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
 		  int freq, enum nl80211_channel_type channel_type);
-int rdev_set_freq(struct cfg80211_registered_device *rdev,
-		  struct wireless_dev *for_wdev,
-		  int freq, enum nl80211_channel_type channel_type);
+int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
+		      struct wireless_dev *wdev, int freq,
+		      enum nl80211_channel_type channel_type);
 
 u16 cfg80211_calculate_bitrate(struct rate_info *rate);
 
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index 6a5acf7..adcabba 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -81,15 +81,10 @@
 			 struct cfg80211_cached_keys *connkeys)
 {
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
-	struct ieee80211_channel *chan;
 	int err;
 
 	ASSERT_WDEV_LOCK(wdev);
 
-	chan = rdev_fixed_channel(rdev, wdev);
-	if (chan && chan != params->channel)
-		return -EBUSY;
-
 	if (wdev->ssid_len)
 		return -EALREADY;
 
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 01da83dd..aaa1aad 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -589,6 +589,7 @@
 		i++;
 		NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
 	}
+	CMD(set_channel, SET_CHANNEL);
 
 #undef CMD
 
@@ -689,10 +690,90 @@
 	return 0;
 }
 
+static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
+{
+	/*
+	 * You can only set the channel explicitly for AP, mesh
+	 * and WDS type interfaces; all others have their channel
+	 * managed via their respective "establish a connection"
+	 * command (connect, join, ...)
+	 *
+	 * Monitors are special as they are normally slaved to
+	 * whatever else is going on, so they behave as though
+	 * you tried setting the wiphy channel itself.
+	 */
+	return !wdev ||
+		wdev->iftype == NL80211_IFTYPE_AP ||
+		wdev->iftype == NL80211_IFTYPE_WDS ||
+		wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
+		wdev->iftype == NL80211_IFTYPE_MONITOR;
+}
+
+static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
+				 struct wireless_dev *wdev,
+				 struct genl_info *info)
+{
+	enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
+	u32 freq;
+	int result;
+
+	if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
+		return -EINVAL;
+
+	if (!nl80211_can_set_dev_channel(wdev))
+		return -EOPNOTSUPP;
+
+	if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+		channel_type = nla_get_u32(info->attrs[
+				   NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+		if (channel_type != NL80211_CHAN_NO_HT &&
+		    channel_type != NL80211_CHAN_HT20 &&
+		    channel_type != NL80211_CHAN_HT40PLUS &&
+		    channel_type != NL80211_CHAN_HT40MINUS)
+			return -EINVAL;
+	}
+
+	freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+
+	mutex_lock(&rdev->devlist_mtx);
+	if (wdev) {
+		wdev_lock(wdev);
+		result = cfg80211_set_freq(rdev, wdev, freq, channel_type);
+		wdev_unlock(wdev);
+	} else {
+		result = cfg80211_set_freq(rdev, NULL, freq, channel_type);
+	}
+	mutex_unlock(&rdev->devlist_mtx);
+
+	return result;
+}
+
+static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_device *rdev;
+	struct net_device *netdev;
+	int result;
+
+	rtnl_lock();
+
+	result = get_rdev_dev_by_info_ifindex(info, &rdev, &netdev);
+	if (result)
+		goto unlock;
+
+	result = __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
+
+ unlock:
+	rtnl_unlock();
+
+	return result;
+}
+
 static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 {
 	struct cfg80211_registered_device *rdev;
-	int result = 0, rem_txq_params = 0;
+	struct net_device *netdev = NULL;
+	struct wireless_dev *wdev;
+	int result, rem_txq_params = 0;
 	struct nlattr *nl_txq_params;
 	u32 changed;
 	u8 retry_short = 0, retry_long = 0;
@@ -701,16 +782,50 @@
 
 	rtnl_lock();
 
+	/*
+	 * Try to find the wiphy and netdev. Normally this
+	 * function shouldn't need the netdev, but this is
+	 * done for backward compatibility -- previously
+	 * setting the channel was done per wiphy, but now
+	 * it is per netdev. Previous userland like hostapd
+	 * also passed a netdev to set_wiphy, so that it is
+	 * possible to let that go to the right netdev!
+	 */
 	mutex_lock(&cfg80211_mutex);
 
-	rdev = __cfg80211_rdev_from_info(info);
-	if (IS_ERR(rdev)) {
-		mutex_unlock(&cfg80211_mutex);
-		result = PTR_ERR(rdev);
-		goto unlock;
+	if (info->attrs[NL80211_ATTR_IFINDEX]) {
+		int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
+
+		netdev = dev_get_by_index(genl_info_net(info), ifindex);
+		if (netdev && netdev->ieee80211_ptr) {
+			rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
+			mutex_lock(&rdev->mtx);
+		} else
+			netdev = NULL;
 	}
 
-	mutex_lock(&rdev->mtx);
+	if (!netdev) {
+		rdev = __cfg80211_rdev_from_info(info);
+		if (IS_ERR(rdev)) {
+			mutex_unlock(&cfg80211_mutex);
+			result = PTR_ERR(rdev);
+			goto unlock;
+		}
+		wdev = NULL;
+		netdev = NULL;
+		result = 0;
+
+		mutex_lock(&rdev->mtx);
+	} else if (netif_running(netdev) &&
+		   nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
+		wdev = netdev->ieee80211_ptr;
+	else
+		wdev = NULL;
+
+	/*
+	 * end workaround code, by now the rdev is available
+	 * and locked, and wdev may or may not be NULL.
+	 */
 
 	if (info->attrs[NL80211_ATTR_WIPHY_NAME])
 		result = cfg80211_dev_rename(
@@ -749,26 +864,7 @@
 	}
 
 	if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
-		enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
-		u32 freq;
-
-		result = -EINVAL;
-
-		if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
-			channel_type = nla_get_u32(info->attrs[
-					   NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
-			if (channel_type != NL80211_CHAN_NO_HT &&
-			    channel_type != NL80211_CHAN_HT20 &&
-			    channel_type != NL80211_CHAN_HT40PLUS &&
-			    channel_type != NL80211_CHAN_HT40MINUS)
-				goto bad_res;
-		}
-
-		freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
-
-		mutex_lock(&rdev->devlist_mtx);
-		result = rdev_set_freq(rdev, NULL, freq, channel_type);
-		mutex_unlock(&rdev->devlist_mtx);
+		result = __nl80211_set_channel(rdev, wdev, info);
 		if (result)
 			goto bad_res;
 	}
@@ -865,6 +961,8 @@
 
  bad_res:
 	mutex_unlock(&rdev->mtx);
+	if (netdev)
+		dev_put(netdev);
  unlock:
 	rtnl_unlock();
 	return result;
@@ -3562,9 +3660,8 @@
 {
 	struct cfg80211_registered_device *rdev;
 	struct net_device *dev;
-	struct wireless_dev *wdev;
 	struct cfg80211_crypto_settings crypto;
-	struct ieee80211_channel *chan, *fixedchan;
+	struct ieee80211_channel *chan;
 	const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
 	int err, ssid_len, ie_len = 0;
 	bool use_mfp = false;
@@ -3607,16 +3704,6 @@
 		goto out;
 	}
 
-	mutex_lock(&rdev->devlist_mtx);
-	wdev = dev->ieee80211_ptr;
-	fixedchan = rdev_fixed_channel(rdev, wdev);
-	if (fixedchan && chan != fixedchan) {
-		err = -EBUSY;
-		mutex_unlock(&rdev->devlist_mtx);
-		goto out;
-	}
-	mutex_unlock(&rdev->devlist_mtx);
-
 	ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
 	ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
 
@@ -5186,6 +5273,12 @@
 		.policy = nl80211_policy,
 		.flags = GENL_ADMIN_PERM,
 	},
+	{
+		.cmd = NL80211_CMD_SET_CHANNEL,
+		.doit = nl80211_set_channel,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
 };
 
 static struct genl_multicast_group nl80211_mlme_mcgrp = {
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 8ddf5ae..72222f0 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -741,7 +741,6 @@
 		       const u8 *prev_bssid)
 {
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
-	struct ieee80211_channel *chan;
 	struct cfg80211_bss *bss = NULL;
 	int err;
 
@@ -750,10 +749,6 @@
 	if (wdev->sme_state != CFG80211_SME_IDLE)
 		return -EALREADY;
 
-	chan = rdev_fixed_channel(rdev, wdev);
-	if (chan && chan != connect->channel)
-		return -EBUSY;
-
 	if (WARN_ON(wdev->connect_keys)) {
 		kfree(wdev->connect_keys);
 		wdev->connect_keys = NULL;
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index a60a277..9634299 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -782,16 +782,22 @@
 		return cfg80211_mgd_wext_siwfreq(dev, info, wextfreq, extra);
 	case NL80211_IFTYPE_ADHOC:
 		return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra);
-	default:
+	case NL80211_IFTYPE_MONITOR:
+	case NL80211_IFTYPE_WDS:
+	case NL80211_IFTYPE_MESH_POINT:
 		freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
 		if (freq < 0)
 			return freq;
 		if (freq == 0)
 			return -EINVAL;
+		wdev_lock(wdev);
 		mutex_lock(&rdev->devlist_mtx);
-		err = rdev_set_freq(rdev, NULL, freq, NL80211_CHAN_NO_HT);
+		err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT);
 		mutex_unlock(&rdev->devlist_mtx);
+		wdev_unlock(wdev);
 		return err;
+	default:
+		return -EOPNOTSUPP;
 	}
 }
 EXPORT_SYMBOL_GPL(cfg80211_wext_siwfreq);
@@ -801,7 +807,6 @@
 			  struct iw_freq *freq, char *extra)
 {
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
-	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
 
 	switch (wdev->iftype) {
 	case NL80211_IFTYPE_STATION:
@@ -809,9 +814,9 @@
 	case NL80211_IFTYPE_ADHOC:
 		return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra);
 	default:
-		if (!rdev->channel)
+		if (!wdev->channel)
 			return -EINVAL;
-		freq->m = rdev->channel->center_freq;
+		freq->m = wdev->channel->center_freq;
 		freq->e = 6;
 		return 0;
 	}
diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c
index d5c6140..9818198 100644
--- a/net/wireless/wext-sme.c
+++ b/net/wireless/wext-sme.c
@@ -108,7 +108,7 @@
 
 	/* SSID is not set, we just want to switch channel */
 	if (chan && !wdev->wext.connect.ssid_len) {
-		err = rdev_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT);
+		err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT);
 		goto out;
 	}