]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - drivers/net/wireless/ath/carl9170/rx.c
carl9170: A-MPDU frame type filter
[net-next-2.6.git] / drivers / net / wireless / ath / carl9170 / rx.c
index 671dbc429547d27b05ae07459fe02dbeae21e887..939a0e96ed1f512f8ce22cacfd8b0c49a4633ed1 100644 (file)
@@ -576,6 +576,41 @@ static void carl9170_ps_beacon(struct ar9170 *ar, void *data, unsigned int len)
        }
 }
 
+static bool carl9170_ampdu_check(struct ar9170 *ar, u8 *buf, u8 ms)
+{
+       __le16 fc;
+
+       if ((ms & AR9170_RX_STATUS_MPDU) == AR9170_RX_STATUS_MPDU_SINGLE) {
+               /*
+                * This frame is not part of an aMPDU.
+                * Therefore it is not subjected to any
+                * of the following content restrictions.
+                */
+               return true;
+       }
+
+       /*
+        * "802.11n - 7.4a.3 A-MPDU contents" describes in which contexts
+        * certain frame types can be part of an aMPDU.
+        *
+        * In order to keep the processing cost down, I opted for a
+        * stateless filter solely based on the frame control field.
+        */
+
+       fc = ((struct ieee80211_hdr *)buf)->frame_control;
+       if (ieee80211_is_data_qos(fc) && ieee80211_is_data_present(fc))
+               return true;
+
+       if (ieee80211_is_ack(fc) || ieee80211_is_back(fc) ||
+           ieee80211_is_back_req(fc))
+               return true;
+
+       if (ieee80211_is_action(fc))
+               return true;
+
+       return false;
+}
+
 /*
  * If the frame alignment is right (or the kernel has
  * CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS), and there
@@ -594,24 +629,19 @@ static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len)
        struct ieee80211_rx_status status;
        struct sk_buff *skb;
        int mpdu_len;
+       u8 mac_status;
 
        if (!IS_STARTED(ar))
                return;
 
-       if (unlikely(len < sizeof(*mac))) {
-               ar->rx_dropped++;
-               return;
-       }
+       if (unlikely(len < sizeof(*mac)))
+               goto drop;
 
        mpdu_len = len - sizeof(*mac);
 
        mac = (void *)(buf + mpdu_len);
-       if (unlikely(mac->error & AR9170_RX_ERROR_FATAL)) {
-               ar->rx_dropped++;
-               return;
-       }
-
-       switch (mac->status & AR9170_RX_STATUS_MPDU) {
+       mac_status = mac->status;
+       switch (mac_status & AR9170_RX_STATUS_MPDU) {
        case AR9170_RX_STATUS_MPDU_FIRST:
                /* Aggregated MPDUs start with an PLCP header */
                if (likely(mpdu_len >= sizeof(struct ar9170_rx_head))) {
@@ -638,8 +668,7 @@ static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len)
                                        "is clipped.\n");
                        }
 
-                       ar->rx_dropped++;
-                       return;
+                       goto drop;
                }
                break;
 
@@ -659,8 +688,7 @@ static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len)
                                        "is clipped.\n");
                        }
 
-                       ar->rx_dropped++;
-                       return;
+                       goto drop;
                }
 
        case AR9170_RX_STATUS_MPDU_MIDDLE:
@@ -672,8 +700,7 @@ static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len)
                        wiphy_err(ar->hw->wiphy, "rx stream does not start "
                                        "with a first_mpdu frame tag.\n");
 
-                       ar->rx_dropped++;
-                       return;
+                       goto drop;
                }
 
                head = &ar->rx_plcp;
@@ -696,16 +723,15 @@ static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len)
        }
 
        /* FC + DU + RA + FCS */
-       if (unlikely(mpdu_len < (2 + 2 + 6 + FCS_LEN))) {
-               ar->rx_dropped++;
-               return;
-       }
+       if (unlikely(mpdu_len < (2 + 2 + ETH_ALEN + FCS_LEN)))
+               goto drop;
 
        memset(&status, 0, sizeof(status));
-       if (unlikely(carl9170_rx_mac_status(ar, head, mac, &status))) {
-               ar->rx_dropped++;
-               return;
-       }
+       if (unlikely(carl9170_rx_mac_status(ar, head, mac, &status)))
+               goto drop;
+
+       if (!carl9170_ampdu_check(ar, buf, mac_status))
+               goto drop;
 
        if (phy)
                carl9170_rx_phy_status(ar, phy, &status);
@@ -713,12 +739,15 @@ static void carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len)
        carl9170_ps_beacon(ar, buf, mpdu_len);
 
        skb = carl9170_rx_copy_data(buf, mpdu_len);
-       if (likely(skb)) {
-               memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
-               ieee80211_rx(ar->hw, skb);
-       } else {
-               ar->rx_dropped++;
-       }
+       if (!skb)
+               goto drop;
+
+       memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
+       ieee80211_rx(ar->hw, skb);
+       return;
+
+drop:
+       ar->rx_dropped++;
 }
 
 static void carl9170_rx_untie_cmds(struct ar9170 *ar, const u8 *respbuf,