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 6090bc1..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.
@@ -345,10 +345,8 @@ void iwl_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
                        pci_unmap_page(priv->pci_dev, rxq->pool[i].page_dma,
                                PAGE_SIZE << priv->hw_params.rx_page_order,
                                PCI_DMA_FROMDEVICE);
-                       __free_pages(rxq->pool[i].page,
-                                    priv->hw_params.rx_page_order);
+                       __iwl_free_pages(priv, rxq->pool[i].page);
                        rxq->pool[i].page = NULL;
-                       priv->alloc_rxb_page--;
                }
        }
 
@@ -416,9 +414,7 @@ void iwl_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
                        pci_unmap_page(priv->pci_dev, rxq->pool[i].page_dma,
                                PAGE_SIZE << priv->hw_params.rx_page_order,
                                PCI_DMA_FROMDEVICE);
-                       priv->alloc_rxb_page--;
-                       __free_pages(rxq->pool[i].page,
-                                    priv->hw_params.rx_page_order);
+                       __iwl_free_pages(priv, rxq->pool[i].page);
                        rxq->pool[i].page = NULL;
                }
                list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
@@ -477,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;
 }
@@ -503,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));
@@ -515,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
@@ -568,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 =
@@ -596,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),
@@ -615,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);
@@ -642,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");
        }
@@ -654,47 +734,6 @@ void iwl_reply_statistics(struct iwl_priv *priv,
 }
 EXPORT_SYMBOL(iwl_reply_statistics);
 
-#define PERFECT_RSSI (-20) /* dBm */
-#define WORST_RSSI (-95)   /* dBm */
-#define RSSI_RANGE (PERFECT_RSSI - WORST_RSSI)
-
-/* Calculate an indication of rx signal quality (a percentage, not dBm!).
- * See http://www.ces.clemson.edu/linux/signal_quality.shtml for info
- *   about formulas used below. */
-static int iwl_calc_sig_qual(int rssi_dbm, int noise_dbm)
-{
-       int sig_qual;
-       int degradation = PERFECT_RSSI - rssi_dbm;
-
-       /* If we get a noise measurement, use signal-to-noise ratio (SNR)
-        * as indicator; formula is (signal dbm - noise dbm).
-        * SNR at or above 40 is a great signal (100%).
-        * Below that, scale to fit SNR of 0 - 40 dB within 0 - 100% indicator.
-        * Weakest usable signal is usually 10 - 15 dB SNR. */
-       if (noise_dbm) {
-               if (rssi_dbm - noise_dbm >= 40)
-                       return 100;
-               else if (rssi_dbm < noise_dbm)
-                       return 0;
-               sig_qual = ((rssi_dbm - noise_dbm) * 5) / 2;
-
-       /* Else use just the signal level.
-        * This formula is a least squares fit of data points collected and
-        *   compared with a reference system that had a percentage (%) display
-        *   for signal quality. */
-       } else
-               sig_qual = (100 * (RSSI_RANGE * RSSI_RANGE) - degradation *
-                           (15 * RSSI_RANGE + 62 * degradation)) /
-                          (RSSI_RANGE * RSSI_RANGE);
-
-       if (sig_qual > 100)
-               sig_qual = 100;
-       else if (sig_qual < 1)
-               sig_qual = 0;
-
-       return sig_qual;
-}
-
 /* Calc max signal level (dBm) among 3 possible receivers */
 static inline int iwl_calc_rssi(struct iwl_priv *priv,
                                struct iwl_rx_phy_res *rx_resp)
@@ -973,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)) ?
@@ -1105,11 +1147,8 @@ void iwl_rx_reply_rx(struct iwl_priv *priv,
        if (iwl_is_associated(priv) &&
            !test_bit(STATUS_SCANNING, &priv->status)) {
                rx_status.noise = priv->last_rx_noise;
-               rx_status.qual = iwl_calc_sig_qual(rx_status.signal,
-                                                        rx_status.noise);
        } else {
                rx_status.noise = IWL_NOISE_MEAS_NOT_AVAILABLE;
-               rx_status.qual = iwl_calc_sig_qual(rx_status.signal, 0);
        }
 
        /* Reset beacon noise level if not associated. */
@@ -1122,8 +1161,8 @@ void iwl_rx_reply_rx(struct iwl_priv *priv,
                iwl_dbg_report_frame(priv, phy_res, len, header, 1);
 #endif
        iwl_dbg_log_rx_data_frame(priv, len, header);
-       IWL_DEBUG_STATS_LIMIT(priv, "Rssi %d, noise %d, qual %d, TSF %llu\n",
-               rx_status.signal, rx_status.noise, rx_status.qual,
+       IWL_DEBUG_STATS_LIMIT(priv, "Rssi %d, noise %d, TSF %llu\n",
+               rx_status.signal, rx_status.noise,
                (unsigned long long)rx_status.mactime);
 
        /*