return 1;
if (unlikely(skb->len < 16 + present_fcs_len + radiotap_len))
return 1;
- if ((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_FTYPE)) ==
- cpu_to_le16(IEEE80211_FTYPE_CTL))
+ if (((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_FTYPE)) ==
+ cpu_to_le16(IEEE80211_FTYPE_CTL)) &&
+ ((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE)) !=
+ cpu_to_le16(IEEE80211_STYPE_PSPOLL)))
return 1;
return 0;
}
struct ieee80211_sub_if_data *sdata;
struct ieee80211_rate *rate;
int needed_headroom = 0;
- struct ieee80211_rtap_hdr {
- struct ieee80211_radiotap_header hdr;
+ struct ieee80211_radiotap_header *rthdr;
+ __le64 *rttsft = NULL;
+ struct ieee80211_rtap_fixed_data {
u8 flags;
u8 rate;
__le16 chan_freq;
u8 antsignal;
u8 padding_for_rxflags;
__le16 rx_flags;
- } __attribute__ ((packed)) *rthdr;
+ } __attribute__ ((packed)) *rtfixed;
struct sk_buff *skb, *skb2;
struct net_device *prev_dev = NULL;
int present_fcs_len = 0;
if (status->flag & RX_FLAG_RADIOTAP)
rtap_len = ieee80211_get_radiotap_len(origskb->data);
else
- needed_headroom = sizeof(*rthdr);
+ /* room for radiotap header, always present fields and TSFT */
+ needed_headroom = sizeof(*rthdr) + sizeof(*rtfixed) + 8;
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
present_fcs_len = FCS_LEN;
* them allocate enough headroom to start with.
*/
if (skb_headroom(skb) < needed_headroom &&
- pskb_expand_head(skb, sizeof(*rthdr), 0, GFP_ATOMIC)) {
+ pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC)) {
dev_kfree_skb(skb);
return NULL;
}
/* if necessary, prepend radiotap information */
if (!(status->flag & RX_FLAG_RADIOTAP)) {
+ rtfixed = (void *) skb_push(skb, sizeof(*rtfixed));
+ rtap_len = sizeof(*rthdr) + sizeof(*rtfixed);
+ if (status->flag & RX_FLAG_TSFT) {
+ rttsft = (void *) skb_push(skb, sizeof(*rttsft));
+ rtap_len += 8;
+ }
rthdr = (void *) skb_push(skb, sizeof(*rthdr));
memset(rthdr, 0, sizeof(*rthdr));
- rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr));
- rthdr->hdr.it_present =
+ memset(rtfixed, 0, sizeof(*rtfixed));
+ rthdr->it_present =
cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
(1 << IEEE80211_RADIOTAP_RATE) |
(1 << IEEE80211_RADIOTAP_CHANNEL) |
(1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |
(1 << IEEE80211_RADIOTAP_RX_FLAGS));
- rthdr->flags = local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS ?
- IEEE80211_RADIOTAP_F_FCS : 0;
+ rtfixed->flags = 0;
+ if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
+ rtfixed->flags |= IEEE80211_RADIOTAP_F_FCS;
+
+ if (rttsft) {
+ *rttsft = cpu_to_le64(status->mactime);
+ rthdr->it_present |=
+ cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
+ }
/* FIXME: when radiotap gets a 'bad PLCP' flag use it here */
- rthdr->rx_flags = 0;
+ rtfixed->rx_flags = 0;
if (status->flag &
(RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
- rthdr->rx_flags |=
+ rtfixed->rx_flags |=
cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADFCS);
rate = ieee80211_get_rate(local, status->phymode,
status->rate);
if (rate)
- rthdr->rate = rate->rate / 5;
+ rtfixed->rate = rate->rate / 5;
- rthdr->chan_freq = cpu_to_le16(status->freq);
+ rtfixed->chan_freq = cpu_to_le16(status->freq);
if (status->phymode == MODE_IEEE80211A)
- rthdr->chan_flags =
+ rtfixed->chan_flags =
cpu_to_le16(IEEE80211_CHAN_OFDM |
IEEE80211_CHAN_5GHZ);
else
- rthdr->chan_flags =
+ rtfixed->chan_flags =
cpu_to_le16(IEEE80211_CHAN_DYN |
IEEE80211_CHAN_2GHZ);
- rthdr->antsignal = status->ssi;
+ rtfixed->antsignal = status->ssi;
+ rthdr->it_len = cpu_to_le16(rtap_len);
}
skb_set_mac_header(skb, 0);
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->flags |= IEEE80211_TXRXD_RX_AMSDU;
+ else
+ rx->flags &= ~IEEE80211_TXRXD_RX_AMSDU;
} else {
if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) {
/* Separate TID for management frames */
static ieee80211_txrx_result
ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx)
{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
struct sk_buff *skb;
int no_pending_pkts;
DECLARE_MAC_BUF(mac);
!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH)))
return TXRX_CONTINUE;
+ if ((sdata->type != IEEE80211_IF_TYPE_AP) &&
+ (sdata->type != IEEE80211_IF_TYPE_VLAN))
+ return TXRX_DROP;
+
skb = skb_dequeue(&rx->sta->tx_filtered);
if (!skb) {
skb = skb_dequeue(&rx->sta->ps_tx_buf);
if (xmit_skb) {
/* send to wireless media */
- xmit_skb->protocol = __constant_htons(ETH_P_802_3);
+ xmit_skb->protocol = htons(ETH_P_802_3);
skb_set_network_header(xmit_skb, 0);
skb_set_mac_header(xmit_skb, 0);
dev_queue_xmit(xmit_skb);
}
}
+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->flags & IEEE80211_TXRXD_RX_AMSDU))
+ 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)
{
* 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