[MAC80211]: split RX handlers into own file
authorJohannes Berg <johannes@sipsolutions.net>
Fri, 27 Jul 2007 13:43:22 +0000 (15:43 +0200)
committerDavid S. Miller <davem@sunset.davemloft.net>
Wed, 10 Oct 2007 23:47:29 +0000 (16:47 -0700)
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Jiri Benc <jbenc@suse.cz>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
include/net/ieee80211_radiotap.h
net/mac80211/Makefile
net/mac80211/ieee80211.c
net/mac80211/ieee80211_i.h
net/mac80211/rx.c [new file with mode: 0644]

index a0c2b41a24d7d2174cd7ca9d7f8c98d8ca1ea56c..22e047758ba12d12fe71853495aa568cd38e6397 100644 (file)
@@ -255,4 +255,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(hdr->it_len);
+}
+
 #endif                         /* IEEE80211_RADIOTAP_H */
index a9c2d0787d4aee8feafd7e935e06514ce469bb55..b5853991324b85662947fe2ba55b64568cc967d5 100644 (file)
@@ -18,4 +18,5 @@ mac80211-objs := \
        aes_ccm.o \
        wme.o \
        ieee80211_cfg.o \
+       rx.o \
        $(mac80211-objs-y)
index ff2172ffd8611758ba00efa2abc349892ca11015..c0094209bed96af9cc34561ef9c7da412c10202c 100644 (file)
@@ -45,11 +45,11 @@ 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[] =
+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[] =
+const unsigned char bridge_tunnel_header[] =
        { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
 
 /* No encapsulation header if EtherType < 0x600 (=length) */
@@ -222,9 +222,9 @@ void ieee80211_prepare_rates(struct ieee80211_local *local,
 }
 
 
-static void ieee80211_key_threshold_notify(struct net_device *dev,
-                                          struct ieee80211_key *key,
-                                          struct sta_info *sta)
+void ieee80211_key_threshold_notify(struct net_device *dev,
+                                   struct ieee80211_key *key,
+                                   struct sta_info *sta)
 {
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct sk_buff *skb;
@@ -258,7 +258,7 @@ static void ieee80211_key_threshold_notify(struct net_device *dev,
 }
 
 
-static u8 * ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len)
+u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len)
 {
        u16 fc;
 
@@ -345,13 +345,6 @@ int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
 }
 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,
@@ -392,7 +385,7 @@ static inline void ieee80211_dump_frame(const char *ifname, const char *title,
 #endif /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */
 
 
-static int ieee80211_is_eapol(const struct sk_buff *skb)
+int ieee80211_is_eapol(const struct sk_buff *skb)
 {
        const struct ieee80211_hdr *hdr;
        u16 fc;
@@ -2762,188 +2755,7 @@ static int header_parse_80211(struct sk_buff *skb, unsigned char *haddr)
        return ETH_ALEN;
 }
 
-static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
-{
-       return compare_ether_addr(raddr, addr) == 0 ||
-              is_broadcast_ether_addr(raddr);
-}
-
-
-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 struct ieee80211_rate *
+struct ieee80211_rate *
 ieee80211_get_rate(struct ieee80211_local *local, int phymode, int hw_rate)
 {
        struct ieee80211_hw_mode *mode;
@@ -3071,69 +2883,6 @@ ieee80211_rx_mgmt(struct ieee80211_local *local, struct sk_buff *skb,
        netif_rx(skb);
 }
 
-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);
-}
-
 int ieee80211_radar_status(struct ieee80211_hw *hw, int channel,
                           int radar, int radar_type)
 {
@@ -3163,1000 +2912,6 @@ int ieee80211_radar_status(struct ieee80211_hw *hw, int channel,
 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;
-
-       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 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 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_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 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 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;
-}
-
-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);
-               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;
-}
-
-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);
-}
-
-/*
- * 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 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)
 {
@@ -4215,65 +2970,6 @@ ieee80211_tx_h_load_stats(struct ieee80211_txrx_data *tx)
 }
 
 
-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;
-       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;
-}
-
 static void ieee80211_stat_refresh(unsigned long data)
 {
        struct ieee80211_local *local = (struct ieee80211_local *) data;
@@ -4311,24 +3007,6 @@ static void ieee80211_stat_refresh(unsigned long data)
 }
 
 
-/* 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)
@@ -4690,41 +3368,6 @@ 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,
index 6f7bae7ef9c02d54cbded6ced59745e856260295..35e2ce52faebdfab53cb6bfc19a011f60e50a92e 100644 (file)
@@ -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"
@@ -707,6 +708,13 @@ 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);
@@ -730,6 +738,16 @@ 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);
+u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len);
+int ieee80211_is_eapol(const struct sk_buff *skb);
+
+extern const unsigned char rfc1042_header[];
+extern const unsigned char bridge_tunnel_header[];
 
 /* ieee80211_ioctl.c */
 extern const struct iw_handler_def ieee80211_iw_handler_def;
@@ -801,6 +819,10 @@ void ieee80211_if_del_mgmt(struct ieee80211_local *local);
 void ieee80211_regdomain_init(void);
 void ieee80211_set_default_regdomain(struct ieee80211_hw_mode *mode);
 
+/* rx handling */
+extern ieee80211_rx_handler ieee80211_rx_pre_handlers[];
+extern ieee80211_rx_handler ieee80211_rx_handlers[];
+
 /* for wiphy privid */
 extern void *mac80211_wiphy_privid;
 
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
new file mode 100644 (file)
index 0000000..8c0d14c
--- /dev/null
@@ -0,0 +1,1360 @@
+/*
+ * 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
+ */
+
+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;
+       if (rx->sta)
+               rx->sta->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)
+{
+       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;
+       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;
+
+       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_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);
+               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;
+}
+
+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_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 */
+
+/*
+ * 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 multicast;
+       int radiotap_len = 0;
+
+       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++;
+       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);
+
+/* 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);