Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=fd4c7f2fce1737105208c564e1458c885918982d
Commit:     fd4c7f2fce1737105208c564e1458c885918982d
Parent:     9f985b0eee4070e494b9d62313da982cfef9b5e3
Author:     Ron Rindjunsky <[EMAIL PROTECTED]>
AuthorDate: Mon Nov 26 16:14:33 2007 +0200
Committer:  David S. Miller <[EMAIL PROTECTED]>
CommitDate: Mon Jan 28 14:55:32 2008 -0800

    mac80211: adding 802.11n essential A-MSDU Rx capability
    
    This patch adds the ability to receive and handle A-MSDU frames.
    
    Signed-off-by: Ron Rindjunsky <[EMAIL PROTECTED]>
    Signed-off-by: John W. Linville <[EMAIL PROTECTED]>
    Signed-off-by: David S. Miller <[EMAIL PROTECTED]>
---
 net/mac80211/ieee80211_i.h |    1 +
 net/mac80211/rx.c          |  124 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 125 insertions(+), 0 deletions(-)

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 982f996..66b6cf3 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -155,6 +155,7 @@ struct ieee80211_txrx_data {
                        int load;
                        u32 tkip_iv32;
                        u16 tkip_iv16;
+                       u8  amsdu_frame;
                } rx;
        } u;
 };
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 478d855..4525d73 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -243,6 +243,10 @@ ieee80211_rx_h_parse_qos(struct ieee80211_txrx_data *rx)
                u8 *qc = data + ieee80211_get_hdrlen(rx->fc) - QOS_CONTROL_LEN;
                /* frame has qos control */
                tid = qc[0] & QOS_CONTROL_TID_MASK;
+               if (qc[0] & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT)
+                       rx->u.rx.amsdu_frame = 1;
+               else
+                       rx->u.rx.amsdu_frame = 0;
        } else {
                if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == 
IEEE80211_FTYPE_MGMT)) {
                        /* Separate TID for management frames */
@@ -1197,6 +1201,125 @@ ieee80211_deliver_skb(struct ieee80211_txrx_data *rx)
 }
 
 static ieee80211_txrx_result
+ieee80211_rx_h_amsdu(struct ieee80211_txrx_data *rx)
+{
+       struct net_device *dev = rx->dev;
+       struct ieee80211_local *local = rx->local;
+       u16 fc, ethertype;
+       u8 *payload;
+       struct sk_buff *skb = rx->skb, *frame = NULL;
+       const struct ethhdr *eth;
+       int remaining, err;
+       u8 dst[ETH_ALEN];
+       u8 src[ETH_ALEN];
+       DECLARE_MAC_BUF(mac);
+
+       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;
+
+       if (!rx->u.rx.amsdu_frame)
+               return TXRX_CONTINUE;
+
+       err = ieee80211_data_to_8023(rx);
+       if (unlikely(err))
+               return TXRX_DROP;
+
+       skb->dev = dev;
+
+       dev->stats.rx_packets++;
+       dev->stats.rx_bytes += skb->len;
+
+       /* skip the wrapping header */
+       eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr));
+       if (!eth)
+               return TXRX_DROP;
+
+       while (skb != frame) {
+               u8 padding;
+               __be16 len = eth->h_proto;
+               unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len);
+
+               remaining = skb->len;
+               memcpy(dst, eth->h_dest, ETH_ALEN);
+               memcpy(src, eth->h_source, ETH_ALEN);
+
+               padding = ((4 - subframe_len) & 0x3);
+               /* the last MSDU has no padding */
+               if (subframe_len > remaining) {
+                       printk(KERN_DEBUG "%s: wrong buffer size", dev->name);
+                       return TXRX_DROP;
+               }
+
+               skb_pull(skb, sizeof(struct ethhdr));
+               /* if last subframe reuse skb */
+               if (remaining <= subframe_len + padding)
+                       frame = skb;
+               else {
+                       frame = dev_alloc_skb(local->hw.extra_tx_headroom +
+                                             subframe_len);
+
+                       if (frame == NULL)
+                               return TXRX_DROP;
+
+                       skb_reserve(frame, local->hw.extra_tx_headroom +
+                                   sizeof(struct ethhdr));
+                       memcpy(skb_put(frame, ntohs(len)), skb->data,
+                               ntohs(len));
+
+                       eth = (struct ethhdr *) skb_pull(skb, ntohs(len) +
+                                                       padding);
+                       if (!eth) {
+                               printk(KERN_DEBUG "%s: wrong buffer size ",
+                                      dev->name);
+                               dev_kfree_skb(frame);
+                               return TXRX_DROP;
+                       }
+               }
+
+               skb_set_network_header(frame, 0);
+               frame->dev = dev;
+               frame->priority = skb->priority;
+               rx->skb = frame;
+
+               if ((ieee80211_drop_802_1x_pae(rx, 0)) ||
+                   (ieee80211_drop_unencrypted(rx, 0))) {
+                       if (skb == frame) /* last frame */
+                               return TXRX_DROP;
+                       dev_kfree_skb(frame);
+                       continue;
+               }
+
+               payload = frame->data;
+               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(frame, 6);
+                       memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
+                       memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
+               } else {
+                       memcpy(skb_push(frame, sizeof(__be16)), &len,
+                               sizeof(__be16));
+                       memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
+                       memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
+               }
+
+
+               ieee80211_deliver_skb(rx);
+       }
+
+       return TXRX_QUEUED;
+}
+
+static ieee80211_txrx_result
 ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
 {
        struct net_device *dev = rx->dev;
@@ -1379,6 +1502,7 @@ ieee80211_rx_handler ieee80211_rx_handlers[] =
         * are not passed to user space by these functions
         */
        ieee80211_rx_h_remove_qos_control,
+       ieee80211_rx_h_amsdu,
        ieee80211_rx_h_data,
        ieee80211_rx_h_mgmt,
        NULL
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to