[PATCH] libertas: reorganize and simplify init sequence
[safe/jmp/linux-2.6] / drivers / net / wireless / ipw2200.c
index 04efe69..61497c4 100644 (file)
@@ -70,7 +70,7 @@
 #define VQ
 #endif
 
-#define IPW2200_VERSION "1.1.2" VK VD VM VP VR VQ
+#define IPW2200_VERSION "1.2.2" VK VD VM VP VR VQ
 #define DRV_DESCRIPTION        "Intel(R) PRO/Wireless 2200/2915 Network Driver"
 #define DRV_COPYRIGHT  "Copyright(c) 2003-2006 Intel Corporation"
 #define DRV_VERSION     IPW2200_VERSION
@@ -83,9 +83,7 @@ MODULE_AUTHOR(DRV_COPYRIGHT);
 MODULE_LICENSE("GPL");
 
 static int cmdlog = 0;
-#ifdef CONFIG_IPW2200_DEBUG
 static int debug = 0;
-#endif
 static int channel = 0;
 static int mode = 0;
 
@@ -189,9 +187,9 @@ static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *);
 static void ipw_rx_queue_free(struct ipw_priv *, struct ipw_rx_queue *);
 static void ipw_rx_queue_replenish(void *);
 static int ipw_up(struct ipw_priv *);
-static void ipw_bg_up(void *);
+static void ipw_bg_up(struct work_struct *work);
 static void ipw_down(struct ipw_priv *);
-static void ipw_bg_down(void *);
+static void ipw_bg_down(struct work_struct *work);
 static int ipw_config(struct ipw_priv *);
 static int init_supported_rates(struct ipw_priv *priv,
                                struct ipw_supported_rates *prates);
@@ -864,11 +862,12 @@ static void ipw_led_link_on(struct ipw_priv *priv)
        spin_unlock_irqrestore(&priv->lock, flags);
 }
 
-static void ipw_bg_led_link_on(void *data)
+static void ipw_bg_led_link_on(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, led_link_on.work);
        mutex_lock(&priv->mutex);
-       ipw_led_link_on(data);
+       ipw_led_link_on(priv);
        mutex_unlock(&priv->mutex);
 }
 
@@ -908,11 +907,12 @@ static void ipw_led_link_off(struct ipw_priv *priv)
        spin_unlock_irqrestore(&priv->lock, flags);
 }
 
-static void ipw_bg_led_link_off(void *data)
+static void ipw_bg_led_link_off(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, led_link_off.work);
        mutex_lock(&priv->mutex);
-       ipw_led_link_off(data);
+       ipw_led_link_off(priv);
        mutex_unlock(&priv->mutex);
 }
 
@@ -987,11 +987,12 @@ static void ipw_led_activity_off(struct ipw_priv *priv)
        spin_unlock_irqrestore(&priv->lock, flags);
 }
 
-static void ipw_bg_led_activity_off(void *data)
+static void ipw_bg_led_activity_off(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, led_act_off.work);
        mutex_lock(&priv->mutex);
-       ipw_led_activity_off(data);
+       ipw_led_activity_off(priv);
        mutex_unlock(&priv->mutex);
 }
 
@@ -1750,7 +1751,7 @@ static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio)
                        /* Make sure the RF_KILL check timer is running */
                        cancel_delayed_work(&priv->rf_kill);
                        queue_delayed_work(priv->workqueue, &priv->rf_kill,
-                                          2 * HZ);
+                                          round_jiffies(2 * HZ));
                } else
                        queue_work(priv->workqueue, &priv->up);
        }
@@ -1846,6 +1847,52 @@ static ssize_t store_net_stats(struct device *d, struct device_attribute *attr,
 static DEVICE_ATTR(net_stats, S_IWUSR | S_IRUGO,
                   show_net_stats, store_net_stats);
 
+static ssize_t show_channels(struct device *d,
+                            struct device_attribute *attr,
+                            char *buf)
+{
+       struct ipw_priv *priv = dev_get_drvdata(d);
+       const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
+       int len = 0, i;
+
+       len = sprintf(&buf[len],
+                     "Displaying %d channels in 2.4Ghz band "
+                     "(802.11bg):\n", geo->bg_channels);
+
+       for (i = 0; i < geo->bg_channels; i++) {
+               len += sprintf(&buf[len], "%d: BSS%s%s, %s, Band %s.\n",
+                              geo->bg[i].channel,
+                              geo->bg[i].flags & IEEE80211_CH_RADAR_DETECT ?
+                              " (radar spectrum)" : "",
+                              ((geo->bg[i].flags & IEEE80211_CH_NO_IBSS) ||
+                               (geo->bg[i].flags & IEEE80211_CH_RADAR_DETECT))
+                              ? "" : ", IBSS",
+                              geo->bg[i].flags & IEEE80211_CH_PASSIVE_ONLY ?
+                              "passive only" : "active/passive",
+                              geo->bg[i].flags & IEEE80211_CH_B_ONLY ?
+                              "B" : "B/G");
+       }
+
+       len += sprintf(&buf[len],
+                      "Displaying %d channels in 5.2Ghz band "
+                      "(802.11a):\n", geo->a_channels);
+       for (i = 0; i < geo->a_channels; i++) {
+               len += sprintf(&buf[len], "%d: BSS%s%s, %s.\n",
+                              geo->a[i].channel,
+                              geo->a[i].flags & IEEE80211_CH_RADAR_DETECT ?
+                              " (radar spectrum)" : "",
+                              ((geo->a[i].flags & IEEE80211_CH_NO_IBSS) ||
+                               (geo->a[i].flags & IEEE80211_CH_RADAR_DETECT))
+                              ? "" : ", IBSS",
+                              geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY ?
+                              "passive only" : "active/passive");
+       }
+
+       return len;
+}
+
+static DEVICE_ATTR(channels, S_IRUSR, show_channels, NULL);
+
 static void notify_wx_assoc_event(struct ipw_priv *priv)
 {
        union iwreq_data wrqu;
@@ -2230,11 +2277,12 @@ static void ipw_adapter_restart(void *adapter)
        }
 }
 
-static void ipw_bg_adapter_restart(void *data)
+static void ipw_bg_adapter_restart(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, adapter_restart);
        mutex_lock(&priv->mutex);
-       ipw_adapter_restart(data);
+       ipw_adapter_restart(priv);
        mutex_unlock(&priv->mutex);
 }
 
@@ -2251,11 +2299,12 @@ static void ipw_scan_check(void *data)
        }
 }
 
-static void ipw_bg_scan_check(void *data)
+static void ipw_bg_scan_check(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, scan_check.work);
        mutex_lock(&priv->mutex);
-       ipw_scan_check(data);
+       ipw_scan_check(priv);
        mutex_unlock(&priv->mutex);
 }
 
@@ -2457,7 +2506,7 @@ static int ipw_send_power_mode(struct ipw_priv *priv, u32 mode)
                break;
        }
 
-       param = cpu_to_le32(mode);
+       param = cpu_to_le32(param);
        return ipw_send_cmd_pdu(priv, IPW_CMD_POWER_MODE, sizeof(param),
                                &param);
 }
@@ -3833,17 +3882,19 @@ static int ipw_disassociate(void *data)
        return 1;
 }
 
-static void ipw_bg_disassociate(void *data)
+static void ipw_bg_disassociate(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, disassociate);
        mutex_lock(&priv->mutex);
-       ipw_disassociate(data);
+       ipw_disassociate(priv);
        mutex_unlock(&priv->mutex);
 }
 
-static void ipw_system_config(void *data)
+static void ipw_system_config(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, system_config);
 
 #ifdef CONFIG_IPW2200_PROMISCUOUS
        if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) {
@@ -4210,11 +4261,12 @@ static void ipw_gather_stats(struct ipw_priv *priv)
                           IPW_STATS_INTERVAL);
 }
 
-static void ipw_bg_gather_stats(void *data)
+static void ipw_bg_gather_stats(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, gather_stats.work);
        mutex_lock(&priv->mutex);
-       ipw_gather_stats(data);
+       ipw_gather_stats(priv);
        mutex_unlock(&priv->mutex);
 }
 
@@ -4270,8 +4322,8 @@ static void ipw_handle_missed_beacon(struct ipw_priv *priv,
                if (!(priv->status & STATUS_ROAMING)) {
                        priv->status |= STATUS_ROAMING;
                        if (!(priv->status & STATUS_SCANNING))
-                               queue_work(priv->workqueue,
-                                          &priv->request_scan);
+                               queue_delayed_work(priv->workqueue,
+                                                  &priv->request_scan, 0);
                }
                return;
        }
@@ -4609,8 +4661,8 @@ static void ipw_rx_notification(struct ipw_priv *priv,
 #ifdef CONFIG_IPW2200_MONITOR
                        if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
                                priv->status |= STATUS_SCAN_FORCED;
-                               queue_work(priv->workqueue,
-                                          &priv->request_scan);
+                               queue_delayed_work(priv->workqueue,
+                                                  &priv->request_scan, 0);
                                break;
                        }
                        priv->status &= ~STATUS_SCAN_FORCED;
@@ -4633,12 +4685,13 @@ static void ipw_rx_notification(struct ipw_priv *priv,
                                        /* Don't schedule if we aborted the scan */
                                        priv->status &= ~STATUS_ROAMING;
                        } else if (priv->status & STATUS_SCAN_PENDING)
-                               queue_work(priv->workqueue,
-                                          &priv->request_scan);
+                               queue_delayed_work(priv->workqueue,
+                                                  &priv->request_scan, 0);
                        else if (priv->config & CFG_BACKGROUND_SCAN
                                 && priv->status & STATUS_ASSOCIATED)
                                queue_delayed_work(priv->workqueue,
-                                                  &priv->request_scan, HZ);
+                                                  &priv->request_scan,
+                                                  round_jiffies(HZ));
 
                        /* Send an empty event to user space.
                         * We don't send the received data on the event because
@@ -5057,11 +5110,12 @@ static void ipw_rx_queue_replenish(void *data)
        ipw_rx_queue_restock(priv);
 }
 
-static void ipw_bg_rx_queue_replenish(void *data)
+static void ipw_bg_rx_queue_replenish(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, rx_replenish);
        mutex_lock(&priv->mutex);
-       ipw_rx_queue_replenish(data);
+       ipw_rx_queue_replenish(priv);
        mutex_unlock(&priv->mutex);
 }
 
@@ -5491,9 +5545,10 @@ static int ipw_find_adhoc_network(struct ipw_priv *priv,
        return 1;
 }
 
-static void ipw_merge_adhoc_network(void *data)
+static void ipw_merge_adhoc_network(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, merge_networks);
        struct ieee80211_network *network = NULL;
        struct ipw_network_match match = {
                .network = priv->assoc_network
@@ -5950,11 +6005,12 @@ static void ipw_adhoc_check(void *data)
                           priv->assoc_request.beacon_interval);
 }
 
-static void ipw_bg_adhoc_check(void *data)
+static void ipw_bg_adhoc_check(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, adhoc_check.work);
        mutex_lock(&priv->mutex);
-       ipw_adhoc_check(data);
+       ipw_adhoc_check(priv);
        mutex_unlock(&priv->mutex);
 }
 
@@ -6169,7 +6225,7 @@ static void ipw_add_scan_channels(struct ipw_priv *priv,
        }
 }
 
-static int ipw_request_scan(struct ipw_priv *priv)
+static int ipw_request_scan_helper(struct ipw_priv *priv, int type)
 {
        struct ipw_scan_request_ext scan;
        int err = 0, scan_type;
@@ -6200,19 +6256,29 @@ static int ipw_request_scan(struct ipw_priv *priv)
        }
 
        memset(&scan, 0, sizeof(scan));
+       scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee));
 
-       if (priv->config & CFG_SPEED_SCAN)
+       if (type == IW_SCAN_TYPE_PASSIVE) {
+               IPW_DEBUG_WX("use passive scanning\n");
+               scan_type = IPW_SCAN_PASSIVE_FULL_DWELL_SCAN;
+               scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] =
+                       cpu_to_le16(120);
+               ipw_add_scan_channels(priv, &scan, scan_type);
+               goto send_request;
+       }
+
+       /* Use active scan by default. */
+       if (priv->config & CFG_SPEED_SCAN)
                scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
-                   cpu_to_le16(30);
+                       cpu_to_le16(30);
        else
                scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
-                   cpu_to_le16(20);
+                       cpu_to_le16(20);
 
        scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] =
-           cpu_to_le16(20);
-       scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(120);
+               cpu_to_le16(20);
 
-       scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee));
+       scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(120);
 
 #ifdef CONFIG_IPW2200_MONITOR
        if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
@@ -6249,7 +6315,7 @@ static int ipw_request_scan(struct ipw_priv *priv)
                 *
                 * TODO: Move SPEED SCAN support to all modes and bands */
                scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] =
-                   cpu_to_le16(2000);
+                       cpu_to_le16(2000);
        } else {
 #endif                         /* CONFIG_IPW2200_MONITOR */
                /* If we are roaming, then make this a directed scan for the
@@ -6275,6 +6341,7 @@ static int ipw_request_scan(struct ipw_priv *priv)
        }
 #endif
 
+send_request:
        err = ipw_send_scan_request_ext(priv, &scan);
        if (err) {
                IPW_DEBUG_HC("Sending scan command failed: %08X\n", err);
@@ -6285,16 +6352,31 @@ static int ipw_request_scan(struct ipw_priv *priv)
        priv->status &= ~STATUS_SCAN_PENDING;
        queue_delayed_work(priv->workqueue, &priv->scan_check,
                           IPW_SCAN_CHECK_WATCHDOG);
-      done:
+done:
        mutex_unlock(&priv->mutex);
        return err;
 }
 
-static void ipw_bg_abort_scan(void *data)
+static void ipw_request_passive_scan(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, request_passive_scan);
+       ipw_request_scan_helper(priv, IW_SCAN_TYPE_PASSIVE);
+}
+
+static void ipw_request_scan(struct work_struct *work)
+{
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, request_scan.work);
+       ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE);
+}
+
+static void ipw_bg_abort_scan(struct work_struct *work)
+{
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, abort_scan);
        mutex_lock(&priv->mutex);
-       ipw_abort_scan(data);
+       ipw_abort_scan(priv);
        mutex_unlock(&priv->mutex);
 }
 
@@ -6903,8 +6985,8 @@ static int ipw_qos_association(struct ipw_priv *priv,
 }
 
 /*
-* handling the beaconing responces. if we get different QoS setting
-* of the network from the the associated setting adjust the QoS
+* handling the beaconing responses. if we get different QoS setting
+* off the network from the associated setting, adjust the QoS
 * setting
 */
 static int ipw_qos_association_resp(struct ipw_priv *priv,
@@ -7067,9 +7149,10 @@ static int ipw_qos_set_tx_queue_command(struct ipw_priv *priv,
 /*
 * background support to run QoS activate functionality
 */
-static void ipw_bg_qos_activate(void *data)
+static void ipw_bg_qos_activate(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, qos_activate);
 
        if (priv == NULL)
                return;
@@ -7377,11 +7460,12 @@ static void ipw_roam(void *data)
        priv->status &= ~STATUS_ROAMING;
 }
 
-static void ipw_bg_roam(void *data)
+static void ipw_bg_roam(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, roam);
        mutex_lock(&priv->mutex);
-       ipw_roam(data);
+       ipw_roam(priv);
        mutex_unlock(&priv->mutex);
 }
 
@@ -7462,8 +7546,8 @@ static int ipw_associate(void *data)
                                                   &priv->request_scan,
                                                   SCAN_INTERVAL);
                        else
-                               queue_work(priv->workqueue,
-                                          &priv->request_scan);
+                               queue_delayed_work(priv->workqueue,
+                                                  &priv->request_scan, 0);
                }
 
                return 0;
@@ -7474,11 +7558,12 @@ static int ipw_associate(void *data)
        return 1;
 }
 
-static void ipw_bg_associate(void *data)
+static void ipw_bg_associate(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, associate);
        mutex_lock(&priv->mutex);
-       ipw_associate(data);
+       ipw_associate(priv);
        mutex_unlock(&priv->mutex);
 }
 
@@ -7639,8 +7724,8 @@ static void ipw_handle_data_packet_monitor(struct ipw_priv *priv,
 
        /* Big bitfield of all the fields we provide in radiotap */
        ipw_rt->rt_hdr.it_present =
-           ((1 << IEEE80211_RADIOTAP_FLAGS) |
-            (1 << IEEE80211_RADIOTAP_TSFT) |
+           ((1 << IEEE80211_RADIOTAP_TSFT) |
+            (1 << IEEE80211_RADIOTAP_FLAGS) |
             (1 << IEEE80211_RADIOTAP_RATE) |
             (1 << IEEE80211_RADIOTAP_CHANNEL) |
             (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
@@ -7649,9 +7734,14 @@ static void ipw_handle_data_packet_monitor(struct ipw_priv *priv,
 
        /* Zero the flags, we'll add to them as we go */
        ipw_rt->rt_flags = 0;
+       ipw_rt->rt_tsf = (u64)(frame->parent_tsf[3] << 24 |
+                              frame->parent_tsf[2] << 16 |
+                              frame->parent_tsf[1] << 8  |
+                              frame->parent_tsf[0]);
 
        /* Convert signal to DBM */
        ipw_rt->rt_dbmsignal = antsignal;
+       ipw_rt->rt_dbmnoise = frame->noise;
 
        /* Convert the channel data and set the flags */
        ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(received_channel));
@@ -7767,7 +7857,6 @@ static void ipw_handle_promiscuous_rx(struct ipw_priv *priv,
        s8 noise = frame->noise;
        u8 rate = frame->rate;
        short len = le16_to_cpu(pkt->u.frame.length);
-       u64 tsf = 0;
        struct sk_buff *skb;
        int hdr_only = 0;
        u16 filter = priv->prom_priv->filter;
@@ -7852,8 +7941,8 @@ static void ipw_handle_promiscuous_rx(struct ipw_priv *priv,
 
        /* Big bitfield of all the fields we provide in radiotap */
        ipw_rt->rt_hdr.it_present =
-           ((1 << IEEE80211_RADIOTAP_FLAGS) |
-            (1 << IEEE80211_RADIOTAP_TSFT) |
+           ((1 << IEEE80211_RADIOTAP_TSFT) |
+            (1 << IEEE80211_RADIOTAP_FLAGS) |
             (1 << IEEE80211_RADIOTAP_RATE) |
             (1 << IEEE80211_RADIOTAP_CHANNEL) |
             (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
@@ -7862,8 +7951,10 @@ static void ipw_handle_promiscuous_rx(struct ipw_priv *priv,
 
        /* Zero the flags, we'll add to them as we go */
        ipw_rt->rt_flags = 0;
-
-       ipw_rt->rt_tsf = tsf;
+       ipw_rt->rt_tsf = (u64)(frame->parent_tsf[3] << 24 |
+                              frame->parent_tsf[2] << 16 |
+                              frame->parent_tsf[1] << 8  |
+                              frame->parent_tsf[0]);
 
        /* Convert to DBM */
        ipw_rt->rt_dbmsignal = signal;
@@ -8089,7 +8180,7 @@ static void ipw_handle_mgmt_packet(struct ipw_priv *priv,
                skb->dev = priv->ieee->dev;
 
                /* Point raw at the ieee80211_stats */
-               skb->mac.raw = skb->data;
+               skb_reset_mac_header(skb);
 
                skb->pkt_type = PACKET_OTHERHOST;
                skb->protocol = __constant_htons(ETH_P_80211_STATS);
@@ -8262,7 +8353,7 @@ static void ipw_rx(struct ipw_priv *priv)
                                    ("Notification: subtype=%02X flags=%02X size=%d\n",
                                     pkt->u.notification.subtype,
                                     pkt->u.notification.flags,
-                                    pkt->u.notification.size);
+                                    le16_to_cpu(pkt->u.notification.size));
                                ipw_rx_notification(priv, &pkt->u.notification);
                                break;
                        }
@@ -8861,8 +8952,6 @@ static int ipw_wx_set_essid(struct net_device *dev,
         }
 
        length = min((int)wrqu->essid.length, IW_ESSID_MAX_SIZE);
-       if (!extra[length - 1])
-               length--;
 
        priv->config |= CFG_STATIC_ESSID;
 
@@ -8939,7 +9028,7 @@ static int ipw_wx_get_nick(struct net_device *dev,
        struct ipw_priv *priv = ieee80211_priv(dev);
        IPW_DEBUG_WX("Getting nick\n");
        mutex_lock(&priv->mutex);
-       wrqu->data.length = strlen(priv->nick) + 1;
+       wrqu->data.length = strlen(priv->nick);
        memcpy(extra, priv->nick, wrqu->data.length);
        wrqu->data.flags = 1;   /* active */
        mutex_unlock(&priv->mutex);
@@ -9124,7 +9213,7 @@ static int ipw_wx_set_rts(struct net_device *dev,
 {
        struct ipw_priv *priv = ieee80211_priv(dev);
        mutex_lock(&priv->mutex);
-       if (wrqu->rts.disabled)
+       if (wrqu->rts.disabled || !wrqu->rts.fixed)
                priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
        else {
                if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
@@ -9213,7 +9302,7 @@ static int ipw_wx_set_frag(struct net_device *dev,
 {
        struct ipw_priv *priv = ieee80211_priv(dev);
        mutex_lock(&priv->mutex);
-       if (wrqu->frag.disabled)
+       if (wrqu->frag.disabled || !wrqu->frag.fixed)
                priv->ieee->fts = DEFAULT_FTS;
        else {
                if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
@@ -9258,13 +9347,13 @@ static int ipw_wx_set_retry(struct net_device *dev,
        if (!(wrqu->retry.flags & IW_RETRY_LIMIT))
                return 0;
 
-       if (wrqu->retry.value < 0 || wrqu->retry.value > 255)
+       if (wrqu->retry.value < 0 || wrqu->retry.value >= 255)
                return -EINVAL;
 
        mutex_lock(&priv->mutex);
-       if (wrqu->retry.flags & IW_RETRY_MIN)
+       if (wrqu->retry.flags & IW_RETRY_SHORT)
                priv->short_retry_limit = (u8) wrqu->retry.value;
-       else if (wrqu->retry.flags & IW_RETRY_MAX)
+       else if (wrqu->retry.flags & IW_RETRY_LONG)
                priv->long_retry_limit = (u8) wrqu->retry.value;
        else {
                priv->short_retry_limit = (u8) wrqu->retry.value;
@@ -9293,11 +9382,11 @@ static int ipw_wx_get_retry(struct net_device *dev,
                return -EINVAL;
        }
 
-       if (wrqu->retry.flags & IW_RETRY_MAX) {
-               wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+       if (wrqu->retry.flags & IW_RETRY_LONG) {
+               wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
                wrqu->retry.value = priv->long_retry_limit;
-       } else if (wrqu->retry.flags & IW_RETRY_MIN) {
-               wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN;
+       } else if (wrqu->retry.flags & IW_RETRY_SHORT) {
+               wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
                wrqu->retry.value = priv->short_retry_limit;
        } else {
                wrqu->retry.flags = IW_RETRY_LIMIT;
@@ -9381,20 +9470,24 @@ static int ipw_wx_set_scan(struct net_device *dev,
                           union iwreq_data *wrqu, char *extra)
 {
        struct ipw_priv *priv = ieee80211_priv(dev);
-       struct iw_scan_req *req = NULL;
-       if (wrqu->data.length
-           && wrqu->data.length == sizeof(struct iw_scan_req)) {
-               req = (struct iw_scan_req *)extra;
+       struct iw_scan_req *req = (struct iw_scan_req *)extra;
+
+       if (wrqu->data.length == sizeof(struct iw_scan_req)) {
                if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
                        ipw_request_direct_scan(priv, req->essid,
                                                req->essid_len);
                        return 0;
                }
+               if (req->scan_type == IW_SCAN_TYPE_PASSIVE) {
+                       queue_work(priv->workqueue,
+                                  &priv->request_passive_scan);
+                       return 0;
+               }
        }
 
        IPW_DEBUG_WX("Start scan\n");
 
-       queue_work(priv->workqueue, &priv->request_scan);
+       queue_delayed_work(priv->workqueue, &priv->request_scan, 0);
 
        return 0;
 }
@@ -9475,6 +9568,7 @@ static int ipw_wx_set_power(struct net_device *dev,
                priv->power_mode = IPW_POWER_ENABLED | IPW_POWER_BATTERY;
        else
                priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
+
        err = ipw_send_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
        if (err) {
                IPW_DEBUG_WX("failed setting power mode.\n");
@@ -9511,22 +9605,19 @@ static int ipw_wx_set_powermode(struct net_device *dev,
        struct ipw_priv *priv = ieee80211_priv(dev);
        int mode = *(int *)extra;
        int err;
+
        mutex_lock(&priv->mutex);
-       if ((mode < 1) || (mode > IPW_POWER_LIMIT)) {
+       if ((mode < 1) || (mode > IPW_POWER_LIMIT))
                mode = IPW_POWER_AC;
-               priv->power_mode = mode;
-       } else {
-               priv->power_mode = IPW_POWER_ENABLED | mode;
-       }
 
-       if (priv->power_mode != mode) {
+       if (IPW_POWER_LEVEL(priv->power_mode) != mode) {
                err = ipw_send_power_mode(priv, mode);
-
                if (err) {
                        IPW_DEBUG_WX("failed setting power mode.\n");
                        mutex_unlock(&priv->mutex);
                        return err;
                }
+               priv->power_mode = IPW_POWER_ENABLED | mode;
        }
        mutex_unlock(&priv->mutex);
        return 0;
@@ -10309,7 +10400,7 @@ static void ipw_handle_promiscuous_tx(struct ipw_priv *priv,
 
                rt_hdr->it_len = dst->len;
 
-               memcpy(skb_put(dst, len), src->data, len);
+               skb_copy_from_linear_data(src, skb_put(dst, len), len);
 
                if (!ieee80211_rx(priv->prom_priv->ieee, dst, &dummystats))
                        dev_kfree_skb_any(dst);
@@ -10443,7 +10534,7 @@ static int ipw_ethtool_set_eeprom(struct net_device *dev,
        return 0;
 }
 
-static struct ethtool_ops ipw_ethtool_ops = {
+static const struct ethtool_ops ipw_ethtool_ops = {
        .get_link = ipw_ethtool_get_link,
        .get_drvinfo = ipw_ethtool_get_drvinfo,
        .get_eeprom_len = ipw_ethtool_get_eeprom_len,
@@ -10451,7 +10542,7 @@ static struct ethtool_ops ipw_ethtool_ops = {
        .set_eeprom = ipw_ethtool_set_eeprom,
 };
 
-static irqreturn_t ipw_isr(int irq, void *data, struct pt_regs *regs)
+static irqreturn_t ipw_isr(int irq, void *data)
 {
        struct ipw_priv *priv = data;
        u32 inta, inta_mask;
@@ -10462,7 +10553,7 @@ static irqreturn_t ipw_isr(int irq, void *data, struct pt_regs *regs)
        spin_lock(&priv->irq_lock);
 
        if (!(priv->status & STATUS_INT_ENABLED)) {
-               /* Shared IRQ */
+               /* IRQ is disabled */
                goto none;
        }
 
@@ -10531,11 +10622,12 @@ static void ipw_rf_kill(void *adapter)
        spin_unlock_irqrestore(&priv->lock, flags);
 }
 
-static void ipw_bg_rf_kill(void *data)
+static void ipw_bg_rf_kill(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, rf_kill.work);
        mutex_lock(&priv->mutex);
-       ipw_rf_kill(data);
+       ipw_rf_kill(priv);
        mutex_unlock(&priv->mutex);
 }
 
@@ -10566,11 +10658,12 @@ static void ipw_link_up(struct ipw_priv *priv)
                queue_delayed_work(priv->workqueue, &priv->request_scan, HZ);
 }
 
-static void ipw_bg_link_up(void *data)
+static void ipw_bg_link_up(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, link_up);
        mutex_lock(&priv->mutex);
-       ipw_link_up(data);
+       ipw_link_up(priv);
        mutex_unlock(&priv->mutex);
 }
 
@@ -10590,15 +10683,16 @@ static void ipw_link_down(struct ipw_priv *priv)
 
        if (!(priv->status & STATUS_EXIT_PENDING)) {
                /* Queue up another scan... */
-               queue_work(priv->workqueue, &priv->request_scan);
+               queue_delayed_work(priv->workqueue, &priv->request_scan, 0);
        }
 }
 
-static void ipw_bg_link_down(void *data)
+static void ipw_bg_link_down(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, link_down);
        mutex_lock(&priv->mutex);
-       ipw_link_down(data);
+       ipw_link_down(priv);
        mutex_unlock(&priv->mutex);
 }
 
@@ -10610,36 +10704,30 @@ static int ipw_setup_deferred_work(struct ipw_priv *priv)
        init_waitqueue_head(&priv->wait_command_queue);
        init_waitqueue_head(&priv->wait_state);
 
-       INIT_WORK(&priv->adhoc_check, ipw_bg_adhoc_check, priv);
-       INIT_WORK(&priv->associate, ipw_bg_associate, priv);
-       INIT_WORK(&priv->disassociate, ipw_bg_disassociate, priv);
-       INIT_WORK(&priv->system_config, ipw_system_config, priv);
-       INIT_WORK(&priv->rx_replenish, ipw_bg_rx_queue_replenish, priv);
-       INIT_WORK(&priv->adapter_restart, ipw_bg_adapter_restart, priv);
-       INIT_WORK(&priv->rf_kill, ipw_bg_rf_kill, priv);
-       INIT_WORK(&priv->up, (void (*)(void *))ipw_bg_up, priv);
-       INIT_WORK(&priv->down, (void (*)(void *))ipw_bg_down, priv);
-       INIT_WORK(&priv->request_scan,
-                 (void (*)(void *))ipw_request_scan, priv);
-       INIT_WORK(&priv->gather_stats,
-                 (void (*)(void *))ipw_bg_gather_stats, priv);
-       INIT_WORK(&priv->abort_scan, (void (*)(void *))ipw_bg_abort_scan, priv);
-       INIT_WORK(&priv->roam, ipw_bg_roam, priv);
-       INIT_WORK(&priv->scan_check, ipw_bg_scan_check, priv);
-       INIT_WORK(&priv->link_up, (void (*)(void *))ipw_bg_link_up, priv);
-       INIT_WORK(&priv->link_down, (void (*)(void *))ipw_bg_link_down, priv);
-       INIT_WORK(&priv->led_link_on, (void (*)(void *))ipw_bg_led_link_on,
-                 priv);
-       INIT_WORK(&priv->led_link_off, (void (*)(void *))ipw_bg_led_link_off,
-                 priv);
-       INIT_WORK(&priv->led_act_off, (void (*)(void *))ipw_bg_led_activity_off,
-                 priv);
-       INIT_WORK(&priv->merge_networks,
-                 (void (*)(void *))ipw_merge_adhoc_network, priv);
+       INIT_DELAYED_WORK(&priv->adhoc_check, ipw_bg_adhoc_check);
+       INIT_WORK(&priv->associate, ipw_bg_associate);
+       INIT_WORK(&priv->disassociate, ipw_bg_disassociate);
+       INIT_WORK(&priv->system_config, ipw_system_config);
+       INIT_WORK(&priv->rx_replenish, ipw_bg_rx_queue_replenish);
+       INIT_WORK(&priv->adapter_restart, ipw_bg_adapter_restart);
+       INIT_DELAYED_WORK(&priv->rf_kill, ipw_bg_rf_kill);
+       INIT_WORK(&priv->up, ipw_bg_up);
+       INIT_WORK(&priv->down, ipw_bg_down);
+       INIT_DELAYED_WORK(&priv->request_scan, ipw_request_scan);
+       INIT_WORK(&priv->request_passive_scan, ipw_request_passive_scan);
+       INIT_DELAYED_WORK(&priv->gather_stats, ipw_bg_gather_stats);
+       INIT_WORK(&priv->abort_scan, ipw_bg_abort_scan);
+       INIT_WORK(&priv->roam, ipw_bg_roam);
+       INIT_DELAYED_WORK(&priv->scan_check, ipw_bg_scan_check);
+       INIT_WORK(&priv->link_up, ipw_bg_link_up);
+       INIT_WORK(&priv->link_down, ipw_bg_link_down);
+       INIT_DELAYED_WORK(&priv->led_link_on, ipw_bg_led_link_on);
+       INIT_DELAYED_WORK(&priv->led_link_off, ipw_bg_led_link_off);
+       INIT_DELAYED_WORK(&priv->led_act_off, ipw_bg_led_activity_off);
+       INIT_WORK(&priv->merge_networks, ipw_merge_adhoc_network);
 
 #ifdef CONFIG_IPW2200_QOS
-       INIT_WORK(&priv->qos_activate, (void (*)(void *))ipw_bg_qos_activate,
-                 priv);
+       INIT_WORK(&priv->qos_activate, ipw_bg_qos_activate);
 #endif                         /* CONFIG_IPW2200_QOS */
 
        tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
@@ -11111,14 +11199,13 @@ static int ipw_up(struct ipw_priv *priv)
                return -EIO;
 
        if (cmdlog && !priv->cmdlog) {
-               priv->cmdlog = kmalloc(sizeof(*priv->cmdlog) * cmdlog,
+               priv->cmdlog = kcalloc(cmdlog, sizeof(*priv->cmdlog),
                                       GFP_KERNEL);
                if (priv->cmdlog == NULL) {
                        IPW_ERROR("Error allocating %d command log entries.\n",
                                  cmdlog);
                        return -ENOMEM;
                } else {
-                       memset(priv->cmdlog, 0, sizeof(*priv->cmdlog) * cmdlog);
                        priv->cmdlog_len = cmdlog;
                }
        }
@@ -11172,7 +11259,8 @@ static int ipw_up(struct ipw_priv *priv)
 
                        /* If configure to try and auto-associate, kick
                         * off a scan. */
-                       queue_work(priv->workqueue, &priv->request_scan);
+                       queue_delayed_work(priv->workqueue,
+                                          &priv->request_scan, 0);
 
                        return 0;
                }
@@ -11193,11 +11281,12 @@ static int ipw_up(struct ipw_priv *priv)
        return -EIO;
 }
 
-static void ipw_bg_up(void *data)
+static void ipw_bg_up(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, up);
        mutex_lock(&priv->mutex);
-       ipw_up(data);
+       ipw_up(priv);
        mutex_unlock(&priv->mutex);
 }
 
@@ -11264,11 +11353,12 @@ static void ipw_down(struct ipw_priv *priv)
        ipw_led_radio_off(priv);
 }
 
-static void ipw_bg_down(void *data)
+static void ipw_bg_down(struct work_struct *work)
 {
-       struct ipw_priv *priv = data;
+       struct ipw_priv *priv =
+               container_of(work, struct ipw_priv, down);
        mutex_lock(&priv->mutex);
-       ipw_down(data);
+       ipw_down(priv);
        mutex_unlock(&priv->mutex);
 }
 
@@ -11338,6 +11428,7 @@ static struct attribute *ipw_sysfs_entries[] = {
        &dev_attr_led.attr,
        &dev_attr_speed_scan.attr,
        &dev_attr_net_stats.attr,
+       &dev_attr_channels.attr,
 #ifdef CONFIG_IPW2200_PROMISCUOUS
        &dev_attr_rtap_iface.attr,
        &dev_attr_rtap_filter.attr,
@@ -11709,12 +11800,18 @@ static int ipw_pci_resume(struct pci_dev *pdev)
 {
        struct ipw_priv *priv = pci_get_drvdata(pdev);
        struct net_device *dev = priv->net_dev;
+       int err;
        u32 val;
 
        printk(KERN_INFO "%s: Coming out of suspend...\n", dev->name);
 
        pci_set_power_state(pdev, PCI_D0);
-       pci_enable_device(pdev);
+       err = pci_enable_device(pdev);
+       if (err) {
+               printk(KERN_ERR "%s: pci_enable_device failed on resume\n",
+                      dev->name);
+               return err;
+       }
        pci_restore_state(pdev);
 
        /*
@@ -11738,6 +11835,16 @@ static int ipw_pci_resume(struct pci_dev *pdev)
 }
 #endif
 
+static void ipw_pci_shutdown(struct pci_dev *pdev)
+{
+       struct ipw_priv *priv = pci_get_drvdata(pdev);
+
+       /* Take down the device; powers it off, etc. */
+       ipw_down(priv);
+
+       pci_disable_device(pdev);
+}
+
 /* driver initialization stuff */
 static struct pci_driver ipw_driver = {
        .name = DRV_NAME,
@@ -11748,6 +11855,7 @@ static struct pci_driver ipw_driver = {
        .suspend = ipw_pci_suspend,
        .resume = ipw_pci_resume,
 #endif
+       .shutdown = ipw_pci_shutdown,
 };
 
 static int __init ipw_init(void)
@@ -11757,7 +11865,7 @@ static int __init ipw_init(void)
        printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
        printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
 
-       ret = pci_module_init(&ipw_driver);
+       ret = pci_register_driver(&ipw_driver);
        if (ret) {
                IPW_ERROR("Unable to initialize PCI module\n");
                return ret;
@@ -11791,10 +11899,8 @@ MODULE_PARM_DESC(auto_create, "auto create adhoc network (default on)");
 module_param(led, int, 0444);
 MODULE_PARM_DESC(led, "enable led control on some systems (default 0 off)\n");
 
-#ifdef CONFIG_IPW2200_DEBUG
 module_param(debug, int, 0444);
 MODULE_PARM_DESC(debug, "debug output mask");
-#endif
 
 module_param(channel, int, 0444);
 MODULE_PARM_DESC(channel, "channel to limit associate to (default 0 [ANY])");