Merge branch 'for-linus' of git://oss.sgi.com/xfs/xfs
[deliverable/linux.git] / drivers / net / wireless / iwlwifi / iwl-trans-pcie.c
index 3cf62c363bc00ab43f49d5336f576de928462466..67d6e324e26f7b2e75910921aceadc034a5a10ed 100644 (file)
@@ -88,18 +88,16 @@ static int iwl_trans_rx_alloc(struct iwl_trans *trans)
                return -EINVAL;
 
        /* Allocate the circular buffer of Read Buffer Descriptors (RBDs) */
-       rxq->bd = dma_alloc_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE,
-                                    &rxq->bd_dma, GFP_KERNEL);
+       rxq->bd = dma_zalloc_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE,
+                                     &rxq->bd_dma, GFP_KERNEL);
        if (!rxq->bd)
                goto err_bd;
-       memset(rxq->bd, 0, sizeof(__le32) * RX_QUEUE_SIZE);
 
        /*Allocate the driver's pointer to receive buffer status */
-       rxq->rb_stts = dma_alloc_coherent(dev, sizeof(*rxq->rb_stts),
-                                         &rxq->rb_stts_dma, GFP_KERNEL);
+       rxq->rb_stts = dma_zalloc_coherent(dev, sizeof(*rxq->rb_stts),
+                                          &rxq->rb_stts_dma, GFP_KERNEL);
        if (!rxq->rb_stts)
                goto err_rb_stts;
-       memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts));
 
        return 0;
 
@@ -1044,7 +1042,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
 
 static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
                struct iwl_device_cmd *dev_cmd, enum iwl_rxon_context_id ctx,
-               u8 sta_id)
+               u8 sta_id, u8 tid)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
@@ -1058,13 +1056,12 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
        dma_addr_t txcmd_phys;
        dma_addr_t scratch_phys;
        u16 len, firstlen, secondlen;
-       u16 seq_number = 0;
        u8 wait_write_ptr = 0;
        u8 txq_id;
-       u8 tid = 0;
        bool is_agg = false;
        __le16 fc = hdr->frame_control;
        u8 hdr_len = ieee80211_hdrlen(fc);
+       u16 __maybe_unused wifi_seq;
 
        /*
         * Send this frame after DTIM -- there's a special queue
@@ -1085,44 +1082,28 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
                txq_id =
                    trans_pcie->ac_to_queue[ctx][skb_get_queue_mapping(skb)];
 
-       if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) {
-               u8 *qc = NULL;
-               struct iwl_tid_data *tid_data;
-               qc = ieee80211_get_qos_ctl(hdr);
-               tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
-               tid_data = &trans->shrd->tid_data[sta_id][tid];
-
-               if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
-                       return -1;
-
-               seq_number = tid_data->seq_number;
-               seq_number &= IEEE80211_SCTL_SEQ;
-               hdr->seq_ctrl = hdr->seq_ctrl &
-                               cpu_to_le16(IEEE80211_SCTL_FRAG);
-               hdr->seq_ctrl |= cpu_to_le16(seq_number);
-               /* aggregation is on for this <sta,tid> */
-               if (info->flags & IEEE80211_TX_CTL_AMPDU) {
-                       if (WARN_ON_ONCE(tid_data->agg.state != IWL_AGG_ON)) {
-                               IWL_ERR(trans, "TX_CTL_AMPDU while not in AGG:"
-                                       " Tx flags = 0x%08x, agg.state = %d",
-                                       info->flags, tid_data->agg.state);
-                               IWL_ERR(trans, "sta_id = %d, tid = %d "
-                                       "txq_id = %d, seq_num = %d", sta_id,
-                                       tid, tid_data->agg.txq_id,
-                                       seq_number >> 4);
-                       }
-                       txq_id = tid_data->agg.txq_id;
-                       is_agg = true;
-               }
-               seq_number += 0x10;
+       /* aggregation is on for this <sta,tid> */
+       if (info->flags & IEEE80211_TX_CTL_AMPDU) {
+               WARN_ON(tid >= IWL_MAX_TID_COUNT);
+               txq_id = trans_pcie->agg_txq[sta_id][tid];
+               is_agg = true;
        }
 
-       /* Copy MAC header from skb into command buffer */
-       memcpy(tx_cmd->hdr, hdr, hdr_len);
-
        txq = &trans_pcie->txq[txq_id];
        q = &txq->q;
 
+       /* In AGG mode, the index in the ring must correspond to the WiFi
+        * sequence number. This is a HW requirements to help the SCD to parse
+        * the BA.
+        * Check here that the packets are in the right place on the ring.
+        */
+#ifdef CONFIG_IWLWIFI_DEBUG
+       wifi_seq = SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
+       WARN_ONCE(is_agg && ((wifi_seq & 0xff) != q->write_ptr),
+                 "Q: %d WiFi Seq %d tfdNum %d",
+                 txq_id, wifi_seq, q->write_ptr);
+#endif
+
        /* Set up driver data for this TFD */
        txq->skbs[q->write_ptr] = skb;
        txq->cmd[q->write_ptr] = dev_cmd;
@@ -1220,13 +1201,6 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
        q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
        iwl_txq_update_write_ptr(trans, txq);
 
-       if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) {
-               trans->shrd->tid_data[sta_id][tid].tfds_in_queue++;
-               if (!ieee80211_has_morefrags(fc))
-                       trans->shrd->tid_data[sta_id][tid].seq_number =
-                               seq_number;
-       }
-
        /*
         * At this point the frame is "transmitted" successfully
         * and we will get a TX status notification eventually,
@@ -1275,85 +1249,30 @@ static int iwl_trans_pcie_request_irq(struct iwl_trans *trans)
        return 0;
 }
 
-static int iwlagn_txq_check_empty(struct iwl_trans *trans,
-                          int sta_id, u8 tid, int txq_id)
-{
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-       struct iwl_queue *q = &trans_pcie->txq[txq_id].q;
-       struct iwl_tid_data *tid_data = &trans->shrd->tid_data[sta_id][tid];
-
-       lockdep_assert_held(&trans->shrd->sta_lock);
-
-       switch (trans->shrd->tid_data[sta_id][tid].agg.state) {
-       case IWL_EMPTYING_HW_QUEUE_DELBA:
-               /* We are reclaiming the last packet of the */
-               /* aggregated HW queue */
-               if ((txq_id  == tid_data->agg.txq_id) &&
-                   (q->read_ptr == q->write_ptr)) {
-                       IWL_DEBUG_TX_QUEUES(trans,
-                               "HW queue empty: continue DELBA flow\n");
-                       iwl_trans_pcie_txq_agg_disable(trans, txq_id);
-                       tid_data->agg.state = IWL_AGG_OFF;
-                       iwl_stop_tx_ba_trans_ready(priv(trans),
-                                                  NUM_IWL_RXON_CTX,
-                                                  sta_id, tid);
-                       iwl_wake_queue(trans, &trans_pcie->txq[txq_id],
-                                      "DELBA flow complete");
-               }
-               break;
-       case IWL_EMPTYING_HW_QUEUE_ADDBA:
-               /* We are reclaiming the last packet of the queue */
-               if (tid_data->tfds_in_queue == 0) {
-                       IWL_DEBUG_TX_QUEUES(trans,
-                               "HW queue empty: continue ADDBA flow\n");
-                       tid_data->agg.state = IWL_AGG_ON;
-                       iwl_start_tx_ba_trans_ready(priv(trans),
-                                                   NUM_IWL_RXON_CTX,
-                                                   sta_id, tid);
-               }
-               break;
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-static void iwl_free_tfds_in_queue(struct iwl_trans *trans,
-                           int sta_id, int tid, int freed)
-{
-       lockdep_assert_held(&trans->shrd->sta_lock);
-
-       if (trans->shrd->tid_data[sta_id][tid].tfds_in_queue >= freed)
-               trans->shrd->tid_data[sta_id][tid].tfds_in_queue -= freed;
-       else {
-               IWL_DEBUG_TX(trans, "free more than tfds_in_queue (%u:%d)\n",
-                       trans->shrd->tid_data[sta_id][tid].tfds_in_queue,
-                       freed);
-               trans->shrd->tid_data[sta_id][tid].tfds_in_queue = 0;
-       }
-}
-
-static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid,
+static int iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid,
                      int txq_id, int ssn, u32 status,
                      struct sk_buff_head *skbs)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id];
-       enum iwl_agg_state agg_state;
        /* n_bd is usually 256 => n_bd - 1 = 0xff */
        int tfd_num = ssn & (txq->q.n_bd - 1);
        int freed = 0;
-       bool cond;
 
        txq->time_stamp = jiffies;
 
-       if (txq->sched_retry) {
-               agg_state =
-                       trans->shrd->tid_data[txq->sta_id][txq->tid].agg.state;
-               cond = (agg_state != IWL_EMPTYING_HW_QUEUE_DELBA);
-       } else {
-               cond = (status != TX_STATUS_FAIL_PASSIVE_NO_RX);
+       if (unlikely(txq_id >= IWLAGN_FIRST_AMPDU_QUEUE &&
+                    txq_id != trans_pcie->agg_txq[sta_id][tid])) {
+               /*
+                * FIXME: this is a uCode bug which need to be addressed,
+                * log the information and return for now.
+                * Since it is can possibly happen very often and in order
+                * not to fill the syslog, don't use IWL_ERR or IWL_WARN
+                */
+               IWL_DEBUG_TX_QUEUES(trans, "Bad queue mapping txq_id %d, "
+                       "agg_txq[sta_id[tid] %d", txq_id,
+                       trans_pcie->agg_txq[sta_id][tid]);
+               return 1;
        }
 
        if (txq->q.read_ptr != tfd_num) {
@@ -1361,12 +1280,12 @@ static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid,
                                txq_id, iwl_get_queue_ac(txq), txq->q.read_ptr,
                                tfd_num, ssn);
                freed = iwl_tx_queue_reclaim(trans, txq_id, tfd_num, skbs);
-               if (iwl_queue_space(&txq->q) > txq->q.low_mark && cond)
+               if (iwl_queue_space(&txq->q) > txq->q.low_mark &&
+                  (!txq->sched_retry ||
+                  status != TX_STATUS_FAIL_PASSIVE_NO_RX))
                        iwl_wake_queue(trans, txq, "Packets reclaimed");
        }
-
-       iwl_free_tfds_in_queue(trans, sta_id, tid, freed);
-       iwlagn_txq_check_empty(trans, sta_id, tid, txq_id);
+       return 0;
 }
 
 static void iwl_trans_pcie_free(struct iwl_trans *trans)
This page took 0.080287 seconds and 5 git commands to generate.