fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / net / wireless / ipw2100.c
index 6290c9f..b85857a 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
 
-  Copyright(c) 2003 - 2005 Intel Corporation. All rights reserved.
+  Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved.
 
   This program is free software; you can redistribute it and/or modify it
   under the terms of version 2 of the GNU General Public License as
@@ -134,7 +134,6 @@ that only one external action is invoked at a time.
 */
 
 #include <linux/compiler.h>
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/if_arp.h>
 #include <linux/in6.h>
@@ -151,7 +150,6 @@ that only one external action is invoked at a time.
 #include <linux/skbuff.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
-#define __KERNEL_SYSCALLS__
 #include <linux/fs.h>
 #include <linux/mm.h>
 #include <linux/slab.h>
@@ -164,15 +162,16 @@ that only one external action is invoked at a time.
 #include <linux/firmware.h>
 #include <linux/acpi.h>
 #include <linux/ctype.h>
+#include <linux/latency.h>
 
 #include "ipw2100.h"
 
-#define IPW2100_VERSION "1.1.3"
+#define IPW2100_VERSION "git-1.2.2"
 
 #define DRV_NAME       "ipw2100"
 #define DRV_VERSION    IPW2100_VERSION
 #define DRV_DESCRIPTION        "Intel(R) PRO/Wireless 2100 Network Driver"
-#define DRV_COPYRIGHT  "Copyright(c) 2003-2005 Intel Corporation"
+#define DRV_COPYRIGHT  "Copyright(c) 2003-2006 Intel Corporation"
 
 /* Debugging stuff */
 #ifdef CONFIG_IPW2100_DEBUG
@@ -317,7 +316,7 @@ static void ipw2100_release_firmware(struct ipw2100_priv *priv,
                                     struct ipw2100_fw *fw);
 static int ipw2100_ucode_download(struct ipw2100_priv *priv,
                                  struct ipw2100_fw *fw);
-static void ipw2100_wx_event_work(struct ipw2100_priv *priv);
+static void ipw2100_wx_event_work(struct work_struct *work);
 static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev);
 static struct iw_handler_def ipw2100_wx_handler_def;
 
@@ -680,7 +679,8 @@ static void schedule_reset(struct ipw2100_priv *priv)
                        queue_delayed_work(priv->workqueue, &priv->reset_work,
                                           priv->reset_backoff * HZ);
                else
-                       queue_work(priv->workqueue, &priv->reset_work);
+                       queue_delayed_work(priv->workqueue, &priv->reset_work,
+                                          0);
 
                if (priv->reset_backoff < MAX_RESET_BACKOFF)
                        priv->reset_backoff++;
@@ -1418,7 +1418,7 @@ static int ipw2100_enable_adapter(struct ipw2100_priv *priv)
        if (priv->status & STATUS_ENABLED)
                return 0;
 
-       down(&priv->adapter_sem);
+       mutex_lock(&priv->adapter_mutex);
 
        if (rf_kill_active(priv)) {
                IPW_DEBUG_HC("Command aborted due to RF kill active.\n");
@@ -1444,7 +1444,7 @@ static int ipw2100_enable_adapter(struct ipw2100_priv *priv)
        }
 
       fail_up:
-       up(&priv->adapter_sem);
+       mutex_unlock(&priv->adapter_mutex);
        return err;
 }
 
@@ -1485,7 +1485,7 @@ static int ipw2100_hw_stop_adapter(struct ipw2100_priv *priv)
                 *
                 * Sending the PREPARE_FOR_POWER_DOWN will restrict the
                 * hardware from going into standby mode and will transition
-                * out of D0-standy if it is already in that state.
+                * out of D0-standby if it is already in that state.
                 *
                 * STATUS_PREPARE_POWER_DOWN_COMPLETE will be sent by the
                 * driver upon completion.  Once received, the driver can
@@ -1576,7 +1576,7 @@ static int ipw2100_disable_adapter(struct ipw2100_priv *priv)
                cancel_delayed_work(&priv->hang_check);
        }
 
-       down(&priv->adapter_sem);
+       mutex_lock(&priv->adapter_mutex);
 
        err = ipw2100_hw_send_command(priv, &cmd);
        if (err) {
@@ -1595,7 +1595,7 @@ static int ipw2100_disable_adapter(struct ipw2100_priv *priv)
        IPW_DEBUG_INFO("TODO: implement scan state machine\n");
 
       fail_up:
-       up(&priv->adapter_sem);
+       mutex_unlock(&priv->adapter_mutex);
        return err;
 }
 
@@ -1672,6 +1672,18 @@ static int ipw2100_start_scan(struct ipw2100_priv *priv)
        return err;
 }
 
+static const struct ieee80211_geo ipw_geos[] = {
+       {                       /* Restricted */
+        "---",
+        .bg_channels = 14,
+        .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+               {2427, 4}, {2432, 5}, {2437, 6},
+               {2442, 7}, {2447, 8}, {2452, 9},
+               {2457, 10}, {2462, 11}, {2467, 12},
+               {2472, 13}, {2484, 14}},
+        },
+};
+
 static int ipw2100_up(struct ipw2100_priv *priv, int deferred)
 {
        unsigned long flags;
@@ -1686,6 +1698,11 @@ static int ipw2100_up(struct ipw2100_priv *priv, int deferred)
                return 0;
        }
 
+       /* the ipw2100 hardware really doesn't want power management delays
+        * longer than 175usec
+        */
+       modify_acceptable_latency("ipw2100", 175);
+
        /* If the interrupt is enabled, turn it off... */
        spin_lock_irqsave(&priv->low_lock, flags);
        ipw2100_disable_interrupts(priv);
@@ -1727,6 +1744,13 @@ static int ipw2100_up(struct ipw2100_priv *priv, int deferred)
                goto exit;
        }
 
+       /* Initialize the geo */
+       if (ieee80211_set_geo(priv->ieee, &ipw_geos[0])) {
+               printk(KERN_WARNING DRV_NAME "Could not set geo\n");
+               return 0;
+       }
+       priv->ieee->freq_band = IEEE80211_24GHZ_BAND;
+
        lock = LOCK_NONE;
        if (ipw2100_set_ordinal(priv, IPW_ORD_PERS_DB_LOCK, &lock, &ord_len)) {
                printk(KERN_ERR DRV_NAME
@@ -1831,6 +1855,8 @@ static void ipw2100_down(struct ipw2100_priv *priv)
        ipw2100_disable_interrupts(priv);
        spin_unlock_irqrestore(&priv->low_lock, flags);
 
+       modify_acceptable_latency("ipw2100", INFINITE_LATENCY);
+
 #ifdef ACPI_CSTATE_LIMIT_DEFINED
        if (priv->config & CFG_C3_DISABLED) {
                IPW_DEBUG_INFO(": Resetting C3 transitions.\n");
@@ -1848,8 +1874,10 @@ static void ipw2100_down(struct ipw2100_priv *priv)
        netif_stop_queue(priv->net_dev);
 }
 
-static void ipw2100_reset_adapter(struct ipw2100_priv *priv)
+static void ipw2100_reset_adapter(struct work_struct *work)
 {
+       struct ipw2100_priv *priv =
+               container_of(work, struct ipw2100_priv, reset_work.work);
        unsigned long flags;
        union iwreq_data wrqu = {
                .ap_addr = {
@@ -1869,7 +1897,7 @@ static void ipw2100_reset_adapter(struct ipw2100_priv *priv)
        priv->status |= STATUS_RESET_PENDING;
        spin_unlock_irqrestore(&priv->low_lock, flags);
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
        /* stop timed checks so that they don't interfere with reset */
        priv->stop_hang_check = 1;
        cancel_delayed_work(&priv->hang_check);
@@ -1879,7 +1907,7 @@ static void ipw2100_reset_adapter(struct ipw2100_priv *priv)
                wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
 
        ipw2100_up(priv, 0);
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
 
 }
 
@@ -2046,9 +2074,9 @@ static void isr_indicate_association_lost(struct ipw2100_priv *priv, u32 status)
                return;
 
        if (priv->status & STATUS_SECURITY_UPDATED)
-               queue_work(priv->workqueue, &priv->security_work);
+               queue_delayed_work(priv->workqueue, &priv->security_work, 0);
 
-       queue_work(priv->workqueue, &priv->wx_event_work);
+       queue_delayed_work(priv->workqueue, &priv->wx_event_work, 0);
 }
 
 static void isr_indicate_rf_kill(struct ipw2100_priv *priv, u32 status)
@@ -2218,7 +2246,7 @@ static int ipw2100_snapshot_alloc(struct ipw2100_priv *priv)
        if (priv->snapshot[0])
                return 1;
        for (i = 0; i < 0x30; i++) {
-               priv->snapshot[i] = (u8 *) kmalloc(0x1000, GFP_ATOMIC);
+               priv->snapshot[i] = kmalloc(0x1000, GFP_ATOMIC);
                if (!priv->snapshot[i]) {
                        IPW_DEBUG_INFO("%s: Error allocating snapshot "
                                       "buffer %d\n", priv->net_dev->name, i);
@@ -2371,15 +2399,6 @@ static void isr_rx(struct ipw2100_priv *priv, int i,
                IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
                return;
        }
-#ifdef CONFIG_IPW2100_MONITOR
-       if (unlikely(priv->ieee->iw_mode == IW_MODE_MONITOR &&
-                    priv->config & CFG_CRC_CHECK &&
-                    status->flags & IPW_STATUS_FLAG_CRC_ERROR)) {
-               IPW_DEBUG_RX("CRC error in packet.  Dropping.\n");
-               priv->ieee->stats.rx_errors++;
-               return;
-       }
-#endif
 
        if (unlikely(priv->ieee->iw_mode != IW_MODE_MONITOR &&
                     !(priv->status & STATUS_ASSOCIATED))) {
@@ -2427,6 +2446,89 @@ static void isr_rx(struct ipw2100_priv *priv, int i,
        priv->rx_queue.drv[i].host_addr = packet->dma_addr;
 }
 
+#ifdef CONFIG_IPW2100_MONITOR
+
+static void isr_rx_monitor(struct ipw2100_priv *priv, int i,
+                  struct ieee80211_rx_stats *stats)
+{
+       struct ipw2100_status *status = &priv->status_queue.drv[i];
+       struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
+
+       /* Magic struct that slots into the radiotap header -- no reason
+        * to build this manually element by element, we can write it much
+        * more efficiently than we can parse it. ORDER MATTERS HERE */
+       struct ipw_rt_hdr {
+               struct ieee80211_radiotap_header rt_hdr;
+               s8 rt_dbmsignal; /* signal in dbM, kluged to signed */
+       } *ipw_rt;
+
+       IPW_DEBUG_RX("Handler...\n");
+
+       if (unlikely(status->frame_size > skb_tailroom(packet->skb) -
+                               sizeof(struct ipw_rt_hdr))) {
+               IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!"
+                              "  Dropping.\n",
+                              priv->net_dev->name,
+                              status->frame_size,
+                              skb_tailroom(packet->skb));
+               priv->ieee->stats.rx_errors++;
+               return;
+       }
+
+       if (unlikely(!netif_running(priv->net_dev))) {
+               priv->ieee->stats.rx_errors++;
+               priv->wstats.discard.misc++;
+               IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
+               return;
+       }
+
+       if (unlikely(priv->config & CFG_CRC_CHECK &&
+                    status->flags & IPW_STATUS_FLAG_CRC_ERROR)) {
+               IPW_DEBUG_RX("CRC error in packet.  Dropping.\n");
+               priv->ieee->stats.rx_errors++;
+               return;
+       }
+
+       pci_unmap_single(priv->pci_dev, packet->dma_addr,
+                        sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE);
+       memmove(packet->skb->data + sizeof(struct ipw_rt_hdr),
+               packet->skb->data, status->frame_size);
+
+       ipw_rt = (struct ipw_rt_hdr *) packet->skb->data;
+
+       ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
+       ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */
+       ipw_rt->rt_hdr.it_len = sizeof(struct ipw_rt_hdr); /* total hdr+data */
+
+       ipw_rt->rt_hdr.it_present = 1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL;
+
+       ipw_rt->rt_dbmsignal = status->rssi + IPW2100_RSSI_TO_DBM;
+
+       skb_put(packet->skb, status->frame_size + sizeof(struct ipw_rt_hdr));
+
+       if (!ieee80211_rx(priv->ieee, packet->skb, stats)) {
+               priv->ieee->stats.rx_errors++;
+
+               /* ieee80211_rx failed, so it didn't free the SKB */
+               dev_kfree_skb_any(packet->skb);
+               packet->skb = NULL;
+       }
+
+       /* We need to allocate a new SKB and attach it to the RDB. */
+       if (unlikely(ipw2100_alloc_skb(priv, packet))) {
+               IPW_DEBUG_WARNING(
+                       "%s: Unable to allocate SKB onto RBD ring - disabling "
+                       "adapter.\n", priv->net_dev->name);
+               /* TODO: schedule adapter shutdown */
+               IPW_DEBUG_INFO("TODO: Shutdown adapter...\n");
+       }
+
+       /* Update the RDB entry */
+       priv->rx_queue.drv[i].host_addr = packet->dma_addr;
+}
+
+#endif
+
 static int ipw2100_corruption_check(struct ipw2100_priv *priv, int i)
 {
        struct ipw2100_status *status = &priv->status_queue.drv[i];
@@ -2558,11 +2660,11 @@ static void __ipw2100_rx_process(struct ipw2100_priv *priv)
                case P8023_DATA_VAL:
 #ifdef CONFIG_IPW2100_MONITOR
                        if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
-                               isr_rx(priv, i, &stats);
+                               isr_rx_monitor(priv, i, &stats);
                                break;
                        }
 #endif
-                       if (stats.len < sizeof(u->rx_data.header))
+                       if (stats.len < sizeof(struct ieee80211_hdr_3addr))
                                break;
                        switch (WLAN_FC_GET_TYPE(u->rx_data.header.frame_ctl)) {
                        case IEEE80211_FTYPE_MGMT:
@@ -3156,7 +3258,7 @@ static void ipw2100_irq_tasklet(struct ipw2100_priv *priv)
        IPW_DEBUG_ISR("exit\n");
 }
 
-static irqreturn_t ipw2100_interrupt(int irq, void *data, struct pt_regs *regs)
+static irqreturn_t ipw2100_interrupt(int irq, void *data)
 {
        struct ipw2100_priv *priv = data;
        u32 inta, inta_mask;
@@ -3750,7 +3852,7 @@ static ssize_t store_memory(struct device *d, struct device_attribute *attr,
        struct net_device *dev = priv->net_dev;
        const char *p = buf;
 
-       (void) dev; /* kill unused-var warning for debug-only code */
+       (void)dev;              /* kill unused-var warning for debug-only code */
 
        if (count < 1)
                return count;
@@ -3863,7 +3965,7 @@ static int ipw2100_switch_mode(struct ipw2100_priv *priv, u32 mode)
 #ifdef CONFIG_IPW2100_MONITOR
        case IW_MODE_MONITOR:
                priv->last_mode = priv->ieee->iw_mode;
-               priv->net_dev->type = ARPHRD_IEEE80211;
+               priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
                break;
 #endif                         /* CONFIG_IPW2100_MONITOR */
        }
@@ -4070,7 +4172,7 @@ static ssize_t store_scan_age(struct device *d, struct device_attribute *attr,
        unsigned long val;
        char *p = buffer;
 
-       (void) dev; /* kill unused-var warning for debug-only code */
+       (void)dev;              /* kill unused-var warning for debug-only code */
 
        IPW_DEBUG_INFO("enter\n");
 
@@ -4119,7 +4221,7 @@ static int ipw_radio_kill_sw(struct ipw2100_priv *priv, int disable_radio)
        IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO  %s\n",
                          disable_radio ? "OFF" : "ON");
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
 
        if (disable_radio) {
                priv->status |= STATUS_RF_KILL_SW;
@@ -4137,7 +4239,7 @@ static int ipw_radio_kill_sw(struct ipw2100_priv *priv, int disable_radio)
                        schedule_reset(priv);
        }
 
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
        return 1;
 }
 
@@ -5107,12 +5209,13 @@ static int ipw2100_set_tx_power(struct ipw2100_priv *priv, u32 tx_power)
                .host_command_length = 4
        };
        int err = 0;
+       u32 tmp = tx_power;
 
        if (tx_power != IPW_TX_POWER_DEFAULT)
-               tx_power = (tx_power - IPW_TX_POWER_MIN_DBM) * 16 /
-                   (IPW_TX_POWER_MAX_DBM - IPW_TX_POWER_MIN_DBM);
+               tmp = (tx_power - IPW_TX_POWER_MIN_DBM) * 16 /
+                     (IPW_TX_POWER_MAX_DBM - IPW_TX_POWER_MIN_DBM);
 
-       cmd.host_command_parameters[0] = tx_power;
+       cmd.host_command_parameters[0] = tmp;
 
        if (priv->ieee->iw_mode == IW_MODE_ADHOC)
                err = ipw2100_hw_send_command(priv, &cmd);
@@ -5264,7 +5367,7 @@ static int ipw2100_set_key(struct ipw2100_priv *priv,
                     idx, keylen, len);
 
        /* NOTE: We don't check cached values in case the firmware was reset
-        * or some other problem is occuring.  If the user is setting the key,
+        * or some other problem is occurring.  If the user is setting the key,
         * then we push the change */
 
        wep_key->idx = idx;
@@ -5365,9 +5468,12 @@ static int ipw2100_configure_security(struct ipw2100_priv *priv, int batch_mode)
                                                     SEC_LEVEL_0, 0, 1);
        } else {
                auth_mode = IPW_AUTH_OPEN;
-               if ((priv->ieee->sec.flags & SEC_AUTH_MODE) &&
-                   (priv->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY))
-                       auth_mode = IPW_AUTH_SHARED;
+               if (priv->ieee->sec.flags & SEC_AUTH_MODE) {
+                       if (priv->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY)
+                               auth_mode = IPW_AUTH_SHARED;
+                       else if (priv->ieee->sec.auth_mode == WLAN_AUTH_LEAP)
+                               auth_mode = IPW_AUTH_LEAP_CISCO_ID;
+               }
 
                sec_level = SEC_LEVEL_0;
                if (priv->ieee->sec.flags & SEC_LEVEL)
@@ -5421,8 +5527,11 @@ static int ipw2100_configure_security(struct ipw2100_priv *priv, int batch_mode)
        return err;
 }
 
-static void ipw2100_security_work(struct ipw2100_priv *priv)
+static void ipw2100_security_work(struct work_struct *work)
 {
+       struct ipw2100_priv *priv =
+               container_of(work, struct ipw2100_priv, security_work.work);
+
        /* If we happen to have reconnected before we get a chance to
         * process this, then update the security settings--which causes
         * a disassociation to occur */
@@ -5437,7 +5546,7 @@ static void shim__set_security(struct net_device *dev,
        struct ipw2100_priv *priv = ieee80211_priv(dev);
        int i, force_update = 0;
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
        if (!(priv->status & STATUS_INITIALIZED))
                goto done;
 
@@ -5510,7 +5619,7 @@ static void shim__set_security(struct net_device *dev,
        if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)))
                ipw2100_configure_security(priv, 0);
       done:
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
 }
 
 static int ipw2100_adapter_setup(struct ipw2100_priv *priv)
@@ -5634,7 +5743,7 @@ static int ipw2100_set_address(struct net_device *dev, void *p)
        if (!is_valid_ether_addr(addr->sa_data))
                return -EADDRNOTAVAIL;
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
 
        priv->config |= CFG_CUSTOM_MAC;
        memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
@@ -5644,12 +5753,12 @@ static int ipw2100_set_address(struct net_device *dev, void *p)
                goto done;
 
        priv->reset_backoff = 0;
-       up(&priv->action_sem);
-       ipw2100_reset_adapter(priv);
+       mutex_unlock(&priv->action_mutex);
+       ipw2100_reset_adapter(&priv->reset_work.work);
        return 0;
 
       done:
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
        return err;
 }
 
@@ -5724,19 +5833,6 @@ static void ipw2100_tx_timeout(struct net_device *dev)
        schedule_reset(priv);
 }
 
-/*
- * TODO: reimplement it so that it reads statistics
- *       from the adapter using ordinal tables
- *       instead of/in addition to collecting them
- *       in the driver
- */
-static struct net_device_stats *ipw2100_stats(struct net_device *dev)
-{
-       struct ipw2100_priv *priv = ieee80211_priv(dev);
-
-       return &priv->ieee->stats;
-}
-
 static int ipw2100_wpa_enable(struct ipw2100_priv *priv, int value)
 {
        /* This is called when wpa_supplicant loads and closes the driver
@@ -5760,6 +5856,9 @@ static int ipw2100_wpa_set_auth_algs(struct ipw2100_priv *priv, int value)
        } else if (value & IW_AUTH_ALG_OPEN_SYSTEM) {
                sec.auth_mode = WLAN_AUTH_OPEN;
                ieee->open_wep = 1;
+       } else if (value & IW_AUTH_ALG_LEAP) {
+               sec.auth_mode = WLAN_AUTH_LEAP;
+               ieee->open_wep = 1;
        } else
                return -EINVAL;
 
@@ -5771,8 +5870,8 @@ static int ipw2100_wpa_set_auth_algs(struct ipw2100_priv *priv, int value)
        return ret;
 }
 
-void ipw2100_wpa_assoc_frame(struct ipw2100_priv *priv,
-                            char *wpa_ie, int wpa_ie_len)
+static void ipw2100_wpa_assoc_frame(struct ipw2100_priv *priv,
+                                   char *wpa_ie, int wpa_ie_len)
 {
 
        struct ipw2100_wpa_assoc_frame frame;
@@ -5812,14 +5911,15 @@ static u32 ipw2100_ethtool_get_link(struct net_device *dev)
        return (priv->status & STATUS_ASSOCIATED) ? 1 : 0;
 }
 
-static struct ethtool_ops ipw2100_ethtool_ops = {
+static const struct ethtool_ops ipw2100_ethtool_ops = {
        .get_link = ipw2100_ethtool_get_link,
        .get_drvinfo = ipw_ethtool_get_drvinfo,
 };
 
-static void ipw2100_hang_check(void *adapter)
+static void ipw2100_hang_check(struct work_struct *work)
 {
-       struct ipw2100_priv *priv = adapter;
+       struct ipw2100_priv *priv =
+               container_of(work, struct ipw2100_priv, hang_check.work);
        unsigned long flags;
        u32 rtc = 0xa5a5a5a5;
        u32 len = sizeof(rtc);
@@ -5859,9 +5959,10 @@ static void ipw2100_hang_check(void *adapter)
        spin_unlock_irqrestore(&priv->low_lock, flags);
 }
 
-static void ipw2100_rf_kill(void *adapter)
+static void ipw2100_rf_kill(struct work_struct *work)
 {
-       struct ipw2100_priv *priv = adapter;
+       struct ipw2100_priv *priv =
+               container_of(work, struct ipw2100_priv, rf_kill.work);
        unsigned long flags;
 
        spin_lock_irqsave(&priv->low_lock, flags);
@@ -5916,7 +6017,6 @@ static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev,
        dev->open = ipw2100_open;
        dev->stop = ipw2100_close;
        dev->init = ipw2100_net_init;
-       dev->get_stats = ipw2100_stats;
        dev->ethtool_ops = &ipw2100_ethtool_ops;
        dev->tx_timeout = ipw2100_tx_timeout;
        dev->wireless_handlers = &ipw2100_wx_handler_def;
@@ -5989,8 +6089,8 @@ static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev,
        strcpy(priv->nick, "ipw2100");
 
        spin_lock_init(&priv->low_lock);
-       sema_init(&priv->action_sem, 1);
-       sema_init(&priv->adapter_sem, 1);
+       mutex_init(&priv->action_mutex);
+       mutex_init(&priv->adapter_mutex);
 
        init_waitqueue_head(&priv->wait_command_queue);
 
@@ -6011,14 +6111,11 @@ static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev,
 
        priv->workqueue = create_workqueue(DRV_NAME);
 
-       INIT_WORK(&priv->reset_work,
-                 (void (*)(void *))ipw2100_reset_adapter, priv);
-       INIT_WORK(&priv->security_work,
-                 (void (*)(void *))ipw2100_security_work, priv);
-       INIT_WORK(&priv->wx_event_work,
-                 (void (*)(void *))ipw2100_wx_event_work, priv);
-       INIT_WORK(&priv->hang_check, ipw2100_hang_check, priv);
-       INIT_WORK(&priv->rf_kill, ipw2100_rf_kill, priv);
+       INIT_DELAYED_WORK(&priv->reset_work, ipw2100_reset_adapter);
+       INIT_DELAYED_WORK(&priv->security_work, ipw2100_security_work);
+       INIT_DELAYED_WORK(&priv->wx_event_work, ipw2100_wx_event_work);
+       INIT_DELAYED_WORK(&priv->hang_check, ipw2100_hang_check);
+       INIT_DELAYED_WORK(&priv->rf_kill, ipw2100_rf_kill);
 
        tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
                     ipw2100_irq_tasklet, (unsigned long)priv);
@@ -6123,14 +6220,14 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
        /* Allocate and initialize the Tx/Rx queues and lists */
        if (ipw2100_queues_allocate(priv)) {
                printk(KERN_WARNING DRV_NAME
-                      "Error calilng ipw2100_queues_allocate.\n");
+                      "Error calling ipw2100_queues_allocate.\n");
                err = -ENOMEM;
                goto fail;
        }
        ipw2100_queues_initialize(priv);
 
        err = request_irq(pci_dev->irq,
-                         ipw2100_interrupt, SA_SHIRQ, dev->name, priv);
+                         ipw2100_interrupt, IRQF_SHARED, dev->name, priv);
        if (err) {
                printk(KERN_WARNING DRV_NAME
                       "Error calling request_irq: %d.\n", pci_dev->irq);
@@ -6155,19 +6252,22 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
         * member to call a function that then just turns and calls ipw2100_up.
         * net_dev->init is called after name allocation but before the
         * notifier chain is called */
-       down(&priv->action_sem);
        err = register_netdev(dev);
        if (err) {
                printk(KERN_WARNING DRV_NAME
                       "Error calling register_netdev.\n");
-               goto fail_unlock;
+               goto fail;
        }
+
+       mutex_lock(&priv->action_mutex);
        registered = 1;
 
        IPW_DEBUG_INFO("%s: Bound to %s\n", dev->name, pci_name(pci_dev));
 
        /* perform this after register_netdev so that dev->name is set */
-       sysfs_create_group(&pci_dev->dev.kobj, &ipw2100_attribute_group);
+       err = sysfs_create_group(&pci_dev->dev.kobj, &ipw2100_attribute_group);
+       if (err)
+               goto fail_unlock;
 
        /* If the RF Kill switch is disabled, go ahead and complete the
         * startup sequence */
@@ -6191,12 +6291,12 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
 
        priv->status |= STATUS_INITIALIZED;
 
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
 
        return 0;
 
       fail_unlock:
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
 
       fail:
        if (dev) {
@@ -6236,7 +6336,7 @@ static void __devexit ipw2100_pci_remove_one(struct pci_dev *pci_dev)
        struct net_device *dev;
 
        if (priv) {
-               down(&priv->action_sem);
+               mutex_lock(&priv->action_mutex);
 
                priv->status &= ~STATUS_INITIALIZED;
 
@@ -6251,9 +6351,9 @@ static void __devexit ipw2100_pci_remove_one(struct pci_dev *pci_dev)
                /* Take down the hardware */
                ipw2100_down(priv);
 
-               /* Release the semaphore so that the network subsystem can
+               /* Release the mutex so that the network subsystem can
                 * complete any needed calls into the driver... */
-               up(&priv->action_sem);
+               mutex_unlock(&priv->action_mutex);
 
                /* Unregister the device first - this results in close()
                 * being called if the device is open.  If we free storage
@@ -6292,7 +6392,7 @@ static int ipw2100_suspend(struct pci_dev *pci_dev, pm_message_t state)
 
        IPW_DEBUG_INFO("%s: Going into suspend...\n", dev->name);
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
        if (priv->status & STATUS_INITIALIZED) {
                /* Take down the device; powers it off, etc. */
                ipw2100_down(priv);
@@ -6305,7 +6405,7 @@ static int ipw2100_suspend(struct pci_dev *pci_dev, pm_message_t state)
        pci_disable_device(pci_dev);
        pci_set_power_state(pci_dev, PCI_D3hot);
 
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
 
        return 0;
 }
@@ -6314,17 +6414,23 @@ static int ipw2100_resume(struct pci_dev *pci_dev)
 {
        struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
        struct net_device *dev = priv->net_dev;
+       int err;
        u32 val;
 
        if (IPW2100_PM_DISABLED)
                return 0;
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
 
        IPW_DEBUG_INFO("%s: Coming out of suspend...\n", dev->name);
 
        pci_set_power_state(pci_dev, PCI_D0);
-       pci_enable_device(pci_dev);
+       err = pci_enable_device(pci_dev);
+       if (err) {
+               printk(KERN_ERR "%s: pci_enable_device failed on resume\n",
+                      dev->name);
+               return err;
+       }
        pci_restore_state(pci_dev);
 
        /*
@@ -6345,7 +6451,7 @@ static int ipw2100_resume(struct pci_dev *pci_dev)
        if (!(priv->status & STATUS_RF_KILL_SW))
                ipw2100_up(priv, 0);
 
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
 
        return 0;
 }
@@ -6432,14 +6538,18 @@ static int __init ipw2100_init(void)
        printk(KERN_INFO DRV_NAME ": %s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
        printk(KERN_INFO DRV_NAME ": %s\n", DRV_COPYRIGHT);
 
-       ret = pci_module_init(&ipw2100_pci_driver);
+       ret = pci_register_driver(&ipw2100_pci_driver);
+       if (ret)
+               goto out;
 
+       set_acceptable_latency("ipw2100", INFINITE_LATENCY);
 #ifdef CONFIG_IPW2100_DEBUG
        ipw2100_debug_level = debug;
-       driver_create_file(&ipw2100_pci_driver.driver,
-                          &driver_attr_debug_level);
+       ret = driver_create_file(&ipw2100_pci_driver.driver,
+                                &driver_attr_debug_level);
 #endif
 
+out:
        return ret;
 }
 
@@ -6454,6 +6564,7 @@ static void __exit ipw2100_exit(void)
                           &driver_attr_debug_level);
 #endif
        pci_unregister_driver(&ipw2100_pci_driver);
+       remove_acceptable_latency("ipw2100");
 }
 
 module_init(ipw2100_init);
@@ -6509,7 +6620,7 @@ static int ipw2100_wx_set_freq(struct net_device *dev,
        if (priv->ieee->iw_mode == IW_MODE_INFRA)
                return -EOPNOTSUPP;
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
        if (!(priv->status & STATUS_INITIALIZED)) {
                err = -EIO;
                goto done;
@@ -6540,7 +6651,7 @@ static int ipw2100_wx_set_freq(struct net_device *dev,
        }
 
       done:
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
        return err;
 }
 
@@ -6581,7 +6692,7 @@ static int ipw2100_wx_set_mode(struct net_device *dev,
        if (wrqu->mode == priv->ieee->iw_mode)
                return 0;
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
        if (!(priv->status & STATUS_INITIALIZED)) {
                err = -EIO;
                goto done;
@@ -6604,7 +6715,7 @@ static int ipw2100_wx_set_mode(struct net_device *dev,
        }
 
       done:
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
        return err;
 }
 
@@ -6786,7 +6897,7 @@ static int ipw2100_wx_set_wap(struct net_device *dev,
        if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
                return -EINVAL;
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
        if (!(priv->status & STATUS_INITIALIZED)) {
                err = -EIO;
                goto done;
@@ -6815,7 +6926,7 @@ static int ipw2100_wx_set_wap(struct net_device *dev,
                     wrqu->ap_addr.sa_data[5] & 0xff);
 
       done:
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
        return err;
 }
 
@@ -6851,14 +6962,14 @@ static int ipw2100_wx_set_essid(struct net_device *dev,
        int length = 0;
        int err = 0;
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
        if (!(priv->status & STATUS_INITIALIZED)) {
                err = -EIO;
                goto done;
        }
 
        if (wrqu->essid.flags && wrqu->essid.length) {
-               length = wrqu->essid.length - 1;
+               length = wrqu->essid.length;
                essid = extra;
        }
 
@@ -6888,7 +6999,7 @@ static int ipw2100_wx_set_essid(struct net_device *dev,
        err = ipw2100_set_essid(priv, essid, length, 0);
 
       done:
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
        return err;
 }
 
@@ -6951,7 +7062,7 @@ static int ipw2100_wx_get_nick(struct net_device *dev,
 
        struct ipw2100_priv *priv = ieee80211_priv(dev);
 
-       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 */
 
@@ -6969,7 +7080,7 @@ static int ipw2100_wx_set_rate(struct net_device *dev,
        u32 rate;
        int err = 0;
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
        if (!(priv->status & STATUS_INITIALIZED)) {
                err = -EIO;
                goto done;
@@ -6996,7 +7107,7 @@ static int ipw2100_wx_set_rate(struct net_device *dev,
 
        IPW_DEBUG_WX("SET Rate -> %04X \n", rate);
       done:
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
        return err;
 }
 
@@ -7016,7 +7127,7 @@ static int ipw2100_wx_get_rate(struct net_device *dev,
                return 0;
        }
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
        if (!(priv->status & STATUS_INITIALIZED)) {
                err = -EIO;
                goto done;
@@ -7048,7 +7159,7 @@ static int ipw2100_wx_get_rate(struct net_device *dev,
        IPW_DEBUG_WX("GET Rate -> %d \n", wrqu->bitrate.value);
 
       done:
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
        return err;
 }
 
@@ -7063,7 +7174,7 @@ static int ipw2100_wx_set_rts(struct net_device *dev,
        if (wrqu->rts.fixed == 0)
                return -EINVAL;
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
        if (!(priv->status & STATUS_INITIALIZED)) {
                err = -EIO;
                goto done;
@@ -7083,7 +7194,7 @@ static int ipw2100_wx_set_rts(struct net_device *dev,
 
        IPW_DEBUG_WX("SET RTS Threshold -> 0x%08X \n", value);
       done:
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
        return err;
 }
 
@@ -7134,7 +7245,7 @@ static int ipw2100_wx_set_txpow(struct net_device *dev,
                value = wrqu->txpower.value;
        }
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
        if (!(priv->status & STATUS_INITIALIZED)) {
                err = -EIO;
                goto done;
@@ -7145,7 +7256,7 @@ static int ipw2100_wx_set_txpow(struct net_device *dev,
        IPW_DEBUG_WX("SET TX Power -> %d \n", value);
 
       done:
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
        return err;
 }
 
@@ -7237,20 +7348,20 @@ static int ipw2100_wx_set_retry(struct net_device *dev,
        if (!(wrqu->retry.flags & IW_RETRY_LIMIT))
                return 0;
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
        if (!(priv->status & STATUS_INITIALIZED)) {
                err = -EIO;
                goto done;
        }
 
-       if (wrqu->retry.flags & IW_RETRY_MIN) {
+       if (wrqu->retry.flags & IW_RETRY_SHORT) {
                err = ipw2100_set_short_retry(priv, wrqu->retry.value);
                IPW_DEBUG_WX("SET Short Retry Limit -> %d \n",
                             wrqu->retry.value);
                goto done;
        }
 
-       if (wrqu->retry.flags & IW_RETRY_MAX) {
+       if (wrqu->retry.flags & IW_RETRY_LONG) {
                err = ipw2100_set_long_retry(priv, wrqu->retry.value);
                IPW_DEBUG_WX("SET Long Retry Limit -> %d \n",
                             wrqu->retry.value);
@@ -7264,7 +7375,7 @@ static int ipw2100_wx_set_retry(struct net_device *dev,
        IPW_DEBUG_WX("SET Both Retry Limits -> %d \n", wrqu->retry.value);
 
       done:
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
        return err;
 }
 
@@ -7283,14 +7394,14 @@ static int ipw2100_wx_get_retry(struct net_device *dev,
        if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME)
                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 {
                wrqu->retry.flags =
                    (priv->short_retry_limit !=
                     priv->long_retry_limit) ?
-                   IW_RETRY_LIMIT | IW_RETRY_MIN : IW_RETRY_LIMIT;
+                   IW_RETRY_LIMIT | IW_RETRY_SHORT : IW_RETRY_LIMIT;
 
                wrqu->retry.value = priv->short_retry_limit;
        }
@@ -7307,7 +7418,7 @@ static int ipw2100_wx_set_scan(struct net_device *dev,
        struct ipw2100_priv *priv = ieee80211_priv(dev);
        int err = 0;
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
        if (!(priv->status & STATUS_INITIALIZED)) {
                err = -EIO;
                goto done;
@@ -7322,7 +7433,7 @@ static int ipw2100_wx_set_scan(struct net_device *dev,
        }
 
       done:
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
        return err;
 }
 
@@ -7372,7 +7483,7 @@ static int ipw2100_wx_set_power(struct net_device *dev,
        struct ipw2100_priv *priv = ieee80211_priv(dev);
        int err = 0;
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
        if (!(priv->status & STATUS_INITIALIZED)) {
                err = -EIO;
                goto done;
@@ -7405,7 +7516,7 @@ static int ipw2100_wx_set_power(struct net_device *dev,
        IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode);
 
       done:
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
        return err;
 
 }
@@ -7454,11 +7565,10 @@ static int ipw2100_wx_set_genie(struct net_device *dev,
                return -EINVAL;
 
        if (wrqu->data.length) {
-               buf = kmalloc(wrqu->data.length, GFP_KERNEL);
+               buf = kmemdup(extra, wrqu->data.length, GFP_KERNEL);
                if (buf == NULL)
                        return -ENOMEM;
 
-               memcpy(buf, extra, wrqu->data.length);
                kfree(ieee->wpa_ie);
                ieee->wpa_ie = buf;
                ieee->wpa_ie_len = wrqu->data.length;
@@ -7709,7 +7819,7 @@ static int ipw2100_wx_set_promisc(struct net_device *dev,
        int enable = (parms[0] > 0);
        int err = 0;
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
        if (!(priv->status & STATUS_INITIALIZED)) {
                err = -EIO;
                goto done;
@@ -7727,7 +7837,7 @@ static int ipw2100_wx_set_promisc(struct net_device *dev,
                        err = ipw2100_switch_mode(priv, priv->last_mode);
        }
       done:
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
        return err;
 }
 
@@ -7750,7 +7860,7 @@ static int ipw2100_wx_set_powermode(struct net_device *dev,
        struct ipw2100_priv *priv = ieee80211_priv(dev);
        int err = 0, mode = *(int *)extra;
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
        if (!(priv->status & STATUS_INITIALIZED)) {
                err = -EIO;
                goto done;
@@ -7762,7 +7872,7 @@ static int ipw2100_wx_set_powermode(struct net_device *dev,
        if (priv->power_mode != mode)
                err = ipw2100_set_power_mode(priv, mode);
       done:
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
        return err;
 }
 
@@ -7814,7 +7924,7 @@ static int ipw2100_wx_set_preamble(struct net_device *dev,
        struct ipw2100_priv *priv = ieee80211_priv(dev);
        int err, mode = *(int *)extra;
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
        if (!(priv->status & STATUS_INITIALIZED)) {
                err = -EIO;
                goto done;
@@ -7832,7 +7942,7 @@ static int ipw2100_wx_set_preamble(struct net_device *dev,
        err = ipw2100_system_config(priv, 0);
 
       done:
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
        return err;
 }
 
@@ -7862,7 +7972,7 @@ static int ipw2100_wx_set_crc_check(struct net_device *dev,
        struct ipw2100_priv *priv = ieee80211_priv(dev);
        int err, mode = *(int *)extra;
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
        if (!(priv->status & STATUS_INITIALIZED)) {
                err = -EIO;
                goto done;
@@ -7879,7 +7989,7 @@ static int ipw2100_wx_set_crc_check(struct net_device *dev,
        err = 0;
 
       done:
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
        return err;
 }
 
@@ -8176,19 +8286,21 @@ static struct iw_handler_def ipw2100_wx_handler_def = {
        .get_wireless_stats = ipw2100_wx_wireless_stats,
 };
 
-static void ipw2100_wx_event_work(struct ipw2100_priv *priv)
+static void ipw2100_wx_event_work(struct work_struct *work)
 {
+       struct ipw2100_priv *priv =
+               container_of(work, struct ipw2100_priv, wx_event_work.work);
        union iwreq_data wrqu;
        int len = ETH_ALEN;
 
        if (priv->status & STATUS_STOPPING)
                return;
 
-       down(&priv->action_sem);
+       mutex_lock(&priv->action_mutex);
 
        IPW_DEBUG_WX("enter\n");
 
-       up(&priv->action_sem);
+       mutex_unlock(&priv->action_mutex);
 
        wrqu.ap_addr.sa_family = ARPHRD_ETHER;
 
@@ -8211,7 +8323,7 @@ static void ipw2100_wx_event_work(struct ipw2100_priv *priv)
 
        if (!(priv->status & STATUS_ASSOCIATED)) {
                IPW_DEBUG_WX("Configuring ESSID\n");
-               down(&priv->action_sem);
+               mutex_lock(&priv->action_mutex);
                /* This is a disassociation event, so kick the firmware to
                 * look for another AP */
                if (priv->config & CFG_STATIC_ESSID)
@@ -8219,7 +8331,7 @@ static void ipw2100_wx_event_work(struct ipw2100_priv *priv)
                                          0);
                else
                        ipw2100_set_essid(priv, NULL, 0, 0);
-               up(&priv->action_sem);
+               mutex_unlock(&priv->action_mutex);
        }
 
        wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);