ath9k: limit multicast buffer hardware queue depth
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / net / wireless / ath / ath9k / xmit.c
index 6c9ff9c..7e19d9b 100644 (file)
@@ -1970,22 +1970,16 @@ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc,
        return bf;
 }
 
-/* Upon failure caller should free skb */
-int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
-                struct ath_tx_control *txctl)
+static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb,
+                         struct ath_tx_control *txctl)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_sta *sta = txctl->sta;
        struct ieee80211_vif *vif = info->control.vif;
        struct ath_softc *sc = hw->priv;
-       struct ath_txq *txq = txctl->txq;
-       struct ath_atx_tid *tid = NULL;
-       struct ath_buf *bf;
-       int padpos, padsize;
        int frmlen = skb->len + FCS_LEN;
-       u8 tidno;
-       int q;
+       int padpos, padsize;
 
        /* NOTE:  sta can be NULL according to net/mac80211.h */
        if (sta)
@@ -2006,6 +2000,11 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
        }
 
+       if ((vif && vif->type != NL80211_IFTYPE_AP &&
+                   vif->type != NL80211_IFTYPE_AP_VLAN) ||
+           !ieee80211_is_data(hdr->frame_control))
+               info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
+
        /* Add the padding after the header if this is not already done */
        padpos = ieee80211_hdrlen(hdr->frame_control);
        padsize = padpos & 3;
@@ -2015,16 +2014,34 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
 
                skb_push(skb, padsize);
                memmove(skb->data, skb->data + padsize, padpos);
-               hdr = (struct ieee80211_hdr *) skb->data;
        }
 
-       if ((vif && vif->type != NL80211_IFTYPE_AP &&
-                   vif->type != NL80211_IFTYPE_AP_VLAN) ||
-           !ieee80211_is_data(hdr->frame_control))
-               info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
-
        setup_frame_info(hw, sta, skb, frmlen);
+       return 0;
+}
+
 
+/* Upon failure caller should free skb */
+int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
+                struct ath_tx_control *txctl)
+{
+       struct ieee80211_hdr *hdr;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_sta *sta = txctl->sta;
+       struct ieee80211_vif *vif = info->control.vif;
+       struct ath_softc *sc = hw->priv;
+       struct ath_txq *txq = txctl->txq;
+       struct ath_atx_tid *tid = NULL;
+       struct ath_buf *bf;
+       u8 tidno;
+       int q;
+       int ret;
+
+       ret = ath_tx_prepare(hw, skb, txctl);
+       if (ret)
+           return ret;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
        /*
         * At this point, the vif, hw_key and sta pointers in the tx control
         * info are no longer valid (overwritten by the ath_frame_info data.
@@ -2086,6 +2103,74 @@ out:
        return 0;
 }
 
+void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                struct sk_buff *skb)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_tx_control txctl = {
+               .txq = sc->beacon.cabq
+       };
+       struct ath_tx_info info = {};
+       struct ieee80211_hdr *hdr;
+       struct ath_buf *bf_tail = NULL;
+       struct ath_buf *bf;
+       LIST_HEAD(bf_q);
+       int duration = 0;
+       int max_duration;
+
+       max_duration =
+               sc->cur_beacon_conf.beacon_interval * 1000 *
+               sc->cur_beacon_conf.dtim_period / ATH_BCBUF;
+
+       do {
+               struct ath_frame_info *fi = get_frame_info(skb);
+
+               if (ath_tx_prepare(hw, skb, &txctl))
+                       break;
+
+               bf = ath_tx_setup_buffer(sc, txctl.txq, NULL, skb);
+               if (!bf)
+                       break;
+
+               bf->bf_lastbf = bf;
+               ath_set_rates(vif, NULL, bf);
+               ath_buf_set_rate(sc, bf, &info, fi->framelen);
+               duration += info.rates[0].PktDuration;
+               if (bf_tail)
+                       bf_tail->bf_next = bf;
+
+               list_add_tail(&bf->list, &bf_q);
+               bf_tail = bf;
+               skb = NULL;
+
+               if (duration > max_duration)
+                       break;
+
+               skb = ieee80211_get_buffered_bc(hw, vif);
+       } while(skb);
+
+       if (skb)
+               ieee80211_free_txskb(hw, skb);
+
+       if (list_empty(&bf_q))
+               return;
+
+       bf = list_first_entry(&bf_q, struct ath_buf, list);
+       hdr = (struct ieee80211_hdr *) bf->bf_mpdu->data;
+
+       if (hdr->frame_control & IEEE80211_FCTL_MOREDATA) {
+               hdr->frame_control &= ~IEEE80211_FCTL_MOREDATA;
+               dma_sync_single_for_device(sc->dev, bf->bf_buf_addr,
+                       sizeof(*hdr), DMA_TO_DEVICE);
+       }
+
+       ath_txq_lock(sc, txctl.txq);
+       ath_tx_fill_desc(sc, bf, txctl.txq, 0);
+       ath_tx_txqaddbuf(sc, txctl.txq, &bf_q, false);
+       TX_STAT_INC(txctl.txq->axq_qnum, queued);
+       ath_txq_unlock(sc, txctl.txq);
+}
+
 /*****************/
 /* TX Completion */
 /*****************/