|
@@ -0,0 +1,200 @@
|
|
|
|
|
+--- a/drivers/net/wireless/ath/ath9k/main.c
|
|
|
|
|
++++ b/drivers/net/wireless/ath/ath9k/main.c
|
|
|
|
|
+@@ -1291,6 +1291,11 @@ static void ath9k_stop(struct ieee80211_
|
|
|
|
|
+ } else
|
|
|
|
|
+ sc->rx.rxlink = NULL;
|
|
|
|
|
+
|
|
|
|
|
++ if (sc->rx.frag) {
|
|
|
|
|
++ dev_kfree_skb_any(sc->rx.frag);
|
|
|
|
|
++ sc->rx.frag = NULL;
|
|
|
|
|
++ }
|
|
|
|
|
++
|
|
|
|
|
+ /* disable HAL and put h/w to sleep */
|
|
|
|
|
+ ath9k_hw_disable(ah);
|
|
|
|
|
+ ath9k_hw_configpcipowersave(ah, 1, 1);
|
|
|
|
|
+--- a/drivers/net/wireless/ath/ath9k/recv.c
|
|
|
|
|
++++ b/drivers/net/wireless/ath/ath9k/recv.c
|
|
|
|
|
+@@ -209,11 +209,6 @@ static int ath_rx_edma_init(struct ath_s
|
|
|
|
|
+ int error = 0, i;
|
|
|
|
|
+ u32 size;
|
|
|
|
|
+
|
|
|
|
|
+-
|
|
|
|
|
+- common->rx_bufsize = roundup(IEEE80211_MAX_MPDU_LEN +
|
|
|
|
|
+- ah->caps.rx_status_len,
|
|
|
|
|
+- min(common->cachelsz, (u16)64));
|
|
|
|
|
+-
|
|
|
|
|
+ ath9k_hw_set_rx_bufsize(ah, common->rx_bufsize -
|
|
|
|
|
+ ah->caps.rx_status_len);
|
|
|
|
|
+
|
|
|
|
|
+@@ -300,12 +295,12 @@ int ath_rx_init(struct ath_softc *sc, in
|
|
|
|
|
+ sc->sc_flags &= ~SC_OP_RXFLUSH;
|
|
|
|
|
+ spin_lock_init(&sc->rx.rxbuflock);
|
|
|
|
|
+
|
|
|
|
|
++ common->rx_bufsize = IEEE80211_MAX_MPDU_LEN / 2 + common->cachelsz +
|
|
|
|
|
++ sc->sc_ah->caps.rx_status_len;
|
|
|
|
|
++
|
|
|
|
|
+ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
|
|
|
|
|
+ return ath_rx_edma_init(sc, nbufs);
|
|
|
|
|
+ } else {
|
|
|
|
|
+- common->rx_bufsize = roundup(IEEE80211_MAX_MPDU_LEN,
|
|
|
|
|
+- min(common->cachelsz, (u16)64));
|
|
|
|
|
+-
|
|
|
|
|
+ ath_dbg(common, ATH_DBG_CONFIG, "cachelsz %u rxbufsize %u\n",
|
|
|
|
|
+ common->cachelsz, common->rx_bufsize);
|
|
|
|
|
+
|
|
|
|
|
+@@ -815,15 +810,9 @@ static bool ath9k_rx_accept(struct ath_c
|
|
|
|
|
+ if (rx_stats->rs_datalen > (common->rx_bufsize - rx_status_len))
|
|
|
|
|
+ return false;
|
|
|
|
|
+
|
|
|
|
|
+- /*
|
|
|
|
|
+- * rs_more indicates chained descriptors which can be used
|
|
|
|
|
+- * to link buffers together for a sort of scatter-gather
|
|
|
|
|
+- * operation.
|
|
|
|
|
+- * reject the frame, we don't support scatter-gather yet and
|
|
|
|
|
+- * the frame is probably corrupt anyway
|
|
|
|
|
+- */
|
|
|
|
|
++ /* Only use error bits from the last fragment */
|
|
|
|
|
+ if (rx_stats->rs_more)
|
|
|
|
|
+- return false;
|
|
|
|
|
++ return true;
|
|
|
|
|
+
|
|
|
|
|
+ /*
|
|
|
|
|
+ * The rx_stats->rs_status will not be set until the end of the
|
|
|
|
|
+@@ -981,6 +970,10 @@ static int ath9k_rx_skb_preprocess(struc
|
|
|
|
|
+ if (!ath9k_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error))
|
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
+
|
|
|
|
|
++ /* Only use status info from the last fragment */
|
|
|
|
|
++ if (rx_stats->rs_more)
|
|
|
|
|
++ return 0;
|
|
|
|
|
++
|
|
|
|
|
+ ath9k_process_rssi(common, hw, hdr, rx_stats);
|
|
|
|
|
+
|
|
|
|
|
+ if (ath9k_process_rate(common, hw, rx_stats, rx_status))
|
|
|
|
|
+@@ -1582,7 +1575,7 @@ div_comb_done:
|
|
|
|
|
+ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
|
|
|
|
|
+ {
|
|
|
|
|
+ struct ath_buf *bf;
|
|
|
|
|
+- struct sk_buff *skb = NULL, *requeue_skb;
|
|
|
|
|
++ struct sk_buff *skb = NULL, *requeue_skb, *hdr_skb;
|
|
|
|
|
+ struct ieee80211_rx_status *rxs;
|
|
|
|
|
+ struct ath_hw *ah = sc->sc_ah;
|
|
|
|
|
+ struct ath_common *common = ath9k_hw_common(ah);
|
|
|
|
|
+@@ -1633,8 +1626,17 @@ int ath_rx_tasklet(struct ath_softc *sc,
|
|
|
|
|
+ if (!skb)
|
|
|
|
|
+ continue;
|
|
|
|
|
+
|
|
|
|
|
+- hdr = (struct ieee80211_hdr *) (skb->data + rx_status_len);
|
|
|
|
|
+- rxs = IEEE80211_SKB_RXCB(skb);
|
|
|
|
|
++ /*
|
|
|
|
|
++ * Take frame header from the first fragment and RX status from
|
|
|
|
|
++ * the last one.
|
|
|
|
|
++ */
|
|
|
|
|
++ if (sc->rx.frag)
|
|
|
|
|
++ hdr_skb = sc->rx.frag;
|
|
|
|
|
++ else
|
|
|
|
|
++ hdr_skb = skb;
|
|
|
|
|
++
|
|
|
|
|
++ hdr = (struct ieee80211_hdr *) (hdr_skb->data + rx_status_len);
|
|
|
|
|
++ rxs = IEEE80211_SKB_RXCB(hdr_skb);
|
|
|
|
|
+
|
|
|
|
|
+ ath_debug_stat_rx(sc, &rs);
|
|
|
|
|
+
|
|
|
|
|
+@@ -1643,12 +1645,12 @@ int ath_rx_tasklet(struct ath_softc *sc,
|
|
|
|
|
+ * chain it back at the queue without processing it.
|
|
|
|
|
+ */
|
|
|
|
|
+ if (flush)
|
|
|
|
|
+- goto requeue;
|
|
|
|
|
++ goto requeue_drop_frag;
|
|
|
|
|
+
|
|
|
|
|
+ retval = ath9k_rx_skb_preprocess(common, hw, hdr, &rs,
|
|
|
|
|
+ rxs, &decrypt_error);
|
|
|
|
|
+ if (retval)
|
|
|
|
|
+- goto requeue;
|
|
|
|
|
++ goto requeue_drop_frag;
|
|
|
|
|
+
|
|
|
|
|
+ rxs->mactime = (tsf & ~0xffffffffULL) | rs.rs_tstamp;
|
|
|
|
|
+ if (rs.rs_tstamp > tsf_lower &&
|
|
|
|
|
+@@ -1668,7 +1670,7 @@ int ath_rx_tasklet(struct ath_softc *sc,
|
|
|
|
|
+ * skb and put it at the tail of the sc->rx.rxbuf list for
|
|
|
|
|
+ * processing. */
|
|
|
|
|
+ if (!requeue_skb)
|
|
|
|
|
+- goto requeue;
|
|
|
|
|
++ goto requeue_drop_frag;
|
|
|
|
|
+
|
|
|
|
|
+ /* Unmap the frame */
|
|
|
|
|
+ dma_unmap_single(sc->dev, bf->bf_buf_addr,
|
|
|
|
|
+@@ -1679,8 +1681,9 @@ int ath_rx_tasklet(struct ath_softc *sc,
|
|
|
|
|
+ if (ah->caps.rx_status_len)
|
|
|
|
|
+ skb_pull(skb, ah->caps.rx_status_len);
|
|
|
|
|
+
|
|
|
|
|
+- ath9k_rx_skb_postprocess(common, skb, &rs,
|
|
|
|
|
+- rxs, decrypt_error);
|
|
|
|
|
++ if (!rs.rs_more)
|
|
|
|
|
++ ath9k_rx_skb_postprocess(common, hdr_skb, &rs,
|
|
|
|
|
++ rxs, decrypt_error);
|
|
|
|
|
+
|
|
|
|
|
+ /* We will now give hardware our shiny new allocated skb */
|
|
|
|
|
+ bf->bf_mpdu = requeue_skb;
|
|
|
|
|
+@@ -1697,6 +1700,38 @@ int ath_rx_tasklet(struct ath_softc *sc,
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
++ if (rs.rs_more) {
|
|
|
|
|
++ /*
|
|
|
|
|
++ * rs_more indicates chained descriptors which can be
|
|
|
|
|
++ * used to link buffers together for a sort of
|
|
|
|
|
++ * scatter-gather operation.
|
|
|
|
|
++ */
|
|
|
|
|
++ if (sc->rx.frag) {
|
|
|
|
|
++ /* too many fragments - cannot handle frame */
|
|
|
|
|
++ dev_kfree_skb_any(sc->rx.frag);
|
|
|
|
|
++ dev_kfree_skb_any(skb);
|
|
|
|
|
++ skb = NULL;
|
|
|
|
|
++ }
|
|
|
|
|
++ sc->rx.frag = skb;
|
|
|
|
|
++ goto requeue;
|
|
|
|
|
++ }
|
|
|
|
|
++
|
|
|
|
|
++ if (sc->rx.frag) {
|
|
|
|
|
++ int space = skb->len - skb_tailroom(hdr_skb);
|
|
|
|
|
++
|
|
|
|
|
++ sc->rx.frag = NULL;
|
|
|
|
|
++
|
|
|
|
|
++ if (pskb_expand_head(hdr_skb, 0, space, GFP_ATOMIC) < 0) {
|
|
|
|
|
++ dev_kfree_skb(skb);
|
|
|
|
|
++ goto requeue_drop_frag;
|
|
|
|
|
++ }
|
|
|
|
|
++
|
|
|
|
|
++ skb_copy_from_linear_data(skb, skb_put(hdr_skb, skb->len),
|
|
|
|
|
++ skb->len);
|
|
|
|
|
++ dev_kfree_skb_any(skb);
|
|
|
|
|
++ skb = hdr_skb;
|
|
|
|
|
++ }
|
|
|
|
|
++
|
|
|
|
|
+ /*
|
|
|
|
|
+ * change the default rx antenna if rx diversity chooses the
|
|
|
|
|
+ * other antenna 3 times in a row.
|
|
|
|
|
+@@ -1722,6 +1757,11 @@ int ath_rx_tasklet(struct ath_softc *sc,
|
|
|
|
|
+
|
|
|
|
|
+ ieee80211_rx(hw, skb);
|
|
|
|
|
+
|
|
|
|
|
++requeue_drop_frag:
|
|
|
|
|
++ if (sc->rx.frag) {
|
|
|
|
|
++ dev_kfree_skb_any(sc->rx.frag);
|
|
|
|
|
++ sc->rx.frag = NULL;
|
|
|
|
|
++ }
|
|
|
|
|
+ requeue:
|
|
|
|
|
+ if (edma) {
|
|
|
|
|
+ list_add_tail(&bf->list, &sc->rx.rxbuf);
|
|
|
|
|
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
|
|
|
|
|
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
|
|
|
|
|
+@@ -311,6 +311,8 @@ struct ath_rx {
|
|
|
|
|
+ struct ath_descdma rxdma;
|
|
|
|
|
+ struct ath_buf *rx_bufptr;
|
|
|
|
|
+ struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX];
|
|
|
|
|
++
|
|
|
|
|
++ struct sk_buff *frag;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ int ath_startrecv(struct ath_softc *sc);
|