Commit 68b99311 authored by Garen Tamrazian's avatar Garen Tamrazian Committed by Wey-Yi Guy

iwlagn: fix radar frame rejection

The microcode may sometimes reject TX frames when
on a radar channel even after we associated as it
clears information during association and needs to
receive a new beacon before allowing that channel
again. This manifests itself as a TX status value
of TX_STATUS_FAIL_PASSIVE_NO_RX. So in this case,
stop the corresponding queue and give the frame
back to mac80211 for retransmission. We start the
queue again when a beacon from the AP is received
which will make the regulatory enforcement in the
device allow transmitting again.
Signed-off-by: default avatarGaren Tamrazian <garenx.tamrazian@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarWey-Yi Guy <wey-yi.w.guy@intel.com>
parent 3ecccbcd
...@@ -172,6 +172,7 @@ static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status) ...@@ -172,6 +172,7 @@ static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status)
static void iwlagn_set_tx_status(struct iwl_priv *priv, static void iwlagn_set_tx_status(struct iwl_priv *priv,
struct ieee80211_tx_info *info, struct ieee80211_tx_info *info,
struct iwl_rxon_context *ctx,
struct iwlagn_tx_resp *tx_resp, struct iwlagn_tx_resp *tx_resp,
int txq_id, bool is_agg) int txq_id, bool is_agg)
{ {
...@@ -186,6 +187,13 @@ static void iwlagn_set_tx_status(struct iwl_priv *priv, ...@@ -186,6 +187,13 @@ static void iwlagn_set_tx_status(struct iwl_priv *priv,
if (!iwl_is_tx_success(status)) if (!iwl_is_tx_success(status))
iwlagn_count_tx_err_status(priv, status); iwlagn_count_tx_err_status(priv, status);
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_stop_queue(priv, &priv->txq[txq_id]);
}
IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x) rate_n_flags " IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x) rate_n_flags "
"0x%x retries %d\n", "0x%x retries %d\n",
txq_id, txq_id,
...@@ -242,15 +250,16 @@ static int iwlagn_tx_status_reply_tx(struct iwl_priv *priv, ...@@ -242,15 +250,16 @@ static int iwlagn_tx_status_reply_tx(struct iwl_priv *priv,
/* # frames attempted by Tx command */ /* # frames attempted by Tx command */
if (agg->frame_count == 1) { if (agg->frame_count == 1) {
struct iwl_tx_info *txb;
/* Only one frame was attempted; no block-ack will arrive */ /* Only one frame was attempted; no block-ack will arrive */
idx = start_idx; idx = start_idx;
IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, StartIdx=%d idx=%d\n", IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, StartIdx=%d idx=%d\n",
agg->frame_count, agg->start_idx, idx); agg->frame_count, agg->start_idx, idx);
iwlagn_set_tx_status(priv, txb = &priv->txq[txq_id].txb[idx];
IEEE80211_SKB_CB( iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(txb->skb),
priv->txq[txq_id].txb[idx].skb), txb->ctx, tx_resp, txq_id, true);
tx_resp, txq_id, true);
agg->wait_for_ba = 0; agg->wait_for_ba = 0;
} else { } else {
/* Two or more frames were attempted; expect block-ack */ /* Two or more frames were attempted; expect block-ack */
...@@ -391,6 +400,7 @@ static void iwlagn_rx_reply_tx(struct iwl_priv *priv, ...@@ -391,6 +400,7 @@ static void iwlagn_rx_reply_tx(struct iwl_priv *priv,
struct iwl_tx_queue *txq = &priv->txq[txq_id]; struct iwl_tx_queue *txq = &priv->txq[txq_id];
struct ieee80211_tx_info *info; struct ieee80211_tx_info *info;
struct iwlagn_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; struct iwlagn_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
struct iwl_tx_info *txb;
u32 status = le16_to_cpu(tx_resp->status.status); u32 status = le16_to_cpu(tx_resp->status.status);
int tid; int tid;
int sta_id; int sta_id;
...@@ -406,7 +416,8 @@ static void iwlagn_rx_reply_tx(struct iwl_priv *priv, ...@@ -406,7 +416,8 @@ static void iwlagn_rx_reply_tx(struct iwl_priv *priv,
} }
txq->time_stamp = jiffies; txq->time_stamp = jiffies;
info = IEEE80211_SKB_CB(txq->txb[txq->q.read_ptr].skb); txb = &txq->txb[txq->q.read_ptr];
info = IEEE80211_SKB_CB(txb->skb);
memset(&info->status, 0, sizeof(info->status)); memset(&info->status, 0, sizeof(info->status));
tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >>
...@@ -450,12 +461,14 @@ static void iwlagn_rx_reply_tx(struct iwl_priv *priv, ...@@ -450,12 +461,14 @@ static void iwlagn_rx_reply_tx(struct iwl_priv *priv,
iwl_wake_queue(priv, txq); iwl_wake_queue(priv, txq);
} }
} else { } else {
iwlagn_set_tx_status(priv, info, tx_resp, txq_id, false); iwlagn_set_tx_status(priv, info, txb->ctx, tx_resp,
txq_id, false);
freed = iwlagn_tx_queue_reclaim(priv, txq_id, index); freed = iwlagn_tx_queue_reclaim(priv, txq_id, index);
iwl_free_tfds_in_queue(priv, sta_id, tid, freed); iwl_free_tfds_in_queue(priv, sta_id, tid, freed);
if (priv->mac80211_registered && if (priv->mac80211_registered &&
(iwl_queue_space(&txq->q) > txq->q.low_mark)) iwl_queue_space(&txq->q) > txq->q.low_mark &&
status != TX_STATUS_FAIL_PASSIVE_NO_RX)
iwl_wake_queue(priv, txq); iwl_wake_queue(priv, txq);
} }
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "iwl-sta.h" #include "iwl-sta.h"
#include "iwl-core.h" #include "iwl-core.h"
#include "iwl-agn-calib.h" #include "iwl-agn-calib.h"
#include "iwl-helpers.h"
static int iwlagn_disable_bss(struct iwl_priv *priv, static int iwlagn_disable_bss(struct iwl_priv *priv,
struct iwl_rxon_context *ctx, struct iwl_rxon_context *ctx,
...@@ -600,6 +601,18 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw, ...@@ -600,6 +601,18 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
priv->timestamp = bss_conf->timestamp; priv->timestamp = bss_conf->timestamp;
ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;
} else { } else {
/*
* If we disassociate while there are pending
* frames, just wake up the queues and let the
* frames "escape" ... This shouldn't really
* be happening to start with, but we should
* not get stuck in this case either since it
* can happen if userspace gets confused.
*/
if (ctx->last_tx_rejected) {
ctx->last_tx_rejected = false;
iwl_wake_any_queue(priv, ctx);
}
ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
} }
} }
......
...@@ -428,6 +428,7 @@ void iwlagn_send_bt_env(struct iwl_priv *priv, u8 action, u8 type) ...@@ -428,6 +428,7 @@ void iwlagn_send_bt_env(struct iwl_priv *priv, u8 action, u8 type)
int iwlagn_alive_notify(struct iwl_priv *priv) int iwlagn_alive_notify(struct iwl_priv *priv)
{ {
const struct queue_to_fifo_ac *queue_to_fifo; const struct queue_to_fifo_ac *queue_to_fifo;
struct iwl_rxon_context *ctx;
u32 a; u32 a;
unsigned long flags; unsigned long flags;
int i, chan; int i, chan;
...@@ -501,6 +502,8 @@ int iwlagn_alive_notify(struct iwl_priv *priv) ...@@ -501,6 +502,8 @@ int iwlagn_alive_notify(struct iwl_priv *priv)
memset(&priv->queue_stopped[0], 0, sizeof(priv->queue_stopped)); memset(&priv->queue_stopped[0], 0, sizeof(priv->queue_stopped));
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
atomic_set(&priv->queue_stop_count[i], 0); atomic_set(&priv->queue_stop_count[i], 0);
for_each_context(priv, ctx)
ctx->last_tx_rejected = false;
/* reset to 0 to enable all the queue first */ /* reset to 0 to enable all the queue first */
priv->txq_ctx_active_msk = 0; priv->txq_ctx_active_msk = 0;
......
...@@ -220,6 +220,7 @@ static inline u32 iwl_tx_status_to_mac80211(u32 status) ...@@ -220,6 +220,7 @@ static inline u32 iwl_tx_status_to_mac80211(u32 status)
case TX_STATUS_DIRECT_DONE: case TX_STATUS_DIRECT_DONE:
return IEEE80211_TX_STAT_ACK; return IEEE80211_TX_STAT_ACK;
case TX_STATUS_FAIL_DEST_PS: case TX_STATUS_FAIL_DEST_PS:
case TX_STATUS_FAIL_PASSIVE_NO_RX:
return IEEE80211_TX_STAT_TX_FILTERED; return IEEE80211_TX_STAT_TX_FILTERED;
default: default:
return 0; return 0;
......
...@@ -1170,6 +1170,8 @@ struct iwl_rxon_context { ...@@ -1170,6 +1170,8 @@ struct iwl_rxon_context {
bool enabled, is_40mhz; bool enabled, is_40mhz;
u8 extension_chan_offset; u8 extension_chan_offset;
} ht; } ht;
bool last_tx_rejected;
}; };
enum iwl_scan_type { enum iwl_scan_type {
......
...@@ -131,6 +131,19 @@ static inline void iwl_stop_queue(struct iwl_priv *priv, ...@@ -131,6 +131,19 @@ static inline void iwl_stop_queue(struct iwl_priv *priv,
ieee80211_stop_queue(priv->hw, ac); ieee80211_stop_queue(priv->hw, ac);
} }
static inline void iwl_wake_any_queue(struct iwl_priv *priv,
struct iwl_rxon_context *ctx)
{
u8 ac;
for (ac = 0; ac < AC_NUM; ac++) {
IWL_DEBUG_INFO(priv, "Queue Status: Q[%d] %s\n",
ac, (atomic_read(&priv->queue_stop_count[ac]) > 0)
? "stopped" : "awake");
iwl_wake_queue(priv, &priv->txq[ctx->ac_to_queue[ac]]);
}
}
#define ieee80211_stop_queue DO_NOT_USE_ieee80211_stop_queue #define ieee80211_stop_queue DO_NOT_USE_ieee80211_stop_queue
#define ieee80211_wake_queue DO_NOT_USE_ieee80211_wake_queue #define ieee80211_wake_queue DO_NOT_USE_ieee80211_wake_queue
......
...@@ -873,6 +873,7 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv, ...@@ -873,6 +873,7 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv,
{ {
struct sk_buff *skb; struct sk_buff *skb;
__le16 fc = hdr->frame_control; __le16 fc = hdr->frame_control;
struct iwl_rxon_context *ctx;
/* We only process data packets if the interface is open */ /* We only process data packets if the interface is open */
if (unlikely(!priv->is_open)) { if (unlikely(!priv->is_open)) {
...@@ -895,6 +896,26 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv, ...@@ -895,6 +896,26 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv,
skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), len); skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), len);
iwl_update_stats(priv, false, fc, len); iwl_update_stats(priv, false, fc, len);
/*
* Wake any queues that were stopped due to a passive channel tx
* failure. This can happen because the regulatory enforcement in
* the device waits for a beacon before allowing transmission,
* sometimes even after already having transmitted frames for the
* association because the new RXON may reset the information.
*/
if (unlikely(ieee80211_is_beacon(fc))) {
for_each_context(priv, ctx) {
if (!ctx->last_tx_rejected)
continue;
if (compare_ether_addr(hdr->addr3,
ctx->active.bssid_addr))
continue;
ctx->last_tx_rejected = false;
iwl_wake_any_queue(priv, ctx);
}
}
memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
ieee80211_rx(priv->hw, skb); ieee80211_rx(priv->hw, skb);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment