iwlwifi: more status bit factoring
[pandora-kernel.git] / drivers / net / wireless / iwlwifi / iwl-agn-tx.c
index df1540c..9f22454 100644 (file)
@@ -2,7 +2,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -74,8 +74,8 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv,
        else if (ieee80211_is_back_req(fc))
                tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK;
        else if (info->band == IEEE80211_BAND_2GHZ &&
-                priv->cfg->bt_params &&
-                priv->cfg->bt_params->advanced_bt_coexist &&
+                cfg(priv)->bt_params &&
+                cfg(priv)->bt_params->advanced_bt_coexist &&
                 (ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) ||
                 ieee80211_is_reassoc_req(fc) ||
                 skb->protocol == cpu_to_be16(ETH_P_PAE)))
@@ -91,6 +91,7 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv,
                tx_cmd->tid_tspec = qc[0] & 0xf;
                tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK;
        } else {
+               tx_cmd->tid_tspec = IWL_TID_NON_QOS;
                if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)
                        tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
                else
@@ -125,7 +126,7 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
        u8 data_retry_limit;
        u8 rate_plcp;
 
-       if (priv->shrd->wowlan) {
+       if (priv->wowlan) {
                rts_retry_limit = IWLAGN_LOW_RETRY_LIMIT;
                data_retry_limit = IWLAGN_LOW_RETRY_LIMIT;
        } else {
@@ -151,7 +152,7 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
        if (ieee80211_is_data(fc)) {
                tx_cmd->initial_rate_index = 0;
                tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK;
-#ifdef CONFIG_IWLWIFI_DEVICE_SVTOOL
+#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE
                if (priv->tm_fixed_rate) {
                        /*
                         * rate overwrite by testmode
@@ -164,7 +165,8 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
                }
 #endif
                return;
-       }
+       } else if (ieee80211_is_back_req(fc))
+               tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK;
 
        /**
         * If the current TX rate stored in mac80211 has the MCS bit set, it's
@@ -190,8 +192,8 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
                rate_flags |= RATE_MCS_CCK_MSK;
 
        /* Set up antennas */
-        if (priv->cfg->bt_params &&
-            priv->cfg->bt_params->advanced_bt_coexist &&
+        if (cfg(priv)->bt_params &&
+            cfg(priv)->bt_params->advanced_bt_coexist &&
             priv->bt_full_concurrent) {
                /* operated as 1x1 in full concurrency mode */
                priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
@@ -206,10 +208,9 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
 }
 
 static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv,
-                                     struct ieee80211_tx_info *info,
-                                     struct iwl_tx_cmd *tx_cmd,
-                                     struct sk_buff *skb_frag,
-                                     int sta_id)
+                                        struct ieee80211_tx_info *info,
+                                        struct iwl_tx_cmd *tx_cmd,
+                                        struct sk_buff *skb_frag)
 {
        struct ieee80211_key_conf *keyconf = info->control.hw_key;
 
@@ -247,6 +248,35 @@ static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv,
        }
 }
 
+/**
+ * iwl_sta_id_or_broadcast - return sta_id or broadcast sta
+ * @context: the current context
+ * @sta: mac80211 station
+ *
+ * In certain circumstances mac80211 passes a station pointer
+ * that may be %NULL, for example during TX or key setup. In
+ * that case, we need to use the broadcast station, so this
+ * inline wraps that pattern.
+ */
+static int iwl_sta_id_or_broadcast(struct iwl_rxon_context *context,
+                                  struct ieee80211_sta *sta)
+{
+       int sta_id;
+
+       if (!sta)
+               return context->bcast_sta_id;
+
+       sta_id = iwl_sta_id(sta);
+
+       /*
+        * mac80211 should not be passing a partially
+        * initialised station!
+        */
+       WARN_ON(sta_id == IWL_INVALID_STATION);
+
+       return sta_id;
+}
+
 /*
  * start REPLY_TX command process
  */
@@ -258,19 +288,16 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
        struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
        struct iwl_device_cmd *dev_cmd = NULL;
        struct iwl_tx_cmd *tx_cmd;
-
        __le16 fc;
        u8 hdr_len;
-       u16 len;
-       u8 sta_id;
-       unsigned long flags;
+       u16 len, seq_number = 0;
+       u8 sta_id, tid = IWL_MAX_TID_COUNT;
        bool is_agg = false;
 
        if (info->control.vif)
                ctx = iwl_rxon_ctx_from_vif(info->control.vif);
 
-       spin_lock_irqsave(&priv->shrd->lock, flags);
-       if (iwl_is_rfkill(priv->shrd)) {
+       if (iwl_is_rfkill(priv)) {
                IWL_DEBUG_DROP(priv, "Dropping - RF KILL\n");
                goto drop_unlock_priv;
        }
@@ -286,6 +313,19 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
                IWL_DEBUG_TX(priv, "Sending REASSOC frame\n");
 #endif
 
+       if (unlikely(ieee80211_is_probe_resp(fc))) {
+               struct iwl_wipan_noa_data *noa_data =
+                       rcu_dereference(priv->noa_data);
+
+               if (noa_data &&
+                   pskb_expand_head(skb, 0, noa_data->length,
+                                    GFP_ATOMIC) == 0) {
+                       memcpy(skb_put(skb, noa_data->length),
+                              noa_data->data, noa_data->length);
+                       hdr = (struct ieee80211_hdr *)skb->data;
+               }
+       }
+
        hdr_len = ieee80211_hdrlen(fc);
 
        /* For management frames use broadcast id to do not break aggregation */
@@ -293,7 +333,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
                sta_id = ctx->bcast_sta_id;
        else {
                /* Find index into station table for destination station */
-               sta_id = iwl_sta_id_or_broadcast(priv, ctx, info->control.sta);
+               sta_id = iwl_sta_id_or_broadcast(ctx, info->control.sta);
                if (sta_id == IWL_INVALID_STATION) {
                        IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n",
                                       hdr->addr1);
@@ -307,7 +347,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
                sta_priv = (void *)info->control.sta->drv_priv;
 
        if (sta_priv && sta_priv->asleep &&
-           (info->flags & IEEE80211_TX_CTL_POLL_RESPONSE)) {
+           (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) {
                /*
                 * This sends an asynchronous command to the device,
                 * but we can rely on it being processed before the
@@ -316,6 +356,10 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
                 * counter.
                 * For now set the counter to just 1 since we do not
                 * support uAPSD yet.
+                *
+                * FIXME: If we get two non-bufferable frames one
+                * after the other, we might only send out one of
+                * them because this is racy.
                 */
                iwl_sta_modify_sleep_tx_count(priv, sta_id, 1);
        }
@@ -323,13 +367,10 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
        if (info->flags & IEEE80211_TX_CTL_AMPDU)
                is_agg = true;
 
-       /* irqs already disabled/saved above when locking priv->shrd->lock */
-       spin_lock(&priv->shrd->sta_lock);
-
-       dev_cmd = kmem_cache_alloc(priv->tx_cmd_pool, GFP_ATOMIC);
+       dev_cmd = kmem_cache_alloc(iwl_tx_cmd_pool, GFP_ATOMIC);
 
        if (unlikely(!dev_cmd))
-               goto drop_unlock_sta;
+               goto drop_unlock_priv;
 
        memset(dev_cmd, 0, sizeof(*dev_cmd));
        tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload;
@@ -339,7 +380,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
        tx_cmd->len = cpu_to_le16(len);
 
        if (info->control.hw_key)
-               iwlagn_tx_cmd_build_hwcrypto(priv, info, tx_cmd, skb, sta_id);
+               iwlagn_tx_cmd_build_hwcrypto(priv, info, tx_cmd, skb);
 
        /* TODO need this for burst mode later on */
        iwlagn_tx_cmd_build_basic(priv, skb, tx_cmd, info, hdr, sta_id);
@@ -354,11 +395,54 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
        info->driver_data[0] = ctx;
        info->driver_data[1] = dev_cmd;
 
-       if (iwl_trans_tx(trans(priv), skb, dev_cmd, ctx->ctxid, sta_id))
+       spin_lock(&priv->sta_lock);
+
+       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;
+               if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
+                       goto drop_unlock_sta;
+               tid_data = &priv->tid_data[sta_id][tid];
+
+               /* aggregation is on for this <sta,tid> */
+               if (info->flags & IEEE80211_TX_CTL_AMPDU &&
+                   tid_data->agg.state != IWL_AGG_ON) {
+                       IWL_ERR(priv, "TX_CTL_AMPDU while not in AGG:"
+                               " Tx flags = 0x%08x, agg.state = %d",
+                               info->flags, tid_data->agg.state);
+                       IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d",
+                               sta_id, tid, SEQ_TO_SN(tid_data->seq_number));
+                       goto drop_unlock_sta;
+               }
+
+               /* We can receive packets from the stack in IWL_AGG_{ON,OFF}
+                * only. Check this here.
+                */
+               if (WARN_ONCE(tid_data->agg.state != IWL_AGG_ON &&
+                   tid_data->agg.state != IWL_AGG_OFF,
+                   "Tx while agg.state = %d", tid_data->agg.state))
+                       goto drop_unlock_sta;
+
+               seq_number = tid_data->seq_number;
+               seq_number &= IEEE80211_SCTL_SEQ;
+               hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
+               hdr->seq_ctrl |= cpu_to_le16(seq_number);
+               seq_number += 0x10;
+       }
+
+       /* Copy MAC header from skb into command buffer */
+       memcpy(tx_cmd->hdr, hdr, hdr_len);
+
+       if (iwl_trans_tx(trans(priv), skb, dev_cmd, ctx->ctxid, sta_id, tid))
                goto drop_unlock_sta;
 
-       spin_unlock(&priv->shrd->sta_lock);
-       spin_unlock_irqrestore(&priv->shrd->lock, flags);
+       if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc) &&
+           !ieee80211_has_morefrags(fc))
+               priv->tid_data[sta_id][tid].seq_number = seq_number;
+
+       spin_unlock(&priv->sta_lock);
 
        /*
         * Avoid atomic ops if it isn't an associated client.
@@ -374,17 +458,81 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
 
 drop_unlock_sta:
        if (dev_cmd)
-               kmem_cache_free(priv->tx_cmd_pool, dev_cmd);
-       spin_unlock(&priv->shrd->sta_lock);
+               kmem_cache_free(iwl_tx_cmd_pool, dev_cmd);
+       spin_unlock(&priv->sta_lock);
 drop_unlock_priv:
-       spin_unlock_irqrestore(&priv->shrd->lock, flags);
        return -1;
 }
 
+int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
+                       struct ieee80211_sta *sta, u16 tid)
+{
+       struct iwl_tid_data *tid_data;
+       int sta_id;
+
+       sta_id = iwl_sta_id(sta);
+
+       if (sta_id == IWL_INVALID_STATION) {
+               IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid);
+               return -ENXIO;
+       }
+
+       spin_lock_bh(&priv->sta_lock);
+
+       tid_data = &priv->tid_data[sta_id][tid];
+
+       switch (priv->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(priv, "AGG stop before setup done\n");
+               goto turn_off;
+       case IWL_AGG_ON:
+               break;
+       default:
+               IWL_WARN(priv, "Stopping AGG while state not ON "
+                        "or starting for %d on %d (%d)\n", sta_id, tid,
+                        priv->tid_data[sta_id][tid].agg.state);
+               spin_unlock_bh(&priv->sta_lock);
+               return 0;
+       }
+
+       tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number);
+
+       /* There are still packets for this RA / TID in the HW */
+       if (tid_data->agg.ssn != tid_data->next_reclaimed) {
+               IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, "
+                                   "next_recl = %d",
+                                   tid_data->agg.ssn,
+                                   tid_data->next_reclaimed);
+               priv->tid_data[sta_id][tid].agg.state =
+                       IWL_EMPTYING_HW_QUEUE_DELBA;
+               spin_unlock_bh(&priv->sta_lock);
+               return 0;
+       }
+
+       IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d",
+                           tid_data->agg.ssn);
+turn_off:
+       priv->tid_data[sta_id][tid].agg.state = IWL_AGG_OFF;
+
+       spin_unlock_bh(&priv->sta_lock);
+
+       iwl_trans_tx_agg_disable(trans(priv), sta_id, tid);
+
+       ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+
+       return 0;
+}
+
 int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
                        struct ieee80211_sta *sta, u16 tid, u16 *ssn)
 {
-       struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
+       struct iwl_tid_data *tid_data;
        int sta_id;
        int ret;
 
@@ -399,7 +547,7 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
        if (unlikely(tid >= IWL_MAX_TID_COUNT))
                return -EINVAL;
 
-       if (priv->shrd->tid_data[sta_id][tid].agg.state != IWL_AGG_OFF) {
+       if (priv->tid_data[sta_id][tid].agg.state != IWL_AGG_OFF) {
                IWL_ERR(priv, "Start AGG when state is not IWL_AGG_OFF !\n");
                return -ENXIO;
        }
@@ -408,27 +556,134 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
        if (ret)
                return ret;
 
-       ret = iwl_trans_tx_agg_alloc(trans(priv), vif_priv->ctx->ctxid, sta_id,
-                                    tid, ssn);
+       spin_lock_bh(&priv->sta_lock);
+
+       tid_data = &priv->tid_data[sta_id][tid];
+       tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number);
+
+       *ssn = tid_data->agg.ssn;
+
+       ret = iwl_trans_tx_agg_alloc(trans(priv), sta_id, tid);
+       if (ret) {
+               spin_unlock_bh(&priv->sta_lock);
+               return ret;
+       }
+
+       if (*ssn == tid_data->next_reclaimed) {
+               IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d",
+                                   tid_data->agg.ssn);
+               tid_data->agg.state = IWL_AGG_ON;
+               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+       } else {
+               IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, "
+                                   "next_reclaimed = %d",
+                                   tid_data->agg.ssn,
+                                   tid_data->next_reclaimed);
+               tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA;
+       }
+
+       spin_unlock_bh(&priv->sta_lock);
 
        return ret;
 }
 
-int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
-                      struct ieee80211_sta *sta, u16 tid)
+int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif,
+                       struct ieee80211_sta *sta, u16 tid, u8 buf_size)
 {
-       int sta_id;
-       struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
+       struct iwl_station_priv *sta_priv = (void *) sta->drv_priv;
+       struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
+       u16 ssn;
 
-       sta_id = iwl_sta_id(sta);
+       buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF);
 
-       if (sta_id == IWL_INVALID_STATION) {
-               IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid);
-               return -ENXIO;
+       spin_lock_bh(&priv->sta_lock);
+       ssn = priv->tid_data[sta_priv->sta_id][tid].agg.ssn;
+       spin_unlock_bh(&priv->sta_lock);
+
+       iwl_trans_tx_agg_setup(trans(priv), ctx->ctxid, sta_priv->sta_id, tid,
+                              buf_size, ssn);
+
+       /*
+        * If the limit is 0, then it wasn't initialised yet,
+        * use the default. We can do that since we take the
+        * minimum below, and we don't want to go above our
+        * default due to hardware restrictions.
+        */
+       if (sta_priv->max_agg_bufsize == 0)
+               sta_priv->max_agg_bufsize =
+                       LINK_QUAL_AGG_FRAME_LIMIT_DEF;
+
+       /*
+        * Even though in theory the peer could have different
+        * aggregation reorder buffer sizes for different sessions,
+        * our ucode doesn't allow for that and has a global limit
+        * for each station. Therefore, use the minimum of all the
+        * aggregation sessions and our default value.
+        */
+       sta_priv->max_agg_bufsize =
+               min(sta_priv->max_agg_bufsize, buf_size);
+
+       if (hw_params(priv).use_rts_for_aggregation) {
+               /*
+                * switch to RTS/CTS if it is the prefer protection
+                * method for HT traffic
+                */
+
+               sta_priv->lq_sta.lq.general_params.flags |=
+                       LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK;
        }
+       priv->agg_tids_count++;
+       IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n",
+                    priv->agg_tids_count);
+
+       sta_priv->lq_sta.lq.agg_params.agg_frame_cnt_limit =
+               sta_priv->max_agg_bufsize;
+
+       IWL_DEBUG_HT(priv, "Tx aggregation enabled on ra = %pM tid = %d\n",
+                sta->addr, tid);
 
-       return iwl_trans_tx_agg_disable(trans(priv), vif_priv->ctx->ctxid,
-                                       sta_id, tid);
+       return iwl_send_lq_cmd(priv, ctx,
+                       &sta_priv->lq_sta.lq, CMD_ASYNC, false);
+}
+
+static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid)
+{
+       struct iwl_tid_data *tid_data = &priv->tid_data[sta_id][tid];
+       enum iwl_rxon_context_id ctx;
+       struct ieee80211_vif *vif;
+       u8 *addr;
+
+       lockdep_assert_held(&priv->sta_lock);
+
+       addr = priv->stations[sta_id].sta.sta.addr;
+       ctx = priv->stations[sta_id].ctxid;
+       vif = priv->contexts[ctx].vif;
+
+       switch (priv->tid_data[sta_id][tid].agg.state) {
+       case IWL_EMPTYING_HW_QUEUE_DELBA:
+               /* There are no packets for this RA / TID in the HW any more */
+               if (tid_data->agg.ssn == tid_data->next_reclaimed) {
+                       IWL_DEBUG_TX_QUEUES(priv,
+                               "Can continue DELBA flow ssn = next_recl ="
+                               " %d", tid_data->next_reclaimed);
+                       iwl_trans_tx_agg_disable(trans(priv), sta_id, tid);
+                       tid_data->agg.state = IWL_AGG_OFF;
+                       ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid);
+               }
+               break;
+       case IWL_EMPTYING_HW_QUEUE_ADDBA:
+               /* There are no packets for this RA / TID in the HW any more */
+               if (tid_data->agg.ssn == tid_data->next_reclaimed) {
+                       IWL_DEBUG_TX_QUEUES(priv,
+                               "Can continue ADDBA flow ssn = next_recl ="
+                               " %d", tid_data->next_reclaimed);
+                       tid_data->agg.state = IWL_AGG_ON;
+                       ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid);
+               }
+               break;
+       default:
+               break;
+       }
 }
 
 static void iwlagn_non_agg_tx_status(struct iwl_priv *priv,
@@ -568,10 +823,12 @@ static void iwl_rx_reply_tx_agg(struct iwl_priv *priv,
                IWLAGN_TX_RES_TID_POS;
        int sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >>
                IWLAGN_TX_RES_RA_POS;
-       struct iwl_ht_agg *agg = &priv->shrd->tid_data[sta_id][tid].agg;
+       struct iwl_ht_agg *agg = &priv->tid_data[sta_id][tid].agg;
        u32 status = le16_to_cpu(tx_resp->status.status);
        int i;
 
+       WARN_ON(tid == IWL_TID_NON_QOS);
+
        if (agg->wait_for_ba)
                IWL_DEBUG_TX_REPLY(priv,
                        "got tx response w/o block-ack\n");
@@ -584,8 +841,8 @@ static void iwl_rx_reply_tx_agg(struct iwl_priv *priv,
         * notification again.
         */
        if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 &&
-           priv->cfg->bt_params &&
-           priv->cfg->bt_params->advanced_bt_coexist) {
+           cfg(priv)->bt_params &&
+           cfg(priv)->bt_params->advanced_bt_coexist) {
                IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n");
        }
 
@@ -743,27 +1000,26 @@ static void iwl_check_abort_status(struct iwl_priv *priv,
 {
        if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) {
                IWL_ERR(priv, "Tx flush command to flush out all frames\n");
-               if (!test_bit(STATUS_EXIT_PENDING, &priv->shrd->status))
-                       queue_work(priv->shrd->workqueue, &priv->tx_flush);
+               if (!test_bit(STATUS_EXIT_PENDING, &priv->status))
+                       queue_work(priv->workqueue, &priv->tx_flush);
        }
 }
 
-int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb,
+int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
                               struct iwl_device_cmd *cmd)
 {
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
        u16 sequence = le16_to_cpu(pkt->hdr.sequence);
        int txq_id = SEQ_TO_QUEUE(sequence);
        int cmd_index __maybe_unused = SEQ_TO_INDEX(sequence);
-       struct iwlagn_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
+       struct iwlagn_tx_resp *tx_resp = (void *)pkt->data;
        struct ieee80211_hdr *hdr;
        u32 status = le16_to_cpu(tx_resp->status.status);
-       u32 ssn = iwlagn_get_scd_ssn(tx_resp);
+       u16 ssn = iwlagn_get_scd_ssn(tx_resp);
        int tid;
        int sta_id;
        int freed;
        struct ieee80211_tx_info *info;
-       unsigned long flags;
        struct sk_buff_head skbs;
        struct sk_buff *skb;
        struct iwl_rxon_context *ctx;
@@ -774,19 +1030,47 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb,
        sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >>
                IWLAGN_TX_RES_RA_POS;
 
-       spin_lock_irqsave(&priv->shrd->sta_lock, flags);
+       spin_lock(&priv->sta_lock);
 
        if (is_agg)
                iwl_rx_reply_tx_agg(priv, tx_resp);
 
+       __skb_queue_head_init(&skbs);
+
        if (tx_resp->frame_count == 1) {
-               __skb_queue_head_init(&skbs);
+               u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl);
+               next_reclaimed = SEQ_TO_SN(next_reclaimed + 0x10);
+
+               if (is_agg) {
+                       /* If this is an aggregation queue, we can rely on the
+                        * ssn since the wifi sequence number corresponds to
+                        * the index in the TFD ring (%256).
+                        * The seq_ctl is the sequence control of the packet
+                        * to which this Tx response relates. But if there is a
+                        * hole in the bitmap of the BA we received, this Tx
+                        * response may allow to reclaim the hole and all the
+                        * subsequent packets that were already acked.
+                        * In that case, seq_ctl != ssn, and the next packet
+                        * to be reclaimed will be ssn and not seq_ctl.
+                        */
+                       next_reclaimed = ssn;
+               }
+
+               if (tid != IWL_TID_NON_QOS) {
+                       priv->tid_data[sta_id][tid].next_reclaimed =
+                               next_reclaimed;
+                       IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d",
+                                                 next_reclaimed);
+               }
+
                /*we can free until ssn % q.n_bd not inclusive */
-               iwl_trans_reclaim(trans(priv), sta_id, tid, txq_id,
-                                 ssn, status, &skbs);
+               WARN_ON(iwl_trans_reclaim(trans(priv), sta_id, tid,
+                                         txq_id, ssn, &skbs));
+               iwlagn_check_ratid_empty(priv, sta_id, tid);
                freed = 0;
-               while (!skb_queue_empty(&skbs)) {
-                       skb = __skb_dequeue(&skbs);
+
+               /* process frames */
+               skb_queue_walk(&skbs, skb) {
                        hdr = (struct ieee80211_hdr *)skb->data;
 
                        if (!ieee80211_is_data_qos(hdr->frame_control))
@@ -794,7 +1078,7 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb,
 
                        info = IEEE80211_SKB_CB(skb);
                        ctx = info->driver_data[0];
-                       kmem_cache_free(priv->tx_cmd_pool,
+                       kmem_cache_free(iwl_tx_cmd_pool,
                                        (info->driver_data[1]));
 
                        memset(&info->status, 0, sizeof(info->status));
@@ -802,8 +1086,11 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb,
                        if (status == TX_STATUS_FAIL_PASSIVE_NO_RX &&
                            iwl_is_associated_ctx(ctx) && ctx->vif &&
                            ctx->vif->type == NL80211_IFTYPE_STATION) {
-                               ctx->last_tx_rejected = true;
-                               iwl_trans_stop_queue(trans(priv), txq_id);
+                               /* block and stop all queues */
+                               priv->passive_no_rx = true;
+                               IWL_DEBUG_TX_QUEUES(priv, "stop all queues: "
+                                                   "passive channel");
+                               ieee80211_stop_queues(priv->hw);
 
                                IWL_DEBUG_TX_REPLY(priv,
                                           "TXQ %d status %s (0x%08x) "
@@ -827,8 +1114,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb,
                        if (!is_agg)
                                iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1);
 
-                       ieee80211_tx_status_irqsafe(priv->hw, skb);
-
                        freed++;
                }
 
@@ -836,7 +1121,13 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb,
        }
 
        iwl_check_abort_status(priv, tx_resp->frame_count, status);
-       spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
+       spin_unlock(&priv->sta_lock);
+
+       while (!skb_queue_empty(&skbs)) {
+               skb = __skb_dequeue(&skbs);
+               ieee80211_tx_status(priv->hw, skb);
+       }
+
        return 0;
 }
 
@@ -847,17 +1138,16 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb,
  * of frames sent via aggregation.
  */
 int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
-                                  struct iwl_rx_mem_buffer *rxb,
+                                  struct iwl_rx_cmd_buffer *rxb,
                                   struct iwl_device_cmd *cmd)
 {
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
-       struct iwl_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba;
+       struct iwl_compressed_ba_resp *ba_resp = (void *)pkt->data;
        struct iwl_ht_agg *agg;
        struct sk_buff_head reclaimed_skbs;
        struct ieee80211_tx_info *info;
        struct ieee80211_hdr *hdr;
        struct sk_buff *skb;
-       unsigned long flags;
        int sta_id;
        int tid;
        int freed;
@@ -877,28 +1167,25 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
 
        sta_id = ba_resp->sta_id;
        tid = ba_resp->tid;
-       agg = &priv->shrd->tid_data[sta_id][tid].agg;
-
-       spin_lock_irqsave(&priv->shrd->sta_lock, flags);
+       agg = &priv->tid_data[sta_id][tid].agg;
 
-       if (unlikely(agg->txq_id != scd_flow)) {
-               /*
-                * FIXME: this is a uCode bug which need to be addressed,
-                * log the information and return for now!
-                * since it is possible happen very often and in order
-                * not to fill the syslog, don't enable the logging by default
-                */
-               IWL_DEBUG_TX_REPLY(priv,
-                       "BA scd_flow %d does not match txq_id %d\n",
-                       scd_flow, agg->txq_id);
-               spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
-               return 0;
-       }
+       spin_lock(&priv->sta_lock);
 
        if (unlikely(!agg->wait_for_ba)) {
                if (unlikely(ba_resp->bitmap))
                        IWL_ERR(priv, "Received BA when not expected\n");
-               spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
+               spin_unlock(&priv->sta_lock);
+               return 0;
+       }
+
+       __skb_queue_head_init(&reclaimed_skbs);
+
+       /* Release all TFDs before the SSN, i.e. all TFDs in front of
+        * block-ack window (we assume that they've been successfully
+        * transmitted ... if not, it's too late anyway). */
+       if (iwl_trans_reclaim(trans(priv), sta_id, tid, scd_flow,
+                             ba_resp_scd_ssn, &reclaimed_skbs)) {
+               spin_unlock(&priv->sta_lock);
                return 0;
        }
 
@@ -909,11 +1196,9 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
                           ba_resp->sta_id);
        IWL_DEBUG_TX_REPLY(priv, "TID = %d, SeqCtl = %d, bitmap = 0x%llx, "
                           "scd_flow = %d, scd_ssn = %d\n",
-                          ba_resp->tid,
-                          ba_resp->seq_ctl,
+                          ba_resp->tid, le16_to_cpu(ba_resp->seq_ctl),
                           (unsigned long long)le64_to_cpu(ba_resp->bitmap),
-                          ba_resp->scd_flow,
-                          ba_resp->scd_ssn);
+                          scd_flow, ba_resp_scd_ssn);
 
        /* Mark that the expected block-ack response arrived */
        agg->wait_for_ba = false;
@@ -932,17 +1217,12 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
        IWL_DEBUG_HT(priv, "agg frames sent:%d, acked:%d\n",
                        ba_resp->txed, ba_resp->txed_2_done);
 
-       __skb_queue_head_init(&reclaimed_skbs);
+       priv->tid_data[sta_id][tid].next_reclaimed = ba_resp_scd_ssn;
 
-       /* Release all TFDs before the SSN, i.e. all TFDs in front of
-        * block-ack window (we assume that they've been successfully
-        * transmitted ... if not, it's too late anyway). */
-       iwl_trans_reclaim(trans(priv), sta_id, tid, scd_flow, ba_resp_scd_ssn,
-                         0, &reclaimed_skbs);
+       iwlagn_check_ratid_empty(priv, sta_id, tid);
        freed = 0;
-       while (!skb_queue_empty(&reclaimed_skbs)) {
 
-               skb = __skb_dequeue(&reclaimed_skbs);
+       skb_queue_walk(&reclaimed_skbs, skb) {
                hdr = (struct ieee80211_hdr *)skb->data;
 
                if (ieee80211_is_data_qos(hdr->frame_control))
@@ -951,7 +1231,7 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
                        WARN_ON_ONCE(1);
 
                info = IEEE80211_SKB_CB(skb);
-               kmem_cache_free(priv->tx_cmd_pool, (info->driver_data[1]));
+               kmem_cache_free(iwl_tx_cmd_pool, (info->driver_data[1]));
 
                if (freed == 1) {
                        /* this is the first skb we deliver in this batch */
@@ -965,10 +1245,14 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
                        iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags,
                                                    info);
                }
+       }
+
+       spin_unlock(&priv->sta_lock);
 
-               ieee80211_tx_status_irqsafe(priv->hw, skb);
+       while (!skb_queue_empty(&reclaimed_skbs)) {
+               skb = __skb_dequeue(&reclaimed_skbs);
+               ieee80211_tx_status(priv->hw, skb);
        }
 
-       spin_unlock_irqrestore(&priv->shrd->sta_lock, flags);
        return 0;
 }