lkml.org 
[lkml]   [2002]   [Mar]   [28]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    Subject[PATCH 2.4.19-pre4] New wireless driver API part 1
    From
    	Hi again Marcelo,

    Sorry to insist, but I need to get this patch in 2.4.X. I'm
    now blocked on two wireless driver (orinoco.c and airo.c) because
    their maintainers can't/won't use 2.5.X. And I hate to see all those
    patches bitroting.

    The deal : new driver API for Wireless Extensions (first
    part). More details below.
    Patch *attached*. I re-diffed against 2.4.19-pre4, and tested
    with both 2.4.19-pre4 and 2.4.19-pre4-ac2 (for ac2, the patch applies
    with fuzz).
    Why would you accept the patch :
    o More than 2 month already in 2.5.X
    o Jeff says it's 2.4.X material :
    http://marc.theaimsgroup.com/?l=linux-kernel&m=101607595127684&w=2
    o Alan says "sounds good" :
    http://marc.theaimsgroup.com/?l=linux-kernel&m=101607595327690&w=2

    Tell me if there is any complications...

    Have fun...

    Jean

    On Wed, Mar 13, 2002 at 06:59:15PM -0800, jt wrote:
    > Hi Marcelo,
    >
    > Some Wireless LAN driver authors have requested me to backport
    > the new Wireless Extension API to 2.4.X so that they can hack their
    > drivers without having to deal with 2.5.X. This patch just do that, so
    > could you please add that to your tree ?
    >
    > This patch is *very* safe, especially compared to other stuff
    > that made it's way in your kernel :
    > o was integrated in 2.5.3-pre2, straight backport (same patch)
    > o tested with various drivers in 2.5.X
    > o reviewed by Jeff Garzik ;-)
    > o doesn't change anything for driver still using the old API
    > (so current kernel/pcmcia drivers won't get broken).
    > o no part of the kernel using the new API, only outside drivers
    >
    > For stability reason, I won't change any of the current driver
    > in the kernel, and those will continue to use the old API (unless you
    > or Jeff request me to update them).
    > Also, I may send you in the future the second part of the new
    > API (that got just integrated in 2.5.7-pre1), but I'm not in a
    > hurry... So, for now, first part only ;-)
    > Below, you will find the original blurb I sent to Linus...
    >
    > Thanks in advance...
    >
    > Jean
    >
    > ---------------------------------------------------------
    > Hi Linus,
    >
    > I feel the first part my new driver API for Wireless
    > Extensions is ready to go in the kernel.
    >
    > I don't want to bore you too much with details, but for
    > Wireless Extensions it replace the call to the device driver ioctl
    > function with individual call to handlers functions.
    > The advantage of the new API are :
    > o Handling of Extensions in driver broken in small contained functions
    > o Tighter checks of ioctl before calling the driver
    > o Flexible commit strategy (at least, the start of it)
    > o Backward compatibility (can be mixed with old API)
    > o Remove all ioctl assumptions in the driver
    > o Driver doesn't have to worry about memory and user-space issues
    > (copy_to/from_user handled by the API, not the driver)
    > The last point is important for the following reasons :
    > o You are now able to call the new driver API from any API you
    > want (including from within other parts of the kernel).
    > o Common mistakes are avoided (buffer overflow, user space copy
    > with irq disabled and so on).
    > More details available at :
    > http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html#newapi
    >
    > Compare to my earlier patch (sent to the list in December), I
    > made the following changes :
    > o cleanup comments
    > o use "return X;" instead of "return(X);" (pointed out to me
    > by Jeff - thanks).
    >
    > This has been tested on 2.5.3-pre2. Would you mind adding that
    > to your kernel ?
    > Regards,
    >
    > Jean
    diff -u -p -r --new-file linux/include/linux-w12/netdevice.h linux/include/linux/netdevice.h
    --- linux/include/linux-w12/netdevice.h Thu Mar 28 15:34:30 2002
    +++ linux/include/linux/netdevice.h Thu Mar 28 15:37:45 2002
    @@ -279,6 +279,10 @@ struct net_device
    struct net_device_stats* (*get_stats)(struct net_device *dev);
    struct iw_statistics* (*get_wireless_stats)(struct net_device *dev);

    + /* List of functions to handle Wireless Extensions (instead of ioctl).
    + * See <net/iw_handler.h> for details. Jean II */
    + struct iw_handler_def * wireless_handlers;
    +
    /*
    * This marks the end of the "visible" part of the structure. All
    * fields hereafter are internal to the system, and may change at
    diff -u -p -r --new-file linux/include/linux-w12/wireless.h linux/include/linux/wireless.h
    --- linux/include/linux-w12/wireless.h Thu Mar 28 15:34:30 2002
    +++ linux/include/linux/wireless.h Thu Mar 28 15:37:45 2002
    @@ -1,9 +1,10 @@
    /*
    * This file define a set of standard wireless extensions
    *
    - * Version : 12 5.10.01
    + * Version : 13 6.12.01
    *
    * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
    + * Copyright (c) 1997-2001 Jean Tourrilhes, All Rights Reserved.
    */

    #ifndef _LINUX_WIRELESS_H
    @@ -11,6 +12,8 @@

    /************************** DOCUMENTATION **************************/
    /*
    + * Initial APIs (1996 -> onward) :
    + * -----------------------------
    * Basically, the wireless extensions are for now a set of standard ioctl
    * call + /proc/net/wireless
    *
    @@ -27,16 +30,27 @@
    * We have the list of command plus a structure descibing the
    * data exchanged...
    * Note that to add these ioctl, I was obliged to modify :
    - * net/core/dev.c (two place + add include)
    - * net/ipv4/af_inet.c (one place + add include)
    + * # net/core/dev.c (two place + add include)
    + * # net/ipv4/af_inet.c (one place + add include)
    *
    * /proc/net/wireless is a copy of /proc/net/dev.
    * We have a structure for data passed from the driver to /proc/net/wireless
    * Too add this, I've modified :
    - * net/core/dev.c (two other places)
    - * include/linux/netdevice.h (one place)
    - * include/linux/proc_fs.h (one place)
    + * # net/core/dev.c (two other places)
    + * # include/linux/netdevice.h (one place)
    + * # include/linux/proc_fs.h (one place)
    + *
    + * New driver API (2001 -> onward) :
    + * -------------------------------
    + * This file is only concerned with the user space API and common definitions.
    + * The new driver API is defined and documented in :
    + * # include/net/iw_handler.h
    *
    + * Note as well that /proc/net/wireless implementation has now moved in :
    + * # include/linux/wireless.c
    + *
    + * Other comments :
    + * --------------
    * Do not add here things that are redundant with other mechanisms
    * (drivers init, ifconfig, /proc/net/dev, ...) and with are not
    * wireless specific.
    @@ -54,16 +68,14 @@
    #include <linux/socket.h> /* for "struct sockaddr" et al */
    #include <linux/if.h> /* for IFNAMSIZ and co... */

    -/**************************** CONSTANTS ****************************/
    -
    -/* --------------------------- VERSION --------------------------- */
    +/***************************** VERSION *****************************/
    /*
    * This constant is used to know the availability of the wireless
    * extensions and to know which version of wireless extensions it is
    * (there is some stuff that will be added in the future...)
    * I just plan to increment with each new version.
    */
    -#define WIRELESS_EXT 12
    +#define WIRELESS_EXT 13

    /*
    * Changes :
    @@ -123,12 +135,20 @@
    * - Add DEV PRIVATE IOCTL to avoid collisions in SIOCDEVPRIVATE space
    * - Add new statistics (frag, retry, beacon)
    * - Add average quality (for user space calibration)
    + *
    + * V12 to V13
    + * ----------
    + * - Document creation of new driver API.
    + * - Extract union iwreq_data from struct iwreq (for new driver API).
    + * - Rename SIOCSIWNAME as SIOCSIWCOMMIT
    */

    +/**************************** CONSTANTS ****************************/
    +
    /* -------------------------- IOCTL LIST -------------------------- */

    /* Basic operations */
    -#define SIOCSIWNAME 0x8B00 /* Unused */
    +#define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */
    #define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */
    #define SIOCSIWNWID 0x8B02 /* set network id (the cell) */
    #define SIOCGIWNWID 0x8B03 /* get network id */
    @@ -414,13 +434,49 @@ struct iw_statistics

    /* ------------------------ IOCTL REQUEST ------------------------ */
    /*
    + * This structure defines the payload of an ioctl, and is used
    + * below.
    + *
    + * Note that this structure should fit on the memory footprint
    + * of iwreq (which is the same as ifreq), which mean a max size of
    + * 16 octets = 128 bits. Warning, pointers might be 64 bits wide...
    + * You should check this when increasing the structures defined
    + * above in this file...
    + */
    +union iwreq_data
    +{
    + /* Config - generic */
    + char name[IFNAMSIZ];
    + /* Name : used to verify the presence of wireless extensions.
    + * Name of the protocol/provider... */
    +
    + struct iw_point essid; /* Extended network name */
    + struct iw_param nwid; /* network id (or domain - the cell) */
    + struct iw_freq freq; /* frequency or channel :
    + * 0-1000 = channel
    + * > 1000 = frequency in Hz */
    +
    + struct iw_param sens; /* signal level threshold */
    + struct iw_param bitrate; /* default bit rate */
    + struct iw_param txpower; /* default transmit power */
    + struct iw_param rts; /* RTS threshold threshold */
    + struct iw_param frag; /* Fragmentation threshold */
    + __u32 mode; /* Operation mode */
    + struct iw_param retry; /* Retry limits & lifetime */
    +
    + struct iw_point encoding; /* Encoding stuff : tokens */
    + struct iw_param power; /* PM duration/timeout */
    +
    + struct sockaddr ap_addr; /* Access point address */
    +
    + struct iw_point data; /* Other large parameters */
    +};
    +
    +/*
    * The structure to exchange data for ioctl.
    * This structure is the same as 'struct ifreq', but (re)defined for
    * convenience...
    - *
    - * Note that it should fit on the same memory footprint !
    - * You should check this when increasing the above structures (16 octets)
    - * 16 octets = 128 bits. Warning, pointers might be 64 bits wide...
    + * Do I need to remind you about structure size (32 octets) ?
    */
    struct iwreq
    {
    @@ -429,35 +485,8 @@ struct iwreq
    char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */
    } ifr_ifrn;

    - /* Data part */
    - union
    - {
    - /* Config - generic */
    - char name[IFNAMSIZ];
    - /* Name : used to verify the presence of wireless extensions.
    - * Name of the protocol/provider... */
    -
    - struct iw_point essid; /* Extended network name */
    - struct iw_param nwid; /* network id (or domain - the cell) */
    - struct iw_freq freq; /* frequency or channel :
    - * 0-1000 = channel
    - * > 1000 = frequency in Hz */
    -
    - struct iw_param sens; /* signal level threshold */
    - struct iw_param bitrate; /* default bit rate */
    - struct iw_param txpower; /* default transmit power */
    - struct iw_param rts; /* RTS threshold threshold */
    - struct iw_param frag; /* Fragmentation threshold */
    - __u32 mode; /* Operation mode */
    - struct iw_param retry; /* Retry limits & lifetime */
    -
    - struct iw_point encoding; /* Encoding stuff : tokens */
    - struct iw_param power; /* PM duration/timeout */
    -
    - struct sockaddr ap_addr; /* Access point address */
    -
    - struct iw_point data; /* Other large parameters */
    - } u;
    + /* Data part (defined just above) */
    + union iwreq_data u;
    };

    /* -------------------------- IOCTL DATA -------------------------- */
    diff -u -p -r --new-file linux/include/net-w12/iw_handler.h linux/include/net/iw_handler.h
    --- linux/include/net-w12/iw_handler.h Wed Dec 31 16:00:00 1969
    +++ linux/include/net/iw_handler.h Thu Mar 28 15:37:45 2002
    @@ -0,0 +1,374 @@
    +/*
    + * This file define the new driver API for Wireless Extensions
    + *
    + * Version : 2 6.12.01
    + *
    + * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
    + * Copyright (c) 2001 Jean Tourrilhes, All Rights Reserved.
    + */
    +
    +#ifndef _IW_HANDLER_H
    +#define _IW_HANDLER_H
    +
    +/************************** DOCUMENTATION **************************/
    +/*
    + * Initial driver API (1996 -> onward) :
    + * -----------------------------------
    + * The initial API just sends the IOCTL request received from user space
    + * to the driver (via the driver ioctl handler). The driver has to
    + * handle all the rest...
    + *
    + * The initial API also defines a specific handler in struct net_device
    + * to handle wireless statistics.
    + *
    + * The initial APIs served us well and has proven a reasonably good design.
    + * However, there is a few shortcommings :
    + * o No events, everything is a request to the driver.
    + * o Large ioctl function in driver with gigantic switch statement
    + * (i.e. spaghetti code).
    + * o Driver has to mess up with copy_to/from_user, and in many cases
    + * does it unproperly. Common mistakes are :
    + * * buffer overflows (no checks or off by one checks)
    + * * call copy_to/from_user with irq disabled
    + * o The user space interface is tied to ioctl because of the use
    + * copy_to/from_user.
    + *
    + * New driver API (2001 -> onward) :
    + * -------------------------------
    + * The new driver API is just a bunch of standard functions (handlers),
    + * each handling a specific Wireless Extension. The driver just export
    + * the list of handler it supports, and those will be called apropriately.
    + *
    + * I tried to keep the main advantage of the previous API (simplicity,
    + * efficiency and light weight), and also I provide a good dose of backward
    + * compatibility (most structures are the same, driver can use both API
    + * simultaneously, ...).
    + * Hopefully, I've also addressed the shortcomming of the initial API.
    + *
    + * The advantage of the new API are :
    + * o Handling of Extensions in driver broken in small contained functions
    + * o Tighter checks of ioctl before calling the driver
    + * o Flexible commit strategy (at least, the start of it)
    + * o Backward compatibility (can be mixed with old API)
    + * o Driver doesn't have to worry about memory and user-space issues
    + * The last point is important for the following reasons :
    + * o You are now able to call the new driver API from any API you
    + * want (including from within other parts of the kernel).
    + * o Common mistakes are avoided (buffer overflow, user space copy
    + * with irq disabled and so on).
    + *
    + * The Drawback of the new API are :
    + * o bloat (especially kernel)
    + * o need to migrate existing drivers to new API
    + * My initial testing shows that the new API adds around 3kB to the kernel
    + * and save between 0 and 5kB from a typical driver.
    + * Also, as all structures and data types are unchanged, the migration is
    + * quite straightforward (but tedious).
    + *
    + * ---
    + *
    + * The new driver API is defined below in this file. User space should
    + * not be aware of what's happening down there...
    + *
    + * A new kernel wrapper is in charge of validating the IOCTLs and calling
    + * the appropriate driver handler. This is implemented in :
    + * # net/core/wireless.c
    + *
    + * The driver export the list of handlers in :
    + * # include/linux/netdevice.h (one place)
    + *
    + * The new driver API is available for WIRELESS_EXT >= 13.
    + * Good luck with migration to the new API ;-)
    + */
    +
    +/* ---------------------- THE IMPLEMENTATION ---------------------- */
    +/*
    + * Some of the choice I've made are pretty controversials. Defining an
    + * API is very much weighting compromises. This goes into some of the
    + * details and the thinking behind the implementation.
    + *
    + * Implementation goals :
    + * --------------------
    + * The implementation goals were as follow :
    + * o Obvious : you should not need a PhD to understand what's happening,
    + * the benefit is easier maintainance.
    + * o Flexible : it should accomodate a wide variety of driver
    + * implementations and be as flexible as the old API.
    + * o Lean : it should be efficient memory wise to minimise the impact
    + * on kernel footprint.
    + * o Transparent to user space : the large number of user space
    + * applications that use Wireless Extensions should not need
    + * any modifications.
    + *
    + * Array of functions versus Struct of functions
    + * ---------------------------------------------
    + * 1) Having an array of functions allow the kernel code to access the
    + * handler in a single lookup, which is much more efficient (think hash
    + * table here).
    + * 2) The only drawback is that driver writer may put their handler in
    + * the wrong slot. This is trivial to test (I set the frequency, the
    + * bitrate changes). Once the handler is in the proper slot, it will be
    + * there forever, because the array is only extended at the end.
    + * 3) Backward/forward compatibility : adding new handler just require
    + * extending the array, so you can put newer driver in older kernel
    + * without having to patch the kernel code (and vice versa).
    + *
    + * All handler are of the same generic type
    + * ----------------------------------------
    + * That's a feature !!!
    + * 1) Having a generic handler allow to have generic code, which is more
    + * efficient. If each of the handler was individually typed I would need
    + * to add a big switch in the kernel (== more bloat). This solution is
    + * more scalable, adding new Wireless Extensions doesn't add new code.
    + * 2) You can use the same handler in different slots of the array. For
    + * hardware, it may be more efficient or logical to handle multiple
    + * Wireless Extensions with a single function, and the API allow you to
    + * do that. (An example would be a single record on the card to control
    + * both bitrate and frequency, the handler would read the old record,
    + * modify it according to info->cmd and rewrite it).
    + *
    + * Functions prototype uses union iwreq_data
    + * -----------------------------------------
    + * Some would have prefered functions defined this way :
    + * static int mydriver_ioctl_setrate(struct net_device *dev,
    + * long rate, int auto)
    + * 1) The kernel code doesn't "validate" the content of iwreq_data, and
    + * can't do it (different hardware may have different notion of what a
    + * valid frequency is), so we don't pretend that we do it.
    + * 2) The above form is not extendable. If I want to add a flag (for
    + * example to distinguish setting max rate and basic rate), I would
    + * break the prototype. Using iwreq_data is more flexible.
    + * 3) Also, the above form is not generic (see above).
    + * 4) I don't expect driver developper using the wrong field of the
    + * union (Doh !), so static typechecking doesn't add much value.
    + * 5) Lastly, you can skip the union by doing :
    + * static int mydriver_ioctl_setrate(struct net_device *dev,
    + * struct iw_request_info *info,
    + * struct iw_param *rrq,
    + * char *extra)
    + * And then adding the handler in the array like this :
    + * (iw_handler) mydriver_ioctl_setrate, // SIOCSIWRATE
    + *
    + * Using functions and not a registry
    + * ----------------------------------
    + * Another implementation option would have been for every instance to
    + * define a registry (a struct containing all the Wireless Extensions)
    + * and only have a function to commit the registry to the hardware.
    + * 1) This approach can be emulated by the current code, but not
    + * vice versa.
    + * 2) Some drivers don't keep any configuration in the driver, for them
    + * adding such a registry would be a significant bloat.
    + * 3) The code to translate from Wireless Extension to native format is
    + * needed anyway, so it would not reduce significantely the amount of code.
    + * 4) The current approach only selectively translate Wireless Extensions
    + * to native format and only selectively set, whereas the registry approach
    + * would require to translate all WE and set all parameters for any single
    + * change.
    + * 5) For many Wireless Extensions, the GET operation return the current
    + * dynamic value, not the value that was set.
    + *
    + * This header is <net/iw_handler.h>
    + * ---------------------------------
    + * 1) This header is kernel space only and should not be exported to
    + * user space. Headers in "include/linux/" are exported, headers in
    + * "include/net/" are not.
    + *
    + * Mixed 32/64 bit issues
    + * ----------------------
    + * The Wireless Extensions are designed to be 64 bit clean, by using only
    + * datatypes with explicit storage size.
    + * There are some issues related to kernel and user space using different
    + * memory model, and in particular 64bit kernel with 32bit user space.
    + * The problem is related to struct iw_point, that contains a pointer
    + * that *may* need to be translated.
    + * This is quite messy. The new API doesn't solve this problem (it can't),
    + * but is a step in the right direction :
    + * 1) Meta data about each ioctl is easily available, so we know what type
    + * of translation is needed.
    + * 2) The move of data between kernel and user space is only done in a single
    + * place in the kernel, so adding specific hooks in there is possible.
    + * 3) In the long term, it allows to move away from using ioctl as the
    + * user space API.
    + *
    + * So many comments and so few code
    + * --------------------------------
    + * That's a feature. Comments won't bloat the resulting kernel binary.
    + */
    +
    +/***************************** INCLUDES *****************************/
    +
    +#include <linux/wireless.h> /* IOCTL user space API */
    +
    +/***************************** VERSION *****************************/
    +/*
    + * This constant is used to know which version of the driver API is
    + * available. Hopefully, this will be pretty stable and no changes
    + * will be needed...
    + * I just plan to increment with each new version.
    + */
    +#define IW_HANDLER_VERSION 2
    +
    +/**************************** CONSTANTS ****************************/
    +
    +/* Special error message for the driver to indicate that we
    + * should do a commit after return from the iw_handler */
    +#define EIWCOMMIT EINPROGRESS
    +
    +/* Flags available in struct iw_request_info */
    +#define IW_REQUEST_FLAG_NONE 0x0000 /* No flag so far */
    +
    +/* Type of headers we know about (basically union iwreq_data) */
    +#define IW_HEADER_TYPE_NULL 0 /* Not available */
    +#define IW_HEADER_TYPE_CHAR 2 /* char [IFNAMSIZ] */
    +#define IW_HEADER_TYPE_UINT 4 /* __u32 */
    +#define IW_HEADER_TYPE_FREQ 5 /* struct iw_freq */
    +#define IW_HEADER_TYPE_POINT 6 /* struct iw_point */
    +#define IW_HEADER_TYPE_PARAM 7 /* struct iw_param */
    +#define IW_HEADER_TYPE_ADDR 8 /* struct sockaddr */
    +
    +/* Handling flags */
    +/* Most are not implemented. I just use them as a reminder of some
    + * cool features we might need one day ;-) */
    +#define IW_DESCR_FLAG_NONE 0x0000 /* Obvious */
    +/* Wrapper level flags */
    +#define IW_DESCR_FLAG_DUMP 0x0001 /* Not part of the dump command */
    +#define IW_DESCR_FLAG_EVENT 0x0002 /* Generate an event on SET */
    +#define IW_DESCR_FLAG_RESTRICT 0x0004 /* GET request is ROOT only */
    +/* Driver level flags */
    +#define IW_DESCR_FLAG_WAIT 0x0100 /* Wait for driver event */
    +
    +/****************************** TYPES ******************************/
    +
    +/* ----------------------- WIRELESS HANDLER ----------------------- */
    +/*
    + * A wireless handler is just a standard function, that looks like the
    + * ioctl handler.
    + * We also define there how a handler list look like... As the Wireless
    + * Extension space is quite dense, we use a simple array, which is faster
    + * (that's the perfect hash table ;-).
    + */
    +
    +/*
    + * Meta data about the request passed to the iw_handler.
    + * Most handlers can safely ignore what's in there.
    + * The 'cmd' field might come handy if you want to use the same handler
    + * for multiple command...
    + * This struct is also my long term insurance. I can add new fields here
    + * without breaking the prototype of iw_handler...
    + */
    +struct iw_request_info
    +{
    + __u16 cmd; /* Wireless Extension command */
    + __u16 flags; /* More to come ;-) */
    +};
    +
    +/*
    + * This is how a function handling a Wireless Extension should look
    + * like (both get and set, standard and private).
    + */
    +typedef int (*iw_handler)(struct net_device *dev, struct iw_request_info *info,
    + union iwreq_data *wrqu, char *extra);
    +
    +/*
    + * This define all the handler that the driver export.
    + * As you need only one per driver type, please use a static const
    + * shared by all driver instances... Same for the members...
    + * This will be linked from net_device in <linux/netdevice.h>
    + */
    +struct iw_handler_def
    +{
    + /* Number of handlers defined (more precisely, index of the
    + * last defined handler + 1) */
    + __u16 num_standard;
    + __u16 num_private;
    + /* Number of private arg description */
    + __u16 num_private_args;
    +
    + /* Array of handlers for standard ioctls
    + * We will call dev->wireless_handlers->standard[ioctl - SIOCSIWNAME]
    + */
    + iw_handler * standard;
    +
    + /* Array of handlers for private ioctls
    + * Will call dev->wireless_handlers->private[ioctl - SIOCIWFIRSTPRIV]
    + */
    + iw_handler * private;
    +
    + /* Arguments of private handler. This one is just a list, so you
    + * can put it in any order you want and should not leave holes...
    + * We will automatically export that to user space... */
    + struct iw_priv_args * private_args;
    +
    + /* In the long term, get_wireless_stats will move from
    + * 'struct net_device' to here, to minimise bloat. */
    +};
    +
    +/* ----------------------- WIRELESS EVENTS ----------------------- */
    +/*
    + * Currently we don't support events, so let's just plan for the
    + * future...
    + */
    +
    +/*
    + * A Wireless Event.
    + */
    +// How do we define short header ? We don't want a flag on length.
    +// Probably a flag on event ? Highest bit to zero...
    +struct iw_event
    +{
    + __u16 length; /* Lenght of this stuff */
    + __u16 event; /* Wireless IOCTL */
    + union iwreq_data header; /* IOCTL fixed payload */
    + char extra[0]; /* Optional IOCTL data */
    +};
    +
    +/* ---------------------- IOCTL DESCRIPTION ---------------------- */
    +/*
    + * One of the main goal of the new interface is to deal entirely with
    + * user space/kernel space memory move.
    + * For that, we need to know :
    + * o if iwreq is a pointer or contain the full data
    + * o what is the size of the data to copy
    + *
    + * For private IOCTLs, we use the same rules as used by iwpriv and
    + * defined in struct iw_priv_args.
    + *
    + * For standard IOCTLs, things are quite different and we need to
    + * use the stuctures below. Actually, this struct is also more
    + * efficient, but that's another story...
    + */
    +
    +/*
    + * Describe how a standard IOCTL looks like.
    + */
    +struct iw_ioctl_description
    +{
    + __u8 header_type; /* NULL, iw_point or other */
    + __u8 token_type; /* Future */
    + __u16 token_size; /* Granularity of payload */
    + __u16 min_tokens; /* Min acceptable token number */
    + __u16 max_tokens; /* Max acceptable token number */
    + __u32 flags; /* Special handling of the request */
    +};
    +
    +/* Need to think of short header translation table. Later. */
    +
    +/**************************** PROTOTYPES ****************************/
    +/*
    + * Functions part of the Wireless Extensions (defined in net/core/wireless.c).
    + * Those may be called only within the kernel.
    + */
    +
    +/* First : function strictly used inside the kernel */
    +
    +/* Handle /proc/net/wireless, called in net/code/dev.c */
    +extern int dev_get_wireless_info(char * buffer, char **start, off_t offset,
    + int length);
    +
    +/* Handle IOCTLs, called in net/code/dev.c */
    +extern int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd);
    +
    +/* Second : functions that may be called by driver modules */
    +/* None yet */
    +
    +#endif /* _LINUX_WIRELESS_H */
    diff -u -p -r --new-file linux/net/core-w12/Makefile linux/net/core/Makefile
    --- linux/net/core-w12/Makefile Thu Mar 28 15:34:30 2002
    +++ linux/net/core/Makefile Thu Mar 28 15:39:58 2002
    @@ -27,5 +27,8 @@ obj-$(CONFIG_NETFILTER) += netfilter.o
    obj-$(CONFIG_NET_DIVERT) += dv.o
    obj-$(CONFIG_NET_PROFILE) += profile.o
    obj-$(CONFIG_NET_PKTGEN) += pktgen.o
    +obj-$(CONFIG_NET_RADIO) += wireless.o
    +# Ugly. I wish all wireless drivers were moved in drivers/net/wireless
    +obj-$(CONFIG_NET_PCMCIA_RADIO) += wireless.o

    include $(TOPDIR)/Rules.make
    diff -u -p -r --new-file linux/net/core-w12/dev.c linux/net/core/dev.c
    --- linux/net/core-w12/dev.c Thu Mar 28 15:34:30 2002
    +++ linux/net/core/dev.c Thu Mar 28 15:37:46 2002
    @@ -102,6 +102,7 @@
    #include <linux/module.h>
    #if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO)
    #include <linux/wireless.h> /* Note : will define WIRELESS_EXT */
    +#include <net/iw_handler.h>
    #endif /* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */
    #ifdef CONFIG_PLIP
    extern int plip_init(void);
    @@ -1796,122 +1797,6 @@ static int dev_proc_stats(char *buffer,
    #endif /* CONFIG_PROC_FS */


    -#ifdef WIRELESS_EXT
    -#ifdef CONFIG_PROC_FS
    -
    -/*
    - * Print one entry of /proc/net/wireless
    - * This is a clone of /proc/net/dev (just above)
    - */
    -static int sprintf_wireless_stats(char *buffer, struct net_device *dev)
    -{
    - /* Get stats from the driver */
    - struct iw_statistics *stats = (dev->get_wireless_stats ?
    - dev->get_wireless_stats(dev) :
    - (struct iw_statistics *) NULL);
    - int size;
    -
    - if (stats != (struct iw_statistics *) NULL) {
    - size = sprintf(buffer,
    - "%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 & 1 ? '.' : ' ',
    - stats->qual.level,
    - stats->qual.updated & 2 ? '.' : ' ',
    - stats->qual.noise,
    - stats->qual.updated & 4 ? '.' : ' ',
    - stats->discard.nwid,
    - stats->discard.code,
    - stats->discard.fragment,
    - stats->discard.retries,
    - stats->discard.misc,
    - stats->miss.beacon);
    - stats->qual.updated = 0;
    - }
    - else
    - size = 0;
    -
    - return size;
    -}
    -
    -/*
    - * Print info for /proc/net/wireless (print all entries)
    - * This is a clone of /proc/net/dev (just above)
    - */
    -static int dev_get_wireless_info(char * buffer, char **start, off_t offset,
    - int length)
    -{
    - int len = 0;
    - off_t begin = 0;
    - off_t pos = 0;
    - int size;
    -
    - struct net_device * dev;
    -
    - size = sprintf(buffer,
    - "Inter-| sta-| Quality | Discarded packets | Missed\n"
    - " face | tus | link level noise | nwid crypt frag retry misc | beacon\n"
    - );
    -
    - pos += size;
    - len += size;
    -
    - read_lock(&dev_base_lock);
    - for (dev = dev_base; dev != NULL; dev = dev->next) {
    - size = sprintf_wireless_stats(buffer + len, dev);
    - len += size;
    - pos = begin + len;
    -
    - if (pos < offset) {
    - len = 0;
    - begin = pos;
    - }
    - if (pos > offset + length)
    - break;
    - }
    - read_unlock(&dev_base_lock);
    -
    - *start = buffer + (offset - begin); /* Start of wanted data */
    - len -= (offset - begin); /* Start slop */
    - if (len > length)
    - len = length; /* Ending slop */
    - if (len < 0)
    - len = 0;
    -
    - return len;
    -}
    -#endif /* CONFIG_PROC_FS */
    -
    -/*
    - * Allow programatic access to /proc/net/wireless even if /proc
    - * doesn't exist... Also more efficient...
    - */
    -static inline int dev_iwstats(struct net_device *dev, struct ifreq *ifr)
    -{
    - /* Get stats from the driver */
    - struct iw_statistics *stats = (dev->get_wireless_stats ?
    - dev->get_wireless_stats(dev) :
    - (struct iw_statistics *) NULL);
    -
    - if (stats != (struct iw_statistics *) NULL) {
    - struct iwreq * wrq = (struct iwreq *)ifr;
    -
    - /* Copy statistics to the user buffer */
    - if(copy_to_user(wrq->u.data.pointer, stats,
    - sizeof(struct iw_statistics)))
    - return -EFAULT;
    -
    - /* Check if we need to clear the update flag */
    - if(wrq->u.data.flags != 0)
    - stats->qual.updated = 0;
    - return(0);
    - } else
    - return -EOPNOTSUPP;
    -}
    -#endif /* WIRELESS_EXT */
    -
    /**
    * netdev_set_master - set up master/slave pair
    * @slave: slave device
    @@ -2209,11 +2094,6 @@ static int dev_ifsioc(struct ifreq *ifr,
    notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev);
    return 0;

    -#ifdef WIRELESS_EXT
    - case SIOCGIWSTATS:
    - return dev_iwstats(dev, ifr);
    -#endif /* WIRELESS_EXT */
    -
    /*
    * Unknown or private ioctl
    */
    @@ -2239,17 +2119,6 @@ static int dev_ifsioc(struct ifreq *ifr,
    return -EOPNOTSUPP;
    }

    -#ifdef WIRELESS_EXT
    - if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
    - if (dev->do_ioctl) {
    - if (!netif_device_present(dev))
    - return -ENODEV;
    - return dev->do_ioctl(dev, ifr, cmd);
    - }
    - return -EOPNOTSUPP;
    - }
    -#endif /* WIRELESS_EXT */
    -
    }
    return -EINVAL;
    }
    @@ -2431,7 +2300,8 @@ int dev_ioctl(unsigned int cmd, void *ar
    }
    dev_load(ifr.ifr_name);
    rtnl_lock();
    - ret = dev_ifsioc(&ifr, cmd);
    + /* Follow me in net/core/wireless.c */
    + ret = wireless_process_ioctl(&ifr, cmd);
    rtnl_unlock();
    if (!ret && IW_IS_GET(cmd) &&
    copy_to_user(arg, &ifr, sizeof(struct ifreq)))
    @@ -2856,6 +2726,7 @@ int __init net_dev_init(void)
    proc_net_create("dev", 0, dev_get_info);
    create_proc_read_entry("net/softnet_stat", 0, 0, dev_proc_stats, NULL);
    #ifdef WIRELESS_EXT
    + /* Available in net/core/wireless.c */
    proc_net_create("wireless", 0, dev_get_wireless_info);
    #endif /* WIRELESS_EXT */
    #endif /* CONFIG_PROC_FS */
    diff -u -p -r --new-file linux/net/core-w12/wireless.c linux/net/core/wireless.c
    --- linux/net/core-w12/wireless.c Wed Dec 31 16:00:00 1969
    +++ linux/net/core/wireless.c Thu Mar 28 15:37:46 2002
    @@ -0,0 +1,733 @@
    +/*
    + * This file implement the Wireless Extensions APIs.
    + *
    + * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
    + * Copyright (c) 1997-2001 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
    + */
    +
    +/***************************** INCLUDES *****************************/
    +
    +#include <asm/uaccess.h> /* copy_to_user() */
    +#include <linux/config.h> /* Not needed ??? */
    +#include <linux/types.h> /* off_t */
    +#include <linux/netdevice.h> /* struct ifreq, dev_get_by_name() */
    +
    +#include <linux/wireless.h> /* Pretty obvious */
    +#include <net/iw_handler.h> /* New driver API */
    +
    +/**************************** CONSTANTS ****************************/
    +
    +/* This will be turned on later on... */
    +#undef WE_STRICT_WRITE /* Check write buffer size */
    +
    +/* Debuging stuff */
    +#undef WE_IOCTL_DEBUG /* Debug IOCTL API */
    +
    +/************************* GLOBAL VARIABLES *************************/
    +/*
    + * You should not use global variables, because or re-entrancy.
    + * On our case, it's only const, so it's OK...
    + */
    +static const struct iw_ioctl_description standard_ioctl[] = {
    + /* SIOCSIWCOMMIT (internal) */
    + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
    + /* SIOCGIWNAME */
    + { IW_HEADER_TYPE_CHAR, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP},
    + /* SIOCSIWNWID */
    + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, IW_DESCR_FLAG_EVENT},
    + /* SIOCGIWNWID */
    + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP},
    + /* SIOCSIWFREQ */
    + { IW_HEADER_TYPE_FREQ, 0, 0, 0, 0, IW_DESCR_FLAG_EVENT},
    + /* SIOCGIWFREQ */
    + { IW_HEADER_TYPE_FREQ, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP},
    + /* SIOCSIWMODE */
    + { IW_HEADER_TYPE_UINT, 0, 0, 0, 0, IW_DESCR_FLAG_EVENT},
    + /* SIOCGIWMODE */
    + { IW_HEADER_TYPE_UINT, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP},
    + /* SIOCSIWSENS */
    + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
    + /* SIOCGIWSENS */
    + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
    + /* SIOCSIWRANGE */
    + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
    + /* SIOCGIWRANGE */
    + { IW_HEADER_TYPE_POINT, 0, 1, 0, sizeof(struct iw_range), IW_DESCR_FLAG_DUMP},
    + /* SIOCSIWPRIV */
    + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
    + /* SIOCGIWPRIV (handled directly by us) */
    + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
    + /* SIOCSIWSTATS */
    + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
    + /* SIOCGIWSTATS (handled directly by us) */
    + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP},
    + /* SIOCSIWSPY */
    + { IW_HEADER_TYPE_POINT, 0, sizeof(struct sockaddr), 0, IW_MAX_SPY, 0},
    + /* SIOCGIWSPY */
    + { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_SPY, 0},
    + /* -- hole -- */
    + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
    + /* -- hole -- */
    + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
    + /* SIOCSIWAP */
    + { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0},
    + /* SIOCGIWAP */
    + { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP},
    + /* -- hole -- */
    + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
    + /* SIOCGIWAPLIST */
    + { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_AP, 0},
    + /* -- hole -- */
    + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
    + /* -- hole -- */
    + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
    + /* SIOCSIWESSID */
    + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, IW_DESCR_FLAG_EVENT},
    + /* SIOCGIWESSID */
    + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, IW_DESCR_FLAG_DUMP},
    + /* SIOCSIWNICKN */
    + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, 0},
    + /* SIOCGIWNICKN */
    + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, 0},
    + /* -- hole -- */
    + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
    + /* -- hole -- */
    + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0},
    + /* SIOCSIWRATE */
    + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
    + /* SIOCGIWRATE */
    + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
    + /* SIOCSIWRTS */
    + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
    + /* SIOCGIWRTS */
    + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
    + /* SIOCSIWFRAG */
    + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
    + /* SIOCGIWFRAG */
    + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
    + /* SIOCSIWTXPOW */
    + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
    + /* SIOCGIWTXPOW */
    + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
    + /* SIOCSIWRETRY */
    + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
    + /* SIOCGIWRETRY */
    + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
    + /* SIOCSIWENCODE */
    + { IW_HEADER_TYPE_POINT, 4, 1, 0, IW_ENCODING_TOKEN_MAX, IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT},
    + /* SIOCGIWENCODE */
    + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ENCODING_TOKEN_MAX, IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT},
    + /* SIOCSIWPOWER */
    + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
    + /* SIOCGIWPOWER */
    + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0},
    +};
    +
    +/* Size (in bytes) of the various private data types */
    +char priv_type_size[] = { 0, 1, 1, 0, 4, 4, 0, 0 };
    +
    +/************************ 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)
    +{
    + 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)
    +{
    + return (dev->get_wireless_stats ?
    + dev->get_wireless_stats(dev) :
    + (struct iw_statistics *) NULL);
    + /* In the future, get_wireless_stats may move from 'struct net_device'
    + * to 'struct iw_handler_def', to de-bloat struct net_device.
    + * Definitely worse a thought... */
    +}
    +
    +/* ---------------------------------------------------------------- */
    +/*
    + * 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 */
    +}
    +
    +/* ---------------------------------------------------------------- */
    +/*
    + * Number 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 * priv_type_size[type];
    +}
    +
    +
    +/******************** /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 int sprintf_wireless_stats(char *buffer, struct net_device *dev)
    +{
    + /* Get stats from the driver */
    + struct iw_statistics *stats;
    + int size;
    +
    + stats = get_wireless_stats(dev);
    + if (stats != (struct iw_statistics *) NULL) {
    + size = sprintf(buffer,
    + "%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 & 1 ? '.' : ' ',
    + stats->qual.level,
    + stats->qual.updated & 2 ? '.' : ' ',
    + stats->qual.noise,
    + stats->qual.updated & 4 ? '.' : ' ',
    + stats->discard.nwid,
    + stats->discard.code,
    + stats->discard.fragment,
    + stats->discard.retries,
    + stats->discard.misc,
    + stats->miss.beacon);
    + stats->qual.updated = 0;
    + }
    + else
    + size = 0;
    +
    + return size;
    +}
    +
    +/* ---------------------------------------------------------------- */
    +/*
    + * Print info for /proc/net/wireless (print all entries)
    + */
    +int dev_get_wireless_info(char * buffer, char **start, off_t offset,
    + int length)
    +{
    + int len = 0;
    + off_t begin = 0;
    + off_t pos = 0;
    + int size;
    +
    + struct net_device * dev;
    +
    + size = sprintf(buffer,
    + "Inter-| sta-| Quality | Discarded packets | Missed\n"
    + " face | tus | link level noise | nwid crypt frag retry misc | beacon\n"
    + );
    +
    + pos += size;
    + len += size;
    +
    + read_lock(&dev_base_lock);
    + for (dev = dev_base; dev != NULL; dev = dev->next) {
    + size = sprintf_wireless_stats(buffer + len, dev);
    + len += size;
    + pos = begin + len;
    +
    + if (pos < offset) {
    + len = 0;
    + begin = pos;
    + }
    + if (pos > offset + length)
    + break;
    + }
    + read_unlock(&dev_base_lock);
    +
    + *start = buffer + (offset - begin); /* Start of wanted data */
    + len -= (offset - begin); /* Start slop */
    + if (len > length)
    + len = length; /* Ending slop */
    + if (len < 0)
    + len = 0;
    +
    + return len;
    +}
    +#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.
    + */
    +
    +/* ---------------------------------------------------------------- */
    +/*
    + * Allow programatic access to /proc/net/wireless even if /proc
    + * doesn't exist... Also more efficient...
    + */
    +static inline int dev_iwstats(struct net_device *dev, struct ifreq *ifr)
    +{
    + /* Get stats from the driver */
    + struct iw_statistics *stats;
    +
    + stats = get_wireless_stats(dev);
    + if (stats != (struct iw_statistics *) NULL) {
    + struct iwreq * wrq = (struct iwreq *)ifr;
    +
    + /* Copy statistics to the user buffer */
    + if(copy_to_user(wrq->u.data.pointer, stats,
    + sizeof(struct iw_statistics)))
    + return -EFAULT;
    +
    + /* Check if we need to clear the update flag */
    + if(wrq->u.data.flags != 0)
    + stats->qual.updated = 0;
    + return 0;
    + } else
    + return -EOPNOTSUPP;
    +}
    +
    +/* ---------------------------------------------------------------- */
    +/*
    + * Export the driver private handler definition
    + * They will be picked up by tools like iwpriv...
    + */
    +static inline int ioctl_export_private(struct net_device * dev,
    + struct ifreq * ifr)
    +{
    + struct iwreq * iwr = (struct iwreq *) ifr;
    +
    + /* 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 NULL pointer */
    + if(iwr->u.data.pointer == NULL)
    + return -EFAULT;
    +#ifdef WE_STRICT_WRITE
    + /* Check if there is enough buffer up there */
    + if(iwr->u.data.length < (SIOCIWLASTPRIV - SIOCIWFIRSTPRIV + 1))
    + return -E2BIG;
    +#endif /* WE_STRICT_WRITE */
    +
    + /* Set the number of available ioctls. */
    + iwr->u.data.length = dev->wireless_handlers->num_private_args;
    +
    + /* Copy structure to the user buffer. */
    + if (copy_to_user(iwr->u.data.pointer,
    + dev->wireless_handlers->private_args,
    + sizeof(struct iw_priv_args) * iwr->u.data.length))
    + return -EFAULT;
    +
    + return 0;
    +}
    +
    +/* ---------------------------------------------------------------- */
    +/*
    + * 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 inline 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 */
    + descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
    +
    +#ifdef WE_IOCTL_DEBUG
    + printk(KERN_DEBUG "%s : Found standard handler for 0x%04X\n",
    + ifr->ifr_name, cmd);
    + printk(KERN_DEBUG "Header type : %d, token type : %d, token_size : %d, max_token : %d\n", 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);
    + } 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;
    + /* 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;
    +#ifdef WE_STRICT_WRITE
    + /* Check if there is enough buffer up there */
    + if(iwr->u.data.length < descr->max_tokens)
    + return -E2BIG;
    +#endif /* WE_STRICT_WRITE */
    + }
    +
    +#ifdef WE_IOCTL_DEBUG
    + printk(KERN_DEBUG "Malloc %d bytes\n",
    + descr->max_tokens * descr->token_size);
    +#endif /* WE_IOCTL_DEBUG */
    +
    + /* Always allocate for max space. Easier, and won't last
    + * long... */
    + extra = kmalloc(descr->max_tokens * descr->token_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 "Got %d bytes\n",
    + iwr->u.data.length * descr->token_size);
    +#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)) {
    + 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 "Wrote %d bytes\n",
    + iwr->u.data.length * descr->token_size);
    +#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);
    +
    + /* 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;
    + 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 : Found private handler for 0x%04X\n",
    + ifr->ifr_name, cmd);
    + if(descr) {
    + printk(KERN_DEBUG "Name %s, set %X, get %X\n",
    + 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)) {
    + /* 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 < IFNAMSIZ))
    + extra_size = 0;
    + } else {
    + /* Size of set 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 "Malloc %d bytes\n", 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 "Got %d elem\n", 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)) {
    + err = copy_to_user(iwr->u.data.pointer, extra,
    + extra_size);
    + if (err)
    + ret = -EFAULT;
    +#ifdef WE_IOCTL_DEBUG
    + printk(KERN_DEBUG "Wrote %d elem\n",
    + 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 dev_iwstats(dev, ifr);
    +
    + 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_export_private(dev, ifr);
    + }
    + // ## 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;
    +}
    \
     
     \ /
      Last update: 2005-03-22 13:25    [W:0.124 / U:0.116 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site