]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/net/wireless/iwlwifi/iwl-agn-tx.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / net / wireless / iwlwifi / iwl-agn-tx.c
index 2b078a995729a0bbaa2ccc311fea83fa74bb7e19..24a11b8f73bc1f707de2a5b528e61d7c7b4c6b82 100644 (file)
  */
 
 static const u8 tid_to_ac[] = {
-       /* this matches the mac80211 numbers */
-       2, 3, 3, 2, 1, 1, 0, 0
+       IEEE80211_AC_BE,
+       IEEE80211_AC_BK,
+       IEEE80211_AC_BK,
+       IEEE80211_AC_BE,
+       IEEE80211_AC_VI,
+       IEEE80211_AC_VI,
+       IEEE80211_AC_VO,
+       IEEE80211_AC_VO
 };
 
 static inline int get_ac_from_tid(u16 tid)
@@ -518,11 +524,11 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
        struct iwl_cmd_meta *out_meta;
        struct iwl_tx_cmd *tx_cmd;
        struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
-       int swq_id, txq_id;
+       int txq_id;
        dma_addr_t phys_addr;
        dma_addr_t txcmd_phys;
        dma_addr_t scratch_phys;
-       u16 len, len_org, firstlen, secondlen;
+       u16 len, firstlen, secondlen;
        u16 seq_number = 0;
        __le16 fc;
        u8 hdr_len;
@@ -531,6 +537,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
        u8 tid = 0;
        u8 *qc = NULL;
        unsigned long flags;
+       bool is_agg = false;
 
        if (info->control.vif)
                ctx = iwl_rxon_ctx_from_vif(info->control.vif);
@@ -567,8 +574,8 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
        if (sta)
                sta_priv = (void *)sta->drv_priv;
 
-       if (sta_priv && sta_priv->asleep) {
-               WARN_ON(!(info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE));
+       if (sta_priv && sta_priv->asleep &&
+           (info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE)) {
                /*
                 * This sends an asynchronous command to the device,
                 * but we can rely on it being processed before the
@@ -616,11 +623,11 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
                if (info->flags & IEEE80211_TX_CTL_AMPDU &&
                    priv->stations[sta_id].tid[tid].agg.state == IWL_AGG_ON) {
                        txq_id = priv->stations[sta_id].tid[tid].agg.txq_id;
+                       is_agg = true;
                }
        }
 
        txq = &priv->txq[txq_id];
-       swq_id = txq->swq_id;
        q = &txq->q;
 
        if (unlikely(iwl_queue_space(q) < q->high_mark)) {
@@ -687,30 +694,23 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
         */
        len = sizeof(struct iwl_tx_cmd) +
                sizeof(struct iwl_cmd_header) + hdr_len;
-
-       len_org = len;
-       firstlen = len = (len + 3) & ~3;
-
-       if (len_org != len)
-               len_org = 1;
-       else
-               len_org = 0;
+       firstlen = (len + 3) & ~3;
 
        /* Tell NIC about any 2-byte padding after MAC header */
-       if (len_org)
+       if (firstlen != len)
                tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK;
 
        /* Physical address of this Tx command's header (not MAC header!),
         * within command buffer array. */
        txcmd_phys = pci_map_single(priv->pci_dev,
-                                   &out_cmd->hdr, len,
+                                   &out_cmd->hdr, firstlen,
                                    PCI_DMA_BIDIRECTIONAL);
        dma_unmap_addr_set(out_meta, mapping, txcmd_phys);
-       dma_unmap_len_set(out_meta, len, len);
+       dma_unmap_len_set(out_meta, len, firstlen);
        /* Add buffer containing Tx command and MAC(!) header to TFD's
         * first entry */
        priv->cfg->ops->lib->txq_attach_buf_to_tfd(priv, txq,
-                                                  txcmd_phys, len, 1, 0);
+                                                  txcmd_phys, firstlen, 1, 0);
 
        if (!ieee80211_has_morefrags(hdr->frame_control)) {
                txq->need_update = 1;
@@ -721,23 +721,21 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
 
        /* Set up TFD's 2nd entry to point directly to remainder of skb,
         * if any (802.11 null frames have no payload). */
-       secondlen = len = skb->len - hdr_len;
-       if (len) {
+       secondlen = skb->len - hdr_len;
+       if (secondlen > 0) {
                phys_addr = pci_map_single(priv->pci_dev, skb->data + hdr_len,
-                                          len, PCI_DMA_TODEVICE);
+                                          secondlen, PCI_DMA_TODEVICE);
                priv->cfg->ops->lib->txq_attach_buf_to_tfd(priv, txq,
-                                                          phys_addr, len,
+                                                          phys_addr, secondlen,
                                                           0, 0);
        }
 
        scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) +
                                offsetof(struct iwl_tx_cmd, scratch);
 
-       len = sizeof(struct iwl_tx_cmd) +
-               sizeof(struct iwl_cmd_header) + hdr_len;
        /* take back ownership of DMA buffer to enable update */
        pci_dma_sync_single_for_cpu(priv->pci_dev, txcmd_phys,
-                                   len, PCI_DMA_BIDIRECTIONAL);
+                                   firstlen, PCI_DMA_BIDIRECTIONAL);
        tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys);
        tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys);
 
@@ -753,7 +751,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
                                                     le16_to_cpu(tx_cmd->len));
 
        pci_dma_sync_single_for_device(priv->pci_dev, txcmd_phys,
-                                      len, PCI_DMA_BIDIRECTIONAL);
+                                      firstlen, PCI_DMA_BIDIRECTIONAL);
 
        trace_iwlwifi_dev_tx(priv,
                             &((struct iwl_tfd *)txq->tfds)[txq->q.write_ptr],
@@ -773,8 +771,14 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
         * whether or not we should update the write pointer.
         */
 
-       /* avoid atomic ops if it isn't an associated client */
-       if (sta_priv && sta_priv->client)
+       /*
+        * Avoid atomic ops if it isn't an associated client.
+        * Also, if this is a packet for aggregation, don't
+        * increase the counter because the ucode will stop
+        * aggregation queues when their respective station
+        * goes to sleep.
+        */
+       if (sta_priv && sta_priv->client && !is_agg)
                atomic_inc(&sta_priv->pending_frames);
 
        if ((iwl_queue_space(q) < q->high_mark) && priv->mac80211_registered) {
@@ -784,7 +788,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
                        iwl_txq_update_write_ptr(priv, txq);
                        spin_unlock_irqrestore(&priv->lock, flags);
                } else {
-                       iwl_stop_queue(priv, txq->swq_id);
+                       iwl_stop_queue(priv, txq);
                }
        }
 
@@ -1013,7 +1017,7 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
        tid_data = &priv->stations[sta_id].tid[tid];
        *ssn = SEQ_TO_SN(tid_data->seq_number);
        tid_data->agg.txq_id = txq_id;
-       priv->txq[txq_id].swq_id = iwl_virtual_agg_queue_num(get_ac_from_tid(tid), txq_id);
+       iwl_set_swq_id(&priv->txq[txq_id], get_ac_from_tid(tid), txq_id);
        spin_unlock_irqrestore(&priv->sta_lock, flags);
 
        ret = priv->cfg->ops->lib->txq_agg_enable(priv, txq_id, tx_fifo,
@@ -1153,14 +1157,15 @@ int iwlagn_txq_check_empty(struct iwl_priv *priv,
        return 0;
 }
 
-static void iwlagn_tx_status(struct iwl_priv *priv, struct iwl_tx_info *tx_info)
+static void iwlagn_non_agg_tx_status(struct iwl_priv *priv,
+                                    struct iwl_rxon_context *ctx,
+                                    const u8 *addr1)
 {
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx_info->skb->data;
        struct ieee80211_sta *sta;
        struct iwl_station_priv *sta_priv;
 
        rcu_read_lock();
-       sta = ieee80211_find_sta(tx_info->ctx->vif, hdr->addr1);
+       sta = ieee80211_find_sta(ctx->vif, addr1);
        if (sta) {
                sta_priv = (void *)sta->drv_priv;
                /* avoid atomic ops if this isn't a client */
@@ -1169,6 +1174,15 @@ static void iwlagn_tx_status(struct iwl_priv *priv, struct iwl_tx_info *tx_info)
                        ieee80211_sta_block_awake(priv->hw, sta, false);
        }
        rcu_read_unlock();
+}
+
+static void iwlagn_tx_status(struct iwl_priv *priv, struct iwl_tx_info *tx_info,
+                            bool is_agg)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx_info->skb->data;
+
+       if (!is_agg)
+               iwlagn_non_agg_tx_status(priv, tx_info->ctx, hdr->addr1);
 
        ieee80211_tx_status_irqsafe(priv->hw, tx_info->skb);
 }
@@ -1193,7 +1207,8 @@ int iwlagn_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index)
             q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
 
                tx_info = &txq->txb[txq->q.read_ptr];
-               iwlagn_tx_status(priv, tx_info);
+               iwlagn_tx_status(priv, tx_info,
+                                txq_id >= IWLAGN_FIRST_AMPDU_QUEUE);
 
                hdr = (struct ieee80211_hdr *)tx_info->skb->data;
                if (hdr && ieee80211_is_data_qos(hdr->frame_control))
@@ -1222,7 +1237,6 @@ static int iwlagn_tx_status_reply_compressed_ba(struct iwl_priv *priv,
        int i, sh, ack;
        u16 seq_ctl = le16_to_cpu(ba_resp->seq_ctl);
        u16 scd_flow = le16_to_cpu(ba_resp->scd_flow);
-       u64 bitmap, sent_bitmap;
        int successes = 0;
        struct ieee80211_tx_info *info;
 
@@ -1241,40 +1255,68 @@ static int iwlagn_tx_status_reply_compressed_ba(struct iwl_priv *priv,
        if (sh < 0) /* tbw something is wrong with indices */
                sh += 0x100;
 
-       /* don't use 64-bit values for now */
-       bitmap = le64_to_cpu(ba_resp->bitmap) >> sh;
-
        if (agg->frame_count > (64 - sh)) {
                IWL_DEBUG_TX_REPLY(priv, "more frames than bitmap size");
                return -1;
        }
+       if (!priv->cfg->base_params->no_agg_framecnt_info && ba_resp->txed) {
+               /*
+                * sent and ack information provided by uCode
+                * use it instead of figure out ourself
+                */
+               if (ba_resp->txed_2_done > ba_resp->txed) {
+                       IWL_DEBUG_TX_REPLY(priv,
+                               "bogus sent(%d) and ack(%d) count\n",
+                               ba_resp->txed, ba_resp->txed_2_done);
+                       /*
+                        * set txed_2_done = txed,
+                        * so it won't impact rate scale
+                        */
+                       ba_resp->txed = ba_resp->txed_2_done;
+               }
+               IWL_DEBUG_HT(priv, "agg frames sent:%d, acked:%d\n",
+                               ba_resp->txed, ba_resp->txed_2_done);
+       } else {
+               u64 bitmap, sent_bitmap;
+
+               /* don't use 64-bit values for now */
+               bitmap = le64_to_cpu(ba_resp->bitmap) >> sh;
+
+               /* check for success or failure according to the
+                * transmitted bitmap and block-ack bitmap */
+               sent_bitmap = bitmap & agg->bitmap;
+
+               /* For each frame attempted in aggregation,
+                * update driver's record of tx frame's status. */
+               i = 0;
+               while (sent_bitmap) {
+                       ack = sent_bitmap & 1ULL;
+                       successes += ack;
+                       IWL_DEBUG_TX_REPLY(priv, "%s ON i=%d idx=%d raw=%d\n",
+                               ack ? "ACK" : "NACK", i,
+                               (agg->start_idx + i) & 0xff,
+                               agg->start_idx + i);
+                       sent_bitmap >>= 1;
+                       ++i;
+               }
 
-       /* check for success or failure according to the
-        * transmitted bitmap and block-ack bitmap */
-       sent_bitmap = bitmap & agg->bitmap;
-
-       /* For each frame attempted in aggregation,
-        * update driver's record of tx frame's status. */
-       i = 0;
-       while (sent_bitmap) {
-               ack = sent_bitmap & 1ULL;
-               successes += ack;
-               IWL_DEBUG_TX_REPLY(priv, "%s ON i=%d idx=%d raw=%d\n",
-                       ack ? "ACK" : "NACK", i, (agg->start_idx + i) & 0xff,
-                       agg->start_idx + i);
-               sent_bitmap >>= 1;
-               ++i;
+               IWL_DEBUG_TX_REPLY(priv, "Bitmap %llx\n",
+                                  (unsigned long long)bitmap);
        }
 
        info = IEEE80211_SKB_CB(priv->txq[scd_flow].txb[agg->start_idx].skb);
        memset(&info->status, 0, sizeof(info->status));
        info->flags |= IEEE80211_TX_STAT_ACK;
        info->flags |= IEEE80211_TX_STAT_AMPDU;
-       info->status.ampdu_ack_len = successes;
-       info->status.ampdu_len = agg->frame_count;
-       iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags, info);
+       if (!priv->cfg->base_params->no_agg_framecnt_info && ba_resp->txed) {
+               info->status.ampdu_ack_len = ba_resp->txed_2_done;
+               info->status.ampdu_len = ba_resp->txed;
 
-       IWL_DEBUG_TX_REPLY(priv, "Bitmap %llx\n", (unsigned long long)bitmap);
+       } else {
+               info->status.ampdu_ack_len = successes;
+               info->status.ampdu_len = agg->frame_count;
+       }
+       iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags, info);
 
        return 0;
 }
@@ -1385,7 +1427,7 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
                if ((iwl_queue_space(&txq->q) > txq->q.low_mark) &&
                    priv->mac80211_registered &&
                    (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA))
-                       iwl_wake_queue(priv, txq->swq_id);
+                       iwl_wake_queue(priv, txq);
 
                iwlagn_txq_check_empty(priv, sta_id, tid, scd_flow);
        }