if (iwl_queue_space(q) > q->low_mark && (txq_id >= 0) &&
                        (txq_id != IWL_CMD_QUEUE_NUM) &&
                        priv->mac80211_registered)
-               ieee80211_wake_queue(priv->hw, txq_id);
+               iwl_wake_queue(priv, txq_id);
 }
 
 /**
 
                            (iwl_queue_space(&txq->q) > txq->q.low_mark) &&
                            (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) {
                                if (agg->state == IWL_AGG_OFF)
-                                       ieee80211_wake_queue(priv->hw, txq_id);
+                                       iwl_wake_queue(priv, txq_id);
                                else
-                                       ieee80211_wake_queue(priv->hw,
-                                                            txq->swq_id);
+                                       iwl_wake_queue(priv, txq->swq_id);
                        }
                }
        } else {
 
                if (priv->mac80211_registered &&
                    (iwl_queue_space(&txq->q) > txq->q.low_mark))
-                       ieee80211_wake_queue(priv->hw, txq_id);
+                       iwl_wake_queue(priv, txq_id);
        }
 
        if (qc && likely(sta_id != IWL_INVALID_STATION))
 
                            (iwl_queue_space(&txq->q) > txq->q.low_mark) &&
                            (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) {
                                if (agg->state == IWL_AGG_OFF)
-                                       ieee80211_wake_queue(priv->hw, txq_id);
+                                       iwl_wake_queue(priv, txq_id);
                                else
-                                       ieee80211_wake_queue(priv->hw,
-                                                            txq->swq_id);
+                                       iwl_wake_queue(priv, txq->swq_id);
                        }
                }
        } else {
 
                if (priv->mac80211_registered &&
                    (iwl_queue_space(&txq->q) > txq->q.low_mark))
-                       ieee80211_wake_queue(priv->hw, txq_id);
+                       iwl_wake_queue(priv, txq_id);
        }
 
        if (ieee80211_is_data_qos(tx_resp->frame_ctrl))
 
 
        /* Default value; 4 EDCA QOS priorities */
        hw->queues = 4;
-       /* queues to support 11n aggregation */
-       if (priv->cfg->sku & IWL_SKU_N)
-               hw->ampdu_queues = priv->cfg->mod_params->num_of_ampdu_queues;
 
        hw->conf.beacon_int = 100;
        hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
 
        u8 key_mapping_key;
        unsigned long ucode_key_table;
 
+       /* queue refcounts */
+#define IWL_MAX_HW_QUEUES      32
+       unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)];
+       /* for each AC */
+       atomic_t queue_stop_count[4];
+
        /* Indication if ieee80211_ops->open has been called */
        u8 is_open;
 
 
        return (desc->v_addr != NULL) ? 0 : -ENOMEM;
 }
 
+/*
+ * we have 8 bits used like this:
+ *
+ * 7 6 5 4 3 2 1 0
+ * | | | | | | | |
+ * | | | | | | +-+-------- AC queue (0-3)
+ * | | | | | |
+ * | +-+-+-+-+------------ HW A-MPDU queue
+ * |
+ * +---------------------- indicates agg queue
+ */
+static inline u8 iwl_virtual_agg_queue_num(u8 ac, u8 hwq)
+{
+       BUG_ON(ac > 3);   /* only have 2 bits */
+       BUG_ON(hwq > 31); /* only have 5 bits */
+
+       return 0x80 | (hwq << 2) | ac;
+}
+
+static inline void iwl_wake_queue(struct iwl_priv *priv, u8 queue)
+{
+       u8 ac = queue;
+       u8 hwq = queue;
+
+       if (queue & 0x80) {
+               ac = queue & 3;
+               hwq = (queue >> 2) & 0x1f;
+       }
+
+       if (test_and_clear_bit(hwq, priv->queue_stopped))
+               if (atomic_dec_return(&priv->queue_stop_count[ac]) <= 0)
+                       ieee80211_wake_queue(priv->hw, ac);
+}
+
+static inline void iwl_stop_queue(struct iwl_priv *priv, u8 queue)
+{
+       u8 ac = queue;
+       u8 hwq = queue;
+
+       if (queue & 0x80) {
+               ac = queue & 3;
+               hwq = (queue >> 2) & 0x1f;
+       }
+
+       if (!test_and_set_bit(hwq, priv->queue_stopped))
+               if (atomic_inc_return(&priv->queue_stop_count[ac]) > 0)
+                       ieee80211_stop_queue(priv->hw, ac);
+}
+
+#define ieee80211_stop_queue DO_NOT_USE_ieee80211_stop_queue
+#define ieee80211_wake_queue DO_NOT_USE_ieee80211_wake_queue
+
 #endif                         /* __iwl_helpers_h__ */
 
                hdr->seq_ctrl |= cpu_to_le16(seq_number);
                seq_number += 0x10;
                /* aggregation is on for this <sta,tid> */
-               if (info->flags & IEEE80211_TX_CTL_AMPDU)
+               if (info->flags & IEEE80211_TX_CTL_AMPDU) {
                        txq_id = priv->stations[sta_id].tid[tid].agg.txq_id;
+                       swq_id = iwl_virtual_agg_queue_num(swq_id, txq_id);
+               }
                priv->stations[sta_id].tid[tid].tfds_in_queue++;
        }
 
                        iwl_txq_update_write_ptr(priv, txq);
                        spin_unlock_irqrestore(&priv->lock, flags);
                } else {
-                       ieee80211_stop_queue(priv->hw, txq->swq_id);
+                       iwl_stop_queue(priv, txq->swq_id);
                }
        }
 
                if ((iwl_queue_space(&txq->q) > txq->q.low_mark) &&
                    priv->mac80211_registered &&
                    (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA))
-                       ieee80211_wake_queue(priv->hw, txq->swq_id);
+                       iwl_wake_queue(priv, txq->swq_id);
 
                iwl_txq_check_empty(priv, sta_id, tid, scd_flow);
        }
 
                        spin_unlock_irqrestore(&priv->lock, flags);
                }
 
-               ieee80211_stop_queue(priv->hw, skb_get_queue_mapping(skb));
+               iwl_stop_queue(priv, skb_get_queue_mapping(skb));
        }
 
        return 0;
 
                        BIT(NL80211_IFTYPE_STATION) |
                        BIT(NL80211_IFTYPE_AP) |
                        BIT(NL80211_IFTYPE_MESH_POINT);
-               hw->ampdu_queues = 1;
 
                hw->flags = IEEE80211_HW_MFP_CAPABLE;
 
 
  * enum ieee80211_max_queues - maximum number of queues
  *
  * @IEEE80211_MAX_QUEUES: Maximum number of regular device queues.
- * @IEEE80211_MAX_AMPDU_QUEUES: Maximum number of queues usable
- *     for A-MPDU operation.
  */
 enum ieee80211_max_queues {
        IEEE80211_MAX_QUEUES =          4,
-       IEEE80211_MAX_AMPDU_QUEUES =    16,
 };
 
 /**
  *     data packets. WMM/QoS requires at least four, these
  *     queues need to have configurable access parameters.
  *
- * @ampdu_queues: number of available hardware transmit queues
- *     for A-MPDU packets, these have no access parameters
- *     because they're used only for A-MPDU frames. Note that
- *     mac80211 will not currently use any of the regular queues
- *     for aggregation.
- *
  * @rate_control_algorithm: rate control algorithm for this hardware.
  *     If unset (NULL), the default algorithm will be used. Must be
  *     set before calling ieee80211_register_hw().
        int vif_data_size;
        int sta_data_size;
        u16 queues;
-       u16 ampdu_queues;
        u16 max_listen_interval;
        s8 max_signal;
        u8 max_rates;
  * @get_tx_stats: Get statistics of the current TX queue status. This is used
  *     to get number of currently queued packets (queue length), maximum queue
  *     size (limit), and total number of packets sent using each TX queue
- *     (count). The 'stats' pointer points to an array that has hw->queues +
- *     hw->ampdu_queues items.
+ *     (count). The 'stats' pointer points to an array that has hw->queues
+ *     items.
  *
  * @get_tsf: Get the current TSF timer value from firmware/hardware. Currently,
  *     this is only used for IBSS mode BSSID merging and debugging. Is not a
 
 
        state = &sta->ampdu_mlme.tid_state_tx[tid];
 
-       if (local->hw.ampdu_queues) {
-               /*
-                * Pretend the driver woke the queue, just in case
-                * it disabled it before the session was stopped.
-                */
-               ieee80211_wake_queue(
-                       &local->hw, local->hw.queues + sta->tid_to_tx_q[tid]);
-       }
        *state = HT_AGG_STATE_REQ_STOP_BA_MSK |
                (initiator << HT_AGG_STATE_INITIATOR_SHIFT);
 
        struct sta_info *sta;
        struct ieee80211_sub_if_data *sdata;
        u8 *state;
-       int i, qn = -1, ret = 0;
+       int ret = 0;
        u16 start_seq_num;
 
        if (WARN_ON(!local->ops->ampdu_action))
                goto err_unlock_sta;
        }
 
-       if (hw->ampdu_queues) {
-               spin_lock(&local->queue_stop_reason_lock);
-               /* reserve a new queue for this session */
-               for (i = 0; i < local->hw.ampdu_queues; i++) {
-                       if (local->ampdu_ac_queue[i] < 0) {
-                               qn = i;
-                               local->ampdu_ac_queue[qn] =
-                                       ieee80211_ac_from_tid(tid);
-                               break;
-                       }
-               }
-               spin_unlock(&local->queue_stop_reason_lock);
-
-               if (qn < 0) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
-                       printk(KERN_DEBUG "BA request denied - "
-                              "queue unavailable for tid %d\n", tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-                       ret = -ENOSPC;
-                       goto err_unlock_sta;
-               }
-       }
-
        /*
         * While we're asking the driver about the aggregation,
         * stop the AC queue so that we don't have to worry
                                        tid);
 #endif
                ret = -ENOMEM;
-               goto err_return_queue;
+               goto err_wake_queue;
        }
 
        skb_queue_head_init(&sta->ampdu_mlme.tid_tx[tid]->pending);
                *state = HT_AGG_STATE_IDLE;
                goto err_free;
        }
-       sta->tid_to_tx_q[tid] = qn;
 
        /* Driver vetoed or OKed, but we can take packets again now */
        ieee80211_wake_queue_by_reason(
  err_free:
        kfree(sta->ampdu_mlme.tid_tx[tid]);
        sta->ampdu_mlme.tid_tx[tid] = NULL;
- err_return_queue:
-       if (qn >= 0) {
-               /* give queue back to pool */
-               spin_lock(&local->queue_stop_reason_lock);
-               local->ampdu_ac_queue[qn] = -1;
-               spin_unlock(&local->queue_stop_reason_lock);
-       }
+ err_wake_queue:
        ieee80211_wake_queue_by_reason(
                &local->hw, ieee80211_ac_from_tid(tid),
                IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
 
 
        const struct ieee80211_ops *ops;
 
-       /* AC queue corresponding to each AMPDU queue */
-       s8 ampdu_ac_queue[IEEE80211_MAX_AMPDU_QUEUES];
-       unsigned int amdpu_ac_stop_refcnt[IEEE80211_MAX_AMPDU_QUEUES];
-
-       unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES +
-                                        IEEE80211_MAX_AMPDU_QUEUES];
+       unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES];
        /* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */
        spinlock_t queue_stop_reason_lock;
 
 
        setup_timer(&local->dynamic_ps_timer,
                    ieee80211_dynamic_ps_timer, (unsigned long) local);
 
-       for (i = 0; i < IEEE80211_MAX_AMPDU_QUEUES; i++)
-               local->ampdu_ac_queue[i] = -1;
-       /* using an s8 won't work with more than that */
-       BUILD_BUG_ON(IEEE80211_MAX_AMPDU_QUEUES > 127);
-
        sta_info_init(local);
 
        for (i = 0; i < IEEE80211_MAX_QUEUES; i++)
         */
        if (hw->queues > IEEE80211_MAX_QUEUES)
                hw->queues = IEEE80211_MAX_QUEUES;
-       if (hw->ampdu_queues > IEEE80211_MAX_AMPDU_QUEUES)
-               hw->ampdu_queues = IEEE80211_MAX_AMPDU_QUEUES;
-       if (hw->queues < 4)
-               hw->ampdu_queues = 0;
 
        mdev = alloc_netdev_mq(sizeof(struct ieee80211_master_priv),
                               "wmaster%d", ieee80211_master_setup,
 
                if (tid_rx)
                        tid_rx->shutdown = true;
 
-               /*
-                * The stop callback cannot find this station any more, but
-                * it didn't complete its work -- start the queue if necessary
-                */
-               if (sta->ampdu_mlme.tid_state_tx[i] & HT_AGG_STATE_INITIATOR_MSK &&
-                   sta->ampdu_mlme.tid_state_tx[i] & HT_AGG_STATE_REQ_STOP_BA_MSK &&
-                   local->hw.ampdu_queues)
-                       ieee80211_wake_queue_by_reason(&local->hw,
-                               local->hw.queues + sta->tid_to_tx_q[i],
-                               IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
-
                spin_unlock_bh(&sta->lock);
 
                /*
                 * enable session_timer's data differentiation. refer to
                 * sta_rx_agg_session_timer_expired for useage */
                sta->timer_to_tid[i] = i;
-               sta->tid_to_tx_q[i] = -1;
                /* rx */
                sta->ampdu_mlme.tid_state_rx[i] = HT_AGG_STATE_IDLE;
                sta->ampdu_mlme.tid_rx[i] = NULL;
 
  * @tid_seq: per-TID sequence numbers for sending to this STA
  * @ampdu_mlme: A-MPDU state machine state
  * @timer_to_tid: identity mapping to ID timers
- * @tid_to_tx_q: map tid to tx queue (invalid == negative values)
  * @llid: Local link ID
  * @plid: Peer link ID
  * @reason: Cancel reason on PLINK_HOLDING state
         */
        struct sta_ampdu_mlme ampdu_mlme;
        u8 timer_to_tid[STA_TID_NUM];
-       s8 tid_to_tx_q[STA_TID_NUM];
 
 #ifdef CONFIG_MAC80211_MESH
        /*
 
                        info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT |
                                         IEEE80211_TX_CTL_FIRST_FRAGMENT);
 
-               /*
-                * Internally, we need to have the queue mapping point to
-                * the real AC queue, not the virtual A-MPDU queue. This
-                * now finally sets the queue to what the driver wants.
-                * We will later move this down into the only driver that
-                * needs it, iwlwifi.
-                */
-               if (sta && local->hw.ampdu_queues &&
-                   info->flags & IEEE80211_TX_CTL_AMPDU) {
-                       unsigned long flags;
-                       u8 *qc = ieee80211_get_qos_ctl((void *) skb->data);
-                       int tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
-
-                       spin_lock_irqsave(&sta->lock, flags);
-                       skb_set_queue_mapping(skb, local->hw.queues +
-                                                  sta->tid_to_tx_q[tid]);
-                       spin_unlock_irqrestore(&sta->lock, flags);
-               }
-
                next = skb->next;
                len = skb->len;
                ret = local->ops->tx(local_to_hw(local), skb);
 
 {
        struct ieee80211_local *local = hw_to_local(hw);
 
-       if (queue >= hw->queues) {
-               if (local->ampdu_ac_queue[queue - hw->queues] < 0)
-                       return;
-
-               /*
-                * for virtual aggregation queues, we need to refcount the
-                * internal mac80211 disable (multiple times!), keep track of
-                * driver disable _and_ make sure the regular queue is
-                * actually enabled.
-                */
-               if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION)
-                       local->amdpu_ac_stop_refcnt[queue - hw->queues]--;
-               else
-                       __clear_bit(reason, &local->queue_stop_reasons[queue]);
-
-               if (local->queue_stop_reasons[queue] ||
-                   local->amdpu_ac_stop_refcnt[queue - hw->queues])
-                       return;
-
-               /* now go on to treat the corresponding regular queue */
-               queue = local->ampdu_ac_queue[queue - hw->queues];
-               reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION;
-       }
+       if (WARN_ON(queue >= hw->queues))
+               return;
 
        __clear_bit(reason, &local->queue_stop_reasons[queue]);
 
 {
        struct ieee80211_local *local = hw_to_local(hw);
 
-       if (queue >= hw->queues) {
-               if (local->ampdu_ac_queue[queue - hw->queues] < 0)
-                       return;
-
-               /*
-                * for virtual aggregation queues, we need to refcount the
-                * internal mac80211 disable (multiple times!), keep track of
-                * driver disable _and_ make sure the regular queue is
-                * actually enabled.
-                */
-               if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION)
-                       local->amdpu_ac_stop_refcnt[queue - hw->queues]++;
-               else
-                       __set_bit(reason, &local->queue_stop_reasons[queue]);
-
-               /* now go on to treat the corresponding regular queue */
-               queue = local->ampdu_ac_queue[queue - hw->queues];
-               reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION;
-       }
+       if (WARN_ON(queue >= hw->queues))
+               return;
 
        /*
         * Only stop if it was previously running, this is necessary
 int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
 {
        struct ieee80211_local *local = hw_to_local(hw);
-       unsigned long flags;
 
-       if (queue >= hw->queues) {
-               spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-               queue = local->ampdu_ac_queue[queue - hw->queues];
-               spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
-               if (queue < 0)
-                       return true;
-       }
+       if (WARN_ON(queue >= hw->queues))
+               return true;
 
        return __netif_subqueue_stopped(local->mdev, queue);
 }
 
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 
-       for (i = 0; i < hw->queues + hw->ampdu_queues; i++)
+       for (i = 0; i < hw->queues; i++)
                __ieee80211_wake_queue(hw, i, reason);
 
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);