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:
committed by
David S. Miller
parent
9f985b0eee
commit
fd4c7f2fce
@@ -155,6 +155,7 @@ struct ieee80211_txrx_data {
|
|||||||
int load;
|
int load;
|
||||||
u32 tkip_iv32;
|
u32 tkip_iv32;
|
||||||
u16 tkip_iv16;
|
u16 tkip_iv16;
|
||||||
|
u8 amsdu_frame;
|
||||||
} rx;
|
} rx;
|
||||||
} u;
|
} u;
|
||||||
};
|
};
|
||||||
|
@@ -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;
|
u8 *qc = data + ieee80211_get_hdrlen(rx->fc) - QOS_CONTROL_LEN;
|
||||||
/* frame has qos control */
|
/* frame has qos control */
|
||||||
tid = qc[0] & QOS_CONTROL_TID_MASK;
|
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 {
|
} else {
|
||||||
if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) {
|
if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) {
|
||||||
/* Separate TID for management frames */
|
/* 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
|
static ieee80211_txrx_result
|
||||||
ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
|
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
|
* are not passed to user space by these functions
|
||||||
*/
|
*/
|
||||||
ieee80211_rx_h_remove_qos_control,
|
ieee80211_rx_h_remove_qos_control,
|
||||||
|
ieee80211_rx_h_amsdu,
|
||||||
ieee80211_rx_h_data,
|
ieee80211_rx_h_data,
|
||||||
ieee80211_rx_h_mgmt,
|
ieee80211_rx_h_mgmt,
|
||||||
NULL
|
NULL
|
||||||
|
Reference in New Issue
Block a user