iwlagn: reclaim the packets in transport layer

The reclaim flow is really transport related. Define a simple API to allow the
upper layer to request from the transport layer to reclaim packets until an
index written in the Tx response / BA notification.
The transport layer prepares a list of the packets that are being freed and
passes this list to the upper layer.
Between the two layers, the CB of the skb is used to pass a pointer to the
context (BSS / PAN) in which the skb was sent.

Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c
index 9d7287e..7438aae 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c
@@ -480,6 +480,9 @@
 	/* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */
 	iwl_trans_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1);
 
+	priv->txq[txq_id].sta_id = sta_id;
+	priv->txq[txq_id].tid = tid;
+
 	spin_unlock_irqrestore(&priv->shrd->lock, flags);
 }
 
@@ -1035,3 +1038,55 @@
 
 	return iwl_trans_pcie_send_cmd(priv, &cmd);
 }
+
+/* Frees buffers until index _not_ inclusive */
+void iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index,
+			    struct sk_buff_head *skbs)
+
+{
+	struct iwl_tx_queue *txq = &priv(trans)->txq[txq_id];
+	struct iwl_queue *q = &txq->q;
+	struct iwl_tx_info *tx_info;
+	struct ieee80211_tx_info *info;
+	int last_to_free;
+
+	/*Since we free until index _not_ inclusive, the one before index is
+	 * the last we will free. This one must be used */
+	last_to_free = iwl_queue_dec_wrap(index, q->n_bd);
+
+	if ((index >= q->n_bd) ||
+	   (iwl_queue_used(q, last_to_free) == 0)) {
+		IWL_ERR(trans, "%s: Read index for DMA queue txq id (%d), "
+			  "last_to_free %d is out of range [0-%d] %d %d.\n",
+			  __func__, txq_id, last_to_free, q->n_bd,
+			  q->write_ptr, q->read_ptr);
+		return;
+	}
+
+	IWL_DEBUG_TX_REPLY(trans, "reclaim: [%d, %d, %d]\n", txq_id,
+			   q->read_ptr, index);
+
+	if (WARN_ON(!skb_queue_empty(skbs)))
+		return;
+
+	for (;
+	     q->read_ptr != index;
+	     q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
+
+		tx_info = &txq->txb[txq->q.read_ptr];
+
+		if (WARN_ON_ONCE(tx_info->skb == NULL))
+			continue;
+
+		info = IEEE80211_SKB_CB(tx_info->skb);
+		info->driver_data[0] = tx_info->ctx;
+
+		__skb_queue_tail(skbs, tx_info->skb);
+
+		tx_info->skb = NULL;
+
+		iwlagn_txq_inval_byte_cnt_tbl(priv(trans), txq);
+
+		iwlagn_txq_free_tfd(priv(trans), txq, txq->q.read_ptr);
+	}
+}