]> err.no Git - linux-2.6/blobdiff - drivers/net/wireless/ipw2200.c
Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik...
[linux-2.6] / drivers / net / wireless / ipw2200.c
index ef1007785030e6ac6fa7cabb95dab5d53f22afc3..b0d195d1721a6b490107c864043596d0a0768d10 100644 (file)
@@ -31,8 +31,9 @@
 ******************************************************************************/
 
 #include "ipw2200.h"
+#include <linux/version.h>
 
-#define IPW2200_VERSION "1.0.5"
+#define IPW2200_VERSION "git-1.0.8"
 #define DRV_DESCRIPTION        "Intel(R) PRO/Wireless 2200/2915 Network Driver"
 #define DRV_COPYRIGHT  "Copyright(c) 2003-2005 Intel Corporation"
 #define DRV_VERSION     IPW2200_VERSION
@@ -44,6 +45,7 @@ MODULE_VERSION(DRV_VERSION);
 MODULE_AUTHOR(DRV_COPYRIGHT);
 MODULE_LICENSE("GPL");
 
+static int cmdlog = 0;
 static int debug = 0;
 static int channel = 0;
 static int mode = 0;
@@ -124,6 +126,7 @@ static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos
                                     *qos_param);
 #endif                         /* CONFIG_IPW_QOS */
 
+static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev);
 static void ipw_remove_current_network(struct ipw_priv *priv);
 static void ipw_rx(struct ipw_priv *priv);
 static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
@@ -148,8 +151,14 @@ static int init_supported_rates(struct ipw_priv *priv,
 static void ipw_set_hwcrypto_keys(struct ipw_priv *);
 static void ipw_send_wep_keys(struct ipw_priv *, int);
 
-static char *snprint_line(char *buf, size_t count,
-                         const u8 * data, u32 len, u32 ofs)
+static int ipw_is_valid_channel(struct ieee80211_device *, u8);
+static int ipw_channel_to_index(struct ieee80211_device *, u8);
+static u8 ipw_freq_to_channel(struct ieee80211_device *, u32);
+static int ipw_set_geo(struct ieee80211_device *, const struct ieee80211_geo *);
+static const struct ieee80211_geo *ipw_get_geo(struct ieee80211_device *);
+
+static int snprint_line(char *buf, size_t count,
+                       const u8 * data, u32 len, u32 ofs)
 {
        int out, i, j, l;
        char c;
@@ -180,7 +189,7 @@ static char *snprint_line(char *buf, size_t count,
                        out += snprintf(buf + out, count - out, " ");
        }
 
-       return buf;
+       return out;
 }
 
 static void printk_buf(int level, const u8 * data, u32 len)
@@ -191,14 +200,33 @@ static void printk_buf(int level, const u8 * data, u32 len)
                return;
 
        while (len) {
-               printk(KERN_DEBUG "%s\n",
-                      snprint_line(line, sizeof(line), &data[ofs],
-                                   min(len, 16U), ofs));
+               snprint_line(line, sizeof(line), &data[ofs],
+                            min(len, 16U), ofs);
+               printk(KERN_DEBUG "%s\n", line);
                ofs += 16;
                len -= min(len, 16U);
        }
 }
 
+static int snprintk_buf(u8 * output, size_t size, const u8 * data, size_t len)
+{
+       size_t out = size;
+       u32 ofs = 0;
+       int total = 0;
+
+       while (size && len) {
+               out = snprint_line(output, size, &data[ofs],
+                                  min_t(size_t, len, 16U), ofs);
+
+               ofs += 16;
+               output += out;
+               size -= out;
+               len -= min_t(size_t, len, 16U);
+               total += out;
+       }
+       return total;
+}
+
 static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg);
 #define ipw_read_reg32(a, b) _ipw_read_reg32(a, b)
 
@@ -272,9 +300,15 @@ static inline u32 __ipw_read32(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
 #define ipw_read32(ipw, ofs) __ipw_read32(__FILE__, __LINE__, ipw, ofs)
 
 static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int);
-#define ipw_read_indirect(a, b, c, d) \
-       IPW_DEBUG_IO("%s %d: read_indirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \
-       _ipw_read_indirect(a, b, c, d)
+static inline void __ipw_read_indirect(const char *f, int l,
+                                      struct ipw_priv *a, u32 b, u8 * c, int d)
+{
+       IPW_DEBUG_IO("%s %d: read_indirect(0x%08X) %d bytes\n", f, l, (u32) (b),
+                    d);
+       _ipw_read_indirect(a, b, c, d);
+}
+
+#define ipw_read_indirect(a, b, c, d) __ipw_read_indirect(__FILE__, __LINE__, a, b, c, d)
 
 static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data,
                                int num);
@@ -493,7 +527,7 @@ static void ipw_dump_error_log(struct ipw_priv *priv,
        for (i = 0; i < error->log_len; i++)
                IPW_ERROR("%i\t0x%08x\t%i\n",
                          error->log[i].time,
-                         error->log[i].event, error->log[i].data);
+                         error->log[i].data, error->log[i].event);
 }
 #endif
 
@@ -1070,6 +1104,7 @@ static struct ipw_fw_error *ipw_alloc_error_log(struct ipw_priv *priv)
                          "failed.\n");
                return NULL;
        }
+       error->jiffies = jiffies;
        error->status = priv->status;
        error->config = priv->config;
        error->elem_len = elem_len;
@@ -1122,7 +1157,8 @@ static ssize_t show_error(struct device *d,
        if (!priv->error)
                return 0;
        len += snprintf(buf + len, PAGE_SIZE - len,
-                       "%08X%08X%08X",
+                       "%08lX%08X%08X%08X",
+                       priv->error->jiffies,
                        priv->error->status,
                        priv->error->config, priv->error->elem_len);
        for (i = 0; i < priv->error->elem_len; i++)
@@ -1162,6 +1198,33 @@ static ssize_t clear_error(struct device *d,
 
 static DEVICE_ATTR(error, S_IRUGO | S_IWUSR, show_error, clear_error);
 
+static ssize_t show_cmd_log(struct device *d,
+                           struct device_attribute *attr, char *buf)
+{
+       struct ipw_priv *priv = dev_get_drvdata(d);
+       u32 len = 0, i;
+       if (!priv->cmdlog)
+               return 0;
+       for (i = (priv->cmdlog_pos + 1) % priv->cmdlog_len;
+            (i != priv->cmdlog_pos) && (PAGE_SIZE - len);
+            i = (i + 1) % priv->cmdlog_len) {
+               len +=
+                   snprintf(buf + len, PAGE_SIZE - len,
+                            "\n%08lX%08X%08X%08X\n", priv->cmdlog[i].jiffies,
+                            priv->cmdlog[i].retcode, priv->cmdlog[i].cmd.cmd,
+                            priv->cmdlog[i].cmd.len);
+               len +=
+                   snprintk_buf(buf + len, PAGE_SIZE - len,
+                                (u8 *) priv->cmdlog[i].cmd.param,
+                                priv->cmdlog[i].cmd.len);
+               len += snprintf(buf + len, PAGE_SIZE - len, "\n");
+       }
+       len += snprintf(buf + len, PAGE_SIZE - len, "\n");
+       return len;
+}
+
+static DEVICE_ATTR(cmd_log, S_IRUGO, show_cmd_log, NULL);
+
 static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
                             char *buf)
 {
@@ -1541,7 +1604,7 @@ static ssize_t store_speed_scan(struct device *d, struct device_attribute *attr,
                        break;
                }
 
-               if (ieee80211_is_valid_channel(priv->ieee, channel))
+               if (ipw_is_valid_channel(priv->ieee, channel))
                        priv->speed_scan[pos++] = channel;
                else
                        IPW_WARNING("Skipping invalid channel request: %d\n",
@@ -1748,7 +1811,6 @@ static void ipw_irq_tasklet(struct ipw_priv *priv)
        spin_unlock_irqrestore(&priv->lock, flags);
 }
 
-#ifdef CONFIG_IPW_DEBUG
 #define IPW_CMD(x) case IPW_CMD_ ## x : return #x
 static char *get_cmd_string(u8 cmd)
 {
@@ -1807,7 +1869,6 @@ static char *get_cmd_string(u8 cmd)
                return "UNKNOWN";
        }
 }
-#endif
 
 #define HOST_COMPLETE_TIMEOUT HZ
 static int ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
@@ -1825,6 +1886,15 @@ static int ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
 
        priv->status |= STATUS_HCMD_ACTIVE;
 
+       if (priv->cmdlog) {
+               priv->cmdlog[priv->cmdlog_pos].jiffies = jiffies;
+               priv->cmdlog[priv->cmdlog_pos].cmd.cmd = cmd->cmd;
+               priv->cmdlog[priv->cmdlog_pos].cmd.len = cmd->len;
+               memcpy(priv->cmdlog[priv->cmdlog_pos].cmd.param, cmd->param,
+                      cmd->len);
+               priv->cmdlog[priv->cmdlog_pos].retcode = -1;
+       }
+
        IPW_DEBUG_HC("%s command (#%d) %d bytes: 0x%08X\n",
                     get_cmd_string(cmd->cmd), cmd->cmd, cmd->len,
                     priv->status);
@@ -1836,7 +1906,7 @@ static int ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
                IPW_ERROR("Failed to send %s: Reason %d\n",
                          get_cmd_string(cmd->cmd), rc);
                spin_unlock_irqrestore(&priv->lock, flags);
-               return rc;
+               goto exit;
        }
        spin_unlock_irqrestore(&priv->lock, flags);
 
@@ -1851,18 +1921,26 @@ static int ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
                                  get_cmd_string(cmd->cmd));
                        priv->status &= ~STATUS_HCMD_ACTIVE;
                        spin_unlock_irqrestore(&priv->lock, flags);
-                       return -EIO;
+                       rc = -EIO;
+                       goto exit;
                }
                spin_unlock_irqrestore(&priv->lock, flags);
-       }
+       } else
+               rc = 0;
 
        if (priv->status & STATUS_RF_KILL_HW) {
                IPW_ERROR("Failed to send %s: Aborted due to RF kill switch.\n",
                          get_cmd_string(cmd->cmd));
-               return -EIO;
+               rc = -EIO;
+               goto exit;
        }
 
-       return 0;
+      exit:
+       if (priv->cmdlog) {
+               priv->cmdlog[priv->cmdlog_pos++].retcode = rc;
+               priv->cmdlog_pos %= priv->cmdlog_len;
+       }
+       return rc;
 }
 
 static int ipw_send_host_complete(struct ipw_priv *priv)
@@ -2123,7 +2201,7 @@ static int ipw_send_tx_power(struct ipw_priv *priv, struct ipw_tx_power *power)
 
 static int ipw_set_tx_power(struct ipw_priv *priv)
 {
-       const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
+       const struct ieee80211_geo *geo = ipw_get_geo(priv->ieee);
        struct ipw_tx_power tx_power;
        s8 max_power;
        int i;
@@ -2757,7 +2835,7 @@ struct fw_chunk {
 };
 
 #define IPW_FW_MAJOR_VERSION 2
-#define IPW_FW_MINOR_VERSION 3
+#define IPW_FW_MINOR_VERSION 4
 
 #define IPW_FW_MINOR(x) ((x & 0xff) >> 8)
 #define IPW_FW_MAJOR(x) (x & 0xff)
@@ -3214,7 +3292,7 @@ static int ipw_load(struct ipw_priv *priv)
        rc = ipw_load_firmware(priv, bootfw->data + sizeof(struct fw_header),
                               bootfw->size - sizeof(struct fw_header));
        if (rc < 0) {
-               IPW_ERROR("Unable to load boot firmware\n");
+               IPW_ERROR("Unable to load boot firmware: %d\n", rc);
                goto error;
        }
 
@@ -3237,7 +3315,7 @@ static int ipw_load(struct ipw_priv *priv)
        rc = ipw_load_ucode(priv, ucode->data + sizeof(struct fw_header),
                            ucode->size - sizeof(struct fw_header));
        if (rc < 0) {
-               IPW_ERROR("Unable to load ucode\n");
+               IPW_ERROR("Unable to load ucode: %d\n", rc);
                goto error;
        }
 
@@ -3249,7 +3327,7 @@ static int ipw_load(struct ipw_priv *priv)
                               sizeof(struct fw_header),
                               firmware->size - sizeof(struct fw_header));
        if (rc < 0) {
-               IPW_ERROR("Unable to load firmware\n");
+               IPW_ERROR("Unable to load firmware: %d\n", rc);
                goto error;
        }
 
@@ -3593,7 +3671,13 @@ static void ipw_send_disassociate(struct ipw_priv *priv, int quiet)
 {
        int err;
 
-       if (!(priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED))) {
+       if (priv->status & STATUS_ASSOCIATING) {
+               IPW_DEBUG_ASSOC("Disassociating while associating.\n");
+               queue_work(priv->workqueue, &priv->disassociate);
+               return;
+       }
+
+       if (!(priv->status & STATUS_ASSOCIATED)) {
                IPW_DEBUG_ASSOC("Disassociating while not associated.\n");
                return;
        }
@@ -3610,6 +3694,7 @@ static void ipw_send_disassociate(struct ipw_priv *priv, int quiet)
                priv->assoc_request.assoc_type = HC_DISASSOC_QUIET;
        else
                priv->assoc_request.assoc_type = HC_DISASSOCIATE;
+
        err = ipw_send_associate(priv, &priv->assoc_request);
        if (err) {
                IPW_DEBUG_HC("Attempt to send [dis]associate command "
@@ -3998,6 +4083,11 @@ static void ipw_bg_gather_stats(void *data)
        up(&priv->sem);
 }
 
+/* Missed beacon behavior:
+ * 1st missed -> roaming_threshold, just wait, don't do any scan/roam.
+ * roaming_threshold -> disassociate_threshold, scan and roam for better signal.
+ * Above disassociate threshold, give up and stop scanning.
+ * Roaming is disabled if disassociate_threshold <= roaming_threshold  */
 static inline void ipw_handle_missed_beacon(struct ipw_priv *priv,
                                            int missed_count)
 {
@@ -4032,9 +4122,12 @@ static inline void ipw_handle_missed_beacon(struct ipw_priv *priv,
                return;
        }
 
-       if (missed_count > priv->roaming_threshold) {
+       if (missed_count > priv->roaming_threshold &&
+           missed_count <= priv->disassociate_threshold) {
                /* If we are not already roaming, set the ROAM
-                * bit in the status and kick off a scan */
+                * bit in the status and kick off a scan.
+                * This can happen several times before we reach
+                * disassociate_threshold. */
                IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
                          "Missed beacon: %d - initiate "
                          "roaming\n", missed_count);
@@ -4372,6 +4465,7 @@ static inline void ipw_rx_notification(struct ipw_priv *priv,
                        priv->status &=
                            ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
 
+                       wake_up_interruptible(&priv->wait_state);
                        cancel_delayed_work(&priv->scan_check);
 
                        if (priv->status & STATUS_EXIT_PENDING)
@@ -4395,11 +4489,16 @@ static inline void ipw_rx_notification(struct ipw_priv *priv,
                                              STATUS_DISASSOCIATING)))
                                queue_work(priv->workqueue, &priv->associate);
                        else if (priv->status & STATUS_ROAMING) {
-                               /* If a scan completed and we are in roam mode, then
-                                * the scan that completed was the one requested as a
-                                * result of entering roam... so, schedule the
-                                * roam work */
-                               queue_work(priv->workqueue, &priv->roam);
+                               if (x->status == SCAN_COMPLETED_STATUS_COMPLETE)
+                                       /* If a scan completed and we are in roam mode, then
+                                        * the scan that completed was the one requested as a
+                                        * result of entering roam... so, schedule the
+                                        * roam work */
+                                       queue_work(priv->workqueue,
+                                                  &priv->roam);
+                               else
+                                       /* 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);
@@ -5413,6 +5512,15 @@ static int ipw_best_network(struct ipw_priv *priv,
                return 0;
        }
 
+       if (!priv->ieee->wpa_enabled && (network->wpa_ie_len > 0 ||
+                                        network->rsn_ie_len > 0)) {
+               IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+                               "because of WPA capability mismatch.\n",
+                               escape_essid(network->ssid, network->ssid_len),
+                               MAC_ARG(network->bssid));
+               return 0;
+       }
+
        if ((priv->config & CFG_STATIC_BSSID) &&
            memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
                IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
@@ -5432,6 +5540,15 @@ static int ipw_best_network(struct ipw_priv *priv,
                return 0;
        }
 
+       /* Filter out invalid channel in current GEO */
+       if (!ipw_is_valid_channel(priv->ieee, network->channel)) {
+               IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
+                               "because of invalid channel in current GEO\n",
+                               escape_essid(network->ssid, network->ssid_len),
+                               MAC_ARG(network->bssid));
+               return 0;
+       }
+
        /* Ensure that the rates supported by the driver are compatible with
         * this AP, including verification of basic rates (mandatory) */
        if (!ipw_compatible_rates(priv, network, &rates)) {
@@ -5469,7 +5586,7 @@ static int ipw_best_network(struct ipw_priv *priv,
 static void ipw_adhoc_create(struct ipw_priv *priv,
                             struct ieee80211_network *network)
 {
-       const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
+       const struct ieee80211_geo *geo = ipw_get_geo(priv->ieee);
        int i;
 
        /*
@@ -5484,10 +5601,10 @@ static void ipw_adhoc_create(struct ipw_priv *priv,
         * FW fatal error.
         *
         */
-       switch (ieee80211_is_valid_channel(priv->ieee, priv->channel)) {
+       switch (ipw_is_valid_channel(priv->ieee, priv->channel)) {
        case IEEE80211_52GHZ_BAND:
                network->mode = IEEE_A;
-               i = ieee80211_channel_to_index(priv->ieee, priv->channel);
+               i = ipw_channel_to_index(priv->ieee, priv->channel);
                if (i == -1)
                        BUG();
                if (geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY) {
@@ -5501,6 +5618,13 @@ static void ipw_adhoc_create(struct ipw_priv *priv,
                        network->mode = IEEE_G;
                else
                        network->mode = IEEE_B;
+               i = ipw_channel_to_index(priv->ieee, priv->channel);
+               if (i == -1)
+                       BUG();
+               if (geo->bg[i].flags & IEEE80211_CH_PASSIVE_ONLY) {
+                       IPW_WARNING("Overriding invalid channel\n");
+                       priv->channel = geo->bg[0].channel;
+               }
                break;
 
        default:
@@ -5658,7 +5782,8 @@ static void ipw_set_hwcrypto_keys(struct ipw_priv *priv)
                                            DCT_FLAG_EXT_SECURITY_CCM,
                                            priv->ieee->sec.active_key);
 
-               ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_CCM);
+               if (!priv->ieee->host_mc_decrypt)
+                       ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_CCM);
                break;
        case SEC_LEVEL_2:
                if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
@@ -5668,14 +5793,13 @@ static void ipw_set_hwcrypto_keys(struct ipw_priv *priv)
                break;
        case SEC_LEVEL_1:
                ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP);
+               ipw_set_hw_decrypt_unicast(priv, priv->ieee->sec.level);
+               ipw_set_hw_decrypt_multicast(priv, priv->ieee->sec.level);
                break;
        case SEC_LEVEL_0:
        default:
                break;
        }
-
-       ipw_set_hw_decrypt_unicast(priv, priv->ieee->sec.level);
-       ipw_set_hw_decrypt_multicast(priv, priv->ieee->sec.level);
 }
 
 static void ipw_adhoc_check(void *data)
@@ -5828,7 +5952,7 @@ static void ipw_add_scan_channels(struct ipw_priv *priv,
        const struct ieee80211_geo *geo;
        int i;
 
-       geo = ieee80211_get_geo(priv->ieee);
+       geo = ipw_get_geo(priv->ieee);
 
        if (priv->ieee->freq_band & IEEE80211_52GHZ_BAND) {
                int start = channel_index;
@@ -5838,7 +5962,11 @@ static void ipw_add_scan_channels(struct ipw_priv *priv,
                                continue;
                        channel_index++;
                        scan->channels_list[channel_index] = geo->a[i].channel;
-                       ipw_set_scan_type(scan, channel_index, scan_type);
+                       ipw_set_scan_type(scan, channel_index,
+                                         geo->a[i].
+                                         flags & IEEE80211_CH_PASSIVE_ONLY ?
+                                         IPW_SCAN_PASSIVE_FULL_DWELL_SCAN :
+                                         scan_type);
                }
 
                if (start != channel_index) {
@@ -5851,6 +5979,7 @@ static void ipw_add_scan_channels(struct ipw_priv *priv,
        if (priv->ieee->freq_band & IEEE80211_24GHZ_BAND) {
                int start = channel_index;
                if (priv->config & CFG_SPEED_SCAN) {
+                       int index;
                        u8 channels[IEEE80211_24GHZ_CHANNELS] = {
                                /* nop out the list */
                                [0] = 0
@@ -5882,8 +6011,14 @@ static void ipw_add_scan_channels(struct ipw_priv *priv,
                                priv->speed_scan_pos++;
                                channel_index++;
                                scan->channels_list[channel_index] = channel;
+                               index =
+                                   ipw_channel_to_index(priv->ieee, channel);
                                ipw_set_scan_type(scan, channel_index,
-                                                 scan_type);
+                                                 geo->bg[index].
+                                                 flags &
+                                                 IEEE80211_CH_PASSIVE_ONLY ?
+                                                 IPW_SCAN_PASSIVE_FULL_DWELL_SCAN
+                                                 : scan_type);
                        }
                } else {
                        for (i = 0; i < geo->bg_channels; i++) {
@@ -5894,7 +6029,11 @@ static void ipw_add_scan_channels(struct ipw_priv *priv,
                                scan->channels_list[channel_index] =
                                    geo->bg[i].channel;
                                ipw_set_scan_type(scan, channel_index,
-                                                 scan_type);
+                                                 geo->bg[i].
+                                                 flags &
+                                                 IEEE80211_CH_PASSIVE_ONLY ?
+                                                 IPW_SCAN_PASSIVE_FULL_DWELL_SCAN
+                                                 : scan_type);
                        }
                }
 
@@ -5946,7 +6085,7 @@ static int ipw_request_scan(struct ipw_priv *priv)
 
        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(20);
+       scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(120);
 
        scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee));
 
@@ -5955,7 +6094,7 @@ static int ipw_request_scan(struct ipw_priv *priv)
                u8 channel;
                u8 band = 0;
 
-               switch (ieee80211_is_valid_channel(priv->ieee, priv->channel)) {
+               switch (ipw_is_valid_channel(priv->ieee, priv->channel)) {
                case IEEE80211_52GHZ_BAND:
                        band = (u8) (IPW_A_MODE << 6) | 1;
                        channel = priv->channel;
@@ -6034,82 +6173,14 @@ static void ipw_bg_abort_scan(void *data)
        up(&priv->sem);
 }
 
-#if WIRELESS_EXT < 18
-/* Support for wpa_supplicant before WE-18, deprecated. */
-
-/* following definitions must match definitions in driver_ipw.c */
-
-#define IPW_IOCTL_WPA_SUPPLICANT               SIOCIWFIRSTPRIV+30
-
-#define IPW_CMD_SET_WPA_PARAM                  1
-#define        IPW_CMD_SET_WPA_IE                      2
-#define IPW_CMD_SET_ENCRYPTION                 3
-#define IPW_CMD_MLME                           4
-
-#define IPW_PARAM_WPA_ENABLED                  1
-#define IPW_PARAM_TKIP_COUNTERMEASURES         2
-#define IPW_PARAM_DROP_UNENCRYPTED             3
-#define IPW_PARAM_PRIVACY_INVOKED              4
-#define IPW_PARAM_AUTH_ALGS                    5
-#define IPW_PARAM_IEEE_802_1X                  6
-
-#define IPW_MLME_STA_DEAUTH                    1
-#define IPW_MLME_STA_DISASSOC                  2
-
-#define IPW_CRYPT_ERR_UNKNOWN_ALG              2
-#define IPW_CRYPT_ERR_UNKNOWN_ADDR             3
-#define IPW_CRYPT_ERR_CRYPT_INIT_FAILED                4
-#define IPW_CRYPT_ERR_KEY_SET_FAILED           5
-#define IPW_CRYPT_ERR_TX_KEY_SET_FAILED                6
-#define IPW_CRYPT_ERR_CARD_CONF_FAILED         7
-
-#define        IPW_CRYPT_ALG_NAME_LEN                  16
-
-struct ipw_param {
-       u32 cmd;
-       u8 sta_addr[ETH_ALEN];
-       union {
-               struct {
-                       u8 name;
-                       u32 value;
-               } wpa_param;
-               struct {
-                       u32 len;
-                       u8 reserved[32];
-                       u8 data[0];
-               } wpa_ie;
-               struct {
-                       u32 command;
-                       u32 reason_code;
-               } mlme;
-               struct {
-                       u8 alg[IPW_CRYPT_ALG_NAME_LEN];
-                       u8 set_tx;
-                       u32 err;
-                       u8 idx;
-                       u8 seq[8];      /* sequence counter (set: RX, get: TX) */
-                       u16 key_len;
-                       u8 key[0];
-               } crypt;
-
-       } u;
-};
-
-/* end of driver_ipw.c code */
-#endif
-
 static int ipw_wpa_enable(struct ipw_priv *priv, int value)
 {
        /* This is called when wpa_supplicant loads and closes the driver
         * interface. */
+       priv->ieee->wpa_enabled = value;
        return 0;
 }
 
-#if WIRELESS_EXT < 18
-#define IW_AUTH_ALG_OPEN_SYSTEM                        0x1
-#define IW_AUTH_ALG_SHARED_KEY                 0x2
-#endif
-
 static int ipw_wpa_set_auth_algs(struct ipw_priv *priv, int value)
 {
        struct ieee80211_device *ieee = priv->ieee;
@@ -6157,401 +6228,6 @@ static int ipw_set_rsn_capa(struct ipw_priv *priv,
        return ipw_send_cmd(priv, &cmd);
 }
 
-#if WIRELESS_EXT < 18
-static int ipw_wpa_set_param(struct net_device *dev, u8 name, u32 value)
-{
-       struct ipw_priv *priv = ieee80211_priv(dev);
-       struct ieee80211_crypt_data *crypt;
-       unsigned long flags;
-       int ret = 0;
-
-       switch (name) {
-       case IPW_PARAM_WPA_ENABLED:
-               ret = ipw_wpa_enable(priv, value);
-               break;
-
-       case IPW_PARAM_TKIP_COUNTERMEASURES:
-               crypt = priv->ieee->crypt[priv->ieee->tx_keyidx];
-               if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags) {
-                       IPW_WARNING("Can't set TKIP countermeasures: "
-                                   "crypt not set!\n");
-                       break;
-               }
-
-               flags = crypt->ops->get_flags(crypt->priv);
-
-               if (value)
-                       flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
-               else
-                       flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
-
-               crypt->ops->set_flags(flags, crypt->priv);
-
-               break;
-
-       case IPW_PARAM_DROP_UNENCRYPTED:{
-                       /* HACK:
-                        *
-                        * wpa_supplicant calls set_wpa_enabled when the driver
-                        * is loaded and unloaded, regardless of if WPA is being
-                        * used.  No other calls are made which can be used to
-                        * determine if encryption will be used or not prior to
-                        * association being expected.  If encryption is not being
-                        * used, drop_unencrypted is set to false, else true -- we
-                        * can use this to determine if the CAP_PRIVACY_ON bit should
-                        * be set.
-                        */
-                       struct ieee80211_security sec = {
-                               .flags = SEC_ENABLED,
-                               .enabled = value,
-                       };
-                       priv->ieee->drop_unencrypted = value;
-                       /* We only change SEC_LEVEL for open mode. Others
-                        * are set by ipw_wpa_set_encryption.
-                        */
-                       if (!value) {
-                               sec.flags |= SEC_LEVEL;
-                               sec.level = SEC_LEVEL_0;
-                       } else {
-                               sec.flags |= SEC_LEVEL;
-                               sec.level = SEC_LEVEL_1;
-                       }
-                       if (priv->ieee->set_security)
-                               priv->ieee->set_security(priv->ieee->dev, &sec);
-                       break;
-               }
-
-       case IPW_PARAM_PRIVACY_INVOKED:
-               priv->ieee->privacy_invoked = value;
-               break;
-
-       case IPW_PARAM_AUTH_ALGS:
-               ret = ipw_wpa_set_auth_algs(priv, value);
-               break;
-
-       case IPW_PARAM_IEEE_802_1X:
-               priv->ieee->ieee802_1x = value;
-               break;
-
-       default:
-               IPW_ERROR("%s: Unknown WPA param: %d\n", dev->name, name);
-               ret = -EOPNOTSUPP;
-       }
-
-       return ret;
-}
-
-static int ipw_wpa_mlme(struct net_device *dev, int command, int reason)
-{
-       struct ipw_priv *priv = ieee80211_priv(dev);
-       int ret = 0;
-
-       switch (command) {
-       case IPW_MLME_STA_DEAUTH:
-               // silently ignore
-               break;
-
-       case IPW_MLME_STA_DISASSOC:
-               ipw_disassociate(priv);
-               break;
-
-       default:
-               IPW_ERROR("%s: Unknown MLME request: %d\n", dev->name, command);
-               ret = -EOPNOTSUPP;
-       }
-
-       return ret;
-}
-
-static int ipw_wpa_ie_cipher2level(u8 cipher)
-{
-       switch (cipher) {
-       case 4:         /* CCMP */
-               return SEC_LEVEL_3;
-       case 2:         /* TKIP */
-               return SEC_LEVEL_2;
-       case 5:         /* WEP104 */
-       case 1:         /* WEP40 */
-               return SEC_LEVEL_1;
-       case 0:         /* NONE */
-               return SEC_LEVEL_0;
-       default:
-               return -1;
-       }
-}
-
-static int ipw_wpa_set_wpa_ie(struct net_device *dev,
-                             struct ipw_param *param, int plen)
-{
-       struct ipw_priv *priv = ieee80211_priv(dev);
-       struct ieee80211_device *ieee = priv->ieee;
-       u8 *buf;
-       u8 *ptk, *gtk;
-       int level;
-
-       if (param->u.wpa_ie.len > MAX_WPA_IE_LEN ||
-           (param->u.wpa_ie.len && param->u.wpa_ie.data == NULL))
-               return -EINVAL;
-
-       if (param->u.wpa_ie.len) {
-               buf = kmalloc(param->u.wpa_ie.len, GFP_KERNEL);
-               if (buf == NULL)
-                       return -ENOMEM;
-
-               memcpy(buf, param->u.wpa_ie.data, param->u.wpa_ie.len);
-               kfree(ieee->wpa_ie);
-               ieee->wpa_ie = buf;
-               ieee->wpa_ie_len = param->u.wpa_ie.len;
-       } else {
-               kfree(ieee->wpa_ie);
-               ieee->wpa_ie = NULL;
-               ieee->wpa_ie_len = 0;
-               goto done;
-       }
-
-       if (priv->ieee->host_encrypt)
-               goto done;
-
-       /* HACK: Parse wpa_ie here to get pairwise suite, otherwise
-        * we need to change driver_ipw.c from wpa_supplicant. This
-        * is OK since -Dipw is deprecated. The -Dwext driver has a
-        * clean way to handle this. */
-       gtk = ptk = (u8 *) ieee->wpa_ie;
-       if (ieee->wpa_ie[0] == 0x30) {  /* RSN IE */
-               gtk += 4 + 3;
-               ptk += 4 + 4 + 2 + 3;
-       } else {                /* WPA IE */
-               gtk += 8 + 3;
-               ptk += 8 + 4 + 2 + 3;
-       }
-
-       if (ptk - (u8 *) ieee->wpa_ie > ieee->wpa_ie_len)
-               return -EINVAL;
-
-       level = ipw_wpa_ie_cipher2level(*gtk);
-       ipw_set_hw_decrypt_multicast(priv, level);
-
-       level = ipw_wpa_ie_cipher2level(*ptk);
-       ipw_set_hw_decrypt_unicast(priv, level);
-
-      done:
-       ipw_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len);
-       return 0;
-}
-
-/* implementation borrowed from hostap driver */
-
-static int ipw_wpa_set_encryption(struct net_device *dev,
-                                 struct ipw_param *param, int param_len)
-{
-       int ret = 0;
-       struct ipw_priv *priv = ieee80211_priv(dev);
-       struct ieee80211_device *ieee = priv->ieee;
-       struct ieee80211_crypto_ops *ops;
-       struct ieee80211_crypt_data **crypt;
-
-       struct ieee80211_security sec = {
-               .flags = 0,
-       };
-
-       param->u.crypt.err = 0;
-       param->u.crypt.alg[IPW_CRYPT_ALG_NAME_LEN - 1] = '\0';
-
-       if (param_len !=
-           (int)((char *)param->u.crypt.key - (char *)param) +
-           param->u.crypt.key_len) {
-               IPW_DEBUG_INFO("Len mismatch %d, %d\n", param_len,
-                              param->u.crypt.key_len);
-               return -EINVAL;
-       }
-       if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
-           param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
-           param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
-               if (param->u.crypt.idx >= WEP_KEYS)
-                       return -EINVAL;
-               crypt = &ieee->crypt[param->u.crypt.idx];
-       } else {
-               return -EINVAL;
-       }
-
-       sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
-       if (strcmp(param->u.crypt.alg, "none") == 0) {
-               if (crypt) {
-                       sec.enabled = 0;
-                       sec.encrypt = 0;
-                       sec.level = SEC_LEVEL_0;
-                       sec.flags |= SEC_LEVEL;
-                       ieee80211_crypt_delayed_deinit(ieee, crypt);
-               }
-               goto done;
-       }
-       sec.enabled = 1;
-       sec.encrypt = 1;
-
-       /* IPW HW cannot build TKIP MIC, host decryption still needed. */
-       if (strcmp(param->u.crypt.alg, "TKIP") == 0)
-               ieee->host_encrypt_msdu = 1;
-
-       if (!(ieee->host_encrypt || ieee->host_encrypt_msdu ||
-             ieee->host_decrypt))
-               goto skip_host_crypt;
-
-       ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
-       if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) {
-               request_module("ieee80211_crypt_wep");
-               ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
-       } else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) {
-               request_module("ieee80211_crypt_tkip");
-               ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
-       } else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) {
-               request_module("ieee80211_crypt_ccmp");
-               ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
-       }
-       if (ops == NULL) {
-               IPW_DEBUG_INFO("%s: unknown crypto alg '%s'\n",
-                              dev->name, param->u.crypt.alg);
-               param->u.crypt.err = IPW_CRYPT_ERR_UNKNOWN_ALG;
-               ret = -EINVAL;
-               goto done;
-       }
-
-       if (*crypt == NULL || (*crypt)->ops != ops) {
-               struct ieee80211_crypt_data *new_crypt;
-
-               ieee80211_crypt_delayed_deinit(ieee, crypt);
-
-               new_crypt = (struct ieee80211_crypt_data *)
-                   kmalloc(sizeof(*new_crypt), GFP_KERNEL);
-               if (new_crypt == NULL) {
-                       ret = -ENOMEM;
-                       goto done;
-               }
-               memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
-               new_crypt->ops = ops;
-               if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
-                       new_crypt->priv =
-                           new_crypt->ops->init(param->u.crypt.idx);
-
-               if (new_crypt->priv == NULL) {
-                       kfree(new_crypt);
-                       param->u.crypt.err = IPW_CRYPT_ERR_CRYPT_INIT_FAILED;
-                       ret = -EINVAL;
-                       goto done;
-               }
-
-               *crypt = new_crypt;
-       }
-
-       if (param->u.crypt.key_len > 0 && (*crypt)->ops->set_key &&
-           (*crypt)->ops->set_key(param->u.crypt.key,
-                                  param->u.crypt.key_len, param->u.crypt.seq,
-                                  (*crypt)->priv) < 0) {
-               IPW_DEBUG_INFO("%s: key setting failed\n", dev->name);
-               param->u.crypt.err = IPW_CRYPT_ERR_KEY_SET_FAILED;
-               ret = -EINVAL;
-               goto done;
-       }
-
-      skip_host_crypt:
-       if (param->u.crypt.set_tx) {
-               ieee->tx_keyidx = param->u.crypt.idx;
-               sec.active_key = param->u.crypt.idx;
-               sec.flags |= SEC_ACTIVE_KEY;
-       } else
-               sec.flags &= ~SEC_ACTIVE_KEY;
-
-       if (param->u.crypt.alg != NULL) {
-               memcpy(sec.keys[param->u.crypt.idx],
-                      param->u.crypt.key, param->u.crypt.key_len);
-               sec.key_sizes[param->u.crypt.idx] = param->u.crypt.key_len;
-               sec.flags |= (1 << param->u.crypt.idx);
-
-               if (strcmp(param->u.crypt.alg, "WEP") == 0) {
-                       sec.flags |= SEC_LEVEL;
-                       sec.level = SEC_LEVEL_1;
-               } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) {
-                       sec.flags |= SEC_LEVEL;
-                       sec.level = SEC_LEVEL_2;
-               } else if (strcmp(param->u.crypt.alg, "CCMP") == 0) {
-                       sec.flags |= SEC_LEVEL;
-                       sec.level = SEC_LEVEL_3;
-               }
-       }
-      done:
-       if (ieee->set_security)
-               ieee->set_security(ieee->dev, &sec);
-
-       /* Do not reset port if card is in Managed mode since resetting will
-        * generate new IEEE 802.11 authentication which may end up in looping
-        * with IEEE 802.1X.  If your hardware requires a reset after WEP
-        * configuration (for example... Prism2), implement the reset_port in
-        * the callbacks structures used to initialize the 802.11 stack. */
-       if (ieee->reset_on_keychange &&
-           ieee->iw_mode != IW_MODE_INFRA &&
-           ieee->reset_port && ieee->reset_port(dev)) {
-               IPW_DEBUG_INFO("%s: reset_port failed\n", dev->name);
-               param->u.crypt.err = IPW_CRYPT_ERR_CARD_CONF_FAILED;
-               return -EINVAL;
-       }
-
-       return ret;
-}
-
-static int ipw_wpa_supplicant(struct net_device *dev, struct iw_point *p)
-{
-       struct ipw_param *param;
-       struct ipw_priv *priv = ieee80211_priv(dev);
-       int ret = 0;
-
-       IPW_DEBUG_INFO("wpa_supplicant: len=%d\n", p->length);
-
-       if (p->length < sizeof(struct ipw_param) || !p->pointer)
-               return -EINVAL;
-
-       param = (struct ipw_param *)kmalloc(p->length, GFP_KERNEL);
-       if (param == NULL)
-               return -ENOMEM;
-
-       if (copy_from_user(param, p->pointer, p->length)) {
-               kfree(param);
-               return -EFAULT;
-       }
-
-       down(&priv->sem);
-       switch (param->cmd) {
-
-       case IPW_CMD_SET_WPA_PARAM:
-               ret = ipw_wpa_set_param(dev, param->u.wpa_param.name,
-                                       param->u.wpa_param.value);
-               break;
-
-       case IPW_CMD_SET_WPA_IE:
-               ret = ipw_wpa_set_wpa_ie(dev, param, p->length);
-               break;
-
-       case IPW_CMD_SET_ENCRYPTION:
-               ret = ipw_wpa_set_encryption(dev, param, p->length);
-               break;
-
-       case IPW_CMD_MLME:
-               ret = ipw_wpa_mlme(dev, param->u.mlme.command,
-                                  param->u.mlme.reason_code);
-               break;
-
-       default:
-               IPW_ERROR("%s: Unknown WPA supplicant request: %d\n",
-                         dev->name, param->cmd);
-               ret = -EOPNOTSUPP;
-       }
-
-       up(&priv->sem);
-       if (ret == 0 && copy_to_user(p->pointer, param, p->length))
-               ret = -EFAULT;
-
-       kfree(param);
-       return ret;
-}
-#else
 /*
  * WE-18 support
  */
@@ -6682,11 +6358,8 @@ static int ipw_wx_set_auth(struct net_device *dev,
 
        case IW_AUTH_TKIP_COUNTERMEASURES:
                crypt = priv->ieee->crypt[priv->ieee->tx_keyidx];
-               if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags) {
-                       IPW_WARNING("Can't set TKIP countermeasures: "
-                                   "crypt not set!\n");
+               if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags)
                        break;
-               }
 
                flags = crypt->ops->get_flags(crypt->priv);
 
@@ -6778,11 +6451,8 @@ static int ipw_wx_get_auth(struct net_device *dev,
 
        case IW_AUTH_TKIP_COUNTERMEASURES:
                crypt = priv->ieee->crypt[priv->ieee->tx_keyidx];
-               if (!crypt || !crypt->ops->get_flags) {
-                       IPW_WARNING("Can't get TKIP countermeasures: "
-                                   "crypt not set!\n");
+               if (!crypt || !crypt->ops->get_flags)
                        break;
-               }
 
                param->value = (crypt->ops->get_flags(crypt->priv) &
                                IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) ? 1 : 0;
@@ -6825,15 +6495,21 @@ static int ipw_wx_set_encodeext(struct net_device *dev,
        struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
 
        if (hwcrypto) {
-               /* IPW HW can't build TKIP MIC, host decryption still needed */
                if (ext->alg == IW_ENCODE_ALG_TKIP) {
-                       priv->ieee->host_encrypt = 0;
-                       priv->ieee->host_encrypt_msdu = 1;
-                       priv->ieee->host_decrypt = 1;
+                       /* IPW HW can't build TKIP MIC,
+                          host decryption still needed */
+                       if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
+                               priv->ieee->host_mc_decrypt = 1;
+                       else {
+                               priv->ieee->host_encrypt = 0;
+                               priv->ieee->host_encrypt_msdu = 1;
+                               priv->ieee->host_decrypt = 1;
+                       }
                } else {
                        priv->ieee->host_encrypt = 0;
                        priv->ieee->host_encrypt_msdu = 0;
                        priv->ieee->host_decrypt = 0;
+                       priv->ieee->host_mc_decrypt = 0;
                }
        }
 
@@ -6874,7 +6550,6 @@ static int ipw_wx_set_mlme(struct net_device *dev,
        }
        return 0;
 }
-#endif
 
 #ifdef CONFIG_IPW_QOS
 
@@ -6903,9 +6578,9 @@ u8 ipw_qos_current_mode(struct ipw_priv * priv)
 /*
 * Handle management frame beacon and probe response
 */
-static int ipw_qos_handle_probe_reponse(struct ipw_priv *priv,
-                                       int active_network,
-                                       struct ieee80211_network *network)
+static int ipw_qos_handle_probe_response(struct ipw_priv *priv,
+                                        int active_network,
+                                        struct ieee80211_network *network)
 {
        u32 size = sizeof(struct ieee80211_qos_parameters);
 
@@ -7299,35 +6974,41 @@ static void ipw_bg_qos_activate(void *data)
        up(&priv->sem);
 }
 
-/*
-* Handler for probe responce and beacon frame
-*/
-static int ipw_handle_management(struct net_device *dev,
-                                struct ieee80211_network *network, u16 type)
+static int ipw_handle_probe_response(struct net_device *dev,
+                                    struct ieee80211_probe_response *resp,
+                                    struct ieee80211_network *network)
 {
        struct ipw_priv *priv = ieee80211_priv(dev);
-       int active_network;
+       int active_network = ((priv->status & STATUS_ASSOCIATED) &&
+                             (network == priv->assoc_network));
 
-       if (priv->status & STATUS_ASSOCIATED && network == priv->assoc_network)
-               active_network = 1;
-       else
-               active_network = 0;
+       ipw_qos_handle_probe_response(priv, active_network, network);
 
-       switch (type) {
-       case IEEE80211_STYPE_PROBE_RESP:
-       case IEEE80211_STYPE_BEACON:
-               ipw_qos_handle_probe_reponse(priv, active_network, network);
-               break;
-       case IEEE80211_STYPE_ASSOC_RESP:
-               ipw_qos_association_resp(priv, network);
-               break;
-       default:
-               break;
-       }
+       return 0;
+}
+
+static int ipw_handle_beacon(struct net_device *dev,
+                            struct ieee80211_beacon *resp,
+                            struct ieee80211_network *network)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       int active_network = ((priv->status & STATUS_ASSOCIATED) &&
+                             (network == priv->assoc_network));
+
+       ipw_qos_handle_probe_response(priv, active_network, network);
 
        return 0;
 }
 
+static int ipw_handle_assoc_response(struct net_device *dev,
+                                    struct ieee80211_assoc_response *resp,
+                                    struct ieee80211_network *network)
+{
+       struct ipw_priv *priv = ieee80211_priv(dev);
+       ipw_qos_association_resp(priv, network);
+       return 0;
+}
+
 static int ipw_send_qos_params_command(struct ipw_priv *priv, struct ieee80211_qos_parameters
                                       *qos_param)
 {
@@ -7627,6 +7308,13 @@ static int ipw_associate(void *data)
                return 0;
        }
 
+       if (priv->status & STATUS_DISASSOCIATING) {
+               IPW_DEBUG_ASSOC("Not attempting association (in "
+                               "disassociating)\n ");
+               queue_work(priv->workqueue, &priv->associate);
+               return 0;
+       }
+
        if (!ipw_is_init(priv) || (priv->status & STATUS_SCANNING)) {
                IPW_DEBUG_ASSOC("Not attempting association (scanning or not "
                                "initialized)\n");
@@ -7713,10 +7401,7 @@ static void ipw_rebuild_decrypted_skb(struct ipw_priv *priv,
                memmove(skb->data + IEEE80211_3ADDR_LEN,
                        skb->data + IEEE80211_3ADDR_LEN + 8,
                        skb->len - IEEE80211_3ADDR_LEN - 8);
-               if (fc & IEEE80211_FCTL_MOREFRAGS)
-                       skb_trim(skb, skb->len - 16);   /* 2*MIC */
-               else
-                       skb_trim(skb, skb->len - 8);    /* MIC */
+               skb_trim(skb, skb->len - 16);   /* CCMP_HDR_LEN + CCMP_MIC_LEN */
                break;
        case SEC_LEVEL_2:
                break;
@@ -7725,10 +7410,7 @@ static void ipw_rebuild_decrypted_skb(struct ipw_priv *priv,
                memmove(skb->data + IEEE80211_3ADDR_LEN,
                        skb->data + IEEE80211_3ADDR_LEN + 4,
                        skb->len - IEEE80211_3ADDR_LEN - 4);
-               if (fc & IEEE80211_FCTL_MOREFRAGS)
-                       skb_trim(skb, skb->len - 8);    /* 2*ICV */
-               else
-                       skb_trim(skb, skb->len - 4);    /* ICV */
+               skb_trim(skb, skb->len - 8);    /* IV + ICV */
                break;
        case SEC_LEVEL_0:
                break;
@@ -7743,6 +7425,7 @@ static void ipw_handle_data_packet(struct ipw_priv *priv,
                                   struct ipw_rx_mem_buffer *rxb,
                                   struct ieee80211_rx_stats *stats)
 {
+       struct ieee80211_hdr_4addr *hdr;
        struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
 
        /* We received data from the HW, so stop the watchdog */
@@ -7772,7 +7455,11 @@ static void ipw_handle_data_packet(struct ipw_priv *priv,
        IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
 
        /* HW decrypt will not clear the WEP bit, MIC, PN, etc. */
-       if (!priv->ieee->host_decrypt)
+       hdr = (struct ieee80211_hdr_4addr *)rxb->skb->data;
+       if (priv->ieee->iw_mode != IW_MODE_MONITOR &&
+           ((is_multicast_ether_addr(hdr->addr1) ||
+             is_broadcast_ether_addr(hdr->addr1)) ?
+            !priv->ieee->host_mc_decrypt : !priv->ieee->host_decrypt))
                ipw_rebuild_decrypted_skb(priv, rxb->skb);
 
        if (!ieee80211_rx(priv->ieee, rxb->skb, stats))
@@ -7783,6 +7470,173 @@ static void ipw_handle_data_packet(struct ipw_priv *priv,
        }
 }
 
+#ifdef CONFIG_IEEE80211_RADIOTAP
+static void ipw_handle_data_packet_monitor(struct ipw_priv *priv,
+                                          struct ipw_rx_mem_buffer *rxb,
+                                          struct ieee80211_rx_stats *stats)
+{
+       struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
+       struct ipw_rx_frame *frame = &pkt->u.frame;
+
+       /* initial pull of some data */
+       u16 received_channel = frame->received_channel;
+       u8 antennaAndPhy = frame->antennaAndPhy;
+       s8 antsignal = frame->rssi_dbm - IPW_RSSI_TO_DBM;       /* call it signed anyhow */
+       u16 pktrate = frame->rate;
+
+       /* 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;
+               u8 rt_flags;    /* radiotap packet flags */
+               u8 rt_rate;     /* rate in 500kb/s */
+               u16 rt_channel; /* channel in mhz */
+               u16 rt_chbitmask;       /* channel bitfield */
+               s8 rt_dbmsignal;        /* signal in dbM, kluged to signed */
+               u8 rt_antenna;  /* antenna number */
+       } *ipw_rt;
+
+       short len = le16_to_cpu(pkt->u.frame.length);
+
+       /* We received data from the HW, so stop the watchdog */
+       priv->net_dev->trans_start = jiffies;
+
+       /* We only process data packets if the
+        * interface is open */
+       if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) >
+                    skb_tailroom(rxb->skb))) {
+               priv->ieee->stats.rx_errors++;
+               priv->wstats.discard.misc++;
+               IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
+               return;
+       } else if (unlikely(!netif_running(priv->net_dev))) {
+               priv->ieee->stats.rx_dropped++;
+               priv->wstats.discard.misc++;
+               IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
+               return;
+       }
+
+       /* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use
+        * that now */
+       if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) {
+               /* FIXME: Should alloc bigger skb instead */
+               priv->ieee->stats.rx_dropped++;
+               priv->wstats.discard.misc++;
+               IPW_DEBUG_DROP("Dropping too large packet in monitor\n");
+               return;
+       }
+
+       /* copy the frame itself */
+       memmove(rxb->skb->data + sizeof(struct ipw_rt_hdr),
+               rxb->skb->data + IPW_RX_FRAME_SIZE, len);
+
+       /* Zero the radiotap static buffer  ...  We only need to zero the bytes NOT
+        * part of our real header, saves a little time.
+        *
+        * No longer necessary since we fill in all our data.  Purge before merging
+        * patch officially.
+        * memset(rxb->skb->data + sizeof(struct ipw_rt_hdr), 0,
+        *        IEEE80211_RADIOTAP_HDRLEN - sizeof(struct ipw_rt_hdr));
+        */
+
+       ipw_rt = (struct ipw_rt_hdr *)rxb->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 header+data */
+
+       /* Big bitfield of all the fields we provide in radiotap */
+       ipw_rt->rt_hdr.it_present =
+           ((1 << IEEE80211_RADIOTAP_FLAGS) |
+            (1 << IEEE80211_RADIOTAP_RATE) |
+            (1 << IEEE80211_RADIOTAP_CHANNEL) |
+            (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
+            (1 << IEEE80211_RADIOTAP_ANTENNA));
+
+       /* Zero the flags, we'll add to them as we go */
+       ipw_rt->rt_flags = 0;
+
+       /* Convert signal to DBM */
+       ipw_rt->rt_dbmsignal = antsignal;
+
+       /* Convert the channel data and set the flags */
+       ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(received_channel));
+       if (received_channel > 14) {    /* 802.11a */
+               ipw_rt->rt_chbitmask =
+                   cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ));
+       } else if (antennaAndPhy & 32) {        /* 802.11b */
+               ipw_rt->rt_chbitmask =
+                   cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ));
+       } else {                /* 802.11g */
+               ipw_rt->rt_chbitmask =
+                   (IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ);
+       }
+
+       /* set the rate in multiples of 500k/s */
+       switch (pktrate) {
+       case IPW_TX_RATE_1MB:
+               ipw_rt->rt_rate = 2;
+               break;
+       case IPW_TX_RATE_2MB:
+               ipw_rt->rt_rate = 4;
+               break;
+       case IPW_TX_RATE_5MB:
+               ipw_rt->rt_rate = 10;
+               break;
+       case IPW_TX_RATE_6MB:
+               ipw_rt->rt_rate = 12;
+               break;
+       case IPW_TX_RATE_9MB:
+               ipw_rt->rt_rate = 18;
+               break;
+       case IPW_TX_RATE_11MB:
+               ipw_rt->rt_rate = 22;
+               break;
+       case IPW_TX_RATE_12MB:
+               ipw_rt->rt_rate = 24;
+               break;
+       case IPW_TX_RATE_18MB:
+               ipw_rt->rt_rate = 36;
+               break;
+       case IPW_TX_RATE_24MB:
+               ipw_rt->rt_rate = 48;
+               break;
+       case IPW_TX_RATE_36MB:
+               ipw_rt->rt_rate = 72;
+               break;
+       case IPW_TX_RATE_48MB:
+               ipw_rt->rt_rate = 96;
+               break;
+       case IPW_TX_RATE_54MB:
+               ipw_rt->rt_rate = 108;
+               break;
+       default:
+               ipw_rt->rt_rate = 0;
+               break;
+       }
+
+       /* antenna number */
+       ipw_rt->rt_antenna = (antennaAndPhy & 3);       /* Is this right? */
+
+       /* set the preamble flag if we have it */
+       if ((antennaAndPhy & 64))
+               ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
+
+       /* Set the size of the skb to the size of the frame */
+       skb_put(rxb->skb, len + sizeof(struct ipw_rt_hdr));
+
+       IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
+
+       if (!ieee80211_rx(priv->ieee, rxb->skb, stats))
+               priv->ieee->stats.rx_errors++;
+       else {                  /* ieee80211_rx succeeded, so it now owns the SKB */
+               rxb->skb = NULL;
+               /* no LED during capture */
+       }
+}
+#endif
+
 static inline int is_network_packet(struct ipw_priv *priv,
                                    struct ieee80211_hdr_4addr *header)
 {
@@ -7794,21 +7648,23 @@ static inline int is_network_packet(struct ipw_priv *priv,
                if (!memcmp(header->addr2, priv->net_dev->dev_addr, ETH_ALEN))
                        return 0;
 
-               /* multicast packets to our IBSS go through */
-               if (is_multicast_ether_addr(header->addr1))
+               /* {broad,multi}cast packets to our BSSID go through */
+               if (is_multicast_ether_addr(header->addr1) ||
+                   is_broadcast_ether_addr(header->addr1))
                        return !memcmp(header->addr3, priv->bssid, ETH_ALEN);
 
                /* packets to our adapter go through */
                return !memcmp(header->addr1, priv->net_dev->dev_addr,
                               ETH_ALEN);
 
-       case IW_MODE_INFRA:     /* Header: Dest. | AP{BSSID} | Source */
+       case IW_MODE_INFRA:     /* Header: Dest. | BSSID | Source */
                /* packets from our adapter are dropped (echo) */
                if (!memcmp(header->addr3, priv->net_dev->dev_addr, ETH_ALEN))
                        return 0;
 
-               /* {broad,multi}cast packets to our IBSS go through */
-               if (is_multicast_ether_addr(header->addr1))
+               /* {broad,multi}cast packets to our BSS go through */
+               if (is_multicast_ether_addr(header->addr1) ||
+                   is_broadcast_ether_addr(header->addr1))
                        return !memcmp(header->addr2, priv->bssid, ETH_ALEN);
 
                /* packets to our adapter go through */
@@ -8012,8 +7868,14 @@ static void ipw_rx(struct ipw_priv *priv)
 
 #ifdef CONFIG_IPW2200_MONITOR
                                if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+#ifdef CONFIG_IEEE80211_RADIOTAP
+                                       ipw_handle_data_packet_monitor(priv,
+                                                                      rxb,
+                                                                      &stats);
+#else
                                        ipw_handle_data_packet(priv, rxb,
                                                               &stats);
+#endif
                                        break;
                                }
 #endif
@@ -8180,7 +8042,11 @@ static int ipw_sw_reset(struct ipw_priv *priv, int init)
 #ifdef CONFIG_IPW2200_MONITOR
        case 2:
                priv->ieee->iw_mode = IW_MODE_MONITOR;
+#ifdef CONFIG_IEEE80211_RADIOTAP
+               priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
+#else
                priv->net_dev->type = ARPHRD_IEEE80211;
+#endif
                break;
 #endif
        default:
@@ -8194,6 +8060,7 @@ static int ipw_sw_reset(struct ipw_priv *priv, int init)
                priv->ieee->host_encrypt = 0;
                priv->ieee->host_encrypt_msdu = 0;
                priv->ieee->host_decrypt = 0;
+               priv->ieee->host_mc_decrypt = 0;
        }
        IPW_DEBUG_INFO("Hardware crypto [%s]\n", hwcrypto ? "on" : "off");
 
@@ -8330,10 +8197,11 @@ static int ipw_wx_set_freq(struct net_device *dev,
                           union iwreq_data *wrqu, char *extra)
 {
        struct ipw_priv *priv = ieee80211_priv(dev);
-       const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
+       const struct ieee80211_geo *geo = ipw_get_geo(priv->ieee);
        struct iw_freq *fwrq = &wrqu->freq;
        int ret = 0, i;
-       u8 channel;
+       u8 channel, flags;
+       int band;
 
        if (fwrq->m == 0) {
                IPW_DEBUG_WX("SET Freq/Channel -> any\n");
@@ -8344,20 +8212,23 @@ static int ipw_wx_set_freq(struct net_device *dev,
        }
        /* if setting by freq convert to channel */
        if (fwrq->e == 1) {
-               channel = ieee80211_freq_to_channel(priv->ieee, fwrq->m);
+               channel = ipw_freq_to_channel(priv->ieee, fwrq->m);
                if (channel == 0)
                        return -EINVAL;
        } else
                channel = fwrq->m;
 
-       if (!ieee80211_is_valid_channel(priv->ieee, channel))
+       if (!(band = ipw_is_valid_channel(priv->ieee, channel)))
                return -EINVAL;
 
-       if (priv->ieee->iw_mode == IW_MODE_ADHOC && priv->ieee->mode & IEEE_A) {
-               i = ieee80211_channel_to_index(priv->ieee, channel);
+       if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+               i = ipw_channel_to_index(priv->ieee, channel);
                if (i == -1)
                        return -EINVAL;
-               if (geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY) {
+
+               flags = (band == IEEE80211_24GHZ_BAND) ?
+                   geo->bg[i].flags : geo->a[i].flags;
+               if (flags & IEEE80211_CH_PASSIVE_ONLY) {
                        IPW_DEBUG_WX("Invalid Ad-Hoc channel for 802.11a\n");
                        return -EINVAL;
                }
@@ -8426,7 +8297,11 @@ static int ipw_wx_set_mode(struct net_device *dev,
                priv->net_dev->type = ARPHRD_ETHER;
 
        if (wrqu->mode == IW_MODE_MONITOR)
+#ifdef CONFIG_IEEE80211_RADIOTAP
+               priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
+#else
                priv->net_dev->type = ARPHRD_IEEE80211;
+#endif
 #endif                         /* CONFIG_IPW2200_MONITOR */
 
        /* Free the existing firmware and reset the fw_loaded
@@ -8475,7 +8350,7 @@ static int ipw_wx_get_range(struct net_device *dev,
 {
        struct ipw_priv *priv = ieee80211_priv(dev);
        struct iw_range *range = (struct iw_range *)extra;
-       const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
+       const struct ieee80211_geo *geo = ipw_get_geo(priv->ieee);
        int i = 0, j;
 
        wrqu->data.length = sizeof(*range);
@@ -8538,6 +8413,13 @@ static int ipw_wx_get_range(struct net_device *dev,
        range->num_frequency = i;
 
        up(&priv->sem);
+
+       /* Event capability (kernel + driver) */
+       range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
+                               IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
+                               IW_EVENT_CAPA_MASK(SIOCGIWAP));
+       range->event_capa[1] = IW_EVENT_CAPA_K_1;
+
        IPW_DEBUG_WX("GET Range\n");
        return 0;
 }
@@ -9038,7 +8920,6 @@ static int ipw_wx_get_retry(struct net_device *dev,
        return 0;
 }
 
-#if WIRELESS_EXT > 17
 static int ipw_request_direct_scan(struct ipw_priv *priv, char *essid,
                                   int essid_len)
 {
@@ -9076,7 +8957,7 @@ static int ipw_request_direct_scan(struct ipw_priv *priv, char *essid,
 
        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(20);
+       scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(120);
        scan.dwell_time[IPW_SCAN_ACTIVE_DIRECT_SCAN] = cpu_to_le16(20);
 
        scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee));
@@ -9102,14 +8983,12 @@ static int ipw_request_direct_scan(struct ipw_priv *priv, char *essid,
        up(&priv->sem);
        return err;
 }
-#endif                         /* WIRELESS_EXT > 17 */
 
 static int ipw_wx_set_scan(struct net_device *dev,
                           struct iw_request_info *info,
                           union iwreq_data *wrqu, char *extra)
 {
        struct ipw_priv *priv = ieee80211_priv(dev);
-#if WIRELESS_EXT > 17
        struct iw_scan_req *req = NULL;
        if (wrqu->data.length
            && wrqu->data.length == sizeof(struct iw_scan_req)) {
@@ -9120,7 +8999,7 @@ static int ipw_wx_set_scan(struct net_device *dev,
                        return 0;
                }
        }
-#endif
+
        IPW_DEBUG_WX("Start scan\n");
 
        queue_work(priv->workqueue, &priv->request_scan);
@@ -9459,7 +9338,11 @@ static int ipw_wx_set_monitor(struct net_device *dev,
        IPW_DEBUG_WX("SET MONITOR: %d %d\n", enable, parms[1]);
        if (enable) {
                if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
+#ifdef CONFIG_IEEE80211_RADIOTAP
+                       priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
+#else
                        priv->net_dev->type = ARPHRD_IEEE80211;
+#endif
                        queue_work(priv->workqueue, &priv->adapter_restart);
                }
 
@@ -9566,7 +9449,6 @@ static iw_handler ipw_wx_handlers[] = {
        IW_IOCTL(SIOCGIWSPY) = iw_handler_get_spy,
        IW_IOCTL(SIOCSIWTHRSPY) = iw_handler_set_thrspy,
        IW_IOCTL(SIOCGIWTHRSPY) = iw_handler_get_thrspy,
-#if WIRELESS_EXT > 17
        IW_IOCTL(SIOCSIWGENIE) = ipw_wx_set_genie,
        IW_IOCTL(SIOCGIWGENIE) = ipw_wx_get_genie,
        IW_IOCTL(SIOCSIWMLME) = ipw_wx_set_mlme,
@@ -9574,7 +9456,6 @@ static iw_handler ipw_wx_handlers[] = {
        IW_IOCTL(SIOCGIWAUTH) = ipw_wx_get_auth,
        IW_IOCTL(SIOCSIWENCODEEXT) = ipw_wx_set_encodeext,
        IW_IOCTL(SIOCGIWENCODEEXT) = ipw_wx_get_encodeext,
-#endif
 };
 
 enum {
@@ -9650,10 +9531,9 @@ static struct iw_handler_def ipw_wx_handler_def = {
        .num_private_args = ARRAY_SIZE(ipw_priv_args),
        .private = ipw_priv_handler,
        .private_args = ipw_priv_args,
+       .get_wireless_stats = ipw_get_wireless_stats,
 };
 
-static struct iw_public_data ipw_wx_data;
-
 /*
  * Get wireless statistics.
  * Called by /proc/net/wireless
@@ -9774,7 +9654,8 @@ static inline int ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb,
        switch (priv->ieee->iw_mode) {
        case IW_MODE_ADHOC:
                hdr_len = IEEE80211_3ADDR_LEN;
-               unicast = !is_multicast_ether_addr(hdr->addr1);
+               unicast = !(is_multicast_ether_addr(hdr->addr1) ||
+                           is_broadcast_ether_addr(hdr->addr1));
                id = ipw_find_station(priv, hdr->addr1);
                if (id == IPW_INVALID_STATION) {
                        id = ipw_add_station(priv, hdr->addr1);
@@ -9789,7 +9670,8 @@ static inline int ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb,
 
        case IW_MODE_INFRA:
        default:
-               unicast = !is_multicast_ether_addr(hdr->addr3);
+               unicast = !(is_multicast_ether_addr(hdr->addr3) ||
+                           is_broadcast_ether_addr(hdr->addr3));
                hdr_len = IEEE80211_3ADDR_LEN;
                id = 0;
                break;
@@ -10617,6 +10499,17 @@ static const struct ieee80211_geo ipw_geos[] = {
               {5210, 42}, {5230, 46}},
         },
 
+       {                       /* Rest of World */
+        "ZZR",
+        .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, IEEE80211_CH_B_ONLY |
+                            IEEE80211_CH_PASSIVE_ONLY}},
+        },
+
        {                       /* High Band */
         "ZZH",
         .bg_channels = 13,
@@ -10704,6 +10597,96 @@ static const struct ieee80211_geo ipw_geos[] = {
         }
 };
 
+/* GEO code borrowed from ieee80211_geo.c */
+static int ipw_is_valid_channel(struct ieee80211_device *ieee, u8 channel)
+{
+       int i;
+
+       /* Driver needs to initialize the geography map before using
+        * these helper functions */
+       BUG_ON(ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0);
+
+       if (ieee->freq_band & IEEE80211_24GHZ_BAND)
+               for (i = 0; i < ieee->geo.bg_channels; i++)
+                       /* NOTE: If G mode is currently supported but
+                        * this is a B only channel, we don't see it
+                        * as valid. */
+                       if ((ieee->geo.bg[i].channel == channel) &&
+                           (!(ieee->mode & IEEE_G) ||
+                            !(ieee->geo.bg[i].flags & IEEE80211_CH_B_ONLY)))
+                               return IEEE80211_24GHZ_BAND;
+
+       if (ieee->freq_band & IEEE80211_52GHZ_BAND)
+               for (i = 0; i < ieee->geo.a_channels; i++)
+                       if (ieee->geo.a[i].channel == channel)
+                               return IEEE80211_52GHZ_BAND;
+
+       return 0;
+}
+
+static int ipw_channel_to_index(struct ieee80211_device *ieee, u8 channel)
+{
+       int i;
+
+       /* Driver needs to initialize the geography map before using
+        * these helper functions */
+       BUG_ON(ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0);
+
+       if (ieee->freq_band & IEEE80211_24GHZ_BAND)
+               for (i = 0; i < ieee->geo.bg_channels; i++)
+                       if (ieee->geo.bg[i].channel == channel)
+                               return i;
+
+       if (ieee->freq_band & IEEE80211_52GHZ_BAND)
+               for (i = 0; i < ieee->geo.a_channels; i++)
+                       if (ieee->geo.a[i].channel == channel)
+                               return i;
+
+       return -1;
+}
+
+static u8 ipw_freq_to_channel(struct ieee80211_device *ieee, u32 freq)
+{
+       int i;
+
+       /* Driver needs to initialize the geography map before using
+        * these helper functions */
+       BUG_ON(ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0);
+
+       freq /= 100000;
+
+       if (ieee->freq_band & IEEE80211_24GHZ_BAND)
+               for (i = 0; i < ieee->geo.bg_channels; i++)
+                       if (ieee->geo.bg[i].freq == freq)
+                               return ieee->geo.bg[i].channel;
+
+       if (ieee->freq_band & IEEE80211_52GHZ_BAND)
+               for (i = 0; i < ieee->geo.a_channels; i++)
+                       if (ieee->geo.a[i].freq == freq)
+                               return ieee->geo.a[i].channel;
+
+       return 0;
+}
+
+static int ipw_set_geo(struct ieee80211_device *ieee,
+                      const struct ieee80211_geo *geo)
+{
+       memcpy(ieee->geo.name, geo->name, 3);
+       ieee->geo.name[3] = '\0';
+       ieee->geo.bg_channels = geo->bg_channels;
+       ieee->geo.a_channels = geo->a_channels;
+       memcpy(ieee->geo.bg, geo->bg, geo->bg_channels *
+              sizeof(struct ieee80211_channel));
+       memcpy(ieee->geo.a, geo->a, ieee->geo.a_channels *
+              sizeof(struct ieee80211_channel));
+       return 0;
+}
+
+static const struct ieee80211_geo *ipw_get_geo(struct ieee80211_device *ieee)
+{
+       return &ieee->geo;
+}
+
 #define MAX_HW_RESTARTS 5
 static int ipw_up(struct ipw_priv *priv)
 {
@@ -10712,12 +10695,24 @@ static int ipw_up(struct ipw_priv *priv)
        if (priv->status & STATUS_EXIT_PENDING)
                return -EIO;
 
+       if (cmdlog && !priv->cmdlog) {
+               priv->cmdlog = kmalloc(sizeof(*priv->cmdlog) * cmdlog,
+                                      GFP_KERNEL);
+               if (priv->cmdlog == NULL) {
+                       IPW_ERROR("Error allocating %d command log entries.\n",
+                                 cmdlog);
+               } else {
+                       memset(priv->cmdlog, 0, sizeof(*priv->cmdlog) * cmdlog);
+                       priv->cmdlog_len = cmdlog;
+               }
+       }
+
        for (i = 0; i < MAX_HW_RESTARTS; i++) {
                /* Load the microcode, firmware, and eeprom.
                 * Also start the clocks. */
                rc = ipw_load(priv);
                if (rc) {
-                       IPW_ERROR("Unable to load firmware: 0x%08X\n", rc);
+                       IPW_ERROR("Unable to load firmware: %d\n", rc);
                        return rc;
                }
 
@@ -10731,9 +10726,14 @@ static int ipw_up(struct ipw_priv *priv)
                                    ipw_geos[j].name, 3))
                                break;
                }
-               if (j == ARRAY_SIZE(ipw_geos))
+               if (j == ARRAY_SIZE(ipw_geos)) {
+                       IPW_WARNING("SKU [%c%c%c] not recognized.\n",
+                                   priv->eeprom[EEPROM_COUNTRY_CODE + 0],
+                                   priv->eeprom[EEPROM_COUNTRY_CODE + 1],
+                                   priv->eeprom[EEPROM_COUNTRY_CODE + 2]);
                        j = 0;
-               if (ieee80211_set_geo(priv->ieee, &ipw_geos[j])) {
+               }
+               if (ipw_set_geo(priv->ieee, &ipw_geos[j])) {
                        IPW_WARNING("Could not set geography.");
                        return 0;
                }
@@ -10859,24 +10859,6 @@ static void ipw_bg_down(void *data)
        up(&priv->sem);
 }
 
-#if WIRELESS_EXT < 18
-static int ipw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
-       struct iwreq *wrq = (struct iwreq *)rq;
-       int ret = -1;
-       switch (cmd) {
-       case IPW_IOCTL_WPA_SUPPLICANT:
-               ret = ipw_wpa_supplicant(dev, &wrq->u.data);
-               return ret;
-
-       default:
-               return -EOPNOTSUPP;
-       }
-
-       return -EOPNOTSUPP;
-}
-#endif
-
 /* Called by register_netdev() */
 static int ipw_net_init(struct net_device *dev)
 {
@@ -10935,6 +10917,7 @@ static struct attribute *ipw_sysfs_entries[] = {
        &dev_attr_cfg.attr,
        &dev_attr_error.attr,
        &dev_attr_event_log.attr,
+       &dev_attr_cmd_log.attr,
        &dev_attr_eeprom_delay.attr,
        &dev_attr_ucode_version.attr,
        &dev_attr_rtc.attr,
@@ -11035,9 +11018,6 @@ static int ipw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        SET_MODULE_OWNER(net_dev);
        SET_NETDEV_DEV(net_dev, &pdev->dev);
 
-       ipw_wx_data.spy_data = &priv->ieee->spy_data;
-       ipw_wx_data.ieee80211 = priv->ieee;
-
        down(&priv->sem);
 
        priv->ieee->hard_start_xmit = ipw_net_hard_start_xmit;
@@ -11045,7 +11025,9 @@ static int ipw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        priv->ieee->is_queue_full = ipw_net_is_queue_full;
 
 #ifdef CONFIG_IPW_QOS
-       priv->ieee->handle_management = ipw_handle_management;
+       priv->ieee->handle_probe_response = ipw_handle_beacon;
+       priv->ieee->handle_beacon = ipw_handle_probe_response;
+       priv->ieee->handle_assoc_response = ipw_handle_assoc_response;
 #endif                         /* CONFIG_IPW_QOS */
 
        priv->ieee->perfect_rssi = -20;
@@ -11054,14 +11036,12 @@ static int ipw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        net_dev->open = ipw_net_open;
        net_dev->stop = ipw_net_stop;
        net_dev->init = ipw_net_init;
-#if WIRELESS_EXT < 18
-       net_dev->do_ioctl = ipw_ioctl;
-#endif
        net_dev->get_stats = ipw_net_get_stats;
        net_dev->set_multicast_list = ipw_net_set_multicast_list;
        net_dev->set_mac_address = ipw_net_set_mac_address;
-       net_dev->get_wireless_stats = ipw_get_wireless_stats;
-       net_dev->wireless_data = &ipw_wx_data;
+       priv->wireless_data.spy_data = &priv->ieee->spy_data;
+       priv->wireless_data.ieee80211 = priv->ieee;
+       net_dev->wireless_data = &priv->wireless_data;
        net_dev->wireless_handlers = &ipw_wx_handler_def;
        net_dev->ethtool_ops = &ipw_ethtool_ops;
        net_dev->irq = pdev->irq;
@@ -11129,6 +11109,10 @@ static void ipw_pci_remove(struct pci_dev *pdev)
        }
        ipw_tx_queue_free(priv);
 
+       if (priv->cmdlog) {
+               kfree(priv->cmdlog);
+               priv->cmdlog = NULL;
+       }
        /* ipw_down will ensure that there is no more pending work
         * in the workqueue's, so we can safely remove them now. */
        cancel_delayed_work(&priv->adhoc_check);
@@ -11302,5 +11286,9 @@ MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS)");
 module_param(hwcrypto, int, 0444);
 MODULE_PARM_DESC(hwcrypto, "enable hardware crypto (default on)");
 
+module_param(cmdlog, int, 0444);
+MODULE_PARM_DESC(cmdlog,
+                "allocate a ring buffer for logging firmware commands");
+
 module_exit(ipw_exit);
 module_init(ipw_init);