diff -u -p --new-file linux/include/linux.w10/netdevice.h linux/include/linux/netdevice.h --- linux/include/linux.w10/netdevice.h Mon Apr 14 13:53:05 2003 +++ linux/include/linux/netdevice.h Mon Apr 14 17:53:35 2003 @@ -226,6 +226,10 @@ struct device struct net_device_stats* (*get_stats)(struct device *dev); struct iw_statistics* (*get_wireless_stats)(struct device *dev); + /* List of functions to handle Wireless Extensions (instead of ioctl). + * See 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 --new-file linux/include/linux.w10/wireless.h linux/include/linux/wireless.h --- linux/include/linux.w10/wireless.h Mon Apr 14 13:53:12 2003 +++ linux/include/linux/wireless.h Mon Apr 14 17:51:58 2003 @@ -1,9 +1,10 @@ /* * This file define a set of standard wireless extensions * - * Version : 9 16.10.99 + * Version : 15 12.7.02 * * Authors : Jean Tourrilhes - HPL - + * Copyright (c) 1997-2002 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,32 @@ * 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 (2002 -> 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 + * + * Wireless Events (2002 -> onward) : + * -------------------------------- + * Events are defined at the end of this file, and implemented 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 +73,14 @@ #include /* for "struct sockaddr" et al */ #include /* 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 10 +#define WIRELESS_EXT 15 /* * Changes : @@ -111,15 +128,57 @@ * - Add PM modifier : MAX/MIN/RELATIVE * - Add encoding option : IW_ENCODE_NOKEY * - Add TxPower ioctls (work like TxRate) + * + * V10 to V11 + * ---------- + * - Add WE version in range (help backward/forward compatibility) + * - Add retry ioctls (work like PM) + * + * V11 to V12 + * ---------- + * - Add SIOCSIWSTATS to get /proc/net/wireless programatically + * - 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 + * + * V13 to V14 + * ---------- + * - Wireless Events support : define struct iw_event + * - Define additional specific event numbers + * - Add "addr" and "param" fields in union iwreq_data + * - AP scanning stuff (SIOCSIWSCAN and friends) + * + * V14 to V15 + * ---------- + * - Add IW_PRIV_TYPE_ADDR for struct sockaddr private arg + * - Make struct iw_freq signed (both m & e), add explicit padding + * - Add IWEVCUSTOM for driver specific event/scanning token + * - Add IW_MAX_GET_SPY for driver returning a lot of addresses + * - Add IW_TXPOW_RANGE for range of Tx Powers + * - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points + * - Add IW_MODE_MONITOR for passive monitor */ +/**************************** CONSTANTS ****************************/ + /* -------------------------- IOCTL LIST -------------------------- */ -/* Basic operations */ -#define SIOCSIWNAME 0x8B00 /* Unused */ +/* Wireless Identification */ +#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 */ +/* SIOCGIWNAME is used to verify the presence of Wireless Extensions. + * Common values : "IEEE 802.11-DS", "IEEE 802.11-FH", "IEEE 802.11b"... + * Don't put the name of your driver there, it's useless. */ + +/* Basic operations */ +#define SIOCSIWNWID 0x8B02 /* set network id (pre-802.11) */ +#define SIOCGIWNWID 0x8B03 /* get network id (the cell) */ #define SIOCSIWFREQ 0x8B04 /* set channel/frequency (Hz) */ #define SIOCGIWFREQ 0x8B05 /* get channel/frequency (Hz) */ #define SIOCSIWMODE 0x8B06 /* set operation mode */ @@ -132,15 +191,21 @@ #define SIOCGIWRANGE 0x8B0B /* Get range of parameters */ #define SIOCSIWPRIV 0x8B0C /* Unused */ #define SIOCGIWPRIV 0x8B0D /* get private ioctl interface info */ +#define SIOCSIWSTATS 0x8B0E /* Unused */ +#define SIOCGIWSTATS 0x8B0F /* Get /proc/net/wireless stats */ +/* SIOCGIWSTATS is strictly used between user space and the kernel, and + * is never passed to the driver (i.e. the driver will never see it). */ -/* Mobile IP support */ +/* Mobile IP support (statistics per MAC address) */ #define SIOCSIWSPY 0x8B10 /* set spy addresses */ #define SIOCGIWSPY 0x8B11 /* get spy info (quality of link) */ /* Access Point manipulation */ #define SIOCSIWAP 0x8B14 /* set access point MAC addresses */ #define SIOCGIWAP 0x8B15 /* get access point MAC addresses */ -#define SIOCGIWAPLIST 0x8B17 /* get list of access point in range */ +#define SIOCGIWAPLIST 0x8B17 /* Deprecated in favor of scanning */ +#define SIOCSIWSCAN 0x8B18 /* trigger scanning (list cells) */ +#define SIOCGIWSCAN 0x8B19 /* get scanning results */ /* 802.11 specific support */ #define SIOCSIWESSID 0x8B1A /* set ESSID (network name) */ @@ -149,11 +214,9 @@ #define SIOCGIWNICKN 0x8B1D /* get node name/nickname */ /* As the ESSID and NICKN are strings up to 32 bytes long, it doesn't fit * within the 'iwreq' structure, so we need to use the 'data' member to - * point to a string in user space, like it is done for RANGE... - * The "flags" member indicate if the ESSID is active or not (promiscuous). - */ + * point to a string in user space, like it is done for RANGE... */ -/* Other parameters usefull in 802.11 and some other devices */ +/* Other parameters useful in 802.11 and some other devices */ #define SIOCSIWRATE 0x8B20 /* set default bit rate (bps) */ #define SIOCGIWRATE 0x8B21 /* get default bit rate (bps) */ #define SIOCSIWRTS 0x8B22 /* set RTS/CTS threshold (bytes) */ @@ -162,6 +225,8 @@ #define SIOCGIWFRAG 0x8B25 /* get fragmentation thr (bytes) */ #define SIOCSIWTXPOW 0x8B26 /* set transmit power (dBm) */ #define SIOCGIWTXPOW 0x8B27 /* get transmit power (dBm) */ +#define SIOCSIWRETRY 0x8B28 /* set retry limits and lifetime */ +#define SIOCGIWRETRY 0x8B29 /* get retry limits and lifetime */ /* Encoding stuff (scrambling, hardware security, WEP...) */ #define SIOCSIWENCODE 0x8B2A /* set encoding token & mode */ @@ -170,21 +235,55 @@ #define SIOCSIWPOWER 0x8B2C /* set Power Management settings */ #define SIOCGIWPOWER 0x8B2D /* get Power Management settings */ +/* -------------------- DEV PRIVATE IOCTL LIST -------------------- */ + +/* These 16 ioctl are wireless device private. + * Each driver is free to use them for whatever purpose it chooses, + * however the driver *must* export the description of those ioctls + * with SIOCGIWPRIV and *must* use arguments as defined below. + * If you don't follow those rules, DaveM is going to hate you (reason : + * it make mixed 32/64bit operation impossible). + */ +#define SIOCIWFIRSTPRIV 0x8BE0 +#define SIOCIWLASTPRIV 0x8BFF +/* Previously, we were using SIOCDEVPRIVATE, but we now have our + * separate range because of collisions with other tools such as + * 'mii-tool'. + * We now have 32 commands, so a bit more space ;-). + * Also, all 'odd' commands are only usable by root and don't return the + * content of ifr/iwr to user (but you are not obliged to use the set/get + * convention, just use every other two command). + * And I repeat : you are not obliged to use them with iwspy, but you + * must be compliant with it. + */ + /* ------------------------- IOCTL STUFF ------------------------- */ /* The first and the last (range) */ #define SIOCIWFIRST 0x8B00 -#define SIOCIWLAST 0x8B30 +#define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */ /* Even : get (world access), odd : set (root access) */ #define IW_IS_SET(cmd) (!((cmd) & 0x1)) #define IW_IS_GET(cmd) ((cmd) & 0x1) +/* ----------------------- WIRELESS EVENTS ----------------------- */ +/* Those are *NOT* ioctls, do not issue request on them !!! */ +/* Most events use the same identifier as ioctl requests */ + +#define IWEVTXDROP 0x8C00 /* Packet dropped to excessive retry */ +#define IWEVQUAL 0x8C01 /* Quality part of statistics (scan) */ +#define IWEVCUSTOM 0x8C02 /* Driver specific ascii string */ +#define IWEVREGISTERED 0x8C03 /* Discovered a new node (AP mode) */ +#define IWEVEXPIRED 0x8C04 /* Expired a node (AP mode) */ + +#define IWEVFIRST 0x8C00 + /* ------------------------- PRIVATE INFO ------------------------- */ /* * The following is used with SIOCGIWPRIV. It allow a driver to define * the interface (name, type of data) for its private ioctl. - * Privates ioctl are SIOCDEVPRIVATE -> SIOCDEVPRIVATE + 0xF + * Privates ioctl are SIOCIWFIRSTPRIV -> SIOCIWLASTPRIV */ #define IW_PRIV_TYPE_MASK 0x7000 /* Type of arguments */ @@ -192,9 +291,10 @@ #define IW_PRIV_TYPE_BYTE 0x1000 /* Char as number */ #define IW_PRIV_TYPE_CHAR 0x2000 /* Char as character */ #define IW_PRIV_TYPE_INT 0x4000 /* 32 bits int */ -#define IW_PRIV_TYPE_FLOAT 0x5000 +#define IW_PRIV_TYPE_FLOAT 0x5000 /* struct iw_freq */ +#define IW_PRIV_TYPE_ADDR 0x6000 /* struct sockaddr */ -#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed nuber of args */ +#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed number of args */ #define IW_PRIV_SIZE_MASK 0x07FF /* Max number of those args */ @@ -216,9 +316,12 @@ /* Maximum tx powers in the range struct */ #define IW_MAX_TXPOWER 8 +/* Note : if you more than 8 TXPowers, just set the max and min or + * a few of them in the struct iw_range. */ /* Maximum of address that you may set with SPY */ -#define IW_MAX_SPY 8 +#define IW_MAX_SPY 8 /* set */ +#define IW_MAX_GET_SPY 64 /* get */ /* Maximum of address that you may get in the list of access points in range */ @@ -234,6 +337,7 @@ #define IW_MODE_MASTER 3 /* Synchronisation master or Access Point */ #define IW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */ #define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */ +#define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */ /* Maximum number of size of encoding token available * they are listed in the range structure */ @@ -269,8 +373,36 @@ #define IW_POWER_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ /* Transmit Power flags available */ +#define IW_TXPOW_TYPE 0x00FF /* Type of value */ #define IW_TXPOW_DBM 0x0000 /* Value is in dBm */ #define IW_TXPOW_MWATT 0x0001 /* Value is in mW */ +#define IW_TXPOW_RANGE 0x1000 /* Range of value between min/max */ + +/* Retry limits and lifetime flags available */ +#define IW_RETRY_ON 0x0000 /* No details... */ +#define IW_RETRY_TYPE 0xF000 /* Type of parameter */ +#define IW_RETRY_LIMIT 0x1000 /* Maximum number of retries*/ +#define IW_RETRY_LIFETIME 0x2000 /* Maximum duration of retries in us */ +#define IW_RETRY_MODIFIER 0x000F /* Modify a parameter */ +#define IW_RETRY_MIN 0x0001 /* Value is a minimum */ +#define IW_RETRY_MAX 0x0002 /* Value is a maximum */ +#define IW_RETRY_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ + +/* Scanning request flags */ +#define IW_SCAN_DEFAULT 0x0000 /* Default scan of the driver */ +#define IW_SCAN_ALL_ESSID 0x0001 /* Scan all ESSIDs */ +#define IW_SCAN_THIS_ESSID 0x0002 /* Scan only this ESSID */ +#define IW_SCAN_ALL_FREQ 0x0004 /* Scan all Frequencies */ +#define IW_SCAN_THIS_FREQ 0x0008 /* Scan only this Frequency */ +#define IW_SCAN_ALL_MODE 0x0010 /* Scan all Modes */ +#define IW_SCAN_THIS_MODE 0x0020 /* Scan only this Mode */ +#define IW_SCAN_ALL_RATE 0x0040 /* Scan all Bit-Rates */ +#define IW_SCAN_THIS_RATE 0x0080 /* Scan only this Bit-Rate */ +/* Maximum size of returned data */ +#define IW_SCAN_MAX_DATA 4096 /* In bytes */ + +/* Max number of char in custom event - use multiple of them if needed */ +#define IW_CUSTOM_MAX 256 /* In bytes */ /****************************** TYPES ******************************/ @@ -288,7 +420,7 @@ struct iw_param /* * For all data larger than 16 octets, we need to use a - * pointer to memory alocated in user space. + * pointer to memory allocated in user space. */ struct iw_point { @@ -307,9 +439,10 @@ struct iw_point */ struct iw_freq { - __u32 m; /* Mantissa */ - __u16 e; /* Exponent */ + __s32 m; /* Mantissa */ + __s16 e; /* Exponent */ __u8 i; /* List index (when in range struct) */ + __u8 pad; /* Unused - just for alignement */ }; /* @@ -317,23 +450,38 @@ struct iw_freq */ struct iw_quality { - __u8 qual; /* link quality (%retries, SNR or better...) */ - __u8 level; /* signal level */ - __u8 noise; /* noise level */ + __u8 qual; /* link quality (%retries, SNR, + %missed beacons or better...) */ + __u8 level; /* signal level (dBm) */ + __u8 noise; /* noise level (dBm) */ __u8 updated; /* Flags to know if updated */ }; /* * Packet discarded in the wireless adapter due to * "wireless" specific problems... + * Note : the list of counter and statistics in net_device_stats + * is already pretty exhaustive, and you should use that first. + * This is only additional stats... */ struct iw_discarded { - __u32 nwid; /* Wrong nwid */ - __u32 code; /* Unable to code/decode */ + __u32 nwid; /* Rx : Wrong nwid/essid */ + __u32 code; /* Rx : Unable to code/decode (WEP) */ + __u32 fragment; /* Rx : Can't perform MAC reassembly */ + __u32 retries; /* Tx : Max MAC retries num reached */ __u32 misc; /* Others cases */ }; +/* + * Packet/Time period missed in the wireless adapter due to + * "wireless" specific problems... + */ +struct iw_missed +{ + __u32 beacon; /* Missed beacons/superframe */ +}; + /* ------------------------ WIRELESS STATS ------------------------ */ /* * Wireless statistics (used for /proc/net/wireless) @@ -346,17 +494,57 @@ struct iw_statistics struct iw_quality qual; /* Quality of the link * (instant/mean/max) */ struct iw_discarded discard; /* Packet discarded counts */ + struct iw_missed miss; /* Packet missed counts */ }; /* ------------------------ 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 iw_quality qual; /* Quality part of statistics */ + + struct sockaddr ap_addr; /* Access point address */ + struct sockaddr addr; /* Destination address (hw) */ + + struct iw_param param; /* Other small parameters */ + 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 { @@ -365,34 +553,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_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 -------------------------- */ @@ -462,6 +624,32 @@ struct iw_range __u16 txpower_capa; /* What options are supported */ __u8 num_txpower; /* Number of entries in the list */ __s32 txpower[IW_MAX_TXPOWER]; /* list, in bps */ + + /* Wireless Extension version info */ + __u8 we_version_compiled; /* Must be WIRELESS_EXT */ + __u8 we_version_source; /* Last update of source */ + + /* Retry limits and lifetime */ + __u16 retry_capa; /* What retry options are supported */ + __u16 retry_flags; /* How to decode max/min retry limit */ + __u16 r_time_flags; /* How to decode max/min retry life */ + __s32 min_retry; /* Minimal number of retries */ + __s32 max_retry; /* Maximal number of retries */ + __s32 min_r_time; /* Minimal retry lifetime */ + __s32 max_r_time; /* Maximal retry lifetime */ + + /* Average quality of link & SNR */ + struct iw_quality avg_qual; /* Quality of the link */ + /* This should contain the average/typical values of the quality + * indicator. This should be the threshold between a "good" and + * a "bad" link (example : monitor going from green to orange). + * Currently, user space apps like quality monitors don't have any + * way to calibrate the measurement. With this, they can split + * the range between 0 and max_qual in different quality level + * (using a geometric subdivision centered on the average). + * I expect that people doing the user space apps will feedback + * us on which value we need to put in each driver... + */ }; /* @@ -475,5 +663,36 @@ struct iw_priv_args __u16 get_args; /* Type and number of args */ char name[IFNAMSIZ]; /* Name of the extension */ }; + +/* ----------------------- WIRELESS EVENTS ----------------------- */ +/* + * Wireless events are carried through the rtnetlink socket to user + * space. They are encapsulated in the IFLA_WIRELESS field of + * a RTM_NEWLINK message. + */ + +/* + * A Wireless Event. Contains basically the same data as the ioctl... + */ +struct iw_event +{ + __u16 len; /* Real lenght of this stuff */ + __u16 cmd; /* Wireless IOCTL */ + union iwreq_data u; /* IOCTL fixed payload */ +}; + +/* Size of the Event prefix (including padding and alignement junk) */ +#define IW_EV_LCP_LEN (sizeof(struct iw_event) - sizeof(union iwreq_data)) +/* Size of the various events */ +#define IW_EV_CHAR_LEN (IW_EV_LCP_LEN + IFNAMSIZ) +#define IW_EV_UINT_LEN (IW_EV_LCP_LEN + sizeof(__u32)) +#define IW_EV_FREQ_LEN (IW_EV_LCP_LEN + sizeof(struct iw_freq)) +#define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point)) +#define IW_EV_PARAM_LEN (IW_EV_LCP_LEN + sizeof(struct iw_param)) +#define IW_EV_ADDR_LEN (IW_EV_LCP_LEN + sizeof(struct sockaddr)) +#define IW_EV_QUAL_LEN (IW_EV_LCP_LEN + sizeof(struct iw_quality)) + +/* Note : in the case of iw_point, the extra data will come at the + * end of the event */ #endif /* _LINUX_WIRELESS_H */ diff -u -p --new-file linux/include/net.w10/iw_handler.h linux/include/net/iw_handler.h --- linux/include/net.w10/iw_handler.h Wed Dec 31 16:00:00 1969 +++ linux/include/net/iw_handler.h Mon Apr 14 17:58:01 2003 @@ -0,0 +1,453 @@ +/* + * This file define the new driver API for Wireless Extensions + * + * Version : 4 21.6.02 + * + * Authors : Jean Tourrilhes - HPL - + * Copyright (c) 2001-2002 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 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 (2002 -> 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 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 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 + * --------------------------------- + * 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 /* 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 4 + +/* + * Changes : + * + * V2 to V3 + * -------- + * - Move event definition in + * - Add Wireless Event support : + * o wireless_send_event() prototype + * o iwe_stream_add_event/point() inline functions + * V3 to V4 + * -------- + * - Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes + */ + +/**************************** 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_ADDR 6 /* struct sockaddr */ +#define IW_HEADER_TYPE_POINT 8 /* struct iw_point */ +#define IW_HEADER_TYPE_PARAM 9 /* struct iw_param */ +#define IW_HEADER_TYPE_QUAL 10 /* struct iw_quality */ + +/* 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 */ + /* SET : Omit payload from generated iwevent */ +/* 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 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 device in + */ +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 device' to here, to minimise bloat. */ +}; + +/* ---------------------- 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, int dummy); + +/* 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 */ + +/* Send a single event to user space */ +extern void wireless_send_event(struct device * dev, + unsigned int cmd, + union iwreq_data * wrqu, + char * extra); + +/* We may need a function to send a stream of events to user space. + * More on that later... */ + +/************************* INLINE FUNTIONS *************************/ +/* + * Function that are so simple that it's more efficient inlining them + */ + +/*------------------------------------------------------------------*/ +/* + * Wrapper to add an Wireless Event to a stream of events. + */ +static inline char * +iwe_stream_add_event(char * stream, /* Stream of events */ + char * ends, /* End of stream */ + struct iw_event *iwe, /* Payload */ + int event_len) /* Real size of payload */ +{ + /* Check if it's possible */ + if((stream + event_len) < ends) { + iwe->len = event_len; + memcpy(stream, (char *) iwe, event_len); + stream += event_len; + } + return stream; +} + +/*------------------------------------------------------------------*/ +/* + * Wrapper to add an short Wireless Event containing a pointer to a + * stream of events. + */ +static inline char * +iwe_stream_add_point(char * stream, /* Stream of events */ + char * ends, /* End of stream */ + struct iw_event *iwe, /* Payload */ + char * extra) +{ + int event_len = IW_EV_POINT_LEN + iwe->u.data.length; + /* Check if it's possible */ + if((stream + event_len) < ends) { + iwe->len = event_len; + memcpy(stream, (char *) iwe, IW_EV_POINT_LEN); + memcpy(stream + IW_EV_POINT_LEN, extra, iwe->u.data.length); + stream += event_len; + } + return stream; +} + +/*------------------------------------------------------------------*/ +/* + * Wrapper to add a value to a Wireless Event in a stream of events. + * Be careful, this one is tricky to use properly : + * At the first run, you need to have (value = event + IW_EV_LCP_LEN). + */ +static inline char * +iwe_stream_add_value(char * event, /* Event in the stream */ + char * value, /* Value in event */ + char * ends, /* End of stream */ + struct iw_event *iwe, /* Payload */ + int event_len) /* Real size of payload */ +{ + /* Don't duplicate LCP */ + event_len -= IW_EV_LCP_LEN; + + /* Check if it's possible */ + if((value + event_len) < ends) { + /* Add new value */ + memcpy(value, (char *) iwe + IW_EV_LCP_LEN, event_len); + value += event_len; + /* Patch LCP */ + iwe->len = value - event; + memcpy(event, (char *) iwe, IW_EV_LCP_LEN); + } + return value; +} + +#endif /* _IW_HANDLER_H */ diff -u -p --new-file linux/net/core.w10/Makefile linux/net/core/Makefile --- linux/net/core.w10/Makefile Sun Mar 25 08:31:11 2001 +++ linux/net/core/Makefile Mon Apr 14 14:36:36 2003 @@ -39,6 +39,10 @@ ifdef CONFIG_NET_PROFILE OX_OBJS += profile.o endif +ifdef CONFIG_NET_RADIO +OX_OBJS += wireless.o +endif + include $(TOPDIR)/Rules.make tar: diff -u -p --new-file linux/net/core.w10/dev.c linux/net/core/dev.c --- linux/net/core.w10/dev.c Mon May 20 16:32:35 2002 +++ linux/net/core/dev.c Mon Apr 14 14:45:41 2003 @@ -92,7 +92,8 @@ #include #include #ifdef CONFIG_NET_RADIO -#include +#include /* Note : will define WIRELESS_EXT */ +#include #endif /* CONFIG_NET_RADIO */ #ifdef CONFIG_PLIP extern int plip_init(void); @@ -1306,91 +1307,6 @@ static int dev_proc_stats(char *buffer, #endif /* CONFIG_PROC_FS */ -#ifdef CONFIG_NET_RADIO -#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 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\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.misc); - 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) - */ -int dev_get_wireless_info(char * buffer, char **start, off_t offset, - int length, int dummy) -{ - int len = 0; - off_t begin = 0; - off_t pos = 0; - int size; - - struct device * dev; - - size = sprintf(buffer, - "Inter-| sta-| Quality | Discarded packets\n" - " face | tus | link level noise | nwid crypt misc\n" - ); - - pos+=size; - len+=size; - - 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; - } - - *start = buffer + (offset - begin); /* Start of wanted data */ - len -= (offset - begin); /* Start slop */ - if(len > length) - len = length; /* Ending slop */ - - return len; -} -#endif /* CONFIG_PROC_FS */ -#endif /* CONFIG_NET_RADIO */ - void dev_set_promiscuity(struct device *dev, int inc) { unsigned short old_flags = dev->flags; @@ -1621,14 +1537,6 @@ static int dev_ifsioc(struct ifreq *ifr, return -EOPNOTSUPP; } -#ifdef CONFIG_NET_RADIO - if(cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { - if (dev->do_ioctl) - return dev->do_ioctl(dev, ifr, cmd); - return -EOPNOTSUPP; - } -#endif /* CONFIG_NET_RADIO */ - } return -EINVAL; } @@ -1749,23 +1657,27 @@ int dev_ioctl(unsigned int cmd, void *ar return -EFAULT; return ret; } -#ifdef CONFIG_NET_RADIO +#ifdef WIRELESS_EXT + /* Take care of Wireless Extensions */ if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { - dev_load(ifr.ifr_name); - if (IW_IS_SET(cmd)) { - if (!suser()) + /* If command is `set a parameter', or + * `get the encoding parameters', check if + * the user has the right to do it */ + if (IW_IS_SET(cmd) || (cmd == SIOCGIWENCODE)) { + if(!capable(CAP_NET_ADMIN)) return -EPERM; - rtnl_lock(); } - ret = dev_ifsioc(&ifr, cmd); - if (IW_IS_SET(cmd)) - rtnl_unlock(); + dev_load(ifr.ifr_name); + rtnl_lock(); + /* 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))) return -EFAULT; return ret; } -#endif /* CONFIG_NET_RADIO */ +#endif /* WIRELESS_EXT */ return -EINVAL; } } @@ -1938,16 +1850,16 @@ static struct proc_dir_entry proc_net_de }; #endif -#ifdef CONFIG_NET_RADIO +#ifdef WIRELESS_EXT #ifdef CONFIG_PROC_FS static struct proc_dir_entry proc_net_wireless = { PROC_NET_WIRELESS, 8, "wireless", S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, - dev_get_wireless_info + dev_get_wireless_info /* Available in net/core/wireless.c */ }; #endif /* CONFIG_PROC_FS */ -#endif /* CONFIG_NET_RADIO */ +#endif /* WIRELESS_EXT */ __initfunc(int net_dev_init(void)) { @@ -2072,11 +1984,11 @@ __initfunc(int net_dev_init(void)) } #endif -#ifdef CONFIG_NET_RADIO +#ifdef WIRELESS_EXT #ifdef CONFIG_PROC_FS proc_net_register(&proc_net_wireless); #endif /* CONFIG_PROC_FS */ -#endif /* CONFIG_NET_RADIO */ +#endif /* WIRELESS_EXT */ init_bh(NET_BH, net_bh); diff -u -p --new-file linux/net/core.w10/wireless.c linux/net/core/wireless.c --- linux/net/core.w10/wireless.c Wed Dec 31 16:00:00 1969 +++ linux/net/core/wireless.c Mon Apr 14 17:35:43 2003 @@ -0,0 +1,1027 @@ +/* + * This file implement the Wireless Extensions APIs. + * + * Authors : Jean Tourrilhes - HPL - + * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved. + * + * (As all part of the Linux kernel, this file is GPL) + */ + +/************************** DOCUMENTATION **************************/ +/* + * API definition : + * -------------- + * See 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) + * + * Kernel 2.2.25 backport : + * o Disable RTnetLink support, which mean disable event support + * -> IFLA_WIRELESS can't be safely added + * o Disable WE_STRICT_WRITE for maximum backward compatibility + * -> User space tools may be antiquated + * o struct net_device => struct device + */ + +/***************************** INCLUDES *****************************/ + +#include /* copy_to_user() */ +#include /* Not needed ??? */ +#include /* off_t */ +#include /* struct ifreq, dev_get_by_name() */ +#include /* rtnetlink stuff */ + +#include /* Pretty obvious */ +#include /* New driver API */ + +/**************************** CONSTANTS ****************************/ + +/* Maximise backward compatibility with user space tools */ +#undef WE_STRICT_WRITE /* Check write buffer size */ + +/* Debuging stuff */ +#undef WE_IOCTL_DEBUG /* Debug IOCTL API */ +#undef WE_EVENT_DEBUG /* Debug Event dispatcher */ + +/* Options */ +#undef WE_EVENT_NETLINK /* Propagate events using rtnetlink */ +#define WE_SET_EVENT /* Generate an event on some set commands */ + +/************************* GLOBAL VARIABLES *************************/ +/* + * You should not use global variables, because or 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 */ + { 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_GET_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}, + /* SIOCSIWSCAN */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + /* SIOCGIWSCAN */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_SCAN_MAX_DATA, 0}, + /* SIOCSIWESSID */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE + 1, IW_DESCR_FLAG_EVENT}, + /* SIOCGIWESSID */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE + 1, IW_DESCR_FLAG_DUMP}, + /* SIOCSIWNICKN */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE + 1, 0}, + /* SIOCGIWNICKN */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE + 1, 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, 0, 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}, +}; +static const int standard_ioctl_num = (sizeof(standard_ioctl) / + sizeof(struct iw_ioctl_description)); + +/* + * Meta-data about all the additional standard Wireless Extension events + * we know about. + */ +static const struct iw_ioctl_description standard_event[] = { + /* IWEVTXDROP */ + { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, + /* IWEVQUAL */ + { IW_HEADER_TYPE_QUAL, 0, 0, 0, 0, 0}, + /* IWEVCUSTOM */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_CUSTOM_MAX, 0}, + /* IWEVREGISTERED */ + { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, + /* IWEVEXPIRED */ + { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, +}; +static const int standard_event_num = (sizeof(standard_event) / + sizeof(struct iw_ioctl_description)); + +/* Size (in bytes) of the various private data types */ +static const char 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 */ +}; + +/************************ 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 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 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 device' + * to 'struct iw_handler_def', to de-bloat struct 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 device * dev) +{ + if((dev->start) && + (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 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 ? '.' : ' ', + ((__u8) stats->qual.level), + stats->qual.updated & 2 ? '.' : ' ', + ((__u8) 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 dummy) +{ + int len = 0; + off_t begin = 0; + off_t pos = 0; + int size; + + struct 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; + + 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; + } + + *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 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 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 < dev->wireless_handlers->num_private_args) { + printk(KERN_ERR "%s (WE) : Buffer for request SIOCGIWPRIV too small (%d<%d)\n", dev->name, iwr->u.data.length, dev->wireless_handlers->num_private_args); + 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 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; + int user_size = 0; + + /* 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 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; + /* Save user space buffer size for checking */ + user_size = iwr->u.data.length; + } + +#ifdef WE_IOCTL_DEBUG + printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n", + dev->name, 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 "%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); + + /* If we have something to return to the user */ + if (!ret && IW_IS_GET(cmd)) { +#ifdef WE_STRICT_WRITE + /* Check if there is enough buffer up there */ + if(user_size < iwr->u.data.length) { + printk(KERN_ERR "%s (WE) : Buffer for request %04X too small (%d<%d)\n", dev->name, cmd, user_size, iwr->u.data.length); + kfree(extra); + return -E2BIG; + } +#endif /* WE_STRICT_WRITE */ + + 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 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 (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 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 "%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)) { + 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 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(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 */ + /* 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_NETLINK +/* "rtnl" is defined in net/core/rtnetlink.c, but we need it here. + * It is declared in */ + +/* ---------------------------------------------------------------- */ +/* + * 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 device * dev, + int type, + char * event, + int event_len) +{ + struct ifinfomsg *r; + struct nlmsghdr *nlh; + unsigned char *b = skb->tail; + + nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r)); + r = NLMSG_DATA(nlh); + r->ifi_family = AF_UNSPEC; + r->ifi_type = dev->type; + r->ifi_index = dev->ifindex; + r->ifi_flags = dev->flags; + 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 - b; + return skb->len; + +nlmsg_failure: +rtattr_failure: + skb_trim(skb, b - skb->data); + 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 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_groups = RTMGRP_LINK; + netlink_broadcast(rtnl, skb, 0, RTMGRP_LINK, GFP_ATOMIC); +} +#endif /* WE_EVENT_NETLINK */ + +/* ---------------------------------------------------------------- */ +/* + * Main event dispatcher. Called from other parts and drivers. + * Send the event on the apropriate channels. + * May be called from interrupt context. + */ +void wireless_send_event(struct 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 */ + /* Don't "optimise" the following variable, it will crash */ + unsigned cmd_index; /* *MUST* be unsigned */ + + /* Get the description of the IOCTL */ + 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; +#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, event_len %d\n", dev->name, cmd, hdr_len, 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, wrqu, hdr_len - IW_EV_LCP_LEN); + if(extra != NULL) + memcpy(((char *) event) + hdr_len, extra, extra_len); + +#ifdef WE_EVENT_NETLINK + /* rtnetlink event channel */ + rtmsg_iwinfo(dev, (char *) event, event_len); +#endif /* WE_EVENT_NETLINK */ + + /* Cleanup */ + kfree(event); + + return; /* Always success, I guess ;-) */ +} diff -u -p linux/net/netsyms.w10.c linux/net/netsyms.c --- linux/net/netsyms.w10.c Mon Apr 14 14:17:28 2003 +++ linux/net/netsyms.c Mon Apr 14 15:16:43 2003 @@ -569,4 +569,9 @@ EXPORT_SYMBOL(unregister_tcf_proto_ops); EXPORT_SYMBOL(register_gifconf); +#ifdef CONFIG_NET_RADIO +#include +EXPORT_SYMBOL(wireless_send_event); +#endif /* CONFIG_NET_RADIO */ + #endif /* CONFIG_NET */ diff -u -p linux/drivers/net/wavelan.w10.c linux/drivers/net/wavelan.c --- linux/drivers/net/wavelan.w10.c Mon Apr 14 14:23:36 2003 +++ linux/drivers/net/wavelan.c Mon Apr 14 14:27:13 2003 @@ -2104,8 +2104,15 @@ wavelan_ioctl(struct device * dev, /* de { struct iw_range range; - /* Set the length (useless: it's constant). */ + /* Set the length (very important for backward compatibility) */ wrq->u.data.length = sizeof(struct iw_range); + + /* Set all the info we don't care or don't know about to zero */ + memset(&range, 0, sizeof(range)); + + /* Set the Wireless Extension versions */ + range.we_version_compiled = WIRELESS_EXT; + range.we_version_source = 9; /* Set information in the range struct. */ range.throughput = 1.6 * 1000 * 1000; /* don't argue on this ! */