All of lore.kernel.org
 help / color / mirror / Atom feed
From: "John W. Linville" <linville@tuxdriver.com>
To: davem@davemloft.net
Cc: linux-wireless@vger.kernel.org, netdev@vger.kernel.org
Subject: Please pull 'upstream-davem' branch of wireless-2.6
Date: Mon, 6 Aug 2007 17:01:31 -0400	[thread overview]
Message-ID: <20070806210131.GM6442@tuxdriver.com> (raw)
In-Reply-To: <20070806201321.GI6442@tuxdriver.com>

Some stuff for 2.6.24, when you are ready for it.

Individual patches here:

	http://www.kernel.org/pub/linux/kernel/people/linville/wireless-2.6/upstream-davem/

Thanks!

John

---

The following changes since commit f27b62d3e7ecca42a75f1c5d3cc225539301ba6d:
  Daniel Drake (1):
        mac80211: don't allow scanning in monitor mode

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6.git upstream-davem

Andy Green (2):
      mac80211: Add get_unaligned to ieee80211_get_radiotap_len
      mac80211: Improve sanity checks on injected packets

Daniel Drake (3):
      mac80211: STA reassociation improvements
      mac80211: improved short preamble handling
      mac80211: implement ERP info change notifications

Ivo van Doorn (1):
      mac80211: Add LONG_RETRY flag to ieee80211_tx_control

Johannes Berg (14):
      mac80211: split RX handlers into own file
      mac80211: move QoS rx handlers into rx.c
      mac80211: rx cleanups (1)
      mac80211: split ieee80211_rx_h_check handler
      mac80211: split up __ieee80211_rx
      mac80211: fix bug for per-sta stats
      mac80211: rx cleanups (2)
      mac80211: split TX path into own file
      mac80211: remove some unnecessary includes
      mac80211: split out some key functions from ieee80211.c
      mac80211: move some rate control functions out of ieee80211.c
      mac80211: reorder interface related functions
      mac80211: introduce util.c
      mac80211: fix add_interface monitor mode behaviour

Michael Wu (2):
      mac80211: improve locking of sta_info related structures
      mac80211: use more GFP_KERNEL instead of GFP_ATOMIC

Thomas Graf (1):
      Use type safe netlink interface

 drivers/net/wireless/rtl8187.h     |    1 +
 drivers/net/wireless/rtl8187_dev.c |    4 +-
 include/net/ieee80211_radiotap.h   |   10 +
 include/net/mac80211.h             |   34 +-
 net/mac80211/Makefile              |    6 +-
 net/mac80211/ieee80211.c           | 4492 ++++--------------------------------
 net/mac80211/ieee80211_i.h         |   74 +-
 net/mac80211/ieee80211_iface.c     |    2 +-
 net/mac80211/ieee80211_ioctl.c     |   17 +-
 net/mac80211/ieee80211_rate.c      |   42 +
 net/mac80211/ieee80211_rate.h      |    6 +
 net/mac80211/ieee80211_sta.c       |   60 +-
 net/mac80211/key.c                 |   69 +
 net/mac80211/rx.c                  | 1453 ++++++++++++
 net/mac80211/sta_info.c            |  184 +-
 net/mac80211/sta_info.h            |   11 +-
 net/mac80211/tx.c                  | 1883 +++++++++++++++
 net/mac80211/util.c                |  488 ++++
 net/mac80211/wme.c                 |   65 -
 net/mac80211/wme.h                 |    9 +-
 net/wireless/wext.c                |   30 +-
 21 files changed, 4590 insertions(+), 4350 deletions(-)
 create mode 100644 net/mac80211/key.c
 create mode 100644 net/mac80211/rx.c
 create mode 100644 net/mac80211/tx.c
 create mode 100644 net/mac80211/util.c

diff --git a/drivers/net/wireless/rtl8187.h b/drivers/net/wireless/rtl8187.h
index 6124e46..7993b3d 100644
--- a/drivers/net/wireless/rtl8187.h
+++ b/drivers/net/wireless/rtl8187.h
@@ -67,6 +67,7 @@ struct rtl8187_priv {
 	struct rtl818x_csr *map;
 	void (*rf_init)(struct ieee80211_hw *);
 	int mode;
+	int if_id;
 
 	/* rtl8187 specific */
 	struct ieee80211_channel channels[14];
diff --git a/drivers/net/wireless/rtl8187_dev.c b/drivers/net/wireless/rtl8187_dev.c
index cea8589..c79663c 100644
--- a/drivers/net/wireless/rtl8187_dev.c
+++ b/drivers/net/wireless/rtl8187_dev.c
@@ -96,7 +96,7 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb,
 	if (control->flags & IEEE80211_TXCTL_USE_RTS_CTS) {
 		tmp |= RTL8187_TX_FLAG_RTS;
 		hdr->rts_duration =
-			ieee80211_rts_duration(dev, skb->len, control);
+			ieee80211_rts_duration(dev, priv->if_id, skb->len, control);
 	}
 	if (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)
 		tmp |= RTL8187_TX_FLAG_CTS;
@@ -510,6 +510,8 @@ static int rtl8187_config_interface(struct ieee80211_hw *dev, int if_id,
 	struct rtl8187_priv *priv = dev->priv;
 	int i;
 
+	priv->if_id = if_id;
+
 	for (i = 0; i < ETH_ALEN; i++)
 		rtl818x_iowrite8(priv, &priv->map->BSSID[i], conf->bssid[i]);
 
diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h
index a0c2b41..dfd8bf6 100644
--- a/include/net/ieee80211_radiotap.h
+++ b/include/net/ieee80211_radiotap.h
@@ -40,6 +40,7 @@
 
 #include <linux/if_ether.h>
 #include <linux/kernel.h>
+#include <asm/unaligned.h>
 
 /* Radiotap header version (from official NetBSD feed) */
 #define IEEE80211RADIOTAP_VERSION	"1.5"
@@ -255,4 +256,13 @@ enum ieee80211_radiotap_type {
 	(((x) == 14) ? 2484 : ((x) * 5) + 2407) : \
 	((x) + 1000) * 5)
 
+/* helpers */
+static inline int ieee80211_get_radiotap_len(unsigned char *data)
+{
+	struct ieee80211_radiotap_header *hdr =
+		(struct ieee80211_radiotap_header *)data;
+
+	return le16_to_cpu(get_unaligned(&hdr->it_len));
+}
+
 #endif				/* IEEE80211_RADIOTAP_H */
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index c34fd9a..e503cd3 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -192,9 +192,15 @@ struct ieee80211_tx_control {
 #define IEEE80211_TXCTL_FIRST_FRAGMENT	(1<<8) /* this is a first fragment of
 						* the frame */
 #define IEEE80211_TXCTL_TKIP_NEW_PHASE1_KEY (1<<9)
+#define IEEE80211_TXCTL_LONG_RETRY_LIMIT (1<<10) /* this frame should be send
+						  * using the through
+						  * set_retry_limit configured
+						  * long retry value */
 	u32 flags;			       /* tx control flags defined
 						* above */
-	u8 retry_limit;		/* 1 = only first attempt, 2 = one retry, .. */
+	u8 retry_limit;		/* 1 = only first attempt, 2 = one retry, ..
+				 * This could be used when set_retry_limit
+				 * is not implemented by the driver */
 	u8 power_level;		/* per-packet transmit power level, in dBm */
 	u8 antenna_sel_tx; 	/* 0 = default/diversity, 1 = Ant0, 2 = Ant1 */
 	s8 key_idx;		/* -1 = do not encrypt, >= 0 keyidx from
@@ -626,8 +632,7 @@ struct ieee80211_ops {
 	 * station hwaddr for individual keys. aid of the station is given
 	 * to help low-level driver in selecting which key->hw_key_idx to use
 	 * for this key. TX control data will use the hw_key_idx selected by
-	 * the low-level driver.
-	 * Must be atomic. */
+	 * the low-level driver. */
 	int (*set_key)(struct ieee80211_hw *hw, set_key_cmd cmd,
 		       u8 *addr, struct ieee80211_key_conf *key, int aid);
 
@@ -692,6 +697,14 @@ struct ieee80211_ops {
 	void (*sta_table_notification)(struct ieee80211_hw *hw,
 				       int num_sta);
 
+	/* Handle ERP IE change notifications. Must be atomic. */
+	void (*erp_ie_changed)(struct ieee80211_hw *hw, u8 changes,
+			       int cts_protection, int preamble);
+
+	/* Flags for the erp_ie_changed changes parameter */
+#define IEEE80211_ERP_CHANGE_PROTECTION (1<<0) /* protection flag changed */
+#define IEEE80211_ERP_CHANGE_PREAMBLE (1<<1) /* barker preamble mode changed */
+
 	/* Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
 	 * bursting) for a hardware TX queue.
 	 * queue = IEEE80211_TX_QUEUE_*.
@@ -830,6 +843,7 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
 /**
  * ieee80211_rts_get - RTS frame generation function
  * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @if_id: interface ID from &struct ieee80211_if_init_conf.
  * @frame: pointer to the frame that is going to be protected by the RTS.
  * @frame_len: the frame length (in octets).
  * @frame_txctl: &struct ieee80211_tx_control of the frame.
@@ -840,7 +854,7 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
  * the next RTS frame from the 802.11 code. The low-level is responsible
  * for calling this function before and RTS frame is needed.
  */
-void ieee80211_rts_get(struct ieee80211_hw *hw,
+void ieee80211_rts_get(struct ieee80211_hw *hw, int if_id,
 		       const void *frame, size_t frame_len,
 		       const struct ieee80211_tx_control *frame_txctl,
 		       struct ieee80211_rts *rts);
@@ -848,6 +862,7 @@ void ieee80211_rts_get(struct ieee80211_hw *hw,
 /**
  * ieee80211_rts_duration - Get the duration field for an RTS frame
  * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @if_id: interface ID from &struct ieee80211_if_init_conf.
  * @frame_len: the length of the frame that is going to be protected by the RTS.
  * @frame_txctl: &struct ieee80211_tx_control of the frame.
  *
@@ -855,13 +870,14 @@ void ieee80211_rts_get(struct ieee80211_hw *hw,
  * the duration field, the low-level driver uses this function to receive
  * the duration field value in little-endian byteorder.
  */
-__le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
+__le16 ieee80211_rts_duration(struct ieee80211_hw *hw, int if_id,
 			      size_t frame_len,
 			      const struct ieee80211_tx_control *frame_txctl);
 
 /**
  * ieee80211_ctstoself_get - CTS-to-self frame generation function
  * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @if_id: interface ID from &struct ieee80211_if_init_conf.
  * @frame: pointer to the frame that is going to be protected by the CTS-to-self.
  * @frame_len: the frame length (in octets).
  * @frame_txctl: &struct ieee80211_tx_control of the frame.
@@ -872,7 +888,7 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
  * the next CTS-to-self frame from the 802.11 code. The low-level is responsible
  * for calling this function before and CTS-to-self frame is needed.
  */
-void ieee80211_ctstoself_get(struct ieee80211_hw *hw,
+void ieee80211_ctstoself_get(struct ieee80211_hw *hw, int if_id,
 			     const void *frame, size_t frame_len,
 			     const struct ieee80211_tx_control *frame_txctl,
 			     struct ieee80211_cts *cts);
@@ -880,6 +896,7 @@ void ieee80211_ctstoself_get(struct ieee80211_hw *hw,
 /**
  * ieee80211_ctstoself_duration - Get the duration field for a CTS-to-self frame
  * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @if_id: interface ID from &struct ieee80211_if_init_conf.
  * @frame_len: the length of the frame that is going to be protected by the CTS-to-self.
  * @frame_txctl: &struct ieee80211_tx_control of the frame.
  *
@@ -887,20 +904,21 @@ void ieee80211_ctstoself_get(struct ieee80211_hw *hw,
  * the duration field, the low-level driver uses this function to receive
  * the duration field value in little-endian byteorder.
  */
-__le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
+__le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, int if_id,
 				    size_t frame_len,
 				    const struct ieee80211_tx_control *frame_txctl);
 
 /**
  * ieee80211_generic_frame_duration - Calculate the duration field for a frame
  * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @if_id: interface ID from &struct ieee80211_if_init_conf.
  * @frame_len: the length of the frame.
  * @rate: the rate (in 100kbps) at which the frame is going to be transmitted.
  *
  * Calculate the duration field of some generic frame, given its
  * length and transmission rate (in 100kbps).
  */
-__le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
+__le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, int if_id,
 					size_t frame_len,
 					int rate);
 
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index a9c2d07..122b23e 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_MAC80211) += mac80211.o rc80211_simple.o
 
 mac80211-objs-$(CONFIG_MAC80211_LEDS) += ieee80211_led.o
 mac80211-objs-$(CONFIG_MAC80211_DEBUGFS) += debugfs.o debugfs_sta.o debugfs_netdev.o debugfs_key.o
+mac80211-objs-$(CONFIG_NET_SCHED) += wme.o
 
 mac80211-objs := \
 	ieee80211.o \
@@ -16,6 +17,9 @@ mac80211-objs := \
 	regdomain.o \
 	tkip.o \
 	aes_ccm.o \
-	wme.o \
 	ieee80211_cfg.o \
+	rx.o \
+	tx.o \
+	key.o \
+	util.o \
 	$(mac80211-objs-y)
diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c
index 8ec5ed1..99179e2 100644
--- a/net/mac80211/ieee80211.c
+++ b/net/mac80211/ieee80211.c
@@ -20,42 +20,19 @@
 #include <linux/if_arp.h>
 #include <linux/wireless.h>
 #include <linux/rtnetlink.h>
-#include <net/iw_handler.h>
-#include <linux/compiler.h>
 #include <linux/bitmap.h>
 #include <net/cfg80211.h>
-#include <asm/unaligned.h>
 
 #include "ieee80211_common.h"
 #include "ieee80211_i.h"
 #include "ieee80211_rate.h"
 #include "wep.h"
-#include "wpa.h"
-#include "tkip.h"
 #include "wme.h"
 #include "aes_ccm.h"
 #include "ieee80211_led.h"
 #include "ieee80211_cfg.h"
 #include "debugfs.h"
 #include "debugfs_netdev.h"
-#include "debugfs_key.h"
-
-/* privid for wiphys to determine whether they belong to us or not */
-void *mac80211_wiphy_privid = &mac80211_wiphy_privid;
-
-/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
-/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
-static const unsigned char rfc1042_header[] =
-	{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
-
-/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
-static const unsigned char bridge_tunnel_header[] =
-	{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
-
-/* No encapsulation header if EtherType < 0x600 (=length) */
-static const unsigned char eapol_header[] =
-	{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x88, 0x8e };
-
 
 /*
  * For seeing transmitted packets on monitor interfaces
@@ -67,2315 +44,245 @@ struct ieee80211_tx_status_rtap_hdr {
 	u8 data_retries;
 } __attribute__ ((packed));
 
+/* common interface routines */
 
-static inline void ieee80211_include_sequence(struct ieee80211_sub_if_data *sdata,
-					      struct ieee80211_hdr *hdr)
-{
-	/* Set the sequence number for this frame. */
-	hdr->seq_ctrl = cpu_to_le16(sdata->sequence);
-
-	/* Increase the sequence number. */
-	sdata->sequence = (sdata->sequence + 0x10) & IEEE80211_SCTL_SEQ;
-}
-
-struct ieee80211_key_conf *
-ieee80211_key_data2conf(struct ieee80211_local *local,
-			const struct ieee80211_key *data)
-{
-	struct ieee80211_key_conf *conf;
-
-	conf = kmalloc(sizeof(*conf) + data->keylen, GFP_ATOMIC);
-	if (!conf)
-		return NULL;
-
-	conf->hw_key_idx = data->hw_key_idx;
-	conf->alg = data->alg;
-	conf->keylen = data->keylen;
-	conf->flags = 0;
-	if (data->force_sw_encrypt)
-		conf->flags |= IEEE80211_KEY_FORCE_SW_ENCRYPT;
-	conf->keyidx = data->keyidx;
-	if (data->default_tx_key)
-		conf->flags |= IEEE80211_KEY_DEFAULT_TX_KEY;
-	if (local->default_wep_only)
-		conf->flags |= IEEE80211_KEY_DEFAULT_WEP_ONLY;
-	memcpy(conf->key, data->key, data->keylen);
-
-	return conf;
-}
-
-struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
-					  int idx, size_t key_len, gfp_t flags)
-{
-	struct ieee80211_key *key;
-
-	key = kzalloc(sizeof(struct ieee80211_key) + key_len, flags);
-	if (!key)
-		return NULL;
-	kref_init(&key->kref);
-	return key;
-}
-
-static void ieee80211_key_release(struct kref *kref)
-{
-	struct ieee80211_key *key;
-
-	key = container_of(kref, struct ieee80211_key, kref);
-	if (key->alg == ALG_CCMP)
-		ieee80211_aes_key_free(key->u.ccmp.tfm);
-	ieee80211_debugfs_key_remove(key);
-	kfree(key);
-}
-
-void ieee80211_key_free(struct ieee80211_key *key)
-{
-	if (key)
-		kref_put(&key->kref, ieee80211_key_release);
-}
-
-static int rate_list_match(const int *rate_list, int rate)
+static struct net_device_stats *ieee80211_get_stats(struct net_device *dev)
 {
-	int i;
-
-	if (!rate_list)
-		return 0;
-
-	for (i = 0; rate_list[i] >= 0; i++)
-		if (rate_list[i] == rate)
-			return 1;
-
-	return 0;
+	struct ieee80211_sub_if_data *sdata;
+	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	return &(sdata->stats);
 }
 
-
-void ieee80211_prepare_rates(struct ieee80211_local *local,
-			     struct ieee80211_hw_mode *mode)
+static int header_parse_80211(struct sk_buff *skb, unsigned char *haddr)
 {
-	int i;
-
-	for (i = 0; i < mode->num_rates; i++) {
-		struct ieee80211_rate *rate = &mode->rates[i];
-
-		rate->flags &= ~(IEEE80211_RATE_SUPPORTED |
-				 IEEE80211_RATE_BASIC);
-
-		if (local->supp_rates[mode->mode]) {
-			if (!rate_list_match(local->supp_rates[mode->mode],
-					     rate->rate))
-				continue;
-		}
-
-		rate->flags |= IEEE80211_RATE_SUPPORTED;
-
-		/* Use configured basic rate set if it is available. If not,
-		 * use defaults that are sane for most cases. */
-		if (local->basic_rates[mode->mode]) {
-			if (rate_list_match(local->basic_rates[mode->mode],
-					    rate->rate))
-				rate->flags |= IEEE80211_RATE_BASIC;
-		} else switch (mode->mode) {
-		case MODE_IEEE80211A:
-			if (rate->rate == 60 || rate->rate == 120 ||
-			    rate->rate == 240)
-				rate->flags |= IEEE80211_RATE_BASIC;
-			break;
-		case MODE_IEEE80211B:
-			if (rate->rate == 10 || rate->rate == 20)
-				rate->flags |= IEEE80211_RATE_BASIC;
-			break;
-		case MODE_ATHEROS_TURBO:
-			if (rate->rate == 120 || rate->rate == 240 ||
-			    rate->rate == 480)
-				rate->flags |= IEEE80211_RATE_BASIC;
-			break;
-		case MODE_IEEE80211G:
-			if (rate->rate == 10 || rate->rate == 20 ||
-			    rate->rate == 55 || rate->rate == 110)
-				rate->flags |= IEEE80211_RATE_BASIC;
-			break;
-		}
-
-		/* Set ERP and MANDATORY flags based on phymode */
-		switch (mode->mode) {
-		case MODE_IEEE80211A:
-			if (rate->rate == 60 || rate->rate == 120 ||
-			    rate->rate == 240)
-				rate->flags |= IEEE80211_RATE_MANDATORY;
-			break;
-		case MODE_IEEE80211B:
-			if (rate->rate == 10)
-				rate->flags |= IEEE80211_RATE_MANDATORY;
-			break;
-		case MODE_ATHEROS_TURBO:
-			break;
-		case MODE_IEEE80211G:
-			if (rate->rate == 10 || rate->rate == 20 ||
-			    rate->rate == 55 || rate->rate == 110 ||
-			    rate->rate == 60 || rate->rate == 120 ||
-			    rate->rate == 240)
-				rate->flags |= IEEE80211_RATE_MANDATORY;
-			break;
-		}
-		if (ieee80211_is_erp_rate(mode->mode, rate->rate))
-			rate->flags |= IEEE80211_RATE_ERP;
-	}
+	memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */
+	return ETH_ALEN;
 }
 
+/* master interface */
 
-static void ieee80211_key_threshold_notify(struct net_device *dev,
-					   struct ieee80211_key *key,
-					   struct sta_info *sta)
+static int ieee80211_master_open(struct net_device *dev)
 {
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct sk_buff *skb;
-	struct ieee80211_msg_key_notification *msg;
-
-	/* if no one will get it anyway, don't even allocate it.
-	 * unlikely because this is only relevant for APs
-	 * where the device must be open... */
-	if (unlikely(!local->apdev))
-		return;
-
-	skb = dev_alloc_skb(sizeof(struct ieee80211_frame_info) +
-			    sizeof(struct ieee80211_msg_key_notification));
-	if (!skb)
-		return;
-
-	skb_reserve(skb, sizeof(struct ieee80211_frame_info));
-	msg = (struct ieee80211_msg_key_notification *)
-		skb_put(skb, sizeof(struct ieee80211_msg_key_notification));
-	msg->tx_rx_count = key->tx_rx_count;
-	memcpy(msg->ifname, dev->name, IFNAMSIZ);
-	if (sta)
-		memcpy(msg->addr, sta->addr, ETH_ALEN);
-	else
-		memset(msg->addr, 0xff, ETH_ALEN);
-
-	key->tx_rx_count = 0;
-
-	ieee80211_rx_mgmt(local, skb, NULL,
-			  ieee80211_msg_key_threshold_notification);
-}
-
-
-static u8 * ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len)
-{
-	u16 fc;
-
-	if (len < 24)
-		return NULL;
-
-	fc = le16_to_cpu(hdr->frame_control);
-
-	switch (fc & IEEE80211_FCTL_FTYPE) {
-	case IEEE80211_FTYPE_DATA:
-		switch (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
-		case IEEE80211_FCTL_TODS:
-			return hdr->addr1;
-		case (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
-			return NULL;
-		case IEEE80211_FCTL_FROMDS:
-			return hdr->addr2;
-		case 0:
-			return hdr->addr3;
-		}
-		break;
-	case IEEE80211_FTYPE_MGMT:
-		return hdr->addr3;
-	case IEEE80211_FTYPE_CTL:
-		if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)
-			return hdr->addr1;
-		else
-			return NULL;
-	}
-
-	return NULL;
-}
-
-int ieee80211_get_hdrlen(u16 fc)
-{
-	int hdrlen = 24;
-
-	switch (fc & IEEE80211_FCTL_FTYPE) {
-	case IEEE80211_FTYPE_DATA:
-		if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS))
-			hdrlen = 30; /* Addr4 */
-		/*
-		 * The QoS Control field is two bytes and its presence is
-		 * indicated by the IEEE80211_STYPE_QOS_DATA bit. Add 2 to
-		 * hdrlen if that bit is set.
-		 * This works by masking out the bit and shifting it to
-		 * bit position 1 so the result has the value 0 or 2.
-		 */
-		hdrlen += (fc & IEEE80211_STYPE_QOS_DATA)
-				>> (ilog2(IEEE80211_STYPE_QOS_DATA)-1);
-		break;
-	case IEEE80211_FTYPE_CTL:
-		/*
-		 * ACK and CTS are 10 bytes, all others 16. To see how
-		 * to get this condition consider
-		 *   subtype mask:   0b0000000011110000 (0x00F0)
-		 *   ACK subtype:    0b0000000011010000 (0x00D0)
-		 *   CTS subtype:    0b0000000011000000 (0x00C0)
-		 *   bits that matter:         ^^^      (0x00E0)
-		 *   value of those: 0b0000000011000000 (0x00C0)
-		 */
-		if ((fc & 0xE0) == 0xC0)
-			hdrlen = 10;
-		else
-			hdrlen = 16;
-		break;
-	}
-
-	return hdrlen;
-}
-EXPORT_SYMBOL(ieee80211_get_hdrlen);
-
-int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
-{
-	const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) skb->data;
-	int hdrlen;
-
-	if (unlikely(skb->len < 10))
-		return 0;
-	hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control));
-	if (unlikely(hdrlen > skb->len))
-		return 0;
-	return hdrlen;
-}
-EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
-
-static int ieee80211_get_radiotap_len(struct sk_buff *skb)
-{
-	struct ieee80211_radiotap_header *hdr =
-		(struct ieee80211_radiotap_header *) skb->data;
-
-	return le16_to_cpu(hdr->it_len);
-}
-
-#ifdef CONFIG_MAC80211_LOWTX_FRAME_DUMP
-static void ieee80211_dump_frame(const char *ifname, const char *title,
-				 const struct sk_buff *skb)
-{
-	const struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-	u16 fc;
-	int hdrlen;
-
-	printk(KERN_DEBUG "%s: %s (len=%d)", ifname, title, skb->len);
-	if (skb->len < 4) {
-		printk("\n");
-		return;
-	}
-
-	fc = le16_to_cpu(hdr->frame_control);
-	hdrlen = ieee80211_get_hdrlen(fc);
-	if (hdrlen > skb->len)
-		hdrlen = skb->len;
-	if (hdrlen >= 4)
-		printk(" FC=0x%04x DUR=0x%04x",
-		       fc, le16_to_cpu(hdr->duration_id));
-	if (hdrlen >= 10)
-		printk(" A1=" MAC_FMT, MAC_ARG(hdr->addr1));
-	if (hdrlen >= 16)
-		printk(" A2=" MAC_FMT, MAC_ARG(hdr->addr2));
-	if (hdrlen >= 24)
-		printk(" A3=" MAC_FMT, MAC_ARG(hdr->addr3));
-	if (hdrlen >= 30)
-		printk(" A4=" MAC_FMT, MAC_ARG(hdr->addr4));
-	printk("\n");
-}
-#else /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */
-static inline void ieee80211_dump_frame(const char *ifname, const char *title,
-					struct sk_buff *skb)
-{
-}
-#endif /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */
-
-
-static int ieee80211_is_eapol(const struct sk_buff *skb)
-{
-	const struct ieee80211_hdr *hdr;
-	u16 fc;
-	int hdrlen;
-
-	if (unlikely(skb->len < 10))
-		return 0;
-
-	hdr = (const struct ieee80211_hdr *) skb->data;
-	fc = le16_to_cpu(hdr->frame_control);
-
-	if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
-		return 0;
-
-	hdrlen = ieee80211_get_hdrlen(fc);
-
-	if (unlikely(skb->len >= hdrlen + sizeof(eapol_header) &&
-		     memcmp(skb->data + hdrlen, eapol_header,
-			    sizeof(eapol_header)) == 0))
-		return 1;
-
-	return 0;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_tx_h_rate_ctrl(struct ieee80211_txrx_data *tx)
-{
-	struct rate_control_extra extra;
-
-	memset(&extra, 0, sizeof(extra));
-	extra.mode = tx->u.tx.mode;
-	extra.mgmt_data = tx->sdata &&
-		tx->sdata->type == IEEE80211_IF_TYPE_MGMT;
-	extra.ethertype = tx->ethertype;
-
-	tx->u.tx.rate = rate_control_get_rate(tx->local, tx->dev, tx->skb,
-					      &extra);
-	if (unlikely(extra.probe != NULL)) {
-		tx->u.tx.control->flags |= IEEE80211_TXCTL_RATE_CTRL_PROBE;
-		tx->u.tx.probe_last_frag = 1;
-		tx->u.tx.control->alt_retry_rate = tx->u.tx.rate->val;
-		tx->u.tx.rate = extra.probe;
-	} else {
-		tx->u.tx.control->alt_retry_rate = -1;
-	}
-	if (!tx->u.tx.rate)
-		return TXRX_DROP;
-	if (tx->u.tx.mode->mode == MODE_IEEE80211G &&
-	    tx->sdata->use_protection && tx->fragmented &&
-	    extra.nonerp) {
-		tx->u.tx.last_frag_rate = tx->u.tx.rate;
-		tx->u.tx.probe_last_frag = extra.probe ? 1 : 0;
-
-		tx->u.tx.rate = extra.nonerp;
-		tx->u.tx.control->rate = extra.nonerp;
-		tx->u.tx.control->flags &= ~IEEE80211_TXCTL_RATE_CTRL_PROBE;
-	} else {
-		tx->u.tx.last_frag_rate = tx->u.tx.rate;
-		tx->u.tx.control->rate = tx->u.tx.rate;
-	}
-	tx->u.tx.control->tx_rate = tx->u.tx.rate->val;
-	if ((tx->u.tx.rate->flags & IEEE80211_RATE_PREAMBLE2) &&
-	    tx->local->short_preamble &&
-	    (!tx->sta || (tx->sta->flags & WLAN_STA_SHORT_PREAMBLE))) {
-		tx->u.tx.short_preamble = 1;
-		tx->u.tx.control->tx_rate = tx->u.tx.rate->val2;
-	}
-
-	return TXRX_CONTINUE;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_tx_h_select_key(struct ieee80211_txrx_data *tx)
-{
-	if (tx->sta)
-		tx->u.tx.control->key_idx = tx->sta->key_idx_compression;
-	else
-		tx->u.tx.control->key_idx = HW_KEY_IDX_INVALID;
-
-	if (unlikely(tx->u.tx.control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT))
-		tx->key = NULL;
-	else if (tx->sta && tx->sta->key)
-		tx->key = tx->sta->key;
-	else if (tx->sdata->default_key)
-		tx->key = tx->sdata->default_key;
-	else if (tx->sdata->drop_unencrypted &&
-		 !(tx->sdata->eapol && ieee80211_is_eapol(tx->skb))) {
-		I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted);
-		return TXRX_DROP;
-	} else
-		tx->key = NULL;
-
-	if (tx->key) {
-		tx->key->tx_rx_count++;
-		if (unlikely(tx->local->key_tx_rx_threshold &&
-			     tx->key->tx_rx_count >
-			     tx->local->key_tx_rx_threshold)) {
-			ieee80211_key_threshold_notify(tx->dev, tx->key,
-						       tx->sta);
-		}
-	}
-
-	return TXRX_CONTINUE;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_tx_h_fragment(struct ieee80211_txrx_data *tx)
-{
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
-	size_t hdrlen, per_fragm, num_fragm, payload_len, left;
-	struct sk_buff **frags, *first, *frag;
-	int i;
-	u16 seq;
-	u8 *pos;
-	int frag_threshold = tx->local->fragmentation_threshold;
-
-	if (!tx->fragmented)
-		return TXRX_CONTINUE;
-
-	first = tx->skb;
-
-	hdrlen = ieee80211_get_hdrlen(tx->fc);
-	payload_len = first->len - hdrlen;
-	per_fragm = frag_threshold - hdrlen - FCS_LEN;
-	num_fragm = (payload_len + per_fragm - 1) / per_fragm;
-
-	frags = kzalloc(num_fragm * sizeof(struct sk_buff *), GFP_ATOMIC);
-	if (!frags)
-		goto fail;
-
-	hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
-	seq = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ;
-	pos = first->data + hdrlen + per_fragm;
-	left = payload_len - per_fragm;
-	for (i = 0; i < num_fragm - 1; i++) {
-		struct ieee80211_hdr *fhdr;
-		size_t copylen;
-
-		if (left <= 0)
-			goto fail;
-
-		/* reserve enough extra head and tail room for possible
-		 * encryption */
-		frag = frags[i] =
-			dev_alloc_skb(tx->local->tx_headroom +
-				      frag_threshold +
-				      IEEE80211_ENCRYPT_HEADROOM +
-				      IEEE80211_ENCRYPT_TAILROOM);
-		if (!frag)
-			goto fail;
-		/* Make sure that all fragments use the same priority so
-		 * that they end up using the same TX queue */
-		frag->priority = first->priority;
-		skb_reserve(frag, tx->local->tx_headroom +
-				  IEEE80211_ENCRYPT_HEADROOM);
-		fhdr = (struct ieee80211_hdr *) skb_put(frag, hdrlen);
-		memcpy(fhdr, first->data, hdrlen);
-		if (i == num_fragm - 2)
-			fhdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREFRAGS);
-		fhdr->seq_ctrl = cpu_to_le16(seq | ((i + 1) & IEEE80211_SCTL_FRAG));
-		copylen = left > per_fragm ? per_fragm : left;
-		memcpy(skb_put(frag, copylen), pos, copylen);
-
-		pos += copylen;
-		left -= copylen;
-	}
-	skb_trim(first, hdrlen + per_fragm);
-
-	tx->u.tx.num_extra_frag = num_fragm - 1;
-	tx->u.tx.extra_frag = frags;
-
-	return TXRX_CONTINUE;
-
- fail:
-	printk(KERN_DEBUG "%s: failed to fragment frame\n", tx->dev->name);
-	if (frags) {
-		for (i = 0; i < num_fragm - 1; i++)
-			if (frags[i])
-				dev_kfree_skb(frags[i]);
-		kfree(frags);
-	}
-	I802_DEBUG_INC(tx->local->tx_handlers_drop_fragment);
-	return TXRX_DROP;
-}
-
-
-static int wep_encrypt_skb(struct ieee80211_txrx_data *tx, struct sk_buff *skb)
-{
-	if (tx->key->force_sw_encrypt) {
-		if (ieee80211_wep_encrypt(tx->local, skb, tx->key))
-			return -1;
-	} else {
-		tx->u.tx.control->key_idx = tx->key->hw_key_idx;
-		if (tx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) {
-			if (ieee80211_wep_add_iv(tx->local, skb, tx->key) ==
-			    NULL)
-				return -1;
-		}
-	}
-	return 0;
-}
-
-
-void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx)
-{
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
-
-	hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
-	if (tx->u.tx.extra_frag) {
-		struct ieee80211_hdr *fhdr;
-		int i;
-		for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
-			fhdr = (struct ieee80211_hdr *)
-				tx->u.tx.extra_frag[i]->data;
-			fhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
-		}
-	}
-}
-
-
-static ieee80211_txrx_result
-ieee80211_tx_h_wep_encrypt(struct ieee80211_txrx_data *tx)
-{
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
-	u16 fc;
-
-	fc = le16_to_cpu(hdr->frame_control);
-
-	if (!tx->key || tx->key->alg != ALG_WEP ||
-	    ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
-	     ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
-	      (fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH)))
-		return TXRX_CONTINUE;
-
-	tx->u.tx.control->iv_len = WEP_IV_LEN;
-	tx->u.tx.control->icv_len = WEP_ICV_LEN;
-	ieee80211_tx_set_iswep(tx);
-
-	if (wep_encrypt_skb(tx, tx->skb) < 0) {
-		I802_DEBUG_INC(tx->local->tx_handlers_drop_wep);
-		return TXRX_DROP;
-	}
-
-	if (tx->u.tx.extra_frag) {
-		int i;
-		for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
-			if (wep_encrypt_skb(tx, tx->u.tx.extra_frag[i]) < 0) {
-				I802_DEBUG_INC(tx->local->
-					       tx_handlers_drop_wep);
-				return TXRX_DROP;
-			}
-		}
-	}
-
-	return TXRX_CONTINUE;
-}
-
-
-static int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
-				    int rate, int erp, int short_preamble)
-{
-	int dur;
-
-	/* calculate duration (in microseconds, rounded up to next higher
-	 * integer if it includes a fractional microsecond) to send frame of
-	 * len bytes (does not include FCS) at the given rate. Duration will
-	 * also include SIFS.
-	 *
-	 * rate is in 100 kbps, so divident is multiplied by 10 in the
-	 * DIV_ROUND_UP() operations.
-	 */
-
-	if (local->hw.conf.phymode == MODE_IEEE80211A || erp ||
-	    local->hw.conf.phymode == MODE_ATHEROS_TURBO) {
-		/*
-		 * OFDM:
-		 *
-		 * N_DBPS = DATARATE x 4
-		 * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS)
-		 *	(16 = SIGNAL time, 6 = tail bits)
-		 * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext
-		 *
-		 * T_SYM = 4 usec
-		 * 802.11a - 17.5.2: aSIFSTime = 16 usec
-		 * 802.11g - 19.8.4: aSIFSTime = 10 usec +
-		 *	signal ext = 6 usec
-		 */
-		/* FIX: Atheros Turbo may have different (shorter) duration? */
-		dur = 16; /* SIFS + signal ext */
-		dur += 16; /* 17.3.2.3: T_PREAMBLE = 16 usec */
-		dur += 4; /* 17.3.2.3: T_SIGNAL = 4 usec */
-		dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10,
-					4 * rate); /* T_SYM x N_SYM */
-	} else {
-		/*
-		 * 802.11b or 802.11g with 802.11b compatibility:
-		 * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime +
-		 * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0.
-		 *
-		 * 802.11 (DS): 15.3.3, 802.11b: 18.3.4
-		 * aSIFSTime = 10 usec
-		 * aPreambleLength = 144 usec or 72 usec with short preamble
-		 * aPLCPHeaderLength = 48 usec or 24 usec with short preamble
-		 */
-		dur = 10; /* aSIFSTime = 10 usec */
-		dur += short_preamble ? (72 + 24) : (144 + 48);
-
-		dur += DIV_ROUND_UP(8 * (len + 4) * 10, rate);
-	}
-
-	return dur;
-}
-
-
-/* Exported duration function for driver use */
-__le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
-					size_t frame_len, int rate)
-{
-	struct ieee80211_local *local = hw_to_local(hw);
-	u16 dur;
-	int erp;
-
-	erp = ieee80211_is_erp_rate(hw->conf.phymode, rate);
-	dur = ieee80211_frame_duration(local, frame_len, rate,
-				       erp, local->short_preamble);
-
-	return cpu_to_le16(dur);
-}
-EXPORT_SYMBOL(ieee80211_generic_frame_duration);
-
-
-static u16 ieee80211_duration(struct ieee80211_txrx_data *tx, int group_addr,
-			      int next_frag_len)
-{
-	int rate, mrate, erp, dur, i;
-	struct ieee80211_rate *txrate = tx->u.tx.rate;
-	struct ieee80211_local *local = tx->local;
-	struct ieee80211_hw_mode *mode = tx->u.tx.mode;
-
-	erp = txrate->flags & IEEE80211_RATE_ERP;
-
-	/*
-	 * data and mgmt (except PS Poll):
-	 * - during CFP: 32768
-	 * - during contention period:
-	 *   if addr1 is group address: 0
-	 *   if more fragments = 0 and addr1 is individual address: time to
-	 *      transmit one ACK plus SIFS
-	 *   if more fragments = 1 and addr1 is individual address: time to
-	 *      transmit next fragment plus 2 x ACK plus 3 x SIFS
-	 *
-	 * IEEE 802.11, 9.6:
-	 * - control response frame (CTS or ACK) shall be transmitted using the
-	 *   same rate as the immediately previous frame in the frame exchange
-	 *   sequence, if this rate belongs to the PHY mandatory rates, or else
-	 *   at the highest possible rate belonging to the PHY rates in the
-	 *   BSSBasicRateSet
-	 */
-
-	if ((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) {
-		/* TODO: These control frames are not currently sent by
-		 * 80211.o, but should they be implemented, this function
-		 * needs to be updated to support duration field calculation.
-		 *
-		 * RTS: time needed to transmit pending data/mgmt frame plus
-		 *    one CTS frame plus one ACK frame plus 3 x SIFS
-		 * CTS: duration of immediately previous RTS minus time
-		 *    required to transmit CTS and its SIFS
-		 * ACK: 0 if immediately previous directed data/mgmt had
-		 *    more=0, with more=1 duration in ACK frame is duration
-		 *    from previous frame minus time needed to transmit ACK
-		 *    and its SIFS
-		 * PS Poll: BIT(15) | BIT(14) | aid
-		 */
-		return 0;
-	}
-
-	/* data/mgmt */
-	if (0 /* FIX: data/mgmt during CFP */)
-		return 32768;
-
-	if (group_addr) /* Group address as the destination - no ACK */
-		return 0;
-
-	/* Individual destination address:
-	 * IEEE 802.11, Ch. 9.6 (after IEEE 802.11g changes)
-	 * CTS and ACK frames shall be transmitted using the highest rate in
-	 * basic rate set that is less than or equal to the rate of the
-	 * immediately previous frame and that is using the same modulation
-	 * (CCK or OFDM). If no basic rate set matches with these requirements,
-	 * the highest mandatory rate of the PHY that is less than or equal to
-	 * the rate of the previous frame is used.
-	 * Mandatory rates for IEEE 802.11g PHY: 1, 2, 5.5, 11, 6, 12, 24 Mbps
-	 */
-	rate = -1;
-	mrate = 10; /* use 1 Mbps if everything fails */
-	for (i = 0; i < mode->num_rates; i++) {
-		struct ieee80211_rate *r = &mode->rates[i];
-		if (r->rate > txrate->rate)
-			break;
-
-		if (IEEE80211_RATE_MODULATION(txrate->flags) !=
-		    IEEE80211_RATE_MODULATION(r->flags))
-			continue;
-
-		if (r->flags & IEEE80211_RATE_BASIC)
-			rate = r->rate;
-		else if (r->flags & IEEE80211_RATE_MANDATORY)
-			mrate = r->rate;
-	}
-	if (rate == -1) {
-		/* No matching basic rate found; use highest suitable mandatory
-		 * PHY rate */
-		rate = mrate;
-	}
-
-	/* Time needed to transmit ACK
-	 * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up
-	 * to closest integer */
-
-	dur = ieee80211_frame_duration(local, 10, rate, erp,
-				       local->short_preamble);
-
-	if (next_frag_len) {
-		/* Frame is fragmented: duration increases with time needed to
-		 * transmit next fragment plus ACK and 2 x SIFS. */
-		dur *= 2; /* ACK + SIFS */
-		/* next fragment */
-		dur += ieee80211_frame_duration(local, next_frag_len,
-						txrate->rate, erp,
-						local->short_preamble);
-	}
-
-	return dur;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
-{
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
-	u16 dur;
-	struct ieee80211_tx_control *control = tx->u.tx.control;
-	struct ieee80211_hw_mode *mode = tx->u.tx.mode;
-
-	if (!is_multicast_ether_addr(hdr->addr1)) {
-		if (tx->skb->len + FCS_LEN > tx->local->rts_threshold &&
-		    tx->local->rts_threshold < IEEE80211_MAX_RTS_THRESHOLD) {
-			control->flags |= IEEE80211_TXCTL_USE_RTS_CTS;
-			control->retry_limit =
-				tx->local->long_retry_limit;
-		} else {
-			control->retry_limit =
-				tx->local->short_retry_limit;
-		}
-	} else {
-		control->retry_limit = 1;
-	}
-
-	if (tx->fragmented) {
-		/* Do not use multiple retry rates when sending fragmented
-		 * frames.
-		 * TODO: The last fragment could still use multiple retry
-		 * rates. */
-		control->alt_retry_rate = -1;
-	}
-
-	/* Use CTS protection for unicast frames sent using extended rates if
-	 * there are associated non-ERP stations and RTS/CTS is not configured
-	 * for the frame. */
-	if (mode->mode == MODE_IEEE80211G &&
-	    (tx->u.tx.rate->flags & IEEE80211_RATE_ERP) &&
-	    tx->u.tx.unicast && tx->sdata->use_protection &&
-	    !(control->flags & IEEE80211_TXCTL_USE_RTS_CTS))
-		control->flags |= IEEE80211_TXCTL_USE_CTS_PROTECT;
-
-	/* Setup duration field for the first fragment of the frame. Duration
-	 * for remaining fragments will be updated when they are being sent
-	 * to low-level driver in ieee80211_tx(). */
-	dur = ieee80211_duration(tx, is_multicast_ether_addr(hdr->addr1),
-				 tx->fragmented ? tx->u.tx.extra_frag[0]->len :
-				 0);
-	hdr->duration_id = cpu_to_le16(dur);
-
-	if ((control->flags & IEEE80211_TXCTL_USE_RTS_CTS) ||
-	    (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)) {
-		struct ieee80211_rate *rate;
-
-		/* Do not use multiple retry rates when using RTS/CTS */
-		control->alt_retry_rate = -1;
-
-		/* Use min(data rate, max base rate) as CTS/RTS rate */
-		rate = tx->u.tx.rate;
-		while (rate > mode->rates &&
-		       !(rate->flags & IEEE80211_RATE_BASIC))
-			rate--;
-
-		control->rts_cts_rate = rate->val;
-		control->rts_rate = rate;
-	}
-
-	if (tx->sta) {
-		tx->sta->tx_packets++;
-		tx->sta->tx_fragments++;
-		tx->sta->tx_bytes += tx->skb->len;
-		if (tx->u.tx.extra_frag) {
-			int i;
-			tx->sta->tx_fragments += tx->u.tx.num_extra_frag;
-			for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
-				tx->sta->tx_bytes +=
-					tx->u.tx.extra_frag[i]->len;
-			}
-		}
-	}
-
-	return TXRX_CONTINUE;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx)
-{
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-	struct sk_buff *skb = tx->skb;
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
-	u32 sta_flags;
-
-	if (unlikely(tx->local->sta_scanning != 0) &&
-	    ((tx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
-	     (tx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PROBE_REQ))
-		return TXRX_DROP;
-
-	if (tx->u.tx.ps_buffered)
-		return TXRX_CONTINUE;
-
-	sta_flags = tx->sta ? tx->sta->flags : 0;
-
-	if (likely(tx->u.tx.unicast)) {
-		if (unlikely(!(sta_flags & WLAN_STA_ASSOC) &&
-			     tx->sdata->type != IEEE80211_IF_TYPE_IBSS &&
-			     (tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-			printk(KERN_DEBUG "%s: dropped data frame to not "
-			       "associated station " MAC_FMT "\n",
-			       tx->dev->name, MAC_ARG(hdr->addr1));
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
-			I802_DEBUG_INC(tx->local->tx_handlers_drop_not_assoc);
-			return TXRX_DROP;
-		}
-	} else {
-		if (unlikely((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
-			     tx->local->num_sta == 0 &&
-			     !tx->local->allow_broadcast_always &&
-			     tx->sdata->type != IEEE80211_IF_TYPE_IBSS)) {
-			/*
-			 * No associated STAs - no need to send multicast
-			 * frames.
-			 */
-			return TXRX_DROP;
-		}
-		return TXRX_CONTINUE;
-	}
-
-	if (unlikely(!tx->u.tx.mgmt_interface && tx->sdata->ieee802_1x &&
-		     !(sta_flags & WLAN_STA_AUTHORIZED))) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-		printk(KERN_DEBUG "%s: dropped frame to " MAC_FMT
-		       " (unauthorized port)\n", tx->dev->name,
-		       MAC_ARG(hdr->addr1));
-#endif
-		I802_DEBUG_INC(tx->local->tx_handlers_drop_unauth_port);
-		return TXRX_DROP;
-	}
-
-	return TXRX_CONTINUE;
-}
-
-static ieee80211_txrx_result
-ieee80211_tx_h_sequence(struct ieee80211_txrx_data *tx)
-{
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
-
-	if (ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control)) >= 24)
-		ieee80211_include_sequence(tx->sdata, hdr);
-
-	return TXRX_CONTINUE;
-}
-
-/* This function is called whenever the AP is about to exceed the maximum limit
- * of buffered frames for power saving STAs. This situation should not really
- * happen often during normal operation, so dropping the oldest buffered packet
- * from each queue should be OK to make some room for new frames. */
-static void purge_old_ps_buffers(struct ieee80211_local *local)
-{
-	int total = 0, purged = 0;
-	struct sk_buff *skb;
 	struct ieee80211_sub_if_data *sdata;
-	struct sta_info *sta;
+	int res = -EOPNOTSUPP;
 
 	read_lock(&local->sub_if_lock);
 	list_for_each_entry(sdata, &local->sub_if_list, list) {
-		struct ieee80211_if_ap *ap;
-		if (sdata->dev == local->mdev ||
-		    sdata->type != IEEE80211_IF_TYPE_AP)
-			continue;
-		ap = &sdata->u.ap;
-		skb = skb_dequeue(&ap->ps_bc_buf);
-		if (skb) {
-			purged++;
-			dev_kfree_skb(skb);
+		if (sdata->dev != dev && netif_running(sdata->dev)) {
+			res = 0;
+			break;
 		}
-		total += skb_queue_len(&ap->ps_bc_buf);
 	}
 	read_unlock(&local->sub_if_lock);
-
-	spin_lock_bh(&local->sta_lock);
-	list_for_each_entry(sta, &local->sta_list, list) {
-		skb = skb_dequeue(&sta->ps_tx_buf);
-		if (skb) {
-			purged++;
-			dev_kfree_skb(skb);
-		}
-		total += skb_queue_len(&sta->ps_tx_buf);
-	}
-	spin_unlock_bh(&local->sta_lock);
-
-	local->total_ps_buffered = total;
-	printk(KERN_DEBUG "%s: PS buffers full - purged %d frames\n",
-	       local->mdev->name, purged);
-}
-
-
-static inline ieee80211_txrx_result
-ieee80211_tx_h_multicast_ps_buf(struct ieee80211_txrx_data *tx)
-{
-	/* broadcast/multicast frame */
-	/* If any of the associated stations is in power save mode,
-	 * the frame is buffered to be sent after DTIM beacon frame */
-	if ((tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING) &&
-	    tx->sdata->type != IEEE80211_IF_TYPE_WDS &&
-	    tx->sdata->bss && atomic_read(&tx->sdata->bss->num_sta_ps) &&
-	    !(tx->fc & IEEE80211_FCTL_ORDER)) {
-		if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
-			purge_old_ps_buffers(tx->local);
-		if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >=
-		    AP_MAX_BC_BUFFER) {
-			if (net_ratelimit()) {
-				printk(KERN_DEBUG "%s: BC TX buffer full - "
-				       "dropping the oldest frame\n",
-				       tx->dev->name);
-			}
-			dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf));
-		} else
-			tx->local->total_ps_buffered++;
-		skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb);
-		return TXRX_QUEUED;
-	}
-
-	return TXRX_CONTINUE;
+	return res;
 }
 
-
-static inline ieee80211_txrx_result
-ieee80211_tx_h_unicast_ps_buf(struct ieee80211_txrx_data *tx)
+static int ieee80211_master_stop(struct net_device *dev)
 {
-	struct sta_info *sta = tx->sta;
-
-	if (unlikely(!sta ||
-		     ((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT &&
-		      (tx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP)))
-		return TXRX_CONTINUE;
-
-	if (unlikely((sta->flags & WLAN_STA_PS) && !sta->pspoll)) {
-		struct ieee80211_tx_packet_data *pkt_data;
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
-		printk(KERN_DEBUG "STA " MAC_FMT " aid %d: PS buffer (entries "
-		       "before %d)\n",
-		       MAC_ARG(sta->addr), sta->aid,
-		       skb_queue_len(&sta->ps_tx_buf));
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
-		sta->flags |= WLAN_STA_TIM;
-		if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
-			purge_old_ps_buffers(tx->local);
-		if (skb_queue_len(&sta->ps_tx_buf) >= STA_MAX_TX_BUFFER) {
-			struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf);
-			if (net_ratelimit()) {
-				printk(KERN_DEBUG "%s: STA " MAC_FMT " TX "
-				       "buffer full - dropping oldest frame\n",
-				       tx->dev->name, MAC_ARG(sta->addr));
-			}
-			dev_kfree_skb(old);
-		} else
-			tx->local->total_ps_buffered++;
-		/* Queue frame to be sent after STA sends an PS Poll frame */
-		if (skb_queue_empty(&sta->ps_tx_buf)) {
-			if (tx->local->ops->set_tim)
-				tx->local->ops->set_tim(local_to_hw(tx->local),
-						       sta->aid, 1);
-			if (tx->sdata->bss)
-				bss_tim_set(tx->local, tx->sdata->bss, sta->aid);
-		}
-		pkt_data = (struct ieee80211_tx_packet_data *)tx->skb->cb;
-		pkt_data->jiffies = jiffies;
-		skb_queue_tail(&sta->ps_tx_buf, tx->skb);
-		return TXRX_QUEUED;
-	}
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
-	else if (unlikely(sta->flags & WLAN_STA_PS)) {
-		printk(KERN_DEBUG "%s: STA " MAC_FMT " in PS mode, but pspoll "
-		       "set -> send frame\n", tx->dev->name,
-		       MAC_ARG(sta->addr));
-	}
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
-	sta->pspoll = 0;
-
-	return TXRX_CONTINUE;
-}
-
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_sub_if_data *sdata;
 
-static ieee80211_txrx_result
-ieee80211_tx_h_ps_buf(struct ieee80211_txrx_data *tx)
-{
-	if (unlikely(tx->u.tx.ps_buffered))
-		return TXRX_CONTINUE;
+	read_lock(&local->sub_if_lock);
+	list_for_each_entry(sdata, &local->sub_if_list, list)
+		if (sdata->dev != dev && netif_running(sdata->dev))
+			dev_close(sdata->dev);
+	read_unlock(&local->sub_if_lock);
 
-	if (tx->u.tx.unicast)
-		return ieee80211_tx_h_unicast_ps_buf(tx);
-	else
-		return ieee80211_tx_h_multicast_ps_buf(tx);
+	return 0;
 }
 
+/* management interface */
 
-/*
- * deal with packet injection down monitor interface
- * with Radiotap Header -- only called for monitor mode interface
- */
-
-static ieee80211_txrx_result
-__ieee80211_parse_tx_radiotap(
-	struct ieee80211_txrx_data *tx,
-	struct sk_buff *skb, struct ieee80211_tx_control *control)
+static void
+ieee80211_fill_frame_info(struct ieee80211_local *local,
+			  struct ieee80211_frame_info *fi,
+			  struct ieee80211_rx_status *status)
 {
-	/*
-	 * this is the moment to interpret and discard the radiotap header that
-	 * must be at the start of the packet injected in Monitor mode
-	 *
-	 * Need to take some care with endian-ness since radiotap
-	 * args are little-endian
-	 */
-
-	struct ieee80211_radiotap_iterator iterator;
-	struct ieee80211_radiotap_header *rthdr =
-		(struct ieee80211_radiotap_header *) skb->data;
-	struct ieee80211_hw_mode *mode = tx->local->hw.conf.mode;
-	int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
-
-	/*
-	 * default control situation for all injected packets
-	 * FIXME: this does not suit all usage cases, expand to allow control
-	 */
-
-	control->retry_limit = 1; /* no retry */
-	control->key_idx = -1; /* no encryption key */
-	control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS |
-			    IEEE80211_TXCTL_USE_CTS_PROTECT);
-	control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT |
-			  IEEE80211_TXCTL_NO_ACK;
-	control->antenna_sel_tx = 0; /* default to default antenna */
-
-	/*
-	 * for every radiotap entry that is present
-	 * (ieee80211_radiotap_iterator_next returns -ENOENT when no more
-	 * entries present, or -EINVAL on error)
-	 */
-
-	while (!ret) {
-		int i, target_rate;
-
-		ret = ieee80211_radiotap_iterator_next(&iterator);
-
-		if (ret)
-			continue;
+	if (status) {
+		struct timespec ts;
+		struct ieee80211_rate *rate;
 
-		/* see if this argument is something we can use */
-		switch (iterator.this_arg_index) {
-		/*
-		 * You must take care when dereferencing iterator.this_arg
-		 * for multibyte types... the pointer is not aligned.  Use
-		 * get_unaligned((type *)iterator.this_arg) to dereference
-		 * iterator.this_arg for type "type" safely on all arches.
-		*/
-		case IEEE80211_RADIOTAP_RATE:
-			/*
-			 * radiotap rate u8 is in 500kbps units eg, 0x02=1Mbps
-			 * ieee80211 rate int is in 100kbps units eg, 0x0a=1Mbps
-			 */
-			target_rate = (*iterator.this_arg) * 5;
-			for (i = 0; i < mode->num_rates; i++) {
-				struct ieee80211_rate *r = &mode->rates[i];
-
-				if (r->rate > target_rate)
-					continue;
-
-				control->rate = r;
-
-				if (r->flags & IEEE80211_RATE_PREAMBLE2)
-					control->tx_rate = r->val2;
-				else
-					control->tx_rate = r->val;
-
-				/* end on exact match */
-				if (r->rate == target_rate)
-					i = mode->num_rates;
-			}
+		jiffies_to_timespec(jiffies, &ts);
+		fi->hosttime = cpu_to_be64((u64) ts.tv_sec * 1000000 +
+					   ts.tv_nsec / 1000);
+		fi->mactime = cpu_to_be64(status->mactime);
+		switch (status->phymode) {
+		case MODE_IEEE80211A:
+			fi->phytype = htonl(ieee80211_phytype_ofdm_dot11_a);
 			break;
-
-		case IEEE80211_RADIOTAP_ANTENNA:
-			/*
-			 * radiotap uses 0 for 1st ant, mac80211 is 1 for
-			 * 1st ant
-			 */
-			control->antenna_sel_tx = (*iterator.this_arg) + 1;
+		case MODE_IEEE80211B:
+			fi->phytype = htonl(ieee80211_phytype_dsss_dot11_b);
 			break;
-
-		case IEEE80211_RADIOTAP_DBM_TX_POWER:
-			control->power_level = *iterator.this_arg;
+		case MODE_IEEE80211G:
+			fi->phytype = htonl(ieee80211_phytype_pbcc_dot11_g);
 			break;
-
-		case IEEE80211_RADIOTAP_FLAGS:
-			if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FCS) {
-				/*
-				 * this indicates that the skb we have been
-				 * handed has the 32-bit FCS CRC at the end...
-				 * we should react to that by snipping it off
-				 * because it will be recomputed and added
-				 * on transmission
-				 */
-				if (skb->len < (iterator.max_length + FCS_LEN))
-					return TXRX_DROP;
-
-				skb_trim(skb, skb->len - FCS_LEN);
-			}
+		case MODE_ATHEROS_TURBO:
+			fi->phytype =
+				htonl(ieee80211_phytype_dsss_dot11_turbo);
 			break;
-
 		default:
+			fi->phytype = htonl(0xAAAAAAAA);
 			break;
 		}
-	}
-
-	if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */
-		return TXRX_DROP;
-
-	/*
-	 * remove the radiotap header
-	 * iterator->max_length was sanity-checked against
-	 * skb->len by iterator init
-	 */
-	skb_pull(skb, iterator.max_length);
-
-	return TXRX_CONTINUE;
-}
-
-
-static ieee80211_txrx_result inline
-__ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
-		       struct sk_buff *skb,
-		       struct net_device *dev,
-		       struct ieee80211_tx_control *control)
-{
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-	struct ieee80211_sub_if_data *sdata;
-	ieee80211_txrx_result res = TXRX_CONTINUE;
-
-	int hdrlen;
-
-	memset(tx, 0, sizeof(*tx));
-	tx->skb = skb;
-	tx->dev = dev; /* use original interface */
-	tx->local = local;
-	tx->sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-	tx->sta = sta_info_get(local, hdr->addr1);
-	tx->fc = le16_to_cpu(hdr->frame_control);
-
-	/*
-	 * set defaults for things that can be set by
-	 * injected radiotap headers
-	 */
-	control->power_level = local->hw.conf.power_level;
-	control->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
-	if (local->sta_antenna_sel != STA_ANTENNA_SEL_AUTO && tx->sta)
-		control->antenna_sel_tx = tx->sta->antenna_sel_tx;
-
-	/* process and remove the injection radiotap header */
-	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-	if (unlikely(sdata->type == IEEE80211_IF_TYPE_MNTR)) {
-		if (__ieee80211_parse_tx_radiotap(tx, skb, control) ==
-								TXRX_DROP) {
-			return TXRX_DROP;
-		}
-		/*
-		 * we removed the radiotap header after this point,
-		 * we filled control with what we could use
-		 * set to the actual ieee header now
-		 */
-		hdr = (struct ieee80211_hdr *) skb->data;
-		res = TXRX_QUEUED; /* indication it was monitor packet */
-	}
-
-	tx->u.tx.control = control;
-	tx->u.tx.unicast = !is_multicast_ether_addr(hdr->addr1);
-	if (is_multicast_ether_addr(hdr->addr1))
-		control->flags |= IEEE80211_TXCTL_NO_ACK;
-	else
-		control->flags &= ~IEEE80211_TXCTL_NO_ACK;
-	tx->fragmented = local->fragmentation_threshold <
-		IEEE80211_MAX_FRAG_THRESHOLD && tx->u.tx.unicast &&
-		skb->len + FCS_LEN > local->fragmentation_threshold &&
-		(!local->ops->set_frag_threshold);
-	if (!tx->sta)
-		control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK;
-	else if (tx->sta->clear_dst_mask) {
-		control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK;
-		tx->sta->clear_dst_mask = 0;
-	}
-	hdrlen = ieee80211_get_hdrlen(tx->fc);
-	if (skb->len > hdrlen + sizeof(rfc1042_header) + 2) {
-		u8 *pos = &skb->data[hdrlen + sizeof(rfc1042_header)];
-		tx->ethertype = (pos[0] << 8) | pos[1];
-	}
-	control->flags |= IEEE80211_TXCTL_FIRST_FRAGMENT;
-
-	return res;
-}
-
-static int inline is_ieee80211_device(struct net_device *dev,
-				      struct net_device *master)
-{
-	return (wdev_priv(dev->ieee80211_ptr) ==
-		wdev_priv(master->ieee80211_ptr));
-}
-
-/* Device in tx->dev has a reference added; use dev_put(tx->dev) when
- * finished with it. */
-static int inline ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
-				       struct sk_buff *skb,
-				       struct net_device *mdev,
-				       struct ieee80211_tx_control *control)
-{
-	struct ieee80211_tx_packet_data *pkt_data;
-	struct net_device *dev;
-
-	pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
-	dev = dev_get_by_index(pkt_data->ifindex);
-	if (unlikely(dev && !is_ieee80211_device(dev, mdev))) {
-		dev_put(dev);
-		dev = NULL;
-	}
-	if (unlikely(!dev))
-		return -ENODEV;
-	__ieee80211_tx_prepare(tx, skb, dev, control);
-	return 0;
-}
-
-static inline int __ieee80211_queue_stopped(const struct ieee80211_local *local,
-					    int queue)
-{
-	return test_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
-}
-
-static inline int __ieee80211_queue_pending(const struct ieee80211_local *local,
-					    int queue)
-{
-	return test_bit(IEEE80211_LINK_STATE_PENDING, &local->state[queue]);
-}
-
-#define IEEE80211_TX_OK		0
-#define IEEE80211_TX_AGAIN	1
-#define IEEE80211_TX_FRAG_AGAIN	2
-
-static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
-			  struct ieee80211_txrx_data *tx)
-{
-	struct ieee80211_tx_control *control = tx->u.tx.control;
-	int ret, i;
-
-	if (!ieee80211_qdisc_installed(local->mdev) &&
-	    __ieee80211_queue_stopped(local, 0)) {
-		netif_stop_queue(local->mdev);
-		return IEEE80211_TX_AGAIN;
-	}
-	if (skb) {
-		ieee80211_dump_frame(local->mdev->name, "TX to low-level driver", skb);
-		ret = local->ops->tx(local_to_hw(local), skb, control);
-		if (ret)
-			return IEEE80211_TX_AGAIN;
-		local->mdev->trans_start = jiffies;
-		ieee80211_led_tx(local, 1);
-	}
-	if (tx->u.tx.extra_frag) {
-		control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS |
-				    IEEE80211_TXCTL_USE_CTS_PROTECT |
-				    IEEE80211_TXCTL_CLEAR_DST_MASK |
-				    IEEE80211_TXCTL_FIRST_FRAGMENT);
-		for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
-			if (!tx->u.tx.extra_frag[i])
-				continue;
-			if (__ieee80211_queue_stopped(local, control->queue))
-				return IEEE80211_TX_FRAG_AGAIN;
-			if (i == tx->u.tx.num_extra_frag) {
-				control->tx_rate = tx->u.tx.last_frag_hwrate;
-				control->rate = tx->u.tx.last_frag_rate;
-				if (tx->u.tx.probe_last_frag)
-					control->flags |=
-						IEEE80211_TXCTL_RATE_CTRL_PROBE;
-				else
-					control->flags &=
-						~IEEE80211_TXCTL_RATE_CTRL_PROBE;
-			}
-
-			ieee80211_dump_frame(local->mdev->name,
-					     "TX to low-level driver",
-					     tx->u.tx.extra_frag[i]);
-			ret = local->ops->tx(local_to_hw(local),
-					    tx->u.tx.extra_frag[i],
-					    control);
-			if (ret)
-				return IEEE80211_TX_FRAG_AGAIN;
-			local->mdev->trans_start = jiffies;
-			ieee80211_led_tx(local, 1);
-			tx->u.tx.extra_frag[i] = NULL;
-		}
-		kfree(tx->u.tx.extra_frag);
-		tx->u.tx.extra_frag = NULL;
-	}
-	return IEEE80211_TX_OK;
-}
-
-static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
-			struct ieee80211_tx_control *control, int mgmt)
-{
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct sta_info *sta;
-	ieee80211_tx_handler *handler;
-	struct ieee80211_txrx_data tx;
-	ieee80211_txrx_result res = TXRX_DROP, res_prepare;
-	int ret, i;
-
-	WARN_ON(__ieee80211_queue_pending(local, control->queue));
-
-	if (unlikely(skb->len < 10)) {
-		dev_kfree_skb(skb);
-		return 0;
-	}
-
-	res_prepare = __ieee80211_tx_prepare(&tx, skb, dev, control);
-
-	if (res_prepare == TXRX_DROP) {
-		dev_kfree_skb(skb);
-		return 0;
-	}
-
-	sta = tx.sta;
-	tx.u.tx.mgmt_interface = mgmt;
-	tx.u.tx.mode = local->hw.conf.mode;
-
-	if (res_prepare == TXRX_QUEUED) { /* if it was an injected packet */
-		res = TXRX_CONTINUE;
-	} else {
-		for (handler = local->tx_handlers; *handler != NULL;
-		     handler++) {
-			res = (*handler)(&tx);
-			if (res != TXRX_CONTINUE)
-				break;
-		}
-	}
-
-	skb = tx.skb; /* handlers are allowed to change skb */
-
-	if (sta)
-		sta_info_put(sta);
-
-	if (unlikely(res == TXRX_DROP)) {
-		I802_DEBUG_INC(local->tx_handlers_drop);
-		goto drop;
-	}
-
-	if (unlikely(res == TXRX_QUEUED)) {
-		I802_DEBUG_INC(local->tx_handlers_queued);
-		return 0;
-	}
-
-	if (tx.u.tx.extra_frag) {
-		for (i = 0; i < tx.u.tx.num_extra_frag; i++) {
-			int next_len, dur;
-			struct ieee80211_hdr *hdr =
-				(struct ieee80211_hdr *)
-				tx.u.tx.extra_frag[i]->data;
-
-			if (i + 1 < tx.u.tx.num_extra_frag) {
-				next_len = tx.u.tx.extra_frag[i + 1]->len;
-			} else {
-				next_len = 0;
-				tx.u.tx.rate = tx.u.tx.last_frag_rate;
-				tx.u.tx.last_frag_hwrate = tx.u.tx.rate->val;
-			}
-			dur = ieee80211_duration(&tx, 0, next_len);
-			hdr->duration_id = cpu_to_le16(dur);
-		}
-	}
-
-retry:
-	ret = __ieee80211_tx(local, skb, &tx);
-	if (ret) {
-		struct ieee80211_tx_stored_packet *store =
-			&local->pending_packet[control->queue];
-
-		if (ret == IEEE80211_TX_FRAG_AGAIN)
-			skb = NULL;
-		set_bit(IEEE80211_LINK_STATE_PENDING,
-			&local->state[control->queue]);
-		smp_mb();
-		/* When the driver gets out of buffers during sending of
-		 * fragments and calls ieee80211_stop_queue, there is
-		 * a small window between IEEE80211_LINK_STATE_XOFF and
-		 * IEEE80211_LINK_STATE_PENDING flags are set. If a buffer
-		 * gets available in that window (i.e. driver calls
-		 * ieee80211_wake_queue), we would end up with ieee80211_tx
-		 * called with IEEE80211_LINK_STATE_PENDING. Prevent this by
-		 * continuing transmitting here when that situation is
-		 * possible to have happened. */
-		if (!__ieee80211_queue_stopped(local, control->queue)) {
-			clear_bit(IEEE80211_LINK_STATE_PENDING,
-				  &local->state[control->queue]);
-			goto retry;
-		}
-		memcpy(&store->control, control,
-		       sizeof(struct ieee80211_tx_control));
-		store->skb = skb;
-		store->extra_frag = tx.u.tx.extra_frag;
-		store->num_extra_frag = tx.u.tx.num_extra_frag;
-		store->last_frag_hwrate = tx.u.tx.last_frag_hwrate;
-		store->last_frag_rate = tx.u.tx.last_frag_rate;
-		store->last_frag_rate_ctrl_probe = tx.u.tx.probe_last_frag;
-	}
-	return 0;
-
- drop:
-	if (skb)
-		dev_kfree_skb(skb);
-	for (i = 0; i < tx.u.tx.num_extra_frag; i++)
-		if (tx.u.tx.extra_frag[i])
-			dev_kfree_skb(tx.u.tx.extra_frag[i]);
-	kfree(tx.u.tx.extra_frag);
-	return 0;
-}
-
-static void ieee80211_tx_pending(unsigned long data)
-{
-	struct ieee80211_local *local = (struct ieee80211_local *)data;
-	struct net_device *dev = local->mdev;
-	struct ieee80211_tx_stored_packet *store;
-	struct ieee80211_txrx_data tx;
-	int i, ret, reschedule = 0;
-
-	netif_tx_lock_bh(dev);
-	for (i = 0; i < local->hw.queues; i++) {
-		if (__ieee80211_queue_stopped(local, i))
-			continue;
-		if (!__ieee80211_queue_pending(local, i)) {
-			reschedule = 1;
-			continue;
-		}
-		store = &local->pending_packet[i];
-		tx.u.tx.control = &store->control;
-		tx.u.tx.extra_frag = store->extra_frag;
-		tx.u.tx.num_extra_frag = store->num_extra_frag;
-		tx.u.tx.last_frag_hwrate = store->last_frag_hwrate;
-		tx.u.tx.last_frag_rate = store->last_frag_rate;
-		tx.u.tx.probe_last_frag = store->last_frag_rate_ctrl_probe;
-		ret = __ieee80211_tx(local, store->skb, &tx);
-		if (ret) {
-			if (ret == IEEE80211_TX_FRAG_AGAIN)
-				store->skb = NULL;
+		fi->channel = htonl(status->channel);
+		rate = ieee80211_get_rate(local, status->phymode,
+					  status->rate);
+		if (rate) {
+			fi->datarate = htonl(rate->rate);
+			if (rate->flags & IEEE80211_RATE_PREAMBLE2) {
+				if (status->rate == rate->val)
+					fi->preamble = htonl(2); /* long */
+				else if (status->rate == rate->val2)
+					fi->preamble = htonl(1); /* short */
+			} else
+				fi->preamble = htonl(0);
 		} else {
-			clear_bit(IEEE80211_LINK_STATE_PENDING,
-				  &local->state[i]);
-			reschedule = 1;
-		}
-	}
-	netif_tx_unlock_bh(dev);
-	if (reschedule) {
-		if (!ieee80211_qdisc_installed(dev)) {
-			if (!__ieee80211_queue_stopped(local, 0))
-				netif_wake_queue(dev);
-		} else
-			netif_schedule(dev);
-	}
-}
-
-static void ieee80211_clear_tx_pending(struct ieee80211_local *local)
-{
-	int i, j;
-	struct ieee80211_tx_stored_packet *store;
-
-	for (i = 0; i < local->hw.queues; i++) {
-		if (!__ieee80211_queue_pending(local, i))
-			continue;
-		store = &local->pending_packet[i];
-		kfree_skb(store->skb);
-		for (j = 0; j < store->num_extra_frag; j++)
-			kfree_skb(store->extra_frag[j]);
-		kfree(store->extra_frag);
-		clear_bit(IEEE80211_LINK_STATE_PENDING, &local->state[i]);
-	}
-}
-
-static int ieee80211_master_start_xmit(struct sk_buff *skb,
-				       struct net_device *dev)
-{
-	struct ieee80211_tx_control control;
-	struct ieee80211_tx_packet_data *pkt_data;
-	struct net_device *odev = NULL;
-	struct ieee80211_sub_if_data *osdata;
-	int headroom;
-	int ret;
-
-	/*
-	 * copy control out of the skb so other people can use skb->cb
-	 */
-	pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
-	memset(&control, 0, sizeof(struct ieee80211_tx_control));
-
-	if (pkt_data->ifindex)
-		odev = dev_get_by_index(pkt_data->ifindex);
-	if (unlikely(odev && !is_ieee80211_device(odev, dev))) {
-		dev_put(odev);
-		odev = NULL;
-	}
-	if (unlikely(!odev)) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-		printk(KERN_DEBUG "%s: Discarded packet with nonexistent "
-		       "originating device\n", dev->name);
-#endif
-		dev_kfree_skb(skb);
-		return 0;
-	}
-	osdata = IEEE80211_DEV_TO_SUB_IF(odev);
-
-	headroom = osdata->local->tx_headroom + IEEE80211_ENCRYPT_HEADROOM;
-	if (skb_headroom(skb) < headroom) {
-		if (pskb_expand_head(skb, headroom, 0, GFP_ATOMIC)) {
-			dev_kfree_skb(skb);
-			dev_put(odev);
-			return 0;
-		}
-	}
-
-	control.ifindex = odev->ifindex;
-	control.type = osdata->type;
-	if (pkt_data->req_tx_status)
-		control.flags |= IEEE80211_TXCTL_REQ_TX_STATUS;
-	if (pkt_data->do_not_encrypt)
-		control.flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
-	if (pkt_data->requeue)
-		control.flags |= IEEE80211_TXCTL_REQUEUE;
-	control.queue = pkt_data->queue;
-
-	ret = ieee80211_tx(odev, skb, &control,
-			   control.type == IEEE80211_IF_TYPE_MGMT);
-	dev_put(odev);
-
-	return ret;
-}
-
-
-int ieee80211_monitor_start_xmit(struct sk_buff *skb,
-				 struct net_device *dev)
-{
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct ieee80211_tx_packet_data *pkt_data;
-	struct ieee80211_radiotap_header *prthdr =
-		(struct ieee80211_radiotap_header *)skb->data;
-	u16 len;
-
-	/*
-	 * there must be a radiotap header at the
-	 * start in this case
-	 */
-	if (unlikely(prthdr->it_version)) {
-		/* only version 0 is supported */
-		dev_kfree_skb(skb);
-		return NETDEV_TX_OK;
-	}
-
-	skb->dev = local->mdev;
-
-	pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
-	memset(pkt_data, 0, sizeof(*pkt_data));
-	pkt_data->ifindex = dev->ifindex;
-	pkt_data->mgmt_iface = 0;
-	pkt_data->do_not_encrypt = 1;
-
-	/* above needed because we set skb device to master */
-
-	/*
-	 * fix up the pointers accounting for the radiotap
-	 * header still being in there.  We are being given
-	 * a precooked IEEE80211 header so no need for
-	 * normal processing
-	 */
-	len = le16_to_cpu(get_unaligned(&prthdr->it_len));
-	skb_set_mac_header(skb, len);
-	skb_set_network_header(skb, len + sizeof(struct ieee80211_hdr));
-	skb_set_transport_header(skb, len + sizeof(struct ieee80211_hdr));
-
-	/*
-	 * pass the radiotap header up to
-	 * the next stage intact
-	 */
-	dev_queue_xmit(skb);
-
-	return NETDEV_TX_OK;
-}
-
-
-/**
- * ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type
- * subinterfaces (wlan#, WDS, and VLAN interfaces)
- * @skb: packet to be sent
- * @dev: incoming interface
- *
- * Returns: 0 on success (and frees skb in this case) or 1 on failure (skb will
- * not be freed, and caller is responsible for either retrying later or freeing
- * skb).
- *
- * This function takes in an Ethernet header and encapsulates it with suitable
- * IEEE 802.11 header based on which interface the packet is coming in. The
- * encapsulated packet will then be passed to master interface, wlan#.11, for
- * transmission (through low-level driver).
- */
-int ieee80211_subif_start_xmit(struct sk_buff *skb,
-			       struct net_device *dev)
-{
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct ieee80211_tx_packet_data *pkt_data;
-	struct ieee80211_sub_if_data *sdata;
-	int ret = 1, head_need;
-	u16 ethertype, hdrlen, fc;
-	struct ieee80211_hdr hdr;
-	const u8 *encaps_data;
-	int encaps_len, skip_header_bytes;
-	int nh_pos, h_pos, no_encrypt = 0;
-	struct sta_info *sta;
-
-	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-	if (unlikely(skb->len < ETH_HLEN)) {
-		printk(KERN_DEBUG "%s: short skb (len=%d)\n",
-		       dev->name, skb->len);
-		ret = 0;
-		goto fail;
-	}
-
-	nh_pos = skb_network_header(skb) - skb->data;
-	h_pos = skb_transport_header(skb) - skb->data;
-
-	/* convert Ethernet header to proper 802.11 header (based on
-	 * operation mode) */
-	ethertype = (skb->data[12] << 8) | skb->data[13];
-	/* TODO: handling for 802.1x authorized/unauthorized port */
-	fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA;
-
-	if (likely(sdata->type == IEEE80211_IF_TYPE_AP ||
-		   sdata->type == IEEE80211_IF_TYPE_VLAN)) {
-		fc |= IEEE80211_FCTL_FROMDS;
-		/* DA BSSID SA */
-		memcpy(hdr.addr1, skb->data, ETH_ALEN);
-		memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
-		memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
-		hdrlen = 24;
-	} else if (sdata->type == IEEE80211_IF_TYPE_WDS) {
-		fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS;
-		/* RA TA DA SA */
-		memcpy(hdr.addr1, sdata->u.wds.remote_addr, ETH_ALEN);
-		memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
-		memcpy(hdr.addr3, skb->data, ETH_ALEN);
-		memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
-		hdrlen = 30;
-	} else if (sdata->type == IEEE80211_IF_TYPE_STA) {
-		fc |= IEEE80211_FCTL_TODS;
-		/* BSSID SA DA */
-		memcpy(hdr.addr1, sdata->u.sta.bssid, ETH_ALEN);
-		memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
-		memcpy(hdr.addr3, skb->data, ETH_ALEN);
-		hdrlen = 24;
-	} else if (sdata->type == IEEE80211_IF_TYPE_IBSS) {
-		/* DA SA BSSID */
-		memcpy(hdr.addr1, skb->data, ETH_ALEN);
-		memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
-		memcpy(hdr.addr3, sdata->u.sta.bssid, ETH_ALEN);
-		hdrlen = 24;
-	} else {
-		ret = 0;
-		goto fail;
-	}
-
-	/* receiver is QoS enabled, use a QoS type frame */
-	sta = sta_info_get(local, hdr.addr1);
-	if (sta) {
-		if (sta->flags & WLAN_STA_WME) {
-			fc |= IEEE80211_STYPE_QOS_DATA;
-			hdrlen += 2;
+			fi->datarate = htonl(0);
+			fi->preamble = htonl(0);
 		}
-		sta_info_put(sta);
-	}
 
-	hdr.frame_control = cpu_to_le16(fc);
-	hdr.duration_id = 0;
-	hdr.seq_ctrl = 0;
-
-	skip_header_bytes = ETH_HLEN;
-	if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) {
-		encaps_data = bridge_tunnel_header;
-		encaps_len = sizeof(bridge_tunnel_header);
-		skip_header_bytes -= 2;
-	} else if (ethertype >= 0x600) {
-		encaps_data = rfc1042_header;
-		encaps_len = sizeof(rfc1042_header);
-		skip_header_bytes -= 2;
+		fi->antenna = htonl(status->antenna);
+		fi->priority = htonl(0xffffffff); /* no clue */
+		fi->ssi_type = htonl(ieee80211_ssi_raw);
+		fi->ssi_signal = htonl(status->ssi);
+		fi->ssi_noise = 0x00000000;
+		fi->encoding = 0;
 	} else {
-		encaps_data = NULL;
-		encaps_len = 0;
-	}
-
-	skb_pull(skb, skip_header_bytes);
-	nh_pos -= skip_header_bytes;
-	h_pos -= skip_header_bytes;
-
-	/* TODO: implement support for fragments so that there is no need to
-	 * reallocate and copy payload; it might be enough to support one
-	 * extra fragment that would be copied in the beginning of the frame
-	 * data.. anyway, it would be nice to include this into skb structure
-	 * somehow
-	 *
-	 * There are few options for this:
-	 * use skb->cb as an extra space for 802.11 header
-	 * allocate new buffer if not enough headroom
-	 * make sure that there is enough headroom in every skb by increasing
-	 * build in headroom in __dev_alloc_skb() (linux/skbuff.h) and
-	 * alloc_skb() (net/core/skbuff.c)
-	 */
-	head_need = hdrlen + encaps_len + local->tx_headroom;
-	head_need -= skb_headroom(skb);
-
-	/* We are going to modify skb data, so make a copy of it if happens to
-	 * be cloned. This could happen, e.g., with Linux bridge code passing
-	 * us broadcast frames. */
-
-	if (head_need > 0 || skb_cloned(skb)) {
-#if 0
-		printk(KERN_DEBUG "%s: need to reallocate buffer for %d bytes "
-		       "of headroom\n", dev->name, head_need);
-#endif
-
-		if (skb_cloned(skb))
-			I802_DEBUG_INC(local->tx_expand_skb_head_cloned);
-		else
-			I802_DEBUG_INC(local->tx_expand_skb_head);
-		/* Since we have to reallocate the buffer, make sure that there
-		 * is enough room for possible WEP IV/ICV and TKIP (8 bytes
-		 * before payload and 12 after). */
-		if (pskb_expand_head(skb, (head_need > 0 ? head_need + 8 : 8),
-				     12, GFP_ATOMIC)) {
-			printk(KERN_DEBUG "%s: failed to reallocate TX buffer"
-			       "\n", dev->name);
-			goto fail;
-		}
-	}
+		/* clear everything because we really don't know.
+		 * the msg_type field isn't present on monitor frames
+		 * so we don't know whether it will be present or not,
+		 * but it's ok to not clear it since it'll be assigned
+		 * anyway */
+		memset(fi, 0, sizeof(*fi) - sizeof(fi->msg_type));
 
-	if (encaps_data) {
-		memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len);
-		nh_pos += encaps_len;
-		h_pos += encaps_len;
+		fi->ssi_type = htonl(ieee80211_ssi_none);
 	}
-	memcpy(skb_push(skb, hdrlen), &hdr, hdrlen);
-	nh_pos += hdrlen;
-	h_pos += hdrlen;
-
-	pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
-	memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
-	pkt_data->ifindex = dev->ifindex;
-	pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT);
-	pkt_data->do_not_encrypt = no_encrypt;
-
-	skb->dev = local->mdev;
-	sdata->stats.tx_packets++;
-	sdata->stats.tx_bytes += skb->len;
-
-	/* Update skb pointers to various headers since this modified frame
-	 * is going to go through Linux networking code that may potentially
-	 * need things like pointer to IP header. */
-	skb_set_mac_header(skb, 0);
-	skb_set_network_header(skb, nh_pos);
-	skb_set_transport_header(skb, h_pos);
-
-	dev->trans_start = jiffies;
-	dev_queue_xmit(skb);
-
-	return 0;
-
- fail:
-	if (!ret)
-		dev_kfree_skb(skb);
-
-	return ret;
+	fi->version = htonl(IEEE80211_FI_VERSION);
+	fi->length = cpu_to_be32(sizeof(*fi) - sizeof(fi->msg_type));
 }
 
-
-/*
- * This is the transmit routine for the 802.11 type interfaces
- * called by upper layers of the linux networking
- * stack when it has a frame to transmit
- */
-static int
-ieee80211_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev)
+/* this routine is actually not just for this, but also
+ * for pushing fake 'management' frames into userspace.
+ * it shall be replaced by a netlink-based system. */
+void
+ieee80211_rx_mgmt(struct ieee80211_local *local, struct sk_buff *skb,
+		  struct ieee80211_rx_status *status, u32 msg_type)
 {
+	struct ieee80211_frame_info *fi;
+	const size_t hlen = sizeof(struct ieee80211_frame_info);
 	struct ieee80211_sub_if_data *sdata;
-	struct ieee80211_tx_packet_data *pkt_data;
-	struct ieee80211_hdr *hdr;
-	u16 fc;
 
-	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	skb->dev = local->apdev;
 
-	if (skb->len < 10) {
-		dev_kfree_skb(skb);
-		return 0;
-	}
+	sdata = IEEE80211_DEV_TO_SUB_IF(local->apdev);
 
-	if (skb_headroom(skb) < sdata->local->tx_headroom) {
-		if (pskb_expand_head(skb, sdata->local->tx_headroom,
-				     0, GFP_ATOMIC)) {
+	if (skb_headroom(skb) < hlen) {
+		I802_DEBUG_INC(local->rx_expand_skb_head);
+		if (pskb_expand_head(skb, hlen, 0, GFP_ATOMIC)) {
 			dev_kfree_skb(skb);
-			return 0;
+			return;
 		}
 	}
 
-	hdr = (struct ieee80211_hdr *) skb->data;
-	fc = le16_to_cpu(hdr->frame_control);
-
-	pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
-	memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
-	pkt_data->ifindex = sdata->dev->ifindex;
-	pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT);
-
-	skb->priority = 20; /* use hardcoded priority for mgmt TX queue */
-	skb->dev = sdata->local->mdev;
-
-	/*
-	 * We're using the protocol field of the the frame control header
-	 * to request TX callback for hostapd. BIT(1) is checked.
-	 */
-	if ((fc & BIT(1)) == BIT(1)) {
-		pkt_data->req_tx_status = 1;
-		fc &= ~BIT(1);
-		hdr->frame_control = cpu_to_le16(fc);
-	}
-
-	pkt_data->do_not_encrypt = !(fc & IEEE80211_FCTL_PROTECTED);
-
-	sdata->stats.tx_packets++;
-	sdata->stats.tx_bytes += skb->len;
-
-	dev_queue_xmit(skb);
-
-	return 0;
-}
-
+	fi = (struct ieee80211_frame_info *) skb_push(skb, hlen);
 
-static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
-				     struct ieee80211_if_ap *bss,
-				     struct sk_buff *skb)
-{
-	u8 *pos, *tim;
-	int aid0 = 0;
-	int i, have_bits = 0, n1, n2;
-
-	/* Generate bitmap for TIM only if there are any STAs in power save
-	 * mode. */
-	spin_lock_bh(&local->sta_lock);
-	if (atomic_read(&bss->num_sta_ps) > 0)
-		/* in the hope that this is faster than
-		 * checking byte-for-byte */
-		have_bits = !bitmap_empty((unsigned long*)bss->tim,
-					  IEEE80211_MAX_AID+1);
-
-	if (bss->dtim_count == 0)
-		bss->dtim_count = bss->dtim_period - 1;
-	else
-		bss->dtim_count--;
-
-	tim = pos = (u8 *) skb_put(skb, 6);
-	*pos++ = WLAN_EID_TIM;
-	*pos++ = 4;
-	*pos++ = bss->dtim_count;
-	*pos++ = bss->dtim_period;
-
-	if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf))
-		aid0 = 1;
-
-	if (have_bits) {
-		/* Find largest even number N1 so that bits numbered 1 through
-		 * (N1 x 8) - 1 in the bitmap are 0 and number N2 so that bits
-		 * (N2 + 1) x 8 through 2007 are 0. */
-		n1 = 0;
-		for (i = 0; i < IEEE80211_MAX_TIM_LEN; i++) {
-			if (bss->tim[i]) {
-				n1 = i & 0xfe;
-				break;
-			}
-		}
-		n2 = n1;
-		for (i = IEEE80211_MAX_TIM_LEN - 1; i >= n1; i--) {
-			if (bss->tim[i]) {
-				n2 = i;
-				break;
-			}
-		}
+	ieee80211_fill_frame_info(local, fi, status);
+	fi->msg_type = htonl(msg_type);
 
-		/* Bitmap control */
-		*pos++ = n1 | aid0;
-		/* Part Virt Bitmap */
-		memcpy(pos, bss->tim + n1, n2 - n1 + 1);
+	sdata->stats.rx_packets++;
+	sdata->stats.rx_bytes += skb->len;
 
-		tim[1] = n2 - n1 + 4;
-		skb_put(skb, n2 - n1);
-	} else {
-		*pos++ = aid0; /* Bitmap control */
-		*pos++ = 0; /* Part Virt Bitmap */
-	}
-	spin_unlock_bh(&local->sta_lock);
+	skb_set_mac_header(skb, 0);
+	skb->ip_summed = CHECKSUM_UNNECESSARY;
+	skb->pkt_type = PACKET_OTHERHOST;
+	skb->protocol = htons(ETH_P_802_2);
+	memset(skb->cb, 0, sizeof(skb->cb));
+	netif_rx(skb);
 }
 
-
-struct sk_buff * ieee80211_beacon_get(struct ieee80211_hw *hw, int if_id,
-				      struct ieee80211_tx_control *control)
+int ieee80211_radar_status(struct ieee80211_hw *hw, int channel,
+			   int radar, int radar_type)
 {
-	struct ieee80211_local *local = hw_to_local(hw);
 	struct sk_buff *skb;
-	struct net_device *bdev;
-	struct ieee80211_sub_if_data *sdata = NULL;
-	struct ieee80211_if_ap *ap = NULL;
-	struct ieee80211_rate *rate;
-	struct rate_control_extra extra;
-	u8 *b_head, *b_tail;
-	int bh_len, bt_len;
-
-	bdev = dev_get_by_index(if_id);
-	if (bdev) {
-		sdata = IEEE80211_DEV_TO_SUB_IF(bdev);
-		ap = &sdata->u.ap;
-		dev_put(bdev);
-	}
-
-	if (!ap || sdata->type != IEEE80211_IF_TYPE_AP ||
-	    !ap->beacon_head) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-		if (net_ratelimit())
-			printk(KERN_DEBUG "no beacon data avail for idx=%d "
-			       "(%s)\n", if_id, bdev ? bdev->name : "N/A");
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
-		return NULL;
-	}
-
-	/* Assume we are generating the normal beacon locally */
-	b_head = ap->beacon_head;
-	b_tail = ap->beacon_tail;
-	bh_len = ap->beacon_head_len;
-	bt_len = ap->beacon_tail_len;
-
-	skb = dev_alloc_skb(local->tx_headroom +
-		bh_len + bt_len + 256 /* maximum TIM len */);
-	if (!skb)
-		return NULL;
-
-	skb_reserve(skb, local->tx_headroom);
-	memcpy(skb_put(skb, bh_len), b_head, bh_len);
-
-	ieee80211_include_sequence(sdata, (struct ieee80211_hdr *)skb->data);
-
-	ieee80211_beacon_add_tim(local, ap, skb);
-
-	if (b_tail) {
-		memcpy(skb_put(skb, bt_len), b_tail, bt_len);
-	}
-
-	if (control) {
-		memset(&extra, 0, sizeof(extra));
-		extra.mode = local->oper_hw_mode;
-
-		rate = rate_control_get_rate(local, local->mdev, skb, &extra);
-		if (!rate) {
-			if (net_ratelimit()) {
-				printk(KERN_DEBUG "%s: ieee80211_beacon_get: no rate "
-				       "found\n", local->mdev->name);
-			}
-			dev_kfree_skb(skb);
-			return NULL;
-		}
-
-		control->tx_rate = (local->short_preamble &&
-				    (rate->flags & IEEE80211_RATE_PREAMBLE2)) ?
-			rate->val2 : rate->val;
-		control->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
-		control->power_level = local->hw.conf.power_level;
-		control->flags |= IEEE80211_TXCTL_NO_ACK;
-		control->retry_limit = 1;
-		control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK;
-	}
-
-	ap->num_beacons++;
-	return skb;
-}
-EXPORT_SYMBOL(ieee80211_beacon_get);
-
-__le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
-			      size_t frame_len,
-			      const struct ieee80211_tx_control *frame_txctl)
-{
+	struct ieee80211_radar_info *msg;
 	struct ieee80211_local *local = hw_to_local(hw);
-	struct ieee80211_rate *rate;
-	int short_preamble = local->short_preamble;
-	int erp;
-	u16 dur;
-
-	rate = frame_txctl->rts_rate;
-	erp = !!(rate->flags & IEEE80211_RATE_ERP);
-
-	/* CTS duration */
-	dur = ieee80211_frame_duration(local, 10, rate->rate,
-				       erp, short_preamble);
-	/* Data frame duration */
-	dur += ieee80211_frame_duration(local, frame_len, rate->rate,
-					erp, short_preamble);
-	/* ACK duration */
-	dur += ieee80211_frame_duration(local, 10, rate->rate,
-					erp, short_preamble);
-
-	return cpu_to_le16(dur);
-}
-EXPORT_SYMBOL(ieee80211_rts_duration);
 
+	if (!local->apdev)
+		return 0;
 
-__le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
-				    size_t frame_len,
-				    const struct ieee80211_tx_control *frame_txctl)
-{
-	struct ieee80211_local *local = hw_to_local(hw);
-	struct ieee80211_rate *rate;
-	int short_preamble = local->short_preamble;
-	int erp;
-	u16 dur;
-
-	rate = frame_txctl->rts_rate;
-	erp = !!(rate->flags & IEEE80211_RATE_ERP);
-
-	/* Data frame duration */
-	dur = ieee80211_frame_duration(local, frame_len, rate->rate,
-				       erp, short_preamble);
-	if (!(frame_txctl->flags & IEEE80211_TXCTL_NO_ACK)) {
-		/* ACK duration */
-		dur += ieee80211_frame_duration(local, 10, rate->rate,
-						erp, short_preamble);
-	}
-
-	return cpu_to_le16(dur);
-}
-EXPORT_SYMBOL(ieee80211_ctstoself_duration);
+	skb = dev_alloc_skb(sizeof(struct ieee80211_frame_info) +
+			    sizeof(struct ieee80211_radar_info));
 
-void ieee80211_rts_get(struct ieee80211_hw *hw,
-		       const void *frame, size_t frame_len,
-		       const struct ieee80211_tx_control *frame_txctl,
-		       struct ieee80211_rts *rts)
-{
-	const struct ieee80211_hdr *hdr = frame;
-	u16 fctl;
-
-	fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS;
-	rts->frame_control = cpu_to_le16(fctl);
-	rts->duration = ieee80211_rts_duration(hw, frame_len, frame_txctl);
-	memcpy(rts->ra, hdr->addr1, sizeof(rts->ra));
-	memcpy(rts->ta, hdr->addr2, sizeof(rts->ta));
-}
-EXPORT_SYMBOL(ieee80211_rts_get);
+	if (!skb)
+		return -ENOMEM;
+	skb_reserve(skb, sizeof(struct ieee80211_frame_info));
 
-void ieee80211_ctstoself_get(struct ieee80211_hw *hw,
-			     const void *frame, size_t frame_len,
-			     const struct ieee80211_tx_control *frame_txctl,
-			     struct ieee80211_cts *cts)
-{
-	const struct ieee80211_hdr *hdr = frame;
-	u16 fctl;
+	msg = (struct ieee80211_radar_info *)
+		skb_put(skb, sizeof(struct ieee80211_radar_info));
+	msg->channel = channel;
+	msg->radar = radar;
+	msg->radar_type = radar_type;
 
-	fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS;
-	cts->frame_control = cpu_to_le16(fctl);
-	cts->duration = ieee80211_ctstoself_duration(hw, frame_len, frame_txctl);
-	memcpy(cts->ra, hdr->addr1, sizeof(cts->ra));
+	ieee80211_rx_mgmt(local, skb, NULL, ieee80211_msg_radar);
+	return 0;
 }
-EXPORT_SYMBOL(ieee80211_ctstoself_get);
+EXPORT_SYMBOL(ieee80211_radar_status);
 
-struct sk_buff *
-ieee80211_get_buffered_bc(struct ieee80211_hw *hw, int if_id,
-			  struct ieee80211_tx_control *control)
+void ieee80211_key_threshold_notify(struct net_device *dev,
+				    struct ieee80211_key *key,
+				    struct sta_info *sta)
 {
-	struct ieee80211_local *local = hw_to_local(hw);
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct sk_buff *skb;
-	struct sta_info *sta;
-	ieee80211_tx_handler *handler;
-	struct ieee80211_txrx_data tx;
-	ieee80211_txrx_result res = TXRX_DROP;
-	struct net_device *bdev;
-	struct ieee80211_sub_if_data *sdata;
-	struct ieee80211_if_ap *bss = NULL;
-
-	bdev = dev_get_by_index(if_id);
-	if (bdev) {
-		sdata = IEEE80211_DEV_TO_SUB_IF(bdev);
-		bss = &sdata->u.ap;
-		dev_put(bdev);
-	}
-	if (!bss || sdata->type != IEEE80211_IF_TYPE_AP || !bss->beacon_head)
-		return NULL;
-
-	if (bss->dtim_count != 0)
-		return NULL; /* send buffered bc/mc only after DTIM beacon */
-	memset(control, 0, sizeof(*control));
-	while (1) {
-		skb = skb_dequeue(&bss->ps_bc_buf);
-		if (!skb)
-			return NULL;
-		local->total_ps_buffered--;
-
-		if (!skb_queue_empty(&bss->ps_bc_buf) && skb->len >= 2) {
-			struct ieee80211_hdr *hdr =
-				(struct ieee80211_hdr *) skb->data;
-			/* more buffered multicast/broadcast frames ==> set
-			 * MoreData flag in IEEE 802.11 header to inform PS
-			 * STAs */
-			hdr->frame_control |=
-				cpu_to_le16(IEEE80211_FCTL_MOREDATA);
-		}
-
-		if (ieee80211_tx_prepare(&tx, skb, local->mdev, control) == 0)
-			break;
-		dev_kfree_skb_any(skb);
-	}
-	sta = tx.sta;
-	tx.u.tx.ps_buffered = 1;
+	struct ieee80211_msg_key_notification *msg;
 
-	for (handler = local->tx_handlers; *handler != NULL; handler++) {
-		res = (*handler)(&tx);
-		if (res == TXRX_DROP || res == TXRX_QUEUED)
-			break;
-	}
-	dev_put(tx.dev);
-	skb = tx.skb; /* handlers are allowed to change skb */
+	/* if no one will get it anyway, don't even allocate it.
+	 * unlikely because this is only relevant for APs
+	 * where the device must be open... */
+	if (unlikely(!local->apdev))
+		return;
 
-	if (res == TXRX_DROP) {
-		I802_DEBUG_INC(local->tx_handlers_drop);
-		dev_kfree_skb(skb);
-		skb = NULL;
-	} else if (res == TXRX_QUEUED) {
-		I802_DEBUG_INC(local->tx_handlers_queued);
-		skb = NULL;
-	}
+	skb = dev_alloc_skb(sizeof(struct ieee80211_frame_info) +
+			    sizeof(struct ieee80211_msg_key_notification));
+	if (!skb)
+		return;
 
+	skb_reserve(skb, sizeof(struct ieee80211_frame_info));
+	msg = (struct ieee80211_msg_key_notification *)
+		skb_put(skb, sizeof(struct ieee80211_msg_key_notification));
+	msg->tx_rx_count = key->tx_rx_count;
+	memcpy(msg->ifname, dev->name, IFNAMSIZ);
 	if (sta)
-		sta_info_put(sta);
-
-	return skb;
-}
-EXPORT_SYMBOL(ieee80211_get_buffered_bc);
-
-static int __ieee80211_if_config(struct net_device *dev,
-				 struct sk_buff *beacon,
-				 struct ieee80211_tx_control *control)
-{
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct ieee80211_if_conf conf;
-	static u8 scan_bssid[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-
-	if (!local->ops->config_interface || !netif_running(dev))
-		return 0;
+		memcpy(msg->addr, sta->addr, ETH_ALEN);
+	else
+		memset(msg->addr, 0xff, ETH_ALEN);
 
-	memset(&conf, 0, sizeof(conf));
-	conf.type = sdata->type;
-	if (sdata->type == IEEE80211_IF_TYPE_STA ||
-	    sdata->type == IEEE80211_IF_TYPE_IBSS) {
-		if (local->sta_scanning &&
-		    local->scan_dev == dev)
-			conf.bssid = scan_bssid;
-		else
-			conf.bssid = sdata->u.sta.bssid;
-		conf.ssid = sdata->u.sta.ssid;
-		conf.ssid_len = sdata->u.sta.ssid_len;
-		conf.generic_elem = sdata->u.sta.extra_ie;
-		conf.generic_elem_len = sdata->u.sta.extra_ie_len;
-	} else if (sdata->type == IEEE80211_IF_TYPE_AP) {
-		conf.ssid = sdata->u.ap.ssid;
-		conf.ssid_len = sdata->u.ap.ssid_len;
-		conf.generic_elem = sdata->u.ap.generic_elem;
-		conf.generic_elem_len = sdata->u.ap.generic_elem_len;
-		conf.beacon = beacon;
-		conf.beacon_control = control;
-	}
-	return local->ops->config_interface(local_to_hw(local),
-					   dev->ifindex, &conf);
-}
+	key->tx_rx_count = 0;
 
-int ieee80211_if_config(struct net_device *dev)
-{
-	return __ieee80211_if_config(dev, NULL, NULL);
+	ieee80211_rx_mgmt(local, skb, NULL,
+			  ieee80211_msg_key_threshold_notification);
 }
 
-int ieee80211_if_config_beacon(struct net_device *dev)
+static int ieee80211_mgmt_open(struct net_device *dev)
 {
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct ieee80211_tx_control control;
-	struct sk_buff *skb;
 
-	if (!(local->hw.flags & IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE))
-		return 0;
-	skb = ieee80211_beacon_get(local_to_hw(local), dev->ifindex, &control);
-	if (!skb)
-		return -ENOMEM;
-	return __ieee80211_if_config(dev, skb, &control);
+	if (!netif_running(local->mdev))
+		return -EOPNOTSUPP;
+	return 0;
 }
 
-int ieee80211_hw_config(struct ieee80211_local *local)
+static int ieee80211_mgmt_stop(struct net_device *dev)
 {
-	struct ieee80211_hw_mode *mode;
-	struct ieee80211_channel *chan;
-	int ret = 0;
-
-	if (local->sta_scanning) {
-		chan = local->scan_channel;
-		mode = local->scan_hw_mode;
-	} else {
-		chan = local->oper_channel;
-		mode = local->oper_hw_mode;
-	}
-
-	local->hw.conf.channel = chan->chan;
-	local->hw.conf.channel_val = chan->val;
-	local->hw.conf.power_level = chan->power_level;
-	local->hw.conf.freq = chan->freq;
-	local->hw.conf.phymode = mode->mode;
-	local->hw.conf.antenna_max = chan->antenna_max;
-	local->hw.conf.chan = chan;
-	local->hw.conf.mode = mode;
-
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-	printk(KERN_DEBUG "HW CONFIG: channel=%d freq=%d "
-	       "phymode=%d\n", local->hw.conf.channel, local->hw.conf.freq,
-	       local->hw.conf.phymode);
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
-
-	if (local->ops->config)
-		ret = local->ops->config(local_to_hw(local), &local->hw.conf);
-
-	return ret;
+	return 0;
 }
 
-
-static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
+static int ieee80211_change_mtu_apdev(struct net_device *dev, int new_mtu)
 {
 	/* FIX: what would be proper limits for MTU?
-	 * This interface uses 802.3 frames. */
-	if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6) {
+	 * This interface uses 802.11 frames. */
+	if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN) {
 		printk(KERN_WARNING "%s: invalid MTU %d\n",
 		       dev->name, new_mtu);
 		return -EINVAL;
@@ -2388,12 +295,27 @@ static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
 	return 0;
 }
 
+void ieee80211_if_mgmt_setup(struct net_device *dev)
+{
+	ether_setup(dev);
+	dev->hard_start_xmit = ieee80211_mgmt_start_xmit;
+	dev->change_mtu = ieee80211_change_mtu_apdev;
+	dev->get_stats = ieee80211_get_stats;
+	dev->open = ieee80211_mgmt_open;
+	dev->stop = ieee80211_mgmt_stop;
+	dev->type = ARPHRD_IEEE80211_PRISM;
+	dev->hard_header_parse = header_parse_80211;
+	dev->uninit = ieee80211_if_reinit;
+	dev->destructor = ieee80211_if_free;
+}
 
-static int ieee80211_change_mtu_apdev(struct net_device *dev, int new_mtu)
+/* regular interfaces */
+
+static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
 {
 	/* FIX: what would be proper limits for MTU?
-	 * This interface uses 802.11 frames. */
-	if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN) {
+	 * This interface uses 802.3 frames. */
+	if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6) {
 		printk(KERN_WARNING "%s: invalid MTU %d\n",
 		       dev->name, new_mtu);
 		return -EINVAL;
@@ -2406,121 +328,6 @@ static int ieee80211_change_mtu_apdev(struct net_device *dev, int new_mtu)
 	return 0;
 }
 
-enum netif_tx_lock_class {
-	TX_LOCK_NORMAL,
-	TX_LOCK_MASTER,
-};
-
-static inline void netif_tx_lock_nested(struct net_device *dev, int subclass)
-{
-	spin_lock_nested(&dev->_xmit_lock, subclass);
-	dev->xmit_lock_owner = smp_processor_id();
-}
-
-static void ieee80211_set_multicast_list(struct net_device *dev)
-{
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-	unsigned short flags;
-
-	netif_tx_lock_nested(local->mdev, TX_LOCK_MASTER);
-	if (((dev->flags & IFF_ALLMULTI) != 0) ^ (sdata->allmulti != 0)) {
-		if (sdata->allmulti) {
-			sdata->allmulti = 0;
-			local->iff_allmultis--;
-		} else {
-			sdata->allmulti = 1;
-			local->iff_allmultis++;
-		}
-	}
-	if (((dev->flags & IFF_PROMISC) != 0) ^ (sdata->promisc != 0)) {
-		if (sdata->promisc) {
-			sdata->promisc = 0;
-			local->iff_promiscs--;
-		} else {
-			sdata->promisc = 1;
-			local->iff_promiscs++;
-		}
-	}
-	if (dev->mc_count != sdata->mc_count) {
-		local->mc_count = local->mc_count - sdata->mc_count +
-				  dev->mc_count;
-		sdata->mc_count = dev->mc_count;
-	}
-	if (local->ops->set_multicast_list) {
-		flags = local->mdev->flags;
-		if (local->iff_allmultis)
-			flags |= IFF_ALLMULTI;
-		if (local->iff_promiscs)
-			flags |= IFF_PROMISC;
-		read_lock(&local->sub_if_lock);
-		local->ops->set_multicast_list(local_to_hw(local), flags,
-					      local->mc_count);
-		read_unlock(&local->sub_if_lock);
-	}
-	netif_tx_unlock(local->mdev);
-}
-
-struct dev_mc_list *ieee80211_get_mc_list_item(struct ieee80211_hw *hw,
-					       struct dev_mc_list *prev,
-					       void **ptr)
-{
-	struct ieee80211_local *local = hw_to_local(hw);
-	struct ieee80211_sub_if_data *sdata = *ptr;
-	struct dev_mc_list *mc;
-
-	if (!prev) {
-		WARN_ON(sdata);
-		sdata = NULL;
-	}
-	if (!prev || !prev->next) {
-		if (sdata)
-			sdata = list_entry(sdata->list.next,
-					   struct ieee80211_sub_if_data, list);
-		else
-			sdata = list_entry(local->sub_if_list.next,
-					   struct ieee80211_sub_if_data, list);
-		if (&sdata->list != &local->sub_if_list)
-			mc = sdata->dev->mc_list;
-		else
-			mc = NULL;
-	} else
-		mc = prev->next;
-
-	*ptr = sdata;
-	return mc;
-}
-EXPORT_SYMBOL(ieee80211_get_mc_list_item);
-
-static struct net_device_stats *ieee80211_get_stats(struct net_device *dev)
-{
-	struct ieee80211_sub_if_data *sdata;
-	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-	return &(sdata->stats);
-}
-
-static void ieee80211_if_shutdown(struct net_device *dev)
-{
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
-	ASSERT_RTNL();
-	switch (sdata->type) {
-	case IEEE80211_IF_TYPE_STA:
-	case IEEE80211_IF_TYPE_IBSS:
-		sdata->u.sta.state = IEEE80211_DISABLED;
-		del_timer_sync(&sdata->u.sta.timer);
-		skb_queue_purge(&sdata->u.sta.skb_queue);
-		if (!local->ops->hw_scan &&
-		    local->scan_dev == sdata->dev) {
-			local->sta_scanning = 0;
-			cancel_delayed_work(&local->scan_work);
-		}
-		flush_workqueue(local->hw.workqueue);
-		break;
-	}
-}
-
 static inline int identical_mac_addr_allowed(int type1, int type2)
 {
 	return (type1 == IEEE80211_IF_TYPE_MNTR ||
@@ -2537,51 +344,6 @@ static inline int identical_mac_addr_allowed(int type1, int type2)
 		  type2 == IEEE80211_IF_TYPE_VLAN)));
 }
 
-static int ieee80211_master_open(struct net_device *dev)
-{
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct ieee80211_sub_if_data *sdata;
-	int res = -EOPNOTSUPP;
-
-	read_lock(&local->sub_if_lock);
-	list_for_each_entry(sdata, &local->sub_if_list, list) {
-		if (sdata->dev != dev && netif_running(sdata->dev)) {
-			res = 0;
-			break;
-		}
-	}
-	read_unlock(&local->sub_if_lock);
-	return res;
-}
-
-static int ieee80211_master_stop(struct net_device *dev)
-{
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct ieee80211_sub_if_data *sdata;
-
-	read_lock(&local->sub_if_lock);
-	list_for_each_entry(sdata, &local->sub_if_list, list)
-		if (sdata->dev != dev && netif_running(sdata->dev))
-			dev_close(sdata->dev);
-	read_unlock(&local->sub_if_lock);
-
-	return 0;
-}
-
-static int ieee80211_mgmt_open(struct net_device *dev)
-{
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-
-	if (!netif_running(local->mdev))
-		return -EOPNOTSUPP;
-	return 0;
-}
-
-static int ieee80211_mgmt_stop(struct net_device *dev)
-{
-	return 0;
-}
-
 /* Check if running monitor interfaces should go to a "soft monitor" mode
  * and switch them if necessary. */
 static inline void ieee80211_start_soft_monitor(struct ieee80211_local *local)
@@ -2613,6 +375,18 @@ static void ieee80211_start_hard_monitor(struct ieee80211_local *local)
 	}
 }
 
+static void ieee80211_if_open(struct net_device *dev)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+	switch (sdata->type) {
+	case IEEE80211_IF_TYPE_STA:
+	case IEEE80211_IF_TYPE_IBSS:
+		sdata->u.sta.prev_bssid_set = 0;
+		break;
+	}
+}
+
 static int ieee80211_open(struct net_device *dev)
 {
 	struct ieee80211_sub_if_data *sdata, *nsdata;
@@ -2646,11 +420,15 @@ static int ieee80211_open(struct net_device *dev)
 		local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP;
 		return 0;
 	}
+	ieee80211_if_open(dev);
 	ieee80211_start_soft_monitor(local);
 
 	conf.if_id = dev->ifindex;
 	conf.type = sdata->type;
-	conf.mac_addr = dev->dev_addr;
+	if (sdata->type == IEEE80211_IF_TYPE_MNTR)
+		conf.mac_addr = NULL;
+	else
+		conf.mac_addr = dev->dev_addr;
 	res = local->ops->add_interface(local_to_hw(local), &conf);
 	if (res) {
 		if (sdata->type == IEEE80211_IF_TYPE_MNTR)
@@ -2689,8 +467,10 @@ static int ieee80211_open(struct net_device *dev)
 	if (sdata->type == IEEE80211_IF_TYPE_MNTR) {
 		local->monitors++;
 		local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP;
-	} else
+	} else {
 		ieee80211_if_config(dev);
+		ieee80211_reset_erp_info(dev);
+	}
 
 	if (sdata->type == IEEE80211_IF_TYPE_STA &&
 	    !local->user_space_mlme)
@@ -2702,6 +482,27 @@ static int ieee80211_open(struct net_device *dev)
 	return 0;
 }
 
+static void ieee80211_if_shutdown(struct net_device *dev)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+	ASSERT_RTNL();
+	switch (sdata->type) {
+	case IEEE80211_IF_TYPE_STA:
+	case IEEE80211_IF_TYPE_IBSS:
+		sdata->u.sta.state = IEEE80211_DISABLED;
+		del_timer_sync(&sdata->u.sta.timer);
+		skb_queue_purge(&sdata->u.sta.skb_queue);
+		if (!local->ops->hw_scan &&
+		    local->scan_dev == sdata->dev) {
+			local->sta_scanning = 0;
+			cancel_delayed_work(&local->scan_work);
+		}
+		flush_workqueue(local->hw.workqueue);
+		break;
+	}
+}
 
 static int ieee80211_stop(struct net_device *dev)
 {
@@ -2755,1524 +556,254 @@ static int ieee80211_stop(struct net_device *dev)
 	return 0;
 }
 
+enum netif_tx_lock_class {
+	TX_LOCK_NORMAL,
+	TX_LOCK_MASTER,
+};
 
-static int header_parse_80211(struct sk_buff *skb, unsigned char *haddr)
-{
-	memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */
-	return ETH_ALEN;
-}
-
-static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
+static inline void netif_tx_lock_nested(struct net_device *dev, int subclass)
 {
-	return compare_ether_addr(raddr, addr) == 0 ||
-	       is_broadcast_ether_addr(raddr);
+	spin_lock_nested(&dev->_xmit_lock, subclass);
+	dev->xmit_lock_owner = smp_processor_id();
 }
 
-
-static ieee80211_txrx_result
-ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
+static void ieee80211_set_multicast_list(struct net_device *dev)
 {
-	struct net_device *dev = rx->dev;
-	struct ieee80211_local *local = rx->local;
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
-	u16 fc, hdrlen, ethertype;
-	u8 *payload;
-	u8 dst[ETH_ALEN];
-	u8 src[ETH_ALEN];
-	struct sk_buff *skb = rx->skb, *skb2;
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	unsigned short flags;
 
-	fc = rx->fc;
-	if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA))
-		return TXRX_CONTINUE;
-
-	if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
-		return TXRX_DROP;
-
-	hdrlen = ieee80211_get_hdrlen(fc);
-
-	/* convert IEEE 802.11 header + possible LLC headers into Ethernet
-	 * header
-	 * IEEE 802.11 address fields:
-	 * ToDS FromDS Addr1 Addr2 Addr3 Addr4
-	 *   0     0   DA    SA    BSSID n/a
-	 *   0     1   DA    BSSID SA    n/a
-	 *   1     0   BSSID SA    DA    n/a
-	 *   1     1   RA    TA    DA    SA
-	 */
-
-	switch (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
-	case IEEE80211_FCTL_TODS:
-		/* BSSID SA DA */
-		memcpy(dst, hdr->addr3, ETH_ALEN);
-		memcpy(src, hdr->addr2, ETH_ALEN);
-
-		if (unlikely(sdata->type != IEEE80211_IF_TYPE_AP &&
-			     sdata->type != IEEE80211_IF_TYPE_VLAN)) {
-			printk(KERN_DEBUG "%s: dropped ToDS frame (BSSID="
-			       MAC_FMT " SA=" MAC_FMT " DA=" MAC_FMT ")\n",
-			       dev->name, MAC_ARG(hdr->addr1),
-			       MAC_ARG(hdr->addr2), MAC_ARG(hdr->addr3));
-			return TXRX_DROP;
-		}
-		break;
-	case (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
-		/* RA TA DA SA */
-		memcpy(dst, hdr->addr3, ETH_ALEN);
-		memcpy(src, hdr->addr4, ETH_ALEN);
-
-		if (unlikely(sdata->type != IEEE80211_IF_TYPE_WDS)) {
-			printk(KERN_DEBUG "%s: dropped FromDS&ToDS frame (RA="
-			       MAC_FMT " TA=" MAC_FMT " DA=" MAC_FMT " SA="
-			       MAC_FMT ")\n",
-			       rx->dev->name, MAC_ARG(hdr->addr1),
-			       MAC_ARG(hdr->addr2), MAC_ARG(hdr->addr3),
-			       MAC_ARG(hdr->addr4));
-			return TXRX_DROP;
-		}
-		break;
-	case IEEE80211_FCTL_FROMDS:
-		/* DA BSSID SA */
-		memcpy(dst, hdr->addr1, ETH_ALEN);
-		memcpy(src, hdr->addr3, ETH_ALEN);
-
-		if (sdata->type != IEEE80211_IF_TYPE_STA) {
-			return TXRX_DROP;
-		}
-		break;
-	case 0:
-		/* DA SA BSSID */
-		memcpy(dst, hdr->addr1, ETH_ALEN);
-		memcpy(src, hdr->addr2, ETH_ALEN);
-
-		if (sdata->type != IEEE80211_IF_TYPE_IBSS) {
-			if (net_ratelimit()) {
-				printk(KERN_DEBUG "%s: dropped IBSS frame (DA="
-				       MAC_FMT " SA=" MAC_FMT " BSSID=" MAC_FMT
-				       ")\n",
-				       dev->name, MAC_ARG(hdr->addr1),
-				       MAC_ARG(hdr->addr2),
-				       MAC_ARG(hdr->addr3));
-			}
-			return TXRX_DROP;
-		}
-		break;
-	}
-
-	payload = skb->data + hdrlen;
-
-	if (unlikely(skb->len - hdrlen < 8)) {
-		if (net_ratelimit()) {
-			printk(KERN_DEBUG "%s: RX too short data frame "
-			       "payload\n", dev->name);
+	netif_tx_lock_nested(local->mdev, TX_LOCK_MASTER);
+	if (((dev->flags & IFF_ALLMULTI) != 0) ^ (sdata->allmulti != 0)) {
+		if (sdata->allmulti) {
+			sdata->allmulti = 0;
+			local->iff_allmultis--;
+		} else {
+			sdata->allmulti = 1;
+			local->iff_allmultis++;
 		}
-		return TXRX_DROP;
-	}
-
-	ethertype = (payload[6] << 8) | payload[7];
-
-	if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
-		    ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
-		   compare_ether_addr(payload, bridge_tunnel_header) == 0)) {
-		/* remove RFC1042 or Bridge-Tunnel encapsulation and
-		 * replace EtherType */
-		skb_pull(skb, hdrlen + 6);
-		memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
-		memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
-	} else {
-		struct ethhdr *ehdr;
-		__be16 len;
-		skb_pull(skb, hdrlen);
-		len = htons(skb->len);
-		ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
-		memcpy(ehdr->h_dest, dst, ETH_ALEN);
-		memcpy(ehdr->h_source, src, ETH_ALEN);
-		ehdr->h_proto = len;
 	}
-	skb->dev = dev;
-
-	skb2 = NULL;
-
-	sdata->stats.rx_packets++;
-	sdata->stats.rx_bytes += skb->len;
-
-	if (local->bridge_packets && (sdata->type == IEEE80211_IF_TYPE_AP
-	    || sdata->type == IEEE80211_IF_TYPE_VLAN) && rx->u.rx.ra_match) {
-		if (is_multicast_ether_addr(skb->data)) {
-			/* send multicast frames both to higher layers in
-			 * local net stack and back to the wireless media */
-			skb2 = skb_copy(skb, GFP_ATOMIC);
-			if (!skb2)
-				printk(KERN_DEBUG "%s: failed to clone "
-				       "multicast frame\n", dev->name);
+	if (((dev->flags & IFF_PROMISC) != 0) ^ (sdata->promisc != 0)) {
+		if (sdata->promisc) {
+			sdata->promisc = 0;
+			local->iff_promiscs--;
 		} else {
-			struct sta_info *dsta;
-			dsta = sta_info_get(local, skb->data);
-			if (dsta && !dsta->dev) {
-				printk(KERN_DEBUG "Station with null dev "
-				       "structure!\n");
-			} else if (dsta && dsta->dev == dev) {
-				/* Destination station is associated to this
-				 * AP, so send the frame directly to it and
-				 * do not pass the frame to local net stack.
-				 */
-				skb2 = skb;
-				skb = NULL;
-			}
-			if (dsta)
-				sta_info_put(dsta);
+			sdata->promisc = 1;
+			local->iff_promiscs++;
 		}
 	}
-
-	if (skb) {
-		/* deliver to local stack */
-		skb->protocol = eth_type_trans(skb, dev);
-		memset(skb->cb, 0, sizeof(skb->cb));
-		netif_rx(skb);
-	}
-
-	if (skb2) {
-		/* send to wireless media */
-		skb2->protocol = __constant_htons(ETH_P_802_3);
-		skb_set_network_header(skb2, 0);
-		skb_set_mac_header(skb2, 0);
-		dev_queue_xmit(skb2);
+	if (dev->mc_count != sdata->mc_count) {
+		local->mc_count = local->mc_count - sdata->mc_count +
+				  dev->mc_count;
+		sdata->mc_count = dev->mc_count;
 	}
-
-	return TXRX_QUEUED;
-}
-
-
-static struct ieee80211_rate *
-ieee80211_get_rate(struct ieee80211_local *local, int phymode, int hw_rate)
-{
-	struct ieee80211_hw_mode *mode;
-	int r;
-
-	list_for_each_entry(mode, &local->modes_list, list) {
-		if (mode->mode != phymode)
-			continue;
-		for (r = 0; r < mode->num_rates; r++) {
-			struct ieee80211_rate *rate = &mode->rates[r];
-			if (rate->val == hw_rate ||
-			    (rate->flags & IEEE80211_RATE_PREAMBLE2 &&
-			     rate->val2 == hw_rate))
-				return rate;
-		}
+	if (local->ops->set_multicast_list) {
+		flags = local->mdev->flags;
+		if (local->iff_allmultis)
+			flags |= IFF_ALLMULTI;
+		if (local->iff_promiscs)
+			flags |= IFF_PROMISC;
+		read_lock(&local->sub_if_lock);
+		local->ops->set_multicast_list(local_to_hw(local), flags,
+					      local->mc_count);
+		read_unlock(&local->sub_if_lock);
 	}
-
-	return NULL;
+	netif_tx_unlock(local->mdev);
 }
 
-static void
-ieee80211_fill_frame_info(struct ieee80211_local *local,
-			  struct ieee80211_frame_info *fi,
-			  struct ieee80211_rx_status *status)
+/* Must not be called for mdev and apdev */
+void ieee80211_if_setup(struct net_device *dev)
 {
-	if (status) {
-		struct timespec ts;
-		struct ieee80211_rate *rate;
-
-		jiffies_to_timespec(jiffies, &ts);
-		fi->hosttime = cpu_to_be64((u64) ts.tv_sec * 1000000 +
-					   ts.tv_nsec / 1000);
-		fi->mactime = cpu_to_be64(status->mactime);
-		switch (status->phymode) {
-		case MODE_IEEE80211A:
-			fi->phytype = htonl(ieee80211_phytype_ofdm_dot11_a);
-			break;
-		case MODE_IEEE80211B:
-			fi->phytype = htonl(ieee80211_phytype_dsss_dot11_b);
-			break;
-		case MODE_IEEE80211G:
-			fi->phytype = htonl(ieee80211_phytype_pbcc_dot11_g);
-			break;
-		case MODE_ATHEROS_TURBO:
-			fi->phytype =
-				htonl(ieee80211_phytype_dsss_dot11_turbo);
-			break;
-		default:
-			fi->phytype = htonl(0xAAAAAAAA);
-			break;
-		}
-		fi->channel = htonl(status->channel);
-		rate = ieee80211_get_rate(local, status->phymode,
-					  status->rate);
-		if (rate) {
-			fi->datarate = htonl(rate->rate);
-			if (rate->flags & IEEE80211_RATE_PREAMBLE2) {
-				if (status->rate == rate->val)
-					fi->preamble = htonl(2); /* long */
-				else if (status->rate == rate->val2)
-					fi->preamble = htonl(1); /* short */
-			} else
-				fi->preamble = htonl(0);
-		} else {
-			fi->datarate = htonl(0);
-			fi->preamble = htonl(0);
-		}
-
-		fi->antenna = htonl(status->antenna);
-		fi->priority = htonl(0xffffffff); /* no clue */
-		fi->ssi_type = htonl(ieee80211_ssi_raw);
-		fi->ssi_signal = htonl(status->ssi);
-		fi->ssi_noise = 0x00000000;
-		fi->encoding = 0;
-	} else {
-		/* clear everything because we really don't know.
-		 * the msg_type field isn't present on monitor frames
-		 * so we don't know whether it will be present or not,
-		 * but it's ok to not clear it since it'll be assigned
-		 * anyway */
-		memset(fi, 0, sizeof(*fi) - sizeof(fi->msg_type));
-
-		fi->ssi_type = htonl(ieee80211_ssi_none);
-	}
-	fi->version = htonl(IEEE80211_FI_VERSION);
-	fi->length = cpu_to_be32(sizeof(*fi) - sizeof(fi->msg_type));
+	ether_setup(dev);
+	dev->hard_start_xmit = ieee80211_subif_start_xmit;
+	dev->wireless_handlers = &ieee80211_iw_handler_def;
+	dev->set_multicast_list = ieee80211_set_multicast_list;
+	dev->change_mtu = ieee80211_change_mtu;
+	dev->get_stats = ieee80211_get_stats;
+	dev->open = ieee80211_open;
+	dev->stop = ieee80211_stop;
+	dev->uninit = ieee80211_if_reinit;
+	dev->destructor = ieee80211_if_free;
 }
 
-/* this routine is actually not just for this, but also
- * for pushing fake 'management' frames into userspace.
- * it shall be replaced by a netlink-based system. */
-void
-ieee80211_rx_mgmt(struct ieee80211_local *local, struct sk_buff *skb,
-		  struct ieee80211_rx_status *status, u32 msg_type)
-{
-	struct ieee80211_frame_info *fi;
-	const size_t hlen = sizeof(struct ieee80211_frame_info);
-	struct ieee80211_sub_if_data *sdata;
-
-	skb->dev = local->apdev;
-
-	sdata = IEEE80211_DEV_TO_SUB_IF(local->apdev);
-
-	if (skb_headroom(skb) < hlen) {
-		I802_DEBUG_INC(local->rx_expand_skb_head);
-		if (pskb_expand_head(skb, hlen, 0, GFP_ATOMIC)) {
-			dev_kfree_skb(skb);
-			return;
-		}
-	}
-
-	fi = (struct ieee80211_frame_info *) skb_push(skb, hlen);
-
-	ieee80211_fill_frame_info(local, fi, status);
-	fi->msg_type = htonl(msg_type);
-
-	sdata->stats.rx_packets++;
-	sdata->stats.rx_bytes += skb->len;
+/* WDS specialties */
 
-	skb_set_mac_header(skb, 0);
-	skb->ip_summed = CHECKSUM_UNNECESSARY;
-	skb->pkt_type = PACKET_OTHERHOST;
-	skb->protocol = htons(ETH_P_802_2);
-	memset(skb->cb, 0, sizeof(skb->cb));
-	netif_rx(skb);
-}
-
-static void
-ieee80211_rx_monitor(struct net_device *dev, struct sk_buff *skb,
-		     struct ieee80211_rx_status *status)
+int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr)
 {
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct ieee80211_sub_if_data *sdata;
-	struct ieee80211_rate *rate;
-	struct ieee80211_rtap_hdr {
-		struct ieee80211_radiotap_header hdr;
-		u8 flags;
-		u8 rate;
-		__le16 chan_freq;
-		__le16 chan_flags;
-		u8 antsignal;
-	} __attribute__ ((packed)) *rthdr;
-
-	skb->dev = dev;
-
-	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
-	if (status->flag & RX_FLAG_RADIOTAP)
-		goto out;
-
-	if (skb_headroom(skb) < sizeof(*rthdr)) {
-		I802_DEBUG_INC(local->rx_expand_skb_head);
-		if (pskb_expand_head(skb, sizeof(*rthdr), 0, GFP_ATOMIC)) {
-			dev_kfree_skb(skb);
-			return;
-		}
-	}
-
-	rthdr = (struct ieee80211_rtap_hdr *) skb_push(skb, sizeof(*rthdr));
-	memset(rthdr, 0, sizeof(*rthdr));
-	rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr));
-	rthdr->hdr.it_present =
-		cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
-			    (1 << IEEE80211_RADIOTAP_RATE) |
-			    (1 << IEEE80211_RADIOTAP_CHANNEL) |
-			    (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL));
-	rthdr->flags = local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS ?
-		       IEEE80211_RADIOTAP_F_FCS : 0;
-	rate = ieee80211_get_rate(local, status->phymode, status->rate);
-	if (rate)
-		rthdr->rate = rate->rate / 5;
-	rthdr->chan_freq = cpu_to_le16(status->freq);
-	rthdr->chan_flags =
-		status->phymode == MODE_IEEE80211A ?
-		cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ) :
-		cpu_to_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ);
-	rthdr->antsignal = status->ssi;
-
- out:
-	sdata->stats.rx_packets++;
-	sdata->stats.rx_bytes += skb->len;
-
-	skb_set_mac_header(skb, 0);
-	skb->ip_summed = CHECKSUM_UNNECESSARY;
-	skb->pkt_type = PACKET_OTHERHOST;
-	skb->protocol = htons(ETH_P_802_2);
-	memset(skb->cb, 0, sizeof(skb->cb));
-	netif_rx(skb);
-}
-
-int ieee80211_radar_status(struct ieee80211_hw *hw, int channel,
-			   int radar, int radar_type)
-{
-	struct sk_buff *skb;
-	struct ieee80211_radar_info *msg;
-	struct ieee80211_local *local = hw_to_local(hw);
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct sta_info *sta;
 
-	if (!local->apdev)
+	if (compare_ether_addr(remote_addr, sdata->u.wds.remote_addr) == 0)
 		return 0;
 
-	skb = dev_alloc_skb(sizeof(struct ieee80211_frame_info) +
-			    sizeof(struct ieee80211_radar_info));
-
-	if (!skb)
+	/* Create STA entry for the new peer */
+	sta = sta_info_add(local, dev, remote_addr, GFP_KERNEL);
+	if (!sta)
 		return -ENOMEM;
-	skb_reserve(skb, sizeof(struct ieee80211_frame_info));
-
-	msg = (struct ieee80211_radar_info *)
-		skb_put(skb, sizeof(struct ieee80211_radar_info));
-	msg->channel = channel;
-	msg->radar = radar;
-	msg->radar_type = radar_type;
-
-	ieee80211_rx_mgmt(local, skb, NULL, ieee80211_msg_radar);
-	return 0;
-}
-EXPORT_SYMBOL(ieee80211_radar_status);
-
-
-static void ap_sta_ps_start(struct net_device *dev, struct sta_info *sta)
-{
-	struct ieee80211_sub_if_data *sdata;
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
-
-	if (sdata->bss)
-		atomic_inc(&sdata->bss->num_sta_ps);
-	sta->flags |= WLAN_STA_PS;
-	sta->pspoll = 0;
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
-	printk(KERN_DEBUG "%s: STA " MAC_FMT " aid %d enters power "
-	       "save mode\n", dev->name, MAC_ARG(sta->addr), sta->aid);
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
-}
-
-
-static int ap_sta_ps_end(struct net_device *dev, struct sta_info *sta)
-{
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct sk_buff *skb;
-	int sent = 0;
-	struct ieee80211_sub_if_data *sdata;
-	struct ieee80211_tx_packet_data *pkt_data;
-
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
-	if (sdata->bss)
-		atomic_dec(&sdata->bss->num_sta_ps);
-	sta->flags &= ~(WLAN_STA_PS | WLAN_STA_TIM);
-	sta->pspoll = 0;
-	if (!skb_queue_empty(&sta->ps_tx_buf)) {
-		if (local->ops->set_tim)
-			local->ops->set_tim(local_to_hw(local), sta->aid, 0);
-		if (sdata->bss)
-			bss_tim_clear(local, sdata->bss, sta->aid);
-	}
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
-	printk(KERN_DEBUG "%s: STA " MAC_FMT " aid %d exits power "
-	       "save mode\n", dev->name, MAC_ARG(sta->addr), sta->aid);
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
-	/* Send all buffered frames to the station */
-	while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
-		pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
-		sent++;
-		pkt_data->requeue = 1;
-		dev_queue_xmit(skb);
-	}
-	while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
-		pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
-		local->total_ps_buffered--;
-		sent++;
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
-		printk(KERN_DEBUG "%s: STA " MAC_FMT " aid %d send PS frame "
-		       "since STA not sleeping anymore\n", dev->name,
-		       MAC_ARG(sta->addr), sta->aid);
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
-		pkt_data->requeue = 1;
-		dev_queue_xmit(skb);
-	}
-
-	return sent;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx)
-{
-	struct sk_buff *skb;
-	int no_pending_pkts;
-
-	if (likely(!rx->sta ||
-		   (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_CTL ||
-		   (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PSPOLL ||
-		   !rx->u.rx.ra_match))
-		return TXRX_CONTINUE;
-
-	skb = skb_dequeue(&rx->sta->tx_filtered);
-	if (!skb) {
-		skb = skb_dequeue(&rx->sta->ps_tx_buf);
-		if (skb)
-			rx->local->total_ps_buffered--;
-	}
-	no_pending_pkts = skb_queue_empty(&rx->sta->tx_filtered) &&
-		skb_queue_empty(&rx->sta->ps_tx_buf);
-
-	if (skb) {
-		struct ieee80211_hdr *hdr =
-			(struct ieee80211_hdr *) skb->data;
-
-		/* tell TX path to send one frame even though the STA may
-		 * still remain is PS mode after this frame exchange */
-		rx->sta->pspoll = 1;
-
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
-		printk(KERN_DEBUG "STA " MAC_FMT " aid %d: PS Poll (entries "
-		       "after %d)\n",
-		       MAC_ARG(rx->sta->addr), rx->sta->aid,
-		       skb_queue_len(&rx->sta->ps_tx_buf));
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
-
-		/* Use MoreData flag to indicate whether there are more
-		 * buffered frames for this STA */
-		if (no_pending_pkts) {
-			hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
-			rx->sta->flags &= ~WLAN_STA_TIM;
-		} else
-			hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
-
-		dev_queue_xmit(skb);
-
-		if (no_pending_pkts) {
-			if (rx->local->ops->set_tim)
-				rx->local->ops->set_tim(local_to_hw(rx->local),
-						       rx->sta->aid, 0);
-			if (rx->sdata->bss)
-				bss_tim_clear(rx->local, rx->sdata->bss, rx->sta->aid);
-		}
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
-	} else if (!rx->u.rx.sent_ps_buffered) {
-		printk(KERN_DEBUG "%s: STA " MAC_FMT " sent PS Poll even "
-		       "though there is no buffered frames for it\n",
-		       rx->dev->name, MAC_ARG(rx->sta->addr));
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
-
-	}
-
-	/* Free PS Poll skb here instead of returning TXRX_DROP that would
-	 * count as an dropped frame. */
-	dev_kfree_skb(rx->skb);
-
-	return TXRX_QUEUED;
-}
-
-
-static inline struct ieee80211_fragment_entry *
-ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
-			 unsigned int frag, unsigned int seq, int rx_queue,
-			 struct sk_buff **skb)
-{
-	struct ieee80211_fragment_entry *entry;
-	int idx;
-
-	idx = sdata->fragment_next;
-	entry = &sdata->fragments[sdata->fragment_next++];
-	if (sdata->fragment_next >= IEEE80211_FRAGMENT_MAX)
-		sdata->fragment_next = 0;
-
-	if (!skb_queue_empty(&entry->skb_list)) {
-#ifdef CONFIG_MAC80211_DEBUG
-		struct ieee80211_hdr *hdr =
-			(struct ieee80211_hdr *) entry->skb_list.next->data;
-		printk(KERN_DEBUG "%s: RX reassembly removed oldest "
-		       "fragment entry (idx=%d age=%lu seq=%d last_frag=%d "
-		       "addr1=" MAC_FMT " addr2=" MAC_FMT "\n",
-		       sdata->dev->name, idx,
-		       jiffies - entry->first_frag_time, entry->seq,
-		       entry->last_frag, MAC_ARG(hdr->addr1),
-		       MAC_ARG(hdr->addr2));
-#endif /* CONFIG_MAC80211_DEBUG */
-		__skb_queue_purge(&entry->skb_list);
-	}
-
-	__skb_queue_tail(&entry->skb_list, *skb); /* no need for locking */
-	*skb = NULL;
-	entry->first_frag_time = jiffies;
-	entry->seq = seq;
-	entry->rx_queue = rx_queue;
-	entry->last_frag = frag;
-	entry->ccmp = 0;
-	entry->extra_len = 0;
-
-	return entry;
-}
-
-
-static inline struct ieee80211_fragment_entry *
-ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata,
-			  u16 fc, unsigned int frag, unsigned int seq,
-			  int rx_queue, struct ieee80211_hdr *hdr)
-{
-	struct ieee80211_fragment_entry *entry;
-	int i, idx;
-
-	idx = sdata->fragment_next;
-	for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) {
-		struct ieee80211_hdr *f_hdr;
-		u16 f_fc;
-
-		idx--;
-		if (idx < 0)
-			idx = IEEE80211_FRAGMENT_MAX - 1;
-
-		entry = &sdata->fragments[idx];
-		if (skb_queue_empty(&entry->skb_list) || entry->seq != seq ||
-		    entry->rx_queue != rx_queue ||
-		    entry->last_frag + 1 != frag)
-			continue;
-
-		f_hdr = (struct ieee80211_hdr *) entry->skb_list.next->data;
-		f_fc = le16_to_cpu(f_hdr->frame_control);
-
-		if ((fc & IEEE80211_FCTL_FTYPE) != (f_fc & IEEE80211_FCTL_FTYPE) ||
-		    compare_ether_addr(hdr->addr1, f_hdr->addr1) != 0 ||
-		    compare_ether_addr(hdr->addr2, f_hdr->addr2) != 0)
-			continue;
-
-		if (entry->first_frag_time + 2 * HZ < jiffies) {
-			__skb_queue_purge(&entry->skb_list);
-			continue;
-		}
-		return entry;
-	}
-
-	return NULL;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx)
-{
-	struct ieee80211_hdr *hdr;
-	u16 sc;
-	unsigned int frag, seq;
-	struct ieee80211_fragment_entry *entry;
-	struct sk_buff *skb;
-
-	hdr = (struct ieee80211_hdr *) rx->skb->data;
-	sc = le16_to_cpu(hdr->seq_ctrl);
-	frag = sc & IEEE80211_SCTL_FRAG;
-
-	if (likely((!(rx->fc & IEEE80211_FCTL_MOREFRAGS) && frag == 0) ||
-		   (rx->skb)->len < 24 ||
-		   is_multicast_ether_addr(hdr->addr1))) {
-		/* not fragmented */
-		goto out;
-	}
-	I802_DEBUG_INC(rx->local->rx_handlers_fragments);
-
-	seq = (sc & IEEE80211_SCTL_SEQ) >> 4;
-
-	if (frag == 0) {
-		/* This is the first fragment of a new frame. */
-		entry = ieee80211_reassemble_add(rx->sdata, frag, seq,
-						 rx->u.rx.queue, &(rx->skb));
-		if (rx->key && rx->key->alg == ALG_CCMP &&
-		    (rx->fc & IEEE80211_FCTL_PROTECTED)) {
-			/* Store CCMP PN so that we can verify that the next
-			 * fragment has a sequential PN value. */
-			entry->ccmp = 1;
-			memcpy(entry->last_pn,
-			       rx->key->u.ccmp.rx_pn[rx->u.rx.queue],
-			       CCMP_PN_LEN);
-		}
-		return TXRX_QUEUED;
-	}
-
-	/* This is a fragment for a frame that should already be pending in
-	 * fragment cache. Add this fragment to the end of the pending entry.
-	 */
-	entry = ieee80211_reassemble_find(rx->sdata, rx->fc, frag, seq,
-					  rx->u.rx.queue, hdr);
-	if (!entry) {
-		I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
-		return TXRX_DROP;
-	}
-
-	/* Verify that MPDUs within one MSDU have sequential PN values.
-	 * (IEEE 802.11i, 8.3.3.4.5) */
-	if (entry->ccmp) {
-		int i;
-		u8 pn[CCMP_PN_LEN], *rpn;
-		if (!rx->key || rx->key->alg != ALG_CCMP)
-			return TXRX_DROP;
-		memcpy(pn, entry->last_pn, CCMP_PN_LEN);
-		for (i = CCMP_PN_LEN - 1; i >= 0; i--) {
-			pn[i]++;
-			if (pn[i])
-				break;
-		}
-		rpn = rx->key->u.ccmp.rx_pn[rx->u.rx.queue];
-		if (memcmp(pn, rpn, CCMP_PN_LEN) != 0) {
-			printk(KERN_DEBUG "%s: defrag: CCMP PN not sequential"
-			       " A2=" MAC_FMT " PN=%02x%02x%02x%02x%02x%02x "
-			       "(expected %02x%02x%02x%02x%02x%02x)\n",
-			       rx->dev->name, MAC_ARG(hdr->addr2),
-			       rpn[0], rpn[1], rpn[2], rpn[3], rpn[4], rpn[5],
-			       pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]);
-			return TXRX_DROP;
-		}
-		memcpy(entry->last_pn, pn, CCMP_PN_LEN);
-	}
-
-	skb_pull(rx->skb, ieee80211_get_hdrlen(rx->fc));
-	__skb_queue_tail(&entry->skb_list, rx->skb);
-	entry->last_frag = frag;
-	entry->extra_len += rx->skb->len;
-	if (rx->fc & IEEE80211_FCTL_MOREFRAGS) {
-		rx->skb = NULL;
-		return TXRX_QUEUED;
-	}
-
-	rx->skb = __skb_dequeue(&entry->skb_list);
-	if (skb_tailroom(rx->skb) < entry->extra_len) {
-		I802_DEBUG_INC(rx->local->rx_expand_skb_head2);
-		if (unlikely(pskb_expand_head(rx->skb, 0, entry->extra_len,
-					      GFP_ATOMIC))) {
-			I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
-			__skb_queue_purge(&entry->skb_list);
-			return TXRX_DROP;
-		}
-	}
-	while ((skb = __skb_dequeue(&entry->skb_list))) {
-		memcpy(skb_put(rx->skb, skb->len), skb->data, skb->len);
-		dev_kfree_skb(skb);
-	}
-
-	/* Complete frame has been reassembled - process it now */
-	rx->fragmented = 1;
-
- out:
-	if (rx->sta)
-		rx->sta->rx_packets++;
-	if (is_multicast_ether_addr(hdr->addr1))
-		rx->local->dot11MulticastReceivedFrameCount++;
-	else
-		ieee80211_led_rx(rx->local);
-	return TXRX_CONTINUE;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_rx_h_monitor(struct ieee80211_txrx_data *rx)
-{
-	if (rx->sdata->type == IEEE80211_IF_TYPE_MNTR) {
-		ieee80211_rx_monitor(rx->dev, rx->skb, rx->u.rx.status);
-		return TXRX_QUEUED;
-	}
-
-	if (rx->u.rx.status->flag & RX_FLAG_RADIOTAP)
-		skb_pull(rx->skb, ieee80211_get_radiotap_len(rx->skb));
-
-	return TXRX_CONTINUE;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
-{
-	struct ieee80211_hdr *hdr;
-	int always_sta_key;
-	hdr = (struct ieee80211_hdr *) rx->skb->data;
-
-	/* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */
-	if (rx->sta && !is_multicast_ether_addr(hdr->addr1)) {
-		if (unlikely(rx->fc & IEEE80211_FCTL_RETRY &&
-			     rx->sta->last_seq_ctrl[rx->u.rx.queue] ==
-			     hdr->seq_ctrl)) {
-			if (rx->u.rx.ra_match) {
-				rx->local->dot11FrameDuplicateCount++;
-				rx->sta->num_duplicates++;
-			}
-			return TXRX_DROP;
-		} else
-			rx->sta->last_seq_ctrl[rx->u.rx.queue] = hdr->seq_ctrl;
-	}
-
-	if ((rx->local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) &&
-	    rx->skb->len > FCS_LEN)
-		skb_trim(rx->skb, rx->skb->len - FCS_LEN);
-
-	if (unlikely(rx->skb->len < 16)) {
-		I802_DEBUG_INC(rx->local->rx_handlers_drop_short);
-		return TXRX_DROP;
-	}
-
-	if (!rx->u.rx.ra_match)
-		rx->skb->pkt_type = PACKET_OTHERHOST;
-	else if (compare_ether_addr(rx->dev->dev_addr, hdr->addr1) == 0)
-		rx->skb->pkt_type = PACKET_HOST;
-	else if (is_multicast_ether_addr(hdr->addr1)) {
-		if (is_broadcast_ether_addr(hdr->addr1))
-			rx->skb->pkt_type = PACKET_BROADCAST;
-		else
-			rx->skb->pkt_type = PACKET_MULTICAST;
-	} else
-		rx->skb->pkt_type = PACKET_OTHERHOST;
-
-	/* Drop disallowed frame classes based on STA auth/assoc state;
-	 * IEEE 802.11, Chap 5.5.
-	 *
-	 * 80211.o does filtering only based on association state, i.e., it
-	 * drops Class 3 frames from not associated stations. hostapd sends
-	 * deauth/disassoc frames when needed. In addition, hostapd is
-	 * responsible for filtering on both auth and assoc states.
-	 */
-	if (unlikely(((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA ||
-		      ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL &&
-		       (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)) &&
-		     rx->sdata->type != IEEE80211_IF_TYPE_IBSS &&
-		     (!rx->sta || !(rx->sta->flags & WLAN_STA_ASSOC)))) {
-		if ((!(rx->fc & IEEE80211_FCTL_FROMDS) &&
-		     !(rx->fc & IEEE80211_FCTL_TODS) &&
-		     (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)
-		    || !rx->u.rx.ra_match) {
-			/* Drop IBSS frames and frames for other hosts
-			 * silently. */
-			return TXRX_DROP;
-		}
-
-		if (!rx->local->apdev)
-			return TXRX_DROP;
-
-		ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
-				  ieee80211_msg_sta_not_assoc);
-		return TXRX_QUEUED;
-	}
-
-	if (rx->sdata->type == IEEE80211_IF_TYPE_STA)
-		always_sta_key = 0;
-	else
-		always_sta_key = 1;
+	sta_info_put(sta);
 
-	if (rx->sta && rx->sta->key && always_sta_key) {
-		rx->key = rx->sta->key;
+	/* Remove STA entry for the old peer */
+	sta = sta_info_get(local, sdata->u.wds.remote_addr);
+	if (sta) {
+		sta_info_free(sta);
+		sta_info_put(sta);
 	} else {
-		if (rx->sta && rx->sta->key)
-			rx->key = rx->sta->key;
-		else
-			rx->key = rx->sdata->default_key;
-
-		if ((rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) &&
-		    rx->fc & IEEE80211_FCTL_PROTECTED) {
-			int keyidx = ieee80211_wep_get_keyidx(rx->skb);
-
-			if (keyidx >= 0 && keyidx < NUM_DEFAULT_KEYS &&
-			    (!rx->sta || !rx->sta->key || keyidx > 0))
-				rx->key = rx->sdata->keys[keyidx];
-
-			if (!rx->key) {
-				if (!rx->u.rx.ra_match)
-					return TXRX_DROP;
-				printk(KERN_DEBUG "%s: RX WEP frame with "
-				       "unknown keyidx %d (A1=" MAC_FMT " A2="
-				       MAC_FMT " A3=" MAC_FMT ")\n",
-				       rx->dev->name, keyidx,
-				       MAC_ARG(hdr->addr1),
-				       MAC_ARG(hdr->addr2),
-				       MAC_ARG(hdr->addr3));
-				if (!rx->local->apdev)
-					return TXRX_DROP;
-				ieee80211_rx_mgmt(
-					rx->local, rx->skb, rx->u.rx.status,
-					ieee80211_msg_wep_frame_unknown_key);
-				return TXRX_QUEUED;
-			}
-		}
+		printk(KERN_DEBUG "%s: could not find STA entry for WDS link "
+		       "peer " MAC_FMT "\n",
+		       dev->name, MAC_ARG(sdata->u.wds.remote_addr));
 	}
 
-	if (rx->fc & IEEE80211_FCTL_PROTECTED && rx->key && rx->u.rx.ra_match) {
-		rx->key->tx_rx_count++;
-		if (unlikely(rx->local->key_tx_rx_threshold &&
-			     rx->key->tx_rx_count >
-			     rx->local->key_tx_rx_threshold)) {
-			ieee80211_key_threshold_notify(rx->dev, rx->key,
-						       rx->sta);
-		}
-	}
+	/* Update WDS link data */
+	memcpy(&sdata->u.wds.remote_addr, remote_addr, ETH_ALEN);
 
-	return TXRX_CONTINUE;
+	return 0;
 }
 
+/* everything else */
 
-static ieee80211_txrx_result
-ieee80211_rx_h_sta_process(struct ieee80211_txrx_data *rx)
+static int __ieee80211_if_config(struct net_device *dev,
+				 struct sk_buff *beacon,
+				 struct ieee80211_tx_control *control)
 {
-	struct sta_info *sta = rx->sta;
-	struct net_device *dev = rx->dev;
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
-
-	if (!sta)
-		return TXRX_CONTINUE;
-
-	/* Update last_rx only for IBSS packets which are for the current
-	 * BSSID to avoid keeping the current IBSS network alive in cases where
-	 * other STAs are using different BSSID. */
-	if (rx->sdata->type == IEEE80211_IF_TYPE_IBSS) {
-		u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len);
-		if (compare_ether_addr(bssid, rx->sdata->u.sta.bssid) == 0)
-			sta->last_rx = jiffies;
-	} else
-	if (!is_multicast_ether_addr(hdr->addr1) ||
-	    rx->sdata->type == IEEE80211_IF_TYPE_STA) {
-		/* Update last_rx only for unicast frames in order to prevent
-		 * the Probe Request frames (the only broadcast frames from a
-		 * STA in infrastructure mode) from keeping a connection alive.
-		 */
-		sta->last_rx = jiffies;
-	}
-
-	if (!rx->u.rx.ra_match)
-		return TXRX_CONTINUE;
-
-	sta->rx_fragments++;
-	sta->rx_bytes += rx->skb->len;
-	sta->last_rssi = (sta->last_rssi * 15 +
-			  rx->u.rx.status->ssi) / 16;
-	sta->last_signal = (sta->last_signal * 15 +
-			    rx->u.rx.status->signal) / 16;
-	sta->last_noise = (sta->last_noise * 15 +
-			   rx->u.rx.status->noise) / 16;
-
-	if (!(rx->fc & IEEE80211_FCTL_MOREFRAGS)) {
-		/* Change STA power saving mode only in the end of a frame
-		 * exchange sequence */
-		if ((sta->flags & WLAN_STA_PS) && !(rx->fc & IEEE80211_FCTL_PM))
-			rx->u.rx.sent_ps_buffered += ap_sta_ps_end(dev, sta);
-		else if (!(sta->flags & WLAN_STA_PS) &&
-			 (rx->fc & IEEE80211_FCTL_PM))
-			ap_sta_ps_start(dev, sta);
-	}
-
-	/* Drop data::nullfunc frames silently, since they are used only to
-	 * control station power saving mode. */
-	if ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
-	    (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_NULLFUNC) {
-		I802_DEBUG_INC(rx->local->rx_handlers_drop_nullfunc);
-		/* Update counter and free packet here to avoid counting this
-		 * as a dropped packed. */
-		sta->rx_packets++;
-		dev_kfree_skb(rx->skb);
-		return TXRX_QUEUED;
-	}
-
-	return TXRX_CONTINUE;
-} /* ieee80211_rx_h_sta_process */
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_if_conf conf;
+	static u8 scan_bssid[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
+	if (!local->ops->config_interface || !netif_running(dev))
+		return 0;
 
-static ieee80211_txrx_result
-ieee80211_rx_h_wep_weak_iv_detection(struct ieee80211_txrx_data *rx)
-{
-	if (!rx->sta || !(rx->fc & IEEE80211_FCTL_PROTECTED) ||
-	    (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
-	    !rx->key || rx->key->alg != ALG_WEP || !rx->u.rx.ra_match)
-		return TXRX_CONTINUE;
-
-	/* Check for weak IVs, if hwaccel did not remove IV from the frame */
-	if ((rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) ||
-	    rx->key->force_sw_encrypt) {
-		u8 *iv = ieee80211_wep_is_weak_iv(rx->skb, rx->key);
-		if (iv) {
-			rx->sta->wep_weak_iv_count++;
-		}
+	memset(&conf, 0, sizeof(conf));
+	conf.type = sdata->type;
+	if (sdata->type == IEEE80211_IF_TYPE_STA ||
+	    sdata->type == IEEE80211_IF_TYPE_IBSS) {
+		if (local->sta_scanning &&
+		    local->scan_dev == dev)
+			conf.bssid = scan_bssid;
+		else
+			conf.bssid = sdata->u.sta.bssid;
+		conf.ssid = sdata->u.sta.ssid;
+		conf.ssid_len = sdata->u.sta.ssid_len;
+		conf.generic_elem = sdata->u.sta.extra_ie;
+		conf.generic_elem_len = sdata->u.sta.extra_ie_len;
+	} else if (sdata->type == IEEE80211_IF_TYPE_AP) {
+		conf.ssid = sdata->u.ap.ssid;
+		conf.ssid_len = sdata->u.ap.ssid_len;
+		conf.generic_elem = sdata->u.ap.generic_elem;
+		conf.generic_elem_len = sdata->u.ap.generic_elem_len;
+		conf.beacon = beacon;
+		conf.beacon_control = control;
 	}
-
-	return TXRX_CONTINUE;
+	return local->ops->config_interface(local_to_hw(local),
+					   dev->ifindex, &conf);
 }
 
-
-static ieee80211_txrx_result
-ieee80211_rx_h_wep_decrypt(struct ieee80211_txrx_data *rx)
+int ieee80211_if_config(struct net_device *dev)
 {
-	/* If the device handles decryption totally, skip this test */
-	if (rx->local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP)
-		return TXRX_CONTINUE;
-
-	if ((rx->key && rx->key->alg != ALG_WEP) ||
-	    !(rx->fc & IEEE80211_FCTL_PROTECTED) ||
-	    ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
-	     ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
-	      (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH)))
-		return TXRX_CONTINUE;
-
-	if (!rx->key) {
-		printk(KERN_DEBUG "%s: RX WEP frame, but no key set\n",
-		       rx->dev->name);
-		return TXRX_DROP;
-	}
-
-	if (!(rx->u.rx.status->flag & RX_FLAG_DECRYPTED) ||
-	    rx->key->force_sw_encrypt) {
-		if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key)) {
-			printk(KERN_DEBUG "%s: RX WEP frame, decrypt "
-			       "failed\n", rx->dev->name);
-			return TXRX_DROP;
-		}
-	} else if (rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) {
-		ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
-		/* remove ICV */
-		skb_trim(rx->skb, rx->skb->len - 4);
-	}
-
-	return TXRX_CONTINUE;
+	return __ieee80211_if_config(dev, NULL, NULL);
 }
 
-
-static ieee80211_txrx_result
-ieee80211_rx_h_802_1x_pae(struct ieee80211_txrx_data *rx)
+int ieee80211_if_config_beacon(struct net_device *dev)
 {
-	if (rx->sdata->eapol && ieee80211_is_eapol(rx->skb) &&
-	    rx->sdata->type != IEEE80211_IF_TYPE_STA && rx->u.rx.ra_match) {
-		/* Pass both encrypted and unencrypted EAPOL frames to user
-		 * space for processing. */
-		if (!rx->local->apdev)
-			return TXRX_DROP;
-		ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
-				  ieee80211_msg_normal);
-		return TXRX_QUEUED;
-	}
-
-	if (unlikely(rx->sdata->ieee802_1x &&
-		     (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
-		     (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_NULLFUNC &&
-		     (!rx->sta || !(rx->sta->flags & WLAN_STA_AUTHORIZED)) &&
-		     !ieee80211_is_eapol(rx->skb))) {
-#ifdef CONFIG_MAC80211_DEBUG
-		struct ieee80211_hdr *hdr =
-			(struct ieee80211_hdr *) rx->skb->data;
-		printk(KERN_DEBUG "%s: dropped frame from " MAC_FMT
-		       " (unauthorized port)\n", rx->dev->name,
-		       MAC_ARG(hdr->addr2));
-#endif /* CONFIG_MAC80211_DEBUG */
-		return TXRX_DROP;
-	}
-
-	return TXRX_CONTINUE;
-}
-
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_tx_control control;
+	struct sk_buff *skb;
 
-static ieee80211_txrx_result
-ieee80211_rx_h_drop_unencrypted(struct ieee80211_txrx_data *rx)
-{
-	/*  If the device handles decryption totally, skip this test */
-	if (rx->local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP)
-		return TXRX_CONTINUE;
-
-	/* Drop unencrypted frames if key is set. */
-	if (unlikely(!(rx->fc & IEEE80211_FCTL_PROTECTED) &&
-		     (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
-		     (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_NULLFUNC &&
-		     (rx->key || rx->sdata->drop_unencrypted) &&
-		     (rx->sdata->eapol == 0 ||
-		      !ieee80211_is_eapol(rx->skb)))) {
-		printk(KERN_DEBUG "%s: RX non-WEP frame, but expected "
-		       "encryption\n", rx->dev->name);
-		return TXRX_DROP;
-	}
-	return TXRX_CONTINUE;
+	if (!(local->hw.flags & IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE))
+		return 0;
+	skb = ieee80211_beacon_get(local_to_hw(local), dev->ifindex, &control);
+	if (!skb)
+		return -ENOMEM;
+	return __ieee80211_if_config(dev, skb, &control);
 }
 
-
-static ieee80211_txrx_result
-ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx)
+int ieee80211_hw_config(struct ieee80211_local *local)
 {
-	struct ieee80211_sub_if_data *sdata;
-
-	if (!rx->u.rx.ra_match)
-		return TXRX_DROP;
+	struct ieee80211_hw_mode *mode;
+	struct ieee80211_channel *chan;
+	int ret = 0;
 
-	sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
-	if ((sdata->type == IEEE80211_IF_TYPE_STA ||
-	     sdata->type == IEEE80211_IF_TYPE_IBSS) &&
-	    !rx->local->user_space_mlme) {
-		ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status);
+	if (local->sta_scanning) {
+		chan = local->scan_channel;
+		mode = local->scan_hw_mode;
 	} else {
-		/* Management frames are sent to hostapd for processing */
-		if (!rx->local->apdev)
-			return TXRX_DROP;
-		ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
-				  ieee80211_msg_normal);
+		chan = local->oper_channel;
+		mode = local->oper_hw_mode;
 	}
-	return TXRX_QUEUED;
-}
-
 
-static ieee80211_txrx_result
-ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx)
-{
-	struct ieee80211_local *local = rx->local;
-	struct sk_buff *skb = rx->skb;
+	local->hw.conf.channel = chan->chan;
+	local->hw.conf.channel_val = chan->val;
+	local->hw.conf.power_level = chan->power_level;
+	local->hw.conf.freq = chan->freq;
+	local->hw.conf.phymode = mode->mode;
+	local->hw.conf.antenna_max = chan->antenna_max;
+	local->hw.conf.chan = chan;
+	local->hw.conf.mode = mode;
 
-	if (unlikely(local->sta_scanning != 0)) {
-		ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status);
-		return TXRX_QUEUED;
-	}
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+	printk(KERN_DEBUG "HW CONFIG: channel=%d freq=%d "
+	       "phymode=%d\n", local->hw.conf.channel, local->hw.conf.freq,
+	       local->hw.conf.phymode);
+#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 
-	if (unlikely(rx->u.rx.in_scan)) {
-		/* scanning finished during invoking of handlers */
-		I802_DEBUG_INC(local->rx_handlers_drop_passive_scan);
-		return TXRX_DROP;
-	}
+	if (local->ops->config)
+		ret = local->ops->config(local_to_hw(local), &local->hw.conf);
 
-	return TXRX_CONTINUE;
+	return ret;
 }
 
-
-static void ieee80211_rx_michael_mic_report(struct net_device *dev,
-					    struct ieee80211_hdr *hdr,
-					    struct sta_info *sta,
-					    struct ieee80211_txrx_data *rx)
+void ieee80211_erp_info_change_notify(struct net_device *dev, u8 changes)
 {
-	int keyidx, hdrlen;
-
-	hdrlen = ieee80211_get_hdrlen_from_skb(rx->skb);
-	if (rx->skb->len >= hdrlen + 4)
-		keyidx = rx->skb->data[hdrlen + 3] >> 6;
-	else
-		keyidx = -1;
-
-	/* TODO: verify that this is not triggered by fragmented
-	 * frames (hw does not verify MIC for them). */
-	printk(KERN_DEBUG "%s: TKIP hwaccel reported Michael MIC "
-	       "failure from " MAC_FMT " to " MAC_FMT " keyidx=%d\n",
-	       dev->name, MAC_ARG(hdr->addr2), MAC_ARG(hdr->addr1), keyidx);
-
-	if (!sta) {
-		/* Some hardware versions seem to generate incorrect
-		 * Michael MIC reports; ignore them to avoid triggering
-		 * countermeasures. */
-		printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
-		       "error for unknown address " MAC_FMT "\n",
-		       dev->name, MAC_ARG(hdr->addr2));
-		goto ignore;
-	}
-
-	if (!(rx->fc & IEEE80211_FCTL_PROTECTED)) {
-		printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
-		       "error for a frame with no ISWEP flag (src "
-		       MAC_FMT ")\n", dev->name, MAC_ARG(hdr->addr2));
-		goto ignore;
-	}
-
-	if ((rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) &&
-	    rx->sdata->type == IEEE80211_IF_TYPE_AP) {
-		keyidx = ieee80211_wep_get_keyidx(rx->skb);
-		/* AP with Pairwise keys support should never receive Michael
-		 * MIC errors for non-zero keyidx because these are reserved
-		 * for group keys and only the AP is sending real multicast
-		 * frames in BSS. */
-		if (keyidx) {
-			printk(KERN_DEBUG "%s: ignored Michael MIC error for "
-			       "a frame with non-zero keyidx (%d) (src " MAC_FMT
-			       ")\n", dev->name, keyidx, MAC_ARG(hdr->addr2));
-			goto ignore;
-		}
-	}
-
-	if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
-	    ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
-	     (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH)) {
-		printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
-		       "error for a frame that cannot be encrypted "
-		       "(fc=0x%04x) (src " MAC_FMT ")\n",
-		       dev->name, rx->fc, MAC_ARG(hdr->addr2));
-		goto ignore;
-	}
-
-	do {
-		union iwreq_data wrqu;
-		char *buf = kmalloc(128, GFP_ATOMIC);
-		if (!buf)
-			break;
-
-		/* TODO: needed parameters: count, key type, TSC */
-		sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
-			"keyid=%d %scast addr=" MAC_FMT ")",
-			keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni",
-			MAC_ARG(hdr->addr2));
-		memset(&wrqu, 0, sizeof(wrqu));
-		wrqu.data.length = strlen(buf);
-		wireless_send_event(rx->dev, IWEVCUSTOM, &wrqu, buf);
-		kfree(buf);
-	} while (0);
-
-	/* TODO: consider verifying the MIC error report with software
-	 * implementation if we get too many spurious reports from the
-	 * hardware. */
-	if (!rx->local->apdev)
-		goto ignore;
-	ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
-			  ieee80211_msg_michael_mic_failure);
-	return;
-
- ignore:
-	dev_kfree_skb(rx->skb);
-	rx->skb = NULL;
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	if (local->ops->erp_ie_changed)
+		local->ops->erp_ie_changed(local_to_hw(local), changes,
+					   sdata->use_protection,
+					   !sdata->short_preamble);
 }
 
-static inline ieee80211_txrx_result __ieee80211_invoke_rx_handlers(
-				struct ieee80211_local *local,
-				ieee80211_rx_handler *handlers,
-				struct ieee80211_txrx_data *rx,
-				struct sta_info *sta)
+void ieee80211_reset_erp_info(struct net_device *dev)
 {
-	ieee80211_rx_handler *handler;
-	ieee80211_txrx_result res = TXRX_DROP;
-
-	for (handler = handlers; *handler != NULL; handler++) {
-		res = (*handler)(rx);
-		if (res != TXRX_CONTINUE) {
-			if (res == TXRX_DROP) {
-				I802_DEBUG_INC(local->rx_handlers_drop);
-				if (sta)
-					sta->rx_dropped++;
-			}
-			if (res == TXRX_QUEUED)
-				I802_DEBUG_INC(local->rx_handlers_queued);
-			break;
-		}
-	}
-
-	if (res == TXRX_DROP) {
-		dev_kfree_skb(rx->skb);
-	}
-	return res;
-}
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-static inline void ieee80211_invoke_rx_handlers(struct ieee80211_local *local,
-						ieee80211_rx_handler *handlers,
-						struct ieee80211_txrx_data *rx,
-						struct sta_info *sta)
-{
-	if (__ieee80211_invoke_rx_handlers(local, handlers, rx, sta) ==
-	    TXRX_CONTINUE)
-		dev_kfree_skb(rx->skb);
+	sdata->short_preamble = 0;
+	sdata->use_protection = 0;
+	ieee80211_erp_info_change_notify(dev,
+					 IEEE80211_ERP_CHANGE_PROTECTION |
+					 IEEE80211_ERP_CHANGE_PREAMBLE);
 }
 
-/*
- * This is the receive path handler. It is called by a low level driver when an
- * 802.11 MPDU is received from the hardware.
- */
-void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
-		    struct ieee80211_rx_status *status)
+struct dev_mc_list *ieee80211_get_mc_list_item(struct ieee80211_hw *hw,
+					       struct dev_mc_list *prev,
+					       void **ptr)
 {
 	struct ieee80211_local *local = hw_to_local(hw);
-	struct ieee80211_sub_if_data *sdata;
-	struct sta_info *sta;
-	struct ieee80211_hdr *hdr;
-	struct ieee80211_txrx_data rx;
-	u16 type;
-	int multicast;
-	int radiotap_len = 0;
-
-	if (status->flag & RX_FLAG_RADIOTAP) {
-		radiotap_len = ieee80211_get_radiotap_len(skb);
-		skb_pull(skb, radiotap_len);
-	}
-
-	hdr = (struct ieee80211_hdr *) skb->data;
-	memset(&rx, 0, sizeof(rx));
-	rx.skb = skb;
-	rx.local = local;
-
-	rx.u.rx.status = status;
-	rx.fc = skb->len >= 2 ? le16_to_cpu(hdr->frame_control) : 0;
-	type = rx.fc & IEEE80211_FCTL_FTYPE;
-	if (type == IEEE80211_FTYPE_DATA || type == IEEE80211_FTYPE_MGMT)
-		local->dot11ReceivedFragmentCount++;
-	multicast = is_multicast_ether_addr(hdr->addr1);
-
-	if (skb->len >= 16)
-		sta = rx.sta = sta_info_get(local, hdr->addr2);
-	else
-		sta = rx.sta = NULL;
-
-	if (sta) {
-		rx.dev = sta->dev;
-		rx.sdata = IEEE80211_DEV_TO_SUB_IF(rx.dev);
-	}
-
-	if ((status->flag & RX_FLAG_MMIC_ERROR)) {
-		ieee80211_rx_michael_mic_report(local->mdev, hdr, sta, &rx);
-		goto end;
-	}
-
-	if (unlikely(local->sta_scanning))
-		rx.u.rx.in_scan = 1;
-
-	if (__ieee80211_invoke_rx_handlers(local, local->rx_pre_handlers, &rx,
-					   sta) != TXRX_CONTINUE)
-		goto end;
-	skb = rx.skb;
-
-	skb_push(skb, radiotap_len);
-	if (sta && !sta->assoc_ap && !(sta->flags & WLAN_STA_WDS) &&
-	    !local->iff_promiscs && !multicast) {
-		rx.u.rx.ra_match = 1;
-		ieee80211_invoke_rx_handlers(local, local->rx_handlers, &rx,
-					     sta);
-	} else {
-		struct ieee80211_sub_if_data *prev = NULL;
-		struct sk_buff *skb_new;
-		u8 *bssid = ieee80211_get_bssid(hdr, skb->len - radiotap_len);
-
-		read_lock(&local->sub_if_lock);
-		list_for_each_entry(sdata, &local->sub_if_list, list) {
-			rx.u.rx.ra_match = 1;
-			switch (sdata->type) {
-			case IEEE80211_IF_TYPE_STA:
-				if (!bssid)
-					continue;
-				if (!ieee80211_bssid_match(bssid,
-							sdata->u.sta.bssid)) {
-					if (!rx.u.rx.in_scan)
-						continue;
-					rx.u.rx.ra_match = 0;
-				} else if (!multicast &&
-					   compare_ether_addr(sdata->dev->dev_addr,
-							      hdr->addr1) != 0) {
-					if (!sdata->promisc)
-						continue;
-					rx.u.rx.ra_match = 0;
-				}
-				break;
-			case IEEE80211_IF_TYPE_IBSS:
-				if (!bssid)
-					continue;
-				if (!ieee80211_bssid_match(bssid,
-							sdata->u.sta.bssid)) {
-					if (!rx.u.rx.in_scan)
-						continue;
-					rx.u.rx.ra_match = 0;
-				} else if (!multicast &&
-					   compare_ether_addr(sdata->dev->dev_addr,
-							      hdr->addr1) != 0) {
-					if (!sdata->promisc)
-						continue;
-					rx.u.rx.ra_match = 0;
-				} else if (!sta)
-					sta = rx.sta =
-						ieee80211_ibss_add_sta(sdata->dev,
-								       skb, bssid,
-								       hdr->addr2);
-				break;
-			case IEEE80211_IF_TYPE_AP:
-				if (!bssid) {
-					if (compare_ether_addr(sdata->dev->dev_addr,
-							       hdr->addr1) != 0)
-						continue;
-				} else if (!ieee80211_bssid_match(bssid,
-							sdata->dev->dev_addr)) {
-					if (!rx.u.rx.in_scan)
-						continue;
-					rx.u.rx.ra_match = 0;
-				}
-				if (sdata->dev == local->mdev &&
-				    !rx.u.rx.in_scan)
-					/* do not receive anything via
-					 * master device when not scanning */
-					continue;
-				break;
-			case IEEE80211_IF_TYPE_WDS:
-				if (bssid ||
-				    (rx.fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)
-					continue;
-				if (compare_ether_addr(sdata->u.wds.remote_addr,
-						       hdr->addr2) != 0)
-					continue;
-				break;
-			}
-
-			if (prev) {
-				skb_new = skb_copy(skb, GFP_ATOMIC);
-				if (!skb_new) {
-					if (net_ratelimit())
-						printk(KERN_DEBUG "%s: failed to copy "
-						       "multicast frame for %s",
-						       local->mdev->name, prev->dev->name);
-					continue;
-				}
-				rx.skb = skb_new;
-				rx.dev = prev->dev;
-				rx.sdata = prev;
-				ieee80211_invoke_rx_handlers(local,
-							     local->rx_handlers,
-							     &rx, sta);
-			}
-			prev = sdata;
-		}
-		if (prev) {
-			rx.skb = skb;
-			rx.dev = prev->dev;
-			rx.sdata = prev;
-			ieee80211_invoke_rx_handlers(local, local->rx_handlers,
-						     &rx, sta);
-		} else
-			dev_kfree_skb(skb);
-		read_unlock(&local->sub_if_lock);
-	}
-
-  end:
-	if (sta)
-		sta_info_put(sta);
-}
-EXPORT_SYMBOL(__ieee80211_rx);
-
-static ieee80211_txrx_result
-ieee80211_tx_h_load_stats(struct ieee80211_txrx_data *tx)
-{
-	struct ieee80211_local *local = tx->local;
-	struct ieee80211_hw_mode *mode = tx->u.tx.mode;
-	struct sk_buff *skb = tx->skb;
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-	u32 load = 0, hdrtime;
-
-	/* TODO: this could be part of tx_status handling, so that the number
-	 * of retries would be known; TX rate should in that case be stored
-	 * somewhere with the packet */
-
-	/* Estimate total channel use caused by this frame */
-
-	/* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values,
-	 * 1 usec = 1/8 * (1080 / 10) = 13.5 */
-
-	if (mode->mode == MODE_IEEE80211A ||
-	    mode->mode == MODE_ATHEROS_TURBO ||
-	    mode->mode == MODE_ATHEROS_TURBOG ||
-	    (mode->mode == MODE_IEEE80211G &&
-	     tx->u.tx.rate->flags & IEEE80211_RATE_ERP))
-		hdrtime = CHAN_UTIL_HDR_SHORT;
-	else
-		hdrtime = CHAN_UTIL_HDR_LONG;
-
-	load = hdrtime;
-	if (!is_multicast_ether_addr(hdr->addr1))
-		load += hdrtime;
-
-	if (tx->u.tx.control->flags & IEEE80211_TXCTL_USE_RTS_CTS)
-		load += 2 * hdrtime;
-	else if (tx->u.tx.control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)
-		load += hdrtime;
-
-	load += skb->len * tx->u.tx.rate->rate_inv;
-
-	if (tx->u.tx.extra_frag) {
-		int i;
-		for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
-			load += 2 * hdrtime;
-			load += tx->u.tx.extra_frag[i]->len *
-				tx->u.tx.rate->rate;
-		}
-	}
-
-	/* Divide channel_use by 8 to avoid wrapping around the counter */
-	load >>= CHAN_UTIL_SHIFT;
-	local->channel_use_raw += load;
-	if (tx->sta)
-		tx->sta->channel_use_raw += load;
-	tx->sdata->channel_use_raw += load;
-
-	return TXRX_CONTINUE;
-}
-
-
-static ieee80211_txrx_result
-ieee80211_rx_h_load_stats(struct ieee80211_txrx_data *rx)
-{
-	struct ieee80211_local *local = rx->local;
-	struct sk_buff *skb = rx->skb;
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-	u32 load = 0, hdrtime;
-	struct ieee80211_rate *rate;
-	struct ieee80211_hw_mode *mode = local->hw.conf.mode;
-	int i;
-
-	/* Estimate total channel use caused by this frame */
-
-	if (unlikely(mode->num_rates < 0))
-		return TXRX_CONTINUE;
+	struct ieee80211_sub_if_data *sdata = *ptr;
+	struct dev_mc_list *mc;
 
-	rate = &mode->rates[0];
-	for (i = 0; i < mode->num_rates; i++) {
-		if (mode->rates[i].val == rx->u.rx.status->rate) {
-			rate = &mode->rates[i];
-			break;
-		}
+	if (!prev) {
+		WARN_ON(sdata);
+		sdata = NULL;
 	}
+	if (!prev || !prev->next) {
+		if (sdata)
+			sdata = list_entry(sdata->list.next,
+					   struct ieee80211_sub_if_data, list);
+		else
+			sdata = list_entry(local->sub_if_list.next,
+					   struct ieee80211_sub_if_data, list);
+		if (&sdata->list != &local->sub_if_list)
+			mc = sdata->dev->mc_list;
+		else
+			mc = NULL;
+	} else
+		mc = prev->next;
 
-	/* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values,
-	 * 1 usec = 1/8 * (1080 / 10) = 13.5 */
-
-	if (mode->mode == MODE_IEEE80211A ||
-	    mode->mode == MODE_ATHEROS_TURBO ||
-	    mode->mode == MODE_ATHEROS_TURBOG ||
-	    (mode->mode == MODE_IEEE80211G &&
-	     rate->flags & IEEE80211_RATE_ERP))
-		hdrtime = CHAN_UTIL_HDR_SHORT;
-	else
-		hdrtime = CHAN_UTIL_HDR_LONG;
-
-	load = hdrtime;
-	if (!is_multicast_ether_addr(hdr->addr1))
-		load += hdrtime;
-
-	load += skb->len * rate->rate_inv;
-
-	/* Divide channel_use by 8 to avoid wrapping around the counter */
-	load >>= CHAN_UTIL_SHIFT;
-	local->channel_use_raw += load;
-	if (rx->sta)
-		rx->sta->channel_use_raw += load;
-	rx->u.rx.load = load;
-
-	return TXRX_CONTINUE;
-}
-
-static ieee80211_txrx_result
-ieee80211_rx_h_if_stats(struct ieee80211_txrx_data *rx)
-{
-	rx->sdata->channel_use_raw += rx->u.rx.load;
-	return TXRX_CONTINUE;
+	*ptr = sdata;
+	return mc;
 }
+EXPORT_SYMBOL(ieee80211_get_mc_list_item);
 
 static void ieee80211_stat_refresh(unsigned long data)
 {
@@ -4284,13 +815,13 @@ static void ieee80211_stat_refresh(unsigned long data)
 		return;
 
 	/* go through all stations */
-	spin_lock_bh(&local->sta_lock);
+	read_lock_bh(&local->sta_lock);
 	list_for_each_entry(sta, &local->sta_list, list) {
 		sta->channel_use = (sta->channel_use_raw / local->stat_time) /
 			CHAN_UTIL_PER_10MS;
 		sta->channel_use_raw = 0;
 	}
-	spin_unlock_bh(&local->sta_lock);
+	read_unlock_bh(&local->sta_lock);
 
 	/* go through all subinterfaces */
 	read_lock(&local->sub_if_lock);
@@ -4310,25 +841,6 @@ static void ieee80211_stat_refresh(unsigned long data)
 	add_timer(&local->stat_timer);
 }
 
-
-/* This is a version of the rx handler that can be called from hard irq
- * context. Post the skb on the queue and schedule the tasklet */
-void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb,
-			  struct ieee80211_rx_status *status)
-{
-	struct ieee80211_local *local = hw_to_local(hw);
-
-	BUILD_BUG_ON(sizeof(struct ieee80211_rx_status) > sizeof(skb->cb));
-
-	skb->dev = local->mdev;
-	/* copy status into skb->cb for use by tasklet */
-	memcpy(skb->cb, status, sizeof(*status));
-	skb->pkt_type = IEEE80211_RX_MSG;
-	skb_queue_tail(&local->skb_queue, skb);
-	tasklet_schedule(&local->tasklet);
-}
-EXPORT_SYMBOL(ieee80211_rx_irqsafe);
-
 void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
 				 struct sk_buff *skb,
 				 struct ieee80211_tx_status *status)
@@ -4405,7 +917,6 @@ static void ieee80211_tasklet_handler(unsigned long data)
 	}
 }
 
-
 /* Remove added headers (e.g., QoS control), encryption header/MIC, etc. to
  * make a prepared TX frame (one that has been given to hw) to look like brand
  * new IEEE 802.11 frame that is ready to go through TX processing again.
@@ -4468,7 +979,6 @@ no_key:
 	}
 }
 
-
 void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
 			 struct ieee80211_tx_status *status)
 {
@@ -4688,164 +1198,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
 }
 EXPORT_SYMBOL(ieee80211_tx_status);
 
-/* TODO: implement register/unregister functions for adding TX/RX handlers
- * into ordered list */
-
-/* rx_pre handlers don't have dev and sdata fields available in
- * ieee80211_txrx_data */
-static ieee80211_rx_handler ieee80211_rx_pre_handlers[] =
-{
-	ieee80211_rx_h_parse_qos,
-	ieee80211_rx_h_load_stats,
-	NULL
-};
-
-static ieee80211_rx_handler ieee80211_rx_handlers[] =
-{
-	ieee80211_rx_h_if_stats,
-	ieee80211_rx_h_monitor,
-	ieee80211_rx_h_passive_scan,
-	ieee80211_rx_h_check,
-	ieee80211_rx_h_sta_process,
-	ieee80211_rx_h_ccmp_decrypt,
-	ieee80211_rx_h_tkip_decrypt,
-	ieee80211_rx_h_wep_weak_iv_detection,
-	ieee80211_rx_h_wep_decrypt,
-	ieee80211_rx_h_defragment,
-	ieee80211_rx_h_ps_poll,
-	ieee80211_rx_h_michael_mic_verify,
-	/* this must be after decryption - so header is counted in MPDU mic
-	 * must be before pae and data, so QOS_DATA format frames
-	 * are not passed to user space by these functions
-	 */
-	ieee80211_rx_h_remove_qos_control,
-	ieee80211_rx_h_802_1x_pae,
-	ieee80211_rx_h_drop_unencrypted,
-	ieee80211_rx_h_data,
-	ieee80211_rx_h_mgmt,
-	NULL
-};
-
-static ieee80211_tx_handler ieee80211_tx_handlers[] =
-{
-	ieee80211_tx_h_check_assoc,
-	ieee80211_tx_h_sequence,
-	ieee80211_tx_h_ps_buf,
-	ieee80211_tx_h_select_key,
-	ieee80211_tx_h_michael_mic_add,
-	ieee80211_tx_h_fragment,
-	ieee80211_tx_h_tkip_encrypt,
-	ieee80211_tx_h_ccmp_encrypt,
-	ieee80211_tx_h_wep_encrypt,
-	ieee80211_tx_h_rate_ctrl,
-	ieee80211_tx_h_misc,
-	ieee80211_tx_h_load_stats,
-	NULL
-};
-
-
-int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr)
-{
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-	struct sta_info *sta;
-
-	if (compare_ether_addr(remote_addr, sdata->u.wds.remote_addr) == 0)
-		return 0;
-
-	/* Create STA entry for the new peer */
-	sta = sta_info_add(local, dev, remote_addr, GFP_KERNEL);
-	if (!sta)
-		return -ENOMEM;
-	sta_info_put(sta);
-
-	/* Remove STA entry for the old peer */
-	sta = sta_info_get(local, sdata->u.wds.remote_addr);
-	if (sta) {
-		sta_info_put(sta);
-		sta_info_free(sta, 0);
-	} else {
-		printk(KERN_DEBUG "%s: could not find STA entry for WDS link "
-		       "peer " MAC_FMT "\n",
-		       dev->name, MAC_ARG(sdata->u.wds.remote_addr));
-	}
-
-	/* Update WDS link data */
-	memcpy(&sdata->u.wds.remote_addr, remote_addr, ETH_ALEN);
-
-	return 0;
-}
-
-/* Must not be called for mdev and apdev */
-void ieee80211_if_setup(struct net_device *dev)
-{
-	ether_setup(dev);
-	dev->hard_start_xmit = ieee80211_subif_start_xmit;
-	dev->wireless_handlers = &ieee80211_iw_handler_def;
-	dev->set_multicast_list = ieee80211_set_multicast_list;
-	dev->change_mtu = ieee80211_change_mtu;
-	dev->get_stats = ieee80211_get_stats;
-	dev->open = ieee80211_open;
-	dev->stop = ieee80211_stop;
-	dev->uninit = ieee80211_if_reinit;
-	dev->destructor = ieee80211_if_free;
-}
-
-void ieee80211_if_mgmt_setup(struct net_device *dev)
-{
-	ether_setup(dev);
-	dev->hard_start_xmit = ieee80211_mgmt_start_xmit;
-	dev->change_mtu = ieee80211_change_mtu_apdev;
-	dev->get_stats = ieee80211_get_stats;
-	dev->open = ieee80211_mgmt_open;
-	dev->stop = ieee80211_mgmt_stop;
-	dev->type = ARPHRD_IEEE80211_PRISM;
-	dev->hard_header_parse = header_parse_80211;
-	dev->uninit = ieee80211_if_reinit;
-	dev->destructor = ieee80211_if_free;
-}
-
-int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
-				 const char *name)
-{
-	struct rate_control_ref *ref, *old;
-
-	ASSERT_RTNL();
-	if (local->open_count || netif_running(local->mdev) ||
-	    (local->apdev && netif_running(local->apdev)))
-		return -EBUSY;
-
-	ref = rate_control_alloc(name, local);
-	if (!ref) {
-		printk(KERN_WARNING "%s: Failed to select rate control "
-		       "algorithm\n", local->mdev->name);
-		return -ENOENT;
-	}
-
-	old = local->rate_ctrl;
-	local->rate_ctrl = ref;
-	if (old) {
-		rate_control_put(old);
-		sta_info_flush(local, NULL);
-	}
-
-	printk(KERN_DEBUG "%s: Selected rate control "
-	       "algorithm '%s'\n", local->mdev->name,
-	       ref->ops->name);
-
-
-	return 0;
-}
-
-static void rate_control_deinitialize(struct ieee80211_local *local)
-{
-	struct rate_control_ref *ref;
-
-	ref = local->rate_ctrl;
-	local->rate_ctrl = NULL;
-	rate_control_put(ref);
-}
-
 struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
 					const struct ieee80211_ops *ops)
 {
@@ -5166,65 +1518,6 @@ void ieee80211_free_hw(struct ieee80211_hw *hw)
 }
 EXPORT_SYMBOL(ieee80211_free_hw);
 
-void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
-{
-	struct ieee80211_local *local = hw_to_local(hw);
-
-	if (test_and_clear_bit(IEEE80211_LINK_STATE_XOFF,
-			       &local->state[queue])) {
-		if (test_bit(IEEE80211_LINK_STATE_PENDING,
-			     &local->state[queue]))
-			tasklet_schedule(&local->tx_pending_tasklet);
-		else
-			if (!ieee80211_qdisc_installed(local->mdev)) {
-				if (queue == 0)
-					netif_wake_queue(local->mdev);
-			} else
-				__netif_schedule(local->mdev);
-	}
-}
-EXPORT_SYMBOL(ieee80211_wake_queue);
-
-void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
-{
-	struct ieee80211_local *local = hw_to_local(hw);
-
-	if (!ieee80211_qdisc_installed(local->mdev) && queue == 0)
-		netif_stop_queue(local->mdev);
-	set_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
-}
-EXPORT_SYMBOL(ieee80211_stop_queue);
-
-void ieee80211_start_queues(struct ieee80211_hw *hw)
-{
-	struct ieee80211_local *local = hw_to_local(hw);
-	int i;
-
-	for (i = 0; i < local->hw.queues; i++)
-		clear_bit(IEEE80211_LINK_STATE_XOFF, &local->state[i]);
-	if (!ieee80211_qdisc_installed(local->mdev))
-		netif_start_queue(local->mdev);
-}
-EXPORT_SYMBOL(ieee80211_start_queues);
-
-void ieee80211_stop_queues(struct ieee80211_hw *hw)
-{
-	int i;
-
-	for (i = 0; i < hw->queues; i++)
-		ieee80211_stop_queue(hw, i);
-}
-EXPORT_SYMBOL(ieee80211_stop_queues);
-
-void ieee80211_wake_queues(struct ieee80211_hw *hw)
-{
-	int i;
-
-	for (i = 0; i < hw->queues; i++)
-		ieee80211_wake_queue(hw, i);
-}
-EXPORT_SYMBOL(ieee80211_wake_queues);
-
 struct net_device_stats *ieee80211_dev_stats(struct net_device *dev)
 {
 	struct ieee80211_sub_if_data *sdata;
@@ -5252,7 +1545,6 @@ static int __init ieee80211_init(void)
 	return 0;
 }
 
-
 static void __exit ieee80211_exit(void)
 {
 	ieee80211_wme_unregister();
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 6f7bae7..cc9999c 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -21,6 +21,7 @@
 #include <linux/workqueue.h>
 #include <linux/types.h>
 #include <linux/spinlock.h>
+#include <linux/etherdevice.h>
 #include <net/wireless.h>
 #include "ieee80211_key.h"
 #include "sta_info.h"
@@ -126,7 +127,6 @@ struct ieee80211_txrx_data {
 			struct ieee80211_tx_control *control;
 			unsigned int unicast:1;
 			unsigned int ps_buffered:1;
-			unsigned int short_preamble:1;
 			unsigned int probe_last_frag:1;
 			struct ieee80211_hw_mode *mode;
 			struct ieee80211_rate *rate;
@@ -285,6 +285,11 @@ struct ieee80211_sub_if_data {
 	unsigned int promisc:1;
 	unsigned int use_protection:1; /* CTS protect ERP frames */
 
+	/* use short preamble with IEEE 802.11b: this flag is set when the AP
+	 * or beacon generator reports that there are no present stations that
+	 * cannot support short preambles */
+	unsigned int short_preamble:1;
+
 	struct net_device_stats stats;
 	int drop_unencrypted;
 	int eapol; /* 0 = process EAPOL frames as normal data frames,
@@ -416,10 +421,9 @@ struct ieee80211_local {
 	struct sk_buff_head skb_queue_unreliable;
 
 	/* Station data structures */
-	spinlock_t sta_lock; /* mutex for STA data structures */
+	rwlock_t sta_lock; /* protects STA data structures */
 	int num_sta; /* number of stations in sta_list */
 	struct list_head sta_list;
-	struct list_head deleted_sta_list;
 	struct sta_info *sta_hash[STA_HASH_SIZE];
 	struct timer_list sta_cleanup;
 
@@ -447,7 +451,6 @@ struct ieee80211_local {
 	int fragmentation_threshold;
 	int short_retry_limit; /* dot11ShortRetryLimit */
 	int long_retry_limit; /* dot11LongRetryLimit */
-	int short_preamble; /* use short preamble with IEEE 802.11b */
 
 	struct crypto_blkcipher *wep_tx_tfm;
 	struct crypto_blkcipher *wep_rx_tfm;
@@ -668,9 +671,9 @@ static inline void __bss_tim_set(struct ieee80211_if_ap *bss, int aid)
 static inline void bss_tim_set(struct ieee80211_local *local,
 			       struct ieee80211_if_ap *bss, int aid)
 {
-	spin_lock_bh(&local->sta_lock);
+	read_lock_bh(&local->sta_lock);
 	__bss_tim_set(bss, aid);
-	spin_unlock_bh(&local->sta_lock);
+	read_unlock_bh(&local->sta_lock);
 }
 
 static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, int aid)
@@ -685,9 +688,9 @@ static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, int aid)
 static inline void bss_tim_clear(struct ieee80211_local *local,
 				 struct ieee80211_if_ap *bss, int aid)
 {
-	spin_lock_bh(&local->sta_lock);
+	read_lock_bh(&local->sta_lock);
 	__bss_tim_clear(bss, aid);
-	spin_unlock_bh(&local->sta_lock);
+	read_unlock_bh(&local->sta_lock);
 }
 
 /**
@@ -707,29 +710,31 @@ static inline int ieee80211_is_erp_rate(int phymode, int rate)
 	return 0;
 }
 
+static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
+{
+	return compare_ether_addr(raddr, addr) == 0 ||
+	       is_broadcast_ether_addr(raddr);
+}
+
+
 /* ieee80211.c */
 int ieee80211_hw_config(struct ieee80211_local *local);
 int ieee80211_if_config(struct net_device *dev);
 int ieee80211_if_config_beacon(struct net_device *dev);
-struct ieee80211_key_conf *
-ieee80211_key_data2conf(struct ieee80211_local *local,
-			const struct ieee80211_key *data);
-struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
-					  int idx, size_t key_len, gfp_t flags);
-void ieee80211_key_free(struct ieee80211_key *key);
 void ieee80211_rx_mgmt(struct ieee80211_local *local, struct sk_buff *skb,
 		       struct ieee80211_rx_status *status, u32 msg_type);
 void ieee80211_prepare_rates(struct ieee80211_local *local,
 			     struct ieee80211_hw_mode *mode);
 void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx);
 int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr);
-int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
-int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
 void ieee80211_if_setup(struct net_device *dev);
 void ieee80211_if_mgmt_setup(struct net_device *dev);
-int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
-				 const char *name);
 struct net_device_stats *ieee80211_dev_stats(struct net_device *dev);
+struct ieee80211_rate *ieee80211_get_rate(struct ieee80211_local *local,
+					  int phymode, int hwrate);
+void ieee80211_key_threshold_notify(struct net_device *dev,
+				    struct ieee80211_key *key,
+				    struct sta_info *sta);
 
 /* ieee80211_ioctl.c */
 extern const struct iw_handler_def ieee80211_iw_handler_def;
@@ -783,6 +788,8 @@ struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev,
 					 u8 *addr);
 int ieee80211_sta_deauthenticate(struct net_device *dev, u16 reason);
 int ieee80211_sta_disassociate(struct net_device *dev, u16 reason);
+void ieee80211_erp_info_change_notify(struct net_device *dev, u8 changes);
+void ieee80211_reset_erp_info(struct net_device *dev);
 
 /* ieee80211_iface.c */
 int ieee80211_if_add(struct net_device *dev, const char *name,
@@ -801,7 +808,34 @@ void ieee80211_if_del_mgmt(struct ieee80211_local *local);
 void ieee80211_regdomain_init(void);
 void ieee80211_set_default_regdomain(struct ieee80211_hw_mode *mode);
 
-/* for wiphy privid */
-extern void *mac80211_wiphy_privid;
+/* rx handling */
+extern ieee80211_rx_handler ieee80211_rx_pre_handlers[];
+extern ieee80211_rx_handler ieee80211_rx_handlers[];
+
+/* tx handling */
+extern ieee80211_tx_handler ieee80211_tx_handlers[];
+void ieee80211_clear_tx_pending(struct ieee80211_local *local);
+void ieee80211_tx_pending(unsigned long data);
+int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev);
+int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
+int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
+int ieee80211_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev);
+
+/* key handling */
+struct ieee80211_key_conf *
+ieee80211_key_data2conf(struct ieee80211_local *local,
+			const struct ieee80211_key *data);
+struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
+					  int idx, size_t key_len, gfp_t flags);
+void ieee80211_key_free(struct ieee80211_key *key);
+
+/* utility functions/constants */
+extern void *mac80211_wiphy_privid; /* for wiphy privid */
+extern const unsigned char rfc1042_header[6];
+extern const unsigned char bridge_tunnel_header[6];
+u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len);
+int ieee80211_is_eapol(const struct sk_buff *skb);
+int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
+			     int rate, int erp, int short_preamble);
 
 #endif /* IEEE80211_I_H */
diff --git a/net/mac80211/ieee80211_iface.c b/net/mac80211/ieee80211_iface.c
index 8532a5c..6db6776 100644
--- a/net/mac80211/ieee80211_iface.c
+++ b/net/mac80211/ieee80211_iface.c
@@ -272,8 +272,8 @@ void ieee80211_if_reinit(struct net_device *dev)
 	case IEEE80211_IF_TYPE_WDS:
 		sta = sta_info_get(local, sdata->u.wds.remote_addr);
 		if (sta) {
+			sta_info_free(sta);
 			sta_info_put(sta);
-			sta_info_free(sta, 0);
 		} else {
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
 			printk(KERN_DEBUG "%s: Someone had deleted my STA "
diff --git a/net/mac80211/ieee80211_ioctl.c b/net/mac80211/ieee80211_ioctl.c
index e7904db..1fde214 100644
--- a/net/mac80211/ieee80211_ioctl.c
+++ b/net/mac80211/ieee80211_ioctl.c
@@ -1054,14 +1054,21 @@ static int ieee80211_ioctl_prism2_param(struct net_device *dev,
 		break;
 
 	case PRISM2_PARAM_CTS_PROTECT_ERP_FRAMES:
-		if (sdata->type != IEEE80211_IF_TYPE_AP)
+		if (sdata->type == IEEE80211_IF_TYPE_AP) {
+			sdata->use_protection = !!value;
+			ieee80211_erp_info_change_notify(dev, IEEE80211_ERP_CHANGE_PROTECTION);
+		} else {
 			ret = -ENOENT;
-		else
-			sdata->use_protection = value;
+		}
 		break;
 
 	case PRISM2_PARAM_PREAMBLE:
-		local->short_preamble = value;
+		if (sdata->type != IEEE80211_IF_TYPE_AP) {
+			sdata->short_preamble = !!value;
+			ieee80211_erp_info_change_notify(dev, IEEE80211_ERP_CHANGE_PREAMBLE);
+		} else {
+			ret = -ENOENT;
+		}
 		break;
 
 	case PRISM2_PARAM_STAT_TIME:
@@ -1184,7 +1191,7 @@ static int ieee80211_ioctl_get_prism2_param(struct net_device *dev,
 		break;
 
 	case PRISM2_PARAM_PREAMBLE:
-		*param = local->short_preamble;
+		*param = sdata->short_preamble;
 		break;
 
 	case PRISM2_PARAM_STAT_TIME:
diff --git a/net/mac80211/ieee80211_rate.c b/net/mac80211/ieee80211_rate.c
index 2118de0..a1ded74 100644
--- a/net/mac80211/ieee80211_rate.c
+++ b/net/mac80211/ieee80211_rate.c
@@ -9,6 +9,7 @@
  */
 
 #include <linux/kernel.h>
+#include <linux/rtnetlink.h>
 #include "ieee80211_rate.h"
 #include "ieee80211_i.h"
 
@@ -137,3 +138,44 @@ void rate_control_put(struct rate_control_ref *ref)
 {
 	kref_put(&ref->kref, rate_control_release);
 }
+
+int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
+				 const char *name)
+{
+	struct rate_control_ref *ref, *old;
+
+	ASSERT_RTNL();
+	if (local->open_count || netif_running(local->mdev) ||
+	    (local->apdev && netif_running(local->apdev)))
+		return -EBUSY;
+
+	ref = rate_control_alloc(name, local);
+	if (!ref) {
+		printk(KERN_WARNING "%s: Failed to select rate control "
+		       "algorithm\n", local->mdev->name);
+		return -ENOENT;
+	}
+
+	old = local->rate_ctrl;
+	local->rate_ctrl = ref;
+	if (old) {
+		rate_control_put(old);
+		sta_info_flush(local, NULL);
+	}
+
+	printk(KERN_DEBUG "%s: Selected rate control "
+	       "algorithm '%s'\n", local->mdev->name,
+	       ref->ops->name);
+
+
+	return 0;
+}
+
+void rate_control_deinitialize(struct ieee80211_local *local)
+{
+	struct rate_control_ref *ref;
+
+	ref = local->rate_ctrl;
+	local->rate_ctrl = NULL;
+	rate_control_put(ref);
+}
diff --git a/net/mac80211/ieee80211_rate.h b/net/mac80211/ieee80211_rate.h
index f021a02..cac91a9 100644
--- a/net/mac80211/ieee80211_rate.h
+++ b/net/mac80211/ieee80211_rate.h
@@ -141,4 +141,10 @@ static inline void rate_control_remove_sta_debugfs(struct sta_info *sta)
 #endif
 }
 
+
+/* functions for rate control related to a device */
+int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
+				 const char *name);
+void rate_control_deinitialize(struct ieee80211_local *local);
+
 #endif /* IEEE80211_RATE_H */
diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c
index 7ba352e..8c41023 100644
--- a/net/mac80211/ieee80211_sta.c
+++ b/net/mac80211/ieee80211_sta.c
@@ -318,6 +318,8 @@ static void ieee80211_handle_erp_ie(struct net_device *dev, u8 erp_value)
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	struct ieee80211_if_sta *ifsta = &sdata->u.sta;
 	int use_protection = (erp_value & WLAN_ERP_USE_PROTECTION) != 0;
+	int preamble_mode = (erp_value & WLAN_ERP_BARKER_PREAMBLE) != 0;
+	u8 changes = 0;
 
 	if (use_protection != sdata->use_protection) {
 		if (net_ratelimit()) {
@@ -328,7 +330,24 @@ static void ieee80211_handle_erp_ie(struct net_device *dev, u8 erp_value)
 			       MAC_ARG(ifsta->bssid));
 		}
 		sdata->use_protection = use_protection;
+		changes |= IEEE80211_ERP_CHANGE_PROTECTION;
 	}
+
+	if (!preamble_mode != sdata->short_preamble) {
+		if (net_ratelimit()) {
+			printk(KERN_DEBUG "%s: switched to %s barker preamble"
+			       " (BSSID=" MAC_FMT ")\n",
+			       dev->name,
+			       (preamble_mode == WLAN_ERP_PREAMBLE_SHORT) ?
+					"short" : "long",
+			       MAC_ARG(ifsta->bssid));
+		}
+		sdata->short_preamble = !preamble_mode;
+		changes |= IEEE80211_ERP_CHANGE_PREAMBLE;
+	}
+
+	if (changes)
+		ieee80211_erp_info_change_notify(dev, changes);
 }
 
 
@@ -344,7 +363,7 @@ static void ieee80211_sta_send_associnfo(struct net_device *dev,
 		return;
 
 	buf = kmalloc(50 + 2 * (ifsta->assocreq_ies_len +
-				ifsta->assocresp_ies_len), GFP_ATOMIC);
+				ifsta->assocresp_ies_len), GFP_KERNEL);
 	if (!buf)
 		return;
 
@@ -387,7 +406,6 @@ static void ieee80211_set_associated(struct net_device *dev,
 				     struct ieee80211_if_sta *ifsta, int assoc)
 {
 	union iwreq_data wrqu;
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
 	if (ifsta->associated == assoc)
 		return;
@@ -415,7 +433,7 @@ static void ieee80211_set_associated(struct net_device *dev,
 		ieee80211_sta_send_associnfo(dev, ifsta);
 	} else {
 		netif_carrier_off(dev);
-		sdata->use_protection = 0;
+		ieee80211_reset_erp_info(dev);
 		memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
 	}
 	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
@@ -626,7 +644,7 @@ static void ieee80211_send_assoc(struct net_device *dev,
 
 	kfree(ifsta->assocreq_ies);
 	ifsta->assocreq_ies_len = (skb->data + skb->len) - ies;
-	ifsta->assocreq_ies = kmalloc(ifsta->assocreq_ies_len, GFP_ATOMIC);
+	ifsta->assocreq_ies = kmalloc(ifsta->assocreq_ies_len, GFP_KERNEL);
 	if (ifsta->assocreq_ies)
 		memcpy(ifsta->assocreq_ies, ies, ifsta->assocreq_ies_len);
 
@@ -773,7 +791,7 @@ static void ieee80211_associated(struct net_device *dev,
 				       "range\n",
 				       dev->name, MAC_ARG(ifsta->bssid));
 				disassoc = 1;
-				sta_info_free(sta, 0);
+				sta_info_free(sta);
 				ifsta->probereq_poll = 0;
 			} else {
 				ieee80211_send_probe_req(dev, ifsta->bssid,
@@ -1187,8 +1205,10 @@ static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev,
 	if (status_code != WLAN_STATUS_SUCCESS) {
 		printk(KERN_DEBUG "%s: AP denied association (code=%d)\n",
 		       dev->name, status_code);
-		if (status_code == WLAN_STATUS_REASSOC_NO_ASSOC)
-			ifsta->prev_bssid_set = 0;
+		/* if this was a reassociation, ensure we try a "full"
+		 * association next time. This works around some broken APs
+		 * which do not correctly reject reassociation requests. */
+		ifsta->prev_bssid_set = 0;
 		return;
 	}
 
@@ -1224,7 +1244,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev,
 
 	kfree(ifsta->assocresp_ies);
 	ifsta->assocresp_ies_len = len - (pos - (u8 *) mgmt);
-	ifsta->assocresp_ies = kmalloc(ifsta->assocresp_ies_len, GFP_ATOMIC);
+	ifsta->assocresp_ies = kmalloc(ifsta->assocresp_ies_len, GFP_KERNEL);
 	if (ifsta->assocresp_ies)
 		memcpy(ifsta->assocresp_ies, pos, ifsta->assocresp_ies_len);
 
@@ -1234,7 +1254,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev,
 	sta = sta_info_get(local, ifsta->bssid);
 	if (!sta) {
 		struct ieee80211_sta_bss *bss;
-		sta = sta_info_add(local, dev, ifsta->bssid, GFP_ATOMIC);
+		sta = sta_info_add(local, dev, ifsta->bssid, GFP_KERNEL);
 		if (!sta) {
 			printk(KERN_DEBUG "%s: failed to add STA entry for the"
 			       " AP\n", dev->name);
@@ -1751,7 +1771,7 @@ static void ieee80211_rx_mgmt_probe_req(struct net_device *dev,
 	}
 
 	/* Reply with ProbeResp */
-	skb = skb_copy(ifsta->probe_resp, GFP_ATOMIC);
+	skb = skb_copy(ifsta->probe_resp, GFP_KERNEL);
 	if (!skb)
 		return;
 
@@ -1890,7 +1910,7 @@ static int ieee80211_sta_active_ibss(struct net_device *dev)
 	int active = 0;
 	struct sta_info *sta;
 
-	spin_lock_bh(&local->sta_lock);
+	read_lock_bh(&local->sta_lock);
 	list_for_each_entry(sta, &local->sta_list, list) {
 		if (sta->dev == dev &&
 		    time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
@@ -1899,7 +1919,7 @@ static int ieee80211_sta_active_ibss(struct net_device *dev)
 			break;
 		}
 	}
-	spin_unlock_bh(&local->sta_lock);
+	read_unlock_bh(&local->sta_lock);
 
 	return active;
 }
@@ -1909,16 +1929,24 @@ static void ieee80211_sta_expire(struct net_device *dev)
 {
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct sta_info *sta, *tmp;
+	LIST_HEAD(tmp_list);
 
-	spin_lock_bh(&local->sta_lock);
+	write_lock_bh(&local->sta_lock);
 	list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
 		if (time_after(jiffies, sta->last_rx +
 			       IEEE80211_IBSS_INACTIVITY_LIMIT)) {
 			printk(KERN_DEBUG "%s: expiring inactive STA " MAC_FMT
 			       "\n", dev->name, MAC_ARG(sta->addr));
-			sta_info_free(sta, 1);
+			__sta_info_get(sta);
+			sta_info_remove(sta);
+			list_add(&sta->list, &tmp_list);
 		}
-	spin_unlock_bh(&local->sta_lock);
+	write_unlock_bh(&local->sta_lock);
+
+	list_for_each_entry_safe(sta, tmp, &tmp_list, list) {
+		sta_info_free(sta);
+		sta_info_put(sta);
+	}
 }
 
 
@@ -2267,7 +2295,7 @@ static int ieee80211_sta_join_ibss(struct net_device *dev,
 			       "for IBSS beacon\n", dev->name);
 			break;
 		}
-		control.tx_rate = (local->short_preamble &&
+		control.tx_rate = (sdata->short_preamble &&
 				   (rate->flags & IEEE80211_RATE_PREAMBLE2)) ?
 			rate->val2 : rate->val;
 		control.antenna_sel_tx = local->hw.conf.antenna_sel_tx;
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
new file mode 100644
index 0000000..b67558c
--- /dev/null
+++ b/net/mac80211/key.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2002-2005, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <net/mac80211.h>
+#include "ieee80211_i.h"
+#include "debugfs_key.h"
+#include "aes_ccm.h"
+
+struct ieee80211_key_conf *
+ieee80211_key_data2conf(struct ieee80211_local *local,
+			const struct ieee80211_key *data)
+{
+	struct ieee80211_key_conf *conf;
+
+	conf = kmalloc(sizeof(*conf) + data->keylen, GFP_ATOMIC);
+	if (!conf)
+		return NULL;
+
+	conf->hw_key_idx = data->hw_key_idx;
+	conf->alg = data->alg;
+	conf->keylen = data->keylen;
+	conf->flags = 0;
+	if (data->force_sw_encrypt)
+		conf->flags |= IEEE80211_KEY_FORCE_SW_ENCRYPT;
+	conf->keyidx = data->keyidx;
+	if (data->default_tx_key)
+		conf->flags |= IEEE80211_KEY_DEFAULT_TX_KEY;
+	if (local->default_wep_only)
+		conf->flags |= IEEE80211_KEY_DEFAULT_WEP_ONLY;
+	memcpy(conf->key, data->key, data->keylen);
+
+	return conf;
+}
+
+struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
+					  int idx, size_t key_len, gfp_t flags)
+{
+	struct ieee80211_key *key;
+
+	key = kzalloc(sizeof(struct ieee80211_key) + key_len, flags);
+	if (!key)
+		return NULL;
+	kref_init(&key->kref);
+	return key;
+}
+
+static void ieee80211_key_release(struct kref *kref)
+{
+	struct ieee80211_key *key;
+
+	key = container_of(kref, struct ieee80211_key, kref);
+	if (key->alg == ALG_CCMP)
+		ieee80211_aes_key_free(key->u.ccmp.tfm);
+	ieee80211_debugfs_key_remove(key);
+	kfree(key);
+}
+
+void ieee80211_key_free(struct ieee80211_key *key)
+{
+	if (key)
+		kref_put(&key->kref, ieee80211_key_release);
+}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
new file mode 100644
index 0000000..95a00eb
--- /dev/null
+++ b/net/mac80211/rx.c
@@ -0,0 +1,1453 @@
+/*
+ * Copyright 2002-2005, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
+ * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <net/iw_handler.h>
+#include <net/mac80211.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "ieee80211_i.h"
+#include "ieee80211_led.h"
+#include "ieee80211_common.h"
+#include "wep.h"
+#include "wpa.h"
+#include "tkip.h"
+#include "wme.h"
+
+/* pre-rx handlers
+ *
+ * these don't have dev/sdata fields in the rx data
+ * The sta value should also not be used because it may
+ * be NULL even though a STA (in IBSS mode) will be added.
+ */
+
+static ieee80211_txrx_result
+ieee80211_rx_h_parse_qos(struct ieee80211_txrx_data *rx)
+{
+	u8 *data = rx->skb->data;
+	int tid;
+
+	/* does the frame have a qos control field? */
+	if (WLAN_FC_IS_QOS_DATA(rx->fc)) {
+		u8 *qc = data + ieee80211_get_hdrlen(rx->fc) - QOS_CONTROL_LEN;
+		/* frame has qos control */
+		tid = qc[0] & QOS_CONTROL_TID_MASK;
+	} else {
+		if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) {
+			/* Separate TID for management frames */
+			tid = NUM_RX_DATA_QUEUES - 1;
+		} else {
+			/* no qos control present */
+			tid = 0; /* 802.1d - Best Effort */
+		}
+	}
+
+	I802_DEBUG_INC(rx->local->wme_rx_queue[tid]);
+	/* only a debug counter, sta might not be assigned properly yet */
+	if (rx->sta)
+		I802_DEBUG_INC(rx->sta->wme_rx_queue[tid]);
+
+	rx->u.rx.queue = tid;
+	/* Set skb->priority to 1d tag if highest order bit of TID is not set.
+	 * For now, set skb->priority to 0 for other cases. */
+	rx->skb->priority = (tid > 7) ? 0 : tid;
+
+	return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_load_stats(struct ieee80211_txrx_data *rx)
+{
+	struct ieee80211_local *local = rx->local;
+	struct sk_buff *skb = rx->skb;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+	u32 load = 0, hdrtime;
+	struct ieee80211_rate *rate;
+	struct ieee80211_hw_mode *mode = local->hw.conf.mode;
+	int i;
+
+	/* Estimate total channel use caused by this frame */
+
+	if (unlikely(mode->num_rates < 0))
+		return TXRX_CONTINUE;
+
+	rate = &mode->rates[0];
+	for (i = 0; i < mode->num_rates; i++) {
+		if (mode->rates[i].val == rx->u.rx.status->rate) {
+			rate = &mode->rates[i];
+			break;
+		}
+	}
+
+	/* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values,
+	 * 1 usec = 1/8 * (1080 / 10) = 13.5 */
+
+	if (mode->mode == MODE_IEEE80211A ||
+	    mode->mode == MODE_ATHEROS_TURBO ||
+	    mode->mode == MODE_ATHEROS_TURBOG ||
+	    (mode->mode == MODE_IEEE80211G &&
+	     rate->flags & IEEE80211_RATE_ERP))
+		hdrtime = CHAN_UTIL_HDR_SHORT;
+	else
+		hdrtime = CHAN_UTIL_HDR_LONG;
+
+	load = hdrtime;
+	if (!is_multicast_ether_addr(hdr->addr1))
+		load += hdrtime;
+
+	load += skb->len * rate->rate_inv;
+
+	/* Divide channel_use by 8 to avoid wrapping around the counter */
+	load >>= CHAN_UTIL_SHIFT;
+	local->channel_use_raw += load;
+	rx->u.rx.load = load;
+
+	return TXRX_CONTINUE;
+}
+
+ieee80211_rx_handler ieee80211_rx_pre_handlers[] =
+{
+	ieee80211_rx_h_parse_qos,
+	ieee80211_rx_h_load_stats,
+	NULL
+};
+
+/* rx handlers */
+
+static ieee80211_txrx_result
+ieee80211_rx_h_if_stats(struct ieee80211_txrx_data *rx)
+{
+	if (rx->sta)
+		rx->sta->channel_use_raw += rx->u.rx.load;
+	rx->sdata->channel_use_raw += rx->u.rx.load;
+	return TXRX_CONTINUE;
+}
+
+static void
+ieee80211_rx_monitor(struct net_device *dev, struct sk_buff *skb,
+		     struct ieee80211_rx_status *status)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_sub_if_data *sdata;
+	struct ieee80211_rate *rate;
+	struct ieee80211_rtap_hdr {
+		struct ieee80211_radiotap_header hdr;
+		u8 flags;
+		u8 rate;
+		__le16 chan_freq;
+		__le16 chan_flags;
+		u8 antsignal;
+	} __attribute__ ((packed)) *rthdr;
+
+	skb->dev = dev;
+
+	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+	if (status->flag & RX_FLAG_RADIOTAP)
+		goto out;
+
+	if (skb_headroom(skb) < sizeof(*rthdr)) {
+		I802_DEBUG_INC(local->rx_expand_skb_head);
+		if (pskb_expand_head(skb, sizeof(*rthdr), 0, GFP_ATOMIC)) {
+			dev_kfree_skb(skb);
+			return;
+		}
+	}
+
+	rthdr = (struct ieee80211_rtap_hdr *) skb_push(skb, sizeof(*rthdr));
+	memset(rthdr, 0, sizeof(*rthdr));
+	rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr));
+	rthdr->hdr.it_present =
+		cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
+			    (1 << IEEE80211_RADIOTAP_RATE) |
+			    (1 << IEEE80211_RADIOTAP_CHANNEL) |
+			    (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL));
+	rthdr->flags = local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS ?
+		       IEEE80211_RADIOTAP_F_FCS : 0;
+	rate = ieee80211_get_rate(local, status->phymode, status->rate);
+	if (rate)
+		rthdr->rate = rate->rate / 5;
+	rthdr->chan_freq = cpu_to_le16(status->freq);
+	rthdr->chan_flags =
+		status->phymode == MODE_IEEE80211A ?
+		cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ) :
+		cpu_to_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ);
+	rthdr->antsignal = status->ssi;
+
+ out:
+	sdata->stats.rx_packets++;
+	sdata->stats.rx_bytes += skb->len;
+
+	skb_set_mac_header(skb, 0);
+	skb->ip_summed = CHECKSUM_UNNECESSARY;
+	skb->pkt_type = PACKET_OTHERHOST;
+	skb->protocol = htons(ETH_P_802_2);
+	memset(skb->cb, 0, sizeof(skb->cb));
+	netif_rx(skb);
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_monitor(struct ieee80211_txrx_data *rx)
+{
+	if (rx->sdata->type == IEEE80211_IF_TYPE_MNTR) {
+		ieee80211_rx_monitor(rx->dev, rx->skb, rx->u.rx.status);
+		return TXRX_QUEUED;
+	}
+
+	if (rx->u.rx.status->flag & RX_FLAG_RADIOTAP)
+		skb_pull(rx->skb, ieee80211_get_radiotap_len(rx->skb->data));
+
+	return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx)
+{
+	struct ieee80211_local *local = rx->local;
+	struct sk_buff *skb = rx->skb;
+
+	if (unlikely(local->sta_scanning != 0)) {
+		ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status);
+		return TXRX_QUEUED;
+	}
+
+	if (unlikely(rx->u.rx.in_scan)) {
+		/* scanning finished during invoking of handlers */
+		I802_DEBUG_INC(local->rx_handlers_drop_passive_scan);
+		return TXRX_DROP;
+	}
+
+	return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
+{
+	struct ieee80211_hdr *hdr;
+	hdr = (struct ieee80211_hdr *) rx->skb->data;
+
+	/* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */
+	if (rx->sta && !is_multicast_ether_addr(hdr->addr1)) {
+		if (unlikely(rx->fc & IEEE80211_FCTL_RETRY &&
+			     rx->sta->last_seq_ctrl[rx->u.rx.queue] ==
+			     hdr->seq_ctrl)) {
+			if (rx->u.rx.ra_match) {
+				rx->local->dot11FrameDuplicateCount++;
+				rx->sta->num_duplicates++;
+			}
+			return TXRX_DROP;
+		} else
+			rx->sta->last_seq_ctrl[rx->u.rx.queue] = hdr->seq_ctrl;
+	}
+
+	if ((rx->local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) &&
+	    rx->skb->len > FCS_LEN)
+		skb_trim(rx->skb, rx->skb->len - FCS_LEN);
+
+	if (unlikely(rx->skb->len < 16)) {
+		I802_DEBUG_INC(rx->local->rx_handlers_drop_short);
+		return TXRX_DROP;
+	}
+
+	if (!rx->u.rx.ra_match)
+		rx->skb->pkt_type = PACKET_OTHERHOST;
+	else if (compare_ether_addr(rx->dev->dev_addr, hdr->addr1) == 0)
+		rx->skb->pkt_type = PACKET_HOST;
+	else if (is_multicast_ether_addr(hdr->addr1)) {
+		if (is_broadcast_ether_addr(hdr->addr1))
+			rx->skb->pkt_type = PACKET_BROADCAST;
+		else
+			rx->skb->pkt_type = PACKET_MULTICAST;
+	} else
+		rx->skb->pkt_type = PACKET_OTHERHOST;
+
+	/* Drop disallowed frame classes based on STA auth/assoc state;
+	 * IEEE 802.11, Chap 5.5.
+	 *
+	 * 80211.o does filtering only based on association state, i.e., it
+	 * drops Class 3 frames from not associated stations. hostapd sends
+	 * deauth/disassoc frames when needed. In addition, hostapd is
+	 * responsible for filtering on both auth and assoc states.
+	 */
+	if (unlikely(((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA ||
+		      ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL &&
+		       (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)) &&
+		     rx->sdata->type != IEEE80211_IF_TYPE_IBSS &&
+		     (!rx->sta || !(rx->sta->flags & WLAN_STA_ASSOC)))) {
+		if ((!(rx->fc & IEEE80211_FCTL_FROMDS) &&
+		     !(rx->fc & IEEE80211_FCTL_TODS) &&
+		     (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)
+		    || !rx->u.rx.ra_match) {
+			/* Drop IBSS frames and frames for other hosts
+			 * silently. */
+			return TXRX_DROP;
+		}
+
+		if (!rx->local->apdev)
+			return TXRX_DROP;
+
+		ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
+				  ieee80211_msg_sta_not_assoc);
+		return TXRX_QUEUED;
+	}
+
+	return TXRX_CONTINUE;
+}
+
+
+static ieee80211_txrx_result
+ieee80211_rx_h_load_key(struct ieee80211_txrx_data *rx)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
+	int always_sta_key;
+
+	if (rx->sdata->type == IEEE80211_IF_TYPE_STA)
+		always_sta_key = 0;
+	else
+		always_sta_key = 1;
+
+	if (rx->sta && rx->sta->key && always_sta_key) {
+		rx->key = rx->sta->key;
+	} else {
+		if (rx->sta && rx->sta->key)
+			rx->key = rx->sta->key;
+		else
+			rx->key = rx->sdata->default_key;
+
+		if ((rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) &&
+		    rx->fc & IEEE80211_FCTL_PROTECTED) {
+			int keyidx = ieee80211_wep_get_keyidx(rx->skb);
+
+			if (keyidx >= 0 && keyidx < NUM_DEFAULT_KEYS &&
+			    (!rx->sta || !rx->sta->key || keyidx > 0))
+				rx->key = rx->sdata->keys[keyidx];
+
+			if (!rx->key) {
+				if (!rx->u.rx.ra_match)
+					return TXRX_DROP;
+				printk(KERN_DEBUG "%s: RX WEP frame with "
+				       "unknown keyidx %d (A1=" MAC_FMT " A2="
+				       MAC_FMT " A3=" MAC_FMT ")\n",
+				       rx->dev->name, keyidx,
+				       MAC_ARG(hdr->addr1),
+				       MAC_ARG(hdr->addr2),
+				       MAC_ARG(hdr->addr3));
+				if (!rx->local->apdev)
+					return TXRX_DROP;
+				ieee80211_rx_mgmt(
+					rx->local, rx->skb, rx->u.rx.status,
+					ieee80211_msg_wep_frame_unknown_key);
+				return TXRX_QUEUED;
+			}
+		}
+	}
+
+	if (rx->fc & IEEE80211_FCTL_PROTECTED && rx->key && rx->u.rx.ra_match) {
+		rx->key->tx_rx_count++;
+		if (unlikely(rx->local->key_tx_rx_threshold &&
+			     rx->key->tx_rx_count >
+			     rx->local->key_tx_rx_threshold)) {
+			ieee80211_key_threshold_notify(rx->dev, rx->key,
+						       rx->sta);
+		}
+	}
+
+	return TXRX_CONTINUE;
+}
+
+static void ap_sta_ps_start(struct net_device *dev, struct sta_info *sta)
+{
+	struct ieee80211_sub_if_data *sdata;
+	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+
+	if (sdata->bss)
+		atomic_inc(&sdata->bss->num_sta_ps);
+	sta->flags |= WLAN_STA_PS;
+	sta->pspoll = 0;
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+	printk(KERN_DEBUG "%s: STA " MAC_FMT " aid %d enters power "
+	       "save mode\n", dev->name, MAC_ARG(sta->addr), sta->aid);
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+}
+
+static int ap_sta_ps_end(struct net_device *dev, struct sta_info *sta)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct sk_buff *skb;
+	int sent = 0;
+	struct ieee80211_sub_if_data *sdata;
+	struct ieee80211_tx_packet_data *pkt_data;
+
+	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	if (sdata->bss)
+		atomic_dec(&sdata->bss->num_sta_ps);
+	sta->flags &= ~(WLAN_STA_PS | WLAN_STA_TIM);
+	sta->pspoll = 0;
+	if (!skb_queue_empty(&sta->ps_tx_buf)) {
+		if (local->ops->set_tim)
+			local->ops->set_tim(local_to_hw(local), sta->aid, 0);
+		if (sdata->bss)
+			bss_tim_clear(local, sdata->bss, sta->aid);
+	}
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+	printk(KERN_DEBUG "%s: STA " MAC_FMT " aid %d exits power "
+	       "save mode\n", dev->name, MAC_ARG(sta->addr), sta->aid);
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+	/* Send all buffered frames to the station */
+	while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
+		pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
+		sent++;
+		pkt_data->requeue = 1;
+		dev_queue_xmit(skb);
+	}
+	while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
+		pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
+		local->total_ps_buffered--;
+		sent++;
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+		printk(KERN_DEBUG "%s: STA " MAC_FMT " aid %d send PS frame "
+		       "since STA not sleeping anymore\n", dev->name,
+		       MAC_ARG(sta->addr), sta->aid);
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+		pkt_data->requeue = 1;
+		dev_queue_xmit(skb);
+	}
+
+	return sent;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_sta_process(struct ieee80211_txrx_data *rx)
+{
+	struct sta_info *sta = rx->sta;
+	struct net_device *dev = rx->dev;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
+
+	if (!sta)
+		return TXRX_CONTINUE;
+
+	/* Update last_rx only for IBSS packets which are for the current
+	 * BSSID to avoid keeping the current IBSS network alive in cases where
+	 * other STAs are using different BSSID. */
+	if (rx->sdata->type == IEEE80211_IF_TYPE_IBSS) {
+		u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len);
+		if (compare_ether_addr(bssid, rx->sdata->u.sta.bssid) == 0)
+			sta->last_rx = jiffies;
+	} else
+	if (!is_multicast_ether_addr(hdr->addr1) ||
+	    rx->sdata->type == IEEE80211_IF_TYPE_STA) {
+		/* Update last_rx only for unicast frames in order to prevent
+		 * the Probe Request frames (the only broadcast frames from a
+		 * STA in infrastructure mode) from keeping a connection alive.
+		 */
+		sta->last_rx = jiffies;
+	}
+
+	if (!rx->u.rx.ra_match)
+		return TXRX_CONTINUE;
+
+	sta->rx_fragments++;
+	sta->rx_bytes += rx->skb->len;
+	sta->last_rssi = (sta->last_rssi * 15 +
+			  rx->u.rx.status->ssi) / 16;
+	sta->last_signal = (sta->last_signal * 15 +
+			    rx->u.rx.status->signal) / 16;
+	sta->last_noise = (sta->last_noise * 15 +
+			   rx->u.rx.status->noise) / 16;
+
+	if (!(rx->fc & IEEE80211_FCTL_MOREFRAGS)) {
+		/* Change STA power saving mode only in the end of a frame
+		 * exchange sequence */
+		if ((sta->flags & WLAN_STA_PS) && !(rx->fc & IEEE80211_FCTL_PM))
+			rx->u.rx.sent_ps_buffered += ap_sta_ps_end(dev, sta);
+		else if (!(sta->flags & WLAN_STA_PS) &&
+			 (rx->fc & IEEE80211_FCTL_PM))
+			ap_sta_ps_start(dev, sta);
+	}
+
+	/* Drop data::nullfunc frames silently, since they are used only to
+	 * control station power saving mode. */
+	if ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
+	    (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_NULLFUNC) {
+		I802_DEBUG_INC(rx->local->rx_handlers_drop_nullfunc);
+		/* Update counter and free packet here to avoid counting this
+		 * as a dropped packed. */
+		sta->rx_packets++;
+		dev_kfree_skb(rx->skb);
+		return TXRX_QUEUED;
+	}
+
+	return TXRX_CONTINUE;
+} /* ieee80211_rx_h_sta_process */
+
+static ieee80211_txrx_result
+ieee80211_rx_h_wep_weak_iv_detection(struct ieee80211_txrx_data *rx)
+{
+	if (!rx->sta || !(rx->fc & IEEE80211_FCTL_PROTECTED) ||
+	    (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
+	    !rx->key || rx->key->alg != ALG_WEP || !rx->u.rx.ra_match)
+		return TXRX_CONTINUE;
+
+	/* Check for weak IVs, if hwaccel did not remove IV from the frame */
+	if ((rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) ||
+	    rx->key->force_sw_encrypt) {
+		u8 *iv = ieee80211_wep_is_weak_iv(rx->skb, rx->key);
+		if (iv) {
+			rx->sta->wep_weak_iv_count++;
+		}
+	}
+
+	return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_wep_decrypt(struct ieee80211_txrx_data *rx)
+{
+	/* If the device handles decryption totally, skip this test */
+	if (rx->local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP)
+		return TXRX_CONTINUE;
+
+	if ((rx->key && rx->key->alg != ALG_WEP) ||
+	    !(rx->fc & IEEE80211_FCTL_PROTECTED) ||
+	    ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
+	     ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
+	      (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH)))
+		return TXRX_CONTINUE;
+
+	if (!rx->key) {
+		printk(KERN_DEBUG "%s: RX WEP frame, but no key set\n",
+		       rx->dev->name);
+		return TXRX_DROP;
+	}
+
+	if (!(rx->u.rx.status->flag & RX_FLAG_DECRYPTED) ||
+	    rx->key->force_sw_encrypt) {
+		if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key)) {
+			printk(KERN_DEBUG "%s: RX WEP frame, decrypt "
+			       "failed\n", rx->dev->name);
+			return TXRX_DROP;
+		}
+	} else if (rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) {
+		ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
+		/* remove ICV */
+		skb_trim(rx->skb, rx->skb->len - 4);
+	}
+
+	return TXRX_CONTINUE;
+}
+
+static inline struct ieee80211_fragment_entry *
+ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
+			 unsigned int frag, unsigned int seq, int rx_queue,
+			 struct sk_buff **skb)
+{
+	struct ieee80211_fragment_entry *entry;
+	int idx;
+
+	idx = sdata->fragment_next;
+	entry = &sdata->fragments[sdata->fragment_next++];
+	if (sdata->fragment_next >= IEEE80211_FRAGMENT_MAX)
+		sdata->fragment_next = 0;
+
+	if (!skb_queue_empty(&entry->skb_list)) {
+#ifdef CONFIG_MAC80211_DEBUG
+		struct ieee80211_hdr *hdr =
+			(struct ieee80211_hdr *) entry->skb_list.next->data;
+		printk(KERN_DEBUG "%s: RX reassembly removed oldest "
+		       "fragment entry (idx=%d age=%lu seq=%d last_frag=%d "
+		       "addr1=" MAC_FMT " addr2=" MAC_FMT "\n",
+		       sdata->dev->name, idx,
+		       jiffies - entry->first_frag_time, entry->seq,
+		       entry->last_frag, MAC_ARG(hdr->addr1),
+		       MAC_ARG(hdr->addr2));
+#endif /* CONFIG_MAC80211_DEBUG */
+		__skb_queue_purge(&entry->skb_list);
+	}
+
+	__skb_queue_tail(&entry->skb_list, *skb); /* no need for locking */
+	*skb = NULL;
+	entry->first_frag_time = jiffies;
+	entry->seq = seq;
+	entry->rx_queue = rx_queue;
+	entry->last_frag = frag;
+	entry->ccmp = 0;
+	entry->extra_len = 0;
+
+	return entry;
+}
+
+static inline struct ieee80211_fragment_entry *
+ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata,
+			  u16 fc, unsigned int frag, unsigned int seq,
+			  int rx_queue, struct ieee80211_hdr *hdr)
+{
+	struct ieee80211_fragment_entry *entry;
+	int i, idx;
+
+	idx = sdata->fragment_next;
+	for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) {
+		struct ieee80211_hdr *f_hdr;
+		u16 f_fc;
+
+		idx--;
+		if (idx < 0)
+			idx = IEEE80211_FRAGMENT_MAX - 1;
+
+		entry = &sdata->fragments[idx];
+		if (skb_queue_empty(&entry->skb_list) || entry->seq != seq ||
+		    entry->rx_queue != rx_queue ||
+		    entry->last_frag + 1 != frag)
+			continue;
+
+		f_hdr = (struct ieee80211_hdr *) entry->skb_list.next->data;
+		f_fc = le16_to_cpu(f_hdr->frame_control);
+
+		if ((fc & IEEE80211_FCTL_FTYPE) != (f_fc & IEEE80211_FCTL_FTYPE) ||
+		    compare_ether_addr(hdr->addr1, f_hdr->addr1) != 0 ||
+		    compare_ether_addr(hdr->addr2, f_hdr->addr2) != 0)
+			continue;
+
+		if (entry->first_frag_time + 2 * HZ < jiffies) {
+			__skb_queue_purge(&entry->skb_list);
+			continue;
+		}
+		return entry;
+	}
+
+	return NULL;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx)
+{
+	struct ieee80211_hdr *hdr;
+	u16 sc;
+	unsigned int frag, seq;
+	struct ieee80211_fragment_entry *entry;
+	struct sk_buff *skb;
+
+	hdr = (struct ieee80211_hdr *) rx->skb->data;
+	sc = le16_to_cpu(hdr->seq_ctrl);
+	frag = sc & IEEE80211_SCTL_FRAG;
+
+	if (likely((!(rx->fc & IEEE80211_FCTL_MOREFRAGS) && frag == 0) ||
+		   (rx->skb)->len < 24 ||
+		   is_multicast_ether_addr(hdr->addr1))) {
+		/* not fragmented */
+		goto out;
+	}
+	I802_DEBUG_INC(rx->local->rx_handlers_fragments);
+
+	seq = (sc & IEEE80211_SCTL_SEQ) >> 4;
+
+	if (frag == 0) {
+		/* This is the first fragment of a new frame. */
+		entry = ieee80211_reassemble_add(rx->sdata, frag, seq,
+						 rx->u.rx.queue, &(rx->skb));
+		if (rx->key && rx->key->alg == ALG_CCMP &&
+		    (rx->fc & IEEE80211_FCTL_PROTECTED)) {
+			/* Store CCMP PN so that we can verify that the next
+			 * fragment has a sequential PN value. */
+			entry->ccmp = 1;
+			memcpy(entry->last_pn,
+			       rx->key->u.ccmp.rx_pn[rx->u.rx.queue],
+			       CCMP_PN_LEN);
+		}
+		return TXRX_QUEUED;
+	}
+
+	/* This is a fragment for a frame that should already be pending in
+	 * fragment cache. Add this fragment to the end of the pending entry.
+	 */
+	entry = ieee80211_reassemble_find(rx->sdata, rx->fc, frag, seq,
+					  rx->u.rx.queue, hdr);
+	if (!entry) {
+		I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
+		return TXRX_DROP;
+	}
+
+	/* Verify that MPDUs within one MSDU have sequential PN values.
+	 * (IEEE 802.11i, 8.3.3.4.5) */
+	if (entry->ccmp) {
+		int i;
+		u8 pn[CCMP_PN_LEN], *rpn;
+		if (!rx->key || rx->key->alg != ALG_CCMP)
+			return TXRX_DROP;
+		memcpy(pn, entry->last_pn, CCMP_PN_LEN);
+		for (i = CCMP_PN_LEN - 1; i >= 0; i--) {
+			pn[i]++;
+			if (pn[i])
+				break;
+		}
+		rpn = rx->key->u.ccmp.rx_pn[rx->u.rx.queue];
+		if (memcmp(pn, rpn, CCMP_PN_LEN) != 0) {
+			printk(KERN_DEBUG "%s: defrag: CCMP PN not sequential"
+			       " A2=" MAC_FMT " PN=%02x%02x%02x%02x%02x%02x "
+			       "(expected %02x%02x%02x%02x%02x%02x)\n",
+			       rx->dev->name, MAC_ARG(hdr->addr2),
+			       rpn[0], rpn[1], rpn[2], rpn[3], rpn[4], rpn[5],
+			       pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]);
+			return TXRX_DROP;
+		}
+		memcpy(entry->last_pn, pn, CCMP_PN_LEN);
+	}
+
+	skb_pull(rx->skb, ieee80211_get_hdrlen(rx->fc));
+	__skb_queue_tail(&entry->skb_list, rx->skb);
+	entry->last_frag = frag;
+	entry->extra_len += rx->skb->len;
+	if (rx->fc & IEEE80211_FCTL_MOREFRAGS) {
+		rx->skb = NULL;
+		return TXRX_QUEUED;
+	}
+
+	rx->skb = __skb_dequeue(&entry->skb_list);
+	if (skb_tailroom(rx->skb) < entry->extra_len) {
+		I802_DEBUG_INC(rx->local->rx_expand_skb_head2);
+		if (unlikely(pskb_expand_head(rx->skb, 0, entry->extra_len,
+					      GFP_ATOMIC))) {
+			I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
+			__skb_queue_purge(&entry->skb_list);
+			return TXRX_DROP;
+		}
+	}
+	while ((skb = __skb_dequeue(&entry->skb_list))) {
+		memcpy(skb_put(rx->skb, skb->len), skb->data, skb->len);
+		dev_kfree_skb(skb);
+	}
+
+	/* Complete frame has been reassembled - process it now */
+	rx->fragmented = 1;
+
+ out:
+	if (rx->sta)
+		rx->sta->rx_packets++;
+	if (is_multicast_ether_addr(hdr->addr1))
+		rx->local->dot11MulticastReceivedFrameCount++;
+	else
+		ieee80211_led_rx(rx->local);
+	return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx)
+{
+	struct sk_buff *skb;
+	int no_pending_pkts;
+
+	if (likely(!rx->sta ||
+		   (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_CTL ||
+		   (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PSPOLL ||
+		   !rx->u.rx.ra_match))
+		return TXRX_CONTINUE;
+
+	skb = skb_dequeue(&rx->sta->tx_filtered);
+	if (!skb) {
+		skb = skb_dequeue(&rx->sta->ps_tx_buf);
+		if (skb)
+			rx->local->total_ps_buffered--;
+	}
+	no_pending_pkts = skb_queue_empty(&rx->sta->tx_filtered) &&
+		skb_queue_empty(&rx->sta->ps_tx_buf);
+
+	if (skb) {
+		struct ieee80211_hdr *hdr =
+			(struct ieee80211_hdr *) skb->data;
+
+		/* tell TX path to send one frame even though the STA may
+		 * still remain is PS mode after this frame exchange */
+		rx->sta->pspoll = 1;
+
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+		printk(KERN_DEBUG "STA " MAC_FMT " aid %d: PS Poll (entries "
+		       "after %d)\n",
+		       MAC_ARG(rx->sta->addr), rx->sta->aid,
+		       skb_queue_len(&rx->sta->ps_tx_buf));
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+
+		/* Use MoreData flag to indicate whether there are more
+		 * buffered frames for this STA */
+		if (no_pending_pkts) {
+			hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
+			rx->sta->flags &= ~WLAN_STA_TIM;
+		} else
+			hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+
+		dev_queue_xmit(skb);
+
+		if (no_pending_pkts) {
+			if (rx->local->ops->set_tim)
+				rx->local->ops->set_tim(local_to_hw(rx->local),
+						       rx->sta->aid, 0);
+			if (rx->sdata->bss)
+				bss_tim_clear(rx->local, rx->sdata->bss, rx->sta->aid);
+		}
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+	} else if (!rx->u.rx.sent_ps_buffered) {
+		printk(KERN_DEBUG "%s: STA " MAC_FMT " sent PS Poll even "
+		       "though there is no buffered frames for it\n",
+		       rx->dev->name, MAC_ARG(rx->sta->addr));
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+
+	}
+
+	/* Free PS Poll skb here instead of returning TXRX_DROP that would
+	 * count as an dropped frame. */
+	dev_kfree_skb(rx->skb);
+
+	return TXRX_QUEUED;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx)
+{
+	u16 fc = rx->fc;
+	u8 *data = rx->skb->data;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) data;
+
+	if (!WLAN_FC_IS_QOS_DATA(fc))
+		return TXRX_CONTINUE;
+
+	/* remove the qos control field, update frame type and meta-data */
+	memmove(data + 2, data, ieee80211_get_hdrlen(fc) - 2);
+	hdr = (struct ieee80211_hdr *) skb_pull(rx->skb, 2);
+	/* change frame type to non QOS */
+	rx->fc = fc &= ~IEEE80211_STYPE_QOS_DATA;
+	hdr->frame_control = cpu_to_le16(fc);
+
+	return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_802_1x_pae(struct ieee80211_txrx_data *rx)
+{
+	if (rx->sdata->eapol && ieee80211_is_eapol(rx->skb) &&
+	    rx->sdata->type != IEEE80211_IF_TYPE_STA && rx->u.rx.ra_match) {
+		/* Pass both encrypted and unencrypted EAPOL frames to user
+		 * space for processing. */
+		if (!rx->local->apdev)
+			return TXRX_DROP;
+		ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
+				  ieee80211_msg_normal);
+		return TXRX_QUEUED;
+	}
+
+	if (unlikely(rx->sdata->ieee802_1x &&
+		     (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
+		     (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_NULLFUNC &&
+		     (!rx->sta || !(rx->sta->flags & WLAN_STA_AUTHORIZED)) &&
+		     !ieee80211_is_eapol(rx->skb))) {
+#ifdef CONFIG_MAC80211_DEBUG
+		struct ieee80211_hdr *hdr =
+			(struct ieee80211_hdr *) rx->skb->data;
+		printk(KERN_DEBUG "%s: dropped frame from " MAC_FMT
+		       " (unauthorized port)\n", rx->dev->name,
+		       MAC_ARG(hdr->addr2));
+#endif /* CONFIG_MAC80211_DEBUG */
+		return TXRX_DROP;
+	}
+
+	return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_drop_unencrypted(struct ieee80211_txrx_data *rx)
+{
+	/*  If the device handles decryption totally, skip this test */
+	if (rx->local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP)
+		return TXRX_CONTINUE;
+
+	/* Drop unencrypted frames if key is set. */
+	if (unlikely(!(rx->fc & IEEE80211_FCTL_PROTECTED) &&
+		     (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
+		     (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_NULLFUNC &&
+		     (rx->key || rx->sdata->drop_unencrypted) &&
+		     (rx->sdata->eapol == 0 ||
+		      !ieee80211_is_eapol(rx->skb)))) {
+		printk(KERN_DEBUG "%s: RX non-WEP frame, but expected "
+		       "encryption\n", rx->dev->name);
+		return TXRX_DROP;
+	}
+	return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
+{
+	struct net_device *dev = rx->dev;
+	struct ieee80211_local *local = rx->local;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
+	u16 fc, hdrlen, ethertype;
+	u8 *payload;
+	u8 dst[ETH_ALEN];
+	u8 src[ETH_ALEN];
+	struct sk_buff *skb = rx->skb, *skb2;
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+	fc = rx->fc;
+	if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA))
+		return TXRX_CONTINUE;
+
+	if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
+		return TXRX_DROP;
+
+	hdrlen = ieee80211_get_hdrlen(fc);
+
+	/* convert IEEE 802.11 header + possible LLC headers into Ethernet
+	 * header
+	 * IEEE 802.11 address fields:
+	 * ToDS FromDS Addr1 Addr2 Addr3 Addr4
+	 *   0     0   DA    SA    BSSID n/a
+	 *   0     1   DA    BSSID SA    n/a
+	 *   1     0   BSSID SA    DA    n/a
+	 *   1     1   RA    TA    DA    SA
+	 */
+
+	switch (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
+	case IEEE80211_FCTL_TODS:
+		/* BSSID SA DA */
+		memcpy(dst, hdr->addr3, ETH_ALEN);
+		memcpy(src, hdr->addr2, ETH_ALEN);
+
+		if (unlikely(sdata->type != IEEE80211_IF_TYPE_AP &&
+			     sdata->type != IEEE80211_IF_TYPE_VLAN)) {
+			printk(KERN_DEBUG "%s: dropped ToDS frame (BSSID="
+			       MAC_FMT " SA=" MAC_FMT " DA=" MAC_FMT ")\n",
+			       dev->name, MAC_ARG(hdr->addr1),
+			       MAC_ARG(hdr->addr2), MAC_ARG(hdr->addr3));
+			return TXRX_DROP;
+		}
+		break;
+	case (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
+		/* RA TA DA SA */
+		memcpy(dst, hdr->addr3, ETH_ALEN);
+		memcpy(src, hdr->addr4, ETH_ALEN);
+
+		if (unlikely(sdata->type != IEEE80211_IF_TYPE_WDS)) {
+			printk(KERN_DEBUG "%s: dropped FromDS&ToDS frame (RA="
+			       MAC_FMT " TA=" MAC_FMT " DA=" MAC_FMT " SA="
+			       MAC_FMT ")\n",
+			       rx->dev->name, MAC_ARG(hdr->addr1),
+			       MAC_ARG(hdr->addr2), MAC_ARG(hdr->addr3),
+			       MAC_ARG(hdr->addr4));
+			return TXRX_DROP;
+		}
+		break;
+	case IEEE80211_FCTL_FROMDS:
+		/* DA BSSID SA */
+		memcpy(dst, hdr->addr1, ETH_ALEN);
+		memcpy(src, hdr->addr3, ETH_ALEN);
+
+		if (sdata->type != IEEE80211_IF_TYPE_STA) {
+			return TXRX_DROP;
+		}
+		break;
+	case 0:
+		/* DA SA BSSID */
+		memcpy(dst, hdr->addr1, ETH_ALEN);
+		memcpy(src, hdr->addr2, ETH_ALEN);
+
+		if (sdata->type != IEEE80211_IF_TYPE_IBSS) {
+			if (net_ratelimit()) {
+				printk(KERN_DEBUG "%s: dropped IBSS frame (DA="
+				       MAC_FMT " SA=" MAC_FMT " BSSID=" MAC_FMT
+				       ")\n",
+				       dev->name, MAC_ARG(hdr->addr1),
+				       MAC_ARG(hdr->addr2),
+				       MAC_ARG(hdr->addr3));
+			}
+			return TXRX_DROP;
+		}
+		break;
+	}
+
+	payload = skb->data + hdrlen;
+
+	if (unlikely(skb->len - hdrlen < 8)) {
+		if (net_ratelimit()) {
+			printk(KERN_DEBUG "%s: RX too short data frame "
+			       "payload\n", dev->name);
+		}
+		return TXRX_DROP;
+	}
+
+	ethertype = (payload[6] << 8) | payload[7];
+
+	if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
+		    ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
+		   compare_ether_addr(payload, bridge_tunnel_header) == 0)) {
+		/* remove RFC1042 or Bridge-Tunnel encapsulation and
+		 * replace EtherType */
+		skb_pull(skb, hdrlen + 6);
+		memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
+		memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
+	} else {
+		struct ethhdr *ehdr;
+		__be16 len;
+		skb_pull(skb, hdrlen);
+		len = htons(skb->len);
+		ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
+		memcpy(ehdr->h_dest, dst, ETH_ALEN);
+		memcpy(ehdr->h_source, src, ETH_ALEN);
+		ehdr->h_proto = len;
+	}
+	skb->dev = dev;
+
+	skb2 = NULL;
+
+	sdata->stats.rx_packets++;
+	sdata->stats.rx_bytes += skb->len;
+
+	if (local->bridge_packets && (sdata->type == IEEE80211_IF_TYPE_AP
+	    || sdata->type == IEEE80211_IF_TYPE_VLAN) && rx->u.rx.ra_match) {
+		if (is_multicast_ether_addr(skb->data)) {
+			/* send multicast frames both to higher layers in
+			 * local net stack and back to the wireless media */
+			skb2 = skb_copy(skb, GFP_ATOMIC);
+			if (!skb2)
+				printk(KERN_DEBUG "%s: failed to clone "
+				       "multicast frame\n", dev->name);
+		} else {
+			struct sta_info *dsta;
+			dsta = sta_info_get(local, skb->data);
+			if (dsta && !dsta->dev) {
+				printk(KERN_DEBUG "Station with null dev "
+				       "structure!\n");
+			} else if (dsta && dsta->dev == dev) {
+				/* Destination station is associated to this
+				 * AP, so send the frame directly to it and
+				 * do not pass the frame to local net stack.
+				 */
+				skb2 = skb;
+				skb = NULL;
+			}
+			if (dsta)
+				sta_info_put(dsta);
+		}
+	}
+
+	if (skb) {
+		/* deliver to local stack */
+		skb->protocol = eth_type_trans(skb, dev);
+		memset(skb->cb, 0, sizeof(skb->cb));
+		netif_rx(skb);
+	}
+
+	if (skb2) {
+		/* send to wireless media */
+		skb2->protocol = __constant_htons(ETH_P_802_3);
+		skb_set_network_header(skb2, 0);
+		skb_set_mac_header(skb2, 0);
+		dev_queue_xmit(skb2);
+	}
+
+	return TXRX_QUEUED;
+}
+
+static ieee80211_txrx_result
+ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx)
+{
+	struct ieee80211_sub_if_data *sdata;
+
+	if (!rx->u.rx.ra_match)
+		return TXRX_DROP;
+
+	sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
+	if ((sdata->type == IEEE80211_IF_TYPE_STA ||
+	     sdata->type == IEEE80211_IF_TYPE_IBSS) &&
+	    !rx->local->user_space_mlme) {
+		ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status);
+	} else {
+		/* Management frames are sent to hostapd for processing */
+		if (!rx->local->apdev)
+			return TXRX_DROP;
+		ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
+				  ieee80211_msg_normal);
+	}
+	return TXRX_QUEUED;
+}
+
+static inline ieee80211_txrx_result __ieee80211_invoke_rx_handlers(
+				struct ieee80211_local *local,
+				ieee80211_rx_handler *handlers,
+				struct ieee80211_txrx_data *rx,
+				struct sta_info *sta)
+{
+	ieee80211_rx_handler *handler;
+	ieee80211_txrx_result res = TXRX_DROP;
+
+	for (handler = handlers; *handler != NULL; handler++) {
+		res = (*handler)(rx);
+
+		switch (res) {
+		case TXRX_CONTINUE:
+			continue;
+		case TXRX_DROP:
+			I802_DEBUG_INC(local->rx_handlers_drop);
+			if (sta)
+				sta->rx_dropped++;
+			break;
+		case TXRX_QUEUED:
+			I802_DEBUG_INC(local->rx_handlers_queued);
+			break;
+		}
+		break;
+	}
+
+	if (res == TXRX_DROP)
+		dev_kfree_skb(rx->skb);
+	return res;
+}
+
+static inline void ieee80211_invoke_rx_handlers(struct ieee80211_local *local,
+						ieee80211_rx_handler *handlers,
+						struct ieee80211_txrx_data *rx,
+						struct sta_info *sta)
+{
+	if (__ieee80211_invoke_rx_handlers(local, handlers, rx, sta) ==
+	    TXRX_CONTINUE)
+		dev_kfree_skb(rx->skb);
+}
+
+static void ieee80211_rx_michael_mic_report(struct net_device *dev,
+					    struct ieee80211_hdr *hdr,
+					    struct sta_info *sta,
+					    struct ieee80211_txrx_data *rx)
+{
+	int keyidx, hdrlen;
+
+	hdrlen = ieee80211_get_hdrlen_from_skb(rx->skb);
+	if (rx->skb->len >= hdrlen + 4)
+		keyidx = rx->skb->data[hdrlen + 3] >> 6;
+	else
+		keyidx = -1;
+
+	/* TODO: verify that this is not triggered by fragmented
+	 * frames (hw does not verify MIC for them). */
+	printk(KERN_DEBUG "%s: TKIP hwaccel reported Michael MIC "
+	       "failure from " MAC_FMT " to " MAC_FMT " keyidx=%d\n",
+	       dev->name, MAC_ARG(hdr->addr2), MAC_ARG(hdr->addr1), keyidx);
+
+	if (!sta) {
+		/* Some hardware versions seem to generate incorrect
+		 * Michael MIC reports; ignore them to avoid triggering
+		 * countermeasures. */
+		printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
+		       "error for unknown address " MAC_FMT "\n",
+		       dev->name, MAC_ARG(hdr->addr2));
+		goto ignore;
+	}
+
+	if (!(rx->fc & IEEE80211_FCTL_PROTECTED)) {
+		printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
+		       "error for a frame with no ISWEP flag (src "
+		       MAC_FMT ")\n", dev->name, MAC_ARG(hdr->addr2));
+		goto ignore;
+	}
+
+	if ((rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) &&
+	    rx->sdata->type == IEEE80211_IF_TYPE_AP) {
+		keyidx = ieee80211_wep_get_keyidx(rx->skb);
+		/* AP with Pairwise keys support should never receive Michael
+		 * MIC errors for non-zero keyidx because these are reserved
+		 * for group keys and only the AP is sending real multicast
+		 * frames in BSS. */
+		if (keyidx) {
+			printk(KERN_DEBUG "%s: ignored Michael MIC error for "
+			       "a frame with non-zero keyidx (%d) (src " MAC_FMT
+			       ")\n", dev->name, keyidx, MAC_ARG(hdr->addr2));
+			goto ignore;
+		}
+	}
+
+	if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
+	    ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
+	     (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH)) {
+		printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
+		       "error for a frame that cannot be encrypted "
+		       "(fc=0x%04x) (src " MAC_FMT ")\n",
+		       dev->name, rx->fc, MAC_ARG(hdr->addr2));
+		goto ignore;
+	}
+
+	do {
+		union iwreq_data wrqu;
+		char *buf = kmalloc(128, GFP_ATOMIC);
+		if (!buf)
+			break;
+
+		/* TODO: needed parameters: count, key type, TSC */
+		sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
+			"keyid=%d %scast addr=" MAC_FMT ")",
+			keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni",
+			MAC_ARG(hdr->addr2));
+		memset(&wrqu, 0, sizeof(wrqu));
+		wrqu.data.length = strlen(buf);
+		wireless_send_event(rx->dev, IWEVCUSTOM, &wrqu, buf);
+		kfree(buf);
+	} while (0);
+
+	/* TODO: consider verifying the MIC error report with software
+	 * implementation if we get too many spurious reports from the
+	 * hardware. */
+	if (!rx->local->apdev)
+		goto ignore;
+	ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status,
+			  ieee80211_msg_michael_mic_failure);
+	return;
+
+ ignore:
+	dev_kfree_skb(rx->skb);
+	rx->skb = NULL;
+}
+
+ieee80211_rx_handler ieee80211_rx_handlers[] =
+{
+	ieee80211_rx_h_if_stats,
+	ieee80211_rx_h_monitor,
+	ieee80211_rx_h_passive_scan,
+	ieee80211_rx_h_check,
+	ieee80211_rx_h_load_key,
+	ieee80211_rx_h_sta_process,
+	ieee80211_rx_h_ccmp_decrypt,
+	ieee80211_rx_h_tkip_decrypt,
+	ieee80211_rx_h_wep_weak_iv_detection,
+	ieee80211_rx_h_wep_decrypt,
+	ieee80211_rx_h_defragment,
+	ieee80211_rx_h_ps_poll,
+	ieee80211_rx_h_michael_mic_verify,
+	/* this must be after decryption - so header is counted in MPDU mic
+	 * must be before pae and data, so QOS_DATA format frames
+	 * are not passed to user space by these functions
+	 */
+	ieee80211_rx_h_remove_qos_control,
+	ieee80211_rx_h_802_1x_pae,
+	ieee80211_rx_h_drop_unencrypted,
+	ieee80211_rx_h_data,
+	ieee80211_rx_h_mgmt,
+	NULL
+};
+
+/* main receive path */
+
+static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
+				u8 *bssid, struct ieee80211_txrx_data *rx,
+				struct ieee80211_hdr *hdr)
+{
+	int multicast = is_multicast_ether_addr(hdr->addr1);
+
+	switch (sdata->type) {
+	case IEEE80211_IF_TYPE_STA:
+		if (!bssid)
+			return 0;
+		if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) {
+			if (!rx->u.rx.in_scan)
+				return 0;
+			rx->u.rx.ra_match = 0;
+		} else if (!multicast &&
+			   compare_ether_addr(sdata->dev->dev_addr,
+					      hdr->addr1) != 0) {
+			if (!sdata->promisc)
+				return 0;
+			rx->u.rx.ra_match = 0;
+		}
+		break;
+	case IEEE80211_IF_TYPE_IBSS:
+		if (!bssid)
+			return 0;
+		if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) {
+			if (!rx->u.rx.in_scan)
+				return 0;
+			rx->u.rx.ra_match = 0;
+		} else if (!multicast &&
+			   compare_ether_addr(sdata->dev->dev_addr,
+					      hdr->addr1) != 0) {
+			if (!sdata->promisc)
+				return 0;
+			rx->u.rx.ra_match = 0;
+		} else if (!rx->sta)
+			rx->sta = ieee80211_ibss_add_sta(sdata->dev, rx->skb,
+							 bssid, hdr->addr2);
+		break;
+	case IEEE80211_IF_TYPE_AP:
+		if (!bssid) {
+			if (compare_ether_addr(sdata->dev->dev_addr,
+					       hdr->addr1))
+				return 0;
+		} else if (!ieee80211_bssid_match(bssid,
+					sdata->dev->dev_addr)) {
+			if (!rx->u.rx.in_scan)
+				return 0;
+			rx->u.rx.ra_match = 0;
+		}
+		if (sdata->dev == sdata->local->mdev && !rx->u.rx.in_scan)
+			/* do not receive anything via
+			 * master device when not scanning */
+			return 0;
+		break;
+	case IEEE80211_IF_TYPE_WDS:
+		if (bssid ||
+		    (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)
+			return 0;
+		if (compare_ether_addr(sdata->u.wds.remote_addr, hdr->addr2))
+			return 0;
+		break;
+	}
+
+	return 1;
+}
+
+/*
+ * This is the receive path handler. It is called by a low level driver when an
+ * 802.11 MPDU is received from the hardware.
+ */
+void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
+		    struct ieee80211_rx_status *status)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+	struct ieee80211_sub_if_data *sdata;
+	struct sta_info *sta;
+	struct ieee80211_hdr *hdr;
+	struct ieee80211_txrx_data rx;
+	u16 type;
+	int radiotap_len = 0, prepres;
+	struct ieee80211_sub_if_data *prev = NULL;
+	struct sk_buff *skb_new;
+	u8 *bssid;
+
+	if (status->flag & RX_FLAG_RADIOTAP) {
+		radiotap_len = ieee80211_get_radiotap_len(skb->data);
+		skb_pull(skb, radiotap_len);
+	}
+
+	hdr = (struct ieee80211_hdr *) skb->data;
+	memset(&rx, 0, sizeof(rx));
+	rx.skb = skb;
+	rx.local = local;
+
+	rx.u.rx.status = status;
+	rx.fc = skb->len >= 2 ? le16_to_cpu(hdr->frame_control) : 0;
+	type = rx.fc & IEEE80211_FCTL_FTYPE;
+	if (type == IEEE80211_FTYPE_DATA || type == IEEE80211_FTYPE_MGMT)
+		local->dot11ReceivedFragmentCount++;
+
+	if (skb->len >= 16) {
+		sta = rx.sta = sta_info_get(local, hdr->addr2);
+		if (sta) {
+			rx.dev = rx.sta->dev;
+			rx.sdata = IEEE80211_DEV_TO_SUB_IF(rx.dev);
+		}
+	} else
+		sta = rx.sta = NULL;
+
+	if ((status->flag & RX_FLAG_MMIC_ERROR)) {
+		ieee80211_rx_michael_mic_report(local->mdev, hdr, sta, &rx);
+		goto end;
+	}
+
+	if (unlikely(local->sta_scanning))
+		rx.u.rx.in_scan = 1;
+
+	if (__ieee80211_invoke_rx_handlers(local, local->rx_pre_handlers, &rx,
+					   sta) != TXRX_CONTINUE)
+		goto end;
+	skb = rx.skb;
+
+	skb_push(skb, radiotap_len);
+	if (sta && !sta->assoc_ap && !(sta->flags & WLAN_STA_WDS) &&
+	    !local->iff_promiscs && !is_multicast_ether_addr(hdr->addr1)) {
+		rx.u.rx.ra_match = 1;
+		ieee80211_invoke_rx_handlers(local, local->rx_handlers, &rx,
+					     rx.sta);
+		sta_info_put(sta);
+		return;
+	}
+
+	bssid = ieee80211_get_bssid(hdr, skb->len - radiotap_len);
+
+	read_lock(&local->sub_if_lock);
+	list_for_each_entry(sdata, &local->sub_if_list, list) {
+		rx.u.rx.ra_match = 1;
+
+		prepres = prepare_for_handlers(sdata, bssid, &rx, hdr);
+		/* prepare_for_handlers can change sta */
+		sta = rx.sta;
+
+		if (!prepres)
+			continue;
+
+		/*
+		 * frame is destined for this interface, but if it's not
+		 * also for the previous one we handle that after the
+		 * loop to avoid copying the SKB once too much
+		 */
+
+		if (!prev) {
+			prev = sdata;
+			continue;
+		}
+
+		/*
+		 * frame was destined for the previous interface
+		 * so invoke RX handlers for it
+		 */
+
+		skb_new = skb_copy(skb, GFP_ATOMIC);
+		if (!skb_new) {
+			if (net_ratelimit())
+				printk(KERN_DEBUG "%s: failed to copy "
+				       "multicast frame for %s",
+				       local->mdev->name, prev->dev->name);
+			continue;
+		}
+		rx.skb = skb_new;
+		rx.dev = prev->dev;
+		rx.sdata = prev;
+		ieee80211_invoke_rx_handlers(local, local->rx_handlers,
+					     &rx, sta);
+		prev = sdata;
+	}
+	if (prev) {
+		rx.skb = skb;
+		rx.dev = prev->dev;
+		rx.sdata = prev;
+		ieee80211_invoke_rx_handlers(local, local->rx_handlers,
+					     &rx, sta);
+	} else
+		dev_kfree_skb(skb);
+	read_unlock(&local->sub_if_lock);
+
+ end:
+	if (sta)
+		sta_info_put(sta);
+}
+EXPORT_SYMBOL(__ieee80211_rx);
+
+/* This is a version of the rx handler that can be called from hard irq
+ * context. Post the skb on the queue and schedule the tasklet */
+void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb,
+			  struct ieee80211_rx_status *status)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+
+	BUILD_BUG_ON(sizeof(struct ieee80211_rx_status) > sizeof(skb->cb));
+
+	skb->dev = local->mdev;
+	/* copy status into skb->cb for use by tasklet */
+	memcpy(skb->cb, status, sizeof(*status));
+	skb->pkt_type = IEEE80211_RX_MSG;
+	skb_queue_tail(&local->skb_queue, skb);
+	tasklet_schedule(&local->tasklet);
+}
+EXPORT_SYMBOL(ieee80211_rx_irqsafe);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index ab7b1f0..34245b8 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -32,38 +32,34 @@ static void sta_info_hash_add(struct ieee80211_local *local,
 
 
 /* Caller must hold local->sta_lock */
-static void sta_info_hash_del(struct ieee80211_local *local,
-			      struct sta_info *sta)
+static int sta_info_hash_del(struct ieee80211_local *local,
+			     struct sta_info *sta)
 {
 	struct sta_info *s;
 
 	s = local->sta_hash[STA_HASH(sta->addr)];
 	if (!s)
-		return;
-	if (memcmp(s->addr, sta->addr, ETH_ALEN) == 0) {
+		return -ENOENT;
+	if (s == sta) {
 		local->sta_hash[STA_HASH(sta->addr)] = s->hnext;
-		return;
+		return 0;
 	}
 
-	while (s->hnext && memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0)
+	while (s->hnext && s->hnext != sta)
 		s = s->hnext;
-	if (s->hnext)
-		s->hnext = s->hnext->hnext;
-	else
-		printk(KERN_ERR "%s: could not remove STA " MAC_FMT " from "
-		       "hash table\n", local->mdev->name, MAC_ARG(sta->addr));
-}
+	if (s->hnext) {
+		s->hnext = sta->hnext;
+		return 0;
+	}
 
-static inline void __sta_info_get(struct sta_info *sta)
-{
-	kref_get(&sta->kref);
+	return -ENOENT;
 }
 
 struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr)
 {
 	struct sta_info *sta;
 
-	spin_lock_bh(&local->sta_lock);
+	read_lock_bh(&local->sta_lock);
 	sta = local->sta_hash[STA_HASH(addr)];
 	while (sta) {
 		if (memcmp(sta->addr, addr, ETH_ALEN) == 0) {
@@ -72,7 +68,7 @@ struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr)
 		}
 		sta = sta->hnext;
 	}
-	spin_unlock_bh(&local->sta_lock);
+	read_unlock_bh(&local->sta_lock);
 
 	return sta;
 }
@@ -85,7 +81,7 @@ int sta_info_min_txrate_get(struct ieee80211_local *local)
 	int min_txrate = 9999999;
 	int i;
 
-	spin_lock_bh(&local->sta_lock);
+	read_lock_bh(&local->sta_lock);
 	mode = local->oper_hw_mode;
 	for (i = 0; i < STA_HASH_SIZE; i++) {
 		sta = local->sta_hash[i];
@@ -95,7 +91,7 @@ int sta_info_min_txrate_get(struct ieee80211_local *local)
 			sta = sta->hnext;
 		}
 	}
-	spin_unlock_bh(&local->sta_lock);
+	read_unlock_bh(&local->sta_lock);
 	if (min_txrate == 9999999)
 		min_txrate = 0;
 
@@ -150,7 +146,6 @@ struct sta_info * sta_info_add(struct ieee80211_local *local,
 	sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl, gfp);
 	if (!sta->rate_ctrl_priv) {
 		rate_control_put(sta->rate_ctrl);
-		kref_put(&sta->kref, sta_info_release);
 		kfree(sta);
 		return NULL;
 	}
@@ -162,14 +157,14 @@ struct sta_info * sta_info_add(struct ieee80211_local *local,
 	skb_queue_head_init(&sta->tx_filtered);
 	__sta_info_get(sta);	/* sta used by caller, decremented by
 				 * sta_info_put() */
-	spin_lock_bh(&local->sta_lock);
+	write_lock_bh(&local->sta_lock);
 	list_add(&sta->list, &local->sta_list);
 	local->num_sta++;
 	sta_info_hash_add(local, sta);
-	spin_unlock_bh(&local->sta_lock);
 	if (local->ops->sta_table_notification)
 		local->ops->sta_table_notification(local_to_hw(local),
 						  local->num_sta);
+	write_unlock_bh(&local->sta_lock);
 	sta->key_idx_compression = HW_KEY_IDX_INVALID;
 
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
@@ -178,47 +173,25 @@ struct sta_info * sta_info_add(struct ieee80211_local *local,
 #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 
 #ifdef CONFIG_MAC80211_DEBUGFS
-	if (!in_interrupt()) {
-		sta->debugfs_registered = 1;
-		ieee80211_sta_debugfs_add(sta);
-		rate_control_add_sta_debugfs(sta);
-	} else {
-		/* debugfs entry adding might sleep, so schedule process
-		 * context task for adding entry for STAs that do not yet
-		 * have one. */
-		queue_work(local->hw.workqueue, &local->sta_debugfs_add);
-	}
+	/* debugfs entry adding might sleep, so schedule process
+	 * context task for adding entry for STAs that do not yet
+	 * have one. */
+	queue_work(local->hw.workqueue, &local->sta_debugfs_add);
 #endif
 
 	return sta;
 }
 
-static void finish_sta_info_free(struct ieee80211_local *local,
-				 struct sta_info *sta)
-{
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-	printk(KERN_DEBUG "%s: Removed STA " MAC_FMT "\n",
-	       local->mdev->name, MAC_ARG(sta->addr));
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
-
-	if (sta->key) {
-		ieee80211_debugfs_key_remove(sta->key);
-		ieee80211_key_free(sta->key);
-		sta->key = NULL;
-	}
-
-	rate_control_remove_sta_debugfs(sta);
-	ieee80211_sta_debugfs_remove(sta);
-
-	sta_info_put(sta);
-}
-
-static void sta_info_remove(struct sta_info *sta)
+/* Caller must hold local->sta_lock */
+void sta_info_remove(struct sta_info *sta)
 {
 	struct ieee80211_local *local = sta->local;
 	struct ieee80211_sub_if_data *sdata;
 
-	sta_info_hash_del(local, sta);
+	/* don't do anything if we've been removed already */
+	if (sta_info_hash_del(local, sta))
+		return;
+
 	list_del(&sta->list);
 	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
 	if (sta->flags & WLAN_STA_PS) {
@@ -228,30 +201,29 @@ static void sta_info_remove(struct sta_info *sta)
 	}
 	local->num_sta--;
 	sta_info_remove_aid_ptr(sta);
+
+	if (local->ops->sta_table_notification)
+		local->ops->sta_table_notification(local_to_hw(local),
+						   local->num_sta);
 }
 
-void sta_info_free(struct sta_info *sta, int locked)
+void sta_info_free(struct sta_info *sta)
 {
 	struct sk_buff *skb;
 	struct ieee80211_local *local = sta->local;
 
-	if (!locked) {
-		spin_lock_bh(&local->sta_lock);
-		sta_info_remove(sta);
-		spin_unlock_bh(&local->sta_lock);
-	} else {
-		sta_info_remove(sta);
-	}
-	if (local->ops->sta_table_notification)
-		local->ops->sta_table_notification(local_to_hw(local),
-						  local->num_sta);
+	might_sleep();
+
+	write_lock_bh(&local->sta_lock);
+	sta_info_remove(sta);
+	write_unlock_bh(&local->sta_lock);
 
 	while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
 		local->total_ps_buffered--;
-		dev_kfree_skb_any(skb);
+		dev_kfree_skb(skb);
 	}
 	while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
-		dev_kfree_skb_any(skb);
+		dev_kfree_skb(skb);
 	}
 
 	if (sta->key) {
@@ -276,13 +248,21 @@ void sta_info_free(struct sta_info *sta, int locked)
 		sta->key_idx_compression = HW_KEY_IDX_INVALID;
 	}
 
-#ifdef CONFIG_MAC80211_DEBUGFS
-	if (in_atomic()) {
-		list_add(&sta->list, &local->deleted_sta_list);
-		queue_work(local->hw.workqueue, &local->sta_debugfs_add);
-	} else
-#endif
-		finish_sta_info_free(local, sta);
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+	printk(KERN_DEBUG "%s: Removed STA " MAC_FMT "\n",
+	       local->mdev->name, MAC_ARG(sta->addr));
+#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
+
+	if (sta->key) {
+		ieee80211_debugfs_key_remove(sta->key);
+		ieee80211_key_free(sta->key);
+		sta->key = NULL;
+	}
+
+	rate_control_remove_sta_debugfs(sta);
+	ieee80211_sta_debugfs_remove(sta);
+
+	sta_info_put(sta);
 }
 
 
@@ -343,13 +323,13 @@ static void sta_info_cleanup(unsigned long data)
 	struct ieee80211_local *local = (struct ieee80211_local *) data;
 	struct sta_info *sta;
 
-	spin_lock_bh(&local->sta_lock);
+	read_lock_bh(&local->sta_lock);
 	list_for_each_entry(sta, &local->sta_list, list) {
 		__sta_info_get(sta);
 		sta_info_cleanup_expire_buffered(local, sta);
 		sta_info_put(sta);
 	}
-	spin_unlock_bh(&local->sta_lock);
+	read_unlock_bh(&local->sta_lock);
 
 	local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL;
 	add_timer(&local->sta_cleanup);
@@ -363,35 +343,20 @@ static void sta_info_debugfs_add_task(struct work_struct *work)
 	struct sta_info *sta, *tmp;
 
 	while (1) {
-		spin_lock_bh(&local->sta_lock);
-		if (!list_empty(&local->deleted_sta_list)) {
-			sta = list_entry(local->deleted_sta_list.next,
-					 struct sta_info, list);
-			list_del(local->deleted_sta_list.next);
-		} else
-			sta = NULL;
-		spin_unlock_bh(&local->sta_lock);
-		if (!sta)
-			break;
-		finish_sta_info_free(local, sta);
-	}
-
-	while (1) {
 		sta = NULL;
-		spin_lock_bh(&local->sta_lock);
+		read_lock_bh(&local->sta_lock);
 		list_for_each_entry(tmp, &local->sta_list, list) {
-			if (!tmp->debugfs_registered) {
+			if (!tmp->debugfs.dir) {
 				sta = tmp;
 				__sta_info_get(sta);
 				break;
 			}
 		}
-		spin_unlock_bh(&local->sta_lock);
+		read_unlock_bh(&local->sta_lock);
 
 		if (!sta)
 			break;
 
-		sta->debugfs_registered = 1;
 		ieee80211_sta_debugfs_add(sta);
 		rate_control_add_sta_debugfs(sta);
 		sta_info_put(sta);
@@ -401,9 +366,8 @@ static void sta_info_debugfs_add_task(struct work_struct *work)
 
 void sta_info_init(struct ieee80211_local *local)
 {
-	spin_lock_init(&local->sta_lock);
+	rwlock_init(&local->sta_lock);
 	INIT_LIST_HEAD(&local->sta_list);
-	INIT_LIST_HEAD(&local->deleted_sta_list);
 
 	init_timer(&local->sta_cleanup);
 	local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL;
@@ -423,17 +387,8 @@ int sta_info_start(struct ieee80211_local *local)
 
 void sta_info_stop(struct ieee80211_local *local)
 {
-	struct sta_info *sta, *tmp;
-
 	del_timer(&local->sta_cleanup);
-
-	list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
-		/* sta_info_free must be called with 0 as the last
-		 * parameter to ensure all debugfs sta entries are
-		 * unregistered. We don't need locking at this
-		 * point. */
-		sta_info_free(sta, 0);
-	}
+	sta_info_flush(local, NULL);
 }
 
 void sta_info_remove_aid_ptr(struct sta_info *sta)
@@ -461,10 +416,19 @@ void sta_info_remove_aid_ptr(struct sta_info *sta)
 void sta_info_flush(struct ieee80211_local *local, struct net_device *dev)
 {
 	struct sta_info *sta, *tmp;
+	LIST_HEAD(tmp_list);
 
-	spin_lock_bh(&local->sta_lock);
+	write_lock_bh(&local->sta_lock);
 	list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
-		if (!dev || dev == sta->dev)
-			sta_info_free(sta, 1);
-	spin_unlock_bh(&local->sta_lock);
+		if (!dev || dev == sta->dev) {
+			__sta_info_get(sta);
+			sta_info_remove(sta);
+			list_add_tail(&sta->list, &tmp_list);
+		}
+	write_unlock_bh(&local->sta_lock);
+
+	list_for_each_entry_safe(sta, tmp, &tmp_list, list) {
+		sta_info_free(sta);
+		sta_info_put(sta);
+	}
 }
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index b5591d2..b5ef723 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -98,9 +98,6 @@ struct sta_info {
 				  * filtering; used only if sta->key is not
 				  * set */
 
-#ifdef CONFIG_MAC80211_DEBUGFS
-	int debugfs_registered;
-#endif
 	int assoc_ap; /* whether this is an AP that we are
 		       * associated with as a client */
 
@@ -149,12 +146,18 @@ struct sta_info {
  */
 #define STA_INFO_CLEANUP_INTERVAL (10 * HZ)
 
+static inline void __sta_info_get(struct sta_info *sta)
+{
+	kref_get(&sta->kref);
+}
+
 struct sta_info * sta_info_get(struct ieee80211_local *local, u8 *addr);
 int sta_info_min_txrate_get(struct ieee80211_local *local);
 void sta_info_put(struct sta_info *sta);
 struct sta_info * sta_info_add(struct ieee80211_local *local,
 			       struct net_device *dev, u8 *addr, gfp_t gfp);
-void sta_info_free(struct sta_info *sta, int locked);
+void sta_info_remove(struct sta_info *sta);
+void sta_info_free(struct sta_info *sta);
 void sta_info_init(struct ieee80211_local *local);
 int sta_info_start(struct ieee80211_local *local);
 void sta_info_stop(struct ieee80211_local *local);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
new file mode 100644
index 0000000..4571668
--- /dev/null
+++ b/net/mac80211/tx.c
@@ -0,0 +1,1883 @@
+/*
+ * Copyright 2002-2005, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
+ * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *
+ * Transmit and frame generation functions.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/bitmap.h>
+#include <net/ieee80211_radiotap.h>
+#include <net/cfg80211.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+
+#include "ieee80211_i.h"
+#include "ieee80211_led.h"
+#include "wep.h"
+#include "wpa.h"
+#include "wme.h"
+#include "ieee80211_rate.h"
+
+#define IEEE80211_TX_OK		0
+#define IEEE80211_TX_AGAIN	1
+#define IEEE80211_TX_FRAG_AGAIN	2
+
+/* misc utils */
+
+static inline void ieee80211_include_sequence(struct ieee80211_sub_if_data *sdata,
+					      struct ieee80211_hdr *hdr)
+{
+	/* Set the sequence number for this frame. */
+	hdr->seq_ctrl = cpu_to_le16(sdata->sequence);
+
+	/* Increase the sequence number. */
+	sdata->sequence = (sdata->sequence + 0x10) & IEEE80211_SCTL_SEQ;
+}
+
+#ifdef CONFIG_MAC80211_LOWTX_FRAME_DUMP
+static void ieee80211_dump_frame(const char *ifname, const char *title,
+				 const struct sk_buff *skb)
+{
+	const struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+	u16 fc;
+	int hdrlen;
+
+	printk(KERN_DEBUG "%s: %s (len=%d)", ifname, title, skb->len);
+	if (skb->len < 4) {
+		printk("\n");
+		return;
+	}
+
+	fc = le16_to_cpu(hdr->frame_control);
+	hdrlen = ieee80211_get_hdrlen(fc);
+	if (hdrlen > skb->len)
+		hdrlen = skb->len;
+	if (hdrlen >= 4)
+		printk(" FC=0x%04x DUR=0x%04x",
+		       fc, le16_to_cpu(hdr->duration_id));
+	if (hdrlen >= 10)
+		printk(" A1=" MAC_FMT, MAC_ARG(hdr->addr1));
+	if (hdrlen >= 16)
+		printk(" A2=" MAC_FMT, MAC_ARG(hdr->addr2));
+	if (hdrlen >= 24)
+		printk(" A3=" MAC_FMT, MAC_ARG(hdr->addr3));
+	if (hdrlen >= 30)
+		printk(" A4=" MAC_FMT, MAC_ARG(hdr->addr4));
+	printk("\n");
+}
+#else /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */
+static inline void ieee80211_dump_frame(const char *ifname, const char *title,
+					struct sk_buff *skb)
+{
+}
+#endif /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */
+
+static u16 ieee80211_duration(struct ieee80211_txrx_data *tx, int group_addr,
+			      int next_frag_len)
+{
+	int rate, mrate, erp, dur, i;
+	struct ieee80211_rate *txrate = tx->u.tx.rate;
+	struct ieee80211_local *local = tx->local;
+	struct ieee80211_hw_mode *mode = tx->u.tx.mode;
+
+	erp = txrate->flags & IEEE80211_RATE_ERP;
+
+	/*
+	 * data and mgmt (except PS Poll):
+	 * - during CFP: 32768
+	 * - during contention period:
+	 *   if addr1 is group address: 0
+	 *   if more fragments = 0 and addr1 is individual address: time to
+	 *      transmit one ACK plus SIFS
+	 *   if more fragments = 1 and addr1 is individual address: time to
+	 *      transmit next fragment plus 2 x ACK plus 3 x SIFS
+	 *
+	 * IEEE 802.11, 9.6:
+	 * - control response frame (CTS or ACK) shall be transmitted using the
+	 *   same rate as the immediately previous frame in the frame exchange
+	 *   sequence, if this rate belongs to the PHY mandatory rates, or else
+	 *   at the highest possible rate belonging to the PHY rates in the
+	 *   BSSBasicRateSet
+	 */
+
+	if ((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) {
+		/* TODO: These control frames are not currently sent by
+		 * 80211.o, but should they be implemented, this function
+		 * needs to be updated to support duration field calculation.
+		 *
+		 * RTS: time needed to transmit pending data/mgmt frame plus
+		 *    one CTS frame plus one ACK frame plus 3 x SIFS
+		 * CTS: duration of immediately previous RTS minus time
+		 *    required to transmit CTS and its SIFS
+		 * ACK: 0 if immediately previous directed data/mgmt had
+		 *    more=0, with more=1 duration in ACK frame is duration
+		 *    from previous frame minus time needed to transmit ACK
+		 *    and its SIFS
+		 * PS Poll: BIT(15) | BIT(14) | aid
+		 */
+		return 0;
+	}
+
+	/* data/mgmt */
+	if (0 /* FIX: data/mgmt during CFP */)
+		return 32768;
+
+	if (group_addr) /* Group address as the destination - no ACK */
+		return 0;
+
+	/* Individual destination address:
+	 * IEEE 802.11, Ch. 9.6 (after IEEE 802.11g changes)
+	 * CTS and ACK frames shall be transmitted using the highest rate in
+	 * basic rate set that is less than or equal to the rate of the
+	 * immediately previous frame and that is using the same modulation
+	 * (CCK or OFDM). If no basic rate set matches with these requirements,
+	 * the highest mandatory rate of the PHY that is less than or equal to
+	 * the rate of the previous frame is used.
+	 * Mandatory rates for IEEE 802.11g PHY: 1, 2, 5.5, 11, 6, 12, 24 Mbps
+	 */
+	rate = -1;
+	mrate = 10; /* use 1 Mbps if everything fails */
+	for (i = 0; i < mode->num_rates; i++) {
+		struct ieee80211_rate *r = &mode->rates[i];
+		if (r->rate > txrate->rate)
+			break;
+
+		if (IEEE80211_RATE_MODULATION(txrate->flags) !=
+		    IEEE80211_RATE_MODULATION(r->flags))
+			continue;
+
+		if (r->flags & IEEE80211_RATE_BASIC)
+			rate = r->rate;
+		else if (r->flags & IEEE80211_RATE_MANDATORY)
+			mrate = r->rate;
+	}
+	if (rate == -1) {
+		/* No matching basic rate found; use highest suitable mandatory
+		 * PHY rate */
+		rate = mrate;
+	}
+
+	/* Time needed to transmit ACK
+	 * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up
+	 * to closest integer */
+
+	dur = ieee80211_frame_duration(local, 10, rate, erp,
+				       tx->sdata->short_preamble);
+
+	if (next_frag_len) {
+		/* Frame is fragmented: duration increases with time needed to
+		 * transmit next fragment plus ACK and 2 x SIFS. */
+		dur *= 2; /* ACK + SIFS */
+		/* next fragment */
+		dur += ieee80211_frame_duration(local, next_frag_len,
+						txrate->rate, erp,
+						tx->sdata->short_preamble);
+	}
+
+	return dur;
+}
+
+static inline int __ieee80211_queue_stopped(const struct ieee80211_local *local,
+					    int queue)
+{
+	return test_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
+}
+
+static inline int __ieee80211_queue_pending(const struct ieee80211_local *local,
+					    int queue)
+{
+	return test_bit(IEEE80211_LINK_STATE_PENDING, &local->state[queue]);
+}
+
+static int inline is_ieee80211_device(struct net_device *dev,
+				      struct net_device *master)
+{
+	return (wdev_priv(dev->ieee80211_ptr) ==
+		wdev_priv(master->ieee80211_ptr));
+}
+
+/* tx handlers */
+
+static ieee80211_txrx_result
+ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx)
+{
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+	struct sk_buff *skb = tx->skb;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
+	u32 sta_flags;
+
+	if (unlikely(tx->local->sta_scanning != 0) &&
+	    ((tx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
+	     (tx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PROBE_REQ))
+		return TXRX_DROP;
+
+	if (tx->u.tx.ps_buffered)
+		return TXRX_CONTINUE;
+
+	sta_flags = tx->sta ? tx->sta->flags : 0;
+
+	if (likely(tx->u.tx.unicast)) {
+		if (unlikely(!(sta_flags & WLAN_STA_ASSOC) &&
+			     tx->sdata->type != IEEE80211_IF_TYPE_IBSS &&
+			     (tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)) {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+			printk(KERN_DEBUG "%s: dropped data frame to not "
+			       "associated station " MAC_FMT "\n",
+			       tx->dev->name, MAC_ARG(hdr->addr1));
+#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
+			I802_DEBUG_INC(tx->local->tx_handlers_drop_not_assoc);
+			return TXRX_DROP;
+		}
+	} else {
+		if (unlikely((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
+			     tx->local->num_sta == 0 &&
+			     !tx->local->allow_broadcast_always &&
+			     tx->sdata->type != IEEE80211_IF_TYPE_IBSS)) {
+			/*
+			 * No associated STAs - no need to send multicast
+			 * frames.
+			 */
+			return TXRX_DROP;
+		}
+		return TXRX_CONTINUE;
+	}
+
+	if (unlikely(!tx->u.tx.mgmt_interface && tx->sdata->ieee802_1x &&
+		     !(sta_flags & WLAN_STA_AUTHORIZED))) {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+		printk(KERN_DEBUG "%s: dropped frame to " MAC_FMT
+		       " (unauthorized port)\n", tx->dev->name,
+		       MAC_ARG(hdr->addr1));
+#endif
+		I802_DEBUG_INC(tx->local->tx_handlers_drop_unauth_port);
+		return TXRX_DROP;
+	}
+
+	return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_tx_h_sequence(struct ieee80211_txrx_data *tx)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
+
+	if (ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control)) >= 24)
+		ieee80211_include_sequence(tx->sdata, hdr);
+
+	return TXRX_CONTINUE;
+}
+
+/* This function is called whenever the AP is about to exceed the maximum limit
+ * of buffered frames for power saving STAs. This situation should not really
+ * happen often during normal operation, so dropping the oldest buffered packet
+ * from each queue should be OK to make some room for new frames. */
+static void purge_old_ps_buffers(struct ieee80211_local *local)
+{
+	int total = 0, purged = 0;
+	struct sk_buff *skb;
+	struct ieee80211_sub_if_data *sdata;
+	struct sta_info *sta;
+
+	read_lock(&local->sub_if_lock);
+	list_for_each_entry(sdata, &local->sub_if_list, list) {
+		struct ieee80211_if_ap *ap;
+		if (sdata->dev == local->mdev ||
+		    sdata->type != IEEE80211_IF_TYPE_AP)
+			continue;
+		ap = &sdata->u.ap;
+		skb = skb_dequeue(&ap->ps_bc_buf);
+		if (skb) {
+			purged++;
+			dev_kfree_skb(skb);
+		}
+		total += skb_queue_len(&ap->ps_bc_buf);
+	}
+	read_unlock(&local->sub_if_lock);
+
+	read_lock_bh(&local->sta_lock);
+	list_for_each_entry(sta, &local->sta_list, list) {
+		skb = skb_dequeue(&sta->ps_tx_buf);
+		if (skb) {
+			purged++;
+			dev_kfree_skb(skb);
+		}
+		total += skb_queue_len(&sta->ps_tx_buf);
+	}
+	read_unlock_bh(&local->sta_lock);
+
+	local->total_ps_buffered = total;
+	printk(KERN_DEBUG "%s: PS buffers full - purged %d frames\n",
+	       local->mdev->name, purged);
+}
+
+static inline ieee80211_txrx_result
+ieee80211_tx_h_multicast_ps_buf(struct ieee80211_txrx_data *tx)
+{
+	/* broadcast/multicast frame */
+	/* If any of the associated stations is in power save mode,
+	 * the frame is buffered to be sent after DTIM beacon frame */
+	if ((tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING) &&
+	    tx->sdata->type != IEEE80211_IF_TYPE_WDS &&
+	    tx->sdata->bss && atomic_read(&tx->sdata->bss->num_sta_ps) &&
+	    !(tx->fc & IEEE80211_FCTL_ORDER)) {
+		if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
+			purge_old_ps_buffers(tx->local);
+		if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >=
+		    AP_MAX_BC_BUFFER) {
+			if (net_ratelimit()) {
+				printk(KERN_DEBUG "%s: BC TX buffer full - "
+				       "dropping the oldest frame\n",
+				       tx->dev->name);
+			}
+			dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf));
+		} else
+			tx->local->total_ps_buffered++;
+		skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb);
+		return TXRX_QUEUED;
+	}
+
+	return TXRX_CONTINUE;
+}
+
+static inline ieee80211_txrx_result
+ieee80211_tx_h_unicast_ps_buf(struct ieee80211_txrx_data *tx)
+{
+	struct sta_info *sta = tx->sta;
+
+	if (unlikely(!sta ||
+		     ((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT &&
+		      (tx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP)))
+		return TXRX_CONTINUE;
+
+	if (unlikely((sta->flags & WLAN_STA_PS) && !sta->pspoll)) {
+		struct ieee80211_tx_packet_data *pkt_data;
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+		printk(KERN_DEBUG "STA " MAC_FMT " aid %d: PS buffer (entries "
+		       "before %d)\n",
+		       MAC_ARG(sta->addr), sta->aid,
+		       skb_queue_len(&sta->ps_tx_buf));
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+		sta->flags |= WLAN_STA_TIM;
+		if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
+			purge_old_ps_buffers(tx->local);
+		if (skb_queue_len(&sta->ps_tx_buf) >= STA_MAX_TX_BUFFER) {
+			struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf);
+			if (net_ratelimit()) {
+				printk(KERN_DEBUG "%s: STA " MAC_FMT " TX "
+				       "buffer full - dropping oldest frame\n",
+				       tx->dev->name, MAC_ARG(sta->addr));
+			}
+			dev_kfree_skb(old);
+		} else
+			tx->local->total_ps_buffered++;
+		/* Queue frame to be sent after STA sends an PS Poll frame */
+		if (skb_queue_empty(&sta->ps_tx_buf)) {
+			if (tx->local->ops->set_tim)
+				tx->local->ops->set_tim(local_to_hw(tx->local),
+						       sta->aid, 1);
+			if (tx->sdata->bss)
+				bss_tim_set(tx->local, tx->sdata->bss, sta->aid);
+		}
+		pkt_data = (struct ieee80211_tx_packet_data *)tx->skb->cb;
+		pkt_data->jiffies = jiffies;
+		skb_queue_tail(&sta->ps_tx_buf, tx->skb);
+		return TXRX_QUEUED;
+	}
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+	else if (unlikely(sta->flags & WLAN_STA_PS)) {
+		printk(KERN_DEBUG "%s: STA " MAC_FMT " in PS mode, but pspoll "
+		       "set -> send frame\n", tx->dev->name,
+		       MAC_ARG(sta->addr));
+	}
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+	sta->pspoll = 0;
+
+	return TXRX_CONTINUE;
+}
+
+
+static ieee80211_txrx_result
+ieee80211_tx_h_ps_buf(struct ieee80211_txrx_data *tx)
+{
+	if (unlikely(tx->u.tx.ps_buffered))
+		return TXRX_CONTINUE;
+
+	if (tx->u.tx.unicast)
+		return ieee80211_tx_h_unicast_ps_buf(tx);
+	else
+		return ieee80211_tx_h_multicast_ps_buf(tx);
+}
+
+
+
+
+static ieee80211_txrx_result
+ieee80211_tx_h_select_key(struct ieee80211_txrx_data *tx)
+{
+	if (tx->sta)
+		tx->u.tx.control->key_idx = tx->sta->key_idx_compression;
+	else
+		tx->u.tx.control->key_idx = HW_KEY_IDX_INVALID;
+
+	if (unlikely(tx->u.tx.control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT))
+		tx->key = NULL;
+	else if (tx->sta && tx->sta->key)
+		tx->key = tx->sta->key;
+	else if (tx->sdata->default_key)
+		tx->key = tx->sdata->default_key;
+	else if (tx->sdata->drop_unencrypted &&
+		 !(tx->sdata->eapol && ieee80211_is_eapol(tx->skb))) {
+		I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted);
+		return TXRX_DROP;
+	} else
+		tx->key = NULL;
+
+	if (tx->key) {
+		tx->key->tx_rx_count++;
+		if (unlikely(tx->local->key_tx_rx_threshold &&
+			     tx->key->tx_rx_count >
+			     tx->local->key_tx_rx_threshold)) {
+			ieee80211_key_threshold_notify(tx->dev, tx->key,
+						       tx->sta);
+		}
+	}
+
+	return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_tx_h_fragment(struct ieee80211_txrx_data *tx)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
+	size_t hdrlen, per_fragm, num_fragm, payload_len, left;
+	struct sk_buff **frags, *first, *frag;
+	int i;
+	u16 seq;
+	u8 *pos;
+	int frag_threshold = tx->local->fragmentation_threshold;
+
+	if (!tx->fragmented)
+		return TXRX_CONTINUE;
+
+	first = tx->skb;
+
+	hdrlen = ieee80211_get_hdrlen(tx->fc);
+	payload_len = first->len - hdrlen;
+	per_fragm = frag_threshold - hdrlen - FCS_LEN;
+	num_fragm = (payload_len + per_fragm - 1) / per_fragm;
+
+	frags = kzalloc(num_fragm * sizeof(struct sk_buff *), GFP_ATOMIC);
+	if (!frags)
+		goto fail;
+
+	hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
+	seq = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ;
+	pos = first->data + hdrlen + per_fragm;
+	left = payload_len - per_fragm;
+	for (i = 0; i < num_fragm - 1; i++) {
+		struct ieee80211_hdr *fhdr;
+		size_t copylen;
+
+		if (left <= 0)
+			goto fail;
+
+		/* reserve enough extra head and tail room for possible
+		 * encryption */
+		frag = frags[i] =
+			dev_alloc_skb(tx->local->tx_headroom +
+				      frag_threshold +
+				      IEEE80211_ENCRYPT_HEADROOM +
+				      IEEE80211_ENCRYPT_TAILROOM);
+		if (!frag)
+			goto fail;
+		/* Make sure that all fragments use the same priority so
+		 * that they end up using the same TX queue */
+		frag->priority = first->priority;
+		skb_reserve(frag, tx->local->tx_headroom +
+				  IEEE80211_ENCRYPT_HEADROOM);
+		fhdr = (struct ieee80211_hdr *) skb_put(frag, hdrlen);
+		memcpy(fhdr, first->data, hdrlen);
+		if (i == num_fragm - 2)
+			fhdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREFRAGS);
+		fhdr->seq_ctrl = cpu_to_le16(seq | ((i + 1) & IEEE80211_SCTL_FRAG));
+		copylen = left > per_fragm ? per_fragm : left;
+		memcpy(skb_put(frag, copylen), pos, copylen);
+
+		pos += copylen;
+		left -= copylen;
+	}
+	skb_trim(first, hdrlen + per_fragm);
+
+	tx->u.tx.num_extra_frag = num_fragm - 1;
+	tx->u.tx.extra_frag = frags;
+
+	return TXRX_CONTINUE;
+
+ fail:
+	printk(KERN_DEBUG "%s: failed to fragment frame\n", tx->dev->name);
+	if (frags) {
+		for (i = 0; i < num_fragm - 1; i++)
+			if (frags[i])
+				dev_kfree_skb(frags[i]);
+		kfree(frags);
+	}
+	I802_DEBUG_INC(tx->local->tx_handlers_drop_fragment);
+	return TXRX_DROP;
+}
+
+static int wep_encrypt_skb(struct ieee80211_txrx_data *tx, struct sk_buff *skb)
+{
+	if (tx->key->force_sw_encrypt) {
+		if (ieee80211_wep_encrypt(tx->local, skb, tx->key))
+			return -1;
+	} else {
+		tx->u.tx.control->key_idx = tx->key->hw_key_idx;
+		if (tx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) {
+			if (ieee80211_wep_add_iv(tx->local, skb, tx->key) ==
+			    NULL)
+				return -1;
+		}
+	}
+	return 0;
+}
+
+static ieee80211_txrx_result
+ieee80211_tx_h_wep_encrypt(struct ieee80211_txrx_data *tx)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
+	u16 fc;
+
+	fc = le16_to_cpu(hdr->frame_control);
+
+	if (!tx->key || tx->key->alg != ALG_WEP ||
+	    ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
+	     ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
+	      (fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH)))
+		return TXRX_CONTINUE;
+
+	tx->u.tx.control->iv_len = WEP_IV_LEN;
+	tx->u.tx.control->icv_len = WEP_ICV_LEN;
+	ieee80211_tx_set_iswep(tx);
+
+	if (wep_encrypt_skb(tx, tx->skb) < 0) {
+		I802_DEBUG_INC(tx->local->tx_handlers_drop_wep);
+		return TXRX_DROP;
+	}
+
+	if (tx->u.tx.extra_frag) {
+		int i;
+		for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
+			if (wep_encrypt_skb(tx, tx->u.tx.extra_frag[i]) < 0) {
+				I802_DEBUG_INC(tx->local->
+					       tx_handlers_drop_wep);
+				return TXRX_DROP;
+			}
+		}
+	}
+
+	return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_tx_h_rate_ctrl(struct ieee80211_txrx_data *tx)
+{
+	struct rate_control_extra extra;
+
+	memset(&extra, 0, sizeof(extra));
+	extra.mode = tx->u.tx.mode;
+	extra.mgmt_data = tx->sdata &&
+		tx->sdata->type == IEEE80211_IF_TYPE_MGMT;
+	extra.ethertype = tx->ethertype;
+
+	tx->u.tx.rate = rate_control_get_rate(tx->local, tx->dev, tx->skb,
+					      &extra);
+	if (unlikely(extra.probe != NULL)) {
+		tx->u.tx.control->flags |= IEEE80211_TXCTL_RATE_CTRL_PROBE;
+		tx->u.tx.probe_last_frag = 1;
+		tx->u.tx.control->alt_retry_rate = tx->u.tx.rate->val;
+		tx->u.tx.rate = extra.probe;
+	} else {
+		tx->u.tx.control->alt_retry_rate = -1;
+	}
+	if (!tx->u.tx.rate)
+		return TXRX_DROP;
+	if (tx->u.tx.mode->mode == MODE_IEEE80211G &&
+	    tx->sdata->use_protection && tx->fragmented &&
+	    extra.nonerp) {
+		tx->u.tx.last_frag_rate = tx->u.tx.rate;
+		tx->u.tx.probe_last_frag = extra.probe ? 1 : 0;
+
+		tx->u.tx.rate = extra.nonerp;
+		tx->u.tx.control->rate = extra.nonerp;
+		tx->u.tx.control->flags &= ~IEEE80211_TXCTL_RATE_CTRL_PROBE;
+	} else {
+		tx->u.tx.last_frag_rate = tx->u.tx.rate;
+		tx->u.tx.control->rate = tx->u.tx.rate;
+	}
+	tx->u.tx.control->tx_rate = tx->u.tx.rate->val;
+
+	return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
+	u16 fc = le16_to_cpu(hdr->frame_control);
+	u16 dur;
+	struct ieee80211_tx_control *control = tx->u.tx.control;
+	struct ieee80211_hw_mode *mode = tx->u.tx.mode;
+
+	if (!is_multicast_ether_addr(hdr->addr1)) {
+		if (tx->skb->len + FCS_LEN > tx->local->rts_threshold &&
+		    tx->local->rts_threshold < IEEE80211_MAX_RTS_THRESHOLD) {
+			control->flags |= IEEE80211_TXCTL_USE_RTS_CTS;
+			control->flags |= IEEE80211_TXCTL_LONG_RETRY_LIMIT;
+			control->retry_limit =
+				tx->local->long_retry_limit;
+		} else {
+			control->retry_limit =
+				tx->local->short_retry_limit;
+		}
+	} else {
+		control->retry_limit = 1;
+	}
+
+	if (tx->fragmented) {
+		/* Do not use multiple retry rates when sending fragmented
+		 * frames.
+		 * TODO: The last fragment could still use multiple retry
+		 * rates. */
+		control->alt_retry_rate = -1;
+	}
+
+	/* Use CTS protection for unicast frames sent using extended rates if
+	 * there are associated non-ERP stations and RTS/CTS is not configured
+	 * for the frame. */
+	if (mode->mode == MODE_IEEE80211G &&
+	    (tx->u.tx.rate->flags & IEEE80211_RATE_ERP) &&
+	    tx->u.tx.unicast && tx->sdata->use_protection &&
+	    !(control->flags & IEEE80211_TXCTL_USE_RTS_CTS))
+		control->flags |= IEEE80211_TXCTL_USE_CTS_PROTECT;
+
+	/* Transmit data frames using short preambles if the driver supports
+	 * short preambles at the selected rate and short preambles are
+	 * available on the network at the current point in time. */
+	if (((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) &&
+	    (tx->u.tx.rate->flags & IEEE80211_RATE_PREAMBLE2) &&
+	    tx->sdata->short_preamble &&
+	    (!tx->sta || (tx->sta->flags & WLAN_STA_SHORT_PREAMBLE))) {
+		tx->u.tx.control->tx_rate = tx->u.tx.rate->val2;
+	}
+
+	/* Setup duration field for the first fragment of the frame. Duration
+	 * for remaining fragments will be updated when they are being sent
+	 * to low-level driver in ieee80211_tx(). */
+	dur = ieee80211_duration(tx, is_multicast_ether_addr(hdr->addr1),
+				 tx->fragmented ? tx->u.tx.extra_frag[0]->len :
+				 0);
+	hdr->duration_id = cpu_to_le16(dur);
+
+	if ((control->flags & IEEE80211_TXCTL_USE_RTS_CTS) ||
+	    (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)) {
+		struct ieee80211_rate *rate;
+
+		/* Do not use multiple retry rates when using RTS/CTS */
+		control->alt_retry_rate = -1;
+
+		/* Use min(data rate, max base rate) as CTS/RTS rate */
+		rate = tx->u.tx.rate;
+		while (rate > mode->rates &&
+		       !(rate->flags & IEEE80211_RATE_BASIC))
+			rate--;
+
+		control->rts_cts_rate = rate->val;
+		control->rts_rate = rate;
+	}
+
+	if (tx->sta) {
+		tx->sta->tx_packets++;
+		tx->sta->tx_fragments++;
+		tx->sta->tx_bytes += tx->skb->len;
+		if (tx->u.tx.extra_frag) {
+			int i;
+			tx->sta->tx_fragments += tx->u.tx.num_extra_frag;
+			for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
+				tx->sta->tx_bytes +=
+					tx->u.tx.extra_frag[i]->len;
+			}
+		}
+	}
+
+	return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result
+ieee80211_tx_h_load_stats(struct ieee80211_txrx_data *tx)
+{
+	struct ieee80211_local *local = tx->local;
+	struct ieee80211_hw_mode *mode = tx->u.tx.mode;
+	struct sk_buff *skb = tx->skb;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+	u32 load = 0, hdrtime;
+
+	/* TODO: this could be part of tx_status handling, so that the number
+	 * of retries would be known; TX rate should in that case be stored
+	 * somewhere with the packet */
+
+	/* Estimate total channel use caused by this frame */
+
+	/* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values,
+	 * 1 usec = 1/8 * (1080 / 10) = 13.5 */
+
+	if (mode->mode == MODE_IEEE80211A ||
+	    mode->mode == MODE_ATHEROS_TURBO ||
+	    mode->mode == MODE_ATHEROS_TURBOG ||
+	    (mode->mode == MODE_IEEE80211G &&
+	     tx->u.tx.rate->flags & IEEE80211_RATE_ERP))
+		hdrtime = CHAN_UTIL_HDR_SHORT;
+	else
+		hdrtime = CHAN_UTIL_HDR_LONG;
+
+	load = hdrtime;
+	if (!is_multicast_ether_addr(hdr->addr1))
+		load += hdrtime;
+
+	if (tx->u.tx.control->flags & IEEE80211_TXCTL_USE_RTS_CTS)
+		load += 2 * hdrtime;
+	else if (tx->u.tx.control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)
+		load += hdrtime;
+
+	load += skb->len * tx->u.tx.rate->rate_inv;
+
+	if (tx->u.tx.extra_frag) {
+		int i;
+		for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
+			load += 2 * hdrtime;
+			load += tx->u.tx.extra_frag[i]->len *
+				tx->u.tx.rate->rate;
+		}
+	}
+
+	/* Divide channel_use by 8 to avoid wrapping around the counter */
+	load >>= CHAN_UTIL_SHIFT;
+	local->channel_use_raw += load;
+	if (tx->sta)
+		tx->sta->channel_use_raw += load;
+	tx->sdata->channel_use_raw += load;
+
+	return TXRX_CONTINUE;
+}
+
+/* TODO: implement register/unregister functions for adding TX/RX handlers
+ * into ordered list */
+
+ieee80211_tx_handler ieee80211_tx_handlers[] =
+{
+	ieee80211_tx_h_check_assoc,
+	ieee80211_tx_h_sequence,
+	ieee80211_tx_h_ps_buf,
+	ieee80211_tx_h_select_key,
+	ieee80211_tx_h_michael_mic_add,
+	ieee80211_tx_h_fragment,
+	ieee80211_tx_h_tkip_encrypt,
+	ieee80211_tx_h_ccmp_encrypt,
+	ieee80211_tx_h_wep_encrypt,
+	ieee80211_tx_h_rate_ctrl,
+	ieee80211_tx_h_misc,
+	ieee80211_tx_h_load_stats,
+	NULL
+};
+
+/* actual transmit path */
+
+/*
+ * deal with packet injection down monitor interface
+ * with Radiotap Header -- only called for monitor mode interface
+ */
+static ieee80211_txrx_result
+__ieee80211_parse_tx_radiotap(
+	struct ieee80211_txrx_data *tx,
+	struct sk_buff *skb, struct ieee80211_tx_control *control)
+{
+	/*
+	 * this is the moment to interpret and discard the radiotap header that
+	 * must be at the start of the packet injected in Monitor mode
+	 *
+	 * Need to take some care with endian-ness since radiotap
+	 * args are little-endian
+	 */
+
+	struct ieee80211_radiotap_iterator iterator;
+	struct ieee80211_radiotap_header *rthdr =
+		(struct ieee80211_radiotap_header *) skb->data;
+	struct ieee80211_hw_mode *mode = tx->local->hw.conf.mode;
+	int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
+
+	/*
+	 * default control situation for all injected packets
+	 * FIXME: this does not suit all usage cases, expand to allow control
+	 */
+
+	control->retry_limit = 1; /* no retry */
+	control->key_idx = -1; /* no encryption key */
+	control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS |
+			    IEEE80211_TXCTL_USE_CTS_PROTECT);
+	control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT |
+			  IEEE80211_TXCTL_NO_ACK;
+	control->antenna_sel_tx = 0; /* default to default antenna */
+
+	/*
+	 * for every radiotap entry that is present
+	 * (ieee80211_radiotap_iterator_next returns -ENOENT when no more
+	 * entries present, or -EINVAL on error)
+	 */
+
+	while (!ret) {
+		int i, target_rate;
+
+		ret = ieee80211_radiotap_iterator_next(&iterator);
+
+		if (ret)
+			continue;
+
+		/* see if this argument is something we can use */
+		switch (iterator.this_arg_index) {
+		/*
+		 * You must take care when dereferencing iterator.this_arg
+		 * for multibyte types... the pointer is not aligned.  Use
+		 * get_unaligned((type *)iterator.this_arg) to dereference
+		 * iterator.this_arg for type "type" safely on all arches.
+		*/
+		case IEEE80211_RADIOTAP_RATE:
+			/*
+			 * radiotap rate u8 is in 500kbps units eg, 0x02=1Mbps
+			 * ieee80211 rate int is in 100kbps units eg, 0x0a=1Mbps
+			 */
+			target_rate = (*iterator.this_arg) * 5;
+			for (i = 0; i < mode->num_rates; i++) {
+				struct ieee80211_rate *r = &mode->rates[i];
+
+				if (r->rate > target_rate)
+					continue;
+
+				control->rate = r;
+
+				if (r->flags & IEEE80211_RATE_PREAMBLE2)
+					control->tx_rate = r->val2;
+				else
+					control->tx_rate = r->val;
+
+				/* end on exact match */
+				if (r->rate == target_rate)
+					i = mode->num_rates;
+			}
+			break;
+
+		case IEEE80211_RADIOTAP_ANTENNA:
+			/*
+			 * radiotap uses 0 for 1st ant, mac80211 is 1 for
+			 * 1st ant
+			 */
+			control->antenna_sel_tx = (*iterator.this_arg) + 1;
+			break;
+
+		case IEEE80211_RADIOTAP_DBM_TX_POWER:
+			control->power_level = *iterator.this_arg;
+			break;
+
+		case IEEE80211_RADIOTAP_FLAGS:
+			if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FCS) {
+				/*
+				 * this indicates that the skb we have been
+				 * handed has the 32-bit FCS CRC at the end...
+				 * we should react to that by snipping it off
+				 * because it will be recomputed and added
+				 * on transmission
+				 */
+				if (skb->len < (iterator.max_length + FCS_LEN))
+					return TXRX_DROP;
+
+				skb_trim(skb, skb->len - FCS_LEN);
+			}
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */
+		return TXRX_DROP;
+
+	/*
+	 * remove the radiotap header
+	 * iterator->max_length was sanity-checked against
+	 * skb->len by iterator init
+	 */
+	skb_pull(skb, iterator.max_length);
+
+	return TXRX_CONTINUE;
+}
+
+static ieee80211_txrx_result inline
+__ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
+		       struct sk_buff *skb,
+		       struct net_device *dev,
+		       struct ieee80211_tx_control *control)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+	struct ieee80211_sub_if_data *sdata;
+	ieee80211_txrx_result res = TXRX_CONTINUE;
+
+	int hdrlen;
+
+	memset(tx, 0, sizeof(*tx));
+	tx->skb = skb;
+	tx->dev = dev; /* use original interface */
+	tx->local = local;
+	tx->sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	tx->sta = sta_info_get(local, hdr->addr1);
+	tx->fc = le16_to_cpu(hdr->frame_control);
+
+	/*
+	 * set defaults for things that can be set by
+	 * injected radiotap headers
+	 */
+	control->power_level = local->hw.conf.power_level;
+	control->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
+	if (local->sta_antenna_sel != STA_ANTENNA_SEL_AUTO && tx->sta)
+		control->antenna_sel_tx = tx->sta->antenna_sel_tx;
+
+	/* process and remove the injection radiotap header */
+	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	if (unlikely(sdata->type == IEEE80211_IF_TYPE_MNTR)) {
+		if (__ieee80211_parse_tx_radiotap(tx, skb, control) ==
+								TXRX_DROP) {
+			return TXRX_DROP;
+		}
+		/*
+		 * we removed the radiotap header after this point,
+		 * we filled control with what we could use
+		 * set to the actual ieee header now
+		 */
+		hdr = (struct ieee80211_hdr *) skb->data;
+		res = TXRX_QUEUED; /* indication it was monitor packet */
+	}
+
+	tx->u.tx.control = control;
+	tx->u.tx.unicast = !is_multicast_ether_addr(hdr->addr1);
+	if (is_multicast_ether_addr(hdr->addr1))
+		control->flags |= IEEE80211_TXCTL_NO_ACK;
+	else
+		control->flags &= ~IEEE80211_TXCTL_NO_ACK;
+	tx->fragmented = local->fragmentation_threshold <
+		IEEE80211_MAX_FRAG_THRESHOLD && tx->u.tx.unicast &&
+		skb->len + FCS_LEN > local->fragmentation_threshold &&
+		(!local->ops->set_frag_threshold);
+	if (!tx->sta)
+		control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK;
+	else if (tx->sta->clear_dst_mask) {
+		control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK;
+		tx->sta->clear_dst_mask = 0;
+	}
+	hdrlen = ieee80211_get_hdrlen(tx->fc);
+	if (skb->len > hdrlen + sizeof(rfc1042_header) + 2) {
+		u8 *pos = &skb->data[hdrlen + sizeof(rfc1042_header)];
+		tx->ethertype = (pos[0] << 8) | pos[1];
+	}
+	control->flags |= IEEE80211_TXCTL_FIRST_FRAGMENT;
+
+	return res;
+}
+
+/* Device in tx->dev has a reference added; use dev_put(tx->dev) when
+ * finished with it. */
+static int inline ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
+				       struct sk_buff *skb,
+				       struct net_device *mdev,
+				       struct ieee80211_tx_control *control)
+{
+	struct ieee80211_tx_packet_data *pkt_data;
+	struct net_device *dev;
+
+	pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
+	dev = dev_get_by_index(pkt_data->ifindex);
+	if (unlikely(dev && !is_ieee80211_device(dev, mdev))) {
+		dev_put(dev);
+		dev = NULL;
+	}
+	if (unlikely(!dev))
+		return -ENODEV;
+	__ieee80211_tx_prepare(tx, skb, dev, control);
+	return 0;
+}
+
+static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
+			  struct ieee80211_txrx_data *tx)
+{
+	struct ieee80211_tx_control *control = tx->u.tx.control;
+	int ret, i;
+
+	if (!ieee80211_qdisc_installed(local->mdev) &&
+	    __ieee80211_queue_stopped(local, 0)) {
+		netif_stop_queue(local->mdev);
+		return IEEE80211_TX_AGAIN;
+	}
+	if (skb) {
+		ieee80211_dump_frame(local->mdev->name, "TX to low-level driver", skb);
+		ret = local->ops->tx(local_to_hw(local), skb, control);
+		if (ret)
+			return IEEE80211_TX_AGAIN;
+		local->mdev->trans_start = jiffies;
+		ieee80211_led_tx(local, 1);
+	}
+	if (tx->u.tx.extra_frag) {
+		control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS |
+				    IEEE80211_TXCTL_USE_CTS_PROTECT |
+				    IEEE80211_TXCTL_CLEAR_DST_MASK |
+				    IEEE80211_TXCTL_FIRST_FRAGMENT);
+		for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
+			if (!tx->u.tx.extra_frag[i])
+				continue;
+			if (__ieee80211_queue_stopped(local, control->queue))
+				return IEEE80211_TX_FRAG_AGAIN;
+			if (i == tx->u.tx.num_extra_frag) {
+				control->tx_rate = tx->u.tx.last_frag_hwrate;
+				control->rate = tx->u.tx.last_frag_rate;
+				if (tx->u.tx.probe_last_frag)
+					control->flags |=
+						IEEE80211_TXCTL_RATE_CTRL_PROBE;
+				else
+					control->flags &=
+						~IEEE80211_TXCTL_RATE_CTRL_PROBE;
+			}
+
+			ieee80211_dump_frame(local->mdev->name,
+					     "TX to low-level driver",
+					     tx->u.tx.extra_frag[i]);
+			ret = local->ops->tx(local_to_hw(local),
+					    tx->u.tx.extra_frag[i],
+					    control);
+			if (ret)
+				return IEEE80211_TX_FRAG_AGAIN;
+			local->mdev->trans_start = jiffies;
+			ieee80211_led_tx(local, 1);
+			tx->u.tx.extra_frag[i] = NULL;
+		}
+		kfree(tx->u.tx.extra_frag);
+		tx->u.tx.extra_frag = NULL;
+	}
+	return IEEE80211_TX_OK;
+}
+
+static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
+			struct ieee80211_tx_control *control, int mgmt)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct sta_info *sta;
+	ieee80211_tx_handler *handler;
+	struct ieee80211_txrx_data tx;
+	ieee80211_txrx_result res = TXRX_DROP, res_prepare;
+	int ret, i;
+
+	WARN_ON(__ieee80211_queue_pending(local, control->queue));
+
+	if (unlikely(skb->len < 10)) {
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	res_prepare = __ieee80211_tx_prepare(&tx, skb, dev, control);
+
+	if (res_prepare == TXRX_DROP) {
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	sta = tx.sta;
+	tx.u.tx.mgmt_interface = mgmt;
+	tx.u.tx.mode = local->hw.conf.mode;
+
+	if (res_prepare == TXRX_QUEUED) { /* if it was an injected packet */
+		res = TXRX_CONTINUE;
+	} else {
+		for (handler = local->tx_handlers; *handler != NULL;
+		     handler++) {
+			res = (*handler)(&tx);
+			if (res != TXRX_CONTINUE)
+				break;
+		}
+	}
+
+	skb = tx.skb; /* handlers are allowed to change skb */
+
+	if (sta)
+		sta_info_put(sta);
+
+	if (unlikely(res == TXRX_DROP)) {
+		I802_DEBUG_INC(local->tx_handlers_drop);
+		goto drop;
+	}
+
+	if (unlikely(res == TXRX_QUEUED)) {
+		I802_DEBUG_INC(local->tx_handlers_queued);
+		return 0;
+	}
+
+	if (tx.u.tx.extra_frag) {
+		for (i = 0; i < tx.u.tx.num_extra_frag; i++) {
+			int next_len, dur;
+			struct ieee80211_hdr *hdr =
+				(struct ieee80211_hdr *)
+				tx.u.tx.extra_frag[i]->data;
+
+			if (i + 1 < tx.u.tx.num_extra_frag) {
+				next_len = tx.u.tx.extra_frag[i + 1]->len;
+			} else {
+				next_len = 0;
+				tx.u.tx.rate = tx.u.tx.last_frag_rate;
+				tx.u.tx.last_frag_hwrate = tx.u.tx.rate->val;
+			}
+			dur = ieee80211_duration(&tx, 0, next_len);
+			hdr->duration_id = cpu_to_le16(dur);
+		}
+	}
+
+retry:
+	ret = __ieee80211_tx(local, skb, &tx);
+	if (ret) {
+		struct ieee80211_tx_stored_packet *store =
+			&local->pending_packet[control->queue];
+
+		if (ret == IEEE80211_TX_FRAG_AGAIN)
+			skb = NULL;
+		set_bit(IEEE80211_LINK_STATE_PENDING,
+			&local->state[control->queue]);
+		smp_mb();
+		/* When the driver gets out of buffers during sending of
+		 * fragments and calls ieee80211_stop_queue, there is
+		 * a small window between IEEE80211_LINK_STATE_XOFF and
+		 * IEEE80211_LINK_STATE_PENDING flags are set. If a buffer
+		 * gets available in that window (i.e. driver calls
+		 * ieee80211_wake_queue), we would end up with ieee80211_tx
+		 * called with IEEE80211_LINK_STATE_PENDING. Prevent this by
+		 * continuing transmitting here when that situation is
+		 * possible to have happened. */
+		if (!__ieee80211_queue_stopped(local, control->queue)) {
+			clear_bit(IEEE80211_LINK_STATE_PENDING,
+				  &local->state[control->queue]);
+			goto retry;
+		}
+		memcpy(&store->control, control,
+		       sizeof(struct ieee80211_tx_control));
+		store->skb = skb;
+		store->extra_frag = tx.u.tx.extra_frag;
+		store->num_extra_frag = tx.u.tx.num_extra_frag;
+		store->last_frag_hwrate = tx.u.tx.last_frag_hwrate;
+		store->last_frag_rate = tx.u.tx.last_frag_rate;
+		store->last_frag_rate_ctrl_probe = tx.u.tx.probe_last_frag;
+	}
+	return 0;
+
+ drop:
+	if (skb)
+		dev_kfree_skb(skb);
+	for (i = 0; i < tx.u.tx.num_extra_frag; i++)
+		if (tx.u.tx.extra_frag[i])
+			dev_kfree_skb(tx.u.tx.extra_frag[i]);
+	kfree(tx.u.tx.extra_frag);
+	return 0;
+}
+
+/* device xmit handlers */
+
+int ieee80211_master_start_xmit(struct sk_buff *skb,
+				struct net_device *dev)
+{
+	struct ieee80211_tx_control control;
+	struct ieee80211_tx_packet_data *pkt_data;
+	struct net_device *odev = NULL;
+	struct ieee80211_sub_if_data *osdata;
+	int headroom;
+	int ret;
+
+	/*
+	 * copy control out of the skb so other people can use skb->cb
+	 */
+	pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
+	memset(&control, 0, sizeof(struct ieee80211_tx_control));
+
+	if (pkt_data->ifindex)
+		odev = dev_get_by_index(pkt_data->ifindex);
+	if (unlikely(odev && !is_ieee80211_device(odev, dev))) {
+		dev_put(odev);
+		odev = NULL;
+	}
+	if (unlikely(!odev)) {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+		printk(KERN_DEBUG "%s: Discarded packet with nonexistent "
+		       "originating device\n", dev->name);
+#endif
+		dev_kfree_skb(skb);
+		return 0;
+	}
+	osdata = IEEE80211_DEV_TO_SUB_IF(odev);
+
+	headroom = osdata->local->tx_headroom + IEEE80211_ENCRYPT_HEADROOM;
+	if (skb_headroom(skb) < headroom) {
+		if (pskb_expand_head(skb, headroom, 0, GFP_ATOMIC)) {
+			dev_kfree_skb(skb);
+			dev_put(odev);
+			return 0;
+		}
+	}
+
+	control.ifindex = odev->ifindex;
+	control.type = osdata->type;
+	if (pkt_data->req_tx_status)
+		control.flags |= IEEE80211_TXCTL_REQ_TX_STATUS;
+	if (pkt_data->do_not_encrypt)
+		control.flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
+	if (pkt_data->requeue)
+		control.flags |= IEEE80211_TXCTL_REQUEUE;
+	control.queue = pkt_data->queue;
+
+	ret = ieee80211_tx(odev, skb, &control,
+			   control.type == IEEE80211_IF_TYPE_MGMT);
+	dev_put(odev);
+
+	return ret;
+}
+
+int ieee80211_monitor_start_xmit(struct sk_buff *skb,
+				 struct net_device *dev)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_tx_packet_data *pkt_data;
+	struct ieee80211_radiotap_header *prthdr =
+		(struct ieee80211_radiotap_header *)skb->data;
+	u16 len_rthdr;
+
+	/* check for not even having the fixed radiotap header part */
+	if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header)))
+		goto fail; /* too short to be possibly valid */
+
+	/* is it a header version we can trust to find length from? */
+	if (unlikely(prthdr->it_version))
+		goto fail; /* only version 0 is supported */
+
+	/* then there must be a radiotap header with a length we can use */
+	len_rthdr = ieee80211_get_radiotap_len(skb->data);
+
+	/* does the skb contain enough to deliver on the alleged length? */
+	if (unlikely(skb->len < len_rthdr))
+		goto fail; /* skb too short for claimed rt header extent */
+
+	skb->dev = local->mdev;
+
+	pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
+	memset(pkt_data, 0, sizeof(*pkt_data));
+	/* needed because we set skb device to master */
+	pkt_data->ifindex = dev->ifindex;
+
+	pkt_data->mgmt_iface = 0;
+	pkt_data->do_not_encrypt = 1;
+
+	/*
+	 * fix up the pointers accounting for the radiotap
+	 * header still being in there.  We are being given
+	 * a precooked IEEE80211 header so no need for
+	 * normal processing
+	 */
+	skb_set_mac_header(skb, len_rthdr);
+	/*
+	 * these are just fixed to the end of the rt area since we
+	 * don't have any better information and at this point, nobody cares
+	 */
+	skb_set_network_header(skb, len_rthdr);
+	skb_set_transport_header(skb, len_rthdr);
+
+	/* pass the radiotap header up to the next stage intact */
+	dev_queue_xmit(skb);
+	return NETDEV_TX_OK;
+
+fail:
+	dev_kfree_skb(skb);
+	return NETDEV_TX_OK; /* meaning, we dealt with the skb */
+}
+
+/**
+ * ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type
+ * subinterfaces (wlan#, WDS, and VLAN interfaces)
+ * @skb: packet to be sent
+ * @dev: incoming interface
+ *
+ * Returns: 0 on success (and frees skb in this case) or 1 on failure (skb will
+ * not be freed, and caller is responsible for either retrying later or freeing
+ * skb).
+ *
+ * This function takes in an Ethernet header and encapsulates it with suitable
+ * IEEE 802.11 header based on which interface the packet is coming in. The
+ * encapsulated packet will then be passed to master interface, wlan#.11, for
+ * transmission (through low-level driver).
+ */
+int ieee80211_subif_start_xmit(struct sk_buff *skb,
+			       struct net_device *dev)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_tx_packet_data *pkt_data;
+	struct ieee80211_sub_if_data *sdata;
+	int ret = 1, head_need;
+	u16 ethertype, hdrlen, fc;
+	struct ieee80211_hdr hdr;
+	const u8 *encaps_data;
+	int encaps_len, skip_header_bytes;
+	int nh_pos, h_pos, no_encrypt = 0;
+	struct sta_info *sta;
+
+	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	if (unlikely(skb->len < ETH_HLEN)) {
+		printk(KERN_DEBUG "%s: short skb (len=%d)\n",
+		       dev->name, skb->len);
+		ret = 0;
+		goto fail;
+	}
+
+	nh_pos = skb_network_header(skb) - skb->data;
+	h_pos = skb_transport_header(skb) - skb->data;
+
+	/* convert Ethernet header to proper 802.11 header (based on
+	 * operation mode) */
+	ethertype = (skb->data[12] << 8) | skb->data[13];
+	/* TODO: handling for 802.1x authorized/unauthorized port */
+	fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA;
+
+	if (likely(sdata->type == IEEE80211_IF_TYPE_AP ||
+		   sdata->type == IEEE80211_IF_TYPE_VLAN)) {
+		fc |= IEEE80211_FCTL_FROMDS;
+		/* DA BSSID SA */
+		memcpy(hdr.addr1, skb->data, ETH_ALEN);
+		memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
+		memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
+		hdrlen = 24;
+	} else if (sdata->type == IEEE80211_IF_TYPE_WDS) {
+		fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS;
+		/* RA TA DA SA */
+		memcpy(hdr.addr1, sdata->u.wds.remote_addr, ETH_ALEN);
+		memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
+		memcpy(hdr.addr3, skb->data, ETH_ALEN);
+		memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
+		hdrlen = 30;
+	} else if (sdata->type == IEEE80211_IF_TYPE_STA) {
+		fc |= IEEE80211_FCTL_TODS;
+		/* BSSID SA DA */
+		memcpy(hdr.addr1, sdata->u.sta.bssid, ETH_ALEN);
+		memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+		memcpy(hdr.addr3, skb->data, ETH_ALEN);
+		hdrlen = 24;
+	} else if (sdata->type == IEEE80211_IF_TYPE_IBSS) {
+		/* DA SA BSSID */
+		memcpy(hdr.addr1, skb->data, ETH_ALEN);
+		memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+		memcpy(hdr.addr3, sdata->u.sta.bssid, ETH_ALEN);
+		hdrlen = 24;
+	} else {
+		ret = 0;
+		goto fail;
+	}
+
+	/* receiver is QoS enabled, use a QoS type frame */
+	sta = sta_info_get(local, hdr.addr1);
+	if (sta) {
+		if (sta->flags & WLAN_STA_WME) {
+			fc |= IEEE80211_STYPE_QOS_DATA;
+			hdrlen += 2;
+		}
+		sta_info_put(sta);
+	}
+
+	hdr.frame_control = cpu_to_le16(fc);
+	hdr.duration_id = 0;
+	hdr.seq_ctrl = 0;
+
+	skip_header_bytes = ETH_HLEN;
+	if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) {
+		encaps_data = bridge_tunnel_header;
+		encaps_len = sizeof(bridge_tunnel_header);
+		skip_header_bytes -= 2;
+	} else if (ethertype >= 0x600) {
+		encaps_data = rfc1042_header;
+		encaps_len = sizeof(rfc1042_header);
+		skip_header_bytes -= 2;
+	} else {
+		encaps_data = NULL;
+		encaps_len = 0;
+	}
+
+	skb_pull(skb, skip_header_bytes);
+	nh_pos -= skip_header_bytes;
+	h_pos -= skip_header_bytes;
+
+	/* TODO: implement support for fragments so that there is no need to
+	 * reallocate and copy payload; it might be enough to support one
+	 * extra fragment that would be copied in the beginning of the frame
+	 * data.. anyway, it would be nice to include this into skb structure
+	 * somehow
+	 *
+	 * There are few options for this:
+	 * use skb->cb as an extra space for 802.11 header
+	 * allocate new buffer if not enough headroom
+	 * make sure that there is enough headroom in every skb by increasing
+	 * build in headroom in __dev_alloc_skb() (linux/skbuff.h) and
+	 * alloc_skb() (net/core/skbuff.c)
+	 */
+	head_need = hdrlen + encaps_len + local->tx_headroom;
+	head_need -= skb_headroom(skb);
+
+	/* We are going to modify skb data, so make a copy of it if happens to
+	 * be cloned. This could happen, e.g., with Linux bridge code passing
+	 * us broadcast frames. */
+
+	if (head_need > 0 || skb_cloned(skb)) {
+#if 0
+		printk(KERN_DEBUG "%s: need to reallocate buffer for %d bytes "
+		       "of headroom\n", dev->name, head_need);
+#endif
+
+		if (skb_cloned(skb))
+			I802_DEBUG_INC(local->tx_expand_skb_head_cloned);
+		else
+			I802_DEBUG_INC(local->tx_expand_skb_head);
+		/* Since we have to reallocate the buffer, make sure that there
+		 * is enough room for possible WEP IV/ICV and TKIP (8 bytes
+		 * before payload and 12 after). */
+		if (pskb_expand_head(skb, (head_need > 0 ? head_need + 8 : 8),
+				     12, GFP_ATOMIC)) {
+			printk(KERN_DEBUG "%s: failed to reallocate TX buffer"
+			       "\n", dev->name);
+			goto fail;
+		}
+	}
+
+	if (encaps_data) {
+		memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len);
+		nh_pos += encaps_len;
+		h_pos += encaps_len;
+	}
+	memcpy(skb_push(skb, hdrlen), &hdr, hdrlen);
+	nh_pos += hdrlen;
+	h_pos += hdrlen;
+
+	pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
+	memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
+	pkt_data->ifindex = dev->ifindex;
+	pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT);
+	pkt_data->do_not_encrypt = no_encrypt;
+
+	skb->dev = local->mdev;
+	sdata->stats.tx_packets++;
+	sdata->stats.tx_bytes += skb->len;
+
+	/* Update skb pointers to various headers since this modified frame
+	 * is going to go through Linux networking code that may potentially
+	 * need things like pointer to IP header. */
+	skb_set_mac_header(skb, 0);
+	skb_set_network_header(skb, nh_pos);
+	skb_set_transport_header(skb, h_pos);
+
+	dev->trans_start = jiffies;
+	dev_queue_xmit(skb);
+
+	return 0;
+
+ fail:
+	if (!ret)
+		dev_kfree_skb(skb);
+
+	return ret;
+}
+
+/*
+ * This is the transmit routine for the 802.11 type interfaces
+ * called by upper layers of the linux networking
+ * stack when it has a frame to transmit
+ */
+int ieee80211_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ieee80211_sub_if_data *sdata;
+	struct ieee80211_tx_packet_data *pkt_data;
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+
+	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+	if (skb->len < 10) {
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	if (skb_headroom(skb) < sdata->local->tx_headroom) {
+		if (pskb_expand_head(skb, sdata->local->tx_headroom,
+				     0, GFP_ATOMIC)) {
+			dev_kfree_skb(skb);
+			return 0;
+		}
+	}
+
+	hdr = (struct ieee80211_hdr *) skb->data;
+	fc = le16_to_cpu(hdr->frame_control);
+
+	pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
+	memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
+	pkt_data->ifindex = sdata->dev->ifindex;
+	pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT);
+
+	skb->priority = 20; /* use hardcoded priority for mgmt TX queue */
+	skb->dev = sdata->local->mdev;
+
+	/*
+	 * We're using the protocol field of the the frame control header
+	 * to request TX callback for hostapd. BIT(1) is checked.
+	 */
+	if ((fc & BIT(1)) == BIT(1)) {
+		pkt_data->req_tx_status = 1;
+		fc &= ~BIT(1);
+		hdr->frame_control = cpu_to_le16(fc);
+	}
+
+	pkt_data->do_not_encrypt = !(fc & IEEE80211_FCTL_PROTECTED);
+
+	sdata->stats.tx_packets++;
+	sdata->stats.tx_bytes += skb->len;
+
+	dev_queue_xmit(skb);
+
+	return 0;
+}
+
+/* helper functions for pending packets for when queues are stopped */
+
+void ieee80211_clear_tx_pending(struct ieee80211_local *local)
+{
+	int i, j;
+	struct ieee80211_tx_stored_packet *store;
+
+	for (i = 0; i < local->hw.queues; i++) {
+		if (!__ieee80211_queue_pending(local, i))
+			continue;
+		store = &local->pending_packet[i];
+		kfree_skb(store->skb);
+		for (j = 0; j < store->num_extra_frag; j++)
+			kfree_skb(store->extra_frag[j]);
+		kfree(store->extra_frag);
+		clear_bit(IEEE80211_LINK_STATE_PENDING, &local->state[i]);
+	}
+}
+
+void ieee80211_tx_pending(unsigned long data)
+{
+	struct ieee80211_local *local = (struct ieee80211_local *)data;
+	struct net_device *dev = local->mdev;
+	struct ieee80211_tx_stored_packet *store;
+	struct ieee80211_txrx_data tx;
+	int i, ret, reschedule = 0;
+
+	netif_tx_lock_bh(dev);
+	for (i = 0; i < local->hw.queues; i++) {
+		if (__ieee80211_queue_stopped(local, i))
+			continue;
+		if (!__ieee80211_queue_pending(local, i)) {
+			reschedule = 1;
+			continue;
+		}
+		store = &local->pending_packet[i];
+		tx.u.tx.control = &store->control;
+		tx.u.tx.extra_frag = store->extra_frag;
+		tx.u.tx.num_extra_frag = store->num_extra_frag;
+		tx.u.tx.last_frag_hwrate = store->last_frag_hwrate;
+		tx.u.tx.last_frag_rate = store->last_frag_rate;
+		tx.u.tx.probe_last_frag = store->last_frag_rate_ctrl_probe;
+		ret = __ieee80211_tx(local, store->skb, &tx);
+		if (ret) {
+			if (ret == IEEE80211_TX_FRAG_AGAIN)
+				store->skb = NULL;
+		} else {
+			clear_bit(IEEE80211_LINK_STATE_PENDING,
+				  &local->state[i]);
+			reschedule = 1;
+		}
+	}
+	netif_tx_unlock_bh(dev);
+	if (reschedule) {
+		if (!ieee80211_qdisc_installed(dev)) {
+			if (!__ieee80211_queue_stopped(local, 0))
+				netif_wake_queue(dev);
+		} else
+			netif_schedule(dev);
+	}
+}
+
+/* functions for drivers to get certain frames */
+
+static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
+				     struct ieee80211_if_ap *bss,
+				     struct sk_buff *skb)
+{
+	u8 *pos, *tim;
+	int aid0 = 0;
+	int i, have_bits = 0, n1, n2;
+
+	/* Generate bitmap for TIM only if there are any STAs in power save
+	 * mode. */
+	read_lock_bh(&local->sta_lock);
+	if (atomic_read(&bss->num_sta_ps) > 0)
+		/* in the hope that this is faster than
+		 * checking byte-for-byte */
+		have_bits = !bitmap_empty((unsigned long*)bss->tim,
+					  IEEE80211_MAX_AID+1);
+
+	if (bss->dtim_count == 0)
+		bss->dtim_count = bss->dtim_period - 1;
+	else
+		bss->dtim_count--;
+
+	tim = pos = (u8 *) skb_put(skb, 6);
+	*pos++ = WLAN_EID_TIM;
+	*pos++ = 4;
+	*pos++ = bss->dtim_count;
+	*pos++ = bss->dtim_period;
+
+	if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf))
+		aid0 = 1;
+
+	if (have_bits) {
+		/* Find largest even number N1 so that bits numbered 1 through
+		 * (N1 x 8) - 1 in the bitmap are 0 and number N2 so that bits
+		 * (N2 + 1) x 8 through 2007 are 0. */
+		n1 = 0;
+		for (i = 0; i < IEEE80211_MAX_TIM_LEN; i++) {
+			if (bss->tim[i]) {
+				n1 = i & 0xfe;
+				break;
+			}
+		}
+		n2 = n1;
+		for (i = IEEE80211_MAX_TIM_LEN - 1; i >= n1; i--) {
+			if (bss->tim[i]) {
+				n2 = i;
+				break;
+			}
+		}
+
+		/* Bitmap control */
+		*pos++ = n1 | aid0;
+		/* Part Virt Bitmap */
+		memcpy(pos, bss->tim + n1, n2 - n1 + 1);
+
+		tim[1] = n2 - n1 + 4;
+		skb_put(skb, n2 - n1);
+	} else {
+		*pos++ = aid0; /* Bitmap control */
+		*pos++ = 0; /* Part Virt Bitmap */
+	}
+	read_unlock_bh(&local->sta_lock);
+}
+
+struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, int if_id,
+				     struct ieee80211_tx_control *control)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+	struct sk_buff *skb;
+	struct net_device *bdev;
+	struct ieee80211_sub_if_data *sdata = NULL;
+	struct ieee80211_if_ap *ap = NULL;
+	struct ieee80211_rate *rate;
+	struct rate_control_extra extra;
+	u8 *b_head, *b_tail;
+	int bh_len, bt_len;
+
+	bdev = dev_get_by_index(if_id);
+	if (bdev) {
+		sdata = IEEE80211_DEV_TO_SUB_IF(bdev);
+		ap = &sdata->u.ap;
+		dev_put(bdev);
+	}
+
+	if (!ap || sdata->type != IEEE80211_IF_TYPE_AP ||
+	    !ap->beacon_head) {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+		if (net_ratelimit())
+			printk(KERN_DEBUG "no beacon data avail for idx=%d "
+			       "(%s)\n", if_id, bdev ? bdev->name : "N/A");
+#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
+		return NULL;
+	}
+
+	/* Assume we are generating the normal beacon locally */
+	b_head = ap->beacon_head;
+	b_tail = ap->beacon_tail;
+	bh_len = ap->beacon_head_len;
+	bt_len = ap->beacon_tail_len;
+
+	skb = dev_alloc_skb(local->tx_headroom +
+		bh_len + bt_len + 256 /* maximum TIM len */);
+	if (!skb)
+		return NULL;
+
+	skb_reserve(skb, local->tx_headroom);
+	memcpy(skb_put(skb, bh_len), b_head, bh_len);
+
+	ieee80211_include_sequence(sdata, (struct ieee80211_hdr *)skb->data);
+
+	ieee80211_beacon_add_tim(local, ap, skb);
+
+	if (b_tail) {
+		memcpy(skb_put(skb, bt_len), b_tail, bt_len);
+	}
+
+	if (control) {
+		memset(&extra, 0, sizeof(extra));
+		extra.mode = local->oper_hw_mode;
+
+		rate = rate_control_get_rate(local, local->mdev, skb, &extra);
+		if (!rate) {
+			if (net_ratelimit()) {
+				printk(KERN_DEBUG "%s: ieee80211_beacon_get: no rate "
+				       "found\n", local->mdev->name);
+			}
+			dev_kfree_skb(skb);
+			return NULL;
+		}
+
+		control->tx_rate = (sdata->short_preamble &&
+				    (rate->flags & IEEE80211_RATE_PREAMBLE2)) ?
+			rate->val2 : rate->val;
+		control->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
+		control->power_level = local->hw.conf.power_level;
+		control->flags |= IEEE80211_TXCTL_NO_ACK;
+		control->retry_limit = 1;
+		control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK;
+	}
+
+	ap->num_beacons++;
+	return skb;
+}
+EXPORT_SYMBOL(ieee80211_beacon_get);
+
+void ieee80211_rts_get(struct ieee80211_hw *hw, int if_id,
+		       const void *frame, size_t frame_len,
+		       const struct ieee80211_tx_control *frame_txctl,
+		       struct ieee80211_rts *rts)
+{
+	const struct ieee80211_hdr *hdr = frame;
+	u16 fctl;
+
+	fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS;
+	rts->frame_control = cpu_to_le16(fctl);
+	rts->duration = ieee80211_rts_duration(hw, if_id, frame_len, frame_txctl);
+	memcpy(rts->ra, hdr->addr1, sizeof(rts->ra));
+	memcpy(rts->ta, hdr->addr2, sizeof(rts->ta));
+}
+EXPORT_SYMBOL(ieee80211_rts_get);
+
+void ieee80211_ctstoself_get(struct ieee80211_hw *hw, int if_id,
+			     const void *frame, size_t frame_len,
+			     const struct ieee80211_tx_control *frame_txctl,
+			     struct ieee80211_cts *cts)
+{
+	const struct ieee80211_hdr *hdr = frame;
+	u16 fctl;
+
+	fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS;
+	cts->frame_control = cpu_to_le16(fctl);
+	cts->duration = ieee80211_ctstoself_duration(hw, if_id, frame_len, frame_txctl);
+	memcpy(cts->ra, hdr->addr1, sizeof(cts->ra));
+}
+EXPORT_SYMBOL(ieee80211_ctstoself_get);
+
+struct sk_buff *
+ieee80211_get_buffered_bc(struct ieee80211_hw *hw, int if_id,
+			  struct ieee80211_tx_control *control)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+	struct sk_buff *skb;
+	struct sta_info *sta;
+	ieee80211_tx_handler *handler;
+	struct ieee80211_txrx_data tx;
+	ieee80211_txrx_result res = TXRX_DROP;
+	struct net_device *bdev;
+	struct ieee80211_sub_if_data *sdata;
+	struct ieee80211_if_ap *bss = NULL;
+
+	bdev = dev_get_by_index(if_id);
+	if (bdev) {
+		sdata = IEEE80211_DEV_TO_SUB_IF(bdev);
+		bss = &sdata->u.ap;
+		dev_put(bdev);
+	}
+	if (!bss || sdata->type != IEEE80211_IF_TYPE_AP || !bss->beacon_head)
+		return NULL;
+
+	if (bss->dtim_count != 0)
+		return NULL; /* send buffered bc/mc only after DTIM beacon */
+	memset(control, 0, sizeof(*control));
+	while (1) {
+		skb = skb_dequeue(&bss->ps_bc_buf);
+		if (!skb)
+			return NULL;
+		local->total_ps_buffered--;
+
+		if (!skb_queue_empty(&bss->ps_bc_buf) && skb->len >= 2) {
+			struct ieee80211_hdr *hdr =
+				(struct ieee80211_hdr *) skb->data;
+			/* more buffered multicast/broadcast frames ==> set
+			 * MoreData flag in IEEE 802.11 header to inform PS
+			 * STAs */
+			hdr->frame_control |=
+				cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+		}
+
+		if (ieee80211_tx_prepare(&tx, skb, local->mdev, control) == 0)
+			break;
+		dev_kfree_skb_any(skb);
+	}
+	sta = tx.sta;
+	tx.u.tx.ps_buffered = 1;
+
+	for (handler = local->tx_handlers; *handler != NULL; handler++) {
+		res = (*handler)(&tx);
+		if (res == TXRX_DROP || res == TXRX_QUEUED)
+			break;
+	}
+	dev_put(tx.dev);
+	skb = tx.skb; /* handlers are allowed to change skb */
+
+	if (res == TXRX_DROP) {
+		I802_DEBUG_INC(local->tx_handlers_drop);
+		dev_kfree_skb(skb);
+		skb = NULL;
+	} else if (res == TXRX_QUEUED) {
+		I802_DEBUG_INC(local->tx_handlers_queued);
+		skb = NULL;
+	}
+
+	if (sta)
+		sta_info_put(sta);
+
+	return skb;
+}
+EXPORT_SYMBOL(ieee80211_get_buffered_bc);
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
new file mode 100644
index 0000000..091ac0d
--- /dev/null
+++ b/net/mac80211/util.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright 2002-2005, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
+ * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * utilities for mac80211
+ */
+
+#include <net/mac80211.h>
+#include <linux/netdevice.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+#include <linux/bitmap.h>
+#include <net/cfg80211.h>
+
+#include "ieee80211_i.h"
+#include "ieee80211_rate.h"
+#include "wme.h"
+
+/* privid for wiphys to determine whether they belong to us or not */
+void *mac80211_wiphy_privid = &mac80211_wiphy_privid;
+
+/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
+/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
+const unsigned char rfc1042_header[] =
+	{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
+const unsigned char bridge_tunnel_header[] =
+	{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
+
+/* No encapsulation header if EtherType < 0x600 (=length) */
+static const unsigned char eapol_header[] =
+	{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x88, 0x8e };
+
+
+static int rate_list_match(const int *rate_list, int rate)
+{
+	int i;
+
+	if (!rate_list)
+		return 0;
+
+	for (i = 0; rate_list[i] >= 0; i++)
+		if (rate_list[i] == rate)
+			return 1;
+
+	return 0;
+}
+
+void ieee80211_prepare_rates(struct ieee80211_local *local,
+			     struct ieee80211_hw_mode *mode)
+{
+	int i;
+
+	for (i = 0; i < mode->num_rates; i++) {
+		struct ieee80211_rate *rate = &mode->rates[i];
+
+		rate->flags &= ~(IEEE80211_RATE_SUPPORTED |
+				 IEEE80211_RATE_BASIC);
+
+		if (local->supp_rates[mode->mode]) {
+			if (!rate_list_match(local->supp_rates[mode->mode],
+					     rate->rate))
+				continue;
+		}
+
+		rate->flags |= IEEE80211_RATE_SUPPORTED;
+
+		/* Use configured basic rate set if it is available. If not,
+		 * use defaults that are sane for most cases. */
+		if (local->basic_rates[mode->mode]) {
+			if (rate_list_match(local->basic_rates[mode->mode],
+					    rate->rate))
+				rate->flags |= IEEE80211_RATE_BASIC;
+		} else switch (mode->mode) {
+		case MODE_IEEE80211A:
+			if (rate->rate == 60 || rate->rate == 120 ||
+			    rate->rate == 240)
+				rate->flags |= IEEE80211_RATE_BASIC;
+			break;
+		case MODE_IEEE80211B:
+			if (rate->rate == 10 || rate->rate == 20)
+				rate->flags |= IEEE80211_RATE_BASIC;
+			break;
+		case MODE_ATHEROS_TURBO:
+			if (rate->rate == 120 || rate->rate == 240 ||
+			    rate->rate == 480)
+				rate->flags |= IEEE80211_RATE_BASIC;
+			break;
+		case MODE_IEEE80211G:
+			if (rate->rate == 10 || rate->rate == 20 ||
+			    rate->rate == 55 || rate->rate == 110)
+				rate->flags |= IEEE80211_RATE_BASIC;
+			break;
+		}
+
+		/* Set ERP and MANDATORY flags based on phymode */
+		switch (mode->mode) {
+		case MODE_IEEE80211A:
+			if (rate->rate == 60 || rate->rate == 120 ||
+			    rate->rate == 240)
+				rate->flags |= IEEE80211_RATE_MANDATORY;
+			break;
+		case MODE_IEEE80211B:
+			if (rate->rate == 10)
+				rate->flags |= IEEE80211_RATE_MANDATORY;
+			break;
+		case MODE_ATHEROS_TURBO:
+			break;
+		case MODE_IEEE80211G:
+			if (rate->rate == 10 || rate->rate == 20 ||
+			    rate->rate == 55 || rate->rate == 110 ||
+			    rate->rate == 60 || rate->rate == 120 ||
+			    rate->rate == 240)
+				rate->flags |= IEEE80211_RATE_MANDATORY;
+			break;
+		}
+		if (ieee80211_is_erp_rate(mode->mode, rate->rate))
+			rate->flags |= IEEE80211_RATE_ERP;
+	}
+}
+
+u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len)
+{
+	u16 fc;
+
+	if (len < 24)
+		return NULL;
+
+	fc = le16_to_cpu(hdr->frame_control);
+
+	switch (fc & IEEE80211_FCTL_FTYPE) {
+	case IEEE80211_FTYPE_DATA:
+		switch (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
+		case IEEE80211_FCTL_TODS:
+			return hdr->addr1;
+		case (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
+			return NULL;
+		case IEEE80211_FCTL_FROMDS:
+			return hdr->addr2;
+		case 0:
+			return hdr->addr3;
+		}
+		break;
+	case IEEE80211_FTYPE_MGMT:
+		return hdr->addr3;
+	case IEEE80211_FTYPE_CTL:
+		if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)
+			return hdr->addr1;
+		else
+			return NULL;
+	}
+
+	return NULL;
+}
+
+int ieee80211_get_hdrlen(u16 fc)
+{
+	int hdrlen = 24;
+
+	switch (fc & IEEE80211_FCTL_FTYPE) {
+	case IEEE80211_FTYPE_DATA:
+		if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS))
+			hdrlen = 30; /* Addr4 */
+		/*
+		 * The QoS Control field is two bytes and its presence is
+		 * indicated by the IEEE80211_STYPE_QOS_DATA bit. Add 2 to
+		 * hdrlen if that bit is set.
+		 * This works by masking out the bit and shifting it to
+		 * bit position 1 so the result has the value 0 or 2.
+		 */
+		hdrlen += (fc & IEEE80211_STYPE_QOS_DATA)
+				>> (ilog2(IEEE80211_STYPE_QOS_DATA)-1);
+		break;
+	case IEEE80211_FTYPE_CTL:
+		/*
+		 * ACK and CTS are 10 bytes, all others 16. To see how
+		 * to get this condition consider
+		 *   subtype mask:   0b0000000011110000 (0x00F0)
+		 *   ACK subtype:    0b0000000011010000 (0x00D0)
+		 *   CTS subtype:    0b0000000011000000 (0x00C0)
+		 *   bits that matter:         ^^^      (0x00E0)
+		 *   value of those: 0b0000000011000000 (0x00C0)
+		 */
+		if ((fc & 0xE0) == 0xC0)
+			hdrlen = 10;
+		else
+			hdrlen = 16;
+		break;
+	}
+
+	return hdrlen;
+}
+EXPORT_SYMBOL(ieee80211_get_hdrlen);
+
+int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
+{
+	const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) skb->data;
+	int hdrlen;
+
+	if (unlikely(skb->len < 10))
+		return 0;
+	hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control));
+	if (unlikely(hdrlen > skb->len))
+		return 0;
+	return hdrlen;
+}
+EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
+
+int ieee80211_is_eapol(const struct sk_buff *skb)
+{
+	const struct ieee80211_hdr *hdr;
+	u16 fc;
+	int hdrlen;
+
+	if (unlikely(skb->len < 10))
+		return 0;
+
+	hdr = (const struct ieee80211_hdr *) skb->data;
+	fc = le16_to_cpu(hdr->frame_control);
+
+	if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
+		return 0;
+
+	hdrlen = ieee80211_get_hdrlen(fc);
+
+	if (unlikely(skb->len >= hdrlen + sizeof(eapol_header) &&
+		     memcmp(skb->data + hdrlen, eapol_header,
+			    sizeof(eapol_header)) == 0))
+		return 1;
+
+	return 0;
+}
+
+void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
+
+	hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+	if (tx->u.tx.extra_frag) {
+		struct ieee80211_hdr *fhdr;
+		int i;
+		for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
+			fhdr = (struct ieee80211_hdr *)
+				tx->u.tx.extra_frag[i]->data;
+			fhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+		}
+	}
+}
+
+int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
+			     int rate, int erp, int short_preamble)
+{
+	int dur;
+
+	/* calculate duration (in microseconds, rounded up to next higher
+	 * integer if it includes a fractional microsecond) to send frame of
+	 * len bytes (does not include FCS) at the given rate. Duration will
+	 * also include SIFS.
+	 *
+	 * rate is in 100 kbps, so divident is multiplied by 10 in the
+	 * DIV_ROUND_UP() operations.
+	 */
+
+	if (local->hw.conf.phymode == MODE_IEEE80211A || erp ||
+	    local->hw.conf.phymode == MODE_ATHEROS_TURBO) {
+		/*
+		 * OFDM:
+		 *
+		 * N_DBPS = DATARATE x 4
+		 * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS)
+		 *	(16 = SIGNAL time, 6 = tail bits)
+		 * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext
+		 *
+		 * T_SYM = 4 usec
+		 * 802.11a - 17.5.2: aSIFSTime = 16 usec
+		 * 802.11g - 19.8.4: aSIFSTime = 10 usec +
+		 *	signal ext = 6 usec
+		 */
+		/* FIX: Atheros Turbo may have different (shorter) duration? */
+		dur = 16; /* SIFS + signal ext */
+		dur += 16; /* 17.3.2.3: T_PREAMBLE = 16 usec */
+		dur += 4; /* 17.3.2.3: T_SIGNAL = 4 usec */
+		dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10,
+					4 * rate); /* T_SYM x N_SYM */
+	} else {
+		/*
+		 * 802.11b or 802.11g with 802.11b compatibility:
+		 * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime +
+		 * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0.
+		 *
+		 * 802.11 (DS): 15.3.3, 802.11b: 18.3.4
+		 * aSIFSTime = 10 usec
+		 * aPreambleLength = 144 usec or 72 usec with short preamble
+		 * aPLCPHeaderLength = 48 usec or 24 usec with short preamble
+		 */
+		dur = 10; /* aSIFSTime = 10 usec */
+		dur += short_preamble ? (72 + 24) : (144 + 48);
+
+		dur += DIV_ROUND_UP(8 * (len + 4) * 10, rate);
+	}
+
+	return dur;
+}
+
+/* Exported duration function for driver use */
+__le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, int if_id,
+					size_t frame_len, int rate)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+	struct net_device *bdev = dev_get_by_index(if_id);
+	struct ieee80211_sub_if_data *sdata;
+	u16 dur;
+	int erp;
+
+	if (unlikely(!bdev))
+		return 0;
+
+	sdata = IEEE80211_DEV_TO_SUB_IF(bdev);
+	erp = ieee80211_is_erp_rate(hw->conf.phymode, rate);
+	dur = ieee80211_frame_duration(local, frame_len, rate,
+				       erp, sdata->short_preamble);
+
+	dev_put(bdev);
+	return cpu_to_le16(dur);
+}
+EXPORT_SYMBOL(ieee80211_generic_frame_duration);
+
+__le16 ieee80211_rts_duration(struct ieee80211_hw *hw, int if_id,
+			      size_t frame_len,
+			      const struct ieee80211_tx_control *frame_txctl)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+	struct ieee80211_rate *rate;
+	struct net_device *bdev = dev_get_by_index(if_id);
+	struct ieee80211_sub_if_data *sdata;
+	int short_preamble;
+	int erp;
+	u16 dur;
+
+	if (unlikely(!bdev))
+		return 0;
+
+	sdata = IEEE80211_DEV_TO_SUB_IF(bdev);
+	short_preamble = sdata->short_preamble;
+
+	rate = frame_txctl->rts_rate;
+	erp = !!(rate->flags & IEEE80211_RATE_ERP);
+
+	/* CTS duration */
+	dur = ieee80211_frame_duration(local, 10, rate->rate,
+				       erp, short_preamble);
+	/* Data frame duration */
+	dur += ieee80211_frame_duration(local, frame_len, rate->rate,
+					erp, short_preamble);
+	/* ACK duration */
+	dur += ieee80211_frame_duration(local, 10, rate->rate,
+					erp, short_preamble);
+
+	dev_put(bdev);
+	return cpu_to_le16(dur);
+}
+EXPORT_SYMBOL(ieee80211_rts_duration);
+
+__le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, int if_id,
+				    size_t frame_len,
+				    const struct ieee80211_tx_control *frame_txctl)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+	struct ieee80211_rate *rate;
+	struct net_device *bdev = dev_get_by_index(if_id);
+	struct ieee80211_sub_if_data *sdata;
+	int short_preamble;
+	int erp;
+	u16 dur;
+
+	if (unlikely(!bdev))
+		return 0;
+
+	sdata = IEEE80211_DEV_TO_SUB_IF(bdev);
+	short_preamble = sdata->short_preamble;
+
+	rate = frame_txctl->rts_rate;
+	erp = !!(rate->flags & IEEE80211_RATE_ERP);
+
+	/* Data frame duration */
+	dur = ieee80211_frame_duration(local, frame_len, rate->rate,
+				       erp, short_preamble);
+	if (!(frame_txctl->flags & IEEE80211_TXCTL_NO_ACK)) {
+		/* ACK duration */
+		dur += ieee80211_frame_duration(local, 10, rate->rate,
+						erp, short_preamble);
+	}
+
+	dev_put(bdev);
+	return cpu_to_le16(dur);
+}
+EXPORT_SYMBOL(ieee80211_ctstoself_duration);
+
+struct ieee80211_rate *
+ieee80211_get_rate(struct ieee80211_local *local, int phymode, int hw_rate)
+{
+	struct ieee80211_hw_mode *mode;
+	int r;
+
+	list_for_each_entry(mode, &local->modes_list, list) {
+		if (mode->mode != phymode)
+			continue;
+		for (r = 0; r < mode->num_rates; r++) {
+			struct ieee80211_rate *rate = &mode->rates[r];
+			if (rate->val == hw_rate ||
+			    (rate->flags & IEEE80211_RATE_PREAMBLE2 &&
+			     rate->val2 == hw_rate))
+				return rate;
+		}
+	}
+
+	return NULL;
+}
+
+void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+
+	if (test_and_clear_bit(IEEE80211_LINK_STATE_XOFF,
+			       &local->state[queue])) {
+		if (test_bit(IEEE80211_LINK_STATE_PENDING,
+			     &local->state[queue]))
+			tasklet_schedule(&local->tx_pending_tasklet);
+		else
+			if (!ieee80211_qdisc_installed(local->mdev)) {
+				if (queue == 0)
+					netif_wake_queue(local->mdev);
+			} else
+				__netif_schedule(local->mdev);
+	}
+}
+EXPORT_SYMBOL(ieee80211_wake_queue);
+
+void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+
+	if (!ieee80211_qdisc_installed(local->mdev) && queue == 0)
+		netif_stop_queue(local->mdev);
+	set_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
+}
+EXPORT_SYMBOL(ieee80211_stop_queue);
+
+void ieee80211_start_queues(struct ieee80211_hw *hw)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+	int i;
+
+	for (i = 0; i < local->hw.queues; i++)
+		clear_bit(IEEE80211_LINK_STATE_XOFF, &local->state[i]);
+	if (!ieee80211_qdisc_installed(local->mdev))
+		netif_start_queue(local->mdev);
+}
+EXPORT_SYMBOL(ieee80211_start_queues);
+
+void ieee80211_stop_queues(struct ieee80211_hw *hw)
+{
+	int i;
+
+	for (i = 0; i < hw->queues; i++)
+		ieee80211_stop_queue(hw, i);
+}
+EXPORT_SYMBOL(ieee80211_stop_queues);
+
+void ieee80211_wake_queues(struct ieee80211_hw *hw)
+{
+	int i;
+
+	for (i = 0; i < hw->queues; i++)
+		ieee80211_wake_queue(hw, i);
+}
+EXPORT_SYMBOL(ieee80211_wake_queues);
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 89ce815..5550d35 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -18,70 +18,6 @@
 #include "ieee80211_i.h"
 #include "wme.h"
 
-static inline int WLAN_FC_IS_QOS_DATA(u16 fc)
-{
-	return (fc & 0x8C) == 0x88;
-}
-
-
-ieee80211_txrx_result
-ieee80211_rx_h_parse_qos(struct ieee80211_txrx_data *rx)
-{
-	u8 *data = rx->skb->data;
-	int tid;
-
-	/* does the frame have a qos control field? */
-	if (WLAN_FC_IS_QOS_DATA(rx->fc)) {
-		u8 *qc = data + ieee80211_get_hdrlen(rx->fc) - QOS_CONTROL_LEN;
-		/* frame has qos control */
-		tid = qc[0] & QOS_CONTROL_TID_MASK;
-	} else {
-		if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) {
-			/* Separate TID for management frames */
-			tid = NUM_RX_DATA_QUEUES - 1;
-		} else {
-			/* no qos control present */
-			tid = 0; /* 802.1d - Best Effort */
-		}
-	}
-#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
-	I802_DEBUG_INC(rx->local->wme_rx_queue[tid]);
-	if (rx->sta) {
-		I802_DEBUG_INC(rx->sta->wme_rx_queue[tid]);
-	}
-#endif /* CONFIG_MAC80211_DEBUG_COUNTERS */
-
-	rx->u.rx.queue = tid;
-	/* Set skb->priority to 1d tag if highest order bit of TID is not set.
-	 * For now, set skb->priority to 0 for other cases. */
-	rx->skb->priority = (tid > 7) ? 0 : tid;
-
-	return TXRX_CONTINUE;
-}
-
-
-ieee80211_txrx_result
-ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx)
-{
-	u16 fc = rx->fc;
-	u8 *data = rx->skb->data;
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) data;
-
-	if (!WLAN_FC_IS_QOS_DATA(fc))
-		return TXRX_CONTINUE;
-
-	/* remove the qos control field, update frame type and meta-data */
-	memmove(data + 2, data, ieee80211_get_hdrlen(fc) - 2);
-	hdr = (struct ieee80211_hdr *) skb_pull(rx->skb, 2);
-	/* change frame type to non QOS */
-	rx->fc = fc &= ~IEEE80211_STYPE_QOS_DATA;
-	hdr->frame_control = cpu_to_le16(fc);
-
-	return TXRX_CONTINUE;
-}
-
-
-#ifdef CONFIG_NET_SCHED
 /* maximum number of hardware queues we support. */
 #define TC_80211_MAX_QUEUES 8
 
@@ -675,4 +611,3 @@ void ieee80211_wme_unregister(void)
 {
 	unregister_qdisc(&wme_qdisc_ops);
 }
-#endif /* CONFIG_NET_SCHED */
diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h
index f0bff10..76c713a 100644
--- a/net/mac80211/wme.h
+++ b/net/mac80211/wme.h
@@ -24,11 +24,10 @@
 
 #define QOS_CONTROL_TAG1D_MASK 0x07
 
-ieee80211_txrx_result
-ieee80211_rx_h_parse_qos(struct ieee80211_txrx_data *rx);
-
-ieee80211_txrx_result
-ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx);
+static inline int WLAN_FC_IS_QOS_DATA(u16 fc)
+{
+	return (fc & 0x8C) == 0x88;
+}
 
 #ifdef CONFIG_NET_SCHED
 void ieee80211_install_qdisc(struct net_device *dev);
diff --git a/net/wireless/wext.c b/net/wireless/wext.c
index d6aaf65..debf519 100644
--- a/net/wireless/wext.c
+++ b/net/wireless/wext.c
@@ -1129,10 +1129,12 @@ static int rtnetlink_fill_iwinfo(struct sk_buff *skb, struct net_device *dev,
 {
 	struct ifinfomsg *r;
 	struct nlmsghdr  *nlh;
-	unsigned char	 *b = skb_tail_pointer(skb);
 
-	nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r));
-	r = NLMSG_DATA(nlh);
+	nlh = nlmsg_put(skb, 0, 0, type, sizeof(*r), 0);
+	if (nlh == NULL)
+		return -EMSGSIZE;
+
+	r = nlmsg_data(nlh);
 	r->ifi_family = AF_UNSPEC;
 	r->__ifi_pad = 0;
 	r->ifi_type = dev->type;
@@ -1141,15 +1143,13 @@ static int rtnetlink_fill_iwinfo(struct sk_buff *skb, struct net_device *dev,
 	r->ifi_change = 0;	/* Wireless changes don't affect those flags */
 
 	/* Add the wireless events in the netlink packet */
-	RTA_PUT(skb, IFLA_WIRELESS, event_len, event);
+	NLA_PUT(skb, IFLA_WIRELESS, event_len, event);
 
-	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
-	return skb->len;
+	return nlmsg_end(skb, nlh);
 
-nlmsg_failure:
-rtattr_failure:
-	nlmsg_trim(skb, b);
-	return -1;
+nla_put_failure:
+	nlmsg_cancel(skb, nlh);
+	return -EMSGSIZE;
 }
 
 /* ---------------------------------------------------------------- */
@@ -1162,17 +1162,19 @@ rtattr_failure:
 static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len)
 {
 	struct sk_buff *skb;
-	int size = NLMSG_GOODSIZE;
+	int err;
 
-	skb = alloc_skb(size, GFP_ATOMIC);
+	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
 	if (!skb)
 		return;
 
-	if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK,
-				  event, event_len) < 0) {
+	err = rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK, event, event_len);
+	if (err < 0) {
+		WARN_ON(err == -EMSGSIZE);
 		kfree_skb(skb);
 		return;
 	}
+
 	NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
 	skb_queue_tail(&wireless_nlevent_queue, skb);
 	tasklet_schedule(&wireless_nlevent_tasklet);
-- 
John W. Linville
linville@tuxdriver.com

  reply	other threads:[~2007-08-06 21:36 UTC|newest]

Thread overview: 63+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-08-06 20:13 Please pull 'fixes-davem' branch of wireless-2.6 John W. Linville
2007-08-06 20:13 ` John W. Linville
2007-08-06 21:01 ` John W. Linville [this message]
2007-08-09  9:00   ` Please pull 'upstream-davem' " David Miller
2007-08-08  1:08 ` Please pull 'fixes-davem' " David Miller
  -- strict thread matches above, loose matches on Subject: below --
2008-01-08 19:29 Please pull 'upstream-davem' " John W. Linville
2008-01-09  7:36 ` David Miller
2008-01-09  7:36   ` David Miller
2007-12-20 15:53 John W. Linville
2007-12-20 22:38 ` John W. Linville
2007-12-21  4:04   ` David Miller
2007-12-21  4:04     ` David Miller
2007-12-17 20:55 John W. Linville
2007-12-18  6:57 ` David Miller
2007-12-18  6:57   ` David Miller
2007-12-01  2:23 John W. Linville
2007-12-01  2:23 ` John W. Linville
2007-12-01 13:54 ` Herbert Xu
2007-12-01 13:54   ` Herbert Xu
2007-11-20 22:11 John W. Linville
2007-11-21  1:27 ` David Miller
2007-11-21  1:27   ` David Miller
2007-11-07  3:08 John W. Linville
2007-11-20  5:24 ` David Miller
2007-10-10  0:21 John W. Linville
2007-10-10  1:13 ` David Miller
2007-10-10  1:13   ` David Miller
2007-10-03  1:25 John W. Linville
2007-10-03  1:45 ` John W. Linville
2007-10-03  1:45   ` John W. Linville
2007-10-03  2:01 ` David Miller
2007-10-03  2:01   ` David Miller
2007-10-03 14:10   ` John W. Linville
2007-10-03 14:10     ` John W. Linville
2007-10-03 21:52     ` David Miller
2007-10-03 21:52       ` David Miller
2007-09-15 13:20 John W. Linville
2007-09-15 13:20 ` John W. Linville
2007-08-28 22:50 John W. Linville
2007-08-29  0:21 ` David Miller
2007-08-15  0:32 Please pull 'fixes-davem' " John W. Linville
2007-08-15  0:34 ` Please pull 'upstream-davem' " John W. Linville
2007-08-15  0:34   ` John W. Linville
2007-08-15 23:09   ` David Miller
2007-08-15 23:09     ` David Miller
2007-08-19 23:32   ` David Miller
2007-08-19 23:32     ` David Miller
2007-07-18  2:16 John W. Linville
2007-07-18  2:16 ` John W. Linville
2007-07-18  3:17 ` David Miller
2007-07-18 15:34   ` John W. Linville
2007-07-18 15:34     ` John W. Linville
2007-07-18 22:32     ` David Miller
2007-07-18 22:32       ` David Miller
2007-07-19  0:45       ` John W. Linville
2007-07-19  1:21         ` David Miller
2007-07-12 20:41 John W. Linville
2007-07-12 20:41 ` John W. Linville
2007-07-15  1:59 ` David Miller
2007-07-15  1:59   ` David Miller
2007-07-15  3:41   ` David Miller
2007-07-15  3:41     ` David Miller
2007-07-16 16:37 ` Andy Green
2007-07-16 16:49   ` Jiri Benc

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20070806210131.GM6442@tuxdriver.com \
    --to=linville@tuxdriver.com \
    --cc=davem@davemloft.net \
    --cc=linux-wireless@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.