mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 08:19:28 +00:00
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:
parent
9f985b0eee
commit
fd4c7f2fce
2 changed files with 125 additions and 0 deletions
|
@ -155,6 +155,7 @@ struct ieee80211_txrx_data {
|
|||
int load;
|
||||
u32 tkip_iv32;
|
||||
u16 tkip_iv16;
|
||||
u8 amsdu_frame;
|
||||
} rx;
|
||||
} 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;
|
||||
/* 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
|
||||
|
|
Loading…
Reference in a new issue