[WEXT]: Move to net/wireless
authorJohannes Berg <johannes@sipsolutions.net>
Fri, 27 Apr 2007 03:42:51 +0000 (20:42 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 27 Apr 2007 03:42:51 +0000 (20:42 -0700)
This patch moves dev/core/wireless.c to net/wireless/wext.c.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/core/Makefile
net/core/dev.c
net/core/wireless.c [deleted file]
net/wireless/Makefile
net/wireless/wext.c [new file with mode: 0644]

index 73272d506e931d447d80d0033ca5999d0bb62f53..4751613e1b59a5c6cb75fbfe45648bd1f36a07ed 100644 (file)
@@ -13,7 +13,6 @@ obj-y              += dev.o ethtool.o dev_mcast.o dst.o netevent.o \
 obj-$(CONFIG_XFRM) += flow.o
 obj-$(CONFIG_SYSFS) += net-sysfs.o
 obj-$(CONFIG_NET_PKTGEN) += pktgen.o
-obj-$(CONFIG_WIRELESS_EXT) += wireless.o
 obj-$(CONFIG_NETPOLL) += netpoll.o
 obj-$(CONFIG_NET_DMA) += user_dma.o
 obj-$(CONFIG_FIB_RULES) += fib_rules.o
index d82d00f5451f158d9c28aea69e3fe31a6f4b4637..700e4b5081b6ee6da6fe1f49b53bc57dfd0f8f63 100644 (file)
@@ -2936,7 +2936,7 @@ int dev_ioctl(unsigned int cmd, void __user *arg)
                                }
                                dev_load(ifr.ifr_name);
                                rtnl_lock();
-                               /* Follow me in net/core/wireless.c */
+                               /* Follow me in net/wireless/wext.c */
                                ret = wireless_process_ioctl(&ifr, cmd);
                                rtnl_unlock();
                                if (IW_IS_GET(cmd) &&
diff --git a/net/core/wireless.c b/net/core/wireless.c
deleted file mode 100644 (file)
index fba295e..0000000
+++ /dev/null
@@ -1,1633 +0,0 @@
-/*
- * This file implement the Wireless Extensions APIs.
- *
- * Authors :   Jean Tourrilhes - HPL - <jt@hpl.hp.com>
- * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
- *
- * (As all part of the Linux kernel, this file is GPL)
- */
-
-/************************** DOCUMENTATION **************************/
-/*
- * API definition :
- * --------------
- * See <linux/wireless.h> for details of the APIs and the rest.
- *
- * History :
- * -------
- *
- * v1 - 5.12.01 - Jean II
- *     o Created this file.
- *
- * v2 - 13.12.01 - Jean II
- *     o Move /proc/net/wireless stuff from net/core/dev.c to here
- *     o Make Wireless Extension IOCTLs go through here
- *     o Added iw_handler handling ;-)
- *     o Added standard ioctl description
- *     o Initial dumb commit strategy based on orinoco.c
- *
- * v3 - 19.12.01 - Jean II
- *     o Make sure we don't go out of standard_ioctl[] in ioctl_standard_call
- *     o Add event dispatcher function
- *     o Add event description
- *     o Propagate events as rtnetlink IFLA_WIRELESS option
- *     o Generate event on selected SET requests
- *
- * v4 - 18.04.02 - Jean II
- *     o Fix stupid off by one in iw_ioctl_description : IW_ESSID_MAX_SIZE + 1
- *
- * v5 - 21.06.02 - Jean II
- *     o Add IW_PRIV_TYPE_ADDR in priv_type_size (+cleanup)
- *     o Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes
- *     o Add IWEVCUSTOM for driver specific event/scanning token
- *     o Turn on WE_STRICT_WRITE by default + kernel warning
- *     o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num)
- *     o Fix off-by-one in test (extra_size <= IFNAMSIZ)
- *
- * v6 - 9.01.03 - Jean II
- *     o Add common spy support : iw_handler_set_spy(), wireless_spy_update()
- *     o Add enhanced spy support : iw_handler_set_thrspy() and event.
- *     o Add WIRELESS_EXT version display in /proc/net/wireless
- *
- * v6 - 18.06.04 - Jean II
- *     o Change get_spydata() method for added safety
- *     o Remove spy #ifdef, they are always on -> cleaner code
- *     o Allow any size GET request if user specifies length > max
- *             and if request has IW_DESCR_FLAG_NOMAX flag or is SIOCGIWPRIV
- *     o Start migrating get_wireless_stats to struct iw_handler_def
- *     o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus
- * Based on patch from Pavel Roskin <proski@gnu.org> :
- *     o Fix kernel data leak to user space in private handler handling
- *
- * v7 - 18.3.05 - Jean II
- *     o Remove (struct iw_point *)->pointer from events and streams
- *     o Remove spy_offset from struct iw_handler_def
- *     o Start deprecating dev->get_wireless_stats, output a warning
- *     o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless
- *     o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats)
- *
- * v8 - 17.02.06 - Jean II
- *     o RtNetlink requests support (SET/GET)
- *
- * v8b - 03.08.06 - Herbert Xu
- *     o Fix Wireless Event locking issues.
- *
- * v9 - 14.3.06 - Jean II
- *     o Change length in ESSID and NICK to strlen() instead of strlen()+1
- *     o Make standard_ioctl_num and standard_event_num unsigned
- *     o Remove (struct net_device *)->get_wireless_stats()
- *
- * v10 - 16.3.07 - Jean II
- *     o Prevent leaking of kernel space in stream on 64 bits.
- */
-
-/***************************** INCLUDES *****************************/
-
-#include <linux/module.h>
-#include <linux/types.h>               /* off_t */
-#include <linux/netdevice.h>           /* struct ifreq, dev_get_by_name() */
-#include <linux/proc_fs.h>
-#include <linux/rtnetlink.h>           /* rtnetlink stuff */
-#include <linux/seq_file.h>
-#include <linux/init.h>                        /* for __init */
-#include <linux/if_arp.h>              /* ARPHRD_ETHER */
-#include <linux/etherdevice.h>         /* compare_ether_addr */
-#include <linux/interrupt.h>
-
-#include <linux/wireless.h>            /* Pretty obvious */
-#include <net/iw_handler.h>            /* New driver API */
-#include <net/netlink.h>
-
-#include <asm/uaccess.h>               /* copy_to_user() */
-
-/**************************** CONSTANTS ****************************/
-
-/* Debugging stuff */
-#undef WE_IOCTL_DEBUG          /* Debug IOCTL API */
-#undef WE_EVENT_DEBUG          /* Debug Event dispatcher */
-#undef WE_SPY_DEBUG            /* Debug enhanced spy support */
-
-/* Options */
-#define WE_EVENT_RTNETLINK     /* Propagate events using RtNetlink */
-#define WE_SET_EVENT           /* Generate an event on some set commands */
-
-/************************* GLOBAL VARIABLES *************************/
-/*
- * You should not use global variables, because of re-entrancy.
- * On our case, it's only const, so it's OK...
- */
-/*
- * Meta-data about all the standard Wireless Extension request we
- * know about.
- */
-static const struct iw_ioctl_description standard_ioctl[] = {
-       [SIOCSIWCOMMIT  - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_NULL,
-       },
-       [SIOCGIWNAME    - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_CHAR,
-               .flags          = IW_DESCR_FLAG_DUMP,
-       },
-       [SIOCSIWNWID    - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_PARAM,
-               .flags          = IW_DESCR_FLAG_EVENT,
-       },
-       [SIOCGIWNWID    - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_PARAM,
-               .flags          = IW_DESCR_FLAG_DUMP,
-       },
-       [SIOCSIWFREQ    - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_FREQ,
-               .flags          = IW_DESCR_FLAG_EVENT,
-       },
-       [SIOCGIWFREQ    - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_FREQ,
-               .flags          = IW_DESCR_FLAG_DUMP,
-       },
-       [SIOCSIWMODE    - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_UINT,
-               .flags          = IW_DESCR_FLAG_EVENT,
-       },
-       [SIOCGIWMODE    - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_UINT,
-               .flags          = IW_DESCR_FLAG_DUMP,
-       },
-       [SIOCSIWSENS    - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_PARAM,
-       },
-       [SIOCGIWSENS    - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_PARAM,
-       },
-       [SIOCSIWRANGE   - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_NULL,
-       },
-       [SIOCGIWRANGE   - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .max_tokens     = sizeof(struct iw_range),
-               .flags          = IW_DESCR_FLAG_DUMP,
-       },
-       [SIOCSIWPRIV    - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_NULL,
-       },
-       [SIOCGIWPRIV    - SIOCIWFIRST] = { /* (handled directly by us) */
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = sizeof(struct iw_priv_args),
-               .max_tokens     = 16,
-               .flags          = IW_DESCR_FLAG_NOMAX,
-       },
-       [SIOCSIWSTATS   - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_NULL,
-       },
-       [SIOCGIWSTATS   - SIOCIWFIRST] = { /* (handled directly by us) */
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .max_tokens     = sizeof(struct iw_statistics),
-               .flags          = IW_DESCR_FLAG_DUMP,
-       },
-       [SIOCSIWSPY     - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = sizeof(struct sockaddr),
-               .max_tokens     = IW_MAX_SPY,
-       },
-       [SIOCGIWSPY     - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = sizeof(struct sockaddr) +
-                                 sizeof(struct iw_quality),
-               .max_tokens     = IW_MAX_SPY,
-       },
-       [SIOCSIWTHRSPY  - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = sizeof(struct iw_thrspy),
-               .min_tokens     = 1,
-               .max_tokens     = 1,
-       },
-       [SIOCGIWTHRSPY  - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = sizeof(struct iw_thrspy),
-               .min_tokens     = 1,
-               .max_tokens     = 1,
-       },
-       [SIOCSIWAP      - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_ADDR,
-       },
-       [SIOCGIWAP      - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_ADDR,
-               .flags          = IW_DESCR_FLAG_DUMP,
-       },
-       [SIOCSIWMLME    - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .min_tokens     = sizeof(struct iw_mlme),
-               .max_tokens     = sizeof(struct iw_mlme),
-       },
-       [SIOCGIWAPLIST  - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = sizeof(struct sockaddr) +
-                                 sizeof(struct iw_quality),
-               .max_tokens     = IW_MAX_AP,
-               .flags          = IW_DESCR_FLAG_NOMAX,
-       },
-       [SIOCSIWSCAN    - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .min_tokens     = 0,
-               .max_tokens     = sizeof(struct iw_scan_req),
-       },
-       [SIOCGIWSCAN    - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .max_tokens     = IW_SCAN_MAX_DATA,
-               .flags          = IW_DESCR_FLAG_NOMAX,
-       },
-       [SIOCSIWESSID   - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .max_tokens     = IW_ESSID_MAX_SIZE,
-               .flags          = IW_DESCR_FLAG_EVENT,
-       },
-       [SIOCGIWESSID   - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .max_tokens     = IW_ESSID_MAX_SIZE,
-               .flags          = IW_DESCR_FLAG_DUMP,
-       },
-       [SIOCSIWNICKN   - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .max_tokens     = IW_ESSID_MAX_SIZE,
-       },
-       [SIOCGIWNICKN   - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .max_tokens     = IW_ESSID_MAX_SIZE,
-       },
-       [SIOCSIWRATE    - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_PARAM,
-       },
-       [SIOCGIWRATE    - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_PARAM,
-       },
-       [SIOCSIWRTS     - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_PARAM,
-       },
-       [SIOCGIWRTS     - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_PARAM,
-       },
-       [SIOCSIWFRAG    - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_PARAM,
-       },
-       [SIOCGIWFRAG    - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_PARAM,
-       },
-       [SIOCSIWTXPOW   - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_PARAM,
-       },
-       [SIOCGIWTXPOW   - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_PARAM,
-       },
-       [SIOCSIWRETRY   - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_PARAM,
-       },
-       [SIOCGIWRETRY   - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_PARAM,
-       },
-       [SIOCSIWENCODE  - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .max_tokens     = IW_ENCODING_TOKEN_MAX,
-               .flags          = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
-       },
-       [SIOCGIWENCODE  - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .max_tokens     = IW_ENCODING_TOKEN_MAX,
-               .flags          = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
-       },
-       [SIOCSIWPOWER   - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_PARAM,
-       },
-       [SIOCGIWPOWER   - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_PARAM,
-       },
-       [SIOCSIWGENIE   - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .max_tokens     = IW_GENERIC_IE_MAX,
-       },
-       [SIOCGIWGENIE   - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .max_tokens     = IW_GENERIC_IE_MAX,
-       },
-       [SIOCSIWAUTH    - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_PARAM,
-       },
-       [SIOCGIWAUTH    - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_PARAM,
-       },
-       [SIOCSIWENCODEEXT - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .min_tokens     = sizeof(struct iw_encode_ext),
-               .max_tokens     = sizeof(struct iw_encode_ext) +
-                                 IW_ENCODING_TOKEN_MAX,
-       },
-       [SIOCGIWENCODEEXT - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .min_tokens     = sizeof(struct iw_encode_ext),
-               .max_tokens     = sizeof(struct iw_encode_ext) +
-                                 IW_ENCODING_TOKEN_MAX,
-       },
-       [SIOCSIWPMKSA - SIOCIWFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .min_tokens     = sizeof(struct iw_pmksa),
-               .max_tokens     = sizeof(struct iw_pmksa),
-       },
-};
-static const unsigned standard_ioctl_num = ARRAY_SIZE(standard_ioctl);
-
-/*
- * Meta-data about all the additional standard Wireless Extension events
- * we know about.
- */
-static const struct iw_ioctl_description standard_event[] = {
-       [IWEVTXDROP     - IWEVFIRST] = {
-               .header_type    = IW_HEADER_TYPE_ADDR,
-       },
-       [IWEVQUAL       - IWEVFIRST] = {
-               .header_type    = IW_HEADER_TYPE_QUAL,
-       },
-       [IWEVCUSTOM     - IWEVFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .max_tokens     = IW_CUSTOM_MAX,
-       },
-       [IWEVREGISTERED - IWEVFIRST] = {
-               .header_type    = IW_HEADER_TYPE_ADDR,
-       },
-       [IWEVEXPIRED    - IWEVFIRST] = {
-               .header_type    = IW_HEADER_TYPE_ADDR,
-       },
-       [IWEVGENIE      - IWEVFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .max_tokens     = IW_GENERIC_IE_MAX,
-       },
-       [IWEVMICHAELMICFAILURE  - IWEVFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .max_tokens     = sizeof(struct iw_michaelmicfailure),
-       },
-       [IWEVASSOCREQIE - IWEVFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .max_tokens     = IW_GENERIC_IE_MAX,
-       },
-       [IWEVASSOCRESPIE        - IWEVFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .max_tokens     = IW_GENERIC_IE_MAX,
-       },
-       [IWEVPMKIDCAND  - IWEVFIRST] = {
-               .header_type    = IW_HEADER_TYPE_POINT,
-               .token_size     = 1,
-               .max_tokens     = sizeof(struct iw_pmkid_cand),
-       },
-};
-static const unsigned standard_event_num = ARRAY_SIZE(standard_event);
-
-/* Size (in bytes) of the various private data types */
-static const char iw_priv_type_size[] = {
-       0,                              /* IW_PRIV_TYPE_NONE */
-       1,                              /* IW_PRIV_TYPE_BYTE */
-       1,                              /* IW_PRIV_TYPE_CHAR */
-       0,                              /* Not defined */
-       sizeof(__u32),                  /* IW_PRIV_TYPE_INT */
-       sizeof(struct iw_freq),         /* IW_PRIV_TYPE_FLOAT */
-       sizeof(struct sockaddr),        /* IW_PRIV_TYPE_ADDR */
-       0,                              /* Not defined */
-};
-
-/* Size (in bytes) of various events */
-static const int event_type_size[] = {
-       IW_EV_LCP_LEN,                  /* IW_HEADER_TYPE_NULL */
-       0,
-       IW_EV_CHAR_LEN,                 /* IW_HEADER_TYPE_CHAR */
-       0,
-       IW_EV_UINT_LEN,                 /* IW_HEADER_TYPE_UINT */
-       IW_EV_FREQ_LEN,                 /* IW_HEADER_TYPE_FREQ */
-       IW_EV_ADDR_LEN,                 /* IW_HEADER_TYPE_ADDR */
-       0,
-       IW_EV_POINT_LEN,                /* Without variable payload */
-       IW_EV_PARAM_LEN,                /* IW_HEADER_TYPE_PARAM */
-       IW_EV_QUAL_LEN,                 /* IW_HEADER_TYPE_QUAL */
-};
-
-/* Size (in bytes) of various events, as packed */
-static const int event_type_pk_size[] = {
-       IW_EV_LCP_PK_LEN,               /* IW_HEADER_TYPE_NULL */
-       0,
-       IW_EV_CHAR_PK_LEN,              /* IW_HEADER_TYPE_CHAR */
-       0,
-       IW_EV_UINT_PK_LEN,              /* IW_HEADER_TYPE_UINT */
-       IW_EV_FREQ_PK_LEN,              /* IW_HEADER_TYPE_FREQ */
-       IW_EV_ADDR_PK_LEN,              /* IW_HEADER_TYPE_ADDR */
-       0,
-       IW_EV_POINT_PK_LEN,             /* Without variable payload */
-       IW_EV_PARAM_PK_LEN,             /* IW_HEADER_TYPE_PARAM */
-       IW_EV_QUAL_PK_LEN,              /* IW_HEADER_TYPE_QUAL */
-};
-
-/************************ COMMON SUBROUTINES ************************/
-/*
- * Stuff that may be used in various place or doesn't fit in one
- * of the section below.
- */
-
-/* ---------------------------------------------------------------- */
-/*
- * Return the driver handler associated with a specific Wireless Extension.
- * Called from various place, so make sure it remains efficient.
- */
-static inline iw_handler get_handler(struct net_device *dev,
-                                    unsigned int cmd)
-{
-       /* Don't "optimise" the following variable, it will crash */
-       unsigned int    index;          /* *MUST* be unsigned */
-
-       /* Check if we have some wireless handlers defined */
-       if (dev->wireless_handlers == NULL)
-               return NULL;
-
-       /* Try as a standard command */
-       index = cmd - SIOCIWFIRST;
-       if (index < dev->wireless_handlers->num_standard)
-               return dev->wireless_handlers->standard[index];
-
-       /* Try as a private command */
-       index = cmd - SIOCIWFIRSTPRIV;
-       if (index < dev->wireless_handlers->num_private)
-               return dev->wireless_handlers->private[index];
-
-       /* Not found */
-       return NULL;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Get statistics out of the driver
- */
-static inline struct iw_statistics *get_wireless_stats(struct net_device *dev)
-{
-       /* New location */
-       if ((dev->wireless_handlers != NULL) &&
-          (dev->wireless_handlers->get_wireless_stats != NULL))
-               return dev->wireless_handlers->get_wireless_stats(dev);
-
-       /* Not found */
-       return (struct iw_statistics *) NULL;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Call the commit handler in the driver
- * (if exist and if conditions are right)
- *
- * Note : our current commit strategy is currently pretty dumb,
- * but we will be able to improve on that...
- * The goal is to try to agreagate as many changes as possible
- * before doing the commit. Drivers that will define a commit handler
- * are usually those that need a reset after changing parameters, so
- * we want to minimise the number of reset.
- * A cool idea is to use a timer : at each "set" command, we re-set the
- * timer, when the timer eventually fires, we call the driver.
- * Hopefully, more on that later.
- *
- * Also, I'm waiting to see how many people will complain about the
- * netif_running(dev) test. I'm open on that one...
- * Hopefully, the driver will remember to do a commit in "open()" ;-)
- */
-static inline int call_commit_handler(struct net_device *      dev)
-{
-       if ((netif_running(dev)) &&
-          (dev->wireless_handlers->standard[0] != NULL)) {
-               /* Call the commit handler on the driver */
-               return dev->wireless_handlers->standard[0](dev, NULL,
-                                                          NULL, NULL);
-       } else
-               return 0;               /* Command completed successfully */
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Calculate size of private arguments
- */
-static inline int get_priv_size(__u16  args)
-{
-       int     num = args & IW_PRIV_SIZE_MASK;
-       int     type = (args & IW_PRIV_TYPE_MASK) >> 12;
-
-       return num * iw_priv_type_size[type];
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Re-calculate the size of private arguments
- */
-static inline int adjust_priv_size(__u16               args,
-                                  union iwreq_data *   wrqu)
-{
-       int     num = wrqu->data.length;
-       int     max = args & IW_PRIV_SIZE_MASK;
-       int     type = (args & IW_PRIV_TYPE_MASK) >> 12;
-
-       /* Make sure the driver doesn't goof up */
-       if (max < num)
-               num = max;
-
-       return num * iw_priv_type_size[type];
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Standard Wireless Handler : get wireless stats
- *     Allow programatic access to /proc/net/wireless even if /proc
- *     doesn't exist... Also more efficient...
- */
-static int iw_handler_get_iwstats(struct net_device *          dev,
-                                 struct iw_request_info *      info,
-                                 union iwreq_data *            wrqu,
-                                 char *                        extra)
-{
-       /* Get stats from the driver */
-       struct iw_statistics *stats;
-
-       stats = get_wireless_stats(dev);
-       if (stats != (struct iw_statistics *) NULL) {
-
-               /* Copy statistics to extra */
-               memcpy(extra, stats, sizeof(struct iw_statistics));
-               wrqu->data.length = sizeof(struct iw_statistics);
-
-               /* Check if we need to clear the updated flag */
-               if (wrqu->data.flags != 0)
-                       stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
-               return 0;
-       } else
-               return -EOPNOTSUPP;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Standard Wireless Handler : get iwpriv definitions
- * Export the driver private handler definition
- * They will be picked up by tools like iwpriv...
- */
-static int iw_handler_get_private(struct net_device *          dev,
-                                 struct iw_request_info *      info,
-                                 union iwreq_data *            wrqu,
-                                 char *                        extra)
-{
-       /* Check if the driver has something to export */
-       if ((dev->wireless_handlers->num_private_args == 0) ||
-          (dev->wireless_handlers->private_args == NULL))
-               return -EOPNOTSUPP;
-
-       /* Check if there is enough buffer up there */
-       if (wrqu->data.length < dev->wireless_handlers->num_private_args) {
-               /* User space can't know in advance how large the buffer
-                * needs to be. Give it a hint, so that we can support
-                * any size buffer we want somewhat efficiently... */
-               wrqu->data.length = dev->wireless_handlers->num_private_args;
-               return -E2BIG;
-       }
-
-       /* Set the number of available ioctls. */
-       wrqu->data.length = dev->wireless_handlers->num_private_args;
-
-       /* Copy structure to the user buffer. */
-       memcpy(extra, dev->wireless_handlers->private_args,
-              sizeof(struct iw_priv_args) * wrqu->data.length);
-
-       return 0;
-}
-
-
-/******************** /proc/net/wireless SUPPORT ********************/
-/*
- * The /proc/net/wireless file is a human readable user-space interface
- * exporting various wireless specific statistics from the wireless devices.
- * This is the most popular part of the Wireless Extensions ;-)
- *
- * This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
- * The content of the file is basically the content of "struct iw_statistics".
- */
-
-#ifdef CONFIG_PROC_FS
-
-/* ---------------------------------------------------------------- */
-/*
- * Print one entry (line) of /proc/net/wireless
- */
-static __inline__ void wireless_seq_printf_stats(struct seq_file *seq,
-                                                struct net_device *dev)
-{
-       /* Get stats from the driver */
-       struct iw_statistics *stats = get_wireless_stats(dev);
-
-       if (stats) {
-               seq_printf(seq, "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d "
-                               "%6d %6d   %6d\n",
-                          dev->name, stats->status, stats->qual.qual,
-                          stats->qual.updated & IW_QUAL_QUAL_UPDATED
-                          ? '.' : ' ',
-                          ((__s32) stats->qual.level) -
-                          ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
-                          stats->qual.updated & IW_QUAL_LEVEL_UPDATED
-                          ? '.' : ' ',
-                          ((__s32) stats->qual.noise) -
-                          ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
-                          stats->qual.updated & IW_QUAL_NOISE_UPDATED
-                          ? '.' : ' ',
-                          stats->discard.nwid, stats->discard.code,
-                          stats->discard.fragment, stats->discard.retries,
-                          stats->discard.misc, stats->miss.beacon);
-               stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
-       }
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Print info for /proc/net/wireless (print all entries)
- */
-static int wireless_seq_show(struct seq_file *seq, void *v)
-{
-       if (v == SEQ_START_TOKEN)
-               seq_printf(seq, "Inter-| sta-|   Quality        |   Discarded "
-                               "packets               | Missed | WE\n"
-                               " face | tus | link level noise |  nwid  "
-                               "crypt   frag  retry   misc | beacon | %d\n",
-                          WIRELESS_EXT);
-       else
-               wireless_seq_printf_stats(seq, v);
-       return 0;
-}
-
-static const struct seq_operations wireless_seq_ops = {
-       .start = dev_seq_start,
-       .next  = dev_seq_next,
-       .stop  = dev_seq_stop,
-       .show  = wireless_seq_show,
-};
-
-static int wireless_seq_open(struct inode *inode, struct file *file)
-{
-       return seq_open(file, &wireless_seq_ops);
-}
-
-static const struct file_operations wireless_seq_fops = {
-       .owner   = THIS_MODULE,
-       .open    = wireless_seq_open,
-       .read    = seq_read,
-       .llseek  = seq_lseek,
-       .release = seq_release,
-};
-
-int __init wireless_proc_init(void)
-{
-       /* Create /proc/net/wireless entry */
-       if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops))
-               return -ENOMEM;
-
-       return 0;
-}
-#endif /* CONFIG_PROC_FS */
-
-/************************** IOCTL SUPPORT **************************/
-/*
- * The original user space API to configure all those Wireless Extensions
- * is through IOCTLs.
- * In there, we check if we need to call the new driver API (iw_handler)
- * or just call the driver ioctl handler.
- */
-
-/* ---------------------------------------------------------------- */
-/*
- * Wrapper to call a standard Wireless Extension handler.
- * We do various checks and also take care of moving data between
- * user space and kernel space.
- */
-static int ioctl_standard_call(struct net_device *     dev,
-                              struct ifreq *           ifr,
-                              unsigned int             cmd,
-                              iw_handler               handler)
-{
-       struct iwreq *                          iwr = (struct iwreq *) ifr;
-       const struct iw_ioctl_description *     descr;
-       struct iw_request_info                  info;
-       int                                     ret = -EINVAL;
-
-       /* Get the description of the IOCTL */
-       if ((cmd - SIOCIWFIRST) >= standard_ioctl_num)
-               return -EOPNOTSUPP;
-       descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
-
-#ifdef WE_IOCTL_DEBUG
-       printk(KERN_DEBUG "%s (WE) : Found standard handler for 0x%04X\n",
-              ifr->ifr_name, cmd);
-       printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
-#endif /* WE_IOCTL_DEBUG */
-
-       /* Prepare the call */
-       info.cmd = cmd;
-       info.flags = 0;
-
-       /* Check if we have a pointer to user space data or not */
-       if (descr->header_type != IW_HEADER_TYPE_POINT) {
-
-               /* No extra arguments. Trivial to handle */
-               ret = handler(dev, &info, &(iwr->u), NULL);
-
-#ifdef WE_SET_EVENT
-               /* Generate an event to notify listeners of the change */
-               if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
-                  ((ret == 0) || (ret == -EIWCOMMIT)))
-                       wireless_send_event(dev, cmd, &(iwr->u), NULL);
-#endif /* WE_SET_EVENT */
-       } else {
-               char *  extra;
-               int     extra_size;
-               int     user_length = 0;
-               int     err;
-               int     essid_compat = 0;
-
-               /* Calculate space needed by arguments. Always allocate
-                * for max space. Easier, and won't last long... */
-               extra_size = descr->max_tokens * descr->token_size;
-
-               /* Check need for ESSID compatibility for WE < 21 */
-               switch (cmd) {
-               case SIOCSIWESSID:
-               case SIOCGIWESSID:
-               case SIOCSIWNICKN:
-               case SIOCGIWNICKN:
-                       if (iwr->u.data.length == descr->max_tokens + 1)
-                               essid_compat = 1;
-                       else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
-                               char essid[IW_ESSID_MAX_SIZE + 1];
-
-                               err = copy_from_user(essid, iwr->u.data.pointer,
-                                                    iwr->u.data.length *
-                                                    descr->token_size);
-                               if (err)
-                                       return -EFAULT;
-
-                               if (essid[iwr->u.data.length - 1] == '\0')
-                                       essid_compat = 1;
-                       }
-                       break;
-               default:
-                       break;
-               }
-
-               iwr->u.data.length -= essid_compat;
-
-               /* Check what user space is giving us */
-               if (IW_IS_SET(cmd)) {
-                       /* Check NULL pointer */
-                       if ((iwr->u.data.pointer == NULL) &&
-                          (iwr->u.data.length != 0))
-                               return -EFAULT;
-                       /* Check if number of token fits within bounds */
-                       if (iwr->u.data.length > descr->max_tokens)
-                               return -E2BIG;
-                       if (iwr->u.data.length < descr->min_tokens)
-                               return -EINVAL;
-               } else {
-                       /* Check NULL pointer */
-                       if (iwr->u.data.pointer == NULL)
-                               return -EFAULT;
-                       /* Save user space buffer size for checking */
-                       user_length = iwr->u.data.length;
-
-                       /* Don't check if user_length > max to allow forward
-                        * compatibility. The test user_length < min is
-                        * implied by the test at the end. */
-
-                       /* Support for very large requests */
-                       if ((descr->flags & IW_DESCR_FLAG_NOMAX) &&
-                          (user_length > descr->max_tokens)) {
-                               /* Allow userspace to GET more than max so
-                                * we can support any size GET requests.
-                                * There is still a limit : -ENOMEM. */
-                               extra_size = user_length * descr->token_size;
-                               /* Note : user_length is originally a __u16,
-                                * and token_size is controlled by us,
-                                * so extra_size won't get negative and
-                                * won't overflow... */
-                       }
-               }
-
-#ifdef WE_IOCTL_DEBUG
-               printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
-                      dev->name, extra_size);
-#endif /* WE_IOCTL_DEBUG */
-
-               /* Create the kernel buffer */
-               /*    kzalloc ensures NULL-termination for essid_compat */
-               extra = kzalloc(extra_size, GFP_KERNEL);
-               if (extra == NULL) {
-                       return -ENOMEM;
-               }
-
-               /* If it is a SET, get all the extra data in here */
-               if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
-                       err = copy_from_user(extra, iwr->u.data.pointer,
-                                            iwr->u.data.length *
-                                            descr->token_size);
-                       if (err) {
-                               kfree(extra);
-                               return -EFAULT;
-                       }
-#ifdef WE_IOCTL_DEBUG
-                       printk(KERN_DEBUG "%s (WE) : Got %d bytes\n",
-                              dev->name,
-                              iwr->u.data.length * descr->token_size);
-#endif /* WE_IOCTL_DEBUG */
-               }
-
-               /* Call the handler */
-               ret = handler(dev, &info, &(iwr->u), extra);
-
-               iwr->u.data.length += essid_compat;
-
-               /* If we have something to return to the user */
-               if (!ret && IW_IS_GET(cmd)) {
-                       /* Check if there is enough buffer up there */
-                       if (user_length < iwr->u.data.length) {
-                               kfree(extra);
-                               return -E2BIG;
-                       }
-
-                       err = copy_to_user(iwr->u.data.pointer, extra,
-                                          iwr->u.data.length *
-                                          descr->token_size);
-                       if (err)
-                               ret =  -EFAULT;
-#ifdef WE_IOCTL_DEBUG
-                       printk(KERN_DEBUG "%s (WE) : Wrote %d bytes\n",
-                              dev->name,
-                              iwr->u.data.length * descr->token_size);
-#endif /* WE_IOCTL_DEBUG */
-               }
-
-#ifdef WE_SET_EVENT
-               /* Generate an event to notify listeners of the change */
-               if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
-                  ((ret == 0) || (ret == -EIWCOMMIT))) {
-                       if (descr->flags & IW_DESCR_FLAG_RESTRICT)
-                               /* If the event is restricted, don't
-                                * export the payload */
-                               wireless_send_event(dev, cmd, &(iwr->u), NULL);
-                       else
-                               wireless_send_event(dev, cmd, &(iwr->u),
-                                                   extra);
-               }
-#endif /* WE_SET_EVENT */
-
-               /* Cleanup - I told you it wasn't that long ;-) */
-               kfree(extra);
-       }
-
-       /* Call commit handler if needed and defined */
-       if (ret == -EIWCOMMIT)
-               ret = call_commit_handler(dev);
-
-       /* Here, we will generate the appropriate event if needed */
-
-       return ret;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Wrapper to call a private Wireless Extension handler.
- * We do various checks and also take care of moving data between
- * user space and kernel space.
- * It's not as nice and slimline as the standard wrapper. The cause
- * is struct iw_priv_args, which was not really designed for the
- * job we are going here.
- *
- * IMPORTANT : This function prevent to set and get data on the same
- * IOCTL and enforce the SET/GET convention. Not doing it would be
- * far too hairy...
- * If you need to set and get data at the same time, please don't use
- * a iw_handler but process it in your ioctl handler (i.e. use the
- * old driver API).
- */
-static inline int ioctl_private_call(struct net_device *       dev,
-                                    struct ifreq *             ifr,
-                                    unsigned int               cmd,
-                                    iw_handler         handler)
-{
-       struct iwreq *                  iwr = (struct iwreq *) ifr;
-       const struct iw_priv_args *     descr = NULL;
-       struct iw_request_info          info;
-       int                             extra_size = 0;
-       int                             i;
-       int                             ret = -EINVAL;
-
-       /* Get the description of the IOCTL */
-       for (i = 0; i < dev->wireless_handlers->num_private_args; i++)
-               if (cmd == dev->wireless_handlers->private_args[i].cmd) {
-                       descr = &(dev->wireless_handlers->private_args[i]);
-                       break;
-               }
-
-#ifdef WE_IOCTL_DEBUG
-       printk(KERN_DEBUG "%s (WE) : Found private handler for 0x%04X\n",
-              ifr->ifr_name, cmd);
-       if (descr) {
-               printk(KERN_DEBUG "%s (WE) : Name %s, set %X, get %X\n",
-                      dev->name, descr->name,
-                      descr->set_args, descr->get_args);
-       }
-#endif /* WE_IOCTL_DEBUG */
-
-       /* Compute the size of the set/get arguments */
-       if (descr != NULL) {
-               if (IW_IS_SET(cmd)) {
-                       int     offset = 0;     /* For sub-ioctls */
-                       /* Check for sub-ioctl handler */
-                       if (descr->name[0] == '\0')
-                               /* Reserve one int for sub-ioctl index */
-                               offset = sizeof(__u32);
-
-                       /* Size of set arguments */
-                       extra_size = get_priv_size(descr->set_args);
-
-                       /* Does it fits in iwr ? */
-                       if ((descr->set_args & IW_PRIV_SIZE_FIXED) &&
-                          ((extra_size + offset) <= IFNAMSIZ))
-                               extra_size = 0;
-               } else {
-                       /* Size of get arguments */
-                       extra_size = get_priv_size(descr->get_args);
-
-                       /* Does it fits in iwr ? */
-                       if ((descr->get_args & IW_PRIV_SIZE_FIXED) &&
-                          (extra_size <= IFNAMSIZ))
-                               extra_size = 0;
-               }
-       }
-
-       /* Prepare the call */
-       info.cmd = cmd;
-       info.flags = 0;
-
-       /* Check if we have a pointer to user space data or not. */
-       if (extra_size == 0) {
-               /* No extra arguments. Trivial to handle */
-               ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));
-       } else {
-               char *  extra;
-               int     err;
-
-               /* Check what user space is giving us */
-               if (IW_IS_SET(cmd)) {
-                       /* Check NULL pointer */
-                       if ((iwr->u.data.pointer == NULL) &&
-                          (iwr->u.data.length != 0))
-                               return -EFAULT;
-
-                       /* Does it fits within bounds ? */
-                       if (iwr->u.data.length > (descr->set_args &
-                                                IW_PRIV_SIZE_MASK))
-                               return -E2BIG;
-               } else {
-                       /* Check NULL pointer */
-                       if (iwr->u.data.pointer == NULL)
-                               return -EFAULT;
-               }
-
-#ifdef WE_IOCTL_DEBUG
-               printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
-                      dev->name, extra_size);
-#endif /* WE_IOCTL_DEBUG */
-
-               /* Always allocate for max space. Easier, and won't last
-                * long... */
-               extra = kmalloc(extra_size, GFP_KERNEL);
-               if (extra == NULL) {
-                       return -ENOMEM;
-               }
-
-               /* If it is a SET, get all the extra data in here */
-               if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
-                       err = copy_from_user(extra, iwr->u.data.pointer,
-                                            extra_size);
-                       if (err) {
-                               kfree(extra);
-                               return -EFAULT;
-                       }
-#ifdef WE_IOCTL_DEBUG
-                       printk(KERN_DEBUG "%s (WE) : Got %d elem\n",
-                              dev->name, iwr->u.data.length);
-#endif /* WE_IOCTL_DEBUG */
-               }
-
-               /* Call the handler */
-               ret = handler(dev, &info, &(iwr->u), extra);
-
-               /* If we have something to return to the user */
-               if (!ret && IW_IS_GET(cmd)) {
-
-                       /* Adjust for the actual length if it's variable,
-                        * avoid leaking kernel bits outside. */
-                       if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) {
-                               extra_size = adjust_priv_size(descr->get_args,
-                                                             &(iwr->u));
-                       }
-
-                       err = copy_to_user(iwr->u.data.pointer, extra,
-                                          extra_size);
-                       if (err)
-                               ret =  -EFAULT;
-#ifdef WE_IOCTL_DEBUG
-                       printk(KERN_DEBUG "%s (WE) : Wrote %d elem\n",
-                              dev->name, iwr->u.data.length);
-#endif /* WE_IOCTL_DEBUG */
-               }
-
-               /* Cleanup - I told you it wasn't that long ;-) */
-               kfree(extra);
-       }
-
-
-       /* Call commit handler if needed and defined */
-       if (ret == -EIWCOMMIT)
-               ret = call_commit_handler(dev);
-
-       return ret;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Main IOCTl dispatcher. Called from the main networking code
- * (dev_ioctl() in net/core/dev.c).
- * Check the type of IOCTL and call the appropriate wrapper...
- */
-int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
-{
-       struct net_device *dev;
-       iw_handler      handler;
-
-       /* Permissions are already checked in dev_ioctl() before calling us.
-        * The copy_to/from_user() of ifr is also dealt with in there */
-
-       /* Make sure the device exist */
-       if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL)
-               return -ENODEV;
-
-       /* A bunch of special cases, then the generic case...
-        * Note that 'cmd' is already filtered in dev_ioctl() with
-        * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
-       switch (cmd) {
-       case SIOCGIWSTATS:
-               /* Get Wireless Stats */
-               return ioctl_standard_call(dev,
-                                          ifr,
-                                          cmd,
-                                          &iw_handler_get_iwstats);
-
-       case SIOCGIWPRIV:
-               /* Check if we have some wireless handlers defined */
-               if (dev->wireless_handlers != NULL) {
-                       /* We export to user space the definition of
-                        * the private handler ourselves */
-                       return ioctl_standard_call(dev,
-                                                  ifr,
-                                                  cmd,
-                                                  &iw_handler_get_private);
-               }
-               // ## Fall-through for old API ##
-       default:
-               /* Generic IOCTL */
-               /* Basic check */
-               if (!netif_device_present(dev))
-                       return -ENODEV;
-               /* New driver API : try to find the handler */
-               handler = get_handler(dev, cmd);
-               if (handler != NULL) {
-                       /* Standard and private are not the same */
-                       if (cmd < SIOCIWFIRSTPRIV)
-                               return ioctl_standard_call(dev,
-                                                          ifr,
-                                                          cmd,
-                                                          handler);
-                       else
-                               return ioctl_private_call(dev,
-                                                         ifr,
-                                                         cmd,
-                                                         handler);
-               }
-               /* Old driver API : call driver ioctl handler */
-               if (dev->do_ioctl) {
-                       return dev->do_ioctl(dev, ifr, cmd);
-               }
-               return -EOPNOTSUPP;
-       }
-       /* Not reached */
-       return -EINVAL;
-}
-
-
-/************************* EVENT PROCESSING *************************/
-/*
- * Process events generated by the wireless layer or the driver.
- * Most often, the event will be propagated through rtnetlink
- */
-
-#ifdef WE_EVENT_RTNETLINK
-/* ---------------------------------------------------------------- */
-/*
- * Locking...
- * ----------
- *
- * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing
- * the locking issue in here and implementing this code !
- *
- * The issue : wireless_send_event() is often called in interrupt context,
- * while the Netlink layer can never be called in interrupt context.
- * The fully formed RtNetlink events are queued, and then a tasklet is run
- * to feed those to Netlink.
- * The skb_queue is interrupt safe, and its lock is not held while calling
- * Netlink, so there is no possibility of dealock.
- * Jean II
- */
-
-static struct sk_buff_head wireless_nlevent_queue;
-
-static int __init wireless_nlevent_init(void)
-{
-       skb_queue_head_init(&wireless_nlevent_queue);
-       return 0;
-}
-
-subsys_initcall(wireless_nlevent_init);
-
-static void wireless_nlevent_process(unsigned long data)
-{
-       struct sk_buff *skb;
-
-       while ((skb = skb_dequeue(&wireless_nlevent_queue)))
-               rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
-}
-
-static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);
-
-/* ---------------------------------------------------------------- */
-/*
- * Fill a rtnetlink message with our event data.
- * Note that we propage only the specified event and don't dump the
- * current wireless config. Dumping the wireless config is far too
- * expensive (for each parameter, the driver need to query the hardware).
- */
-static inline int rtnetlink_fill_iwinfo(struct sk_buff *       skb,
-                                       struct net_device *     dev,
-                                       int                     type,
-                                       char *                  event,
-                                       int                     event_len)
-{
-       struct ifinfomsg *r;
-       struct nlmsghdr  *nlh;
-       unsigned char    *b = skb_tail_pointer(skb);
-
-       nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r));
-       r = NLMSG_DATA(nlh);
-       r->ifi_family = AF_UNSPEC;
-       r->__ifi_pad = 0;
-       r->ifi_type = dev->type;
-       r->ifi_index = dev->ifindex;
-       r->ifi_flags = dev_get_flags(dev);
-       r->ifi_change = 0;      /* Wireless changes don't affect those flags */
-
-       /* Add the wireless events in the netlink packet */
-       RTA_PUT(skb, IFLA_WIRELESS, event_len, event);
-
-       nlh->nlmsg_len = skb_tail_pointer(skb) - b;
-       return skb->len;
-
-nlmsg_failure:
-rtattr_failure:
-       nlmsg_trim(skb, b);
-       return -1;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Create and broadcast and send it on the standard rtnetlink socket
- * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c
- * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field
- * within a RTM_NEWLINK event.
- */
-static inline void rtmsg_iwinfo(struct net_device *    dev,
-                               char *                  event,
-                               int                     event_len)
-{
-       struct sk_buff *skb;
-       int size = NLMSG_GOODSIZE;
-
-       skb = alloc_skb(size, GFP_ATOMIC);
-       if (!skb)
-               return;
-
-       if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK,
-                                 event, event_len) < 0) {
-               kfree_skb(skb);
-               return;
-       }
-       NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
-       skb_queue_tail(&wireless_nlevent_queue, skb);
-       tasklet_schedule(&wireless_nlevent_tasklet);
-}
-
-#endif /* WE_EVENT_RTNETLINK */
-
-/* ---------------------------------------------------------------- */
-/*
- * Main event dispatcher. Called from other parts and drivers.
- * Send the event on the appropriate channels.
- * May be called from interrupt context.
- */
-void wireless_send_event(struct net_device *   dev,
-                        unsigned int           cmd,
-                        union iwreq_data *     wrqu,
-                        char *                 extra)
-{
-       const struct iw_ioctl_description *     descr = NULL;
-       int extra_len = 0;
-       struct iw_event  *event;                /* Mallocated whole event */
-       int event_len;                          /* Its size */
-       int hdr_len;                            /* Size of the event header */
-       int wrqu_off = 0;                       /* Offset in wrqu */
-       /* Don't "optimise" the following variable, it will crash */
-       unsigned        cmd_index;              /* *MUST* be unsigned */
-
-       /* Get the description of the Event */
-       if (cmd <= SIOCIWLAST) {
-               cmd_index = cmd - SIOCIWFIRST;
-               if (cmd_index < standard_ioctl_num)
-                       descr = &(standard_ioctl[cmd_index]);
-       } else {
-               cmd_index = cmd - IWEVFIRST;
-               if (cmd_index < standard_event_num)
-                       descr = &(standard_event[cmd_index]);
-       }
-       /* Don't accept unknown events */
-       if (descr == NULL) {
-               /* Note : we don't return an error to the driver, because
-                * the driver would not know what to do about it. It can't
-                * return an error to the user, because the event is not
-                * initiated by a user request.
-                * The best the driver could do is to log an error message.
-                * We will do it ourselves instead...
-                */
-               printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n",
-                      dev->name, cmd);
-               return;
-       }
-#ifdef WE_EVENT_DEBUG
-       printk(KERN_DEBUG "%s (WE) : Got event 0x%04X\n",
-              dev->name, cmd);
-       printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
-#endif /* WE_EVENT_DEBUG */
-
-       /* Check extra parameters and set extra_len */
-       if (descr->header_type == IW_HEADER_TYPE_POINT) {
-               /* Check if number of token fits within bounds */
-               if (wrqu->data.length > descr->max_tokens) {
-                       printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length);
-                       return;
-               }
-               if (wrqu->data.length < descr->min_tokens) {
-                       printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length);
-                       return;
-               }
-               /* Calculate extra_len - extra is NULL for restricted events */
-               if (extra != NULL)
-                       extra_len = wrqu->data.length * descr->token_size;
-               /* Always at an offset in wrqu */
-               wrqu_off = IW_EV_POINT_OFF;
-#ifdef WE_EVENT_DEBUG
-               printk(KERN_DEBUG "%s (WE) : Event 0x%04X, tokens %d, extra_len %d\n", dev->name, cmd, wrqu->data.length, extra_len);
-#endif /* WE_EVENT_DEBUG */
-       }
-
-       /* Total length of the event */
-       hdr_len = event_type_size[descr->header_type];
-       event_len = hdr_len + extra_len;
-
-#ifdef WE_EVENT_DEBUG
-       printk(KERN_DEBUG "%s (WE) : Event 0x%04X, hdr_len %d, wrqu_off %d, event_len %d\n", dev->name, cmd, hdr_len, wrqu_off, event_len);
-#endif /* WE_EVENT_DEBUG */
-
-       /* Create temporary buffer to hold the event */
-       event = kmalloc(event_len, GFP_ATOMIC);
-       if (event == NULL)
-               return;
-
-       /* Fill event */
-       event->len = event_len;
-       event->cmd = cmd;
-       memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
-       if (extra != NULL)
-               memcpy(((char *) event) + hdr_len, extra, extra_len);
-
-#ifdef WE_EVENT_RTNETLINK
-       /* Send via the RtNetlink event channel */
-       rtmsg_iwinfo(dev, (char *) event, event_len);
-#endif /* WE_EVENT_RTNETLINK */
-
-       /* Cleanup */
-       kfree(event);
-
-       return;         /* Always success, I guess ;-) */
-}
-
-/********************** ENHANCED IWSPY SUPPORT **********************/
-/*
- * In the old days, the driver was handling spy support all by itself.
- * Now, the driver can delegate this task to Wireless Extensions.
- * It needs to use those standard spy iw_handler in struct iw_handler_def,
- * push data to us via wireless_spy_update() and include struct iw_spy_data
- * in its private part (and export it in net_device->wireless_data->spy_data).
- * One of the main advantage of centralising spy support here is that
- * it becomes much easier to improve and extend it without having to touch
- * the drivers. One example is the addition of the Spy-Threshold events.
- */
-
-/* ---------------------------------------------------------------- */
-/*
- * Return the pointer to the spy data in the driver.
- * Because this is called on the Rx path via wireless_spy_update(),
- * we want it to be efficient...
- */
-static inline struct iw_spy_data * get_spydata(struct net_device *dev)
-{
-       /* This is the new way */
-       if (dev->wireless_data)
-               return(dev->wireless_data->spy_data);
-       return NULL;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Standard Wireless Handler : set Spy List
- */
-int iw_handler_set_spy(struct net_device *     dev,
-                      struct iw_request_info * info,
-                      union iwreq_data *       wrqu,
-                      char *                   extra)
-{
-       struct iw_spy_data *    spydata = get_spydata(dev);
-       struct sockaddr *       address = (struct sockaddr *) extra;
-
-       /* Make sure driver is not buggy or using the old API */
-       if (!spydata)
-               return -EOPNOTSUPP;
-
-       /* Disable spy collection while we copy the addresses.
-        * While we copy addresses, any call to wireless_spy_update()
-        * will NOP. This is OK, as anyway the addresses are changing. */
-       spydata->spy_number = 0;
-
-       /* We want to operate without locking, because wireless_spy_update()
-        * most likely will happen in the interrupt handler, and therefore
-        * have its own locking constraints and needs performance.
-        * The rtnl_lock() make sure we don't race with the other iw_handlers.
-        * This make sure wireless_spy_update() "see" that the spy list
-        * is temporarily disabled. */
-       smp_wmb();
-
-       /* Are there are addresses to copy? */
-       if (wrqu->data.length > 0) {
-               int i;
-
-               /* Copy addresses */
-               for (i = 0; i < wrqu->data.length; i++)
-                       memcpy(spydata->spy_address[i], address[i].sa_data,
-                              ETH_ALEN);
-               /* Reset stats */
-               memset(spydata->spy_stat, 0,
-                      sizeof(struct iw_quality) * IW_MAX_SPY);
-
-#ifdef WE_SPY_DEBUG
-               printk(KERN_DEBUG "iw_handler_set_spy() :  wireless_data %p, spydata %p, num %d\n", dev->wireless_data, spydata, wrqu->data.length);
-               for (i = 0; i < wrqu->data.length; i++)
-                       printk(KERN_DEBUG
-                              "%02X:%02X:%02X:%02X:%02X:%02X \n",
-                              spydata->spy_address[i][0],
-                              spydata->spy_address[i][1],
-                              spydata->spy_address[i][2],
-                              spydata->spy_address[i][3],
-                              spydata->spy_address[i][4],
-                              spydata->spy_address[i][5]);
-#endif /* WE_SPY_DEBUG */
-       }
-
-       /* Make sure above is updated before re-enabling */
-       smp_wmb();
-
-       /* Enable addresses */
-       spydata->spy_number = wrqu->data.length;
-
-       return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Standard Wireless Handler : get Spy List
- */
-int iw_handler_get_spy(struct net_device *     dev,
-                      struct iw_request_info * info,
-                      union iwreq_data *       wrqu,
-                      char *                   extra)
-{
-       struct iw_spy_data *    spydata = get_spydata(dev);
-       struct sockaddr *       address = (struct sockaddr *) extra;
-       int                     i;
-
-       /* Make sure driver is not buggy or using the old API */
-       if (!spydata)
-               return -EOPNOTSUPP;
-
-       wrqu->data.length = spydata->spy_number;
-
-       /* Copy addresses. */
-       for (i = 0; i < spydata->spy_number; i++)       {
-               memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
-               address[i].sa_family = AF_UNIX;
-       }
-       /* Copy stats to the user buffer (just after). */
-       if (spydata->spy_number > 0)
-               memcpy(extra  + (sizeof(struct sockaddr) *spydata->spy_number),
-                      spydata->spy_stat,
-                      sizeof(struct iw_quality) * spydata->spy_number);
-       /* Reset updated flags. */
-       for (i = 0; i < spydata->spy_number; i++)
-               spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
-       return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Standard Wireless Handler : set spy threshold
- */
-int iw_handler_set_thrspy(struct net_device *  dev,
-                         struct iw_request_info *info,
-                         union iwreq_data *    wrqu,
-                         char *                extra)
-{
-       struct iw_spy_data *    spydata = get_spydata(dev);
-       struct iw_thrspy *      threshold = (struct iw_thrspy *) extra;
-
-       /* Make sure driver is not buggy or using the old API */
-       if (!spydata)
-               return -EOPNOTSUPP;
-
-       /* Just do it */
-       memcpy(&(spydata->spy_thr_low), &(threshold->low),
-              2 * sizeof(struct iw_quality));
-
-       /* Clear flag */
-       memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
-
-#ifdef WE_SPY_DEBUG
-       printk(KERN_DEBUG "iw_handler_set_thrspy() :  low %d ; high %d\n", spydata->spy_thr_low.level, spydata->spy_thr_high.level);
-#endif /* WE_SPY_DEBUG */
-
-       return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Standard Wireless Handler : get spy threshold
- */
-int iw_handler_get_thrspy(struct net_device *  dev,
-                         struct iw_request_info *info,
-                         union iwreq_data *    wrqu,
-                         char *                extra)
-{
-       struct iw_spy_data *    spydata = get_spydata(dev);
-       struct iw_thrspy *      threshold = (struct iw_thrspy *) extra;
-
-       /* Make sure driver is not buggy or using the old API */
-       if (!spydata)
-               return -EOPNOTSUPP;
-
-       /* Just do it */
-       memcpy(&(threshold->low), &(spydata->spy_thr_low),
-              2 * sizeof(struct iw_quality));
-
-       return 0;
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Prepare and send a Spy Threshold event
- */
-static void iw_send_thrspy_event(struct net_device *   dev,
-                                struct iw_spy_data *   spydata,
-                                unsigned char *        address,
-                                struct iw_quality *    wstats)
-{
-       union iwreq_data        wrqu;
-       struct iw_thrspy        threshold;
-
-       /* Init */
-       wrqu.data.length = 1;
-       wrqu.data.flags = 0;
-       /* Copy address */
-       memcpy(threshold.addr.sa_data, address, ETH_ALEN);
-       threshold.addr.sa_family = ARPHRD_ETHER;
-       /* Copy stats */
-       memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
-       /* Copy also thresholds */
-       memcpy(&(threshold.low), &(spydata->spy_thr_low),
-              2 * sizeof(struct iw_quality));
-
-#ifdef WE_SPY_DEBUG
-       printk(KERN_DEBUG "iw_send_thrspy_event() : address %02X:%02X:%02X:%02X:%02X:%02X, level %d, up = %d\n",
-              threshold.addr.sa_data[0],
-              threshold.addr.sa_data[1],
-              threshold.addr.sa_data[2],
-              threshold.addr.sa_data[3],
-              threshold.addr.sa_data[4],
-              threshold.addr.sa_data[5], threshold.qual.level);
-#endif /* WE_SPY_DEBUG */
-
-       /* Send event to user space */
-       wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Call for the driver to update the spy data.
- * For now, the spy data is a simple array. As the size of the array is
- * small, this is good enough. If we wanted to support larger number of
- * spy addresses, we should use something more efficient...
- */
-void wireless_spy_update(struct net_device *   dev,
-                        unsigned char *        address,
-                        struct iw_quality *    wstats)
-{
-       struct iw_spy_data *    spydata = get_spydata(dev);
-       int                     i;
-       int                     match = -1;
-
-       /* Make sure driver is not buggy or using the old API */
-       if (!spydata)
-               return;
-
-#ifdef WE_SPY_DEBUG
-       printk(KERN_DEBUG "wireless_spy_update() :  wireless_data %p, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_data, spydata, address[0], address[1], address[2], address[3], address[4], address[5]);
-#endif /* WE_SPY_DEBUG */
-
-       /* Update all records that match */
-       for (i = 0; i < spydata->spy_number; i++)
-               if (!compare_ether_addr(address, spydata->spy_address[i])) {
-                       memcpy(&(spydata->spy_stat[i]), wstats,
-                              sizeof(struct iw_quality));
-                       match = i;
-               }
-
-       /* Generate an event if we cross the spy threshold.
-        * To avoid event storms, we have a simple hysteresis : we generate
-        * event only when we go under the low threshold or above the
-        * high threshold. */
-       if (match >= 0) {
-               if (spydata->spy_thr_under[match]) {
-                       if (wstats->level > spydata->spy_thr_high.level) {
-                               spydata->spy_thr_under[match] = 0;
-                               iw_send_thrspy_event(dev, spydata,
-                                                    address, wstats);
-                       }
-               } else {
-                       if (wstats->level < spydata->spy_thr_low.level) {
-                               spydata->spy_thr_under[match] = 1;
-                               iw_send_thrspy_event(dev, spydata,
-                                                    address, wstats);
-                       }
-               }
-       }
-}
-
-EXPORT_SYMBOL(iw_handler_get_spy);
-EXPORT_SYMBOL(iw_handler_get_thrspy);
-EXPORT_SYMBOL(iw_handler_set_spy);
-EXPORT_SYMBOL(iw_handler_set_thrspy);
-EXPORT_SYMBOL(wireless_send_event);
-EXPORT_SYMBOL(wireless_spy_update);
index 3f082ffae387d25e5d4ae6f8c9983f804734b77c..3a96ae60271cf1d79641d6ef155b472cf7889e96 100644 (file)
@@ -1,3 +1,4 @@
+obj-$(CONFIG_WIRELESS_EXT) += wext.o
 obj-$(CONFIG_CFG80211) += cfg80211.o
 
 cfg80211-y += core.o sysfs.o
diff --git a/net/wireless/wext.c b/net/wireless/wext.c
new file mode 100644 (file)
index 0000000..fba295e
--- /dev/null
@@ -0,0 +1,1633 @@
+/*
+ * This file implement the Wireless Extensions APIs.
+ *
+ * Authors :   Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
+ *
+ * (As all part of the Linux kernel, this file is GPL)
+ */
+
+/************************** DOCUMENTATION **************************/
+/*
+ * API definition :
+ * --------------
+ * See <linux/wireless.h> for details of the APIs and the rest.
+ *
+ * History :
+ * -------
+ *
+ * v1 - 5.12.01 - Jean II
+ *     o Created this file.
+ *
+ * v2 - 13.12.01 - Jean II
+ *     o Move /proc/net/wireless stuff from net/core/dev.c to here
+ *     o Make Wireless Extension IOCTLs go through here
+ *     o Added iw_handler handling ;-)
+ *     o Added standard ioctl description
+ *     o Initial dumb commit strategy based on orinoco.c
+ *
+ * v3 - 19.12.01 - Jean II
+ *     o Make sure we don't go out of standard_ioctl[] in ioctl_standard_call
+ *     o Add event dispatcher function
+ *     o Add event description
+ *     o Propagate events as rtnetlink IFLA_WIRELESS option
+ *     o Generate event on selected SET requests
+ *
+ * v4 - 18.04.02 - Jean II
+ *     o Fix stupid off by one in iw_ioctl_description : IW_ESSID_MAX_SIZE + 1
+ *
+ * v5 - 21.06.02 - Jean II
+ *     o Add IW_PRIV_TYPE_ADDR in priv_type_size (+cleanup)
+ *     o Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes
+ *     o Add IWEVCUSTOM for driver specific event/scanning token
+ *     o Turn on WE_STRICT_WRITE by default + kernel warning
+ *     o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num)
+ *     o Fix off-by-one in test (extra_size <= IFNAMSIZ)
+ *
+ * v6 - 9.01.03 - Jean II
+ *     o Add common spy support : iw_handler_set_spy(), wireless_spy_update()
+ *     o Add enhanced spy support : iw_handler_set_thrspy() and event.
+ *     o Add WIRELESS_EXT version display in /proc/net/wireless
+ *
+ * v6 - 18.06.04 - Jean II
+ *     o Change get_spydata() method for added safety
+ *     o Remove spy #ifdef, they are always on -> cleaner code
+ *     o Allow any size GET request if user specifies length > max
+ *             and if request has IW_DESCR_FLAG_NOMAX flag or is SIOCGIWPRIV
+ *     o Start migrating get_wireless_stats to struct iw_handler_def
+ *     o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus
+ * Based on patch from Pavel Roskin <proski@gnu.org> :
+ *     o Fix kernel data leak to user space in private handler handling
+ *
+ * v7 - 18.3.05 - Jean II
+ *     o Remove (struct iw_point *)->pointer from events and streams
+ *     o Remove spy_offset from struct iw_handler_def
+ *     o Start deprecating dev->get_wireless_stats, output a warning
+ *     o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless
+ *     o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats)
+ *
+ * v8 - 17.02.06 - Jean II
+ *     o RtNetlink requests support (SET/GET)
+ *
+ * v8b - 03.08.06 - Herbert Xu
+ *     o Fix Wireless Event locking issues.
+ *
+ * v9 - 14.3.06 - Jean II
+ *     o Change length in ESSID and NICK to strlen() instead of strlen()+1
+ *     o Make standard_ioctl_num and standard_event_num unsigned
+ *     o Remove (struct net_device *)->get_wireless_stats()
+ *
+ * v10 - 16.3.07 - Jean II
+ *     o Prevent leaking of kernel space in stream on 64 bits.
+ */
+
+/***************************** INCLUDES *****************************/
+
+#include <linux/module.h>
+#include <linux/types.h>               /* off_t */
+#include <linux/netdevice.h>           /* struct ifreq, dev_get_by_name() */
+#include <linux/proc_fs.h>
+#include <linux/rtnetlink.h>           /* rtnetlink stuff */
+#include <linux/seq_file.h>
+#include <linux/init.h>                        /* for __init */
+#include <linux/if_arp.h>              /* ARPHRD_ETHER */
+#include <linux/etherdevice.h>         /* compare_ether_addr */
+#include <linux/interrupt.h>
+
+#include <linux/wireless.h>            /* Pretty obvious */
+#include <net/iw_handler.h>            /* New driver API */
+#include <net/netlink.h>
+
+#include <asm/uaccess.h>               /* copy_to_user() */
+
+/**************************** CONSTANTS ****************************/
+
+/* Debugging stuff */
+#undef WE_IOCTL_DEBUG          /* Debug IOCTL API */
+#undef WE_EVENT_DEBUG          /* Debug Event dispatcher */
+#undef WE_SPY_DEBUG            /* Debug enhanced spy support */
+
+/* Options */
+#define WE_EVENT_RTNETLINK     /* Propagate events using RtNetlink */
+#define WE_SET_EVENT           /* Generate an event on some set commands */
+
+/************************* GLOBAL VARIABLES *************************/
+/*
+ * You should not use global variables, because of re-entrancy.
+ * On our case, it's only const, so it's OK...
+ */
+/*
+ * Meta-data about all the standard Wireless Extension request we
+ * know about.
+ */
+static const struct iw_ioctl_description standard_ioctl[] = {
+       [SIOCSIWCOMMIT  - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_NULL,
+       },
+       [SIOCGIWNAME    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_CHAR,
+               .flags          = IW_DESCR_FLAG_DUMP,
+       },
+       [SIOCSIWNWID    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+               .flags          = IW_DESCR_FLAG_EVENT,
+       },
+       [SIOCGIWNWID    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+               .flags          = IW_DESCR_FLAG_DUMP,
+       },
+       [SIOCSIWFREQ    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_FREQ,
+               .flags          = IW_DESCR_FLAG_EVENT,
+       },
+       [SIOCGIWFREQ    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_FREQ,
+               .flags          = IW_DESCR_FLAG_DUMP,
+       },
+       [SIOCSIWMODE    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_UINT,
+               .flags          = IW_DESCR_FLAG_EVENT,
+       },
+       [SIOCGIWMODE    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_UINT,
+               .flags          = IW_DESCR_FLAG_DUMP,
+       },
+       [SIOCSIWSENS    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCGIWSENS    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCSIWRANGE   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_NULL,
+       },
+       [SIOCGIWRANGE   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = sizeof(struct iw_range),
+               .flags          = IW_DESCR_FLAG_DUMP,
+       },
+       [SIOCSIWPRIV    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_NULL,
+       },
+       [SIOCGIWPRIV    - SIOCIWFIRST] = { /* (handled directly by us) */
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = sizeof(struct iw_priv_args),
+               .max_tokens     = 16,
+               .flags          = IW_DESCR_FLAG_NOMAX,
+       },
+       [SIOCSIWSTATS   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_NULL,
+       },
+       [SIOCGIWSTATS   - SIOCIWFIRST] = { /* (handled directly by us) */
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = sizeof(struct iw_statistics),
+               .flags          = IW_DESCR_FLAG_DUMP,
+       },
+       [SIOCSIWSPY     - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = sizeof(struct sockaddr),
+               .max_tokens     = IW_MAX_SPY,
+       },
+       [SIOCGIWSPY     - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = sizeof(struct sockaddr) +
+                                 sizeof(struct iw_quality),
+               .max_tokens     = IW_MAX_SPY,
+       },
+       [SIOCSIWTHRSPY  - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = sizeof(struct iw_thrspy),
+               .min_tokens     = 1,
+               .max_tokens     = 1,
+       },
+       [SIOCGIWTHRSPY  - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = sizeof(struct iw_thrspy),
+               .min_tokens     = 1,
+               .max_tokens     = 1,
+       },
+       [SIOCSIWAP      - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_ADDR,
+       },
+       [SIOCGIWAP      - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_ADDR,
+               .flags          = IW_DESCR_FLAG_DUMP,
+       },
+       [SIOCSIWMLME    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .min_tokens     = sizeof(struct iw_mlme),
+               .max_tokens     = sizeof(struct iw_mlme),
+       },
+       [SIOCGIWAPLIST  - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = sizeof(struct sockaddr) +
+                                 sizeof(struct iw_quality),
+               .max_tokens     = IW_MAX_AP,
+               .flags          = IW_DESCR_FLAG_NOMAX,
+       },
+       [SIOCSIWSCAN    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .min_tokens     = 0,
+               .max_tokens     = sizeof(struct iw_scan_req),
+       },
+       [SIOCGIWSCAN    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_SCAN_MAX_DATA,
+               .flags          = IW_DESCR_FLAG_NOMAX,
+       },
+       [SIOCSIWESSID   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_ESSID_MAX_SIZE,
+               .flags          = IW_DESCR_FLAG_EVENT,
+       },
+       [SIOCGIWESSID   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_ESSID_MAX_SIZE,
+               .flags          = IW_DESCR_FLAG_DUMP,
+       },
+       [SIOCSIWNICKN   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_ESSID_MAX_SIZE,
+       },
+       [SIOCGIWNICKN   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_ESSID_MAX_SIZE,
+       },
+       [SIOCSIWRATE    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCGIWRATE    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCSIWRTS     - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCGIWRTS     - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCSIWFRAG    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCGIWFRAG    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCSIWTXPOW   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCGIWTXPOW   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCSIWRETRY   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCGIWRETRY   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCSIWENCODE  - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_ENCODING_TOKEN_MAX,
+               .flags          = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
+       },
+       [SIOCGIWENCODE  - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_ENCODING_TOKEN_MAX,
+               .flags          = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
+       },
+       [SIOCSIWPOWER   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCGIWPOWER   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCSIWGENIE   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_GENERIC_IE_MAX,
+       },
+       [SIOCGIWGENIE   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_GENERIC_IE_MAX,
+       },
+       [SIOCSIWAUTH    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCGIWAUTH    - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCSIWENCODEEXT - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .min_tokens     = sizeof(struct iw_encode_ext),
+               .max_tokens     = sizeof(struct iw_encode_ext) +
+                                 IW_ENCODING_TOKEN_MAX,
+       },
+       [SIOCGIWENCODEEXT - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .min_tokens     = sizeof(struct iw_encode_ext),
+               .max_tokens     = sizeof(struct iw_encode_ext) +
+                                 IW_ENCODING_TOKEN_MAX,
+       },
+       [SIOCSIWPMKSA - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .min_tokens     = sizeof(struct iw_pmksa),
+               .max_tokens     = sizeof(struct iw_pmksa),
+       },
+};
+static const unsigned standard_ioctl_num = ARRAY_SIZE(standard_ioctl);
+
+/*
+ * Meta-data about all the additional standard Wireless Extension events
+ * we know about.
+ */
+static const struct iw_ioctl_description standard_event[] = {
+       [IWEVTXDROP     - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_ADDR,
+       },
+       [IWEVQUAL       - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_QUAL,
+       },
+       [IWEVCUSTOM     - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_CUSTOM_MAX,
+       },
+       [IWEVREGISTERED - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_ADDR,
+       },
+       [IWEVEXPIRED    - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_ADDR,
+       },
+       [IWEVGENIE      - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_GENERIC_IE_MAX,
+       },
+       [IWEVMICHAELMICFAILURE  - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = sizeof(struct iw_michaelmicfailure),
+       },
+       [IWEVASSOCREQIE - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_GENERIC_IE_MAX,
+       },
+       [IWEVASSOCRESPIE        - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = IW_GENERIC_IE_MAX,
+       },
+       [IWEVPMKIDCAND  - IWEVFIRST] = {
+               .header_type    = IW_HEADER_TYPE_POINT,
+               .token_size     = 1,
+               .max_tokens     = sizeof(struct iw_pmkid_cand),
+       },
+};
+static const unsigned standard_event_num = ARRAY_SIZE(standard_event);
+
+/* Size (in bytes) of the various private data types */
+static const char iw_priv_type_size[] = {
+       0,                              /* IW_PRIV_TYPE_NONE */
+       1,                              /* IW_PRIV_TYPE_BYTE */
+       1,                              /* IW_PRIV_TYPE_CHAR */
+       0,                              /* Not defined */
+       sizeof(__u32),                  /* IW_PRIV_TYPE_INT */
+       sizeof(struct iw_freq),         /* IW_PRIV_TYPE_FLOAT */
+       sizeof(struct sockaddr),        /* IW_PRIV_TYPE_ADDR */
+       0,                              /* Not defined */
+};
+
+/* Size (in bytes) of various events */
+static const int event_type_size[] = {
+       IW_EV_LCP_LEN,                  /* IW_HEADER_TYPE_NULL */
+       0,
+       IW_EV_CHAR_LEN,                 /* IW_HEADER_TYPE_CHAR */
+       0,
+       IW_EV_UINT_LEN,                 /* IW_HEADER_TYPE_UINT */
+       IW_EV_FREQ_LEN,                 /* IW_HEADER_TYPE_FREQ */
+       IW_EV_ADDR_LEN,                 /* IW_HEADER_TYPE_ADDR */
+       0,
+       IW_EV_POINT_LEN,                /* Without variable payload */
+       IW_EV_PARAM_LEN,                /* IW_HEADER_TYPE_PARAM */
+       IW_EV_QUAL_LEN,                 /* IW_HEADER_TYPE_QUAL */
+};
+
+/* Size (in bytes) of various events, as packed */
+static const int event_type_pk_size[] = {
+       IW_EV_LCP_PK_LEN,               /* IW_HEADER_TYPE_NULL */
+       0,
+       IW_EV_CHAR_PK_LEN,              /* IW_HEADER_TYPE_CHAR */
+       0,
+       IW_EV_UINT_PK_LEN,              /* IW_HEADER_TYPE_UINT */
+       IW_EV_FREQ_PK_LEN,              /* IW_HEADER_TYPE_FREQ */
+       IW_EV_ADDR_PK_LEN,              /* IW_HEADER_TYPE_ADDR */
+       0,
+       IW_EV_POINT_PK_LEN,             /* Without variable payload */
+       IW_EV_PARAM_PK_LEN,             /* IW_HEADER_TYPE_PARAM */
+       IW_EV_QUAL_PK_LEN,              /* IW_HEADER_TYPE_QUAL */
+};
+
+/************************ COMMON SUBROUTINES ************************/
+/*
+ * Stuff that may be used in various place or doesn't fit in one
+ * of the section below.
+ */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Return the driver handler associated with a specific Wireless Extension.
+ * Called from various place, so make sure it remains efficient.
+ */
+static inline iw_handler get_handler(struct net_device *dev,
+                                    unsigned int cmd)
+{
+       /* Don't "optimise" the following variable, it will crash */
+       unsigned int    index;          /* *MUST* be unsigned */
+
+       /* Check if we have some wireless handlers defined */
+       if (dev->wireless_handlers == NULL)
+               return NULL;
+
+       /* Try as a standard command */
+       index = cmd - SIOCIWFIRST;
+       if (index < dev->wireless_handlers->num_standard)
+               return dev->wireless_handlers->standard[index];
+
+       /* Try as a private command */
+       index = cmd - SIOCIWFIRSTPRIV;
+       if (index < dev->wireless_handlers->num_private)
+               return dev->wireless_handlers->private[index];
+
+       /* Not found */
+       return NULL;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Get statistics out of the driver
+ */
+static inline struct iw_statistics *get_wireless_stats(struct net_device *dev)
+{
+       /* New location */
+       if ((dev->wireless_handlers != NULL) &&
+          (dev->wireless_handlers->get_wireless_stats != NULL))
+               return dev->wireless_handlers->get_wireless_stats(dev);
+
+       /* Not found */
+       return (struct iw_statistics *) NULL;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Call the commit handler in the driver
+ * (if exist and if conditions are right)
+ *
+ * Note : our current commit strategy is currently pretty dumb,
+ * but we will be able to improve on that...
+ * The goal is to try to agreagate as many changes as possible
+ * before doing the commit. Drivers that will define a commit handler
+ * are usually those that need a reset after changing parameters, so
+ * we want to minimise the number of reset.
+ * A cool idea is to use a timer : at each "set" command, we re-set the
+ * timer, when the timer eventually fires, we call the driver.
+ * Hopefully, more on that later.
+ *
+ * Also, I'm waiting to see how many people will complain about the
+ * netif_running(dev) test. I'm open on that one...
+ * Hopefully, the driver will remember to do a commit in "open()" ;-)
+ */
+static inline int call_commit_handler(struct net_device *      dev)
+{
+       if ((netif_running(dev)) &&
+          (dev->wireless_handlers->standard[0] != NULL)) {
+               /* Call the commit handler on the driver */
+               return dev->wireless_handlers->standard[0](dev, NULL,
+                                                          NULL, NULL);
+       } else
+               return 0;               /* Command completed successfully */
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Calculate size of private arguments
+ */
+static inline int get_priv_size(__u16  args)
+{
+       int     num = args & IW_PRIV_SIZE_MASK;
+       int     type = (args & IW_PRIV_TYPE_MASK) >> 12;
+
+       return num * iw_priv_type_size[type];
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Re-calculate the size of private arguments
+ */
+static inline int adjust_priv_size(__u16               args,
+                                  union iwreq_data *   wrqu)
+{
+       int     num = wrqu->data.length;
+       int     max = args & IW_PRIV_SIZE_MASK;
+       int     type = (args & IW_PRIV_TYPE_MASK) >> 12;
+
+       /* Make sure the driver doesn't goof up */
+       if (max < num)
+               num = max;
+
+       return num * iw_priv_type_size[type];
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Standard Wireless Handler : get wireless stats
+ *     Allow programatic access to /proc/net/wireless even if /proc
+ *     doesn't exist... Also more efficient...
+ */
+static int iw_handler_get_iwstats(struct net_device *          dev,
+                                 struct iw_request_info *      info,
+                                 union iwreq_data *            wrqu,
+                                 char *                        extra)
+{
+       /* Get stats from the driver */
+       struct iw_statistics *stats;
+
+       stats = get_wireless_stats(dev);
+       if (stats != (struct iw_statistics *) NULL) {
+
+               /* Copy statistics to extra */
+               memcpy(extra, stats, sizeof(struct iw_statistics));
+               wrqu->data.length = sizeof(struct iw_statistics);
+
+               /* Check if we need to clear the updated flag */
+               if (wrqu->data.flags != 0)
+                       stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
+               return 0;
+       } else
+               return -EOPNOTSUPP;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Standard Wireless Handler : get iwpriv definitions
+ * Export the driver private handler definition
+ * They will be picked up by tools like iwpriv...
+ */
+static int iw_handler_get_private(struct net_device *          dev,
+                                 struct iw_request_info *      info,
+                                 union iwreq_data *            wrqu,
+                                 char *                        extra)
+{
+       /* Check if the driver has something to export */
+       if ((dev->wireless_handlers->num_private_args == 0) ||
+          (dev->wireless_handlers->private_args == NULL))
+               return -EOPNOTSUPP;
+
+       /* Check if there is enough buffer up there */
+       if (wrqu->data.length < dev->wireless_handlers->num_private_args) {
+               /* User space can't know in advance how large the buffer
+                * needs to be. Give it a hint, so that we can support
+                * any size buffer we want somewhat efficiently... */
+               wrqu->data.length = dev->wireless_handlers->num_private_args;
+               return -E2BIG;
+       }
+
+       /* Set the number of available ioctls. */
+       wrqu->data.length = dev->wireless_handlers->num_private_args;
+
+       /* Copy structure to the user buffer. */
+       memcpy(extra, dev->wireless_handlers->private_args,
+              sizeof(struct iw_priv_args) * wrqu->data.length);
+
+       return 0;
+}
+
+
+/******************** /proc/net/wireless SUPPORT ********************/
+/*
+ * The /proc/net/wireless file is a human readable user-space interface
+ * exporting various wireless specific statistics from the wireless devices.
+ * This is the most popular part of the Wireless Extensions ;-)
+ *
+ * This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
+ * The content of the file is basically the content of "struct iw_statistics".
+ */
+
+#ifdef CONFIG_PROC_FS
+
+/* ---------------------------------------------------------------- */
+/*
+ * Print one entry (line) of /proc/net/wireless
+ */
+static __inline__ void wireless_seq_printf_stats(struct seq_file *seq,
+                                                struct net_device *dev)
+{
+       /* Get stats from the driver */
+       struct iw_statistics *stats = get_wireless_stats(dev);
+
+       if (stats) {
+               seq_printf(seq, "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d "
+                               "%6d %6d   %6d\n",
+                          dev->name, stats->status, stats->qual.qual,
+                          stats->qual.updated & IW_QUAL_QUAL_UPDATED
+                          ? '.' : ' ',
+                          ((__s32) stats->qual.level) -
+                          ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
+                          stats->qual.updated & IW_QUAL_LEVEL_UPDATED
+                          ? '.' : ' ',
+                          ((__s32) stats->qual.noise) -
+                          ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
+                          stats->qual.updated & IW_QUAL_NOISE_UPDATED
+                          ? '.' : ' ',
+                          stats->discard.nwid, stats->discard.code,
+                          stats->discard.fragment, stats->discard.retries,
+                          stats->discard.misc, stats->miss.beacon);
+               stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
+       }
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Print info for /proc/net/wireless (print all entries)
+ */
+static int wireless_seq_show(struct seq_file *seq, void *v)
+{
+       if (v == SEQ_START_TOKEN)
+               seq_printf(seq, "Inter-| sta-|   Quality        |   Discarded "
+                               "packets               | Missed | WE\n"
+                               " face | tus | link level noise |  nwid  "
+                               "crypt   frag  retry   misc | beacon | %d\n",
+                          WIRELESS_EXT);
+       else
+               wireless_seq_printf_stats(seq, v);
+       return 0;
+}
+
+static const struct seq_operations wireless_seq_ops = {
+       .start = dev_seq_start,
+       .next  = dev_seq_next,
+       .stop  = dev_seq_stop,
+       .show  = wireless_seq_show,
+};
+
+static int wireless_seq_open(struct inode *inode, struct file *file)
+{
+       return seq_open(file, &wireless_seq_ops);
+}
+
+static const struct file_operations wireless_seq_fops = {
+       .owner   = THIS_MODULE,
+       .open    = wireless_seq_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = seq_release,
+};
+
+int __init wireless_proc_init(void)
+{
+       /* Create /proc/net/wireless entry */
+       if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops))
+               return -ENOMEM;
+
+       return 0;
+}
+#endif /* CONFIG_PROC_FS */
+
+/************************** IOCTL SUPPORT **************************/
+/*
+ * The original user space API to configure all those Wireless Extensions
+ * is through IOCTLs.
+ * In there, we check if we need to call the new driver API (iw_handler)
+ * or just call the driver ioctl handler.
+ */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a standard Wireless Extension handler.
+ * We do various checks and also take care of moving data between
+ * user space and kernel space.
+ */
+static int ioctl_standard_call(struct net_device *     dev,
+                              struct ifreq *           ifr,
+                              unsigned int             cmd,
+                              iw_handler               handler)
+{
+       struct iwreq *                          iwr = (struct iwreq *) ifr;
+       const struct iw_ioctl_description *     descr;
+       struct iw_request_info                  info;
+       int                                     ret = -EINVAL;
+
+       /* Get the description of the IOCTL */
+       if ((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+               return -EOPNOTSUPP;
+       descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
+
+#ifdef WE_IOCTL_DEBUG
+       printk(KERN_DEBUG "%s (WE) : Found standard handler for 0x%04X\n",
+              ifr->ifr_name, cmd);
+       printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
+#endif /* WE_IOCTL_DEBUG */
+
+       /* Prepare the call */
+       info.cmd = cmd;
+       info.flags = 0;
+
+       /* Check if we have a pointer to user space data or not */
+       if (descr->header_type != IW_HEADER_TYPE_POINT) {
+
+               /* No extra arguments. Trivial to handle */
+               ret = handler(dev, &info, &(iwr->u), NULL);
+
+#ifdef WE_SET_EVENT
+               /* Generate an event to notify listeners of the change */
+               if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
+                  ((ret == 0) || (ret == -EIWCOMMIT)))
+                       wireless_send_event(dev, cmd, &(iwr->u), NULL);
+#endif /* WE_SET_EVENT */
+       } else {
+               char *  extra;
+               int     extra_size;
+               int     user_length = 0;
+               int     err;
+               int     essid_compat = 0;
+
+               /* Calculate space needed by arguments. Always allocate
+                * for max space. Easier, and won't last long... */
+               extra_size = descr->max_tokens * descr->token_size;
+
+               /* Check need for ESSID compatibility for WE < 21 */
+               switch (cmd) {
+               case SIOCSIWESSID:
+               case SIOCGIWESSID:
+               case SIOCSIWNICKN:
+               case SIOCGIWNICKN:
+                       if (iwr->u.data.length == descr->max_tokens + 1)
+                               essid_compat = 1;
+                       else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
+                               char essid[IW_ESSID_MAX_SIZE + 1];
+
+                               err = copy_from_user(essid, iwr->u.data.pointer,
+                                                    iwr->u.data.length *
+                                                    descr->token_size);
+                               if (err)
+                                       return -EFAULT;
+
+                               if (essid[iwr->u.data.length - 1] == '\0')
+                                       essid_compat = 1;
+                       }
+                       break;
+               default:
+                       break;
+               }
+
+               iwr->u.data.length -= essid_compat;
+
+               /* Check what user space is giving us */
+               if (IW_IS_SET(cmd)) {
+                       /* Check NULL pointer */
+                       if ((iwr->u.data.pointer == NULL) &&
+                          (iwr->u.data.length != 0))
+                               return -EFAULT;
+                       /* Check if number of token fits within bounds */
+                       if (iwr->u.data.length > descr->max_tokens)
+                               return -E2BIG;
+                       if (iwr->u.data.length < descr->min_tokens)
+                               return -EINVAL;
+               } else {
+                       /* Check NULL pointer */
+                       if (iwr->u.data.pointer == NULL)
+                               return -EFAULT;
+                       /* Save user space buffer size for checking */
+                       user_length = iwr->u.data.length;
+
+                       /* Don't check if user_length > max to allow forward
+                        * compatibility. The test user_length < min is
+                        * implied by the test at the end. */
+
+                       /* Support for very large requests */
+                       if ((descr->flags & IW_DESCR_FLAG_NOMAX) &&
+                          (user_length > descr->max_tokens)) {
+                               /* Allow userspace to GET more than max so
+                                * we can support any size GET requests.
+                                * There is still a limit : -ENOMEM. */
+                               extra_size = user_length * descr->token_size;
+                               /* Note : user_length is originally a __u16,
+                                * and token_size is controlled by us,
+                                * so extra_size won't get negative and
+                                * won't overflow... */
+                       }
+               }
+
+#ifdef WE_IOCTL_DEBUG
+               printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
+                      dev->name, extra_size);
+#endif /* WE_IOCTL_DEBUG */
+
+               /* Create the kernel buffer */
+               /*    kzalloc ensures NULL-termination for essid_compat */
+               extra = kzalloc(extra_size, GFP_KERNEL);
+               if (extra == NULL) {
+                       return -ENOMEM;
+               }
+
+               /* If it is a SET, get all the extra data in here */
+               if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
+                       err = copy_from_user(extra, iwr->u.data.pointer,
+                                            iwr->u.data.length *
+                                            descr->token_size);
+                       if (err) {
+                               kfree(extra);
+                               return -EFAULT;
+                       }
+#ifdef WE_IOCTL_DEBUG
+                       printk(KERN_DEBUG "%s (WE) : Got %d bytes\n",
+                              dev->name,
+                              iwr->u.data.length * descr->token_size);
+#endif /* WE_IOCTL_DEBUG */
+               }
+
+               /* Call the handler */
+               ret = handler(dev, &info, &(iwr->u), extra);
+
+               iwr->u.data.length += essid_compat;
+
+               /* If we have something to return to the user */
+               if (!ret && IW_IS_GET(cmd)) {
+                       /* Check if there is enough buffer up there */
+                       if (user_length < iwr->u.data.length) {
+                               kfree(extra);
+                               return -E2BIG;
+                       }
+
+                       err = copy_to_user(iwr->u.data.pointer, extra,
+                                          iwr->u.data.length *
+                                          descr->token_size);
+                       if (err)
+                               ret =  -EFAULT;
+#ifdef WE_IOCTL_DEBUG
+                       printk(KERN_DEBUG "%s (WE) : Wrote %d bytes\n",
+                              dev->name,
+                              iwr->u.data.length * descr->token_size);
+#endif /* WE_IOCTL_DEBUG */
+               }
+
+#ifdef WE_SET_EVENT
+               /* Generate an event to notify listeners of the change */
+               if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
+                  ((ret == 0) || (ret == -EIWCOMMIT))) {
+                       if (descr->flags & IW_DESCR_FLAG_RESTRICT)
+                               /* If the event is restricted, don't
+                                * export the payload */
+                               wireless_send_event(dev, cmd, &(iwr->u), NULL);
+                       else
+                               wireless_send_event(dev, cmd, &(iwr->u),
+                                                   extra);
+               }
+#endif /* WE_SET_EVENT */
+
+               /* Cleanup - I told you it wasn't that long ;-) */
+               kfree(extra);
+       }
+
+       /* Call commit handler if needed and defined */
+       if (ret == -EIWCOMMIT)
+               ret = call_commit_handler(dev);
+
+       /* Here, we will generate the appropriate event if needed */
+
+       return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a private Wireless Extension handler.
+ * We do various checks and also take care of moving data between
+ * user space and kernel space.
+ * It's not as nice and slimline as the standard wrapper. The cause
+ * is struct iw_priv_args, which was not really designed for the
+ * job we are going here.
+ *
+ * IMPORTANT : This function prevent to set and get data on the same
+ * IOCTL and enforce the SET/GET convention. Not doing it would be
+ * far too hairy...
+ * If you need to set and get data at the same time, please don't use
+ * a iw_handler but process it in your ioctl handler (i.e. use the
+ * old driver API).
+ */
+static inline int ioctl_private_call(struct net_device *       dev,
+                                    struct ifreq *             ifr,
+                                    unsigned int               cmd,
+                                    iw_handler         handler)
+{
+       struct iwreq *                  iwr = (struct iwreq *) ifr;
+       const struct iw_priv_args *     descr = NULL;
+       struct iw_request_info          info;
+       int                             extra_size = 0;
+       int                             i;
+       int                             ret = -EINVAL;
+
+       /* Get the description of the IOCTL */
+       for (i = 0; i < dev->wireless_handlers->num_private_args; i++)
+               if (cmd == dev->wireless_handlers->private_args[i].cmd) {
+                       descr = &(dev->wireless_handlers->private_args[i]);
+                       break;
+               }
+
+#ifdef WE_IOCTL_DEBUG
+       printk(KERN_DEBUG "%s (WE) : Found private handler for 0x%04X\n",
+              ifr->ifr_name, cmd);
+       if (descr) {
+               printk(KERN_DEBUG "%s (WE) : Name %s, set %X, get %X\n",
+                      dev->name, descr->name,
+                      descr->set_args, descr->get_args);
+       }
+#endif /* WE_IOCTL_DEBUG */
+
+       /* Compute the size of the set/get arguments */
+       if (descr != NULL) {
+               if (IW_IS_SET(cmd)) {
+                       int     offset = 0;     /* For sub-ioctls */
+                       /* Check for sub-ioctl handler */
+                       if (descr->name[0] == '\0')
+                               /* Reserve one int for sub-ioctl index */
+                               offset = sizeof(__u32);
+
+                       /* Size of set arguments */
+                       extra_size = get_priv_size(descr->set_args);
+
+                       /* Does it fits in iwr ? */
+                       if ((descr->set_args & IW_PRIV_SIZE_FIXED) &&
+                          ((extra_size + offset) <= IFNAMSIZ))
+                               extra_size = 0;
+               } else {
+                       /* Size of get arguments */
+                       extra_size = get_priv_size(descr->get_args);
+
+                       /* Does it fits in iwr ? */
+                       if ((descr->get_args & IW_PRIV_SIZE_FIXED) &&
+                          (extra_size <= IFNAMSIZ))
+                               extra_size = 0;
+               }
+       }
+
+       /* Prepare the call */
+       info.cmd = cmd;
+       info.flags = 0;
+
+       /* Check if we have a pointer to user space data or not. */
+       if (extra_size == 0) {
+               /* No extra arguments. Trivial to handle */
+               ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));
+       } else {
+               char *  extra;
+               int     err;
+
+               /* Check what user space is giving us */
+               if (IW_IS_SET(cmd)) {
+                       /* Check NULL pointer */
+                       if ((iwr->u.data.pointer == NULL) &&
+                          (iwr->u.data.length != 0))
+                               return -EFAULT;
+
+                       /* Does it fits within bounds ? */
+                       if (iwr->u.data.length > (descr->set_args &
+                                                IW_PRIV_SIZE_MASK))
+                               return -E2BIG;
+               } else {
+                       /* Check NULL pointer */
+                       if (iwr->u.data.pointer == NULL)
+                               return -EFAULT;
+               }
+
+#ifdef WE_IOCTL_DEBUG
+               printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
+                      dev->name, extra_size);
+#endif /* WE_IOCTL_DEBUG */
+
+               /* Always allocate for max space. Easier, and won't last
+                * long... */
+               extra = kmalloc(extra_size, GFP_KERNEL);
+               if (extra == NULL) {
+                       return -ENOMEM;
+               }
+
+               /* If it is a SET, get all the extra data in here */
+               if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
+                       err = copy_from_user(extra, iwr->u.data.pointer,
+                                            extra_size);
+                       if (err) {
+                               kfree(extra);
+                               return -EFAULT;
+                       }
+#ifdef WE_IOCTL_DEBUG
+                       printk(KERN_DEBUG "%s (WE) : Got %d elem\n",
+                              dev->name, iwr->u.data.length);
+#endif /* WE_IOCTL_DEBUG */
+               }
+
+               /* Call the handler */
+               ret = handler(dev, &info, &(iwr->u), extra);
+
+               /* If we have something to return to the user */
+               if (!ret && IW_IS_GET(cmd)) {
+
+                       /* Adjust for the actual length if it's variable,
+                        * avoid leaking kernel bits outside. */
+                       if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) {
+                               extra_size = adjust_priv_size(descr->get_args,
+                                                             &(iwr->u));
+                       }
+
+                       err = copy_to_user(iwr->u.data.pointer, extra,
+                                          extra_size);
+                       if (err)
+                               ret =  -EFAULT;
+#ifdef WE_IOCTL_DEBUG
+                       printk(KERN_DEBUG "%s (WE) : Wrote %d elem\n",
+                              dev->name, iwr->u.data.length);
+#endif /* WE_IOCTL_DEBUG */
+               }
+
+               /* Cleanup - I told you it wasn't that long ;-) */
+               kfree(extra);
+       }
+
+
+       /* Call commit handler if needed and defined */
+       if (ret == -EIWCOMMIT)
+               ret = call_commit_handler(dev);
+
+       return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Main IOCTl dispatcher. Called from the main networking code
+ * (dev_ioctl() in net/core/dev.c).
+ * Check the type of IOCTL and call the appropriate wrapper...
+ */
+int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
+{
+       struct net_device *dev;
+       iw_handler      handler;
+
+       /* Permissions are already checked in dev_ioctl() before calling us.
+        * The copy_to/from_user() of ifr is also dealt with in there */
+
+       /* Make sure the device exist */
+       if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL)
+               return -ENODEV;
+
+       /* A bunch of special cases, then the generic case...
+        * Note that 'cmd' is already filtered in dev_ioctl() with
+        * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
+       switch (cmd) {
+       case SIOCGIWSTATS:
+               /* Get Wireless Stats */
+               return ioctl_standard_call(dev,
+                                          ifr,
+                                          cmd,
+                                          &iw_handler_get_iwstats);
+
+       case SIOCGIWPRIV:
+               /* Check if we have some wireless handlers defined */
+               if (dev->wireless_handlers != NULL) {
+                       /* We export to user space the definition of
+                        * the private handler ourselves */
+                       return ioctl_standard_call(dev,
+                                                  ifr,
+                                                  cmd,
+                                                  &iw_handler_get_private);
+               }
+               // ## Fall-through for old API ##
+       default:
+               /* Generic IOCTL */
+               /* Basic check */
+               if (!netif_device_present(dev))
+                       return -ENODEV;
+               /* New driver API : try to find the handler */
+               handler = get_handler(dev, cmd);
+               if (handler != NULL) {
+                       /* Standard and private are not the same */
+                       if (cmd < SIOCIWFIRSTPRIV)
+                               return ioctl_standard_call(dev,
+                                                          ifr,
+                                                          cmd,
+                                                          handler);
+                       else
+                               return ioctl_private_call(dev,
+                                                         ifr,
+                                                         cmd,
+                                                         handler);
+               }
+               /* Old driver API : call driver ioctl handler */
+               if (dev->do_ioctl) {
+                       return dev->do_ioctl(dev, ifr, cmd);
+               }
+               return -EOPNOTSUPP;
+       }
+       /* Not reached */
+       return -EINVAL;
+}
+
+
+/************************* EVENT PROCESSING *************************/
+/*
+ * Process events generated by the wireless layer or the driver.
+ * Most often, the event will be propagated through rtnetlink
+ */
+
+#ifdef WE_EVENT_RTNETLINK
+/* ---------------------------------------------------------------- */
+/*
+ * Locking...
+ * ----------
+ *
+ * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing
+ * the locking issue in here and implementing this code !
+ *
+ * The issue : wireless_send_event() is often called in interrupt context,
+ * while the Netlink layer can never be called in interrupt context.
+ * The fully formed RtNetlink events are queued, and then a tasklet is run
+ * to feed those to Netlink.
+ * The skb_queue is interrupt safe, and its lock is not held while calling
+ * Netlink, so there is no possibility of dealock.
+ * Jean II
+ */
+
+static struct sk_buff_head wireless_nlevent_queue;
+
+static int __init wireless_nlevent_init(void)
+{
+       skb_queue_head_init(&wireless_nlevent_queue);
+       return 0;
+}
+
+subsys_initcall(wireless_nlevent_init);
+
+static void wireless_nlevent_process(unsigned long data)
+{
+       struct sk_buff *skb;
+
+       while ((skb = skb_dequeue(&wireless_nlevent_queue)))
+               rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
+}
+
+static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);
+
+/* ---------------------------------------------------------------- */
+/*
+ * Fill a rtnetlink message with our event data.
+ * Note that we propage only the specified event and don't dump the
+ * current wireless config. Dumping the wireless config is far too
+ * expensive (for each parameter, the driver need to query the hardware).
+ */
+static inline int rtnetlink_fill_iwinfo(struct sk_buff *       skb,
+                                       struct net_device *     dev,
+                                       int                     type,
+                                       char *                  event,
+                                       int                     event_len)
+{
+       struct ifinfomsg *r;
+       struct nlmsghdr  *nlh;
+       unsigned char    *b = skb_tail_pointer(skb);
+
+       nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r));
+       r = NLMSG_DATA(nlh);
+       r->ifi_family = AF_UNSPEC;
+       r->__ifi_pad = 0;
+       r->ifi_type = dev->type;
+       r->ifi_index = dev->ifindex;
+       r->ifi_flags = dev_get_flags(dev);
+       r->ifi_change = 0;      /* Wireless changes don't affect those flags */
+
+       /* Add the wireless events in the netlink packet */
+       RTA_PUT(skb, IFLA_WIRELESS, event_len, event);
+
+       nlh->nlmsg_len = skb_tail_pointer(skb) - b;
+       return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+       nlmsg_trim(skb, b);
+       return -1;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Create and broadcast and send it on the standard rtnetlink socket
+ * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c
+ * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field
+ * within a RTM_NEWLINK event.
+ */
+static inline void rtmsg_iwinfo(struct net_device *    dev,
+                               char *                  event,
+                               int                     event_len)
+{
+       struct sk_buff *skb;
+       int size = NLMSG_GOODSIZE;
+
+       skb = alloc_skb(size, GFP_ATOMIC);
+       if (!skb)
+               return;
+
+       if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK,
+                                 event, event_len) < 0) {
+               kfree_skb(skb);
+               return;
+       }
+       NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
+       skb_queue_tail(&wireless_nlevent_queue, skb);
+       tasklet_schedule(&wireless_nlevent_tasklet);
+}
+
+#endif /* WE_EVENT_RTNETLINK */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Main event dispatcher. Called from other parts and drivers.
+ * Send the event on the appropriate channels.
+ * May be called from interrupt context.
+ */
+void wireless_send_event(struct net_device *   dev,
+                        unsigned int           cmd,
+                        union iwreq_data *     wrqu,
+                        char *                 extra)
+{
+       const struct iw_ioctl_description *     descr = NULL;
+       int extra_len = 0;
+       struct iw_event  *event;                /* Mallocated whole event */
+       int event_len;                          /* Its size */
+       int hdr_len;                            /* Size of the event header */
+       int wrqu_off = 0;                       /* Offset in wrqu */
+       /* Don't "optimise" the following variable, it will crash */
+       unsigned        cmd_index;              /* *MUST* be unsigned */
+
+       /* Get the description of the Event */
+       if (cmd <= SIOCIWLAST) {
+               cmd_index = cmd - SIOCIWFIRST;
+               if (cmd_index < standard_ioctl_num)
+                       descr = &(standard_ioctl[cmd_index]);
+       } else {
+               cmd_index = cmd - IWEVFIRST;
+               if (cmd_index < standard_event_num)
+                       descr = &(standard_event[cmd_index]);
+       }
+       /* Don't accept unknown events */
+       if (descr == NULL) {
+               /* Note : we don't return an error to the driver, because
+                * the driver would not know what to do about it. It can't
+                * return an error to the user, because the event is not
+                * initiated by a user request.
+                * The best the driver could do is to log an error message.
+                * We will do it ourselves instead...
+                */
+               printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n",
+                      dev->name, cmd);
+               return;
+       }
+#ifdef WE_EVENT_DEBUG
+       printk(KERN_DEBUG "%s (WE) : Got event 0x%04X\n",
+              dev->name, cmd);
+       printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
+#endif /* WE_EVENT_DEBUG */
+
+       /* Check extra parameters and set extra_len */
+       if (descr->header_type == IW_HEADER_TYPE_POINT) {
+               /* Check if number of token fits within bounds */
+               if (wrqu->data.length > descr->max_tokens) {
+                       printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length);
+                       return;
+               }
+               if (wrqu->data.length < descr->min_tokens) {
+                       printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length);
+                       return;
+               }
+               /* Calculate extra_len - extra is NULL for restricted events */
+               if (extra != NULL)
+                       extra_len = wrqu->data.length * descr->token_size;
+               /* Always at an offset in wrqu */
+               wrqu_off = IW_EV_POINT_OFF;
+#ifdef WE_EVENT_DEBUG
+               printk(KERN_DEBUG "%s (WE) : Event 0x%04X, tokens %d, extra_len %d\n", dev->name, cmd, wrqu->data.length, extra_len);
+#endif /* WE_EVENT_DEBUG */
+       }
+
+       /* Total length of the event */
+       hdr_len = event_type_size[descr->header_type];
+       event_len = hdr_len + extra_len;
+
+#ifdef WE_EVENT_DEBUG
+       printk(KERN_DEBUG "%s (WE) : Event 0x%04X, hdr_len %d, wrqu_off %d, event_len %d\n", dev->name, cmd, hdr_len, wrqu_off, event_len);
+#endif /* WE_EVENT_DEBUG */
+
+       /* Create temporary buffer to hold the event */
+       event = kmalloc(event_len, GFP_ATOMIC);
+       if (event == NULL)
+               return;
+
+       /* Fill event */
+       event->len = event_len;
+       event->cmd = cmd;
+       memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
+       if (extra != NULL)
+               memcpy(((char *) event) + hdr_len, extra, extra_len);
+
+#ifdef WE_EVENT_RTNETLINK
+       /* Send via the RtNetlink event channel */
+       rtmsg_iwinfo(dev, (char *) event, event_len);
+#endif /* WE_EVENT_RTNETLINK */
+
+       /* Cleanup */
+       kfree(event);
+
+       return;         /* Always success, I guess ;-) */
+}
+
+/********************** ENHANCED IWSPY SUPPORT **********************/
+/*
+ * In the old days, the driver was handling spy support all by itself.
+ * Now, the driver can delegate this task to Wireless Extensions.
+ * It needs to use those standard spy iw_handler in struct iw_handler_def,
+ * push data to us via wireless_spy_update() and include struct iw_spy_data
+ * in its private part (and export it in net_device->wireless_data->spy_data).
+ * One of the main advantage of centralising spy support here is that
+ * it becomes much easier to improve and extend it without having to touch
+ * the drivers. One example is the addition of the Spy-Threshold events.
+ */
+
+/* ---------------------------------------------------------------- */
+/*
+ * Return the pointer to the spy data in the driver.
+ * Because this is called on the Rx path via wireless_spy_update(),
+ * we want it to be efficient...
+ */
+static inline struct iw_spy_data * get_spydata(struct net_device *dev)
+{
+       /* This is the new way */
+       if (dev->wireless_data)
+               return(dev->wireless_data->spy_data);
+       return NULL;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : set Spy List
+ */
+int iw_handler_set_spy(struct net_device *     dev,
+                      struct iw_request_info * info,
+                      union iwreq_data *       wrqu,
+                      char *                   extra)
+{
+       struct iw_spy_data *    spydata = get_spydata(dev);
+       struct sockaddr *       address = (struct sockaddr *) extra;
+
+       /* Make sure driver is not buggy or using the old API */
+       if (!spydata)
+               return -EOPNOTSUPP;
+
+       /* Disable spy collection while we copy the addresses.
+        * While we copy addresses, any call to wireless_spy_update()
+        * will NOP. This is OK, as anyway the addresses are changing. */
+       spydata->spy_number = 0;
+
+       /* We want to operate without locking, because wireless_spy_update()
+        * most likely will happen in the interrupt handler, and therefore
+        * have its own locking constraints and needs performance.
+        * The rtnl_lock() make sure we don't race with the other iw_handlers.
+        * This make sure wireless_spy_update() "see" that the spy list
+        * is temporarily disabled. */
+       smp_wmb();
+
+       /* Are there are addresses to copy? */
+       if (wrqu->data.length > 0) {
+               int i;
+
+               /* Copy addresses */
+               for (i = 0; i < wrqu->data.length; i++)
+                       memcpy(spydata->spy_address[i], address[i].sa_data,
+                              ETH_ALEN);
+               /* Reset stats */
+               memset(spydata->spy_stat, 0,
+                      sizeof(struct iw_quality) * IW_MAX_SPY);
+
+#ifdef WE_SPY_DEBUG
+               printk(KERN_DEBUG "iw_handler_set_spy() :  wireless_data %p, spydata %p, num %d\n", dev->wireless_data, spydata, wrqu->data.length);
+               for (i = 0; i < wrqu->data.length; i++)
+                       printk(KERN_DEBUG
+                              "%02X:%02X:%02X:%02X:%02X:%02X \n",
+                              spydata->spy_address[i][0],
+                              spydata->spy_address[i][1],
+                              spydata->spy_address[i][2],
+                              spydata->spy_address[i][3],
+                              spydata->spy_address[i][4],
+                              spydata->spy_address[i][5]);
+#endif /* WE_SPY_DEBUG */
+       }
+
+       /* Make sure above is updated before re-enabling */
+       smp_wmb();
+
+       /* Enable addresses */
+       spydata->spy_number = wrqu->data.length;
+
+       return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : get Spy List
+ */
+int iw_handler_get_spy(struct net_device *     dev,
+                      struct iw_request_info * info,
+                      union iwreq_data *       wrqu,
+                      char *                   extra)
+{
+       struct iw_spy_data *    spydata = get_spydata(dev);
+       struct sockaddr *       address = (struct sockaddr *) extra;
+       int                     i;
+
+       /* Make sure driver is not buggy or using the old API */
+       if (!spydata)
+               return -EOPNOTSUPP;
+
+       wrqu->data.length = spydata->spy_number;
+
+       /* Copy addresses. */
+       for (i = 0; i < spydata->spy_number; i++)       {
+               memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
+               address[i].sa_family = AF_UNIX;
+       }
+       /* Copy stats to the user buffer (just after). */
+       if (spydata->spy_number > 0)
+               memcpy(extra  + (sizeof(struct sockaddr) *spydata->spy_number),
+                      spydata->spy_stat,
+                      sizeof(struct iw_quality) * spydata->spy_number);
+       /* Reset updated flags. */
+       for (i = 0; i < spydata->spy_number; i++)
+               spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
+       return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : set spy threshold
+ */
+int iw_handler_set_thrspy(struct net_device *  dev,
+                         struct iw_request_info *info,
+                         union iwreq_data *    wrqu,
+                         char *                extra)
+{
+       struct iw_spy_data *    spydata = get_spydata(dev);
+       struct iw_thrspy *      threshold = (struct iw_thrspy *) extra;
+
+       /* Make sure driver is not buggy or using the old API */
+       if (!spydata)
+               return -EOPNOTSUPP;
+
+       /* Just do it */
+       memcpy(&(spydata->spy_thr_low), &(threshold->low),
+              2 * sizeof(struct iw_quality));
+
+       /* Clear flag */
+       memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
+
+#ifdef WE_SPY_DEBUG
+       printk(KERN_DEBUG "iw_handler_set_thrspy() :  low %d ; high %d\n", spydata->spy_thr_low.level, spydata->spy_thr_high.level);
+#endif /* WE_SPY_DEBUG */
+
+       return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Standard Wireless Handler : get spy threshold
+ */
+int iw_handler_get_thrspy(struct net_device *  dev,
+                         struct iw_request_info *info,
+                         union iwreq_data *    wrqu,
+                         char *                extra)
+{
+       struct iw_spy_data *    spydata = get_spydata(dev);
+       struct iw_thrspy *      threshold = (struct iw_thrspy *) extra;
+
+       /* Make sure driver is not buggy or using the old API */
+       if (!spydata)
+               return -EOPNOTSUPP;
+
+       /* Just do it */
+       memcpy(&(threshold->low), &(spydata->spy_thr_low),
+              2 * sizeof(struct iw_quality));
+
+       return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Prepare and send a Spy Threshold event
+ */
+static void iw_send_thrspy_event(struct net_device *   dev,
+                                struct iw_spy_data *   spydata,
+                                unsigned char *        address,
+                                struct iw_quality *    wstats)
+{
+       union iwreq_data        wrqu;
+       struct iw_thrspy        threshold;
+
+       /* Init */
+       wrqu.data.length = 1;
+       wrqu.data.flags = 0;
+       /* Copy address */
+       memcpy(threshold.addr.sa_data, address, ETH_ALEN);
+       threshold.addr.sa_family = ARPHRD_ETHER;
+       /* Copy stats */
+       memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
+       /* Copy also thresholds */
+       memcpy(&(threshold.low), &(spydata->spy_thr_low),
+              2 * sizeof(struct iw_quality));
+
+#ifdef WE_SPY_DEBUG
+       printk(KERN_DEBUG "iw_send_thrspy_event() : address %02X:%02X:%02X:%02X:%02X:%02X, level %d, up = %d\n",
+              threshold.addr.sa_data[0],
+              threshold.addr.sa_data[1],
+              threshold.addr.sa_data[2],
+              threshold.addr.sa_data[3],
+              threshold.addr.sa_data[4],
+              threshold.addr.sa_data[5], threshold.qual.level);
+#endif /* WE_SPY_DEBUG */
+
+       /* Send event to user space */
+       wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Call for the driver to update the spy data.
+ * For now, the spy data is a simple array. As the size of the array is
+ * small, this is good enough. If we wanted to support larger number of
+ * spy addresses, we should use something more efficient...
+ */
+void wireless_spy_update(struct net_device *   dev,
+                        unsigned char *        address,
+                        struct iw_quality *    wstats)
+{
+       struct iw_spy_data *    spydata = get_spydata(dev);
+       int                     i;
+       int                     match = -1;
+
+       /* Make sure driver is not buggy or using the old API */
+       if (!spydata)
+               return;
+
+#ifdef WE_SPY_DEBUG
+       printk(KERN_DEBUG "wireless_spy_update() :  wireless_data %p, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_data, spydata, address[0], address[1], address[2], address[3], address[4], address[5]);
+#endif /* WE_SPY_DEBUG */
+
+       /* Update all records that match */
+       for (i = 0; i < spydata->spy_number; i++)
+               if (!compare_ether_addr(address, spydata->spy_address[i])) {
+                       memcpy(&(spydata->spy_stat[i]), wstats,
+                              sizeof(struct iw_quality));
+                       match = i;
+               }
+
+       /* Generate an event if we cross the spy threshold.
+        * To avoid event storms, we have a simple hysteresis : we generate
+        * event only when we go under the low threshold or above the
+        * high threshold. */
+       if (match >= 0) {
+               if (spydata->spy_thr_under[match]) {
+                       if (wstats->level > spydata->spy_thr_high.level) {
+                               spydata->spy_thr_under[match] = 0;
+                               iw_send_thrspy_event(dev, spydata,
+                                                    address, wstats);
+                       }
+               } else {
+                       if (wstats->level < spydata->spy_thr_low.level) {
+                               spydata->spy_thr_under[match] = 1;
+                               iw_send_thrspy_event(dev, spydata,
+                                                    address, wstats);
+                       }
+               }
+       }
+}
+
+EXPORT_SYMBOL(iw_handler_get_spy);
+EXPORT_SYMBOL(iw_handler_get_thrspy);
+EXPORT_SYMBOL(iw_handler_set_spy);
+EXPORT_SYMBOL(iw_handler_set_thrspy);
+EXPORT_SYMBOL(wireless_send_event);
+EXPORT_SYMBOL(wireless_spy_update);