iwl_write_prph(bus(trans), SCD_QUEUE_RDPTR(txq_id), index);
}
-void iwl_trans_tx_queue_set_status(struct iwl_priv *priv,
+void iwl_trans_tx_queue_set_status(struct iwl_trans *trans,
struct iwl_tx_queue *txq,
int tx_fifo_id, int scd_retry)
{
int txq_id = txq->q.id;
- int active = test_bit(txq_id, &priv->txq_ctx_active_msk) ? 1 : 0;
+ int active =
+ test_bit(txq_id, &priv(trans)->txq_ctx_active_msk) ? 1 : 0;
- iwl_write_prph(bus(priv), SCD_QUEUE_STATUS_BITS(txq_id),
+ iwl_write_prph(bus(trans), SCD_QUEUE_STATUS_BITS(txq_id),
(active << SCD_QUEUE_STTS_REG_POS_ACTIVE) |
(tx_fifo_id << SCD_QUEUE_STTS_REG_POS_TXF) |
(1 << SCD_QUEUE_STTS_REG_POS_WSL) |
txq->sched_retry = scd_retry;
- IWL_DEBUG_INFO(priv, "%s %s Queue %d on FIFO %d\n",
+ IWL_DEBUG_INFO(trans, "%s %s Queue %d on FIFO %d\n",
active ? "Activate" : "Deactivate",
scd_retry ? "BA" : "AC/CMD", txq_id, tx_fifo_id);
}
return -EINVAL;
}
-void iwl_trans_pcie_txq_agg_setup(struct iwl_priv *priv,
- enum iwl_rxon_context_id ctx, int sta_id,
- int tid, int frame_limit)
+void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans,
+ enum iwl_rxon_context_id ctx, int sta_id,
+ int tid, int frame_limit)
{
int tx_fifo, txq_id, ssn_idx;
u16 ra_tid;
unsigned long flags;
struct iwl_tid_data *tid_data;
- struct iwl_trans *trans = trans(priv);
struct iwl_trans_pcie *trans_pcie =
IWL_TRANS_GET_PCIE_TRANS(trans);
return;
}
- spin_lock_irqsave(&priv->shrd->sta_lock, flags);
- tid_data = &priv->shrd->tid_data[sta_id][tid];
+ spin_lock_irqsave(&trans->shrd->sta_lock, flags);
+ tid_data = &trans->shrd->tid_data[sta_id][tid];
ssn_idx = SEQ_TO_SN(tid_data->seq_number);
txq_id = tid_data->agg.txq_id;
- spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
+ spin_unlock_irqrestore(&trans->shrd->sta_lock, flags);
ra_tid = BUILD_RAxTID(sta_id, tid);
- spin_lock_irqsave(&priv->shrd->lock, flags);
+ spin_lock_irqsave(&trans->shrd->lock, flags);
/* Stop this Tx queue before configuring it */
iwlagn_tx_queue_stop_scheduler(trans, txq_id);
iwlagn_tx_queue_set_q2ratid(trans, ra_tid, txq_id);
/* Set this queue as a chain-building queue */
- iwl_set_bits_prph(bus(priv), SCD_QUEUECHAIN_SEL, (1<<txq_id));
+ iwl_set_bits_prph(bus(trans), SCD_QUEUECHAIN_SEL, (1<<txq_id));
/* enable aggregations for the queue */
- iwl_set_bits_prph(bus(priv), SCD_AGGR_SEL, (1<<txq_id));
+ iwl_set_bits_prph(bus(trans), SCD_AGGR_SEL, (1<<txq_id));
/* Place first TFD at index corresponding to start sequence number.
* Assumes that ssn_idx is valid (!= 0xFFF) */
- priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
- priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
+ priv(trans)->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
+ priv(trans)->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
iwl_trans_set_wr_ptrs(trans, txq_id, ssn_idx);
/* Set up Tx window size and frame limit for this queue */
- iwl_write_targ_mem(bus(priv), trans_pcie->scd_base_addr +
+ iwl_write_targ_mem(bus(trans), trans_pcie->scd_base_addr +
SCD_CONTEXT_QUEUE_OFFSET(txq_id) +
sizeof(u32),
((frame_limit <<
SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK));
- iwl_set_bits_prph(bus(priv), SCD_INTERRUPT_MASK, (1 << txq_id));
+ iwl_set_bits_prph(bus(trans), SCD_INTERRUPT_MASK, (1 << txq_id));
/* 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);
+ iwl_trans_tx_queue_set_status(trans, &priv(trans)->txq[txq_id],
+ tx_fifo, 1);
- priv->txq[txq_id].sta_id = sta_id;
- priv->txq[txq_id].tid = tid;
+ priv(trans)->txq[txq_id].sta_id = sta_id;
+ priv(trans)->txq[txq_id].tid = tid;
- spin_unlock_irqrestore(&priv->shrd->lock, flags);
+ spin_unlock_irqrestore(&trans->shrd->lock, flags);
}
/*
return 0;
}
-int iwl_trans_pcie_txq_agg_disable(struct iwl_priv *priv, u16 txq_id)
+
+void iwl_trans_pcie_txq_agg_disable(struct iwl_trans *trans, int txq_id)
{
- struct iwl_trans *trans = trans(priv);
+ iwlagn_tx_queue_stop_scheduler(trans, txq_id);
+
+ iwl_clear_bits_prph(bus(trans), SCD_AGGR_SEL, (1 << txq_id));
+
+ priv(trans)->txq[txq_id].q.read_ptr = 0;
+ priv(trans)->txq[txq_id].q.write_ptr = 0;
+ /* supposes that ssn_idx is valid (!= 0xFFF) */
+ iwl_trans_set_wr_ptrs(trans, txq_id, 0);
+
+ iwl_clear_bits_prph(bus(trans), SCD_INTERRUPT_MASK, (1 << txq_id));
+ iwl_txq_ctx_deactivate(priv(trans), txq_id);
+ iwl_trans_tx_queue_set_status(trans, &priv(trans)->txq[txq_id], 0, 0);
+}
+
+int iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans,
+ enum iwl_rxon_context_id ctx, int sta_id,
+ int tid)
+{
+ unsigned long flags;
+ int read_ptr, write_ptr;
+ struct iwl_tid_data *tid_data;
+ int txq_id;
+
+ spin_lock_irqsave(&trans->shrd->sta_lock, flags);
+
+ tid_data = &trans->shrd->tid_data[sta_id][tid];
+ txq_id = tid_data->agg.txq_id;
+
if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) ||
(IWLAGN_FIRST_AMPDU_QUEUE +
- hw_params(priv).num_ampdu_queues <= txq_id)) {
- IWL_ERR(priv,
+ hw_params(trans).num_ampdu_queues <= txq_id)) {
+ IWL_ERR(trans,
"queue number out of range: %d, must be %d to %d\n",
txq_id, IWLAGN_FIRST_AMPDU_QUEUE,
IWLAGN_FIRST_AMPDU_QUEUE +
- hw_params(priv).num_ampdu_queues - 1);
+ hw_params(trans).num_ampdu_queues - 1);
+ spin_unlock_irqrestore(&trans->shrd->sta_lock, flags);
return -EINVAL;
}
- iwlagn_tx_queue_stop_scheduler(trans, txq_id);
+ switch (trans->shrd->tid_data[sta_id][tid].agg.state) {
+ case IWL_EMPTYING_HW_QUEUE_ADDBA:
+ /*
+ * This can happen if the peer stops aggregation
+ * again before we've had a chance to drain the
+ * queue we selected previously, i.e. before the
+ * session was really started completely.
+ */
+ IWL_DEBUG_HT(trans, "AGG stop before setup done\n");
+ goto turn_off;
+ case IWL_AGG_ON:
+ break;
+ default:
+ IWL_WARN(trans, "Stopping AGG while state not ON"
+ "or starting\n");
+ }
- iwl_clear_bits_prph(bus(priv), SCD_AGGR_SEL, (1 << txq_id));
+ write_ptr = priv(trans)->txq[txq_id].q.write_ptr;
+ read_ptr = priv(trans)->txq[txq_id].q.read_ptr;
- priv->txq[txq_id].q.read_ptr = 0;
- priv->txq[txq_id].q.write_ptr = 0;
- /* supposes that ssn_idx is valid (!= 0xFFF) */
- iwl_trans_set_wr_ptrs(trans, txq_id, 0);
+ /* The queue is not empty */
+ if (write_ptr != read_ptr) {
+ IWL_DEBUG_HT(trans, "Stopping a non empty AGG HW QUEUE\n");
+ trans->shrd->tid_data[sta_id][tid].agg.state =
+ IWL_EMPTYING_HW_QUEUE_DELBA;
+ spin_unlock_irqrestore(&trans->shrd->sta_lock, flags);
+ return 0;
+ }
+
+ IWL_DEBUG_HT(trans, "HW queue is empty\n");
+turn_off:
+ trans->shrd->tid_data[sta_id][tid].agg.state = IWL_AGG_OFF;
- iwl_clear_bits_prph(bus(priv), SCD_INTERRUPT_MASK, (1 << txq_id));
- iwl_txq_ctx_deactivate(priv, txq_id);
- iwl_trans_tx_queue_set_status(priv, &priv->txq[txq_id], 0, 0);
+ /* do not restore/save irqs */
+ spin_unlock(&trans->shrd->sta_lock);
+ spin_lock(&trans->shrd->lock);
+
+ iwl_trans_pcie_txq_agg_disable(trans, txq_id);
+
+ spin_unlock_irqrestore(&trans->shrd->lock, flags);
+
+ iwl_stop_tx_ba_trans_ready(priv(trans), ctx, sta_id, tid);
return 0;
}
}
/* 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)
+int 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;
int last_to_free;
+ int freed = 0;
/*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 %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;
+ return 0;
}
IWL_DEBUG_TX_REPLY(trans, "reclaim: [%d, %d, %d]\n", txq_id,
q->read_ptr, index);
if (WARN_ON(!skb_queue_empty(skbs)))
- return;
+ return 0;
for (;
q->read_ptr != index;
iwlagn_txq_inval_byte_cnt_tbl(trans, txq);
iwlagn_txq_free_tfd(trans, txq, txq->q.read_ptr);
+ freed++;
}
+ return freed;
}