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 <ron.rindjunsky@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Ron Rindjunsky 2007-11-26 16:14:33 +02:00 committed by David S. Miller
parent 9f985b0eee
commit fd4c7f2fce
2 changed files with 125 additions and 0 deletions

View File

@ -155,6 +155,7 @@ struct ieee80211_txrx_data {
int load;
u32 tkip_iv32;
u16 tkip_iv16;
u8 amsdu_frame;
} rx;
} u;
};

View File

@ -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 */
@ -1196,6 +1200,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)
{
@ -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