summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath/carl9170
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath/carl9170')
-rw-r--r--drivers/net/wireless/ath/carl9170/rx.c89
-rw-r--r--drivers/net/wireless/ath/carl9170/wlan.h14
2 files changed, 70 insertions, 33 deletions
diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
index 671dbc4..939a0e9 100644
--- a/drivers/net/wireless/ath/carl9170/rx.c
+++ b/drivers/net/wireless/ath/carl9170/rx.c
@@ -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,
diff --git a/drivers/net/wireless/ath/carl9170/wlan.h b/drivers/net/wireless/ath/carl9170/wlan.h
index 48ead22..24d63b5 100644
--- a/drivers/net/wireless/ath/carl9170/wlan.h
+++ b/drivers/net/wireless/ath/carl9170/wlan.h
@@ -74,6 +74,9 @@
#define AR9170_RX_STATUS_MPDU_MIDDLE 0x30
#define AR9170_RX_STATUS_MPDU_LAST 0x10
+#define AR9170_RX_STATUS_CONT_AGGR 0x40
+#define AR9170_RX_STATUS_TOTAL_ERROR 0x80
+
#define AR9170_RX_ERROR_RXTO 0x01
#define AR9170_RX_ERROR_OVERRUN 0x02
#define AR9170_RX_ERROR_DECRYPT 0x04
@@ -81,7 +84,6 @@
#define AR9170_RX_ERROR_WRONG_RA 0x10
#define AR9170_RX_ERROR_PLCP 0x20
#define AR9170_RX_ERROR_MMIC 0x40
-#define AR9170_RX_ERROR_FATAL 0x80
/* these are either-or */
#define AR9170_TX_MAC_PROT_RTS 0x0001
@@ -329,13 +331,15 @@ struct _carl9170_tx_superframe {
#define CARL9170_TX_SUPERDESC_LEN 24
#define AR9170_TX_HWDESC_LEN 8
-#define AR9170_TX_SUPERFRAME_LEN (CARL9170_TX_HWDESC_LEN + \
- AR9170_TX_SUPERDESC_LEN)
+#define CARL9170_TX_SUPERFRAME_LEN (CARL9170_TX_SUPERDESC_LEN + \
+ AR9170_TX_HWDESC_LEN)
struct ar9170_rx_head {
u8 plcp[12];
} __packed;
+#define AR9170_RX_HEAD_LEN 12
+
struct ar9170_rx_phystatus {
union {
struct {
@@ -350,12 +354,16 @@ struct ar9170_rx_phystatus {
u8 phy_err;
} __packed;
+#define AR9170_RX_PHYSTATUS_LEN 20
+
struct ar9170_rx_macstatus {
u8 SAidx, DAidx;
u8 error;
u8 status;
} __packed;
+#define AR9170_RX_MACSTATUS_LEN 4
+
struct ar9170_rx_frame_single {
struct ar9170_rx_head phy_head;
struct ieee80211_hdr i3e;