Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[cascardo/linux.git] / drivers / net / wireless / iwlwifi / iwl-rx.c
index 6f36b6e..0f718f6 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -473,8 +473,8 @@ int iwl_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
                           (rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)|
                           (rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS));
 
-       /* Set interrupt coalescing timer to 64 x 32 = 2048 usecs */
-       iwl_write8(priv, CSR_INT_COALESCING, 0x40);
+       /* Set interrupt coalescing timer to default (2048 usecs) */
+       iwl_write8(priv, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF);
 
        return 0;
 }
@@ -499,9 +499,10 @@ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv,
        struct iwl_missed_beacon_notif *missed_beacon;
 
        missed_beacon = &pkt->u.missed_beacon;
-       if (le32_to_cpu(missed_beacon->consequtive_missed_beacons) > 5) {
+       if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) >
+           priv->missed_beacon_threshold) {
                IWL_DEBUG_CALIB(priv, "missed bcn cnsq %d totl %d rcd %d expctd %d\n",
-                   le32_to_cpu(missed_beacon->consequtive_missed_beacons),
+                   le32_to_cpu(missed_beacon->consecutive_missed_beacons),
                    le32_to_cpu(missed_beacon->total_missed_becons),
                    le32_to_cpu(missed_beacon->num_recvd_beacons),
                    le32_to_cpu(missed_beacon->num_expected_beacons));
@@ -511,6 +512,24 @@ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv,
 }
 EXPORT_SYMBOL(iwl_rx_missed_beacon_notif);
 
+void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv,
+                                         struct iwl_rx_mem_buffer *rxb)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_spectrum_notification *report = &(pkt->u.spectrum_notif);
+
+       if (!report->state) {
+               IWL_DEBUG_11H(priv,
+                       "Spectrum Measure Notification: Start\n");
+               return;
+       }
+
+       memcpy(&priv->measure_report, report, sizeof(*report));
+       priv->measurement_status |= MEASUREMENT_READY;
+}
+EXPORT_SYMBOL(iwl_rx_spectrum_measure_notif);
+
+
 
 /* Calculate noise level, based on measurements during network silence just
  *   before arriving beacon.  This measurement can be done only if we know
@@ -564,15 +583,24 @@ static void iwl_accumulative_statistics(struct iwl_priv *priv,
        int i;
        __le32 *prev_stats;
        u32 *accum_stats;
+       u32 *delta, *max_delta;
 
        prev_stats = (__le32 *)&priv->statistics;
        accum_stats = (u32 *)&priv->accum_statistics;
+       delta = (u32 *)&priv->delta_statistics;
+       max_delta = (u32 *)&priv->max_delta;
 
        for (i = sizeof(__le32); i < sizeof(struct iwl_notif_statistics);
-            i += sizeof(__le32), stats++, prev_stats++, accum_stats++)
-               if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats))
-                       *accum_stats += (le32_to_cpu(*stats) -
+            i += sizeof(__le32), stats++, prev_stats++, delta++,
+            max_delta++, accum_stats++) {
+               if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) {
+                       *delta = (le32_to_cpu(*stats) -
                                le32_to_cpu(*prev_stats));
+                       *accum_stats += *delta;
+                       if (*delta > *max_delta)
+                               *max_delta = *delta;
+               }
+       }
 
        /* reset accumulative statistics for "no-counter" type statistics */
        priv->accum_statistics.general.temperature =
@@ -592,11 +620,15 @@ static void iwl_accumulative_statistics(struct iwl_priv *priv,
 
 #define REG_RECALIB_PERIOD (60)
 
+#define PLCP_MSG "plcp_err exceeded %u, %u, %u, %u, %u, %d, %u mSecs\n"
 void iwl_rx_statistics(struct iwl_priv *priv,
                              struct iwl_rx_mem_buffer *rxb)
 {
        int change;
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       int combined_plcp_delta;
+       unsigned int plcp_msec;
+       unsigned long plcp_received_jiffies;
 
        IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n",
                     (int)sizeof(priv->statistics),
@@ -611,6 +643,56 @@ void iwl_rx_statistics(struct iwl_priv *priv,
 #ifdef CONFIG_IWLWIFI_DEBUG
        iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats);
 #endif
+       /*
+        * check for plcp_err and trigger radio reset if it exceeds
+        * the plcp error threshold plcp_delta.
+        */
+       plcp_received_jiffies = jiffies;
+       plcp_msec = jiffies_to_msecs((long) plcp_received_jiffies -
+                                       (long) priv->plcp_jiffies);
+       priv->plcp_jiffies = plcp_received_jiffies;
+       /*
+        * check to make sure plcp_msec is not 0 to prevent division
+        * by zero.
+        */
+       if (plcp_msec) {
+               combined_plcp_delta =
+                       (le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err) -
+                       le32_to_cpu(priv->statistics.rx.ofdm.plcp_err)) +
+                       (le32_to_cpu(pkt->u.stats.rx.ofdm_ht.plcp_err) -
+                       le32_to_cpu(priv->statistics.rx.ofdm_ht.plcp_err));
+
+               if ((combined_plcp_delta > 0) &&
+                       ((combined_plcp_delta * 100) / plcp_msec) >
+                       priv->cfg->plcp_delta_threshold) {
+                       /*
+                        * if plcp_err exceed the threshold, the following
+                        * data is printed in csv format:
+                        *    Text: plcp_err exceeded %d,
+                        *    Received ofdm.plcp_err,
+                        *    Current ofdm.plcp_err,
+                        *    Received ofdm_ht.plcp_err,
+                        *    Current ofdm_ht.plcp_err,
+                        *    combined_plcp_delta,
+                        *    plcp_msec
+                        */
+                       IWL_DEBUG_RADIO(priv, PLCP_MSG,
+                               priv->cfg->plcp_delta_threshold,
+                               le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err),
+                               le32_to_cpu(priv->statistics.rx.ofdm.plcp_err),
+                               le32_to_cpu(pkt->u.stats.rx.ofdm_ht.plcp_err),
+                               le32_to_cpu(
+                                       priv->statistics.rx.ofdm_ht.plcp_err),
+                               combined_plcp_delta, plcp_msec);
+
+                       /*
+                        * Reset the RF radio due to the high plcp
+                        * error rate
+                        */
+                       iwl_force_rf_reset(priv);
+               }
+       }
+
        memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics));
 
        set_bit(STATUS_STATISTICS, &priv->status);
@@ -638,11 +720,13 @@ void iwl_reply_statistics(struct iwl_priv *priv,
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
 
        if (le32_to_cpu(pkt->u.stats.flag) & UCODE_STATISTICS_CLEAR_MSK) {
-               memset(&priv->statistics, 0,
-                       sizeof(struct iwl_notif_statistics));
 #ifdef CONFIG_IWLWIFI_DEBUG
                memset(&priv->accum_statistics, 0,
                        sizeof(struct iwl_notif_statistics));
+               memset(&priv->delta_statistics, 0,
+                       sizeof(struct iwl_notif_statistics));
+               memset(&priv->max_delta, 0,
+                       sizeof(struct iwl_notif_statistics));
 #endif
                IWL_DEBUG_RX(priv, "Statistics have been cleared\n");
        }
@@ -928,7 +1012,10 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv,
        if (ieee80211_is_mgmt(fc) ||
            ieee80211_has_protected(fc) ||
            ieee80211_has_morefrags(fc) ||
-           le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)
+           le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG ||
+           (ieee80211_is_data_qos(fc) &&
+            *ieee80211_get_qos_ctl(hdr) &
+            IEEE80211_QOS_CONTROL_A_MSDU_PRESENT))
                ret = skb_linearize(skb);
        else
                ret = __pskb_pull_tail(skb, min_t(u16, IWL_LINK_HDR_MAX, len)) ?