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);