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.c b/drivers/net/wireless/iwlwifi/iwl-trans.c
index 63a3101..06cec82 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.c
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.c
@@ -1106,7 +1106,7 @@
 	 * regardless of the value of ret. "ret" only indicates
 	 * whether or not we should update the write pointer.
 	 */
-	if ((iwl_queue_space(q) < q->high_mark) && priv->mac80211_registered) {
+	if (iwl_queue_space(q) < q->high_mark) {
 		if (wait_write_ptr) {
 			txq->need_update = 1;
 			iwl_txq_update_write_ptr(priv, txq);
@@ -1148,6 +1148,34 @@
 	return 0;
 }
 
+static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id,
+		      int ssn, u32 status, struct sk_buff_head *skbs)
+{
+	struct iwl_priv *priv = priv(trans);
+	struct iwl_tx_queue *txq = &priv->txq[txq_id];
+	/* n_bd is usually 256 => n_bd - 1 = 0xff */
+	int tfd_num = ssn & (txq->q.n_bd - 1);
+	u8 agg_state;
+	bool cond;
+
+	if (txq->sched_retry) {
+		agg_state =
+			priv->stations[txq->sta_id].tid[txq->tid].agg.state;
+		cond = (agg_state != IWL_EMPTYING_HW_QUEUE_DELBA);
+	} else {
+		cond = (status != TX_STATUS_FAIL_PASSIVE_NO_RX);
+	}
+
+	if (txq->q.read_ptr != tfd_num) {
+		IWL_DEBUG_TX_REPLY(trans, "Retry scheduler reclaim "
+				"scd_ssn=%d idx=%d txq=%d swq=%d\n",
+				ssn , tfd_num, txq_id, txq->swq_id);
+		iwl_tx_queue_reclaim(trans, txq_id, tfd_num, skbs);
+		if (iwl_queue_space(&txq->q) > txq->q.low_mark && cond)
+			iwl_wake_queue(priv, txq);
+	}
+}
+
 static void iwl_trans_pcie_disable_sync_irq(struct iwl_trans *trans)
 {
 	unsigned long flags;
@@ -1626,6 +1654,7 @@
 
 	.get_tx_cmd = iwl_trans_pcie_get_tx_cmd,
 	.tx = iwl_trans_pcie_tx,
+	.reclaim = iwl_trans_pcie_reclaim,
 
 	.txq_agg_disable = iwl_trans_pcie_txq_agg_disable,
 	.txq_agg_setup = iwl_trans_pcie_txq_agg_setup,