iwlagn: fix radar frame rejection
authorGaren Tamrazian <garenx.tamrazian@intel.com>
Wed, 30 Mar 2011 09:29:32 +0000 (02:29 -0700)
committerWey-Yi Guy <wey-yi.w.guy@intel.com>
Fri, 8 Apr 2011 14:59:37 +0000 (07:59 -0700)
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: Garen Tamrazian <garenx.tamrazian@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
drivers/net/wireless/iwlwifi/iwl-agn-lib.c
drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
drivers/net/wireless/iwlwifi/iwl-agn-ucode.c
drivers/net/wireless/iwlwifi/iwl-agn.h
drivers/net/wireless/iwlwifi/iwl-dev.h
drivers/net/wireless/iwlwifi/iwl-helpers.h
drivers/net/wireless/iwlwifi/iwl-rx.c

index 9e47be6a7393cf203fed83cf14168ca12fd529a9..cccf7471c00359ee9ce19139bd3fb25a5498f7b0 100644 (file)
@@ -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,
                                 struct ieee80211_tx_info *info,
+                                struct iwl_rxon_context *ctx,
                                 struct iwlagn_tx_resp *tx_resp,
                                 int txq_id, bool is_agg)
 {
@@ -186,6 +187,13 @@ static void iwlagn_set_tx_status(struct iwl_priv *priv,
        if (!iwl_is_tx_success(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 "
                           "0x%x retries %d\n",
                           txq_id,
@@ -242,15 +250,16 @@ static int iwlagn_tx_status_reply_tx(struct iwl_priv *priv,
 
        /* # frames attempted by Tx command */
        if (agg->frame_count == 1) {
+               struct iwl_tx_info *txb;
+
                /* Only one frame was attempted; no block-ack will arrive */
                idx = start_idx;
 
                IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, StartIdx=%d idx=%d\n",
                                   agg->frame_count, agg->start_idx, idx);
-               iwlagn_set_tx_status(priv,
-                                    IEEE80211_SKB_CB(
-                                       priv->txq[txq_id].txb[idx].skb),
-                                    tx_resp, txq_id, true);
+               txb = &priv->txq[txq_id].txb[idx];
+               iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(txb->skb),
+                                    txb->ctx, tx_resp, txq_id, true);
                agg->wait_for_ba = 0;
        } else {
                /* Two or more frames were attempted; expect block-ack */
@@ -391,7 +400,8 @@ static void iwlagn_rx_reply_tx(struct iwl_priv *priv,
        struct iwl_tx_queue *txq = &priv->txq[txq_id];
        struct ieee80211_tx_info *info;
        struct iwlagn_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
-       u32  status = le16_to_cpu(tx_resp->status.status);
+       struct iwl_tx_info *txb;
+       u32 status = le16_to_cpu(tx_resp->status.status);
        int tid;
        int sta_id;
        int freed;
@@ -406,7 +416,8 @@ static void iwlagn_rx_reply_tx(struct iwl_priv *priv,
        }
 
        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));
 
        tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >>
@@ -450,12 +461,14 @@ static void iwlagn_rx_reply_tx(struct iwl_priv *priv,
                                iwl_wake_queue(priv, txq);
                }
        } 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);
                iwl_free_tfds_in_queue(priv, sta_id, tid, freed);
 
                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);
        }
 
index c335ee6883eefa2ddb0aa831089a2edf733c5e18..56f46ee3bacd26d9313d45ee7d85a79ca69b1f69 100644 (file)
@@ -29,6 +29,7 @@
 #include "iwl-sta.h"
 #include "iwl-core.h"
 #include "iwl-agn-calib.h"
+#include "iwl-helpers.h"
 
 static int iwlagn_disable_bss(struct iwl_priv *priv,
                              struct iwl_rxon_context *ctx,
@@ -600,6 +601,18 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
                        priv->timestamp = bss_conf->timestamp;
                        ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;
                } 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;
                }
        }
index 01a6d2fc795c56c6ec7062f8d66c26138763c2bf..5c30f6b19a7f00b7fd5e803b654088f0feb1ffe8 100644 (file)
@@ -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)
 {
        const struct queue_to_fifo_ac *queue_to_fifo;
+       struct iwl_rxon_context *ctx;
        u32 a;
        unsigned long flags;
        int i, chan;
@@ -501,6 +502,8 @@ int iwlagn_alive_notify(struct iwl_priv *priv)
        memset(&priv->queue_stopped[0], 0, sizeof(priv->queue_stopped));
        for (i = 0; i < 4; i++)
                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 */
        priv->txq_ctx_active_msk = 0;
index 290a2081469247526a24ee954957d047e8b2b8ae..078a23e5d99decfd05cd0a0081fcebe5a507a1a8 100644 (file)
@@ -220,6 +220,7 @@ static inline u32 iwl_tx_status_to_mac80211(u32 status)
        case TX_STATUS_DIRECT_DONE:
                return IEEE80211_TX_STAT_ACK;
        case TX_STATUS_FAIL_DEST_PS:
+       case TX_STATUS_FAIL_PASSIVE_NO_RX:
                return IEEE80211_TX_STAT_TX_FILTERED;
        default:
                return 0;
index 72133368c1f53f53baa19a217a3c56cbf6254593..14f7d8fb6886f7809b53bd90d92f7c7503eacb93 100644 (file)
@@ -1170,6 +1170,8 @@ struct iwl_rxon_context {
                bool enabled, is_40mhz;
                u8 extension_chan_offset;
        } ht;
+
+       bool last_tx_rejected;
 };
 
 enum iwl_scan_type {
index 5da5761c74b176f3705443de1d66ba116b13f12f..9309ff2df4c219e1b44fc882edab4e16c728300e 100644 (file)
@@ -131,6 +131,19 @@ static inline void iwl_stop_queue(struct iwl_priv *priv,
                        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_wake_queue DO_NOT_USE_ieee80211_wake_queue
 
index c421f566982f6afd2195942f128535316f8db8af..4472761fc5917a540e21aab6fdb4f63c760c4263 100644 (file)
@@ -873,6 +873,7 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv,
 {
        struct sk_buff *skb;
        __le16 fc = hdr->frame_control;
+       struct iwl_rxon_context *ctx;
 
        /* We only process data packets if the interface is open */
        if (unlikely(!priv->is_open)) {
@@ -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);
 
        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));
 
        ieee80211_rx(priv->hw, skb);