iwlwifi: configure missed beacon threshold
[safe/jmp/linux-2.6] / drivers / net / wireless / iwlwifi / iwl3945-base.c
index 05f1185..9c0b6eb 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.
@@ -33,6 +33,7 @@
 #include <linux/pci.h>
 #include <linux/dma-mapping.h>
 #include <linux/delay.h>
+#include <linux/sched.h>
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
 #include <linux/wireless.h>
 #define VS
 #endif
 
-#define IWL39_VERSION "1.2.26k" VD VS
-#define DRV_COPYRIGHT  "Copyright(c) 2003-2009 Intel Corporation"
+#define DRV_VERSION  IWLWIFI_VERSION VD VS
+#define DRV_COPYRIGHT  "Copyright(c) 2003-2010 Intel Corporation"
 #define DRV_AUTHOR     "<ilw@linux.intel.com>"
-#define DRV_VERSION     IWL39_VERSION
-
 
 MODULE_DESCRIPTION(DRV_DESCRIPTION);
 MODULE_VERSION(DRV_VERSION);
@@ -549,6 +548,9 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
        txq = &priv->txq[txq_id];
        q = &txq->q;
 
+       if ((iwl_queue_space(q) < q->high_mark))
+               goto drop;
+
        spin_lock_irqsave(&priv->lock, flags);
 
        idx = get_cmd_index(q, q->write_ptr, 0);
@@ -813,7 +815,7 @@ static int iwl3945_get_measurement(struct iwl_priv *priv,
                break;
        }
 
-       free_pages(cmd.reply_page, priv->hw_params.rx_page_order);
+       iwl_free_pages(priv, cmd.reply_page);
 
        return rc;
 }
@@ -1199,9 +1201,7 @@ void iwl3945_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);
@@ -1248,10 +1248,8 @@ static void iwl3945_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rx
                        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--;
                }
        }
 
@@ -1301,47 +1299,6 @@ int iwl3945_calc_db_from_ratio(int sig_ratio)
        return (int)ratio2dB[sig_ratio];
 }
 
-#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. */
-int iwl3945_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;
-}
-
 /**
  * iwl3945_rx_handle - Main entry function for receiving responses from uCode
  *
@@ -1482,7 +1439,6 @@ static inline void iwl_synchronize_irq(struct iwl_priv *priv)
        tasklet_kill(&priv->irq_tasklet);
 }
 
-#ifdef CONFIG_IWLWIFI_DEBUG
 static const char *desc_lookup(int i)
 {
        switch (i) {
@@ -1562,8 +1518,9 @@ void iwl3945_dump_nic_error_log(struct iwl_priv *priv)
  * iwl3945_print_event_log - Dump error event log to syslog
  *
  */
-static void iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx,
-                               u32 num_events, u32 mode)
+static int iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx,
+                                 u32 num_events, u32 mode,
+                                 int pos, char **buf, size_t bufsz)
 {
        u32 i;
        u32 base;       /* SRAM byte address of event log header */
@@ -1573,7 +1530,7 @@ static void iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx,
        unsigned long reg_flags;
 
        if (num_events == 0)
-               return;
+               return pos;
 
        base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
 
@@ -1599,24 +1556,81 @@ static void iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx,
                time = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
                if (mode == 0) {
                        /* data, ev */
-                       IWL_ERR(priv, "0x%08x\t%04u\n", time, ev);
-                       trace_iwlwifi_dev_ucode_event(priv, 0, time, ev);
+                       if (bufsz) {
+                               pos += scnprintf(*buf + pos, bufsz - pos,
+                                               "0x%08x:%04u\n",
+                                               time, ev);
+                       } else {
+                               IWL_ERR(priv, "0x%08x\t%04u\n", time, ev);
+                               trace_iwlwifi_dev_ucode_event(priv, 0,
+                                                             time, ev);
+                       }
                } else {
                        data = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
-                       IWL_ERR(priv, "%010u\t0x%08x\t%04u\n", time, data, ev);
-                       trace_iwlwifi_dev_ucode_event(priv, time, data, ev);
+                       if (bufsz) {
+                               pos += scnprintf(*buf + pos, bufsz - pos,
+                                               "%010u:0x%08x:%04u\n",
+                                                time, data, ev);
+                       } else {
+                               IWL_ERR(priv, "%010u\t0x%08x\t%04u\n",
+                                       time, data, ev);
+                               trace_iwlwifi_dev_ucode_event(priv, time,
+                                                             data, ev);
+                       }
                }
        }
 
        /* Allow device to power down */
        iwl_release_nic_access(priv);
        spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
+       return pos;
+}
+
+/**
+ * iwl3945_print_last_event_logs - Dump the newest # of event log to syslog
+ */
+static int iwl3945_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
+                                     u32 num_wraps, u32 next_entry,
+                                     u32 size, u32 mode,
+                                     int pos, char **buf, size_t bufsz)
+{
+       /*
+        * display the newest DEFAULT_LOG_ENTRIES entries
+        * i.e the entries just before the next ont that uCode would fill.
+        */
+       if (num_wraps) {
+               if (next_entry < size) {
+                       pos = iwl3945_print_event_log(priv,
+                                            capacity - (size - next_entry),
+                                            size - next_entry, mode,
+                                            pos, buf, bufsz);
+                       pos = iwl3945_print_event_log(priv, 0,
+                                                     next_entry, mode,
+                                                     pos, buf, bufsz);
+               } else
+                       pos = iwl3945_print_event_log(priv, next_entry - size,
+                                                     size, mode,
+                                                     pos, buf, bufsz);
+       } else {
+               if (next_entry < size)
+                       pos = iwl3945_print_event_log(priv, 0,
+                                                     next_entry, mode,
+                                                     pos, buf, bufsz);
+               else
+                       pos = iwl3945_print_event_log(priv, next_entry - size,
+                                                     size, mode,
+                                                     pos, buf, bufsz);
+       }
+       return pos;
 }
 
 /* For sanity check only.  Actual size is determined by uCode, typ. 512 */
 #define IWL3945_MAX_EVENT_LOG_SIZE (512)
 
-void iwl3945_dump_nic_event_log(struct iwl_priv *priv)
+#define DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES (20)
+
+int iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
+                           char **buf, bool display)
 {
        u32 base;       /* SRAM byte address of event log header */
        u32 capacity;   /* event log capacity in # entries */
@@ -1624,11 +1638,13 @@ void iwl3945_dump_nic_event_log(struct iwl_priv *priv)
        u32 num_wraps;  /* # times uCode wrapped to top of log */
        u32 next_entry; /* index of next entry to be written by uCode */
        u32 size;       /* # entries that we'll print */
+       int pos = 0;
+       size_t bufsz = 0;
 
        base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
        if (!iwl3945_hw_valid_rtc_data_addr(base)) {
                IWL_ERR(priv, "Invalid event log pointer 0x%08X\n", base);
-               return;
+               return  -EINVAL;
        }
 
        /* event log header */
@@ -1654,32 +1670,55 @@ void iwl3945_dump_nic_event_log(struct iwl_priv *priv)
        /* bail out if nothing in log */
        if (size == 0) {
                IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n");
-               return;
+               return pos;
        }
 
-       IWL_ERR(priv, "Start IWL Event Log Dump: display count %d, wraps %d\n",
-                 size, num_wraps);
-
-       /* if uCode has wrapped back to top of log, start at the oldest entry,
-        * i.e the next one that uCode would fill. */
-       if (num_wraps)
-               iwl3945_print_event_log(priv, next_entry,
-                                   capacity - next_entry, mode);
-
-       /* (then/else) start at top of log */
-       iwl3945_print_event_log(priv, 0, next_entry, mode);
-
-}
+#ifdef CONFIG_IWLWIFI_DEBUG
+       if (!(iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) && !full_log)
+               size = (size > DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES)
+                       ? DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES : size;
 #else
-void iwl3945_dump_nic_event_log(struct iwl_priv *priv)
-{
-}
+       size = (size > DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES)
+               ? DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES : size;
+#endif
 
-void iwl3945_dump_nic_error_log(struct iwl_priv *priv)
-{
-}
+       IWL_ERR(priv, "Start IWL Event Log Dump: display last %d count\n",
+                 size);
 
+#ifdef CONFIG_IWLWIFI_DEBUG
+       if (display) {
+               if (full_log)
+                       bufsz = capacity * 48;
+               else
+                       bufsz = size * 48;
+               *buf = kmalloc(bufsz, GFP_KERNEL);
+               if (!*buf)
+                       return -ENOMEM;
+       }
+       if ((iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) || full_log) {
+               /* if uCode has wrapped back to top of log,
+                * start at the oldest entry,
+                * i.e the next one that uCode would fill.
+                */
+               if (num_wraps)
+                       pos = iwl3945_print_event_log(priv, next_entry,
+                                               capacity - next_entry, mode,
+                                               pos, buf, bufsz);
+
+               /* (then/else) start at top of log */
+               pos = iwl3945_print_event_log(priv, 0, next_entry, mode,
+                                             pos, buf, bufsz);
+       } else
+               pos = iwl3945_print_last_event_logs(priv, capacity, num_wraps,
+                                                   next_entry, size, mode,
+                                                   pos, buf, bufsz);
+#else
+       pos = iwl3945_print_last_event_logs(priv, capacity, num_wraps,
+                                           next_entry, size, mode,
+                                           pos, buf, bufsz);
 #endif
+       return pos;
+}
 
 static void iwl3945_irq_tasklet(struct iwl_priv *priv)
 {
@@ -2493,7 +2532,7 @@ static void iwl3945_alive_start(struct iwl_priv *priv)
        priv->active_rate = priv->rates_mask;
        priv->active_rate_basic = priv->rates_mask & IWL_BASIC_RATES_MASK;
 
-       iwl_power_update_mode(priv, false);
+       iwl_power_update_mode(priv, true);
 
        if (iwl_is_associated(priv)) {
                struct iwl3945_rxon_cmd *active_rxon =
@@ -3649,7 +3688,7 @@ static ssize_t show_statistics(struct device *d,
                return -EAGAIN;
 
        mutex_lock(&priv->mutex);
-       rc = iwl_send_statistics_request(priv, 0);
+       rc = iwl_send_statistics_request(priv, CMD_SYNC, false);
        mutex_unlock(&priv->mutex);
 
        if (rc) {
@@ -3827,7 +3866,6 @@ static int iwl3945_init_drv(struct iwl_priv *priv)
        priv->retry_rate = 1;
        priv->ibss_beacon = NULL;
 
-       spin_lock_init(&priv->lock);
        spin_lock_init(&priv->sta_lock);
        spin_lock_init(&priv->hcmd_lock);
 
@@ -3843,6 +3881,7 @@ static int iwl3945_init_drv(struct iwl_priv *priv)
        priv->band = IEEE80211_BAND_2GHZ;
 
        priv->iw_mode = NL80211_IFTYPE_STATION;
+       priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF;
 
        iwl_reset_qos(priv);
 
@@ -3896,18 +3935,18 @@ static int iwl3945_setup_mac(struct iwl_priv *priv)
        /* Tell mac80211 our characteristics */
        hw->flags = IEEE80211_HW_SIGNAL_DBM |
                    IEEE80211_HW_NOISE_DBM |
-                   IEEE80211_HW_SPECTRUM_MGMT |
-                   IEEE80211_HW_SUPPORTS_PS |
-                   IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
+                   IEEE80211_HW_SPECTRUM_MGMT;
+
+       if (!priv->cfg->broken_powersave)
+               hw->flags |= IEEE80211_HW_SUPPORTS_PS |
+                            IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
 
        hw->wiphy->interface_modes =
                BIT(NL80211_IFTYPE_STATION) |
                BIT(NL80211_IFTYPE_ADHOC);
 
-       hw->wiphy->custom_regulatory = true;
-
-       /* Firmware does not support this */
-       hw->wiphy->disable_beacon_hints = true;
+       hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY |
+                           WIPHY_FLAG_DISABLE_BEACON_HINTS;
 
        hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX_3945;
        /* we create the 802.11 header and a zero-length SSID element */
@@ -4019,10 +4058,11 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
         * PCI Tx retries from interfering with C3 CPU state */
        pci_write_config_byte(pdev, 0x41, 0x00);
 
-       /* this spin lock will be used in apm_ops.init and EEPROM access
+       /* these spin locks will be used in apm_ops.init and EEPROM access
         * we should init now
         */
        spin_lock_init(&priv->reg_lock);
+       spin_lock_init(&priv->lock);
 
        /***********************
         * 4. Read EEPROM