iwlagn: cosmetics in iwl-trans.h
[pandora-kernel.git] / drivers / net / wireless / iwlwifi / iwl-trans-tx-pcie.c
index 9f4ffeb..28c606c 100644 (file)
@@ -29,7 +29,6 @@
 #include <linux/etherdevice.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
-#include <net/mac80211.h>
 
 #include "iwl-agn.h"
 #include "iwl-dev.h"
@@ -86,7 +85,7 @@ void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans,
 /**
  * iwl_txq_update_write_ptr - Send new write index to hardware
  */
-void iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq)
+void iwl_txq_update_write_ptr(struct iwl_trans *trans, struct iwl_tx_queue *txq)
 {
        u32 reg = 0;
        int txq_id = txq->q.id;
@@ -94,28 +93,28 @@ void iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq)
        if (txq->need_update == 0)
                return;
 
-       if (priv->cfg->base_params->shadow_reg_enable) {
+       if (hw_params(trans).shadow_reg_enable) {
                /* shadow register enabled */
-               iwl_write32(bus(priv), HBUS_TARG_WRPTR,
+               iwl_write32(bus(trans), HBUS_TARG_WRPTR,
                            txq->q.write_ptr | (txq_id << 8));
        } else {
                /* if we're trying to save power */
-               if (test_bit(STATUS_POWER_PMI, &priv->shrd->status)) {
+               if (test_bit(STATUS_POWER_PMI, &trans->shrd->status)) {
                        /* wake up nic if it's powered down ...
                         * uCode will wake up, and interrupt us again, so next
                         * time we'll skip this part. */
-                       reg = iwl_read32(bus(priv), CSR_UCODE_DRV_GP1);
+                       reg = iwl_read32(bus(trans), CSR_UCODE_DRV_GP1);
 
                        if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
-                               IWL_DEBUG_INFO(priv,
+                               IWL_DEBUG_INFO(trans,
                                        "Tx queue %d requesting wakeup,"
                                        " GP1 = 0x%x\n", txq_id, reg);
-                               iwl_set_bit(bus(priv), CSR_GP_CNTRL,
+                               iwl_set_bit(bus(trans), CSR_GP_CNTRL,
                                        CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
                                return;
                        }
 
-                       iwl_write_direct32(bus(priv), HBUS_TARG_WRPTR,
+                       iwl_write_direct32(bus(trans), HBUS_TARG_WRPTR,
                                     txq->q.write_ptr | (txq_id << 8));
 
                /*
@@ -124,7 +123,7 @@ void iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq)
                 * trying to tx (during RFKILL, we're not trying to tx).
                 */
                } else
-                       iwl_write32(bus(priv), HBUS_TARG_WRPTR,
+                       iwl_write32(bus(trans), HBUS_TARG_WRPTR,
                                    txq->q.write_ptr | (txq_id << 8));
        }
        txq->need_update = 0;
@@ -215,15 +214,15 @@ void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq,
                         DMA_TO_DEVICE);
 
        /* free SKB */
-       if (txq->txb) {
+       if (txq->skbs) {
                struct sk_buff *skb;
 
-               skb = txq->txb[index].skb;
+               skb = txq->skbs[index];
 
                /* can be called from irqs-disabled context */
                if (skb) {
                        dev_kfree_skb_any(skb);
-                       txq->txb[index].skb = NULL;
+                       txq->skbs[index] = NULL;
                }
        }
 }
@@ -404,14 +403,15 @@ void iwl_trans_set_wr_ptrs(struct iwl_trans *trans,
        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) |
@@ -419,20 +419,31 @@ void iwl_trans_tx_queue_set_status(struct iwl_priv *priv,
 
        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);
 }
 
-void iwl_trans_pcie_txq_agg_setup(struct iwl_priv *priv, int sta_id, int tid,
-                                               int frame_limit)
+static inline int get_fifo_from_tid(struct iwl_trans_pcie *trans_pcie,
+                                   u8 ctx, u16 tid)
+{
+       const u8 *ac_to_fifo = trans_pcie->ac_to_fifo[ctx];
+       if (likely(tid < ARRAY_SIZE(tid_to_ac)))
+               return ac_to_fifo[tid_to_ac[tid]];
+
+       /* no support for TIDs 8-15 yet */
+       return -EINVAL;
+}
+
+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);
 
@@ -441,16 +452,21 @@ void iwl_trans_pcie_txq_agg_setup(struct iwl_priv *priv, int sta_id, int tid,
        if (WARN_ON(tid >= IWL_MAX_TID_COUNT))
                return;
 
-       spin_lock_irqsave(&priv->shrd->sta_lock, flags);
-       tid_data = &priv->shrd->tid_data[sta_id][tid];
+       tx_fifo = get_fifo_from_tid(trans_pcie, ctx, tid);
+       if (WARN_ON(tx_fifo < 0)) {
+               IWL_ERR(trans, "txq_agg_setup, bad fifo: %d\n", tx_fifo);
+               return;
+       }
+
+       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;
-       tx_fifo = tid_data->agg.tx_fifo;
-       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);
@@ -459,19 +475,19 @@ void iwl_trans_pcie_txq_agg_setup(struct iwl_priv *priv, int sta_id, int tid,
        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 <<
@@ -481,44 +497,155 @@ void iwl_trans_pcie_txq_agg_setup(struct iwl_priv *priv, int sta_id, int tid,
                        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);
 }
 
-int iwl_trans_pcie_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
-                                 u16 ssn_idx, u8 tx_fifo)
+/*
+ * Find first available (lowest unused) Tx Queue, mark it "active".
+ * Called only when finding queue for aggregation.
+ * Should never return anything < 7, because they should already
+ * be in use as EDCA AC (0-3), Command (4), reserved (5, 6)
+ */
+static int iwlagn_txq_ctx_activate_free(struct iwl_trans *trans)
 {
-       struct iwl_trans *trans = trans(priv);
+       int txq_id;
+
+       for (txq_id = 0; txq_id < hw_params(trans).max_txq_num; txq_id++)
+               if (!test_and_set_bit(txq_id,
+                                       &priv(trans)->txq_ctx_active_msk))
+                       return txq_id;
+       return -1;
+}
+
+int iwl_trans_pcie_tx_agg_alloc(struct iwl_trans *trans,
+                               enum iwl_rxon_context_id ctx, int sta_id,
+                               int tid, u16 *ssn)
+{
+       struct iwl_tid_data *tid_data;
+       unsigned long flags;
+       u16 txq_id;
+       struct iwl_priv *priv = priv(trans);
+
+       txq_id = iwlagn_txq_ctx_activate_free(trans);
+       if (txq_id == -1) {
+               IWL_ERR(trans, "No free aggregation queue available\n");
+               return -ENXIO;
+       }
+
+       spin_lock_irqsave(&trans->shrd->sta_lock, flags);
+       tid_data = &trans->shrd->tid_data[sta_id][tid];
+       *ssn = SEQ_TO_SN(tid_data->seq_number);
+       tid_data->agg.txq_id = txq_id;
+       iwl_set_swq_id(&priv->txq[txq_id], get_ac_from_tid(tid), txq_id);
+
+       tid_data = &trans->shrd->tid_data[sta_id][tid];
+       if (tid_data->tfds_in_queue == 0) {
+               IWL_DEBUG_HT(trans, "HW queue is empty\n");
+               tid_data->agg.state = IWL_AGG_ON;
+               iwl_start_tx_ba_trans_ready(priv(trans), ctx, sta_id, tid);
+       } else {
+               IWL_DEBUG_HT(trans, "HW queue is NOT empty: %d packets in HW"
+                            "queue\n", tid_data->tfds_in_queue);
+               tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA;
+       }
+       spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
+
+       return 0;
+}
+
+void iwl_trans_pcie_txq_agg_disable(struct iwl_trans *trans, int txq_id)
+{
+       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 +
-               priv->cfg->base_params->num_of_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 +
-                       priv->cfg->base_params->num_of_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 = (ssn_idx & 0xff);
-       priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
-       /* supposes that ssn_idx is valid (!= 0xFFF) */
-       iwl_trans_set_wr_ptrs(trans, txq_id, ssn_idx);
+       /* 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;
+
+       /* 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);
 
-       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], tx_fifo, 0);
+       spin_unlock_irqrestore(&trans->shrd->lock, flags);
+
+       iwl_stop_tx_ba_trans_ready(priv(trans), ctx, sta_id, tid);
 
        return 0;
 }
@@ -536,8 +663,7 @@ int iwl_trans_pcie_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
  */
 static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
 {
-       struct iwl_priv *priv = priv(trans);
-       struct iwl_tx_queue *txq = &priv->txq[priv->shrd->cmd_queue];
+       struct iwl_tx_queue *txq = &priv(trans)->txq[trans->shrd->cmd_queue];
        struct iwl_queue *q = &txq->q;
        struct iwl_device_cmd *out_cmd;
        struct iwl_cmd_meta *out_meta;
@@ -560,7 +686,7 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
                return -EIO;
        }
 
-       if ((priv->ucode_owner == IWL_OWNERSHIP_TM) &&
+       if ((trans->shrd->ucode_owner == IWL_OWNERSHIP_TM) &&
            !(cmd->flags & CMD_ON_DEMAND)) {
                IWL_DEBUG_HC(trans, "tm own the uCode, no regular hcmd send\n");
                return -EIO;
@@ -607,10 +733,10 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
                spin_unlock_irqrestore(&trans->hcmd_lock, flags);
 
                IWL_ERR(trans, "No space in command queue\n");
-               is_ct_kill = iwl_check_for_ct_kill(priv);
+               is_ct_kill = iwl_check_for_ct_kill(priv(trans));
                if (!is_ct_kill) {
                        IWL_ERR(trans, "Restarting adapter queue is full\n");
-                       iwlagn_fw_error(priv, false);
+                       iwlagn_fw_error(priv(trans), false);
                }
                return -ENOSPC;
        }
@@ -702,7 +828,7 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
        /* check that tracing gets all possible blocks */
        BUILD_BUG_ON(IWL_MAX_CMD_TFDS + 1 != 3);
 #ifdef CONFIG_IWLWIFI_DEVICE_TRACING
-       trace_iwlwifi_dev_hcmd(priv, cmd->flags,
+       trace_iwlwifi_dev_hcmd(priv(trans), cmd->flags,
                               trace_bufs[0], trace_lens[0],
                               trace_bufs[1], trace_lens[1],
                               trace_bufs[2], trace_lens[2]);
@@ -710,7 +836,7 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
 
        /* Increment and update queue's write index */
        q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
-       iwl_txq_update_write_ptr(priv, txq);
+       iwl_txq_update_write_ptr(trans, txq);
 
  out:
        spin_unlock_irqrestore(&trans->hcmd_lock, flags);
@@ -1052,14 +1178,13 @@ int iwl_trans_pcie_send_cmd_pdu(struct iwl_trans *trans, u8 id, u32 flags,
 }
 
 /* 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;
-       struct iwl_tx_info *tx_info;
-       struct ieee80211_tx_info *info;
        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 */
@@ -1071,33 +1196,30 @@ void iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index,
                          "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;
             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))
+               if (WARN_ON_ONCE(txq->skbs[txq->q.read_ptr] == NULL))
                        continue;
 
-               info = IEEE80211_SKB_CB(tx_info->skb);
-               info->driver_data[0] = tx_info->ctx;
-
-               __skb_queue_tail(skbs, tx_info->skb);
+               __skb_queue_tail(skbs, txq->skbs[txq->q.read_ptr]);
 
-               tx_info->skb = NULL;
+               txq->skbs[txq->q.read_ptr] = NULL;
 
                iwlagn_txq_inval_byte_cnt_tbl(trans, txq);
 
                iwlagn_txq_free_tfd(trans, txq, txq->q.read_ptr);
+               freed++;
        }
+       return freed;
 }