summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-agn-tx.c')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-tx.c319
1 files changed, 203 insertions, 116 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
index 7d614c4d3c62..2b078a995729 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
@@ -71,18 +71,6 @@ static const u8 tid_to_ac[] = {
2, 3, 3, 2, 1, 1, 0, 0
};
-static const u8 ac_to_fifo[] = {
- IWL_TX_FIFO_VO,
- IWL_TX_FIFO_VI,
- IWL_TX_FIFO_BE,
- IWL_TX_FIFO_BK,
-};
-
-static inline int get_fifo_from_ac(u8 ac)
-{
- return ac_to_fifo[ac];
-}
-
static inline int get_ac_from_tid(u16 tid)
{
if (likely(tid < ARRAY_SIZE(tid_to_ac)))
@@ -92,10 +80,10 @@ static inline int get_ac_from_tid(u16 tid)
return -EINVAL;
}
-static inline int get_fifo_from_tid(u16 tid)
+static inline int get_fifo_from_tid(struct iwl_rxon_context *ctx, u16 tid)
{
if (likely(tid < ARRAY_SIZE(tid_to_ac)))
- return get_fifo_from_ac(tid_to_ac[tid]);
+ return ctx->ac_to_fifo[tid_to_ac[tid]];
/* no support for TIDs 8-15 yet */
return -EINVAL;
@@ -118,7 +106,7 @@ void iwlagn_txq_update_byte_cnt_tbl(struct iwl_priv *priv,
WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX);
- if (txq_id != IWL_CMD_QUEUE_NUM) {
+ if (txq_id != priv->cmd_queue) {
sta_id = txq->cmd[txq->q.write_ptr]->cmd.tx.sta_id;
sec_ctl = txq->cmd[txq->q.write_ptr]->cmd.tx.sec_ctl;
@@ -155,7 +143,7 @@ void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_priv *priv,
WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX);
- if (txq_id != IWL_CMD_QUEUE_NUM)
+ if (txq_id != priv->cmd_queue)
sta_id = txq->cmd[read_ptr]->cmd.tx.sta_id;
bc_ent = cpu_to_le16(1 | (sta_id << 12));
@@ -233,22 +221,25 @@ int iwlagn_txq_agg_enable(struct iwl_priv *priv, int txq_id,
{
unsigned long flags;
u16 ra_tid;
+ int ret;
if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) ||
- (IWLAGN_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues
- <= txq_id)) {
+ (IWLAGN_FIRST_AMPDU_QUEUE +
+ priv->cfg->base_params->num_of_ampdu_queues <= txq_id)) {
IWL_WARN(priv,
"queue number out of range: %d, must be %d to %d\n",
txq_id, IWLAGN_FIRST_AMPDU_QUEUE,
IWLAGN_FIRST_AMPDU_QUEUE +
- priv->cfg->num_of_ampdu_queues - 1);
+ priv->cfg->base_params->num_of_ampdu_queues - 1);
return -EINVAL;
}
ra_tid = BUILD_RAxTID(sta_id, tid);
/* Modify device's station table to Tx this TID */
- iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
+ ret = iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
+ if (ret)
+ return ret;
spin_lock_irqsave(&priv->lock, flags);
@@ -295,13 +286,13 @@ int iwlagn_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
u16 ssn_idx, u8 tx_fifo)
{
if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) ||
- (IWLAGN_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues
- <= txq_id)) {
+ (IWLAGN_FIRST_AMPDU_QUEUE +
+ priv->cfg->base_params->num_of_ampdu_queues <= txq_id)) {
IWL_ERR(priv,
"queue number out of range: %d, must be %d to %d\n",
txq_id, IWLAGN_FIRST_AMPDU_QUEUE,
IWLAGN_FIRST_AMPDU_QUEUE +
- priv->cfg->num_of_ampdu_queues - 1);
+ priv->cfg->base_params->num_of_ampdu_queues - 1);
return -EINVAL;
}
@@ -330,19 +321,15 @@ void iwlagn_txq_set_sched(struct iwl_priv *priv, u32 mask)
iwl_write_prph(priv, IWLAGN_SCD_TXFACT, mask);
}
-static inline int get_queue_from_ac(u16 ac)
-{
- return ac;
-}
-
/*
* handle build REPLY_TX command notification.
*/
static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv,
- struct iwl_tx_cmd *tx_cmd,
- struct ieee80211_tx_info *info,
- struct ieee80211_hdr *hdr,
- u8 std_id)
+ struct sk_buff *skb,
+ struct iwl_tx_cmd *tx_cmd,
+ struct ieee80211_tx_info *info,
+ struct ieee80211_hdr *hdr,
+ u8 std_id)
{
__le16 fc = hdr->frame_control;
__le32 tx_flags = tx_cmd->tx_flags;
@@ -362,6 +349,13 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv,
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 &&
+ (ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) ||
+ ieee80211_is_reassoc_req(fc) ||
+ skb->protocol == cpu_to_be16(ETH_P_PAE)))
+ tx_flags |= TX_CMD_FLG_IGNORE_BT;
tx_cmd->sta_id = std_id;
@@ -376,10 +370,7 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv,
tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
}
- priv->cfg->ops->utils->rts_tx_cmd_flag(info, &tx_flags);
-
- if ((tx_flags & TX_CMD_FLG_RTS_MSK) || (tx_flags & TX_CMD_FLG_CTS_MSK))
- tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK;
+ priv->cfg->ops->utils->tx_cmd_protection(priv, info, fc, &tx_flags);
tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK);
if (ieee80211_is_mgmt(fc)) {
@@ -453,23 +444,16 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE))
rate_flags |= RATE_MCS_CCK_MSK;
- /* Set up RTS and CTS flags for certain packets */
- switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) {
- case cpu_to_le16(IEEE80211_STYPE_AUTH):
- case cpu_to_le16(IEEE80211_STYPE_DEAUTH):
- case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ):
- case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ):
- if (tx_cmd->tx_flags & TX_CMD_FLG_RTS_MSK) {
- tx_cmd->tx_flags &= ~TX_CMD_FLG_RTS_MSK;
- tx_cmd->tx_flags |= TX_CMD_FLG_CTS_MSK;
- }
- break;
- default:
- break;
- }
-
/* Set up antennas */
- priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant);
+ if (priv->cfg->bt_params &&
+ priv->cfg->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,
+ first_antenna(priv->hw_params.valid_tx_ant));
+ } else
+ priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
+ priv->hw_params.valid_tx_ant);
rate_flags |= iwl_ant_idx_to_flags(priv->mgmt_tx_ant);
/* Set the rate in the TX cmd */
@@ -484,8 +468,8 @@ static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv,
{
struct ieee80211_key_conf *keyconf = info->control.hw_key;
- switch (keyconf->alg) {
- case ALG_CCMP:
+ switch (keyconf->cipher) {
+ case WLAN_CIPHER_SUITE_CCMP:
tx_cmd->sec_ctl = TX_CMD_SEC_CCM;
memcpy(tx_cmd->key, keyconf->key, keyconf->keylen);
if (info->flags & IEEE80211_TX_CTL_AMPDU)
@@ -493,20 +477,20 @@ static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv,
IWL_DEBUG_TX(priv, "tx_cmd with AES hwcrypto\n");
break;
- case ALG_TKIP:
+ case WLAN_CIPHER_SUITE_TKIP:
tx_cmd->sec_ctl = TX_CMD_SEC_TKIP;
ieee80211_get_tkip_key(keyconf, skb_frag,
IEEE80211_TKIP_P2_KEY, tx_cmd->key);
IWL_DEBUG_TX(priv, "tx_cmd with tkip hwcrypto\n");
break;
- case ALG_WEP:
+ case WLAN_CIPHER_SUITE_WEP104:
+ tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128;
+ /* fall through */
+ case WLAN_CIPHER_SUITE_WEP40:
tx_cmd->sec_ctl |= (TX_CMD_SEC_WEP |
(keyconf->keyidx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT);
- if (keyconf->keylen == WEP_KEY_LEN_128)
- tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128;
-
memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen);
IWL_DEBUG_TX(priv, "Configuring packet for WEP encryption "
@@ -514,7 +498,7 @@ static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv,
break;
default:
- IWL_ERR(priv, "Unknown encode alg %d\n", keyconf->alg);
+ IWL_ERR(priv, "Unknown encode cipher %x\n", keyconf->cipher);
break;
}
}
@@ -533,6 +517,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
struct iwl_device_cmd *out_cmd;
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;
dma_addr_t phys_addr;
dma_addr_t txcmd_phys;
@@ -547,6 +532,9 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
u8 *qc = NULL;
unsigned long flags;
+ if (info->control.vif)
+ ctx = iwl_rxon_ctx_from_vif(info->control.vif);
+
spin_lock_irqsave(&priv->lock, flags);
if (iwl_is_rfkill(priv)) {
IWL_DEBUG_DROP(priv, "Dropping - RF KILL\n");
@@ -567,10 +555,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
hdr_len = ieee80211_hdrlen(fc);
/* Find index into station table for destination station */
- if (!info->control.sta)
- sta_id = priv->hw_params.bcast_sta_id;
- else
- sta_id = iwl_sta_id(info->control.sta);
+ sta_id = iwl_sta_id_or_broadcast(priv, ctx, info->control.sta);
if (sta_id == IWL_INVALID_STATION) {
IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n",
hdr->addr1);
@@ -582,8 +567,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
if (sta)
sta_priv = (void *)sta->drv_priv;
- if (sta_priv && sta_id != priv->hw_params.bcast_sta_id &&
- sta_priv->asleep) {
+ if (sta_priv && sta_priv->asleep) {
WARN_ON(!(info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE));
/*
* This sends an asynchronous command to the device,
@@ -597,12 +581,31 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
iwl_sta_modify_sleep_tx_count(priv, sta_id, 1);
}
- txq_id = get_queue_from_ac(skb_get_queue_mapping(skb));
+ /*
+ * Send this frame after DTIM -- there's a special queue
+ * reserved for this for contexts that support AP mode.
+ */
+ if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
+ txq_id = ctx->mcast_queue;
+ /*
+ * The microcode will clear the more data
+ * bit in the last frame it transmits.
+ */
+ hdr->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ } else
+ txq_id = ctx->ac_to_queue[skb_get_queue_mapping(skb)];
+
+ /* irqs already disabled/saved above when locking priv->lock */
+ spin_lock(&priv->sta_lock);
+
if (ieee80211_is_data_qos(fc)) {
qc = ieee80211_get_qos_ctl(hdr);
tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
- if (unlikely(tid >= MAX_TID_COUNT))
+ if (WARN_ON_ONCE(tid >= MAX_TID_COUNT)) {
+ spin_unlock(&priv->sta_lock);
goto drop_unlock;
+ }
seq_number = priv->stations[sta_id].tid[tid].seq_number;
seq_number &= IEEE80211_SCTL_SEQ;
hdr->seq_ctrl = hdr->seq_ctrl &
@@ -620,15 +623,23 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
swq_id = txq->swq_id;
q = &txq->q;
- if (unlikely(iwl_queue_space(q) < q->high_mark))
+ if (unlikely(iwl_queue_space(q) < q->high_mark)) {
+ spin_unlock(&priv->sta_lock);
goto drop_unlock;
+ }
- if (ieee80211_is_data_qos(fc))
+ if (ieee80211_is_data_qos(fc)) {
priv->stations[sta_id].tid[tid].tfds_in_queue++;
+ if (!ieee80211_has_morefrags(fc))
+ priv->stations[sta_id].tid[tid].seq_number = seq_number;
+ }
+
+ spin_unlock(&priv->sta_lock);
/* Set up driver data for this TFD */
memset(&(txq->txb[q->write_ptr]), 0, sizeof(struct iwl_tx_info));
- txq->txb[q->write_ptr].skb[0] = skb;
+ txq->txb[q->write_ptr].skb = skb;
+ txq->txb[q->write_ptr].ctx = ctx;
/* Set up first empty entry in queue's array of Tx/cmd buffers */
out_cmd = txq->cmd[q->write_ptr];
@@ -659,7 +670,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
iwlagn_tx_cmd_build_hwcrypto(priv, info, tx_cmd, skb, sta_id);
/* TODO need this for burst mode later on */
- iwlagn_tx_cmd_build_basic(priv, tx_cmd, info, hdr, sta_id);
+ iwlagn_tx_cmd_build_basic(priv, skb, tx_cmd, info, hdr, sta_id);
iwl_dbg_log_tx_data_frame(priv, len, hdr);
iwlagn_tx_cmd_build_rate(priv, tx_cmd, info, fc);
@@ -694,8 +705,8 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
txcmd_phys = pci_map_single(priv->pci_dev,
&out_cmd->hdr, len,
PCI_DMA_BIDIRECTIONAL);
- pci_unmap_addr_set(out_meta, mapping, txcmd_phys);
- pci_unmap_len_set(out_meta, len, len);
+ dma_unmap_addr_set(out_meta, mapping, txcmd_phys);
+ dma_unmap_len_set(out_meta, len, len);
/* Add buffer containing Tx command and MAC(!) header to TFD's
* first entry */
priv->cfg->ops->lib->txq_attach_buf_to_tfd(priv, txq,
@@ -703,8 +714,6 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
if (!ieee80211_has_morefrags(hdr->frame_control)) {
txq->need_update = 1;
- if (qc)
- priv->stations[sta_id].tid[tid].seq_number = seq_number;
} else {
wait_write_ptr = 1;
txq->need_update = 0;
@@ -819,7 +828,7 @@ void iwlagn_hw_txq_ctx_free(struct iwl_priv *priv)
/* Tx queues */
if (priv->txq) {
for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++)
- if (txq_id == IWL_CMD_QUEUE_NUM)
+ if (txq_id == priv->cmd_queue)
iwl_cmd_queue_free(priv);
else
iwl_tx_queue_free(priv, txq_id);
@@ -876,9 +885,9 @@ int iwlagn_txq_ctx_alloc(struct iwl_priv *priv)
spin_unlock_irqrestore(&priv->lock, flags);
- /* Alloc and init all Tx queues, including the command queue (#4) */
+ /* Alloc and init all Tx queues, including the command queue (#4/#9) */
for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) {
- slots_num = (txq_id == IWL_CMD_QUEUE_NUM) ?
+ slots_num = (txq_id == priv->cmd_queue) ?
TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
ret = iwl_tx_queue_init(priv, &priv->txq[txq_id], slots_num,
txq_id);
@@ -916,7 +925,7 @@ void iwlagn_txq_ctx_reset(struct iwl_priv *priv)
/* Alloc and init all Tx queues, including the command queue (#4) */
for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) {
- slots_num = txq_id == IWL_CMD_QUEUE_NUM ?
+ slots_num = txq_id == priv->cmd_queue ?
TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
iwl_tx_queue_reset(priv, &priv->txq[txq_id], slots_num, txq_id);
}
@@ -938,9 +947,12 @@ void iwlagn_txq_ctx_stop(struct iwl_priv *priv)
/* Stop each Tx DMA channel, and wait for it to be idle */
for (ch = 0; ch < priv->hw_params.dma_chnl_num; ch++) {
iwl_write_direct32(priv, FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0);
- iwl_poll_direct_bit(priv, FH_TSSR_TX_STATUS_REG,
+ if (iwl_poll_direct_bit(priv, FH_TSSR_TX_STATUS_REG,
FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch),
- 1000);
+ 1000))
+ IWL_ERR(priv, "Failing on timeout while stopping"
+ " DMA channel %d [0x%08x]", ch,
+ iwl_read_direct32(priv, FH_TSSR_TX_STATUS_REG));
}
spin_unlock_irqrestore(&priv->lock, flags);
}
@@ -971,7 +983,7 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
unsigned long flags;
struct iwl_tid_data *tid_data;
- tx_fifo = get_fifo_from_tid(tid);
+ tx_fifo = get_fifo_from_tid(iwl_rxon_ctx_from_vif(vif), tid);
if (unlikely(tx_fifo < 0))
return tx_fifo;
@@ -1009,6 +1021,8 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
if (ret)
return ret;
+ spin_lock_irqsave(&priv->sta_lock, flags);
+ tid_data = &priv->stations[sta_id].tid[tid];
if (tid_data->tfds_in_queue == 0) {
IWL_DEBUG_HT(priv, "HW queue is empty\n");
tid_data->agg.state = IWL_AGG_ON;
@@ -1018,18 +1032,19 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
tid_data->tfds_in_queue);
tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA;
}
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
return ret;
}
int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid)
{
- int tx_fifo_id, txq_id, sta_id, ssn = -1;
+ int tx_fifo_id, txq_id, sta_id, ssn;
struct iwl_tid_data *tid_data;
int write_ptr, read_ptr;
unsigned long flags;
- tx_fifo_id = get_fifo_from_tid(tid);
+ tx_fifo_id = get_fifo_from_tid(iwl_rxon_ctx_from_vif(vif), tid);
if (unlikely(tx_fifo_id < 0))
return tx_fifo_id;
@@ -1040,20 +1055,28 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
return -ENXIO;
}
- if (priv->stations[sta_id].tid[tid].agg.state ==
- IWL_EMPTYING_HW_QUEUE_ADDBA) {
- IWL_DEBUG_HT(priv, "AGG stop before setup done\n");
- ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
- priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF;
- return 0;
- }
-
- if (priv->stations[sta_id].tid[tid].agg.state != IWL_AGG_ON)
- IWL_WARN(priv, "Stopping AGG while state not ON or starting\n");
+ spin_lock_irqsave(&priv->sta_lock, flags);
tid_data = &priv->stations[sta_id].tid[tid];
ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4;
txq_id = tid_data->agg.txq_id;
+
+ switch (priv->stations[sta_id].tid[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\n");
+ }
+
write_ptr = priv->txq[txq_id].q.write_ptr;
read_ptr = priv->txq[txq_id].q.read_ptr;
@@ -1062,13 +1085,18 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
IWL_DEBUG_HT(priv, "Stopping a non empty AGG HW QUEUE\n");
priv->stations[sta_id].tid[tid].agg.state =
IWL_EMPTYING_HW_QUEUE_DELBA;
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
return 0;
}
IWL_DEBUG_HT(priv, "HW queue is empty\n");
+ turn_off:
priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF;
- spin_lock_irqsave(&priv->lock, flags);
+ /* do not restore/save irqs */
+ spin_unlock(&priv->sta_lock);
+ spin_lock(&priv->lock);
+
/*
* the only reason this call can fail is queue number out of range,
* which can happen if uCode is reloaded and all the station
@@ -1091,6 +1119,11 @@ int iwlagn_txq_check_empty(struct iwl_priv *priv,
struct iwl_queue *q = &priv->txq[txq_id].q;
u8 *addr = priv->stations[sta_id].sta.sta.addr;
struct iwl_tid_data *tid_data = &priv->stations[sta_id].tid[tid];
+ struct iwl_rxon_context *ctx;
+
+ ctx = &priv->contexts[priv->stations[sta_id].ctxid];
+
+ lockdep_assert_held(&priv->sta_lock);
switch (priv->stations[sta_id].tid[tid].agg.state) {
case IWL_EMPTYING_HW_QUEUE_DELBA:
@@ -1099,12 +1132,12 @@ int iwlagn_txq_check_empty(struct iwl_priv *priv,
if ((txq_id == tid_data->agg.txq_id) &&
(q->read_ptr == q->write_ptr)) {
u16 ssn = SEQ_TO_SN(tid_data->seq_number);
- int tx_fifo = get_fifo_from_tid(tid);
+ int tx_fifo = get_fifo_from_tid(ctx, tid);
IWL_DEBUG_HT(priv, "HW queue empty: continue DELBA flow\n");
priv->cfg->ops->lib->txq_agg_disable(priv, txq_id,
ssn, tx_fifo);
tid_data->agg.state = IWL_AGG_OFF;
- ieee80211_stop_tx_ba_cb_irqsafe(priv->vif, addr, tid);
+ ieee80211_stop_tx_ba_cb_irqsafe(ctx->vif, addr, tid);
}
break;
case IWL_EMPTYING_HW_QUEUE_ADDBA:
@@ -1112,21 +1145,22 @@ int iwlagn_txq_check_empty(struct iwl_priv *priv,
if (tid_data->tfds_in_queue == 0) {
IWL_DEBUG_HT(priv, "HW queue empty: continue ADDBA flow\n");
tid_data->agg.state = IWL_AGG_ON;
- ieee80211_start_tx_ba_cb_irqsafe(priv->vif, addr, tid);
+ ieee80211_start_tx_ba_cb_irqsafe(ctx->vif, addr, tid);
}
break;
}
+
return 0;
}
-static void iwlagn_tx_status(struct iwl_priv *priv, struct sk_buff *skb)
+static void iwlagn_tx_status(struct iwl_priv *priv, struct iwl_tx_info *tx_info)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ 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(priv->vif, hdr->addr1);
+ sta = ieee80211_find_sta(tx_info->ctx->vif, hdr->addr1);
if (sta) {
sta_priv = (void *)sta->drv_priv;
/* avoid atomic ops if this isn't a client */
@@ -1136,7 +1170,7 @@ static void iwlagn_tx_status(struct iwl_priv *priv, struct sk_buff *skb)
}
rcu_read_unlock();
- ieee80211_tx_status_irqsafe(priv->hw, skb);
+ ieee80211_tx_status_irqsafe(priv->hw, tx_info->skb);
}
int iwlagn_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index)
@@ -1159,12 +1193,12 @@ 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->skb[0]);
+ iwlagn_tx_status(priv, tx_info);
- hdr = (struct ieee80211_hdr *)tx_info->skb[0]->data;
+ hdr = (struct ieee80211_hdr *)tx_info->skb->data;
if (hdr && ieee80211_is_data_qos(hdr->frame_control))
nfreed++;
- tx_info->skb[0] = NULL;
+ tx_info->skb = NULL;
if (priv->cfg->ops->lib->txq_inval_byte_cnt_tbl)
priv->cfg->ops->lib->txq_inval_byte_cnt_tbl(priv, txq);
@@ -1188,12 +1222,13 @@ 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;
+ u64 bitmap, sent_bitmap;
int successes = 0;
struct ieee80211_tx_info *info;
if (unlikely(!agg->wait_for_ba)) {
- IWL_ERR(priv, "Received BA when not expected\n");
+ if (unlikely(ba_resp->bitmap))
+ IWL_ERR(priv, "Received BA when not expected\n");
return -EINVAL;
}
@@ -1216,24 +1251,26 @@ static int iwlagn_tx_status_reply_compressed_ba(struct iwl_priv *priv,
/* check for success or failure according to the
* transmitted bitmap and block-ack bitmap */
- bitmap &= agg->bitmap;
+ sent_bitmap = bitmap & agg->bitmap;
/* For each frame attempted in aggregation,
* update driver's record of tx frame's status. */
- for (i = 0; i < agg->frame_count ; i++) {
- ack = bitmap & (1ULL << i);
- successes += !!ack;
+ 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;
}
- info = IEEE80211_SKB_CB(priv->txq[scd_flow].txb[agg->start_idx].skb[0]);
+ 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_ack_map = bitmap;
info->status.ampdu_len = agg->frame_count;
iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags, info);
@@ -1281,6 +1318,7 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
int index;
int sta_id;
int tid;
+ unsigned long flags;
/* "flow" corresponds to Tx queue */
u16 scd_flow = le16_to_cpu(ba_resp->scd_flow);
@@ -1300,7 +1338,14 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
tid = ba_resp->tid;
agg = &priv->stations[sta_id].tid[tid].agg;
if (unlikely(agg->txq_id != scd_flow)) {
- IWL_ERR(priv, "BA scd_flow %d does not match txq_id %d\n",
+ /*
+ * 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);
return;
}
@@ -1308,7 +1353,7 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
/* Find index just before block-ack window */
index = iwl_queue_dec_wrap(ba_resp_scd_ssn & 0xff, txq->q.n_bd);
- /* TODO: Need to get this copy more safely - now good for debug */
+ spin_lock_irqsave(&priv->sta_lock, flags);
IWL_DEBUG_TX_REPLY(priv, "REPLY_COMPRESSED_BA [%d] Received from %pM, "
"sta_id = %d\n",
@@ -1344,4 +1389,46 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
iwlagn_txq_check_empty(priv, sta_id, tid, scd_flow);
}
+
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+const char *iwl_get_tx_fail_reason(u32 status)
+{
+#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x
+#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x
+
+ switch (status & TX_STATUS_MSK) {
+ case TX_STATUS_SUCCESS:
+ return "SUCCESS";
+ TX_STATUS_POSTPONE(DELAY);
+ TX_STATUS_POSTPONE(FEW_BYTES);
+ TX_STATUS_POSTPONE(BT_PRIO);
+ TX_STATUS_POSTPONE(QUIET_PERIOD);
+ TX_STATUS_POSTPONE(CALC_TTAK);
+ TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY);
+ TX_STATUS_FAIL(SHORT_LIMIT);
+ TX_STATUS_FAIL(LONG_LIMIT);
+ TX_STATUS_FAIL(FIFO_UNDERRUN);
+ TX_STATUS_FAIL(DRAIN_FLOW);
+ TX_STATUS_FAIL(RFKILL_FLUSH);
+ TX_STATUS_FAIL(LIFE_EXPIRE);
+ TX_STATUS_FAIL(DEST_PS);
+ TX_STATUS_FAIL(HOST_ABORTED);
+ TX_STATUS_FAIL(BT_RETRY);
+ TX_STATUS_FAIL(STA_INVALID);
+ TX_STATUS_FAIL(FRAG_DROPPED);
+ TX_STATUS_FAIL(TID_DISABLE);
+ TX_STATUS_FAIL(FIFO_FLUSHED);
+ TX_STATUS_FAIL(INSUFFICIENT_CF_POLL);
+ TX_STATUS_FAIL(PASSIVE_NO_RX);
+ TX_STATUS_FAIL(NO_BEACON_ON_RADAR);
+ }
+
+ return "UNKNOWN";
+
+#undef TX_STATUS_FAIL
+#undef TX_STATUS_POSTPONE
}
+#endif /* CONFIG_IWLWIFI_DEBUG */