OSDN Git Service

v29
authorchris-kirby <chris.kirby@hpe.com>
Tue, 11 Oct 2016 21:17:19 +0000 (15:17 -0600)
committerchris-kirby <chris.kirby@hpe.com>
Tue, 11 Oct 2016 21:17:19 +0000 (15:17 -0600)
23 files changed:
wireless_tools/19-udev-ifrename.rules [new file with mode: 0644]
wireless_tools/CHANGELOG.h
wireless_tools/IFRENAME-VS-XXX.txt
wireless_tools/INSTALL
wireless_tools/Makefile
wireless_tools/README
wireless_tools/ifrename.8
wireless_tools/ifrename.c
wireless_tools/iftab.5
wireless_tools/iwconfig.8
wireless_tools/iwconfig.c
wireless_tools/iwlib.c
wireless_tools/iwlib.h
wireless_tools/iwlist.8
wireless_tools/iwlist.c
wireless_tools/iwmulticall.c
wireless_tools/iwpriv.8
wireless_tools/iwpriv.c
wireless_tools/iwspy.8
wireless_tools/sample_pm.c
wireless_tools/udev.import_devpath.diff [new file with mode: 0644]
wireless_tools/wireless.21.h [new file with mode: 0644]
wireless_tools/wireless.22.h [new file with mode: 0644]

diff --git a/wireless_tools/19-udev-ifrename.rules b/wireless_tools/19-udev-ifrename.rules
new file mode 100644 (file)
index 0000000..c8b319e
--- /dev/null
@@ -0,0 +1,13 @@
+# udev rules to properly integrate ifrename.
+# Renaming is done using /etc/iftab, with full ifrename functionality.
+# Require udev version 107 or later.
+# Please double check the path to ifrename, and make sure its available
+# when udev runs (i.e. on boot partition).
+
+# Enable this rule to test with udevtest.
+#ENV{UDEV_LOG}=="6", SUBSYSTEM=="net", ACTION=="add", IMPORT="/sbin/ifrename -D -V -u -i %k", NAME:="%k"
+
+# Main ifrename rule.
+# If interface is found in /etc/iftab, subsequent rename rules are bypassed.
+# If interface is not found in /etc/iftab, subsequent rename rules applies.
+SUBSYSTEM=="net", ACTION=="add", IMPORT="/sbin/ifrename -u -i %k", NAME:="%k"
index 94855c1..2084760 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *     Wireless Tools
  *
- *             Jean II - HPLB 97->99 - HPL 99->04
+ *             Jean II - HPLB 97->99 - HPL 99->07
  *
  * The changelog...
  *
  *     o Add WE-20 headers, compile with that as default
  *     ---
  *     o Fix 'inline' for gcc-4 as well. Grrr... [iwlib]
+ *
+ * wireless 29 :
+ * -----------
+ *     o Add new power value : 'power saving' [iwconfig/iwlist/iwlib]
+ *     o Optimise getting iwrange when setting TxPower [iwconfig]
+ *     o Optimise displaying current power values (better loop) [iwlist]
+ *     ---
+ *     o Add modulation bitmasks ioctls [iwconfig/iwlist]
+ *     o Add short and long retries [iwconfig/iwlist/iwlib]
+ *     o Fix 'relative' power saving to not be *1000 [iwconfig/iwlib]
+ *     o iw_print_pm_value() require we_version [iwlib]
+ *     o Optimise displaying range power values (subroutine) [iwlist]
+ *     ---
+ *     o Fix 'relative' retry to not be *1000 [iwconfig/iwlib]
+ *     o iw_print_retry_value() require we_version [iwlib]
+ *     o Optimise getting iwrange when setting PowerSaving [iwconfig]
+ *     o Optimise displaying current retry values (better loop) [iwlist]
+ *     o Optimise displaying range retry values (subroutine) [iwlist]
+ *     ---
+ *     o Fix stupid bug in displaying range retry values [iwlist]
+ *     ---
+ *     o Add support for unicast and broadcast bitrates [iwconfig/iwlist]
+ *     ---
+ *     o Replace spaghetti code with real dispatcher in set_info() [iwconfig]
+ *             Code is more readable, maintainable, and save 700 bytes...
+ *     o Drop 'domain' alias for 'nwid'. Obsolete. [iwconfig]
+ *     o Make iw_usage() use dispatcher data instead of hardcoded [iwconfig]
+ *     o Factor out modifier parsing for retry/power [iwconfig]
+ *     o Fix iwmulticall to compile with new dispatcher above [iwmulticall]
+ *     o Add WE_ESSENTIAL compile option to drop 10kB [Makefile]
+ *     ---
+ *     o Update manpages with new features above [man]
+ *     ---
+ *     o Add temp variable to sscanf() to fix 64 bits issues [iwconfig]
+ *     o De-inline get_pm_value/get_retry_value to reduce footprint [iwlist]
+ *     o Optimise iw_print_ie_cipher/iw_print_ie_auth [iwlist]
+ *     o Add "Memory footprint reduction" section in doc [README]
+ *     o Add 'last' scan option for left-over scan [iwlist]
+ *             (From Stavros Markou <smarkou@patras.atmel.com>)
+ *     o Add 'essid' scan option for directed scan [iwlist]
+ *     ---
+ *             (Bug reported by Henrik Brix Andersen <brix@gentoo.org>)
+ *     o Fix segfault on setting bitrate (parse wrong arg) [iwconfig]
+ *     ---
+ *     o Revert 'CC=gcc' to normal [Makefile]
+ *     o Integrate properly patch below [iwlist]
+ *             (From Brian Eaton <eaton.lists@gmail.com>)
+ *     o More WPA support : iwlist auth/wpakeys/genie [iwlist]
+ *     ---
+ *     o Tweak man pages : interface is often optional [iwlist.8/iwspy.8]
+ *     o Drop obsolete port/roam code from [iwpriv]
+ *             (From Pavel Roskin <proski@gnu.org>)
+ *     o Fix bug where all auth masks use iw_auth_capa_name [iwlist]
+ *             (From Dima Ryazanov <someone@berkeley.edu>)
+ *     o Fix iw_scan()/iw_process_scan() for non-root -> EPERM [iwlib]
+ *             (Bug reported by Arkadiusz Miskiewicz <arekm@pld-linux.org>)
+ *     o Fix "iwconfig nickname" (was abreviated) [iwconfig]
+ *             (Bug reported by Charles Plessy)
+ *     o Invalid mode from driver segfault iwlist scan [iwlist]
+ *             (From Aurelien Jacobs <aurel@gnuage.org>)
+ *     o Replace index() with strchr() [iwlib/iwconfig/iwpriv]
+ *             (From Jens Thoms Toerring)
+ *     o Parser/printf/sscanf fixes and optimisation [iwconfig]
+ *     ---
+ *             (From Pavel Roskin <proski@gnu.org>)
+ *     o Fix bug extracting mountpoint of sysfs (wrong field) [ifrename]
+ *             (Suggested by Pavel Roskin <proski@gnu.org>)
+ *     o Read sysfs symlinks transparently [ifrename]
+ *     ---
+ *     o Fix README header to talk about ifrename [README]
+ *     o Add 'prevname' selector for udev compatibility [ifrename]
+ *     o Read parent directory names in SYSFS selector [ifrename]
+ *     o Make dry-run output compatible with udev [ifrename]
+ *     o Update man page with useful SYSFS selectors [iftab.5]
+ *     ---
+ *     o Factorise wildcard rewrite '*'->'%d' to hide it from -D -V [ifrename]
+ *     o Reorganise sysfs description, better wording [iftab.5]
+ *             (Suggested by Pavel Roskin <proski@gnu.org>)
+ *     o Enhance explanation of arp and iwproto [iftab.5]
+ *     ---
+ *             (Bug reported by Johannes Berg <johannes@sipsolutions.net>)
+ *     o Band-aid for the 64->32bit iwevent/iwscan issues [iwlib]
+ *     ---
+ *     o Better band-aid for the 64->32bit iwevent/iwscan issues [iwlib]
+ *             (Suggested by Kay Sievers <kay.sievers@vrfy.org>)
+ *     o Add udev compatible output, print new DEVPATH [ifrename]
+ *     ---
+ *     o Fix DEVPATH output to use the real devpath from udev [ifrename]
+ *     o Add udev rules for ifrename integration [19-udev-ifrename.rules]
+ *     ---
+ *     o Add largest bitrate in easy scan API [iwlib]
+ *     ---
+ *     o Debug version : output IW_EV_LCP_LEN [iwlist]
+ *     ---
+ *             (Bug reported by Santiago Gala/Roy Marples)
+ *     o Fix 64->32bit band-aid on 64 bits, target is local aligned [iwlib]
+ *     ---
+ *             (Bug reported by Santiago Gala/Roy Marples)
+ *     o More fix to the 64->32bit band-aid on 64 bits [iwlib]
+ *     ---
+ *             (Bug reported by Dimitris Kogias)
+ *     o Fix GENIE parsing os chipher/key_mngt [iwlist]
+ *             (Bug reported by Guus Sliepen <guus@debian.org>)
+ *     o Compiler warning on DEBUG code [iwlist]
+ *     ---
+ *     o --version output WE_MAX_VERSION instead of WE_VERSION [iwlib]
+ *     o Change iwstats dBm range to [-192;63] in iw_print_stats() [iwlib.c]
+ *     o Implement iwstats IW_QUAL_RCPI in iw_print_stats()  [iwlib.c]
+ *             (Bug reported by Guus Sliepen <guus@sliepen.eu.org>)
+ *     o LINUX_VERSION_CODE removed, only use GENERIC_HEADERS [iwlib.h]
+ *             (Bug reported by Johan Danielsson <joda11147@gmail.com>)
+ *     o Fix OUI type check for WPA 1 IE [iwlist.c]
+ *     ---
+ *             (Bug reported by Florent Daignière)
+ *     o Don't look for "fixed" out of array in set_txpower_info() [iwconfig]
  */
 
 /* ----------------------------- TODO ----------------------------- */
  * --------
  *     Make disable a per encryption key modifier if some hardware
  *     requires it.
- *
- * iwpriv :
- * ------
- *     Remove 'port' and 'roam' cruft now that we have mode in iwconfig
+ *     IW_QUAL_RCPI
  *
  * iwspy :
  * -----
index 3e6b8a0..dba3dfe 100644 (file)
@@ -79,21 +79,21 @@ interfaces, with rules such as :
                http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html
 
        Advantages over 'ifrename' :
-               + integrated into 'udev'
                + simpler to setup if 'udev' is already properly setup
+               + automatically generates persistent rules
        Drawbacks compared to 'ifrename' :
                - Less selectors that 'ifrename'
                - Require kernel 2.6.X or later with sysfs support
                - Do no support non-hotplug interfaces
                - Require 'udev', not everybody uses it (static /dev, devfs)
-               - Does not support module on-demand loading
        Comments :
                o 'udev' support many selectors, basically all those
-present in 'sysfs', even if the documentation only show instructions
-to use the MAC address (which is problematic with virtual devices some
-drivers - see above). 'ifrename' can also use all selectors present in
-'sysfs' (like 'udev'), plus some other selectors not present in sysfs
-that were found to be useful.
+present in 'sysfs' (excluding symlinks), even if the documentation
+only show instructions to use the MAC address (which is problematic
+with virtual devices some drivers - see above). 'ifrename' can also
+use all selectors present in 'sysfs' (like 'udev'), can use sysfs
+symlinks and parent directories, plus some other selectors not present
+in sysfs that were found to be useful.
                o Not all interfaces are managed by hotplug. All
 virtual devices, such as tunnels and loopbacks, are not associated
 with a hardware bus, and therefore are not managed by hotplug. All
@@ -102,7 +102,9 @@ hotplug. 'udev' can't deal with those devices.
                o It is common practice on embedded system to use a
 static /dev and not 'udev' to save space and boot time. And to not use
 hotplug for the same reasons.
-               o 'ifrename' could be better integrated in 'udev', I don't foresee any technical issues.
+               o 'ifrename' has now a udev compatiblity mode that
+enables to trivially integrate it into 'udev' as an IMPORT rule. This
+requires udev version 107 or better and ifrename 29-pre17 or better.
 
 SELECTOR AWARE NETWORK SCRIPTS
 ------------------------------
index 9a1b980..5cc017d 100644 (file)
@@ -36,17 +36,87 @@ Create a local copy of the tools :
 --------------------------------
        By default, the package is built with iwlib as a dynamic
 library, and the tool will expect to use the default version of libiw
-on the system.
+on the system. This means you can't use the tools until they are
+properly installed.
        If you just want to experiment with a "local" version of the
 tools, you may want to pass the BUILD_STATIC flag to Makefile. It will
 create a self contained version of the tools.
--------------
-make clean
-make BUILD_STATIC='y'
--------------
+               -------------
+               make clean
+               make BUILD_STATIC='y'
+               -------------
        The resulting binary can be used in the compilation directory
 or installed in any place you like.
 
+Other useful Makefile options :
+-----------------------------
+       PREFIX : where the tools will be installed (default : /usr/local)
+       CC : Compiler to use (defaul : gcc)
+       BUILD_STATIC : build tools with a static version of the wireless lib
+       BUILD_NOLIBM : build tools without mathematical lib (slower)
+       BUILD_STRIPPING : strip symbols from tools/lib.
+       BUILD_WE_ESSENTIAL : remove less used and obsolete features.
+
+       You can pass those options on the command line of make, or
+modify the top of the Makefile. You can also set them as environment
+variable, but this is not recommended.
+       If you pass those options on the command line, you should pass
+the same command line options for all invocations of make ("make" and
+"make install").
+
+Memory footprint reduction :
+--------------------------
+       The Wireless Tools are used in various embedded systems where
+memory footprint is a great concern. The Wireless Tools package offer
+multiple options to customise the compilation depending on the level
+of features you want.
+       The list below details the must useful combinations of these
+options, from the largest footprint to the smallest. Footprint depend
+on lot's of factor and is purely indicative (version 29-pre7+, i386,
+glibc, gcc 3.3.5).
+
+       1) Static build
+       Command line : make BUILD_STATIC='y'
+       - : Largest footprint
+       - : libiw not included (other third party tools may depend on it)
+       Size : ~280 kB
+
+       2) Default build
+       Command line : make
+       + : Fully featured version of the tools
+       - : Largest footprint (except for static version of tools)
+       Size : ~190 kB (libiw : ~29 kB ; ifrename : ~29 kB)
+
+       3) Stripping (remove function symbols)
+       Command line : make BUILD_STRIPPING='y'
+       + : Fully featured version of the tools
+       - : Still quite large
+       Size : ~110 kB (libiw : ~23 kB ; ifrename : ~17 kB)
+
+       4) Multicall version (include stripping)
+       Command line : make iwmulticall ; make install-iwmulticall
+       + : Fully featured version of the tools
+       + : Small
+       - : libiw not included (other third party tools may depend on it)
+       - : ifrename is not included
+       Size : ~55 kB
+
+       5) Multicall + Essential
+       Command line : make BUILD_WE_ESSENTIAL='y' iwmulticall
+       + : Smaller
+       - : Some less used features are left out 
+       - : libiw not included (other third party tools may depend on it)
+       - : ifrename is not included
+       Size : ~44 kB
+
+       6) iwconfig only + essential + static
+       Command line : make BUILD_WE_ESSENTIAL='y'  BUILD_STATIC='y' BUILD_STRIPPING='y' iwconfig
+       + : Very small
+       - : Very limited functionality : no scanning, no event, no iwpriv
+       - : libiw not included (other third party tools may depend on it)
+       - : ifrename is not included
+       Size : ~28 kB
+
 Wireless headers (past history) :
 -------------------------------
        Previous version of the Wireless Tools had to be compiled with
@@ -63,18 +133,6 @@ you that.
        Note that the previous option to make versioned installed of
 the tools no longer make sense and therefore is gone.
 
-Other useful Makefile options :
------------------------------
-       PREFIX : where the tools will be installed (default : /usr/local)
-       BUILD_STATIC : build tools with a static version of the wireless lib
-       BUILD_NOLIBM : build tools without mathematical lib (slower)
-       Note that you should pass the same command line options for
-all invocations of make ("make" and "make install").
-
-       If you want the absolute minimal footprint, you may want to
-look into the multicall version of the tools. You can build it with
-"make iwmulticall" and install it with "make install-iwmulticall".
-
 Old kernel with older Wireless Extensions :
 -----------------------------------------
        Kernel prior to 2.2.14 : Those kernels include Wireless
index bc4fbd6..2e43159 100644 (file)
@@ -2,31 +2,36 @@
 ## Please check the configurion parameters below
 ##
 
-## Installation directory. By default, go in /usr/local
+## Installation directory. By default, go in /usr/local.
 ## Distributions should probably use /, but they probably know better...
 ifndef PREFIX
   PREFIX = /usr/local
 endif
 
-## Compiler to use (modify this for cross compile)
+## Compiler to use (modify this for cross compile).
 CC = gcc
-## Other tools you need to modify for cross compile (static lib only)
+## Other tools you need to modify for cross compile (static lib only).
 AR = ar
 RANLIB = ranlib
 
-## Uncomment this to build tools using static version of the library
+## Uncomment this to build tools using static version of the library.
 ## Mostly useful for embedded platforms without ldd, or to create
 ## a local version (non-root).
 # BUILD_STATIC = y
 
-## Uncomment this to build without using libm (less efficient)
+## Uncomment this to build without using libm (less efficient).
 ## This is mostly useful for embedded platforms without maths.
 # BUILD_NOLIBM = y
 
-## Uncomment this to strip binary from symbols. This reduce binary size
+## Uncomment this to strip binary from symbols. This reduce binary size.
 ## by a few percent but make debug worse...
 # BUILD_STRIPPING = y
 
+## Uncomment this to build with only essential functionality.
+## This leaves out the less used features and cut in half the tools.
+## This is mostly useful for embedded platforms without limited feature needs.
+# BUILD_WE_ESSENTIAL = y
+
 # ***************************************************************************
 # ***** Most users should not need to change anything beyond this point *****
 # ***************************************************************************
@@ -91,12 +96,17 @@ else
   STRIPFLAGS=
 endif
 
+# Do we want to build with only essential functionality ?
+ifdef BUILD_WE_ESSENTIAL
+  WEDEF_FLAG= -DWE_ESSENTIAL=y
+endif
+
 # Other flags
 CFLAGS=-Os -W -Wall -Wstrict-prototypes -Wmissing-prototypes -Wshadow \
        -Wpointer-arith -Wcast-qual -Winline -I.
 #CFLAGS=-O2 -W -Wall -Wstrict-prototypes -I.
 DEPFLAGS=-MMD
-XCFLAGS=$(CFLAGS) $(DEPFLAGS) $(WARN) $(HEADERS) $(WELIB_FLAG)
+XCFLAGS=$(CFLAGS) $(DEPFLAGS) $(WARN) $(HEADERS) $(WELIB_FLAG) $(WEDEF_FLAG)
 PICFLAG=-fPIC
 
 # Standard compilation targets
index 55341bf..442c622 100644 (file)
@@ -1,10 +1,12 @@
-       Wireless Tools
-       --------------
+       Wireless Tools & IfRename
+       -------------------------
 
        This package contains the Wireless tools, used to manipulate
 the Wireless Extensions. The Wireless Extensions is an interface
 allowing you to set Wireless LAN specific parameters and get the
 specific stats.
+       It also contains the IfRename package, used for advance
+renaming of network interfaces.
 
 web page :
 --------
@@ -112,6 +114,10 @@ iwlib.c
        The Wireless Tools helper library. May be useful if you want
 to create your own applications using Wireless Extensions.
 
+iwmulticall.c
+-------------
+       Multicall version of the tools for embedded systems.
+
 Changelog, contributions :
 ------------------------
        See CHANGELOG.h
@@ -136,6 +142,10 @@ more tricky features of Wireless Extensions in your driver.
 alone works, but it should point you in the proper direction.
        Also, have a look at existing drivers in the Linux kernel.
 
+19-udev-ifrename.rules :
+----------------------
+       udev rules to integrate properly ifrename (udev >= 107).
+
 Other tools :
 -----------
        My web page lists many other tools using Wireless
index 376c58d..da10181 100644 (file)
@@ -1,7 +1,7 @@
-.\" Jean II - HPL - 2004
+.\" Jean II - HPL - 2004-2007
 .\" ifrename.8
 .\"
-.TH IFRENAME 8 "01 March 2004" "wireless-tools" "Linux Programmer's Manual"
+.TH IFRENAME 8 "26 February 2007" "wireless-tools" "Linux Programmer's Manual"
 .\"
 .\" NAME part
 .\"
@@ -11,7 +11,7 @@ ifrename \- rename network interfaces based on various static criteria
 .\" SYNOPSIS part
 .\"
 .SH SYNOPSIS
-.B "ifrename [-c configfile] [-p] [-d] [-v] [-V] [-D]"
+.B "ifrename [-c configfile] [-p] [-d] [-u] [-v] [-V] [-D]"
 .br
 .B "ifrename [-c configfile] [-i interface] [-n newname]"
 .\"
@@ -124,12 +124,38 @@ In any case, name swapping and the use of this feature is discouraged,
 and you are invited to choose unique and unambiguous names for your
 interfaces...
 .TP
+.B -u
+Enable
+.I udev
+output mode. This enables proper integration of
+.B ifrename
+in the
+.I udev
+framework,
+.BR udevd (8)
+will use
+.B ifrename
+to assign interface names present in
+.IR /etc/iftab .
+In this mode the output of ifrename can be parsed
+directly by
+.BR udevd (8)
+as an IMPORT action. This requires
+.I udev
+version 107 or later.
+.TP
 .B -D
 Dry-run mode. Ifrename won't change any interface, it will only print
 new interface name, if applicable, and return.
 .br
 In dry-run mode, interface name wildcards are not resolved. New
 interface name is printed, even if it is the same as the old name.
+.br
+Be also aware that some selectors can only be read by root, for
+example those based on
+.BR ethtool ),
+and will fail silently if run by a normal user. In other words,
+dry-run mode under a standard user may not give the expected result.
 .TP
 .B -V
 Verbose mode. Ifrename will display internal results of parsing its
index 047eb18..9a29d17 100644 (file)
@@ -1,14 +1,14 @@
 /*
  *     Wireless Tools
  *
- *             Jean II - HPL 04
+ *             Jean II - HPL 04 -> 07
  *
  * Main code for "ifrename". This is tool allows to rename network
  * interfaces based on various criteria (not only wireless).
  * You need to link this code against "iwlib.c" and "-lm".
  *
  * This file is released under the GPL license.
- *     Copyright (c) 2004 Jean Tourrilhes <jt@hpl.hp.com>
+ *     Copyright (c) 2007 Jean Tourrilhes <jt@hpl.hp.com>
  */
 
 /* 
@@ -30,7 +30,7 @@
  *     Difference with standard 'nameif' :
  *     o 'nameif' has only a single selector, the interface MAC address.
  *     o Modular selector architecture, easily add new selectors.
- *     o Wide range of selector, including sysfs...
+ *     o Wide range of selector, including sysfs and sysfs symlinks...
  *     o hotplug invocation support.
  *     o module loading support.
  *     o MAC address wildcard.
@@ -82,7 +82,8 @@ const int SELECT_INTERRUPT    = 9;    /* Select by HW Irq line */
 const int SELECT_IWPROTO       = 10;   /* Select by Wireless Protocol */
 const int SELECT_PCMCIASLOT    = 11;   /* Select by Pcmcia Slot */
 const int SELECT_SYSFS         = 12;   /* Select by sysfs file */
-#define SELECT_NUM             13
+const int SELECT_PREVNAME      = 13;   /* Select by previous interface name */
+#define SELECT_NUM             14
 
 #define HAS_MAC_EXACT  1
 #define HAS_MAC_FILTER 2
@@ -99,6 +100,7 @@ const struct option long_opt[] =
   {"interface", 1, NULL, 'i' },
   {"newname", 1, NULL, 'n' },
   {"takeover", 0, NULL, 't' },
+  {"udev", 0, NULL, 'u' },
   {"version", 0, NULL, 'v' },
   {"verbose", 0, NULL, 'V' },
   {NULL, 0, NULL, '\0' },
@@ -108,8 +110,8 @@ const struct option long_opt[] =
 #define PCMCIA_STAB1   "/var/lib/pcmcia/stab"
 #define PCMCIA_STAB2   "/var/run/stab"
 
-/* Max number of sysfs file we support */
-#define SYSFS_MAX_FILE 5
+/* Max number of sysfs file types we support */
+#define SYSFS_MAX_FILE 8
 
 /* Userspace headers lag, fix that... */
 #ifndef ARPHRD_IEEE1394
@@ -160,6 +162,8 @@ typedef struct if_mapping
 
   /* Name of this interface */
   char                 ifname[IFNAMSIZ+1];
+  char *               sysfs_devpath;
+  int                  sysfs_devplen;
 
   /* Selectors for this interface */
   int                  active[SELECT_NUM];     /* Selectors active */
@@ -177,6 +181,7 @@ typedef struct if_mapping
   char                 iwproto[IFNAMSIZ + 1];  /* Wireless/protocol name */
   int                  pcmcia_slot;            /* Pcmcia slot */
   char *               sysfs[SYSFS_MAX_FILE];  /* sysfs selectors */
+  char                 prevname[IFNAMSIZ+1];   /* previous interface name */
 } if_mapping;
 
 /* Extra parsing information when adding a mapping */
@@ -358,6 +363,21 @@ static int
                         const char *           ifname,
                         struct if_mapping *    target,
                         int                    flag);
+static int
+       mapping_addprevname(struct if_mapping * ifnode,
+                          int *                active,
+                          char *               pos,
+                          size_t               len,
+                          struct add_extra *   extra,
+                          int                  linenum);
+static int
+       mapping_cmpprevname(struct if_mapping * ifnode,
+                          struct if_mapping *  target);
+static int
+       mapping_getprevname(int                 skfd,
+                          const char *         ifname,
+                          struct if_mapping *  target,
+                          int                  flag);
 
 /**************************** VARIABLES ****************************/
 
@@ -390,6 +410,8 @@ const struct mapping_selector       selector_list[] =
   { "pcmciaslot", &mapping_addpcmciaslot, &mapping_cmppcmciaslot, &mapping_getpcmciaslot },
   /* sysfs file (udev emulation) */
   { "sysfs", &mapping_addsysfs, &mapping_cmpsysfs, &mapping_getsysfs },
+  /* previous interface name */
+  { "prevname", &mapping_addprevname, &mapping_cmpprevname, &mapping_getprevname },
   /* The Terminator */
   { NULL, NULL, NULL, NULL },
 };
@@ -419,6 +441,9 @@ int dry_run = 0;            /* Just print new name, don't rename */
 /* Verbose support (i.e. debugging) */
 int    verbose = 0;
 
+/* udev output support (print new DEVPATH) */
+int    udev_output = 0;
+
 /* sysfs global data */
 struct sysfs_metadata  sysfs_global =
 {
@@ -449,8 +474,8 @@ if_match_ifname(const char *        pattern,
   int          n;
   int          ret;
 
-  /* Check for a wildcard (converted from '*' to '%d' in mapping_create()) */
-  p = strstr(pattern, "%d");
+  /* Check for a wildcard */
+  p = strchr(pattern, '*');
 
   /* No wildcard, simple comparison */
   if(p == NULL)
@@ -473,7 +498,7 @@ if_match_ifname(const char *        pattern,
   while(isdigit(*v));
 
   /* Pattern suffix */
-  p += 2;
+  p += 1;
 
   /* Compare suffixes */
   return(strcmp(p, v));
@@ -539,6 +564,7 @@ if_set_name(int                     skfd,
            char *              retname)
 {
   struct ifreq ifr;
+  char *       star;
   int          ret;
 
   /* The kernel doesn't check is the interface already has the correct
@@ -562,6 +588,22 @@ if_set_name(int                    skfd,
   strncpy(ifr.ifr_name, oldname, IFNAMSIZ); 
   strncpy(ifr.ifr_newname, newname, IFNAMSIZ); 
 
+  /* Check for wildcard interface name, such as 'eth*' or 'wlan*'...
+   * This require specific kernel support (2.6.2-rc1 and later).
+   * We externally use '*', but the kernel doesn't know about that,
+   * so convert it to something it knows about... */
+  star = strchr(newname, '*');
+  if(star != NULL)
+    {
+      int      slen = star - newname;
+      /* Replace '*' with '%d' in the new buffer */
+      star = ifr.ifr_newname + slen;
+      /* Size was checked in process_rename() and mapping_create() */
+      memmove(star + 2, star + 1, IFNAMSIZ - slen - 2);
+      star[0] = '%';
+      star[1] = 'd';
+    }
+
   /* Do it */
   ret = ioctl(skfd, SIOCSIFNAME, &ifr);
 
@@ -613,7 +655,7 @@ mapping_addmac(struct if_mapping *  ifnode,
   /* Verify validity of string */
   if(len >= sizeof(ifnode->mac_filter))
     { 
-      fprintf(stderr, "MAC address too long at line %d\n", linenum);  
+      fprintf(stderr, "Error : MAC address too long at line %d\n", linenum);  
       return(-1);
     }
   n = strspn(string, "0123456789ABCDEFabcdef:*"); 
@@ -1544,6 +1586,8 @@ mapping_getsysfs(int                      skfd,
                 int                    flag)
 {
   FILE *       stream;
+  char *       fname;
+  int          fnsize;
   char *       linebuf = NULL;
   size_t       linelen = 0; 
   char *       sdup;
@@ -1553,83 +1597,116 @@ mapping_getsysfs(int                   skfd,
   skfd = skfd;
   flag = flag;
 
-  /* Check if we know the root of the sysfs filesystem */
-  if(sysfs_global.root == NULL)
+  /* Check if we know the devpath of this device */
+  if(target->sysfs_devpath == NULL)
     {
-      /* Open the mount file for reading */
-      stream = fopen("/proc/mounts", "r");
-      if(!stream) 
-       {
-         fprintf(stderr, "Error: Can't open /proc/mounts file: %s\n",
-                 strerror(errno)); 
-         return(-1);
-       }
-
-      /* Read each line of file
-       * getline is a GNU extension :-( The buffer is recycled and increased
-       * as needed by getline. */
-      while(getline(&linebuf, &linelen, stream) > 0)
+      /* Check if we know the root of the sysfs filesystem */
+      if(sysfs_global.root == NULL)
        {
-         char *        p;
-         size_t        n;
+         /* Open the mount file for reading */
+         stream = fopen("/proc/mounts", "r");
+         if(!stream) 
+           {
+             fprintf(stderr, "Error: Can't open /proc/mounts file: %s\n",
+                     strerror(errno)); 
+             return(-1);
+           }
 
-         /* Get the line starting with sysfs */
-         p = linebuf;
-         while(isspace(*p))
-           ++p; 
-         if(!strncasecmp(p, "sysfs ", 6))
+         /* Read each line of file
+          * getline is a GNU extension :-( The buffer is recycled and
+          * increased as needed by getline. */
+         while(getline(&linebuf, &linelen, stream) > 0)
            {
-             /* Find the mount point */
-             p += 6;
-             while(isspace(*p))
-               ++p;
-             n = strcspn(p, " \t\n");
-             sdup = strndup(p, n);
-             if((n == 0) || (sdup == NULL))
+             int               i;
+             char *    p;
+             size_t    n;
+             char *    token[3];
+             size_t    toklen[3];
+
+             /* The format of /proc/mounts is similar to /etc/fstab (5).
+              * The first argument is the device. For sysfs, there is no
+              * associated device, so this argument is ignored.
+              * The second argument is the mount point.
+              * The third argument is the filesystem type.
+              */
+
+             /* Extract the first 3 tokens */
+             p = linebuf;
+             for(i = 0; i < 3; i++)
                {
-                 fprintf(stderr, "Error: Can't parse /proc/mounts file: %s\n",
-                         strerror(errno)); 
-                 return(-1);
+                 while(isspace(*p))
+                   ++p; 
+                 token[i] = p;
+                 n = strcspn(p, " \t\n");
+                 toklen[i] = n;
+                 p += n;
                }
-             /* Store it */
-             sysfs_global.root = sdup;
-             sysfs_global.rlen = n;
-             break;
+             /* Get the filesystem which type is "sysfs" */
+             if((n == 5) && (!strncasecmp(token[2], "sysfs", 5)))
+               {
+                 /* Get its mount point */
+                 n = toklen[1];
+                 sdup = strndup(token[1], n);
+                 if((n == 0) || (sdup == NULL))
+                   {
+                     fprintf(stderr,
+                             "Error: Can't parse /proc/mounts file: %s\n",
+                             strerror(errno)); 
+                     return(-1);
+                   }
+                 /* Store it */
+                 sysfs_global.root = sdup;
+                 sysfs_global.rlen = n;
+                 break;
+               }
+             /* Finished -> next line */
            }
-         /* Finished -> next line */
-       }
 
-      /* Cleanup */
-      fclose(stream);
+         /* Cleanup */
+         fclose(stream);
 
-      /* Check if we found it */
-      if(sysfs_global.root == NULL)
+         /* Check if we found it */
+         if(sysfs_global.root == NULL)
+           {
+             fprintf(stderr,
+                     "Error: Can't find sysfs in /proc/mounts file\n");
+             free(linebuf);
+             return(-1);
+           }
+       }
+
+      /* Construct devpath for this interface.
+       * Reserve enough space to replace name without realloc. */
+      fnsize = (sysfs_global.rlen + 11 + IFNAMSIZ + 1);
+      fname = malloc(fnsize);
+      if(fname == NULL)
        {
-         fprintf(stderr, "Error: Can't find sysfs in /proc/mounts file\n");
-         free(linebuf);
+         fprintf(stderr, "Error: Can't allocate SYSFS devpath\n");  
          return(-1);
        }
+      /* Not true devpath for 2.6.20+, but this syslink should work */
+      target->sysfs_devplen = sprintf(fname, "%s/class/net/%s",
+                                     sysfs_global.root, ifname);
+      target->sysfs_devpath = fname;
     }
 
   /* Loop on all sysfs selector */
   for(findex = 0; findex < sysfs_global.filenum; findex++)
     {
-      char *   fname;
-      int      flen;
       char *   p;
       ssize_t  n;
 
       /* Construct complete filename for the sysfs selector */
-      flen = (sysfs_global.rlen + 11 + strlen(ifname) + 1 +
-             strlen(sysfs_global.filename[findex]) + 1);
-      fname = malloc(flen);
+      fnsize = (target->sysfs_devplen + 1 +
+               strlen(sysfs_global.filename[findex]) + 1);
+      fname = malloc(fnsize);
       if(fname == NULL)
        {
          fprintf(stderr, "Error: Can't allocate SYSFS filename\n");  
          free(linebuf);
          return(-1);
        }
-      sprintf(fname, "%s/class/net/%s/%s", sysfs_global.root, ifname,
+      sprintf(fname, "%s/%s", target->sysfs_devpath,
              sysfs_global.filename[findex]);
 
       /* Open the sysfs file for reading */
@@ -1649,18 +1726,103 @@ mapping_getsysfs(int                   skfd,
       fclose(stream);
       if(n <= 0)
        {
-         /* Some sysfs attribute are void for some interface */
-         if(verbose)
-           fprintf(stderr, "Error: Can't read file `%s'\n", fname);
-         /* Next sysfs selector */
-         continue;
-       }
+         /* Some attributes are just symlinks to another directory.
+          * We can read the attributes in that other directory
+          * just fine, but sometimes the symlink itself gives a lot
+          * of information.
+          * Examples : SYSFS{device} and SYSFS{device/driver}
+          * In such cases, get the name of the directory pointed to...
+          */
+         /*
+          * I must note that the API for readlink() is very bad,
+          * which force us to have this ugly code. Yuck !
+          */
+         int           allocsize = 128;        /* 256 = Good start */
+         int           retry = 16;
+         char *        linkpath = NULL;
+         int           pathlen;
 
-      /* Get content, remove trailing '/n', save it */
-      p = linebuf;
-      if(p[n - 1] == '\n')
-       n--;
-      sdup = strndup(p, n);
+         /* Try reading the link with increased buffer size */
+         do
+           {
+             allocsize *= 2;
+             linkpath = realloc(linkpath, allocsize);
+             pathlen = readlink(fname, linkpath, allocsize);
+             /* If we did not hit the buffer limit, success */
+             if(pathlen < allocsize)
+               break;
+           }
+         while(retry-- > 0);
+
+         /* Check for error, most likely ENOENT */
+         if(pathlen > 0)
+           /* We have a symlink ;-) Terminate the string. */
+           linkpath[pathlen] = '\0';
+         else
+           {
+             /* Error ! */
+             free(linkpath);
+
+             /* A lot of information in the sysfs is implicit, given
+              * by the position of a file in the tree. It is therefore
+              * important to be able to read the various components
+              * of a path. For this reason, we resolve '..' to the
+              * real name of the parent directory... */
+             /* We have at least 11 char, see above */
+             if(!strcmp(fname + fnsize - 4, "/.."))
+               //if(!strcmp(fname + strlen(fname) - 3, "/.."))
+               {
+                 /* This procedure to get the realpath is not very
+                  * nice, but it's the "best practice". Hmm... */
+                 int   cwd_fd = open(".", O_RDONLY);
+                 linkpath = NULL;
+                 if(cwd_fd > 0)
+                   {
+                     int       ret = chdir(fname);
+                     if(ret == 0)
+                       /* Using getcwd with NULL is a GNU extension. Nice. */
+                       linkpath = getcwd(NULL, 0);
+                     /* This may fail, but it's not fatal */
+                     fchdir(cwd_fd);
+                   }
+                 /* Check if we suceeded */
+                 if(!linkpath)
+                   {
+                     free(linkpath);
+                     if(verbose)
+                       fprintf(stderr, "Error: Can't read parent directory `%s'\n", fname);
+                     /* Next sysfs selector */
+                     continue;
+                   }
+               }
+             else
+               {
+                 /* Some sysfs attribute are void for some interface,
+                  * we may have a real directory, or we may have permission
+                  * issues... */
+                 if(verbose)
+                   fprintf(stderr, "Error: Can't read file `%s'\n", fname);
+                 /* Next sysfs selector */
+                 continue;
+               }
+           }
+
+         /* Here, we have a link name or a parent directory name */
+
+         /* Keep only the last component of path name, save it */
+         p = basename(linkpath);
+         sdup = strdup(p);
+         free(linkpath);
+       }
+      else
+       {
+         /* This is a regular file (well, pseudo file) */
+         /* Get content, remove trailing '/n', save it */
+         p = linebuf;
+         if(p[n - 1] == '\n')
+           n--;
+         sdup = strndup(p, n);
+       }
       if(sdup == NULL)
        {
          fprintf(stderr, "Error: Can't allocate SYSFS value\n"); 
@@ -1686,6 +1848,77 @@ mapping_getsysfs(int                     skfd,
   return(target->active[SELECT_SYSFS] ? 0 : -1);
 }
 
+/*------------------------------------------------------------------*/
+/*
+ * Add a Previous Interface Name selector to a mapping
+ */
+static int
+mapping_addprevname(struct if_mapping *        ifnode,
+                  int *                active,
+                  char *               string,
+                  size_t               len,
+                  struct add_extra *   extra,
+                  int                  linenum)
+{
+  /* Avoid "Unused parameter" warning */
+  extra = extra;
+
+  /* Verify validity of string */
+  if(len >= sizeof(ifnode->prevname))
+    { 
+      fprintf(stderr, "Old Interface Name too long at line %d\n", linenum);  
+      return(-1);
+    }
+
+  /* Copy */
+  memcpy(ifnode->prevname, string, len + 1); 
+
+  /* Activate */
+  ifnode->active[SELECT_PREVNAME] = 1;
+  active[SELECT_PREVNAME] = 1;
+
+  if(verbose)
+    fprintf(stderr,
+           "Parsing : Added Old Interface Name `%s' from line %d.\n",
+           ifnode->prevname, linenum);
+
+  return(0);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Compare the Previous Interface Name of two mappings
+ * Note : this one is special.
+ */
+static int
+mapping_cmpprevname(struct if_mapping *        ifnode,
+                  struct if_mapping *  target)
+{
+  /* Do wildcard matching, case insensitive */
+  return(fnmatch(ifnode->prevname, target->ifname, FNM_CASEFOLD));
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Extract the Previous Interface Name from a live interface
+ */
+static int
+mapping_getprevname(int                        skfd,
+                  const char *         ifname,
+                  struct if_mapping *  target,
+                  int                  flag)
+{
+  /* Avoid "Unused parameter" warning */
+  skfd = skfd; ifname = ifname; flag = flag;
+
+  /* Don't do anything, it's already in target->ifname ;-) */
+
+  /* Activate */
+  target->active[SELECT_PREVNAME] = 1;
+
+  return(0);
+}
+
 
 /*********************** MAPPING MANAGEMENTS ***********************/
 /*
@@ -1706,8 +1939,10 @@ mapping_create(char *    pos,
   struct if_mapping *  ifnode;
   char *               star;
 
-  /* Check overflow. */
-  if(len > IFNAMSIZ)
+  star = memchr(pos, '*', len);
+
+  /* Check overflow, need one extra char for wildcard */
+  if((len + (star != NULL)) > IFNAMSIZ)
     {
       fprintf(stderr, "Error: Interface name `%.*s' too long at line %d\n",
              (int) len, pos, linenum);  
@@ -1738,29 +1973,6 @@ mapping_create(char *    pos,
     fprintf(stderr, "Warning: Alias device `%s' at line %d probably can't be mapped.\n",
            ifnode->ifname, linenum);
 
-  /* Check for wildcard interface name, such as 'eth*' or 'wlan*'...
-   * This require specific kernel support (2.6.2-rc1 and later).
-   * We externally use '*', but the kernel doesn't know about that,
-   * so convert it to something it knows about... */
-  star = strchr(ifnode->ifname, '*');
-  if(star != NULL)
-    {
-      /* We need an extra char */
-      if(len >= IFNAMSIZ)
-       {
-         fprintf(stderr,
-                 "Error: Interface wildcard `%s' too long at line %d\n",
-                 ifnode->ifname, linenum);  
-         free(ifnode);
-         return(NULL);
-       }
-
-      /* Replace '*' with '%d' */
-      memmove(star + 2, star + 1, len + 1 - (star - ifnode->ifname));
-      star[0] = '%';
-      star[1] = 'd';
-    }
-
   if(verbose)
     fprintf(stderr, "Parsing : Added Mapping `%s' from line %d.\n",
            ifnode->ifname, linenum);
@@ -2215,37 +2427,23 @@ probe_debian(int                skfd)
 static int
 process_rename(int     skfd,
               char *   ifname,
-              char *   pattern)
+              char *   newname)
 {
-  char         newname[IFNAMSIZ+1];
   char         retname[IFNAMSIZ+1];
   int          len;
   char *       star;
 
-  len = strlen(pattern);
-  star = strchr(pattern, '*');
+  len = strlen(newname);
+  star = strchr(newname, '*');
 
   /* Check newname length, need one extra char for wildcard */
   if((len + (star != NULL)) > IFNAMSIZ)
     {
       fprintf(stderr, "Error: Interface name `%s' too long.\n",
-             pattern);  
+             newname);  
       return(-1);
     }
 
-  /* Copy to local buffer */
-  memcpy(newname, pattern, len + 1);
-
-  /* Convert wildcard to the proper format */
-  if(star != NULL)
-    {
-      /* Replace '*' with '%d' in the new buffer */
-      star += newname - pattern;
-      memmove(star + 2, star + 1, len + 1 - (star - newname));
-      star[0] = '%';
-      star[1] = 'd';
-    }
-
   /* Change the name of the interface */
   if(if_set_name(skfd, ifname, newname, retname) < 0)
     {
@@ -2285,6 +2483,24 @@ process_ifname(int       skfd,
   if(target == NULL)
     return(-1);
 
+  /* If udev is calling us, get the real devpath. */
+  if(udev_output)
+    {
+      const char *env;
+      /* It's passed to us as an environment variable */
+      env = getenv("DEVPATH");
+      if(env)
+       {
+         int   env_len = strlen(env);
+         target->sysfs_devplen = env_len;
+         /* Make enough space for new interface name */
+         target->sysfs_devpath = malloc(env_len + IFNAMSIZ + 1);
+         if(target->sysfs_devpath != NULL)
+           memcpy(target->sysfs_devpath, env, env_len + 1);
+       }
+      /* We will get a second chance is the user has some sysfs selectors */
+    }
+
   /* Find matching mapping */
   mapping = mapping_find(target);
   if(mapping == NULL)
@@ -2300,26 +2516,47 @@ process_ifname(int      skfd,
    * That would be tricky to do... */
   if(dry_run)
     {
-      printf("Dry-run : Would rename %s to %s.\n",
-            target->ifname, mapping->ifname);
-      return(0);
+      strcpy(retname, mapping->ifname);
+      fprintf(stderr, "Dry-run : Would rename %s to %s.\n",
+             target->ifname, mapping->ifname);
     }
-
-  /* Change the name of the interface */
-  if(if_set_name(skfd, target->ifname, mapping->ifname, retname) < 0)
+  else
     {
-      fprintf(stderr, "Error: cannot change name of %s to %s: %s\n",
-             target->ifname, mapping->ifname, strerror(errno)); 
-      return(-1);
+      /* Change the name of the interface */
+      if(if_set_name(skfd, target->ifname, mapping->ifname, retname) < 0)
+       {
+         fprintf(stderr, "Error: cannot change name of %s to %s: %s\n",
+                 target->ifname, mapping->ifname, strerror(errno)); 
+         return(-1);
+       }
     }
 
   /* Check if called with an explicit interface name */
   if(print_newname)
     {
-      /* Always print out the *new* interface name so that
-       * the calling script can pick it up and know where its interface
-       * has gone. */
-      printf("%s\n", retname);
+      if(!udev_output)
+       /* Always print out the *new* interface name so that
+        * the calling script can pick it up and know where its interface
+        * has gone. */
+       printf("%s\n", retname);
+      else
+       /* udev likes to call us as an IMPORT action. This means that
+        * we need to return udev the environment variables changed.
+        * Obviously, we don't want to return anything is nothing changed. */
+       if(strcmp(target->ifname, retname))
+         {
+           char *      pos;
+           /* Hack */
+           if(!target->sysfs_devpath)
+             mapping_getsysfs(skfd, ifname, target, 0);
+           /* Update devpath. Size is large enough. */
+           pos = strrchr(target->sysfs_devpath, '/');
+           if((pos != NULL) && (!strcmp(target->ifname, pos + 1)))
+             strcpy(pos + 1, retname);
+           /* Return new environment variables */
+           printf("DEVPATH=%s\nINTERFACE=%s\nINTERFACE_OLD=%s\n",
+                  target->sysfs_devpath, retname, target->ifname);
+         }
     }
 
   /* Done */
@@ -2387,7 +2624,7 @@ main(int  argc,
   /* Loop over all command line options */
   while(1)
     {
-      int c = getopt_long(argc, argv, "c:dDi:n:ptvV", long_opt, NULL);
+      int c = getopt_long(argc, argv, "c:dDi:n:ptuvV", long_opt, NULL);
       if(c == -1)
        break;
 
@@ -2417,6 +2654,9 @@ main(int  argc,
        case 't':
          force_takeover = 1;
          break;
+       case 'u':
+         udev_output = 1;
+         break;
        case 'v':
          printf("%-8.16s  Wireless-Tools version %d\n", "ifrename", WT_VERSION);
          return(0);
@@ -2449,7 +2689,8 @@ main(int  argc,
       else
        {
          /* Rename only this interface based on mappings
-          * Mostly used for HotPlug processing (from /etc/hotplug/net.agent).
+          * Mostly used for HotPlug processing (from /etc/hotplug/net.agent)
+          * or udev processing (from a udev IMPORT rule).
           * Process the network interface specified on the command line,
           * and return the new name on stdout.
           */
index c264f74..b0cc970 100644 (file)
@@ -1,7 +1,7 @@
-.\" Jean II - HPL - 2004
+.\" Jean II - HPL - 2004-2007
 .\" iftab.5
 .\"
-.TH IFTAB 5 "01 March 2004" "wireless-tools" "Linux Programmer's Manual"
+.TH IFTAB 5 "26 February 2007" "wireless-tools" "Linux Programmer's Manual"
 .\"
 .\" NAME part
 .\"
@@ -98,7 +98,9 @@ the goal is to uniquely identify each piece of hardware.
 .PP
 Most users will only use the
 .B mac
-selector, other selectors are for more specialised setup.
+selector despite its potential problems, other selectors are for more
+specialised setup. Most selectors accept a '*' in the selector value
+for wilcard matching, and most selectors are case insensitive.
 .TP
 .BI mac " mac address"
 Matches the MAC Address of the interface with the specified MAC
@@ -106,19 +108,27 @@ address. The MAC address of the interface can be shown using
 .IR ifconfig (8)
 or
 .IR ip (8).
-The specified MAC address may contain a '*' for wilcard matching.
 .br
 This is the most common selector, as most interfaces have a unique MAC
 address allowing to identify network interfaces without ambiguity.
 However, some interfaces don't have a valid MAC address until they are
-brought up, in such case using this selector is tricky.
+brought up, in such case using this selector is tricky or impossible.
 .TP
 .BI arp " arp type"
 Matches the ARP Type (also called Link Type) of the interface with the
-specified ARP type. The ARP Type of the interface can be shown using
+specified ARP type as a number. The ARP Type of the interface can be
+shown using
 .IR ifconfig (8)
 or
-.IR ip (8).
+.IR ip (8),
+the
+.B link/ether
+type correspond to
+.B 1
+and the
+.B link/ieee802.11
+type correspond to
+.BR 801 .
 .br
 This selector is useful when a driver create multiple network
 interfaces for a single network card.
@@ -159,7 +169,9 @@ not sufficient to uniquely identify an interface.
 Matches the Wireless Protocol of the interface with the specified
 wireless protocol. The Wireless Protocol of the interface can be shown
 using
-.IR iwconfig (8).
+.IR iwconfig (8)
+or
+.IR iwgetid (8).
 .br
 This selector is only supported on wireless interfaces and is not
 sufficient to uniquely identify an interface.
@@ -174,21 +186,97 @@ This selector is usually only supported on 16 bits cards, for 32 bits
 cards it is advised to use the selector
 .BR businfo .
 .TP
+.BI prevname " previous interface name"
+Matches the name of the interface prior to renaming with the specified
+oldname.
+.br
+This selector should be avoided as the previous interface name may
+vary depending on various condition. A system/kernel/driver update may
+change the original name. Then, ifrename or another tool may rename it
+prior to the execution of this selector.
+.TP
 .BI SYSFS{ filename } " value"
-Matches the sysfs attribute given by filename to the specified value. sysfs attributes of the interface can be read in one of the directory in the directory 
-.IR /sys/class/net/ .
-For example, the filename
-.I address
-is the MAC address of the device and should be identical to the selector
-.BR mac .
+Matches the content the sysfs attribute given by filename to the
+specified value. For symlinks and parents directories, match the
+actual directory name of the sysfs attribute given by filename to the
+specified value.
 .br
+A list of the most useful sysfs attributes is given in the next
+section.
+.\"
+.\" SYSFS DESCRIPTORS part
+.\"
+.SH SYSFS DESCRIPTORS
+Sysfs attributes for a specific interface are located on most systems
+in the directory named after that interface at
+.IR /sys/class/net/ .
+Most sysfs attribute are files, and their values can be read using
+.IR cat "(1) or " more (1).
+It is also possible to match attributes in subdirectories.
+.PP
+Some sysfs attributes are symlinks, pointing to another directory in
+sysfs. If the attribute filename is a symlink the sysfs attribute
+resolves to the name of the directory pointed by the symlink using
+.IR readlink (1).
+The location is a directory in the sysfs tree is also important. If
+the attribute filename ends with
+.IR /.. ,
+the sysfs attribute resolves to the real name of the parent directory
+using
+.IR pwd (1).
+.PP
 The sysfs filesystem is only supported with 2.6.X kernel and need to
-be mounted. sysfs selectors are not as efficient as other selectors,
-therefore they should be avoided for maximum performance.
+be mounted (usually in 
+.IR /sys ).
+sysfs selectors are not as efficient as other selectors, therefore
+they should be avoided for maximum performance.
+.PP
+These are common sysfs attributes and their corresponding ifrename
+descriptors.
+.TP
+.BI SYSFS{address} " value"
+Same as the
+.B mac
+descriptor.
+.TP
+.BI SYSFS{type} " value"
+Same as the
+.B arp
+descriptor.
+.TP
+.BI SYSFS{device} " value"
+Valid only up to kernel 2.6.20. Same as the
+.B businfo
+descriptor.
+.TP
+.BI SYSFS{..} " value"
+Valid only from kernel 2.6.21. Same as the
+.B businfo
+descriptor.
+.TP
+.BI SYSFS{device/driver} " value"
+Valid only up to kernel 2.6.20. Same as the
+.B driver
+descriptor.
+.TP
+.BI SYSFS{../driver} " value"
+Valid only from kernel 2.6.21. Same as the
+.B driver
+descriptor.
+.TP
+.BI SYSFS{device/irq} " value"
+Valid only up to kernel 2.6.20. Same as the
+.B irq
+descriptor.
+.TP
+.BI SYSFS{../irq} " value"
+Valid only from kernel 2.6.21. Same as the
+.B irq
+descriptor.
 .\"
-.\" EXAMPLE part
+.\" EXAMPLES part
 .\"
-.SH EXAMPLE
+.SH EXAMPLES
 # This is a comment
 .br
 eth2           mac 08:00:09:DE:82:0E
@@ -199,7 +287,11 @@ eth4               driver pcnet32 businfo 0000:02:05.0
 .br
 air*           mac 00:07:0E:* arp 1
 .br
-myvpn  SYSFS{address} 00:10:83:*
+myvpn  SYSFS{address} 00:10:83:* SYSFS{type} 1
+.br
+bcm*           SYSFS{device} 0000:03:00.0 SYSFS{device/driver} bcm43xx
+.br
+bcm*           SYSFS{..} 0000:03:00.0 SYSFS{../driver} bcm43xx
 .\"
 .\" AUTHOR part
 .\"
index d03710e..f43c2b8 100644 (file)
@@ -1,7 +1,7 @@
 .\" Jean II - HPLB - 1996 => HPL - 2004
 .\" iwconfig.8
 .\"
-.TH IWCONFIG 8 "09 March 2006" "wireless-tools" "Linux Programmer's Manual"
+.TH IWCONFIG 8 "30 March 2006" "wireless-tools" "Linux Programmer's Manual"
 .\"
 .\" NAME part
 .\"
@@ -21,7 +21,7 @@ iwconfig \- configure a wireless network interface
 .br
 .BI "                   [enc " E "] [key " K "] [power " P "] [retry " R ]
 .br
-.BI "                   [commit]
+.BI "                   [modu " M "] [commit]
 .br
 .BI "iwconfig --help"
 .br
@@ -78,11 +78,10 @@ to escape it.
 .br
 .I "   iwconfig eth0 essid -- ""ANY""
 .TP
-.BR nwid / domain
-Set the Network ID (in some products it may also be called Domain
-ID). As all adjacent wireless networks share the same medium, this
-parameter is used to differenciate them (create logical colocated
-networks) and identify nodes belonging to the same cell.
+.BR nwid
+Set the Network ID. As all adjacent wireless networks share the same
+medium, this parameter is used to differentiate them (create logical
+colocated networks) and identify nodes belonging to the same cell.
 .br
 This parameter is only used for pre-802.11 hardware, the 802.11
 protocol uses the ESSID and AP Address for this function.
@@ -285,14 +284,15 @@ behaviour of the retry mechanism.
 .br
 To set the maximum number of retries, enter
 .IR "limit `value'" .
-This is an absolute value (without unit).
+This is an absolute value (without unit), and the default (when
+nothing is specified).
 To set the maximum length of time the MAC should retry, enter
 .IR "lifetime `value'" .
 By defaults, this value in in seconds, append the suffix m or u to
 specify values in milliseconds or microseconds.
 .br
 You can also add the
-.IR min " and " max
+.IR short ", " long ", " min " and " max
 modifiers. If the card supports automatic mode, they define the bounds
 of the limit or lifetime. Some other cards define different values
 depending on packet size, for example in 802.11
@@ -305,6 +305,8 @@ is the short retry limit (non RTS/CTS packets).
 .br
 .I "   iwconfig eth0 retry lifetime 300m"
 .br
+.I "   iwconfig eth0 retry short 12"
+.br
 .I "   iwconfig eth0 retry min limit 8"
 .TP
 .BR rts [_threshold]
@@ -406,12 +408,14 @@ To set the period between wake ups, enter
 .IR "period `value'" .
 To set the timeout before going back to sleep, enter
 .IR "timeout `value'" .
+To set the generic level of power saving, enter
+.IR "saving `value'" .
 You can also add the
 .IR min " and " max
 modifiers. By default, those values are in seconds, append the suffix
 m or u to specify values in milliseconds or microseconds. Sometimes,
-those values are without units (number of beacon periods, dwell or
-similar).
+those values are without units (number of beacon periods, dwell,
+percentage or similar).
 .br
 .IR off " and " on
 disable and reenable power management. Finally, you may set the power
@@ -431,15 +435,43 @@ management mode to
 .br
 .I "   iwconfig eth0 power timeout 300u all"
 .br
+.I "   iwconfig eth0 power saving 3"
+.br
 .I "   iwconfig eth0 power off"
 .br
 .I "   iwconfig eth0 power min period 2 power max period 4"
 .TP
+.BR modu [lation]
+Force the card to use a specific set of modulations. Modern cards
+support various modulations, some which are standard, such as 802.11b
+or 802.11g, and some proprietary. This command force the card to only
+use the specific set of modulations listed on the command line. This
+can be used to fix interoperability issues.
+.br
+The list of available modulations depend on the card/driver and can be
+displayed using
+.IR "iwlist modulation" .
+Note that some card/driver may not be able to select each modulation
+listed independantly, some may come as a group. You may also set this
+parameter to
+.IR auto
+let the card/driver do its best.
+.br
+.B Examples :
+.br
+.I "   iwconfig eth0 modu 11g"
+.br
+.I "   iwconfig eth0 modu CCK OFDMa"
+.br
+.I "   iwconfig eth0 modu auto"
+.TP
 .BR commit
 Some cards may not apply changes done through Wireless Extensions
 immediately (they may wait to aggregate the changes or apply it only
-when the card is brought up via ifconfig). This command (when
-available) forces the card to apply all pending changes.
+when the card is brought up via
+.IR ifconfig ).
+This command (when available) forces the card to apply all pending
+changes.
 .br
 This is normally not needed, because the card will eventually apply
 the changes, but can be useful for debugging.
index 03093e6..d01f29a 100644 (file)
@@ -1,48 +1,37 @@
 /*
  *     Wireless Tools
  *
- *             Jean II - HPLB 97->99 - HPL 99->04
+ *             Jean II - HPLB 97->99 - HPL 99->07
  *
  * Main code for "iwconfig". This is the generic tool for most
  * manipulations...
  * You need to link this code against "iwlib.c" and "-lm".
  *
  * This file is released under the GPL license.
- *     Copyright (c) 1997-2004 Jean Tourrilhes <jt@hpl.hp.com>
+ *     Copyright (c) 1997-2007 Jean Tourrilhes <jt@hpl.hp.com>
  */
 
 #include "iwlib.h"             /* Header */
 
-/************************* MISC SUBROUTINES **************************/
+/**************************** CONSTANTS ****************************/
 
-/*------------------------------------------------------------------*/
 /*
- * Print usage string
+ * Error codes defined for setting args
  */
-static void
-iw_usage(void)
-{
-  fprintf(stderr,
-       "Usage: iwconfig interface [essid {NN|on|off}]\n"
-       "                          [nwid {NN|on|off}]\n"
-       "                          [mode {managed|ad-hoc|...}\n"
-       "                          [freq N.NNNN[k|M|G]]\n"
-       "                          [channel N]\n"
-       "                          [ap {N|off|auto}]\n"
-       "                          [sens N]\n"
-       "                          [nick N]\n"
-       "                          [rate {N|auto|fixed}]\n"
-       "                          [rts {N|auto|fixed|off}]\n"
-       "                          [frag {N|auto|fixed|off}]\n"
-       "                          [enc {NNNN-NNNN|off}]\n"
-       "                          [power {period N|timeout N}]\n"
-       "                          [retry {limit N|lifetime N}]\n"
-       "                          [txpower N {mW|dBm}]\n"
-       "                          [commit]\n"
-       "       Check man pages for more details.\n\n"
-  );
-}
+#define IWERR_ARG_NUM          -2
+#define IWERR_ARG_TYPE         -3
+#define IWERR_ARG_SIZE         -4
+#define IWERR_ARG_CONFLICT     -5
+#define IWERR_SET_EXT          -6
+#define IWERR_GET_EXT          -7
+
+/**************************** VARIABLES ****************************/
 
+/*
+ * Ugly, but deal with errors in set_info() efficiently...
+ */
+static int     errarg;
+static int     errmax;
 
 /************************* DISPLAY ROUTINES **************************/
 
@@ -79,13 +68,6 @@ get_info(int                 skfd,
   if(iw_get_range_info(skfd, ifname, &(info->range)) >= 0)
     info->has_range = 1;
 
-  /* Get sensitivity */
-  if(iw_get_ext(skfd, ifname, SIOCGIWSENS, &wrq) >= 0)
-    {
-      info->has_sens = 1;
-      memcpy(&(info->sens), &(wrq.u.sens), sizeof(iwparam));
-    }
-
   /* Get AP address */
   if(iw_get_ext(skfd, ifname, SIOCGIWAP, &wrq) >= 0)
     {
@@ -93,14 +75,6 @@ get_info(int                 skfd,
       memcpy(&(info->ap_addr), &(wrq.u.ap_addr), sizeof (sockaddr));
     }
 
-  /* Get NickName */
-  wrq.u.essid.pointer = (caddr_t) info->nickname;
-  wrq.u.essid.length = IW_ESSID_MAX_SIZE + 1;
-  wrq.u.essid.flags = 0;
-  if(iw_get_ext(skfd, ifname, SIOCGIWNICKN, &wrq) >= 0)
-    if(wrq.u.data.length > 1)
-      info->has_nickname = 1;
-
   /* Get bit rate */
   if(iw_get_ext(skfd, ifname, SIOCGIWRATE, &wrq) >= 0)
     {
@@ -108,20 +82,6 @@ get_info(int                        skfd,
       memcpy(&(info->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
     }
 
-  /* Get RTS threshold */
-  if(iw_get_ext(skfd, ifname, SIOCGIWRTS, &wrq) >= 0)
-    {
-      info->has_rts = 1;
-      memcpy(&(info->rts), &(wrq.u.rts), sizeof(iwparam));
-    }
-
-  /* Get fragmentation threshold */
-  if(iw_get_ext(skfd, ifname, SIOCGIWFRAG, &wrq) >= 0)
-    {
-      info->has_frag = 1;
-      memcpy(&(info->frag), &(wrq.u.frag), sizeof(iwparam));
-    }
-
   /* Get Power Management settings */
   wrq.u.power.flags = 0;
   if(iw_get_ext(skfd, ifname, SIOCGIWPOWER, &wrq) >= 0)
@@ -130,6 +90,22 @@ get_info(int                        skfd,
       memcpy(&(info->power), &(wrq.u.power), sizeof(iwparam));
     }
 
+  /* Get stats */
+  if(iw_get_stats(skfd, ifname, &(info->stats),
+                 &info->range, info->has_range) >= 0)
+    {
+      info->has_stats = 1;
+    }
+
+#ifndef WE_ESSENTIAL
+  /* Get NickName */
+  wrq.u.essid.pointer = (caddr_t) info->nickname;
+  wrq.u.essid.length = IW_ESSID_MAX_SIZE + 1;
+  wrq.u.essid.flags = 0;
+  if(iw_get_ext(skfd, ifname, SIOCGIWNICKN, &wrq) >= 0)
+    if(wrq.u.data.length > 1)
+      info->has_nickname = 1;
+
   if((info->has_range) && (info->range.we_version_compiled > 9))
     {
       /* Get Transmit Power */
@@ -140,6 +116,13 @@ get_info(int                       skfd,
        }
     }
 
+  /* Get sensitivity */
+  if(iw_get_ext(skfd, ifname, SIOCGIWSENS, &wrq) >= 0)
+    {
+      info->has_sens = 1;
+      memcpy(&(info->sens), &(wrq.u.sens), sizeof(iwparam));
+    }
+
   if((info->has_range) && (info->range.we_version_compiled > 10))
     {
       /* Get retry limit/lifetime */
@@ -150,44 +133,20 @@ get_info(int                      skfd,
        }
     }
 
-  /* Get stats */
-  if(iw_get_stats(skfd, ifname, &(info->stats),
-                 &info->range, info->has_range) >= 0)
+  /* Get RTS threshold */
+  if(iw_get_ext(skfd, ifname, SIOCGIWRTS, &wrq) >= 0)
     {
-      info->has_stats = 1;
+      info->has_rts = 1;
+      memcpy(&(info->rts), &(wrq.u.rts), sizeof(iwparam));
     }
 
-#ifdef DISPLAY_WPA
-  /* Note : currently disabled to not bloat iwconfig output. Also,
-   * if does not make total sense to display parameters that we
-   * don't allow (yet) to configure.
-   * For now, use iwlist instead... Jean II */
-
-  /* Get WPA/802.1x/802.11i security parameters */
-  if((info->has_range) && (info->range.we_version_compiled > 17))
+  /* Get fragmentation threshold */
+  if(iw_get_ext(skfd, ifname, SIOCGIWFRAG, &wrq) >= 0)
     {
-      wrq.u.param.flags = IW_AUTH_KEY_MGMT;
-      if(iw_get_ext(skfd, ifname, SIOCGIWAUTH, &wrq) >= 0)
-       {
-         info->has_auth_key_mgmt = 1;
-         info->auth_key_mgmt = wrq.u.param.value;
-       }
-
-      wrq.u.param.flags = IW_AUTH_CIPHER_PAIRWISE;
-      if(iw_get_ext(skfd, ifname, SIOCGIWAUTH, &wrq) >= 0)
-       {
-         info->has_auth_cipher_pairwise = 1;
-         info->auth_cipher_pairwise = wrq.u.param.value;
-       }
-
-      wrq.u.param.flags = IW_AUTH_CIPHER_GROUP;
-      if(iw_get_ext(skfd, ifname, SIOCGIWAUTH, &wrq) >= 0)
-       {
-         info->has_auth_cipher_group = 1;
-         info->auth_cipher_group = wrq.u.param.value;
-       }
+      info->has_frag = 1;
+      memcpy(&(info->frag), &(wrq.u.frag), sizeof(iwparam));
     }
-#endif
+#endif /* WE_ESSENTIAL */
 
   return(0);
 }
@@ -225,9 +184,11 @@ display_info(struct wireless_info *        info,
        printf("ESSID:off/any  ");
     }
 
+#ifndef WE_ESSENTIAL
   /* Display NickName (station name), if any */
   if(info->has_nickname)
     printf("Nickname:\"%s\"", info->nickname);
+#endif /* WE_ESSENTIAL */
 
   /* Formatting */
   if(info->b.has_essid || info->has_nickname)
@@ -236,6 +197,7 @@ display_info(struct wireless_info * info,
       tokens = 0;
     }
 
+#ifndef WE_ESSENTIAL
   /* Display Network ID */
   if(info->b.has_nwid)
     {
@@ -247,6 +209,7 @@ display_info(struct wireless_info * info,
        printf("NWID:%X  ", info->b.nwid.value);
       tokens +=2;
     }
+#endif /* WE_ESSENTIAL */
 
   /* Display the current mode of operation */
   if(info->b.has_mode)
@@ -306,6 +269,7 @@ display_info(struct wireless_info * info,
       printf("Bit Rate%c%s   ", (info->bitrate.fixed ? '=' : ':'), buffer);
     }
 
+#ifndef WE_ESSENTIAL
   /* Display the Transmit Power */
   if(info->has_txpower)
     {
@@ -334,10 +298,7 @@ display_info(struct wireless_info *        info,
       tokens +=4;
 
       /* Fixed ? */
-      if(info->sens.fixed)
-       printf("Sensitivity=");
-      else
-       printf("Sensitivity:");
+      printf("Sensitivity%c", info->sens.fixed ? '=' : ':');
 
       if(info->has_range)
        /* Display in dBm ? */
@@ -348,10 +309,12 @@ display_info(struct wireless_info *       info,
       else
        printf("%d  ", info->sens.value);
     }
+#endif /* WE_ESSENTIAL */
 
   printf("\n          ");
   tokens = 0;
 
+#ifndef WE_ESSENTIAL
   /* Display retry limit/lifetime information */
   if(info->has_retry)
     { 
@@ -365,7 +328,8 @@ display_info(struct wireless_info * info,
          if(info->retry.flags & IW_RETRY_TYPE)
            {
              iw_print_retry_value(buffer, sizeof(buffer),
-                                  info->retry.value, info->retry.flags);
+                                  info->retry.value, info->retry.flags,
+                                  info->range.we_version_compiled);
              printf("%s", buffer);
            }
 
@@ -386,12 +350,9 @@ display_info(struct wireless_info *        info,
       else
        {
          /* Fixed ? */
-         if(info->rts.fixed)
-           printf("RTS thr=");
-         else
-           printf("RTS thr:");
-
-         printf("%d B   ", info->rts.value);
+         printf("RTS thr%c%d B   ",
+                info->rts.fixed ? '=' : ':',
+                info->rts.value);
        }
       tokens += 3;
     }
@@ -413,18 +374,16 @@ display_info(struct wireless_info *       info,
       else
        {
          /* Fixed ? */
-         if(info->frag.fixed)
-           printf("Fragment thr=");
-         else
-           printf("Fragment thr:");
-
-         printf("%d B   ", info->frag.value);
+         printf("Fragment thr%c%d B   ",
+                info->frag.fixed ? '=' : ':',
+                info->frag.value);
        }
     }
 
   /* Formating */
   if(tokens > 0)
     printf("\n          ");
+#endif /* WE_ESSENTIAL */
 
   /* Display encryption information */
   /* Note : we display only the "current" key, use iwlist to list all keys */
@@ -451,22 +410,6 @@ display_info(struct wireless_info *        info,
       printf("\n          ");
     }
 
-#ifdef DISPLAY_WPA
-  /* Display WPA/802.1x/802.11i security parameters */
-  if(info->has_auth_key_mgmt || info->has_auth_cipher_pairwise ||
-     info->has_auth_cipher_group)
-    {
-      printf("Auth params:");
-      if(info->has_auth_key_mgmt)
-       printf(" key_mgmt:0x%X ", info->auth_key_mgmt);
-      if(info->has_auth_cipher_pairwise)
-       printf(" cipher_pairwise:0x%X ", info->auth_cipher_pairwise);
-      if(info->has_auth_cipher_group)
-       printf(" cipher_group:0x%X ", info->auth_cipher_group);
-      printf("\n          ");
-    }
-#endif
-
   /* Display Power Management information */
   /* Note : we display only one parameter, period or timeout. If a device
    * (such as HiperLan) has both, the user need to use iwlist... */
@@ -482,7 +425,8 @@ display_info(struct wireless_info * info,
          if(info->power.flags & IW_POWER_TYPE)
            {
              iw_print_pm_value(buffer, sizeof(buffer),
-                               info->power.value, info->power.flags);
+                               info->power.value, info->power.flags,
+                               info->range.we_version_compiled);
              printf("%s  ", buffer);
            }
 
@@ -558,864 +502,1403 @@ print_info(int         skfd,
   return(rc);
 }
 
-/************************* SETTING ROUTINES **************************/
+/****************** COMMAND LINE MODIFIERS PARSING ******************/
+/*
+ * Factor out the parsing of command line modifiers.
+ */
 
 /*------------------------------------------------------------------*/
 /*
- * Macro to handle errors when setting WE
- * Print a nice error message and exit...
- * We define them as macro so that "return" do the right thing.
- * The "do {...} while(0)" is a standard trick
+ * Map command line modifiers to the proper flags...
  */
-#define ERR_SET_EXT(rname, request) \
-       fprintf(stderr, "Error for wireless request \"%s\" (%X) :\n", \
-               rname, request)
-
-#define ABORT_ARG_NUM(rname, request) \
-       do { \
-               ERR_SET_EXT(rname, request); \
-               fprintf(stderr, "    too few arguments.\n"); \
-               return(-1); \
-       } while(0)
-
-#define ABORT_ARG_TYPE(rname, request, arg) \
-       do { \
-               ERR_SET_EXT(rname, request); \
-               fprintf(stderr, "    invalid argument \"%s\".\n", arg); \
-               return(-2); \
-       } while(0)
-
-#define ABORT_ARG_SIZE(rname, request, max) \
-       do { \
-               ERR_SET_EXT(rname, request); \
-               fprintf(stderr, "    argument too big (max %d)\n", max); \
-               return(-3); \
-       } while(0)
+typedef struct iwconfig_modifier {
+  const char *         cmd;            /* Command line shorthand */
+  __u16                        flag;           /* Flags to add */
+  __u16                        exclude;        /* Modifiers to exclude */
+} iwconfig_modifier;
 
 /*------------------------------------------------------------------*/
 /*
- * Wrapper to push some Wireless Parameter in the driver
- * Use standard wrapper and add pretty error message if fail...
+ * Modifiers for Power
  */
-#define IW_SET_EXT_ERR(skfd, ifname, request, wrq, rname) \
-       do { \
-       if(iw_set_ext(skfd, ifname, request, wrq) < 0) { \
-               ERR_SET_EXT(rname, request); \
-               fprintf(stderr, "    SET failed on device %-1.16s ; %s.\n", \
-                       ifname, strerror(errno)); \
-               return(-5); \
-       } } while(0)
+static const struct iwconfig_modifier  iwmod_power[] = {
+  { "min",     IW_POWER_MIN,           IW_POWER_MAX },
+  { "max",     IW_POWER_MAX,           IW_POWER_MIN },
+  { "period",  IW_POWER_PERIOD,        IW_POWER_TIMEOUT | IW_POWER_SAVING },
+  { "timeout", IW_POWER_TIMEOUT,       IW_POWER_PERIOD | IW_POWER_SAVING },
+  { "saving",  IW_POWER_SAVING,        IW_POWER_TIMEOUT | IW_POWER_PERIOD },
+};
+#define IWMOD_POWER_NUM        (sizeof(iwmod_power)/sizeof(iwmod_power[0]))
 
 /*------------------------------------------------------------------*/
 /*
- * Wrapper to extract some Wireless Parameter out of the driver
- * Use standard wrapper and add pretty error message if fail...
+ * Modifiers for Retry
  */
-#define IW_GET_EXT_ERR(skfd, ifname, request, wrq, rname) \
-       do { \
-       if(iw_get_ext(skfd, ifname, request, wrq) < 0) { \
-               ERR_SET_EXT(rname, request); \
-               fprintf(stderr, "    GET failed on device %-1.16s ; %s.\n", \
-                       ifname, strerror(errno)); \
-               return(-6); \
-       } } while(0)
+#ifndef WE_ESSENTIAL
+static const struct iwconfig_modifier  iwmod_retry[] = {
+  { "min",     IW_RETRY_MIN,           IW_RETRY_MAX },
+  { "max",     IW_RETRY_MAX,           IW_RETRY_MIN },
+  { "short",   IW_RETRY_SHORT,         IW_RETRY_LONG },
+  { "long",    IW_RETRY_LONG,          IW_RETRY_SHORT },
+  { "limit",   IW_RETRY_LIMIT,         IW_RETRY_LIFETIME },
+  { "lifetime",        IW_RETRY_LIFETIME,      IW_RETRY_LIMIT },
+};
+#define IWMOD_RETRY_NUM        (sizeof(iwmod_retry)/sizeof(iwmod_retry[0]))
+#endif /* WE_ESSENTIAL */
 
 /*------------------------------------------------------------------*/
 /*
- * Set the wireless options requested on command line
- * This function is too long and probably should be split,
- * because it look like the perfect definition of spaghetti code,
- * but I'm way to lazy
+ * Parse command line modifiers.
+ * Return error or number arg parsed.
+ * Modifiers must be at the beggining of command line.
  */
 static int
-set_info(int           skfd,           /* The socket */
-        char *         args[],         /* Command line args */
-        int            count,          /* Args count */
-        char *         ifname)         /* Dev name */
+parse_modifiers(char *         args[],         /* Command line args */
+               int             count,          /* Args count */
+               __u16 *         pout,           /* Flags to write */
+               const struct iwconfig_modifier  modifier[],
+               int             modnum)
 {
-  struct iwreq         wrq;
-  int                  i;
+  int          i = 0;
+  int          k = 0;
+  __u16                result = 0;     /* Default : no flag set */
 
-  /* if nothing after the device name - will never happen */
-  if(count < 1)
+  /* Get all modifiers and value types on the command line */
+  do
     {
-      fprintf(stderr, "Error : too few arguments.\n");
-      return(-1);
+      for(k = 0; k < modnum; k++)
+       {
+         /* Check if matches */
+         if(!strcasecmp(args[i], modifier[k].cmd))
+           {
+             /* Check for conflicting flags */
+             if(result & modifier[k].exclude)
+               {
+                 errarg = i;
+                 return(IWERR_ARG_CONFLICT);
+               }
+             /* Just add it */
+             result |= modifier[k].flag;
+             ++i;
+             break;
+           }
+       }
     }
+  /* For as long as current arg matched and not out of args */
+  while((i < count) && (k < modnum));
+
+  /* Check there remains one arg for value */
+  if(i >= count)
+    return(IWERR_ARG_NUM);
+
+  /* Return result */
+  *pout = result;
+  return(i);
+}
+
+
+/*********************** SETTING SUB-ROUTINES ***********************/
+/*
+ * The following functions are use to set some wireless parameters and
+ * are called by the set dispatcher set_info().
+ * They take as arguments the remaining of the command line, with
+ * arguments processed already removed.
+ * An error is indicated by a negative return value.
+ * 0 and positive return values indicate the number of args consumed.
+ */
 
-  /* The other args on the line specify options to be set... */
-  for(i = 0; i < count; i++)
+/*------------------------------------------------------------------*/
+/*
+ * Set ESSID
+ */
+static int
+set_essid_info(int             skfd,
+              char *           ifname,
+              char *           args[],         /* Command line args */
+              int              count)          /* Args count */
+{
+  struct iwreq         wrq;
+  int                  i = 1;
+  char                 essid[IW_ESSID_MAX_SIZE + 1];
+  int                  we_kernel_version;
+
+  if((!strcasecmp(args[0], "off")) ||
+     (!strcasecmp(args[0], "any")))
     {
-      /* ---------- Commit changes to driver ---------- */
-      if(!strncmp(args[i], "commit", 6))
-       {
-         /* No args */
-         IW_SET_EXT_ERR(skfd, ifname, SIOCSIWCOMMIT, &wrq,
-                        "Commit changes");
-         continue;
-       }
+      wrq.u.essid.flags = 0;
+      essid[0] = '\0';
+    }
+  else
+    if(!strcasecmp(args[0], "on"))
+      {
+       /* Get old essid */
+       memset(essid, '\0', sizeof(essid));
+       wrq.u.essid.pointer = (caddr_t) essid;
+       wrq.u.essid.length = IW_ESSID_MAX_SIZE + 1;
+       wrq.u.essid.flags = 0;
+       if(iw_get_ext(skfd, ifname, SIOCGIWESSID, &wrq) < 0)
+         return(IWERR_GET_EXT);
+       wrq.u.essid.flags = 1;
+      }
+    else
+      {
+       i = 0;
+
+       /* '-' or '--' allow to escape the ESSID string, allowing
+        * to set it to the string "any" or "off".
+        * This is a big ugly, but it will do for now */
+       if((!strcmp(args[0], "-")) || (!strcmp(args[0], "--")))
+         {
+           if(++i >= count)
+             return(IWERR_ARG_NUM);
+         }
+
+       /* Check the size of what the user passed us to avoid
+        * buffer overflows */
+       if(strlen(args[i]) > IW_ESSID_MAX_SIZE)
+         {
+           errmax = IW_ESSID_MAX_SIZE;
+           return(IWERR_ARG_SIZE);
+         }
+       else
+         {
+           int         temp;
 
-      /* ---------- Set network ID ---------- */
-      if((!strcasecmp(args[i], "nwid")) ||
-        (!strcasecmp(args[i], "domain")))
-       {
-         i++;
-         if(i >= count)
-           ABORT_ARG_NUM("Set NWID", SIOCSIWNWID);
-         if((!strcasecmp(args[i], "off")) ||
-            (!strcasecmp(args[i], "any")))
-           wrq.u.nwid.disabled = 1;
-         else
-           if(!strcasecmp(args[i], "on"))
+           wrq.u.essid.flags = 1;
+           strcpy(essid, args[i]);     /* Size checked, all clear */
+           i++;
+
+           /* Check for ESSID index */
+           if((i < count) &&
+              (sscanf(args[i], "[%i]", &temp) == 1) &&
+              (temp > 0) && (temp < IW_ENCODE_INDEX))
              {
-               /* Get old nwid */
-               IW_GET_EXT_ERR(skfd, ifname, SIOCGIWNWID, &wrq,
-                              "Set NWID");
-               wrq.u.nwid.disabled = 0;
+               wrq.u.essid.flags = temp;
+               ++i;
              }
-           else
-             if(sscanf(args[i], "%lX", (unsigned long *) &(wrq.u.nwid.value))
-                != 1)
-               ABORT_ARG_TYPE("Set NWID", SIOCSIWNWID, args[i]);
-             else
-               wrq.u.nwid.disabled = 0;
-         wrq.u.nwid.fixed = 1;
-
-         /* Set new nwid */
-         IW_SET_EXT_ERR(skfd, ifname, SIOCSIWNWID, &wrq,
-                        "Set NWID");
-         continue;
-       }
+         }
+      }
 
-      /* ---------- Set frequency / channel ---------- */
-      if((!strncmp(args[i], "freq", 4)) ||
-        (!strcmp(args[i], "channel")))
-       {
-         double                freq;
+  /* Get version from kernel, device may not have range... */
+  we_kernel_version = iw_get_kernel_we_version();
 
-         if(++i >= count)
-           ABORT_ARG_NUM("Set Frequency", SIOCSIWFREQ);
-         if(!strcasecmp(args[i], "auto"))
-           {
-             wrq.u.freq.m = -1;
-             wrq.u.freq.e = 0;
-             wrq.u.freq.flags = 0;
-           }
-         else
-           {
-             if(!strcasecmp(args[i], "fixed"))
-               {
-                 /* Get old bitrate */
-                 IW_GET_EXT_ERR(skfd, ifname, SIOCGIWFREQ, &wrq,
-                                "Set Bit Rate");
-                 wrq.u.freq.flags = IW_FREQ_FIXED;
-               }
-             else                      /* Should be a numeric value */
-               {
-                 if(sscanf(args[i], "%lg", &(freq)) != 1)
-                   ABORT_ARG_TYPE("Set Frequency", SIOCSIWFREQ, args[i]);
-                 if(index(args[i], 'G')) freq *= GIGA;
-                 if(index(args[i], 'M')) freq *= MEGA;
-                 if(index(args[i], 'k')) freq *= KILO;
+  /* Finally set the ESSID value */
+  wrq.u.essid.pointer = (caddr_t) essid;
+  wrq.u.essid.length = strlen(essid);
+  if(we_kernel_version < 21)
+    wrq.u.essid.length++;
 
-                 iw_float2freq(freq, &(wrq.u.freq));
+  if(iw_set_ext(skfd, ifname, SIOCSIWESSID, &wrq) < 0)
+    return(IWERR_SET_EXT);
 
-                 wrq.u.freq.flags = IW_FREQ_FIXED;
+  /* Var args */
+  return(i);
+}
 
-                 /* Check for an additional argument */
-                 if(((i+1) < count) &&
-                    (!strcasecmp(args[i+1], "auto")))
-                   {
-                     wrq.u.freq.flags = 0;
-                     ++i;
-                   }
-                 if(((i+1) < count) &&
-                    (!strcasecmp(args[i+1], "fixed")))
-                   {
-                     wrq.u.freq.flags = IW_FREQ_FIXED;
-                     ++i;
-                   }
-               }
-           }
+/*------------------------------------------------------------------*/
+/*
+ * Set Mode
+ */
+static int
+set_mode_info(int              skfd,
+             char *            ifname,
+             char *            args[],         /* Command line args */
+             int               count)          /* Args count */
+{
+  struct iwreq         wrq;
+  unsigned int         k;              /* Must be unsigned */
 
-         IW_SET_EXT_ERR(skfd, ifname, SIOCSIWFREQ, &wrq,
-                        "Set Frequency");
-         continue;
-       }
+  /* Avoid "Unused parameter" warning */
+  count = count;
+
+  /* Check if it is a uint, otherwise get is as a string */
+  if(sscanf(args[0], "%i", &k) != 1)
+    {
+      k = 0;
+      while((k < IW_NUM_OPER_MODE) &&
+           strncasecmp(args[0], iw_operation_mode[k], 3))
+       k++;
+    }
+  if(k >= IW_NUM_OPER_MODE)
+    {
+      errarg = 0;
+      return(IWERR_ARG_TYPE);
+    }
+
+  wrq.u.mode = k;
+  if(iw_set_ext(skfd, ifname, SIOCSIWMODE, &wrq) < 0)
+    return(IWERR_SET_EXT);
+
+  /* 1 arg */
+  return(1);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Set frequency/channel
+ */
+static int
+set_freq_info(int              skfd,
+             char *            ifname,
+             char *            args[],         /* Command line args */
+             int               count)          /* Args count */
+{
+  struct iwreq         wrq;
+  int                  i = 1;
 
-      /* ---------- Set sensitivity ---------- */
-      if(!strncmp(args[i], "sens", 4))
+  if(!strcasecmp(args[0], "auto"))
+    {
+      wrq.u.freq.m = -1;
+      wrq.u.freq.e = 0;
+      wrq.u.freq.flags = 0;
+    }
+  else
+    {
+      if(!strcasecmp(args[0], "fixed"))
        {
-         if(++i >= count)
-           ABORT_ARG_NUM("Set Sensitivity", SIOCSIWSENS);
-         if(sscanf(args[i], "%i", &(wrq.u.sens.value)) != 1)
-           ABORT_ARG_TYPE("Set Sensitivity", SIOCSIWSENS, args[i]);
-
-         IW_SET_EXT_ERR(skfd, ifname, SIOCSIWSENS, &wrq,
-                        "Set Sensitivity");
-         continue;
+         /* Get old frequency */
+         if(iw_get_ext(skfd, ifname, SIOCGIWFREQ, &wrq) < 0)
+           return(IWERR_GET_EXT);
+         wrq.u.freq.flags = IW_FREQ_FIXED;
        }
-
-      /* ---------- Set encryption stuff ---------- */
-      if((!strncmp(args[i], "enc", 3)) ||
-        (!strcmp(args[i], "key")))
+      else                     /* Should be a numeric value */
        {
-         unsigned char key[IW_ENCODING_TOKEN_MAX];
-
-         if(++i >= count)
-           ABORT_ARG_NUM("Set Encode", SIOCSIWENCODE);
+         double                freq;
+         char *                unit;
 
-         if(!strcasecmp(args[i], "on"))
+         freq = strtod(args[0], &unit);
+         if(unit == args[0])
            {
-             /* Get old encryption information */
-             wrq.u.data.pointer = (caddr_t) key;
-             wrq.u.data.length = IW_ENCODING_TOKEN_MAX;
-             wrq.u.data.flags = 0;
-             IW_GET_EXT_ERR(skfd, ifname, SIOCGIWENCODE, &wrq,
-                            "Set Encode");
-             wrq.u.data.flags &= ~IW_ENCODE_DISABLED;  /* Enable */
+             errarg = 0;
+             return(IWERR_ARG_TYPE);
            }
-         else
+         if(unit != NULL)
            {
-             int       gotone = 0;
-             int       oldone;
-             int       keylen;
-             int       temp;
-
-             wrq.u.data.pointer = (caddr_t) NULL;
-             wrq.u.data.flags = 0;
-             wrq.u.data.length = 0;
-
-             /* Allow arguments in any order (it's safe) */
-             do
-               {
-                 oldone = gotone;
-
-                 /* -- Check for the key -- */
-                 if(i < count)
-                   {
-                     keylen = iw_in_key_full(skfd, ifname,
-                                             args[i], key, &wrq.u.data.flags);
-                     if(keylen > 0)
-                       {
-                         wrq.u.data.length = keylen;
-                         wrq.u.data.pointer = (caddr_t) key;
-                         ++i;
-                         gotone++;
-                       }
-                   }
-
-                 /* -- Check for token index -- */
-                 if((i < count) &&
-                    (sscanf(args[i], "[%i]", &temp) == 1) &&
-                    (temp > 0) && (temp < IW_ENCODE_INDEX))
-                   {
-                     wrq.u.encoding.flags |= temp;
-                     ++i;
-                     gotone++;
-                   }
+             if(unit[0] == 'G') freq *= GIGA;
+             if(unit[0] == 'M') freq *= MEGA;
+             if(unit[0] == 'k') freq *= KILO;
+           }
 
-                 /* -- Check the various flags -- */
-                 if((i < count) && (!strcasecmp(args[i], "off")))
-                   {
-                     wrq.u.data.flags |= IW_ENCODE_DISABLED;
-                     ++i;
-                     gotone++;
-                   }
-                 if((i < count) && (!strcasecmp(args[i], "open")))
-                   {
-                     wrq.u.data.flags |= IW_ENCODE_OPEN;
-                     ++i;
-                     gotone++;
-                   }
-                 if((i < count) && (!strncasecmp(args[i], "restricted", 5)))
-                   {
-                     wrq.u.data.flags |= IW_ENCODE_RESTRICTED;
-                     ++i;
-                     gotone++;
-                   }
-                 if((i < count) && (!strncasecmp(args[i], "temporary", 4)))
-                   {
-                     wrq.u.data.flags |= IW_ENCODE_TEMP;
-                     ++i;
-                     gotone++;
-                   }
-               }
-             while(gotone != oldone);
+         iw_float2freq(freq, &(wrq.u.freq));
 
-             /* Pointer is absent in new API */
-             if(wrq.u.data.pointer == NULL)
-               wrq.u.data.flags |= IW_ENCODE_NOKEY;
+         wrq.u.freq.flags = IW_FREQ_FIXED;
 
-             /* Check if we have any invalid argument */
-             if(!gotone)
-               ABORT_ARG_TYPE("Set Encode", SIOCSIWENCODE, args[i]);
-             /* Get back to last processed argument */
-             --i;
+         /* Check for an additional argument */
+         if((i < count) && (!strcasecmp(args[i], "auto")))
+           {
+             wrq.u.freq.flags = 0;
+             ++i;
            }
-
-         IW_SET_EXT_ERR(skfd, ifname, SIOCSIWENCODE, &wrq,
-                        "Set Encode");
-         continue;
-       }
-
-      /* ---------- Set ESSID ---------- */
-      if(!strcasecmp(args[i], "essid"))
-       {
-         char          essid[IW_ESSID_MAX_SIZE + 1];
-         int           we_kernel_version;
-
-         i++;
-         if(i >= count)
-           ABORT_ARG_NUM("Set ESSID", SIOCSIWESSID);
-         if((!strcasecmp(args[i], "off")) ||
-            (!strcasecmp(args[i], "any")))
+         if((i < count) && (!strcasecmp(args[i], "fixed")))
            {
-             wrq.u.essid.flags = 0;
-             essid[0] = '\0';
+             wrq.u.freq.flags = IW_FREQ_FIXED;
+             ++i;
            }
-         else
-           if(!strcasecmp(args[i], "on"))
-             {
-               /* Get old essid */
-               memset(essid, '\0', sizeof(essid));
-               wrq.u.essid.pointer = (caddr_t) essid;
-               wrq.u.essid.length = IW_ESSID_MAX_SIZE + 1;
-               wrq.u.essid.flags = 0;
-               IW_GET_EXT_ERR(skfd, ifname, SIOCGIWESSID, &wrq,
-                              "Set ESSID");
-               wrq.u.essid.flags = 1;
-             }
-           else
-             {
-               /* '-' or '--' allow to escape the ESSID string, allowing
-                * to set it to the string "any" or "off".
-                * This is a big ugly, but it will do for now */
-               if((!strcmp(args[i], "-")) || (!strcmp(args[i], "--")))
-                 {
-                   i++;
-                   if(i >= count)
-                     ABORT_ARG_NUM("Set ESSID", SIOCSIWESSID);
-                 }
+       }
+    }
 
-               /* Check the size of what the user passed us to avoid
-                * buffer overflows */
-               if(strlen(args[i]) > IW_ESSID_MAX_SIZE)
-                 ABORT_ARG_SIZE("Set ESSID", SIOCSIWESSID, IW_ESSID_MAX_SIZE);
-               else
-                 {
-                   int         temp;
+  if(iw_set_ext(skfd, ifname, SIOCSIWFREQ, &wrq) < 0)
+    return(IWERR_SET_EXT);
 
-                   wrq.u.essid.flags = 1;
-                   strcpy(essid, args[i]);     /* Size checked, all clear */
+  /* Var args */
+  return(i);
+}
 
-                   /* Check for ESSID index */
-                   if(((i+1) < count) &&
-                      (sscanf(args[i+1], "[%i]", &temp) == 1) &&
-                      (temp > 0) && (temp < IW_ENCODE_INDEX))
-                     {
-                       wrq.u.essid.flags = temp;
-                       ++i;
-                     }
-                 }
-             }
+/*------------------------------------------------------------------*/
+/*
+ * Set Bit Rate
+ */
+static int
+set_bitrate_info(int           skfd,
+                char *         ifname,
+                char *         args[],         /* Command line args */
+                int            count)          /* Args count */
+{
+  struct iwreq         wrq;
+  int                  i = 1;
 
-         /* Get version from kernel, device may not have range... */
-         we_kernel_version = iw_get_kernel_we_version();
-
-         /* Finally set the ESSID value */
-         wrq.u.essid.pointer = (caddr_t) essid;
-         wrq.u.essid.length = strlen(essid) + 1;
-         if(we_kernel_version > 20)
-           wrq.u.essid.length--;
-         IW_SET_EXT_ERR(skfd, ifname, SIOCSIWESSID, &wrq,
-                        "Set ESSID");
-         continue;
+  wrq.u.bitrate.flags = 0;
+  if(!strcasecmp(args[0], "auto"))
+    {
+      wrq.u.bitrate.value = -1;
+      wrq.u.bitrate.fixed = 0;
+    }
+  else
+    {
+      if(!strcasecmp(args[0], "fixed"))
+       {
+         /* Get old bitrate */
+         if(iw_get_ext(skfd, ifname, SIOCGIWRATE, &wrq) < 0)
+           return(IWERR_GET_EXT);
+         wrq.u.bitrate.fixed = 1;
        }
-
-      /* ---------- Set AP address ---------- */
-      if(!strcasecmp(args[i], "ap"))
+      else                     /* Should be a numeric value */
        {
-         if(++i >= count)
-           ABORT_ARG_NUM("Set AP Address", SIOCSIWAP);
+         double                brate;
+         char *                unit;
 
-         if((!strcasecmp(args[i], "auto")) ||
-            (!strcasecmp(args[i], "any")))
+         brate = strtod(args[0], &unit);
+         if(unit == args[0])
            {
-             /* Send a broadcast address */
-             iw_broad_ether(&(wrq.u.ap_addr));
+             errarg = 0;
+             return(IWERR_ARG_TYPE);
            }
-         else
+         if(unit != NULL)
            {
-             if(!strcasecmp(args[i], "off"))
-               {
-                 /* Send a NULL address */
-                 iw_null_ether(&(wrq.u.ap_addr));
-               }
-             else
-               {
-                 /* Get the address and check if the interface supports it */
-                 if(iw_in_addr(skfd, ifname, args[i++], &(wrq.u.ap_addr)) < 0)
-                   ABORT_ARG_TYPE("Set AP Address", SIOCSIWAP, args[i-1]);
-               }
+             if(unit[0] == 'G') brate *= GIGA;
+             if(unit[0] == 'M') brate *= MEGA;
+             if(unit[0] == 'k') brate *= KILO;
            }
+         wrq.u.bitrate.value = (long) brate;
+         wrq.u.bitrate.fixed = 1;
 
-         IW_SET_EXT_ERR(skfd, ifname, SIOCSIWAP, &wrq,
-                        "Set AP Address");
-         continue;
-       }
-
-      /* ---------- Set NickName ---------- */
-      if(!strncmp(args[i], "nick", 4))
-       {
-         int           we_kernel_version;
-
-         i++;
-         if(i >= count)
-           ABORT_ARG_NUM("Set Nickname", SIOCSIWNICKN);
-         if(strlen(args[i]) > IW_ESSID_MAX_SIZE)
-           ABORT_ARG_SIZE("Set Nickname", SIOCSIWNICKN, IW_ESSID_MAX_SIZE);
-
-         we_kernel_version = iw_get_kernel_we_version();
-
-         wrq.u.essid.pointer = (caddr_t) args[i];
-         wrq.u.essid.length = strlen(args[i]) + 1;
-         if(we_kernel_version > 20)
-           wrq.u.essid.length--;
-         IW_SET_EXT_ERR(skfd, ifname, SIOCSIWNICKN, &wrq,
-                        "Set Nickname");
-         continue;
-       }
-
-      /* ---------- Set Bit-Rate ---------- */
-      if((!strncmp(args[i], "bit", 3)) ||
-        (!strcmp(args[i], "rate")))
-       {
-         if(++i >= count)
-           ABORT_ARG_NUM("Set Bit Rate", SIOCSIWRATE);
-         if(!strcasecmp(args[i], "auto"))
+         /* Check for an additional argument */
+         if((i < count) && (!strcasecmp(args[i], "auto")))
            {
-             wrq.u.bitrate.value = -1;
              wrq.u.bitrate.fixed = 0;
+             ++i;
            }
-         else
+         if((i < count) && (!strcasecmp(args[i], "fixed")))
            {
-             if(!strcasecmp(args[i], "fixed"))
-               {
-                 /* Get old bitrate */
-                 IW_GET_EXT_ERR(skfd, ifname, SIOCGIWRATE, &wrq,
-                                "Set Bit Rate");
-                 wrq.u.bitrate.fixed = 1;
-               }
-             else                      /* Should be a numeric value */
-               {
-                 double                brate;
-
-                 if(sscanf(args[i], "%lg", &(brate)) != 1)
-                   ABORT_ARG_TYPE("Set Bit Rate", SIOCSIWRATE, args[i]);
-                 if(index(args[i], 'G')) brate *= GIGA;
-                 if(index(args[i], 'M')) brate *= MEGA;
-                 if(index(args[i], 'k')) brate *= KILO;
-                 wrq.u.bitrate.value = (long) brate;
-                 wrq.u.bitrate.fixed = 1;
-
-                 /* Check for an additional argument */
-                 if(((i+1) < count) &&
-                    (!strcasecmp(args[i+1], "auto")))
-                   {
-                     wrq.u.bitrate.fixed = 0;
-                     ++i;
-                   }
-                 if(((i+1) < count) &&
-                    (!strcasecmp(args[i+1], "fixed")))
-                   {
-                     wrq.u.bitrate.fixed = 1;
-                     ++i;
-                   }
-               }
+             wrq.u.bitrate.fixed = 1;
+             ++i;
+           }
+         if((i < count) && (!strcasecmp(args[i], "unicast")))
+           {
+             wrq.u.bitrate.flags |= IW_BITRATE_UNICAST;
+             ++i;
+           }
+         if((i < count) && (!strcasecmp(args[i], "broadcast")))
+           {
+             wrq.u.bitrate.flags |= IW_BITRATE_BROADCAST;
+             ++i;
            }
-
-         IW_SET_EXT_ERR(skfd, ifname, SIOCSIWRATE, &wrq,
-                        "Set Bit Rate");
-         continue;
        }
+    }
+
+  if(iw_set_ext(skfd, ifname, SIOCSIWRATE, &wrq) < 0)
+    return(IWERR_SET_EXT);
+
+  /* Var args */
+  return(i);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Set encryption
+ */
+static int
+set_enc_info(int               skfd,
+            char *             ifname,
+            char *             args[],         /* Command line args */
+            int                count)          /* Args count */
+{
+  struct iwreq         wrq;
+  int                  i = 1;
+  unsigned char                key[IW_ENCODING_TOKEN_MAX];
 
-      /* ---------- Set RTS threshold ---------- */
-      if(!strncasecmp(args[i], "rts", 3))
+  if(!strcasecmp(args[0], "on"))
+    {
+      /* Get old encryption information */
+      wrq.u.data.pointer = (caddr_t) key;
+      wrq.u.data.length = IW_ENCODING_TOKEN_MAX;
+      wrq.u.data.flags = 0;
+      if(iw_get_ext(skfd, ifname, SIOCGIWENCODE, &wrq) < 0)
+       return(IWERR_GET_EXT);
+      wrq.u.data.flags &= ~IW_ENCODE_DISABLED; /* Enable */
+    }
+  else
+    {
+      int      gotone = 0;
+      int      oldone;
+      int      keylen;
+      int      temp;
+
+      wrq.u.data.pointer = (caddr_t) NULL;
+      wrq.u.data.flags = 0;
+      wrq.u.data.length = 0;
+      i = 0;
+
+      /* Allow arguments in any order (it's safe) */
+      do
        {
-         i++;
-         if(i >= count)
-           ABORT_ARG_NUM("Set RTS Threshold", SIOCSIWRTS);
-         wrq.u.rts.value = -1;
-         wrq.u.rts.fixed = 1;
-         wrq.u.rts.disabled = 0;
-         if(!strcasecmp(args[i], "off"))
-           wrq.u.rts.disabled = 1;     /* i.e. max size */
-         else
-           if(!strcasecmp(args[i], "auto"))
-             wrq.u.rts.fixed = 0;
-           else
-             {
-               if(!strcasecmp(args[i], "fixed"))
-                 {
-                   /* Get old RTS threshold */
-                   IW_GET_EXT_ERR(skfd, ifname, SIOCGIWRTS, &wrq,
-                                  "Set RTS Threshold");
-                   wrq.u.rts.fixed = 1;
-                 }
-               else                    /* Should be a numeric value */
-                 if(sscanf(args[i], "%li", (unsigned long *) &(wrq.u.rts.value))
-                    != 1)
-                   ABORT_ARG_TYPE("Set RTS Threshold", SIOCSIWRTS, args[i]);
+         oldone = gotone;
+
+         /* -- Check for the key -- */
+         if(i < count)
+           {
+             keylen = iw_in_key_full(skfd, ifname,
+                                     args[i], key, &wrq.u.data.flags);
+             if(keylen > 0)
+               {
+                 wrq.u.data.length = keylen;
+                 wrq.u.data.pointer = (caddr_t) key;
+                 ++i;
+                 gotone++;
+               }
            }
 
-         IW_SET_EXT_ERR(skfd, ifname, SIOCSIWRTS, &wrq,
-                        "Set RTS Threshold");
-         continue;
+         /* -- Check for token index -- */
+         if((i < count) &&
+            (sscanf(args[i], "[%i]", &temp) == 1) &&
+            (temp > 0) && (temp < IW_ENCODE_INDEX))
+           {
+             wrq.u.encoding.flags |= temp;
+             ++i;
+             gotone++;
+           }
+
+         /* -- Check the various flags -- */
+         if((i < count) && (!strcasecmp(args[i], "off")))
+           {
+             wrq.u.data.flags |= IW_ENCODE_DISABLED;
+             ++i;
+             gotone++;
+           }
+         if((i < count) && (!strcasecmp(args[i], "open")))
+           {
+             wrq.u.data.flags |= IW_ENCODE_OPEN;
+             ++i;
+             gotone++;
+           }
+         if((i < count) && (!strncasecmp(args[i], "restricted", 5)))
+           {
+             wrq.u.data.flags |= IW_ENCODE_RESTRICTED;
+             ++i;
+             gotone++;
+           }
+         if((i < count) && (!strncasecmp(args[i], "temporary", 4)))
+           {
+             wrq.u.data.flags |= IW_ENCODE_TEMP;
+             ++i;
+             gotone++;
+           }
        }
+      while(gotone != oldone);
+
+      /* Pointer is absent in new API */
+      if(wrq.u.data.pointer == NULL)
+       wrq.u.data.flags |= IW_ENCODE_NOKEY;
 
-      /* ---------- Set fragmentation threshold ---------- */
-      if(!strncmp(args[i], "frag", 4))
+      /* Check if we have any invalid argument */
+      if(!gotone)
        {
-         i++;
-         if(i >= count)
-           ABORT_ARG_NUM("Set Fragmentation Threshold", SIOCSIWFRAG);
-         wrq.u.frag.value = -1;
-         wrq.u.frag.fixed = 1;
-         wrq.u.frag.disabled = 0;
-         if(!strcasecmp(args[i], "off"))
-           wrq.u.frag.disabled = 1;    /* i.e. max size */
-         else
-           if(!strcasecmp(args[i], "auto"))
-             wrq.u.frag.fixed = 0;
+         errarg = 0;
+         return(IWERR_ARG_TYPE);
+       }
+    }
+
+  if(iw_set_ext(skfd, ifname, SIOCSIWENCODE, &wrq) < 0)
+    return(IWERR_SET_EXT);
+
+  /* Var arg */
+  return(i);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Set Power Management
+ */
+static int
+set_power_info(int             skfd,
+              char *           ifname,
+              char *           args[],         /* Command line args */
+              int              count)          /* Args count */
+{
+  struct iwreq         wrq;
+  int                  i = 1;
+
+  if(!strcasecmp(args[0], "off"))
+    wrq.u.power.disabled = 1;  /* i.e. max size */
+  else
+    if(!strcasecmp(args[0], "on"))
+      {
+       /* Get old Power info */
+       wrq.u.power.flags = 0;
+       if(iw_get_ext(skfd, ifname, SIOCGIWPOWER, &wrq) < 0)
+         return(IWERR_GET_EXT);
+       wrq.u.power.disabled = 0;
+      }
+    else
+      {
+       double          value;
+       char *          unit;
+       int             gotone = 0;
+
+       /* Parse modifiers */
+       i = parse_modifiers(args, count, &wrq.u.power.flags,
+                           iwmod_power, IWMOD_POWER_NUM);
+       if(i < 0)
+         return(i);
+
+       wrq.u.power.disabled = 0;
+
+       /* Is there any value to grab ? */
+       value = strtod(args[0], &unit);
+       if(unit != args[0])
+         {
+           struct iw_range     range;
+           int                 flags;
+           /* Extract range info to handle properly 'relative' */
+           if(iw_get_range_info(skfd, ifname, &range) < 0)
+             memset(&range, 0, sizeof(range));
+
+           /* Get the flags to be able to do the proper conversion */
+           switch(wrq.u.power.flags & IW_POWER_TYPE)
+             {
+             case IW_POWER_SAVING:
+               flags = range.pms_flags;
+               break;
+             case IW_POWER_TIMEOUT:
+               flags = range.pmt_flags;
+               break;
+             default:
+               flags = range.pmp_flags;
+               break;
+             }
+           /* Check if time or relative */
+           if(flags & IW_POWER_RELATIVE)
+             {
+               if(range.we_version_compiled < 21)
+                 value *= MEGA;
+               else
+                 wrq.u.power.flags |= IW_POWER_RELATIVE;
+             }
            else
              {
-               if(!strcasecmp(args[i], "fixed"))
-                 {
-                   /* Get old fragmentation threshold */
-                   IW_GET_EXT_ERR(skfd, ifname, SIOCGIWFRAG, &wrq,
-                                  "Set Fragmentation Threshold");
-                   wrq.u.frag.fixed = 1;
-                 }
-               else                    /* Should be a numeric value */
-                 if(sscanf(args[i], "%li",
-                           (unsigned long *) &(wrq.u.frag.value))
-                    != 1)
-                   ABORT_ARG_TYPE("Set Fragmentation Threshold", SIOCSIWFRAG,
-                                  args[i]);
-           }
+               value *= MEGA;  /* default = s */
+               if(unit[0] == 'u') value /= MEGA;
+               if(unit[0] == 'm') value /= KILO;
+             }
+           wrq.u.power.value = (long) value;
+           /* Set some default type if none */
+           if((wrq.u.power.flags & IW_POWER_TYPE) == 0)
+             wrq.u.power.flags |= IW_POWER_PERIOD;
+           ++i;
+           gotone = 1;
+         }
+
+       /* Now, check the mode */
+       if(i < count)
+         {
+           if(!strcasecmp(args[i], "all"))
+             wrq.u.power.flags |= IW_POWER_ALL_R;
+           if(!strncasecmp(args[i], "unicast", 4))
+             wrq.u.power.flags |= IW_POWER_UNICAST_R;
+           if(!strncasecmp(args[i], "multicast", 5))
+             wrq.u.power.flags |= IW_POWER_MULTICAST_R;
+           if(!strncasecmp(args[i], "force", 5))
+             wrq.u.power.flags |= IW_POWER_FORCE_S;
+           if(!strcasecmp(args[i], "repeat"))
+             wrq.u.power.flags |= IW_POWER_REPEATER;
+           if(wrq.u.power.flags & IW_POWER_MODE)
+             {
+               ++i;
+               gotone = 1;
+             }
+         }
+       if(!gotone)
+         {
+           errarg = i;
+           return(IWERR_ARG_TYPE);
+         }
+      }
+
+  if(iw_set_ext(skfd, ifname, SIOCSIWPOWER, &wrq) < 0)
+    return(IWERR_SET_EXT);
+
+  /* Var args */
+  return(i);
+}
 
-         IW_SET_EXT_ERR(skfd, ifname, SIOCSIWFRAG, &wrq,
-                        "Set Fragmentation Threshold");
-         continue;
-       }
+#ifndef WE_ESSENTIAL
+/*------------------------------------------------------------------*/
+/*
+ * Set Nickname
+ */
+static int
+set_nick_info(int              skfd,
+             char *            ifname,
+             char *            args[],         /* Command line args */
+             int               count)          /* Args count */
+{
+  struct iwreq         wrq;
+  int                  we_kernel_version;
+
+  /* Avoid "Unused parameter" warning */
+  count = count;
 
-      /* ---------- Set operation mode ---------- */
-      if(!strcmp(args[i], "mode"))
+  if(strlen(args[0]) > IW_ESSID_MAX_SIZE)
+    {
+      errmax = IW_ESSID_MAX_SIZE;
+      return(IWERR_ARG_SIZE);
+    }
+
+  we_kernel_version = iw_get_kernel_we_version();
+
+  wrq.u.essid.pointer = (caddr_t) args[0];
+  wrq.u.essid.length = strlen(args[0]);
+  if(we_kernel_version < 21)
+    wrq.u.essid.length++;
+
+  if(iw_set_ext(skfd, ifname, SIOCSIWNICKN, &wrq) < 0)
+    return(IWERR_SET_EXT);
+
+  /* 1 args */
+  return(1);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Set commit
+ */
+static int
+set_nwid_info(int              skfd,
+             char *            ifname,
+             char *            args[],         /* Command line args */
+             int               count)          /* Args count */
+{
+  struct iwreq         wrq;
+  unsigned long                temp;
+
+  /* Avoid "Unused parameter" warning */
+  count = count;
+
+  if((!strcasecmp(args[0], "off")) ||
+     (!strcasecmp(args[0], "any")))
+    wrq.u.nwid.disabled = 1;
+  else
+    if(!strcasecmp(args[0], "on"))
+      {
+       /* Get old nwid */
+       if(iw_get_ext(skfd, ifname, SIOCGIWNWID, &wrq) < 0)
+         return(IWERR_GET_EXT);
+       wrq.u.nwid.disabled = 0;
+      }
+    else
+      if(sscanf(args[0], "%lX", &(temp)) != 1)
        {
-         int   k;
+         errarg = 0;
+         return(IWERR_ARG_TYPE);
+       }
+      else
+       {
+         wrq.u.nwid.value = temp;
+         wrq.u.nwid.disabled = 0;
+       }
 
-         i++;
-         if(i >= count)
-           ABORT_ARG_NUM("Set Mode", SIOCSIWMODE);
+  wrq.u.nwid.fixed = 1;
 
-         if(sscanf(args[i], "%i", &k) != 1)
+  /* Set new nwid */
+  if(iw_set_ext(skfd, ifname, SIOCSIWNWID, &wrq) < 0)
+    return(IWERR_SET_EXT);
+
+  /* 1 arg */
+  return(1);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Set AP Address
+ */
+static int
+set_apaddr_info(int            skfd,
+               char *          ifname,
+               char *          args[],         /* Command line args */
+               int             count)          /* Args count */
+{
+  struct iwreq         wrq;
+
+  /* Avoid "Unused parameter" warning */
+  count = count;
+
+  if((!strcasecmp(args[0], "auto")) ||
+     (!strcasecmp(args[0], "any")))
+    {
+      /* Send a broadcast address */
+      iw_broad_ether(&(wrq.u.ap_addr));
+    }
+  else
+    {
+      if(!strcasecmp(args[0], "off"))
+       {
+         /* Send a NULL address */
+         iw_null_ether(&(wrq.u.ap_addr));
+       }
+      else
+       {
+         /* Get the address and check if the interface supports it */
+         if(iw_in_addr(skfd, ifname, args[0], &(wrq.u.ap_addr)) < 0)
            {
-             k = 0;
-             while((k < IW_NUM_OPER_MODE) &&
-                   strncasecmp(args[i], iw_operation_mode[k], 3))
-               k++;
+             errarg = 0;
+             return(IWERR_ARG_TYPE);
            }
-         if((k >= IW_NUM_OPER_MODE) || (k < 0))
-           ABORT_ARG_TYPE("Set Mode", SIOCSIWMODE, args[i]);
-
-         wrq.u.mode = k;
-         IW_SET_EXT_ERR(skfd, ifname, SIOCSIWMODE, &wrq,
-                        "Set Mode");
-         continue;
        }
+    }
 
-      /* ---------- Set Power Management ---------- */
-      if(!strncmp(args[i], "power", 3))
-       {
-         if(++i >= count)
-           ABORT_ARG_NUM("Set Power Management", SIOCSIWPOWER);
+  if(iw_set_ext(skfd, ifname, SIOCSIWAP, &wrq) < 0)
+    return(IWERR_SET_EXT);
 
-         if(!strcasecmp(args[i], "off"))
-           wrq.u.power.disabled = 1;   /* i.e. max size */
-         else
-           if(!strcasecmp(args[i], "on"))
+  /* 1 args */
+  return(1);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Set Tx Power
+ */
+static int
+set_txpower_info(int           skfd,
+               char *          ifname,
+               char *          args[],         /* Command line args */
+               int             count)          /* Args count */
+{
+  struct iwreq         wrq;
+  int                  i = 1;
+
+  /* Avoid "Unused parameter" warning */
+  args = args; count = count;
+
+  /* Prepare the request */
+  wrq.u.txpower.value = -1;
+  wrq.u.txpower.fixed = 1;
+  wrq.u.txpower.disabled = 0;
+  wrq.u.txpower.flags = IW_TXPOW_DBM;
+
+  if(!strcasecmp(args[0], "off"))
+    wrq.u.txpower.disabled = 1;        /* i.e. turn radio off */
+  else
+    if(!strcasecmp(args[0], "auto"))
+      wrq.u.txpower.fixed = 0; /* i.e. use power control */
+    else
+      {
+       if(!strcasecmp(args[0], "on"))
+         {
+           /* Get old tx-power */
+           if(iw_get_ext(skfd, ifname, SIOCGIWTXPOW, &wrq) < 0)
+             return(IWERR_GET_EXT);
+           wrq.u.txpower.disabled = 0;
+         }
+       else
+         {
+           if(!strcasecmp(args[0], "fixed"))
              {
-               /* Get old Power info */
-               wrq.u.power.flags = 0;
-               IW_GET_EXT_ERR(skfd, ifname, SIOCGIWPOWER, &wrq,
-                              "Set Power Management");
-               wrq.u.power.disabled = 0;
+               /* Get old tx-power */
+               if(iw_get_ext(skfd, ifname, SIOCGIWTXPOW, &wrq) < 0)
+                 return(IWERR_GET_EXT);
+               wrq.u.txpower.fixed = 1;
+               wrq.u.txpower.disabled = 0;
              }
-           else
+           else                        /* Should be a numeric value */
              {
-               double          temp;
-               int             gotone = 0;
-               /* Default - nope */
-               wrq.u.power.flags = IW_POWER_ON;
-               wrq.u.power.disabled = 0;
-
-               /* Check value modifier */
-               if(!strcasecmp(args[i], "min"))
+               int             power;
+               int             ismwatt = 0;
+               struct iw_range range;
+
+               /* Extract range info to do proper conversion */
+               if(iw_get_range_info(skfd, ifname, &range) < 0)
+                 memset(&range, 0, sizeof(range));
+
+               /* Get the value */
+               if(sscanf(args[0], "%i", &(power)) != 1)
                  {
-                   wrq.u.power.flags |= IW_POWER_MIN;
-                   if(++i >= count)
-                     ABORT_ARG_NUM("Set Power Management", SIOCSIWPOWER);
+                   errarg = 0;
+                   return(IWERR_ARG_TYPE);
                  }
-               else
-                 if(!strcasecmp(args[i], "max"))
-                   {
-                     wrq.u.power.flags |= IW_POWER_MAX;
-                     if(++i >= count)
-                       ABORT_ARG_NUM("Set Power Management", SIOCSIWPOWER);
-                   }
 
-               /* Check value type */
-               if(!strcasecmp(args[i], "period"))
+               /* Check if milliWatt
+                * We authorise a single 'm' as a shorthand for 'mW',
+                * on the other hand a 'd' probably means 'dBm'... */
+               ismwatt = ((strchr(args[0], 'm') != NULL)
+                          && (strchr(args[0], 'd') == NULL));
+
+               /* We could check 'W' alone... Another time... */
+
+               /* Convert */
+               if(range.txpower_capa & IW_TXPOW_RELATIVE)
                  {
-                   wrq.u.power.flags |= IW_POWER_PERIOD;
-                   if(++i >= count)
-                     ABORT_ARG_NUM("Set Power Management", SIOCSIWPOWER);
+                   /* Can't convert */
+                   if(ismwatt)
+                     {
+                       errarg = 0;
+                       return(IWERR_ARG_TYPE);
+                     }
+                   wrq.u.txpower.flags = IW_TXPOW_RELATIVE;
                  }
                else
-                 if(!strcasecmp(args[i], "timeout"))
+                 if(range.txpower_capa & IW_TXPOW_MWATT)
+                   {
+                     if(!ismwatt)
+                       power = iw_dbm2mwatt(power);
+                     wrq.u.txpower.flags = IW_TXPOW_MWATT;
+                   }
+                 else
                    {
-                     wrq.u.power.flags |= IW_POWER_TIMEOUT;
-                     if(++i >= count)
-                       ABORT_ARG_NUM("Set Power Management", SIOCSIWPOWER);
+                     if(ismwatt)
+                       power = iw_mwatt2dbm(power);
+                     wrq.u.txpower.flags = IW_TXPOW_DBM;
                    }
+               wrq.u.txpower.value = power;
 
-               /* Is there any value to grab ? */
-               if(sscanf(args[i], "%lg", &(temp)) == 1)
+               /* Check for an additional argument */
+               if((i < count) && (!strcasecmp(args[i], "auto")))
                  {
-                   temp *= MEGA;       /* default = s */
-                   if(index(args[i], 'u')) temp /= MEGA;
-                   if(index(args[i], 'm')) temp /= KILO;
-                   wrq.u.power.value = (long) temp;
-                   if((wrq.u.power.flags & IW_POWER_TYPE) == 0)
-                     wrq.u.power.flags |= IW_POWER_PERIOD;
+                   wrq.u.txpower.fixed = 0;
                    ++i;
-                   gotone = 1;
                  }
-
-               /* Now, check the mode */
-               if(i < count)
+               if((i < count) && (!strcasecmp(args[i], "fixed")))
                  {
-                   if(!strcasecmp(args[i], "all"))
-                     wrq.u.power.flags |= IW_POWER_ALL_R;
-                   if(!strncasecmp(args[i], "unicast", 4))
-                     wrq.u.power.flags |= IW_POWER_UNICAST_R;
-                   if(!strncasecmp(args[i], "multicast", 5))
-                     wrq.u.power.flags |= IW_POWER_MULTICAST_R;
-                   if(!strncasecmp(args[i], "force", 5))
-                     wrq.u.power.flags |= IW_POWER_FORCE_S;
-                   if(!strcasecmp(args[i], "repeat"))
-                     wrq.u.power.flags |= IW_POWER_REPEATER;
-                   if(wrq.u.power.flags & IW_POWER_MODE)
-                     {
-                       ++i;
-                       gotone = 1;
-                     }
+                   wrq.u.txpower.fixed = 1;
+                   ++i;
                  }
-               if(!gotone)
-                 ABORT_ARG_TYPE("Set Power Management", SIOCSIWPOWER,
-                                args[i]);
-               --i;
              }
+         }
+      }
+
+  if(iw_set_ext(skfd, ifname, SIOCSIWTXPOW, &wrq) < 0)
+    return(IWERR_SET_EXT);
 
-         IW_SET_EXT_ERR(skfd, ifname, SIOCSIWPOWER, &wrq,
-                      "Set Power Management");
-         continue;
-       }
+  /* Var args */
+  return(i);
+}
 
-      /* ---------- Set Transmit-Power ---------- */
-      if(!strncmp(args[i], "txpower", 3))
+/*------------------------------------------------------------------*/
+/*
+ * Set Sensitivity
+ */
+static int
+set_sens_info(int              skfd,
+             char *            ifname,
+             char *            args[],         /* Command line args */
+             int               count)          /* Args count */
+{
+  struct iwreq         wrq;
+  int                  temp;
+
+  /* Avoid "Unused parameter" warning */
+  count = count;
+
+  if(sscanf(args[0], "%i", &(temp)) != 1)
+    {
+      errarg = 0;
+      return(IWERR_ARG_TYPE);
+    }
+  wrq.u.sens.value = temp;
+
+  if(iw_set_ext(skfd, ifname, SIOCSIWSENS, &wrq) < 0)
+    return(IWERR_SET_EXT);
+
+  /* 1 arg */
+  return(1);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Set Retry Limit
+ */
+static int
+set_retry_info(int             skfd,
+              char *           ifname,
+              char *           args[],         /* Command line args */
+              int              count)          /* Args count */
+{
+  struct iwreq         wrq;
+  int                  i = 0;
+  double               value;
+  char *               unit;
+
+  /* Parse modifiers */
+  i = parse_modifiers(args, count, &wrq.u.retry.flags,
+                     iwmod_retry, IWMOD_RETRY_NUM);
+  if(i < 0)
+    return(i);
+
+  /* Add default type if none */
+  if((wrq.u.retry.flags & IW_RETRY_TYPE) == 0)
+    wrq.u.retry.flags |= IW_RETRY_LIMIT;
+
+  wrq.u.retry.disabled = 0;
+
+  /* Is there any value to grab ? */
+  value = strtod(args[0], &unit);
+  if(unit == args[0])
+    {
+      errarg = i;
+      return(IWERR_ARG_TYPE);
+    }
+
+  /* Limit is absolute, on the other hand lifetime is seconds */
+  if(wrq.u.retry.flags & IW_RETRY_LIFETIME)
+    {
+      struct iw_range  range;
+      /* Extract range info to handle properly 'relative' */
+      if(iw_get_range_info(skfd, ifname, &range) < 0)
+       memset(&range, 0, sizeof(range));
+
+      if(range.r_time_flags & IW_RETRY_RELATIVE)
        {
-         struct iw_range       range;
-
-         if(++i >= count)
-           ABORT_ARG_NUM("Set Tx Power", SIOCSIWTXPOW);
-
-         /* Extract range info */
-         if(iw_get_range_info(skfd, ifname, &range) < 0)
-           memset(&range, 0, sizeof(range));
-
-         /* Prepare the request */
-         wrq.u.txpower.value = -1;
-         wrq.u.txpower.fixed = 1;
-         wrq.u.txpower.disabled = 0;
-         wrq.u.txpower.flags = IW_TXPOW_DBM;
-         if(!strcasecmp(args[i], "off"))
-           wrq.u.txpower.disabled = 1; /* i.e. turn radio off */
+         if(range.we_version_compiled < 21)
+           value *= MEGA;
          else
-           if(!strcasecmp(args[i], "auto"))
-             wrq.u.txpower.fixed = 0;  /* i.e. use power control */
-           else
-             {
-               if(!strcasecmp(args[i], "on"))
-                 {
-                   /* Get old tx-power */
-                   IW_GET_EXT_ERR(skfd, ifname, SIOCGIWTXPOW, &wrq,
-                                  "Set Tx Power");
-                   wrq.u.txpower.disabled = 0;
-                 }
-               else
-                 {
-                   if(!strcasecmp(args[i], "fixed"))
-                     {
-                       /* Get old tx-power */
-                       IW_GET_EXT_ERR(skfd, ifname, SIOCGIWTXPOW, &wrq,
-                                      "Set Tx Power");
-                       wrq.u.txpower.fixed = 1;
-                       wrq.u.txpower.disabled = 0;
-                     }
-                   else                        /* Should be a numeric value */
-                     {
-                       int             power;
-                       int             ismwatt = 0;
-
-                       /* Get the value */
-                       if(sscanf(args[i], "%i", &(power)) != 1)
-                         ABORT_ARG_TYPE("Set Tx Power", SIOCSIWTXPOW,
-                                        args[i]);
-
-                       /* Check if milliWatt
-                        * We authorise a single 'm' as a shorthand for 'mW',
-                        * on the other hand a 'd' probably means 'dBm'... */
-                       ismwatt = ((index(args[i], 'm') != NULL)
-                                  && (index(args[i], 'd') == NULL));
-
-                       /* We could check 'W' alone... Another time... */
-
-                       /* Convert */
-                       if(range.txpower_capa & IW_TXPOW_RELATIVE)
-                         {
-                           /* Can't convert */
-                           if(ismwatt)
-                             ABORT_ARG_TYPE("Set Tx Power",
-                                            SIOCSIWTXPOW,
-                                            args[i]);
-                         }
-                       else
-                         if(range.txpower_capa & IW_TXPOW_MWATT)
-                           {
-                             if(!ismwatt)
-                               power = iw_dbm2mwatt(power);
-                             wrq.u.txpower.flags = IW_TXPOW_MWATT;
-                           }
-                         else
-                           {
-                             if(ismwatt)
-                               power = iw_mwatt2dbm(power);
-                             wrq.u.txpower.flags = IW_TXPOW_DBM;
-                           }
-                       wrq.u.txpower.value = power;
-
-                       /* Check for an additional argument */
-                       if(((i+1) < count) &&
-                          (!strcasecmp(args[i+1], "auto")))
-                         {
-                           wrq.u.txpower.fixed = 0;
-                           ++i;
-                         }
-                       if(((i+1) < count) &&
-                          (!strcasecmp(args[i+1], "fixed")))
-                         {
-                           wrq.u.txpower.fixed = 1;
-                           ++i;
-                         }
-                     }
-                 }
-             }
-
-         IW_SET_EXT_ERR(skfd, ifname, SIOCSIWTXPOW, &wrq,
-                        "Set Tx Power");
-         continue;
+           wrq.u.retry.flags |= IW_RETRY_RELATIVE;
        }
-
-      /* ---------- Set Retry limit ---------- */
-      if(!strncmp(args[i], "retry", 3))
+      else
        {
-         double                temp;
-         int           gotone = 0;
+         /* Normalise lifetime */
+         value *= MEGA;        /* default = s */
+         if(unit[0] == 'u') value /= MEGA;
+         if(unit[0] == 'm') value /= KILO;
+       }
+    }
+  wrq.u.retry.value = (long) value;
+  ++i;
 
-         if(++i >= count)
-           ABORT_ARG_NUM("Set Retry Limit", SIOCSIWRETRY);
+  if(iw_set_ext(skfd, ifname, SIOCSIWRETRY, &wrq) < 0)
+    return(IWERR_SET_EXT);
 
-         /* Default - nope */
-         wrq.u.retry.flags = IW_RETRY_LIMIT;
-         wrq.u.retry.disabled = 0;
+  /* Var args */
+  return(i);
+}
 
-         /* Check value modifier */
-         if(!strcasecmp(args[i], "min"))
-           {
-             wrq.u.retry.flags |= IW_RETRY_MIN;
-             if(++i >= count)
-               ABORT_ARG_NUM("Set Retry Limit", SIOCSIWRETRY);
-           }
-         else
-           if(!strcasecmp(args[i], "max"))
+/*------------------------------------------------------------------*/
+/*
+ * Set RTS Threshold
+ */
+static int
+set_rts_info(int               skfd,
+            char *             ifname,
+            char *             args[],         /* Command line args */
+            int                count)          /* Args count */
+{
+  struct iwreq         wrq;
+
+  /* Avoid "Unused parameter" warning */
+  count = count;
+
+  wrq.u.rts.value = -1;
+  wrq.u.rts.fixed = 1;
+  wrq.u.rts.disabled = 0;
+
+  if(!strcasecmp(args[0], "off"))
+    wrq.u.rts.disabled = 1;    /* i.e. max size */
+  else
+    if(!strcasecmp(args[0], "auto"))
+      wrq.u.rts.fixed = 0;
+    else
+      {
+       if(!strcasecmp(args[0], "fixed"))
+         {
+           /* Get old RTS threshold */
+           if(iw_get_ext(skfd, ifname, SIOCGIWRTS, &wrq) < 0)
+             return(IWERR_GET_EXT);
+           wrq.u.rts.fixed = 1;
+         }
+       else
+         {     /* Should be a numeric value */
+           long        temp;
+           if(sscanf(args[0], "%li", (unsigned long *) &(temp)) != 1)
              {
-               wrq.u.retry.flags |= IW_RETRY_MAX;
-               if(++i >= count)
-                 ABORT_ARG_NUM("Set Retry Limit", SIOCSIWRETRY);
+               errarg = 0;
+               return(IWERR_ARG_TYPE);
              }
+           wrq.u.rts.value = temp;
+         }
+      }
 
-         /* Check value type */
-         if(!strcasecmp(args[i], "limit"))
-           {
-             wrq.u.retry.flags |= IW_RETRY_LIMIT;
-             if(++i >= count)
-               ABORT_ARG_NUM("Set Retry Limit", SIOCSIWRETRY);
-           }
-         else
-           if(!strncasecmp(args[i], "lifetime", 4))
+  if(iw_set_ext(skfd, ifname, SIOCSIWRTS, &wrq) < 0)
+    return(IWERR_SET_EXT);
+
+  /* 1 arg */
+  return(1);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Set Fragmentation Threshold
+ */
+static int
+set_frag_info(int              skfd,
+             char *            ifname,
+             char *            args[],         /* Command line args */
+             int               count)          /* Args count */
+{
+  struct iwreq         wrq;
+
+  /* Avoid "Unused parameter" warning */
+  count = count;
+
+  wrq.u.frag.value = -1;
+  wrq.u.frag.fixed = 1;
+  wrq.u.frag.disabled = 0;
+
+  if(!strcasecmp(args[0], "off"))
+    wrq.u.frag.disabled = 1;   /* i.e. max size */
+  else
+    if(!strcasecmp(args[0], "auto"))
+      wrq.u.frag.fixed = 0;
+    else
+      {
+       if(!strcasecmp(args[0], "fixed"))
+         {
+           /* Get old fragmentation threshold */
+           if(iw_get_ext(skfd, ifname, SIOCGIWFRAG, &wrq) < 0)
+             return(IWERR_GET_EXT);
+           wrq.u.frag.fixed = 1;
+         }
+       else
+         {     /* Should be a numeric value */
+           long        temp;
+           if(sscanf(args[0], "%li", &(temp))
+              != 1)
              {
-               wrq.u.retry.flags &= ~IW_RETRY_LIMIT;
-               wrq.u.retry.flags |= IW_RETRY_LIFETIME;
-               if(++i >= count)
-                 ABORT_ARG_NUM("Set Retry Limit", SIOCSIWRETRY);
+               errarg = 0;
+               return(IWERR_ARG_TYPE);
              }
+           wrq.u.frag.value = temp;
+         }
+      }
+
+  if(iw_set_ext(skfd, ifname, SIOCSIWFRAG, &wrq) < 0)
+    return(IWERR_SET_EXT);
+
+  /* 1 arg */
+  return(1);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Set Modulation
+ */
+static int
+set_modulation_info(int                skfd,
+                   char *      ifname,
+                   char *      args[],         /* Command line args */
+                   int         count)          /* Args count */
+{
+  struct iwreq         wrq;
+  int                  i = 1;
+
+  /* Avoid "Unused parameter" warning */
+  args = args; count = count;
+
+  if(!strcasecmp(args[0], "auto"))
+    wrq.u.param.fixed = 0;     /* i.e. use any modulation */
+  else
+    {
+      if(!strcasecmp(args[0], "fixed"))
+       {
+         /* Get old modulation */
+         if(iw_get_ext(skfd, ifname, SIOCGIWMODUL, &wrq) < 0)
+           return(IWERR_GET_EXT);
+         wrq.u.param.fixed = 1;
+       }
+      else
+       {
+         int           k;
 
-         /* Is there any value to grab ? */
-         if(sscanf(args[i], "%lg", &(temp)) == 1)
+         /* Allow multiple modulations, combine them together */
+         wrq.u.param.value = 0x0;
+         i = 0;
+         do
            {
-             /* Limit is absolute, on the other hand lifetime is seconds */
-             if(!(wrq.u.retry.flags & IW_RETRY_LIMIT))
+             for(k = 0; k < IW_SIZE_MODUL_LIST; k++)
                {
-                 /* Normalise lifetime */
-                 temp *= MEGA; /* default = s */
-                 if(index(args[i], 'u')) temp /= MEGA;
-                 if(index(args[i], 'm')) temp /= KILO;
+                 if(!strcasecmp(args[i], iw_modul_list[k].cmd))
+                   {
+                     wrq.u.param.value |= iw_modul_list[k].mask;
+                     ++i;
+                     break;
+                   }
                }
-             wrq.u.retry.value = (long) temp;
+           }
+         /* For as long as current arg matched and not out of args */
+         while((i < count) && (k < IW_SIZE_MODUL_LIST));
+
+         /* Check we got something */
+         if(i == 0)
+           {
+             errarg = 0;
+             return(IWERR_ARG_TYPE);
+           }
+
+         /* Check for an additional argument */
+         if((i < count) && (!strcasecmp(args[i], "auto")))
+           {
+             wrq.u.param.fixed = 0;
+             ++i;
+           }
+         if((i < count) && (!strcasecmp(args[i], "fixed")))
+           {
+             wrq.u.param.fixed = 1;
              ++i;
-             gotone = 1;
            }
+       }
+    }
+
+  if(iw_set_ext(skfd, ifname, SIOCSIWMODUL, &wrq) < 0)
+    return(IWERR_SET_EXT);
+
+  /* Var args */
+  return(i);
+}
+#endif /* WE_ESSENTIAL */
+
+/*------------------------------------------------------------------*/
+/*
+ * Set commit
+ */
+static int
+set_commit_info(int            skfd,
+               char *          ifname,
+               char *          args[],         /* Command line args */
+               int             count)          /* Args count */
+{
+  struct iwreq         wrq;
+
+  /* Avoid "Unused parameter" warning */
+  args = args; count = count;
+
+  if(iw_set_ext(skfd, ifname, SIOCSIWCOMMIT, &wrq) < 0)
+    return(IWERR_SET_EXT);
+
+  /* No args */
+  return(0);
+}
 
-         if(!gotone)
-           ABORT_ARG_TYPE("Set Retry Limit", SIOCSIWRETRY, args[i]);
-         --i;
+/************************** SET DISPATCHER **************************/
+/*
+ * This is a modified version of the dispatcher in iwlist.
+ * The main difference is that here we may have multiple commands per
+ * line. Also, most commands here do take arguments, and most often
+ * a variable number of them.
+ * Therefore, the handler *must* return how many args were consumed...
+ *
+ * Note that the use of multiple commands per line is not advised
+ * in scripts, as it makes error management hard. All commands before
+ * the error are executed, but commands after the error are not
+ * processed.
+ * We also try to give as much clue as possible via stderr to the caller
+ * on which command did fail, but if there are two time the same command,
+ * you don't know which one failed...
+ */
 
-         IW_SET_EXT_ERR(skfd, ifname, SIOCSIWRETRY, &wrq,
-                        "Set Retry Limit");
-         continue;
+/*------------------------------------------------------------------*/
+/*
+ * Map command line arguments to the proper procedure...
+ */
+typedef struct iwconfig_entry {
+  const char *         cmd;            /* Command line shorthand */
+  iw_enum_handler      fn;             /* Subroutine */
+  int                  min_count;
+  int                  request;        /* WE numerical ID */
+  const char *         name;           /* Human readable string */
+  const char *         argsname;       /* Args as human readable string */
+} iwconfig_cmd;
+
+static const struct iwconfig_entry iwconfig_cmds[] = {
+  { "essid",           set_essid_info,         1,      SIOCSIWESSID,
+       "Set ESSID",                    "{NNN|any|on|off}" },
+  { "mode",            set_mode_info,          1,      SIOCSIWMODE,
+       "Set Mode",                     "{managed|ad-hoc|master|...}" },
+  { "freq",            set_freq_info,          1,      SIOCSIWFREQ,
+       "Set Frequency",                "N.NNN[k|M|G]" },
+  { "channel",         set_freq_info,          1,      SIOCSIWFREQ,
+       "Set Frequency",                "N" },
+  { "bit",             set_bitrate_info,       1,      SIOCSIWRATE,
+       "Set Bit Rate",                 "{N[k|M|G]|auto|fixed}" },
+  { "rate",            set_bitrate_info,       1,      SIOCSIWRATE,
+       "Set Bit Rate",                 "{N[k|M|G]|auto|fixed}" },
+  { "enc",             set_enc_info,           1,      SIOCSIWENCODE,
+       "Set Encode",                   "{NNNN-NNNN|off}" },
+  { "key",             set_enc_info,           1,      SIOCSIWENCODE,
+       "Set Encode",                   "{NNNN-NNNN|off}"  },
+  { "power",           set_power_info,         1,      SIOCSIWPOWER,
+       "Set Power Management",         "{period N|timeout N|saving N|off}" },
+#ifndef WE_ESSENTIAL
+  { "nickname",                set_nick_info,          1,      SIOCSIWNICKN,
+       "Set Nickname",                 "NNN" },
+  { "nwid",            set_nwid_info,          1,      SIOCSIWNWID,
+       "Set NWID",                     "{NN|on|off}" },
+  { "ap",              set_apaddr_info,        1,      SIOCSIWAP,
+       "Set AP Address",               "{N|off|auto}" },
+  { "txpower",         set_txpower_info,       1,      SIOCSIWTXPOW,
+       "Set Tx Power",                 "{NmW|NdBm|off|auto}" },
+  { "sens",            set_sens_info,          1,      SIOCSIWSENS,
+       "Set Sensitivity",              "N" },
+  { "retry",           set_retry_info,         1,      SIOCSIWRETRY,
+       "Set Retry Limit",              "{limit N|lifetime N}" },
+  { "rts",             set_rts_info,           1,      SIOCSIWRTS,
+       "Set RTS Threshold",            "{N|auto|fixed|off}" },
+  { "frag",            set_frag_info,          1,      SIOCSIWFRAG,
+       "Set Fragmentation Threshold",  "{N|auto|fixed|off}" },
+  { "modulation",      set_modulation_info,    1,      SIOCGIWMODUL,
+       "Set Modulation",               "{11g|11a|CCK|OFDMg|...}" },
+#endif /* WE_ESSENTIAL */
+  { "commit",          set_commit_info,        0,      SIOCSIWCOMMIT,
+       "Commit changes",               "" },
+  { NULL, NULL, 0, 0, NULL, NULL },
+};
+
+/*------------------------------------------------------------------*/
+/*
+ * Find the most appropriate command matching the command line
+ */
+static inline const iwconfig_cmd *
+find_command(const char *      cmd)
+{
+  const iwconfig_cmd * found = NULL;
+  int                  ambig = 0;
+  unsigned int         len = strlen(cmd);
+  int                  i;
+
+  /* Go through all commands */
+  for(i = 0; iwconfig_cmds[i].cmd != NULL; ++i)
+    {
+      /* No match -> next one */
+      if(strncasecmp(iwconfig_cmds[i].cmd, cmd, len) != 0)
+       continue;
+
+      /* Exact match -> perfect */
+      if(len == strlen(iwconfig_cmds[i].cmd))
+       return &iwconfig_cmds[i];
+
+      /* Partial match */
+      if(found == NULL)
+       /* First time */
+       found = &iwconfig_cmds[i];
+      else
+       /* Another time */
+       if (iwconfig_cmds[i].fn != found->fn)
+         ambig = 1;
+    }
+
+  if(found == NULL)
+    {
+      fprintf(stderr, "iwconfig: unknown command \"%s\"\n", cmd);
+      return NULL;
+    }
+
+  if(ambig)
+    {
+      fprintf(stderr, "iwconfig: command \"%s\" is ambiguous\n", cmd);
+      return NULL;
+    }
+
+  return found;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Set the wireless options requested on command line
+ * Find the individual commands and call the appropriate subroutine
+ */
+static int
+set_info(int           skfd,           /* The socket */
+        char *         args[],         /* Command line args */
+        int            count,          /* Args count */
+        char *         ifname)         /* Dev name */
+{
+  const iwconfig_cmd * iwcmd;
+  int                  ret;
+
+  /* Loop until we run out of args... */
+  while(count > 0)
+    {
+      /* find the command matching the keyword */
+      iwcmd = find_command(args[0]);
+      if(iwcmd == NULL)
+       {
+         /* Here we have an unrecognised arg... Error already printed out. */
+         return(-1);
        }
 
-      /* ---------- Other ---------- */
-      /* Here we have an unrecognised arg... */
-      fprintf(stderr, "Error : unrecognised wireless request \"%s\"\n",
-             args[i]);
-      return(-1);
-    }          /* for(index ... */
+      /* One arg is consumed (the command name) */
+      args++;
+      count--;
+
+      /* Check arg numbers */
+      if(count < iwcmd->min_count)
+       ret = IWERR_ARG_NUM;
+      else
+       ret = 0;
+
+      /* Call the command */
+      if(!ret)
+       ret = (*iwcmd->fn)(skfd, ifname, args, count);
+
+      /* Deal with various errors */
+      if(ret < 0)
+       {
+         int   request = iwcmd->request;
+         if(ret == IWERR_GET_EXT)
+           request++;  /* Transform the SET into GET */
+
+         fprintf(stderr, "Error for wireless request \"%s\" (%X) :\n",
+                 iwcmd->name, request);
+         switch(ret)
+           {
+           case IWERR_ARG_NUM:
+             fprintf(stderr, "    too few arguments.\n");
+             break;
+           case IWERR_ARG_TYPE:
+             if(errarg < 0)
+               errarg = 0;
+             if(errarg >= count)
+               errarg = count - 1;
+             fprintf(stderr, "    invalid argument \"%s\".\n", args[errarg]);
+             break;
+           case IWERR_ARG_SIZE:
+             fprintf(stderr, "    argument too big (max %d)\n", errmax);
+             break;
+           case IWERR_ARG_CONFLICT:
+             if(errarg < 0)
+               errarg = 0;
+             if(errarg >= count)
+               errarg = count - 1;
+             fprintf(stderr, "    conflicting argument \"%s\".\n", args[errarg]);
+             break;
+           case IWERR_SET_EXT:
+             fprintf(stderr, "    SET failed on device %-1.16s ; %s.\n",
+                     ifname, strerror(errno));
+             break;
+           case IWERR_GET_EXT:
+             fprintf(stderr, "    GET failed on device %-1.16s ; %s.\n",
+                     ifname, strerror(errno));
+             break;
+           }
+         /* Stop processing, we don't know if we are in a consistent state
+          * in reading the command line */
+         return(ret);
+       }
+
+      /* Substract consumed args from command line */
+      args += ret;
+      count -= ret;
+
+      /* Loop back */
+    }
+
+  /* Done, all done */
   return(0);
 }
 
+/*------------------------------------------------------------------*/
+/*
+ * Display help
+ */
+static inline void
+iw_usage(void)
+{
+  int i;
+
+  fprintf(stderr,   "Usage: iwconfig [interface]\n");
+  for(i = 0; iwconfig_cmds[i].cmd != NULL; ++i)
+    fprintf(stderr, "                interface %s %s\n",
+           iwconfig_cmds[i].cmd, iwconfig_cmds[i].argsname);
+  fprintf(stderr,   "       Check man pages for more details.\n");
+}
+
+
 /******************************* MAIN ********************************/
 
 /*------------------------------------------------------------------*/
index 7d5f1a4..0afa8c1 100644 (file)
@@ -1,12 +1,12 @@
 /*
  *     Wireless Tools
  *
- *             Jean II - HPLB 97->99 - HPL 99->04
+ *             Jean II - HPLB 97->99 - HPL 99->07
  *
  * Common subroutines to all the wireless tools...
  *
  * This file is released under the GPL license.
- *     Copyright (c) 1997-2004 Jean Tourrilhes <jt@hpl.hp.com>
+ *     Copyright (c) 1997-2007 Jean Tourrilhes <jt@hpl.hp.com>
  */
 
 /***************************** INCLUDES *****************************/
@@ -99,7 +99,44 @@ const char * const iw_operation_mode[] = { "Auto",
                                        "Master",
                                        "Repeater",
                                        "Secondary",
-                                       "Monitor" };
+                                       "Monitor",
+                                       "Unknown/bug" };
+
+/* Modulations as human readable strings */
+const struct iw_modul_descr    iw_modul_list[] = {
+  /* Start with aggregate types, so that they display first */
+  { IW_MODUL_11AG, "11ag",
+    "IEEE 802.11a + 802.11g (2.4 & 5 GHz, up to 54 Mb/s)" },
+  { IW_MODUL_11AB, "11ab",
+    "IEEE 802.11a + 802.11b (2.4 & 5 GHz, up to 54 Mb/s)" },
+  { IW_MODUL_11G, "11g", "IEEE 802.11g (2.4 GHz, up to 54 Mb/s)" },
+  { IW_MODUL_11A, "11a", "IEEE 802.11a (5 GHz, up to 54 Mb/s)" },
+  { IW_MODUL_11B, "11b", "IEEE 802.11b (2.4 GHz, up to 11 Mb/s)" },
+
+  /* Proprietary aggregates */
+  { IW_MODUL_TURBO | IW_MODUL_11A, "turboa",
+    "Atheros turbo mode at 5 GHz (up to 108 Mb/s)" },
+  { IW_MODUL_TURBO | IW_MODUL_11G, "turbog",
+    "Atheros turbo mode at 2.4 GHz (up to 108 Mb/s)" },
+  { IW_MODUL_PBCC | IW_MODUL_11B, "11+",
+    "TI 802.11+ (2.4 GHz, up to 22 Mb/s)" },
+
+  /* Individual modulations */
+  { IW_MODUL_OFDM_G, "OFDMg",
+    "802.11g higher rates, OFDM at 2.4 GHz (up to 54 Mb/s)" },
+  { IW_MODUL_OFDM_A, "OFDMa", "802.11a, OFDM at 5 GHz (up to 54 Mb/s)" },
+  { IW_MODUL_CCK, "CCK", "802.11b higher rates (2.4 GHz, up to 11 Mb/s)" },
+  { IW_MODUL_DS, "DS", "802.11 Direct Sequence (2.4 GHz, up to 2 Mb/s)" },
+  { IW_MODUL_FH, "FH", "802.11 Frequency Hopping (2,4 GHz, up to 2 Mb/s)" },
+
+  /* Proprietary modulations */
+  { IW_MODUL_TURBO, "turbo",
+    "Atheros turbo mode, channel bonding (up to 108 Mb/s)" },
+  { IW_MODUL_PBCC, "PBCC",
+    "TI 802.11+ higher rates (2.4 GHz, up to 22 Mb/s)" },
+  { IW_MODUL_CUSTOM, "custom",
+    "Driver specific modulation (check driver documentation)" },
+};
 
 /* Disable runtime version warning in iw_get_range_info() */
 int    iw_ignore_version = 0;
@@ -407,7 +444,7 @@ iw_print_version_info(const char *  toolname)
   if(toolname != NULL)
     printf("%-8.16s  Wireless-Tools version %d\n", toolname, WT_VERSION);
   printf("          Compatible with Wireless Extension v11 to v%d.\n\n",
-        WE_VERSION);
+        WE_MAX_VERSION);
 
   /* Get version from kernel */
   we_kernel_version = iw_get_kernel_we_version();
@@ -521,7 +558,7 @@ iw_get_range_info(int               skfd,
       if(range->we_version_compiled > WE_MAX_VERSION)
        {
          fprintf(stderr, "Warning: Driver for device %s has been compiled with version %d\n", ifname, range->we_version_compiled);
-         fprintf(stderr, "of Wireless Extension, while this program supports up to version %d.\n", WE_VERSION);
+         fprintf(stderr, "of Wireless Extension, while this program supports up to version %d.\n", WE_MAX_VERSION);
          fprintf(stderr, "Some things may be broken...\n\n");
        }
 
@@ -681,9 +718,12 @@ iw_get_basic_config(int                    skfd,
   /* Get operation mode */
   if(iw_get_ext(skfd, ifname, SIOCGIWMODE, &wrq) >= 0)
     {
-      info->mode = wrq.u.mode;
-      if((info->mode < IW_NUM_OPER_MODE) && (info->mode >= 0))
-       info->has_mode = 1;
+      info->has_mode = 1;
+      /* Note : event->u.mode is unsigned, no need to check <= 0 */
+      if(wrq.u.mode < IW_NUM_OPER_MODE)
+       info->mode = wrq.u.mode;
+      else
+       info->mode = IW_NUM_OPER_MODE;  /* Unknown/bug */
     }
 
   return(0);
@@ -802,10 +842,10 @@ iw_set_basic_config(int                   skfd,
       we_kernel_version = iw_get_kernel_we_version();
 
       wrq.u.essid.pointer = (caddr_t) info->essid;
-      wrq.u.essid.length = strlen(info->essid) + 1;
+      wrq.u.essid.length = strlen(info->essid);
       wrq.u.data.flags = info->essid_on;
-      if(we_kernel_version > 20)
-       wrq.u.essid.length--;
+      if(we_kernel_version < 21)
+       wrq.u.essid.length++;
 
       if(iw_set_ext(skfd, ifname, SIOCSIWESSID, &wrq) < 0)
        {
@@ -1320,7 +1360,9 @@ iw_print_stats(char *             buffer,
    * Further, on 8 bits, 0x100 == 256 == 0.
    *
    * Relative/percent values are always encoded unsigned, between 0 and 255.
-   * Absolute/dBm values are always encoded negative, between -255 and 0.
+   * Absolute/dBm values are always encoded between -192 and 63.
+   * (Note that up to version 28 of Wireless Tools, dBm used to be
+   *  encoded always negative, between -256 and -1).
    *
    * How do we separate relative from absolute values ?
    * The old way is to use the range to do that. As of WE-19, we have
@@ -1329,7 +1371,7 @@ iw_print_stats(char *             buffer,
    * range struct only specify one bound of the value, we assume that
    * the other bound is 0 (zero).
    * For relative values, range is [0 ; range->max].
-   * For absolute values, range is [range->max ; 0].
+   * For absolute values, range is [range->max ; 63].
    *
    * Let's take two example :
    * 1) value is 75%. qual->value = 75 ; range->max_qual.value = 100
@@ -1343,7 +1385,8 @@ iw_print_stats(char *             buffer,
    * The old way to detect dBm require both the range and a non-null
    * level (which confuse the test). The new way can deal with level of 0
    * because it does an explicit test on the flag. */
-  if(has_range && ((qual->level != 0) || (qual->updated & IW_QUAL_DBM)))
+  if(has_range && ((qual->level != 0)
+                  || (qual->updated & (IW_QUAL_DBM | IW_QUAL_RCPI))))
     {
       /* Deal with quality : always a relative value */
       if(!(qual->updated & IW_QUAL_QUAL_INVALID))
@@ -1355,16 +1398,17 @@ iw_print_stats(char *           buffer,
          buflen -= len;
        }
 
-      /* Check if the statistics are in dBm or relative */
-      if((qual->updated & IW_QUAL_DBM)
-        || (qual->level > range->max_qual.level))
+      /* Check if the statistics are in RCPI (IEEE 802.11k) */
+      if(qual->updated & IW_QUAL_RCPI)
        {
-         /* Deal with signal level in dBm  (absolute power measurement) */
+         /* Deal with signal level in RCPI */
+         /* RCPI = int{(Power in dBm +110)*2} for 0dbm > Power > -110dBm */
          if(!(qual->updated & IW_QUAL_LEVEL_INVALID))
            {
-             len = snprintf(buffer, buflen, "Signal level%c%d dBm  ",
+             double    rcpilevel = (qual->level / 2.0) - 110.0;
+             len = snprintf(buffer, buflen, "Signal level%c%g dBm  ",
                             qual->updated & IW_QUAL_LEVEL_UPDATED ? '=' : ':',
-                            qual->level - 0x100);
+                            rcpilevel);
              buffer += len;
              buflen -= len;
            }
@@ -1372,29 +1416,63 @@ iw_print_stats(char *           buffer,
          /* Deal with noise level in dBm (absolute power measurement) */
          if(!(qual->updated & IW_QUAL_NOISE_INVALID))
            {
-             len = snprintf(buffer, buflen, "Noise level%c%d dBm",
+             double    rcpinoise = (qual->noise / 2.0) - 110.0;
+             len = snprintf(buffer, buflen, "Noise level%c%g dBm",
                             qual->updated & IW_QUAL_NOISE_UPDATED ? '=' : ':',
-                            qual->noise - 0x100);
+                            rcpinoise);
            }
        }
       else
        {
-         /* Deal with signal level as relative value (0 -> max) */
-         if(!(qual->updated & IW_QUAL_LEVEL_INVALID))
+         /* Check if the statistics are in dBm */
+         if((qual->updated & IW_QUAL_DBM)
+            || (qual->level > range->max_qual.level))
            {
-             len = snprintf(buffer, buflen, "Signal level%c%d/%d  ",
-                            qual->updated & IW_QUAL_LEVEL_UPDATED ? '=' : ':',
-                            qual->level, range->max_qual.level);
-             buffer += len;
-             buflen -= len;
-           }
+             /* Deal with signal level in dBm  (absolute power measurement) */
+             if(!(qual->updated & IW_QUAL_LEVEL_INVALID))
+               {
+                 int   dblevel = qual->level;
+                 /* Implement a range for dBm [-192; 63] */
+                 if(qual->level >= 64)
+                   dblevel -= 0x100;
+                 len = snprintf(buffer, buflen, "Signal level%c%d dBm  ",
+                                qual->updated & IW_QUAL_LEVEL_UPDATED ? '=' : ':',
+                                dblevel);
+                 buffer += len;
+                 buflen -= len;
+               }
 
-         /* Deal with noise level as relative value (0 -> max) */
-         if(!(qual->updated & IW_QUAL_NOISE_INVALID))
+             /* Deal with noise level in dBm (absolute power measurement) */
+             if(!(qual->updated & IW_QUAL_NOISE_INVALID))
+               {
+                 int   dbnoise = qual->noise;
+                 /* Implement a range for dBm [-192; 63] */
+                 if(qual->noise >= 64)
+                   dbnoise -= 0x100;
+                 len = snprintf(buffer, buflen, "Noise level%c%d dBm",
+                                qual->updated & IW_QUAL_NOISE_UPDATED ? '=' : ':',
+                                dbnoise);
+               }
+           }
+         else
            {
-             len = snprintf(buffer, buflen, "Noise level%c%d/%d",
-                            qual->updated & IW_QUAL_NOISE_UPDATED ? '=' : ':',
-                            qual->noise, range->max_qual.noise);
+             /* Deal with signal level as relative value (0 -> max) */
+             if(!(qual->updated & IW_QUAL_LEVEL_INVALID))
+               {
+                 len = snprintf(buffer, buflen, "Signal level%c%d/%d  ",
+                                qual->updated & IW_QUAL_LEVEL_UPDATED ? '=' : ':',
+                                qual->level, range->max_qual.level);
+                 buffer += len;
+                 buflen -= len;
+               }
+
+             /* Deal with noise level as relative value (0 -> max) */
+             if(!(qual->updated & IW_QUAL_NOISE_INVALID))
+               {
+                 len = snprintf(buffer, buflen, "Noise level%c%d/%d",
+                                qual->updated & IW_QUAL_NOISE_UPDATED ? '=' : ':',
+                                qual->noise, range->max_qual.noise);
+               }
            }
        }
     }
@@ -1644,7 +1722,8 @@ void
 iw_print_pm_value(char *       buffer,
                  int           buflen,
                  int           value,
-                 int           flags)
+                 int           flags,
+                 int           we_version)
 {
   /* Check size */
   if(buflen < 25)
@@ -1674,13 +1753,25 @@ iw_print_pm_value(char *        buffer,
     }
   else
     {
-      strcpy(buffer, " period:");                      /* Size checked */
-      buffer += 8;
+      if(flags & IW_POWER_SAVING)
+       {
+         strcpy(buffer, " saving:");                   /* Size checked */
+         buffer += 8;
+       }
+      else
+       {
+         strcpy(buffer, " period:");                   /* Size checked */
+         buffer += 8;
+       }
     }
 
   /* Display value without units */
   if(flags & IW_POWER_RELATIVE)
-    snprintf(buffer, buflen, "%g", ((double) value) / MEGA);
+    {
+      if(we_version < 21)
+       value /= MEGA;
+      snprintf(buffer, buflen, "%d", value);
+    }
   else
     {
       /* Display value with units */
@@ -1744,15 +1835,16 @@ void
 iw_print_retry_value(char *    buffer,
                     int        buflen,
                     int        value,
-                    int        flags)
+                    int        flags,
+                    int        we_version)
 {
   /* Check buffer size */
-  if(buflen < 18)
+  if(buflen < 20)
     {
       snprintf(buffer, buflen, "<too big>");
       return;
     }
-  buflen -= 18;
+  buflen -= 20;
 
   /* Modifiers */
   if(flags & IW_RETRY_MIN)
@@ -1765,6 +1857,16 @@ iw_print_retry_value(char *      buffer,
       strcpy(buffer, " max");                          /* Size checked */
       buffer += 4;
     }
+  if(flags & IW_RETRY_SHORT)
+    {
+      strcpy(buffer, " short");                                /* Size checked */
+      buffer += 6;
+    }
+  if(flags & IW_RETRY_LONG)
+    {
+      strcpy(buffer, "  long");                                /* Size checked */
+      buffer += 6;
+    }
 
   /* Type lifetime of limit */
   if(flags & IW_RETRY_LIFETIME)
@@ -1773,8 +1875,12 @@ iw_print_retry_value(char *      buffer,
       buffer += 10;
 
       /* Display value without units */
-      if(flags & IW_POWER_RELATIVE)
-       snprintf(buffer, buflen, "%g", ((double) value) / MEGA);
+      if(flags & IW_RETRY_RELATIVE)
+       {
+         if(we_version < 21)
+           value /= MEGA;
+         snprintf(buffer, buflen, "%d", value);
+       }
       else
        {
          /* Display value with units */
@@ -2125,7 +2231,7 @@ iw_in_addr(int            skfd,
           struct sockaddr *sap)
 {
   /* Check if it is a hardware or IP address */
-  if(index(bufp, ':') == NULL)
+  if(strchr(bufp, ':') == NULL)
     {
       struct sockaddr  if_address;
       struct arpreq    arp_query;
@@ -2467,6 +2573,12 @@ static const struct iw_ioctl_description standard_ioctl_descr[] = {
        [SIOCGIWPOWER   - SIOCIWFIRST] = {
                .header_type    = IW_HEADER_TYPE_PARAM,
        },
+       [SIOCSIWMODUL   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
+       [SIOCGIWMODUL   - SIOCIWFIRST] = {
+               .header_type    = IW_HEADER_TYPE_PARAM,
+       },
        [SIOCSIWGENIE   - SIOCIWFIRST] = {
                .header_type    = IW_HEADER_TYPE_POINT,
                .token_size     = 1,
@@ -2560,17 +2672,17 @@ static const unsigned int standard_event_num = (sizeof(standard_event_descr) /
 
 /* Size (in bytes) of various events */
 static const int event_type_size[] = {
-       IW_EV_LCP_LEN,          /* IW_HEADER_TYPE_NULL */
+       IW_EV_LCP_PK_LEN,       /* IW_HEADER_TYPE_NULL */
        0,
-       IW_EV_CHAR_LEN,         /* IW_HEADER_TYPE_CHAR */
+       IW_EV_CHAR_PK_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 */
+       IW_EV_UINT_PK_LEN,      /* IW_HEADER_TYPE_UINT */
+       IW_EV_FREQ_PK_LEN,      /* IW_HEADER_TYPE_FREQ */
+       IW_EV_ADDR_PK_LEN,      /* IW_HEADER_TYPE_ADDR */
        0,
-       IW_EV_POINT_LEN,        /* Without variable payload */
-       IW_EV_PARAM_LEN,        /* IW_HEADER_TYPE_PARAM */
-       IW_EV_QUAL_LEN,         /* IW_HEADER_TYPE_QUAL */
+       IW_EV_POINT_PK_LEN,     /* Without variable payload */
+       IW_EV_PARAM_PK_LEN,     /* IW_HEADER_TYPE_PARAM */
+       IW_EV_QUAL_PK_LEN,      /* IW_HEADER_TYPE_QUAL */
 };
 
 /*------------------------------------------------------------------*/
@@ -2607,29 +2719,26 @@ iw_extract_event_stream(struct stream_descr *   stream, /* Stream of events */
   /* Don't "optimise" the following variable, it will crash */
   unsigned     cmd_index;              /* *MUST* be unsigned */
 
-  /* Unused for now. Will be later on... */
-  we_version = we_version;
-
   /* Check for end of stream */
-  if((stream->current + IW_EV_LCP_LEN) > stream->end)
+  if((stream->current + IW_EV_LCP_PK_LEN) > stream->end)
     return(0);
 
-#if DEBUG
+#ifdef DEBUG
   printf("DBG - stream->current = %p, stream->value = %p, stream->end = %p\n",
         stream->current, stream->value, stream->end);
 #endif
 
   /* Extract the event header (to get the event id).
    * Note : the event may be unaligned, therefore copy... */
-  memcpy((char *) iwe, stream->current, IW_EV_LCP_LEN);
+  memcpy((char *) iwe, stream->current, IW_EV_LCP_PK_LEN);
 
-#if DEBUG
+#ifdef DEBUG
   printf("DBG - iwe->cmd = 0x%X, iwe->len = %d\n",
         iwe->cmd, iwe->len);
 #endif
 
   /* Check invalid events */
-  if(iwe->len <= IW_EV_LCP_LEN)
+  if(iwe->len <= IW_EV_LCP_PK_LEN)
     return(-1);
 
   /* Get the type and length of that event */
@@ -2647,28 +2756,28 @@ iw_extract_event_stream(struct stream_descr *   stream, /* Stream of events */
     }
   if(descr != NULL)
     event_type = descr->header_type;
-  /* Unknown events -> event_type=0 => IW_EV_LCP_LEN */
+  /* Unknown events -> event_type=0 => IW_EV_LCP_PK_LEN */
   event_len = event_type_size[event_type];
   /* Fixup for earlier version of WE */
   if((we_version <= 18) && (event_type == IW_HEADER_TYPE_POINT))
     event_len += IW_EV_POINT_OFF;
 
   /* Check if we know about this event */
-  if(event_len <= IW_EV_LCP_LEN)
+  if(event_len <= IW_EV_LCP_PK_LEN)
     {
       /* Skip to next event */
       stream->current += iwe->len;
       return(2);
     }
-  event_len -= IW_EV_LCP_LEN;
+  event_len -= IW_EV_LCP_PK_LEN;
 
   /* Set pointer on data */
   if(stream->value != NULL)
     pointer = stream->value;                   /* Next value in event */
   else
-    pointer = stream->current + IW_EV_LCP_LEN; /* First value in event */
+    pointer = stream->current + IW_EV_LCP_PK_LEN;      /* First value in event */
 
-#if DEBUG
+#ifdef DEBUG
   printf("DBG - event_type = %d, event_len = %d, pointer = %p\n",
         event_type, event_len, pointer);
 #endif
@@ -2681,6 +2790,7 @@ iw_extract_event_stream(struct stream_descr *     stream, /* Stream of events */
       return(-2);
     }
   /* Fixup for WE-19 and later : pointer no longer in the stream */
+  /* Beware of alignement. Dest has local alignement, not packed */
   if((we_version > 18) && (event_type == IW_HEADER_TYPE_POINT))
     memcpy((char *) iwe + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
           pointer, event_len);
@@ -2694,7 +2804,7 @@ iw_extract_event_stream(struct stream_descr *     stream, /* Stream of events */
   if(event_type == IW_HEADER_TYPE_POINT)
     {
       /* Check the length of the payload */
-      unsigned int     extra_len = iwe->len - (event_len + IW_EV_LCP_LEN);
+      unsigned int     extra_len = iwe->len - (event_len + IW_EV_LCP_PK_LEN);
       if(extra_len > 0)
        {
          /* Set pointer on variable part (warning : non aligned) */
@@ -2709,9 +2819,35 @@ iw_extract_event_stream(struct stream_descr *    stream, /* Stream of events */
              /* Those checks are actually pretty hard to trigger,
               * because of the checks done in the kernel... */
 
+             unsigned int      token_len = iwe->u.data.length * descr->token_size;
+
+             /* Ugly fixup for alignement issues.
+              * If the kernel is 64 bits and userspace 32 bits,
+              * we have an extra 4+4 bytes.
+              * Fixing that in the kernel would break 64 bits userspace. */
+             if((token_len != extra_len) && (extra_len >= 4))
+               {
+                 __u16         alt_dlen = *((__u16 *) pointer);
+                 unsigned int  alt_token_len = alt_dlen * descr->token_size;
+                 if((alt_token_len + 8) == extra_len)
+                   {
+#ifdef DEBUG
+                     printf("DBG - alt_token_len = %d\n", alt_token_len);
+#endif
+                     /* Ok, let's redo everything */
+                     pointer -= event_len;
+                     pointer += 4;
+                     /* Dest has local alignement, not packed */
+                     memcpy((char *) iwe + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
+                            pointer, event_len);
+                     pointer += event_len + 4;
+                     iwe->u.data.pointer = pointer;
+                     token_len = alt_token_len;
+                   }
+               }
+
              /* Discard bogus events which advertise more tokens than
               * what they carry... */
-             unsigned int      token_len = iwe->u.data.length * descr->token_size;
              if(token_len > extra_len)
                iwe->u.data.pointer = NULL;     /* Discard paylod */
              /* Check that the advertised token size is not going to
@@ -2722,7 +2858,7 @@ iw_extract_event_stream(struct stream_descr *     stream, /* Stream of events */
              /* Same for underflows... */
              if(iwe->u.data.length < descr->min_tokens)
                iwe->u.data.pointer = NULL;     /* Discard paylod */
-#if DEBUG
+#ifdef DEBUG
              printf("DBG - extra_len = %d, token_len = %d, token = %d, max = %d, min = %d\n",
                     extra_len, token_len, iwe->u.data.length, descr->max_tokens, descr->min_tokens);
 #endif
@@ -2737,6 +2873,25 @@ iw_extract_event_stream(struct stream_descr *    stream, /* Stream of events */
     }
   else
     {
+      /* Ugly fixup for alignement issues.
+       * If the kernel is 64 bits and userspace 32 bits,
+       * we have an extra 4 bytes.
+       * Fixing that in the kernel would break 64 bits userspace. */
+      if((stream->value == NULL)
+        && ((((iwe->len - IW_EV_LCP_PK_LEN) % event_len) == 4)
+            || ((iwe->len == 12) && ((event_type == IW_HEADER_TYPE_UINT) ||
+                                     (event_type == IW_HEADER_TYPE_QUAL))) ))
+       {
+#ifdef DEBUG
+         printf("DBG - alt iwe->len = %d\n", iwe->len - 4);
+#endif
+         pointer -= event_len;
+         pointer += 4;
+         /* Beware of alignement. Dest has local alignement, not packed */
+         memcpy((char *) iwe + IW_EV_LCP_LEN, pointer, event_len);
+         pointer += event_len;
+       }
+
       /* Is there more value in the event ? */
       if((pointer + event_len) <= (stream->current + iwe->len))
        /* Go to next value */
@@ -2847,8 +3002,14 @@ iw_process_scanning_token(struct iw_event *              event,
       memcpy(&wscan->stats.qual, &event->u.qual, sizeof(struct iw_quality));
       break;
     case SIOCGIWRATE:
-      /* Scan may return a list of bitrates. Should we really bother with
-       * an array of bitrates ? Or only the maximum bitrate ? Jean II */
+      /* Scan may return a list of bitrates. As we have space for only
+       * a single bitrate, we only keep the largest one. */
+      if((!wscan->has_maxbitrate) ||
+        (event->u.bitrate.value > wscan->maxbitrate.value))
+       {
+         wscan->has_maxbitrate = 1;
+         memcpy(&(wscan->maxbitrate), &(event->u.bitrate), sizeof(iwparam));
+       }
     case IWEVCUSTOM:
       /* How can we deal with those sanely ? Jean II */
     default:
@@ -2893,7 +3054,9 @@ iw_process_scan(int                       skfd,
       wrq.u.data.pointer = NULL;               /* Later */
       wrq.u.data.flags = 0;
       wrq.u.data.length = 0;
-      if(iw_set_ext(skfd, ifname, SIOCSIWSCAN, &wrq) < 0)
+      /* Remember that as non-root, we will get an EPERM here */
+      if((iw_set_ext(skfd, ifname, SIOCSIWSCAN, &wrq) < 0)
+        && (errno != EPERM))
        return(-1);
       /* Success : now, just wait for event or results */
       return(250);     /* Wait 250 ms */
@@ -2959,7 +3122,7 @@ iw_process_scan(int                       skfd,
       struct stream_descr      stream;
       struct wireless_scan *   wscan = NULL;
       int                      ret;
-#if DEBUG
+#ifdef DEBUG
       /* Debugging code. In theory useless, because it's debugged ;-) */
       int      i;
       printf("Scan result [%02X", buffer[0]);
index 6730621..31cf39b 100644 (file)
@@ -1,12 +1,12 @@
 /*
  *     Wireless Tools
  *
- *             Jean II - HPLB 97->99 - HPL 99->04
+ *             Jean II - HPLB 97->99 - HPL 99->07
  *
  * Common header for the Wireless Extension library...
  *
  * This file is released under the GPL license.
- *     Copyright (c) 1997-2004 Jean Tourrilhes <jt@hpl.hp.com>
+ *     Copyright (c) 1997-2007 Jean Tourrilhes <jt@hpl.hp.com>
  */
 
 #ifndef IWLIB_H
 #include <unistd.h>
 
 /* This is our header selection. Try to hide the mess and the misery :-(
- * Don't look, you would go blind ;-) */
-
-#ifndef LINUX_VERSION_CODE
-#include <linux/version.h>
-#endif
-
-/* Kernel headers 2.4.X + Glibc 2.2 - Mandrake 8.0, Debian 2.3, RH 7.1
- * Kernel headers 2.2.X + Glibc 2.2 - Slackware 8.0 */
-#if defined(__GLIBC__) \
-    && __GLIBC__ == 2 \
-    && __GLIBC_MINOR__ >= 2 \
-    && LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
-#define HEADERS_GENERIC
-
-/* Kernel headers 2.4.X + Glibc 2.1 - Debian 2.2 upgraded, RH 7.0
- * Kernel headers 2.2.X + Glibc 2.1 - Debian 2.2, RH 6.1 */
-#elif defined(__GLIBC__) \
-      && __GLIBC__ == 2 \
-      && __GLIBC_MINOR__ == 1 \
-      && LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
-#define HEADERS_GENERIC
-#define HEADERS_KERNEL
-
-/* Unsupported combination */
-#else
-#error "Your kernel/libc combination is not supported"
-#endif
+ * Don't look, you would go blind ;-)
+ * Note : compatibility with *old* distributions has been removed,
+ * you will need Glibc 2.2 and older to compile (which means 
+ * Mandrake 8.0, Debian 2.3, RH 7.1 or older).
+ */
 
-#ifdef HEADERS_GENERIC 
-/* Proposed by Dr. Michael Rietz <rietz@mail.amps.de>, 27.3.2 */
+/* Set of headers proposed by Dr. Michael Rietz <rietz@mail.amps.de>, 27.3.2 */
 #include <net/if_arp.h>                /* For ARPHRD_ETHER */
 #include <sys/socket.h>                /* For AF_INET & struct sockaddr */
 #include <netinet/in.h>         /* For struct sockaddr_in */
 #include <netinet/if_ether.h>
-#endif /* HEADERS_GENERIC */    
 
 /* Fixup to be able to include kernel includes in userspace.
  * Basically, kill the sparse annotations... Jean II */
 
 #include <linux/types.h>               /* for "caddr_t" et al          */
 
-#ifdef HEADERS_KERNEL
-/* Traditionally we have used kernel headers, included in wireless.h */
-#include <linux/socket.h>              /* for "struct sockaddr" et al  */
-#include <linux/if.h>                  /* for IFNAMSIZ and co... */
-#else  /* !HEADERS_KERNEL */
 /* Glibc systems headers are supposedly less problematic than kernel ones */
 #include <sys/socket.h>                        /* for "struct sockaddr" et al  */
 #include <net/if.h>                    /* for IFNAMSIZ and co... */
-#endif /* !HEADERS_KERNEL */
 
 /* Private copy of Wireless extensions (in this directoty) */
 #include "wireless.h"
@@ -126,16 +96,17 @@ extern "C" {
 
 /****************************** DEBUG ******************************/
 
+//#define DEBUG 1
 
 /************************ CONSTANTS & MACROS ************************/
 
 /* Various versions information */
 /* Recommended Wireless Extension version */
-#define WE_VERSION     20
+#define WE_VERSION     21
 /* Maximum forward compatibility built in this version of WT */
-#define WE_MAX_VERSION 21
+#define WE_MAX_VERSION 22
 /* Version of Wireless Tools */
-#define WT_VERSION     28
+#define WT_VERSION     29
 
 /* Paths */
 #define PROC_NET_WIRELESS      "/proc/net/wireless"
@@ -153,6 +124,36 @@ extern "C" {
 #define ARPHRD_IEEE80211 801           /* IEEE 802.11                  */
 #endif /* ARPHRD_IEEE80211 */
 
+#ifndef IW_EV_LCP_PK_LEN
+/* Size of the Event prefix when packed in stream */
+#define IW_EV_LCP_PK_LEN       (4)
+/* Size of the various events when packed in stream */
+#define IW_EV_CHAR_PK_LEN      (IW_EV_LCP_PK_LEN + IFNAMSIZ)
+#define IW_EV_UINT_PK_LEN      (IW_EV_LCP_PK_LEN + sizeof(__u32))
+#define IW_EV_FREQ_PK_LEN      (IW_EV_LCP_PK_LEN + sizeof(struct iw_freq))
+#define IW_EV_PARAM_PK_LEN     (IW_EV_LCP_PK_LEN + sizeof(struct iw_param))
+#define IW_EV_ADDR_PK_LEN      (IW_EV_LCP_PK_LEN + sizeof(struct sockaddr))
+#define IW_EV_QUAL_PK_LEN      (IW_EV_LCP_PK_LEN + sizeof(struct iw_quality))
+#define IW_EV_POINT_PK_LEN     (IW_EV_LCP_PK_LEN + 4)
+
+struct iw_pk_event
+{
+       __u16           len;                    /* Real lenght of this stuff */
+       __u16           cmd;                    /* Wireless IOCTL */
+       union iwreq_data        u;              /* IOCTL fixed payload */
+} __attribute__ ((packed));
+struct iw_pk_point
+{
+  void __user  *pointer;       /* Pointer to the data  (in user space) */
+  __u16                length;         /* number of fields or size in bytes */
+  __u16                flags;          /* Optional params */
+} __attribute__ ((packed));
+
+#define IW_EV_LCP_PK2_LEN      (sizeof(struct iw_pk_event) - sizeof(union iwreq_data))
+#define IW_EV_POINT_PK2_LEN    (IW_EV_LCP_PK2_LEN + sizeof(struct iw_pk_point) - IW_EV_POINT_OFF)
+
+#endif /* IW_EV_LCP_PK_LEN */
+
 /****************************** TYPES ******************************/
 
 /* Shortcuts */
@@ -244,6 +245,8 @@ typedef struct wireless_scan
   struct wireless_config       b;      /* Basic information */
   iwstats      stats;                  /* Signal strength */
   int          has_stats;
+  iwparam      maxbitrate;             /* Max bit rate in bps */
+  int          has_maxbitrate;
 } wireless_scan;
 
 /*
@@ -271,6 +274,14 @@ typedef int (*iw_enum_handler)(int skfd,
                               char *   args[],
                               int      count);
 
+/* Describe a modulation */
+typedef struct iw_modul_descr
+{
+  unsigned int         mask;           /* Modulation bitmask */
+  char                 cmd[8];         /* Short name */
+  char *               verbose;        /* Verbose description */
+} iw_modul_descr;
+
 /**************************** PROTOTYPES ****************************/
 /*
  * All the functions in iwcommon.c
@@ -379,7 +390,8 @@ void
        iw_print_pm_value(char *        buffer,
                          int           buflen,
                          int           value,
-                         int           flags);
+                         int           flags,
+                         int           we_version);
 void
        iw_print_pm_mode(char *         buffer,
                         int            buflen,
@@ -389,7 +401,8 @@ void
        iw_print_retry_value(char *     buffer,
                             int        buflen,
                             int        value,
-                            int        flags);
+                            int        flags,
+                            int        we_version);
 /* ----------------------- TIME SUBROUTINES ----------------------- */
 void
        iw_print_timeval(char *                         buffer,
@@ -469,6 +482,11 @@ int
 /* Modes as human readable strings */
 extern const char * const      iw_operation_mode[];
 #define IW_NUM_OPER_MODE       7
+#define IW_NUM_OPER_MODE_EXT   8
+
+/* Modulations as human readable strings */
+extern const struct iw_modul_descr     iw_modul_list[];
+#define IW_SIZE_MODUL_LIST     16
 
 /************************* INLINE FUNTIONS *************************/
 /*
index 7b3de73..2f0020e 100644 (file)
@@ -1,7 +1,7 @@
 .\" Jean II - HPLB - 96
 .\" iwlist.8
 .\"
-.TH IWLIST 8 "23 June 2004" "wireless-tools" "Linux Programmer's Manual"
+.TH IWLIST 8 "13 April 2006" "wireless-tools" "Linux Programmer's Manual"
 .\"
 .\" NAME part
 .\"
@@ -11,21 +11,29 @@ iwlist \- Get more detailed wireless information from a wireless interface
 .\" SYNOPSIS part
 .\"
 .SH SYNOPSIS
-.BI "iwlist " interface " scanning"
+.BI "iwlist [" interface "] scanning"
 .br
-.BI "iwlist " interface " frequency"
+.BI "iwlist [" interface "] frequency"
 .br
-.BI "iwlist " interface " rate"
+.BI "iwlist [" interface "] rate"
 .br
-.BI "iwlist " interface " key"
+.BI "iwlist [" interface "] keys"
 .br
-.BI "iwlist " interface " power"
+.BI "iwlist [" interface "] power"
 .br
-.BI "iwlist " interface " txpower"
+.BI "iwlist [" interface "] txpower"
 .br
-.BI "iwlist " interface " retry"
+.BI "iwlist [" interface "] retry"
 .br
-.BI "iwlist " interface " event"
+.BI "iwlist [" interface "] event"
+.br
+.BI "iwlist [" interface "] auth"
+.br
+.BI "iwlist [" interface "] wpakeys"
+.br
+.BI "iwlist [" interface "] genie"
+.br
+.BI "iwlist [" interface "] modulation"
 .br
 .BI "iwlist --help"
 .br
@@ -58,10 +66,15 @@ the card supports.
 Triggering scanning is a privileged operation
 .RI ( root
 only) and normal users can only read left-over scan results. By
-default, the way scanning is done (the scope of the scan) will be
-impacted by the current setting of the driver. Also, this command is
-supposed to take extra arguments to control the scanning behaviour,
-but this is currently not implemented.
+default, the way scanning is done (the scope of the scan) is dependant
+on the card and card settings.
+.br
+This command take optional arguments, however most drivers will ignore
+those. The option
+.B essid
+is used to specify a scan on a specific ESSID. The option
+.B last
+do not trigger a scan and read left-over scan results.
 .TP
 .BR freq [uency]/ channel
 Give the list of available frequencies in the device and the number of
@@ -73,9 +86,9 @@ displayed and channel numbers.
 .BR rate / bit [rate]
 List the bit-rates supported by the device.
 .TP
-.BR key / enc [ryption]
-List the encryption key sizes supported and display all the encryption
-keys available in the device.
+.BR keys / enc [ryption]
+List the encryption key sizes supported and list all the encryption
+keys set in the device.
 .TP
 .B power
 List the various Power Management attributes and modes of the device.
@@ -100,10 +113,27 @@ the card. See your driver documentation for details.
 .B event
 List the wireless events supported by the device.
 .TP
+.B auth
+List the WPA authentication parametes curently set.
+.TP
+.BR wpa [keys]
+List all the WPA encryption keys set in the device.
+.TP
+.B genie
+List the Generic Information Elements set in the device (used for WPA
+support).
+.TP
+.BR modu [lation]
+List the modulations supported by the device and the modulations
+currently enabled.
+.TP
 .B --version
 Display the version of the tools, as well as the recommended and
 current Wireless Extensions version for the tool and the various
 wireless interfaces.
+.TP
+.B --help
+Display short help message.
 .\"
 .\" FILES part
 .\"
index f59ab1e..4a633a3 100644 (file)
@@ -1,14 +1,14 @@
 /*
  *     Wireless Tools
  *
- *             Jean II - HPLB '99 - HPL 99->04
+ *             Jean II - HPLB '99 - HPL 99->07
  *
  * This tool can access various piece of information on the card
  * not part of iwconfig...
  * You need to link this code against "iwlist.c" and "-lm".
  *
  * This file is released under the GPL license.
- *     Copyright (c) 1997-2004 Jean Tourrilhes <jt@hpl.hp.com>
+ *     Copyright (c) 1997-2007 Jean Tourrilhes <jt@hpl.hp.com>
  */
 
 #include "iwlib.h"             /* Header */
@@ -26,758 +26,412 @@ typedef struct iwscan_state
   int                  val_index;      /* Value in table 0->(N-1) */
 } iwscan_state;
 
+/*
+ * Bit to name mapping
+ */
+typedef struct iwmask_name
+{
+  unsigned int mask;   /* bit mask for the value */
+  const char * name;   /* human readable name for the value */
+} iwmask_name;
 
-/*********************** FREQUENCIES/CHANNELS ***********************/
-
-/*------------------------------------------------------------------*/
 /*
- * Print the number of channels and available frequency for the device
+ * Types of authentication parameters
  */
-static int
-print_freq_info(int            skfd,
-               char *          ifname,
-               char *          args[],         /* Command line args */
-               int             count)          /* Args count */
+typedef struct iw_auth_descr
 {
-  struct iwreq         wrq;
-  struct iw_range      range;
-  double               freq;
-  int                  k;
-  int                  channel;
-  char                 buffer[128];    /* Temporary buffer */
+  int                          value;          /* Type of auth value */
+  const char *                 label;          /* User readable version */
+  const struct iwmask_name *   names;          /* Names for this value */
+  const int                    num_names;      /* Number of names */
+} iw_auth_descr;
 
-  /* Avoid "Unused parameter" warning */
-  args = args; count = count;
+/**************************** CONSTANTS ****************************/
 
-  /* Get list of frequencies / channels */
-  if(iw_get_range_info(skfd, ifname, &range) < 0)
-      fprintf(stderr, "%-8.16s  no frequency information.\n\n",
-                     ifname);
-  else
-    {
-      if(range.num_frequency > 0)
-       {
-         printf("%-8.16s  %d channels in total; available frequencies :\n",
-                ifname, range.num_channels);
-         /* Print them all */
-         for(k = 0; k < range.num_frequency; k++)
-           {
-             freq = iw_freq2float(&(range.freq[k]));
-             iw_print_freq_value(buffer, sizeof(buffer), freq);
-             printf("          Channel %.2d : %s\n",
-                    range.freq[k].i, buffer);
-           }
-       }
-      else
-       printf("%-8.16s  %d channels\n",
-              ifname, range.num_channels);
+#define IW_SCAN_HACK           0x8000
 
-      /* Get current frequency / channel and display it */
-      if(iw_get_ext(skfd, ifname, SIOCGIWFREQ, &wrq) >= 0)
-       {
-         freq = iw_freq2float(&(wrq.u.freq));
-         channel = iw_freq_to_channel(freq, &range);
-         iw_print_freq(buffer, sizeof(buffer),
-                       freq, channel, wrq.u.freq.flags);
-         printf("          Current %s\n\n", buffer);
-       }
-    }
-  return(0);
-}
+#define IW_EXTKEY_SIZE (sizeof(struct iw_encode_ext) + IW_ENCODING_TOKEN_MAX)
 
-/************************ ACCESS POINT LIST ************************/
+/* ------------------------ WPA CAPA NAMES ------------------------ */
 /*
- * Note : now that we have scanning support, this is depracted and
- * won't survive long. Actually, next version it's out !
+ * This is the user readable name of a bunch of WPA constants in wireless.h
+ * Maybe this should go in iwlib.c ?
  */
 
-/*------------------------------------------------------------------*/
-/*
- * Display the list of ap addresses and the associated stats
- * Exacly the same as the spy list, only with different IOCTL and messages
- */
-static int
-print_ap_info(int      skfd,
-             char *    ifname,
-             char *    args[],         /* Command line args */
-             int       count)          /* Args count */
-{
-  struct iwreq         wrq;
-  char         buffer[(sizeof(struct iw_quality) +
-                       sizeof(struct sockaddr)) * IW_MAX_AP];
-  char         temp[128];
-  struct sockaddr *    hwa;
-  struct iw_quality *  qual;
-  iwrange      range;
-  int          has_range = 0;
-  int          has_qual = 0;
-  int          n;
-  int          i;
+#ifndef WE_ESSENTIAL
+#define IW_ARRAY_LEN(x) (sizeof(x)/sizeof((x)[0]))
 
-  /* Avoid "Unused parameter" warning */
-  args = args; count = count;
+//static const struct iwmask_name iw_enc_mode_name[] = {
+//  { IW_ENCODE_RESTRICTED,    "restricted" },
+//  { IW_ENCODE_OPEN,          "open" },
+//};
+//#define      IW_ENC_MODE_NUM         IW_ARRAY_LEN(iw_enc_mode_name)
 
-  /* Collect stats */
-  wrq.u.data.pointer = (caddr_t) buffer;
-  wrq.u.data.length = IW_MAX_AP;
-  wrq.u.data.flags = 0;
-  if(iw_get_ext(skfd, ifname, SIOCGIWAPLIST, &wrq) < 0)
-    {
-      fprintf(stderr, "%-8.16s  Interface doesn't have a list of Peers/Access-Points\n\n", ifname);
-      return(-1);
-    }
+static const struct iwmask_name iw_auth_capa_name[] = {
+  { IW_ENC_CAPA_WPA,           "WPA" },
+  { IW_ENC_CAPA_WPA2,          "WPA2" },
+  { IW_ENC_CAPA_CIPHER_TKIP,   "CIPHER-TKIP" },
+  { IW_ENC_CAPA_CIPHER_CCMP,   "CIPHER-CCMP" },
+};
+#define        IW_AUTH_CAPA_NUM        IW_ARRAY_LEN(iw_auth_capa_name)
+
+static const struct iwmask_name iw_auth_cypher_name[] = {
+  { IW_AUTH_CIPHER_NONE,       "none" },
+  { IW_AUTH_CIPHER_WEP40,      "WEP-40" },
+  { IW_AUTH_CIPHER_TKIP,       "TKIP" },
+  { IW_AUTH_CIPHER_CCMP,       "CCMP" },
+  { IW_AUTH_CIPHER_WEP104,     "WEP-104" },
+};
+#define        IW_AUTH_CYPHER_NUM      IW_ARRAY_LEN(iw_auth_cypher_name)
 
-  /* Number of addresses */
-  n = wrq.u.data.length;
-  has_qual = wrq.u.data.flags;
+static const struct iwmask_name iw_wpa_ver_name[] = {
+  { IW_AUTH_WPA_VERSION_DISABLED,      "disabled" },
+  { IW_AUTH_WPA_VERSION_WPA,           "WPA" },
+  { IW_AUTH_WPA_VERSION_WPA2,          "WPA2" },
+};
+#define        IW_WPA_VER_NUM          IW_ARRAY_LEN(iw_wpa_ver_name)
 
-  /* The two lists */
-  hwa = (struct sockaddr *) buffer;
-  qual = (struct iw_quality *) (buffer + (sizeof(struct sockaddr) * n));
+static const struct iwmask_name iw_auth_key_mgmt_name[] = {
+  { IW_AUTH_KEY_MGMT_802_1X,   "802.1x" },
+  { IW_AUTH_KEY_MGMT_PSK,      "PSK" },
+};
+#define        IW_AUTH_KEY_MGMT_NUM    IW_ARRAY_LEN(iw_auth_key_mgmt_name)
 
-  /* Check if we have valid mac address type */
-  if(iw_check_mac_addr_type(skfd, ifname) < 0)
-    {
-      fprintf(stderr, "%-8.16s  Interface doesn't support MAC addresses\n\n", ifname);
-      return(-2);
-    }
+static const struct iwmask_name iw_auth_alg_name[] = {
+  { IW_AUTH_ALG_OPEN_SYSTEM,   "open" },
+  { IW_AUTH_ALG_SHARED_KEY,    "shared-key" },
+  { IW_AUTH_ALG_LEAP,          "LEAP" },
+};
+#define        IW_AUTH_ALG_NUM         IW_ARRAY_LEN(iw_auth_alg_name)
+
+static const struct iw_auth_descr      iw_auth_settings[] = {
+  { IW_AUTH_WPA_VERSION, "WPA version", iw_wpa_ver_name, IW_WPA_VER_NUM },
+  { IW_AUTH_KEY_MGMT, "Key management", iw_auth_key_mgmt_name, IW_AUTH_KEY_MGMT_NUM },
+  { IW_AUTH_CIPHER_PAIRWISE, "Pairwise cipher", iw_auth_cypher_name, IW_AUTH_CYPHER_NUM },
+  { IW_AUTH_CIPHER_GROUP, "Pairwise cipher", iw_auth_cypher_name, IW_AUTH_CYPHER_NUM },
+  { IW_AUTH_TKIP_COUNTERMEASURES, "TKIP countermeasures", NULL, 0 },
+  { IW_AUTH_DROP_UNENCRYPTED, "Drop unencrypted", NULL, 0 },
+  { IW_AUTH_80211_AUTH_ALG, "Authentication algorithm", iw_auth_alg_name, IW_AUTH_ALG_NUM },
+  { IW_AUTH_RX_UNENCRYPTED_EAPOL, "Receive unencrypted EAPOL", NULL, 0 },
+  { IW_AUTH_ROAMING_CONTROL, "Roaming control", NULL, 0 },
+  { IW_AUTH_PRIVACY_INVOKED, "Privacy invoked", NULL, 0 },
+};
+#define        IW_AUTH_SETTINGS_NUM            IW_ARRAY_LEN(iw_auth_settings)
+
+/* Values for the IW_ENCODE_ALG_* returned by SIOCSIWENCODEEXT */
+static const char *    iw_encode_alg_name[] = {
+       "none",
+       "WEP",
+       "TKIP",
+       "CCMP",
+       "unknown"
+};
+#define        IW_ENCODE_ALG_NUM               IW_ARRAY_LEN(iw_encode_alg_name)
+
+#ifndef IW_IE_CIPHER_NONE
+/* Cypher values in GENIE (pairwise and group) */
+#define IW_IE_CIPHER_NONE      0
+#define IW_IE_CIPHER_WEP40     1
+#define IW_IE_CIPHER_TKIP      2
+#define IW_IE_CIPHER_WRAP      3
+#define IW_IE_CIPHER_CCMP      4
+#define IW_IE_CIPHER_WEP104    5
+/* Key management in GENIE */
+#define IW_IE_KEY_MGMT_NONE    0
+#define IW_IE_KEY_MGMT_802_1X  1
+#define IW_IE_KEY_MGMT_PSK     2
+#endif /* IW_IE_CIPHER_NONE */
+
+/* Values for the IW_IE_CIPHER_* in GENIE */
+static const char *    iw_ie_cypher_name[] = {
+       "none",
+       "WEP-40",
+       "TKIP",
+       "WRAP",
+       "CCMP",
+       "WEP-104",
+};
+#define        IW_IE_CYPHER_NUM        IW_ARRAY_LEN(iw_ie_cypher_name)
 
-  /* Get range info if we can */
-  if(iw_get_range_info(skfd, ifname, &(range)) >= 0)
-    has_range = 1;
+/* Values for the IW_IE_KEY_MGMT_* in GENIE */
+static const char *    iw_ie_key_mgmt_name[] = {
+       "none",
+       "802.1x",
+       "PSK",
+};
+#define        IW_IE_KEY_MGMT_NUM      IW_ARRAY_LEN(iw_ie_key_mgmt_name)
 
-  /* Display it */
-  if(n == 0)
-    printf("%-8.16s  No Peers/Access-Point in range\n", ifname);
-  else
-    printf("%-8.16s  Peers/Access-Points in range:\n", ifname);
-  for(i = 0; i < n; i++)
+#endif /* WE_ESSENTIAL */
+
+/************************* WPA SUBROUTINES *************************/
+
+#ifndef WE_ESSENTIAL
+/*------------------------------------------------------------------*/
+/*
+ * Print all names corresponding to a mask.
+ * This may want to be used in iw_print_retry_value() ?
+ */
+static void 
+iw_print_mask_name(unsigned int                        mask,
+                  const struct iwmask_name     names[],
+                  const unsigned int           num_names,
+                  const char *                 sep)
+{
+  unsigned int i;
+
+  /* Print out all names for the bitmask */
+  for(i = 0; i < num_names; i++)
     {
-      if(has_qual)
+      if(mask & names[i].mask)
        {
-         /* Print stats for this address */
-         printf("    %s : ", iw_saether_ntop(&hwa[i], temp));
-         iw_print_stats(temp, sizeof(buffer), &qual[i], &range, has_range);
-         printf("%s\n", temp);
+         /* Print out */
+         printf("%s%s", sep, names[i].name);
+         /* Remove the bit from the mask */
+         mask &= ~names[i].mask;
        }
-      else
-       /* Only print the address */
-       printf("    %s\n", iw_saether_ntop(&hwa[i], temp));
     }
-  printf("\n");
-  return(0);
+  /* If there is unconsumed bits... */
+  if(mask != 0)
+    printf("%sUnknown", sep);
 }
 
-/***************************** BITRATES *****************************/
-
 /*------------------------------------------------------------------*/
 /*
- * Print the number of available bitrates for the device
+ * Print the name corresponding to a value, with overflow check.
  */
-static int
-print_bitrate_info(int         skfd,
-                  char *       ifname,
-                  char *       args[],         /* Command line args */
-                  int          count)          /* Args count */
+static void
+iw_print_value_name(unsigned int               value,
+                   const char *                names[],
+                   const unsigned int          num_names)
 {
-  struct iwreq         wrq;
-  struct iw_range      range;
-  int                  k;
-  char                 buffer[128];
+  if(value >= num_names)
+    printf(" unknown (%d)", value);
+  else
+    printf(" %s", names[value]);
+}
 
-  /* Avoid "Unused parameter" warning */
-  args = args; count = count;
+/*------------------------------------------------------------------*/
+/*
+ * Parse, and display the results of an unknown IE.
+ *
+ */
+static void 
+iw_print_ie_unknown(unsigned char *    iebuf,
+                   int                 buflen)
+{
+  int  ielen = iebuf[1] + 2;
+  int  i;
 
-  /* Extract range info */
-  if(iw_get_range_info(skfd, ifname, &range) < 0)
-      fprintf(stderr, "%-8.16s  no bit-rate information.\n\n",
-                     ifname);
-  else
-    {
-      if((range.num_bitrates > 0) && (range.num_bitrates <= IW_MAX_BITRATES))
-       {
-         printf("%-8.16s  %d available bit-rates :\n",
-                ifname, range.num_bitrates);
-         /* Print them all */
-         for(k = 0; k < range.num_bitrates; k++)
-           {
-             iw_print_bitrate(buffer, sizeof(buffer), range.bitrate[k]);
-             /* Maybe this should be %10s */
-             printf("\t  %s\n", buffer);
-           }
-       }
-      else
-       printf("%-8.16s  unknown bit-rate information.\n", ifname);
+  if(ielen > buflen)
+    ielen = buflen;
 
-      /* Get current bit rate */
-      if(iw_get_ext(skfd, ifname, SIOCGIWRATE, &wrq) >= 0)
-       {
-         iw_print_bitrate(buffer, sizeof(buffer), wrq.u.bitrate.value);
-         printf("          Current Bit Rate%c%s\n\n",
-                (wrq.u.bitrate.fixed ? '=' : ':'), buffer);
-       }
-    }
-  return(0);
+  printf("Unknown: ");
+  for(i = 0; i < ielen; i++)
+    printf("%02X", iebuf[i]);
+  printf("\n");
 }
 
-/************************* ENCRYPTION KEYS *************************/
-
 /*------------------------------------------------------------------*/
 /*
- * Print the number of available encryption key for the device
+ * Parse, and display the results of a WPA or WPA2 IE.
+ *
  */
-static int
-print_keys_info(int            skfd,
-               char *          ifname,
-               char *          args[],         /* Command line args */
-               int             count)          /* Args count */
+static inline void 
+iw_print_ie_wpa(unsigned char *        iebuf,
+               int             buflen)
 {
-  struct iwreq         wrq;
-  struct iw_range      range;
-  unsigned char                key[IW_ENCODING_TOKEN_MAX];
-  int                  k;
-  char                 buffer[128];
+  int                  ielen = iebuf[1] + 2;
+  int                  offset = 2;     /* Skip the IE id, and the length. */
+  unsigned char                wpa1_oui[3] = {0x00, 0x50, 0xf2};
+  unsigned char                wpa2_oui[3] = {0x00, 0x0f, 0xac};
+  unsigned char *      wpa_oui;
+  int                  i;
+  uint16_t             ver = 0;
+  uint16_t             cnt = 0;
 
-  /* Avoid "Unused parameter" warning */
-  args = args; count = count;
+  if(ielen > buflen)
+    ielen = buflen;
 
-  /* Extract range info */
-  if(iw_get_range_info(skfd, ifname, &range) < 0)
-      fprintf(stderr, "%-8.16s  no encryption keys information.\n\n",
-                     ifname);
-  else
+#ifdef DEBUG
+  /* Debugging code. In theory useless, because it's debugged ;-) */
+  printf("IE raw value %d [%02X", buflen, iebuf[0]);
+  for(i = 1; i < buflen; i++)
+    printf(":%02X", iebuf[i]);
+  printf("]\n");
+#endif
+
+  switch(iebuf[0])
     {
-      printf("%-8.16s  ", ifname);
-      /* Print key sizes */
-      if((range.num_encoding_sizes > 0) &&
-        (range.num_encoding_sizes < IW_MAX_ENCODING_SIZES))
+    case 0x30:         /* WPA2 */
+      /* Check if we have enough data */
+      if(ielen < 4)
        {
-         printf("%d key sizes : %d", range.num_encoding_sizes,
-                range.encoding_size[0] * 8);
-         /* Print them all */
-         for(k = 1; k < range.num_encoding_sizes; k++)
-           printf(", %d", range.encoding_size[k] * 8);
-         printf("bits\n          ");
+         iw_print_ie_unknown(iebuf, buflen);
+         return;
        }
-      /* Print the keys and associate mode */
-      printf("%d keys available :\n", range.max_encoding_tokens);
-      for(k = 1; k <= range.max_encoding_tokens; k++)
-       {
-         wrq.u.data.pointer = (caddr_t) key;
-         wrq.u.data.length = IW_ENCODING_TOKEN_MAX;
-         wrq.u.data.flags = k;
-         if(iw_get_ext(skfd, ifname, SIOCGIWENCODE, &wrq) < 0)
-           {
-             fprintf(stderr, "Error reading wireless keys (SIOCGIWENCODE): %s\n", strerror(errno));
-             break;
-           }
-         if((wrq.u.data.flags & IW_ENCODE_DISABLED) ||
-            (wrq.u.data.length == 0))
-           printf("\t\t[%d]: off\n", k);
-         else
-           {
-             /* Display the key */
-             iw_print_key(buffer, sizeof(buffer),
-                          key, wrq.u.data.length, wrq.u.data.flags);
-             printf("\t\t[%d]: %s", k, buffer);
 
-             /* Other info... */
-             printf(" (%d bits)", wrq.u.data.length * 8);
-             printf("\n");
-           }
-       }
-      /* Print current key and mode */
-      wrq.u.data.pointer = (caddr_t) key;
-      wrq.u.data.length = IW_ENCODING_TOKEN_MAX;
-      wrq.u.data.flags = 0;    /* Set index to zero to get current */
-      if(iw_get_ext(skfd, ifname, SIOCGIWENCODE, &wrq) >= 0)
-       {
-         /* Note : if above fails, we have already printed an error
-          * message int the loop above */
-         printf("          Current Transmit Key: [%d]\n",
-                wrq.u.data.flags & IW_ENCODE_INDEX);
-         if(wrq.u.data.flags & IW_ENCODE_RESTRICTED)
-           printf("          Security mode:restricted\n");
-         if(wrq.u.data.flags & IW_ENCODE_OPEN)
-           printf("          Security mode:open\n");
-       }
+      wpa_oui = wpa2_oui;
+      break;
 
-      /* Print WPA/802.1x/802.11i security parameters */
-      if(range.we_version_compiled > 17)
-       {
-         /* Display advance encryption capabilities */
-         if(range.enc_capa)
-           {
-             const char *      auth_string[] = { "WPA",
-                                                 "WPA2",
-                                                 "CIPHER TKIP",
-                                                 "CIPHER CCMP" };
-             const int         auth_num = (sizeof(auth_string) /
-                                           sizeof(auth_string[1]));
-             int               i;
-             int               mask = 0x1;
-
-             printf("          Authentication capabilities :\n");
-             for(i = 0; i < auth_num; i++)
-               {
-                 if(range.enc_capa & mask)
-                   printf("\t\t%s\n", auth_string[i]);
-                 mask <<= 1;
-               }
-           }
+    case 0xdd:         /* WPA or else */
+      wpa_oui = wpa1_oui;
+      /* Not all IEs that start with 0xdd are WPA. 
+       * So check that the OUI is valid. Note : offset==2 */
+      if((ielen < 8)
+        || (memcmp(&iebuf[offset], wpa_oui, 3) != 0)
+        || (iebuf[offset + 3] != 0x01))
+       {
+         iw_print_ie_unknown(iebuf, buflen);
+         return;
+       }
 
-         /* Current values for authentication */
-         wrq.u.param.flags = IW_AUTH_KEY_MGMT;
-         if(iw_get_ext(skfd, ifname, SIOCGIWAUTH, &wrq) >= 0)
-             printf("          Current key_mgmt:0x%X\n",
-                    wrq.u.param.value);
-
-         wrq.u.param.flags = IW_AUTH_CIPHER_PAIRWISE;
-         if(iw_get_ext(skfd, ifname, SIOCGIWAUTH, &wrq) >= 0)
-             printf("          Current cipher_pairwise:0x%X\n",
-                    wrq.u.param.value);
-
-         wrq.u.param.flags = IW_AUTH_CIPHER_GROUP;
-         if(iw_get_ext(skfd, ifname, SIOCGIWAUTH, &wrq) >= 0)
-           printf("          Current cipher_group:0x%X\n",
-                  wrq.u.param.value);
-       }
+      /* Skip the OUI type */
+      offset += 4;
+      break;
 
-     printf("\n\n");
+    default:
+      return;
     }
-  return(0);
-}
+  
+  /* Pick version number (little endian) */
+  ver = iebuf[offset] | (iebuf[offset + 1] << 8);
+  offset += 2;
 
-/************************* POWER MANAGEMENT *************************/
+  if(iebuf[0] == 0xdd)
+    printf("WPA Version %d\n", ver);
+  if(iebuf[0] == 0x30)
+    printf("IEEE 802.11i/WPA2 Version %d\n", ver);
 
-/*------------------------------------------------------------------*/
-/*
- * Print Power Management info for each device
- */
-static inline int
-get_pm_value(int               skfd,
-            char *             ifname,
-            struct iwreq *     pwrq,
-            int                flags,
-            char *             buffer,
-            int                buflen)
-{
-  /* Get Another Power Management value */
-  pwrq->u.power.flags = flags;
-  if(iw_get_ext(skfd, ifname, SIOCGIWPOWER, pwrq) >= 0)
+  /* From here, everything is technically optional. */
+
+  /* Check if we are done */
+  if(ielen < (offset + 4))
     {
-      /* Let's check the value and its type */
-      if(pwrq->u.power.flags & IW_POWER_TYPE)
-       {
-         iw_print_pm_value(buffer, buflen,
-                           pwrq->u.power.value, pwrq->u.power.flags);
-         printf("\n                 %s", buffer);
-       }
+      /* We have a short IE.  So we should assume TKIP/TKIP. */
+      printf("                        Group Cipher : TKIP\n");
+      printf("                        Pairwise Cipher : TKIP\n");
+      return;
+    }
+  /* Next we have our group cipher. */
+  if(memcmp(&iebuf[offset], wpa_oui, 3) != 0)
+    {
+      printf("                        Group Cipher : Proprietary\n");
     }
-  return(pwrq->u.power.flags);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Print Power Management info for each device
- */
-static int
-print_pm_info(int              skfd,
-             char *            ifname,
-             char *            args[],         /* Command line args */
-             int               count)          /* Args count */
-{
-  struct iwreq         wrq;
-  struct iw_range      range;
-  char                 buffer[128];
-
-  /* Avoid "Unused parameter" warning */
-  args = args; count = count;
-
-  /* Extract range info */
-  if((iw_get_range_info(skfd, ifname, &range) < 0) ||
-     (range.we_version_compiled < 10))
-      fprintf(stderr, "%-8.16s  no power management information.\n\n",
-                     ifname);
   else
     {
-      printf("%-8.16s  ", ifname);
+      printf("                        Group Cipher :");
+      iw_print_value_name(iebuf[offset+3],
+                         iw_ie_cypher_name, IW_IE_CYPHER_NUM);
+      printf("\n");
+    }
+  offset += 4;
 
-      /* Display modes availables */
-      if(range.pm_capa & IW_POWER_MODE)
-       {
-         printf("Supported modes :\n          ");
-         if(range.pm_capa & (IW_POWER_UNICAST_R | IW_POWER_MULTICAST_R))
-           printf("\t\to Receive all packets (unicast & multicast)\n          ");
-         if(range.pm_capa & IW_POWER_UNICAST_R)
-           printf("\t\to Receive Unicast only (discard multicast)\n          ");
-         if(range.pm_capa & IW_POWER_MULTICAST_R)
-           printf("\t\to Receive Multicast only (discard unicast)\n          ");
-         if(range.pm_capa & IW_POWER_FORCE_S)
-           printf("\t\to Force sending using Power Management\n          ");
-         if(range.pm_capa & IW_POWER_REPEATER)
-           printf("\t\to Repeat multicast\n          ");
-       }
-      /* Display min/max period availables */
-      if(range.pmp_flags & IW_POWER_PERIOD)
-       {
-         int   flags = (range.pmp_flags & ~(IW_POWER_MIN | IW_POWER_MAX));
-         /* Display if auto or fixed */
-         if(range.pmp_flags & IW_POWER_MIN)
-           printf("Auto  period  ; ");
-         else
-           printf("Fixed period  ; ");
-         /* Print the range */
-         iw_print_pm_value(buffer, sizeof(buffer),
-                           range.min_pmp, flags | IW_POWER_MIN);
-         printf("%s\n                          ", buffer);
-         iw_print_pm_value(buffer, sizeof(buffer),
-                           range.max_pmp, flags | IW_POWER_MAX);
-         printf("%s\n          ", buffer);
-       }
-      /* Display min/max timeout availables */
-      if(range.pmt_flags & IW_POWER_TIMEOUT)
-       {
-         int   flags = (range.pmt_flags & ~(IW_POWER_MIN | IW_POWER_MAX));
-         /* Display if auto or fixed */
-         if(range.pmt_flags & IW_POWER_MIN)
-           printf("Auto  timeout ; ");
-         else
-           printf("Fixed timeout ; ");
-         /* Print the range */
-         iw_print_pm_value(buffer, sizeof(buffer),
-                           range.min_pmt, flags | IW_POWER_MIN);
-         printf("%s\n                          ", buffer);
-         iw_print_pm_value(buffer, sizeof(buffer),
-                           range.max_pmt, flags | IW_POWER_MAX);
-         printf("%s\n          ", buffer);
-       }
+  /* Check if we are done */
+  if(ielen < (offset + 2))
+    {
+      /* We don't have a pairwise cipher, or auth method. Assume TKIP. */
+      printf("                        Pairwise Ciphers : TKIP\n");
+      return;
+    }
 
-      /* Get current Power Management settings */
-      wrq.u.power.flags = 0;
-      if(iw_get_ext(skfd, ifname, SIOCGIWPOWER, &wrq) >= 0)
-       {
-         int   flags = wrq.u.power.flags;
+  /* Otherwise, we have some number of pairwise ciphers. */
+  cnt = iebuf[offset] | (iebuf[offset + 1] << 8);
+  offset += 2;
+  printf("                        Pairwise Ciphers (%d) :", cnt);
 
-         /* Is it disabled ? */
-         if(wrq.u.power.disabled)
-           printf("Current mode:off\n          ");
-         else
-           {
-             int       pm_mask = 0;
+  if(ielen < (offset + 4*cnt))
+    return;
 
-             /* Let's check the mode */
-             iw_print_pm_mode(buffer, sizeof(buffer), flags);
-             printf("Current %s", buffer);
+  for(i = 0; i < cnt; i++)
+    {
+      if(memcmp(&iebuf[offset], wpa_oui, 3) != 0)
+       {
+         printf(" Proprietary");
+       }
+      else
+       {
+         iw_print_value_name(iebuf[offset+3],
+                             iw_ie_cypher_name, IW_IE_CYPHER_NUM);
+       }
+      offset+=4;
+    }
+  printf("\n");
+  /* Check if we are done */
+  if(ielen < (offset + 2))
+    return;
 
-             /* Let's check if nothing (simply on) */
-             if((flags & IW_POWER_MODE) == IW_POWER_ON)
-               printf("mode:on");
-             printf("\n                 ");
+  /* Now, we have authentication suites. */
+  cnt = iebuf[offset] | (iebuf[offset + 1] << 8);
+  offset += 2;
+  printf("                        Authentication Suites (%d) :", cnt);
 
-             /* Let's check the value and its type */
-             if(wrq.u.power.flags & IW_POWER_TYPE)
-               {
-                 iw_print_pm_value(buffer, sizeof(buffer),
-                                   wrq.u.power.value, wrq.u.power.flags);
-                 printf("%s", buffer);
-               }
+  if(ielen < (offset + 4*cnt))
+    return;
 
-             /* If we have been returned a MIN value, ask for the MAX */
-             if(flags & IW_POWER_MIN)
-               pm_mask = IW_POWER_MAX;
-             /* If we have been returned a MAX value, ask for the MIN */
-             if(flags & IW_POWER_MAX)
-               pm_mask = IW_POWER_MIN;
-             /* If we have something to ask for... */
-             if(pm_mask)
-               get_pm_value(skfd, ifname, &wrq, pm_mask,
-                            buffer, sizeof(buffer));
-
-             /* And if we have both a period and a timeout, ask the other */
-             pm_mask = (range.pm_capa & (~(wrq.u.power.flags) &
-                                         IW_POWER_TYPE));
-             if(pm_mask)
-               {
-                 int   base_mask = pm_mask;
-                 flags = get_pm_value(skfd, ifname, &wrq, pm_mask,
-                                      buffer, sizeof(buffer));
-                 pm_mask = 0;
+  for(i = 0; i < cnt; i++)
+    {
+      if(memcmp(&iebuf[offset], wpa_oui, 3) != 0)
+       {
+         printf(" Proprietary");
+       }
+      else
+       {
+         iw_print_value_name(iebuf[offset+3],
+                             iw_ie_key_mgmt_name, IW_IE_KEY_MGMT_NUM);
+       }
+       offset+=4;
+     }
+  printf("\n");
+  /* Check if we are done */
+  if(ielen < (offset + 1))
+    return;
 
-                 /* If we have been returned a MIN value, ask for the MAX */
-                 if(flags & IW_POWER_MIN)
-                   pm_mask = IW_POWER_MAX | base_mask;
-                 /* If we have been returned a MAX value, ask for the MIN */
-                 if(flags & IW_POWER_MAX)
-                   pm_mask = IW_POWER_MIN | base_mask;
-                 /* If we have something to ask for... */
-                 if(pm_mask)
-                   get_pm_value(skfd, ifname, &wrq, pm_mask,
-                                buffer, sizeof(buffer));
-               }
-           }
-       }
-      printf("\n");
+  /* Otherwise, we have capabilities bytes.
+   * For now, we only care about preauth which is in bit position 1 of the
+   * first byte.  (But, preauth with WPA version 1 isn't supposed to be 
+   * allowed.) 8-) */
+  if(iebuf[offset] & 0x01)
+    {
+      printf("                       Preauthentication Supported\n");
     }
-  return(0);
 }
-
-/************************** TRANSMIT POWER **************************/
-
 /*------------------------------------------------------------------*/
 /*
- * Print the number of available transmit powers for the device
+ * Process a generic IE and display the info in human readable form
+ * for some of the most interesting ones.
+ * For now, we only decode the WPA IEs.
  */
-static int
-print_txpower_info(int         skfd,
-                  char *       ifname,
-                  char *       args[],         /* Command line args */
-                  int          count)          /* Args count */
+static inline void
+iw_print_gen_ie(unsigned char *        buffer,
+               int             buflen)
 {
-  struct iwreq         wrq;
-  struct iw_range      range;
-  int                  dbm;
-  int                  mwatt;
-  int                  k;
-
-  /* Avoid "Unused parameter" warning */
-  args = args; count = count;
-
-  /* Extract range info */
-  if((iw_get_range_info(skfd, ifname, &range) < 0) ||
-     (range.we_version_compiled < 10))
-      fprintf(stderr, "%-8.16s  no transmit-power information.\n\n",
-                     ifname);
-  else
-    {
-      if((range.num_txpower <= 0) || (range.num_txpower > IW_MAX_TXPOWER))
-       printf("%-8.16s  unknown transmit-power information.\n\n", ifname);
-      else
-       {
-         printf("%-8.16s  %d available transmit-powers :\n",
-                ifname, range.num_txpower);
-         /* Print them all */
-         for(k = 0; k < range.num_txpower; k++)
-           {
-             /* Check for relative values */
-             if(range.txpower_capa & IW_TXPOW_RELATIVE)
-               {
-                 printf("\t  %d (no units)\n", range.txpower[k]);
-               }
-             else
-               {
-                 if(range.txpower_capa & IW_TXPOW_MWATT)
-                   {
-                     dbm = iw_mwatt2dbm(range.txpower[k]);
-                     mwatt = range.txpower[k];
-                   }
-                 else
-                   {
-                     dbm = range.txpower[k];
-                     mwatt = iw_dbm2mwatt(range.txpower[k]);
-                   }
-                 printf("\t  %d dBm  \t(%d mW)\n", dbm, mwatt);
-               }
-           }
-       }
-
-      /* Get current Transmit Power */
-      if(iw_get_ext(skfd, ifname, SIOCGIWTXPOW, &wrq) >= 0)
-       {
-         printf("          Current Tx-Power");
-         /* Disabled ? */
-         if(wrq.u.txpower.disabled)
-           printf(":off\n\n");
-         else
-           {
-             /* Fixed ? */
-             if(wrq.u.txpower.fixed)
-               printf("=");
-             else
-               printf(":");
-             /* Check for relative values */
-             if(wrq.u.txpower.flags & IW_TXPOW_RELATIVE)
-               {
-                 /* I just hate relative value, because they are
-                  * driver specific, so not very meaningfull to apps.
-                  * But, we have to support that, because
-                  * this is the way hardware is... */
-                 printf("\t  %d (no units)\n", wrq.u.txpower.value);
-               }
-             else
-               {
-                 if(wrq.u.txpower.flags & IW_TXPOW_MWATT)
-                   {
-                     dbm = iw_mwatt2dbm(wrq.u.txpower.value);
-                     mwatt = wrq.u.txpower.value;
-                   }
-                 else
-                   {
-                     dbm = wrq.u.txpower.value;
-                     mwatt = iw_dbm2mwatt(wrq.u.txpower.value);
-                   }
-                 printf("%d dBm  \t(%d mW)\n\n", dbm, mwatt);
-               }
-           }
-       }
-    }
-  return(0);
-}
-
-/*********************** RETRY LIMIT/LIFETIME ***********************/
-
-/*------------------------------------------------------------------*/
-/*
- * Print one retry value
- */
-static inline int
-get_retry_value(int            skfd,
-               char *          ifname,
-               struct iwreq *  pwrq,
-               int             flags,
-               char *          buffer,
-               int             buflen)
-{
-  /* Get Another retry value */
-  pwrq->u.retry.flags = flags;
-  if(iw_get_ext(skfd, ifname, SIOCGIWRETRY, pwrq) >= 0)
-    {
-      /* Let's check the value and its type */
-      if(pwrq->u.retry.flags & IW_RETRY_TYPE)
-       {
-         iw_print_retry_value(buffer, buflen,
-                              pwrq->u.retry.value, pwrq->u.retry.flags);
-         printf("%s\n                 ", buffer);
-       }
-    }
-  return(pwrq->u.retry.flags);
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Print Retry info for each device
- */
-static int
-print_retry_info(int           skfd,
-                char *         ifname,
-                char *         args[],         /* Command line args */
-                int            count)          /* Args count */
-{
-  struct iwreq         wrq;
-  struct iw_range      range;
-  char                 buffer[128];
-
-  /* Avoid "Unused parameter" warning */
-  args = args; count = count;
+  int offset = 0;
 
-  /* Extract range info */
-  if((iw_get_range_info(skfd, ifname, &range) < 0) ||
-     (range.we_version_compiled < 11))
-    fprintf(stderr, "%-8.16s  no retry limit/lifetime information.\n\n",
-           ifname);
-  else
+  /* Loop on each IE, each IE is minimum 2 bytes */
+  while(offset <= (buflen - 2))
     {
-      printf("%-8.16s  ", ifname);
-
-      /* Display min/max limit availables */
-      if(range.retry_flags & IW_RETRY_LIMIT)
-       {
-         int   flags = (range.retry_flags & ~(IW_RETRY_MIN | IW_RETRY_MAX));
-         /* Display if auto or fixed */
-         if(range.retry_flags & IW_RETRY_MIN)
-           printf("Auto  limit    ; ");
-         else
-           printf("Fixed limit    ; ");
-         /* Print the range */
-         iw_print_retry_value(buffer, sizeof(buffer),
-                              range.min_retry, flags | IW_RETRY_MIN);
-         printf("%s\n                           ", buffer);
-         iw_print_retry_value(buffer, sizeof(buffer),
-                              range.max_retry, flags | IW_RETRY_MAX);
-         printf("%s\n          ", buffer);
-         
-       }
-      /* Display min/max lifetime availables */
-      if(range.r_time_flags & IW_RETRY_LIFETIME)
-       {
-         int   flags = (range.r_time_flags & ~(IW_RETRY_MIN | IW_RETRY_MAX));
-         /* Display if auto or fixed */
-         if(range.r_time_flags & IW_RETRY_MIN)
-           printf("Auto  lifetime ; ");
-         else
-           printf("Fixed lifetime ; ");
-         /* Print the range */
-         iw_print_retry_value(buffer, sizeof(buffer),
-                              range.min_r_time, flags | IW_RETRY_MIN);
-         printf("%s\n                           ", buffer);
-         iw_print_retry_value(buffer, sizeof(buffer),
-                              range.max_r_time, flags | IW_RETRY_MAX);
-         printf("%s\n          ", buffer);
-         
-       }
+      printf("                    IE: ");
 
-      /* Get current retry settings */
-      wrq.u.retry.flags = 0;
-      if(iw_get_ext(skfd, ifname, SIOCGIWRETRY, &wrq) >= 0)
+      /* Check IE type */
+      switch(buffer[offset])
        {
-         int   flags = wrq.u.retry.flags;
-
-         /* Is it disabled ? */
-         if(wrq.u.retry.disabled)
-           printf("Current mode:off\n          ");
-         else
-           {
-             int       retry_mask = 0;
-
-             /* Let's check the mode */
-             printf("Current mode:on\n                 ");
-
-             /* Let's check the value and its type */
-             if(wrq.u.retry.flags & IW_RETRY_TYPE)
-               {
-                 iw_print_retry_value(buffer, sizeof(buffer),
-                                      wrq.u.retry.value, wrq.u.retry.flags);
-                 printf("%s\n                 ", buffer);
-               }
-
-             /* If we have been returned a MIN value, ask for the MAX */
-             if(flags & IW_RETRY_MIN)
-               retry_mask = IW_RETRY_MAX;
-             /* If we have been returned a MAX value, ask for the MIN */
-             if(flags & IW_RETRY_MAX)
-               retry_mask = IW_RETRY_MIN;
-             /* If we have something to ask for... */
-             if(retry_mask)
-               get_retry_value(skfd, ifname, &wrq, retry_mask,
-                               buffer, sizeof(buffer));
-
-             /* And if we have both a period and a timeout, ask the other */
-             retry_mask = (range.retry_capa & (~(wrq.u.retry.flags) &
-                                         IW_RETRY_TYPE));
-             if(retry_mask)
-               {
-                 int   base_mask = retry_mask;
-                 flags = get_retry_value(skfd, ifname, &wrq, retry_mask,
-                                         buffer, sizeof(buffer));
-                 retry_mask = 0;
-
-                 /* If we have been returned a MIN value, ask for the MAX */
-                 if(flags & IW_RETRY_MIN)
-                   retry_mask = IW_RETRY_MAX | base_mask;
-                 /* If we have been returned a MAX value, ask for the MIN */
-                 if(flags & IW_RETRY_MAX)
-                   retry_mask = IW_RETRY_MIN | base_mask;
-                 /* If we have something to ask for... */
-                 if(retry_mask)
-                   get_retry_value(skfd, ifname, &wrq, retry_mask,
-                                   buffer, sizeof(buffer));
-               }
-           }
+       case 0xdd:      /* WPA1 (and other) */
+       case 0x30:      /* WPA2 */
+         iw_print_ie_wpa(buffer + offset, buflen);
+         break;
+       default:
+         iw_print_ie_unknown(buffer + offset, buflen);
        }
-      printf("\n");
+      /* Skip over this IE to the next one in the list. */
+      offset += buffer[offset+1] + 2;
     }
-  return(0);
 }
+#endif /* WE_ESSENTIAL */
 
 /***************************** SCANNING *****************************/
 /*
@@ -792,317 +446,53 @@ print_retry_info(int             skfd,
 
 /*------------------------------------------------------------------*/
 /*
- * Parse, and display the results of a WPA or WPA2 IE.
- *
+ * Print one element from the scanning results
  */
-static void 
-iw_print_ie_unknown(unsigned char *    iebuf,
-                   int                 buflen)
+static inline void
+print_scanning_token(struct stream_descr *     stream, /* Stream of events */
+                    struct iw_event *          event,  /* Extracted token */
+                    struct iwscan_state *      state,
+                    struct iw_range *  iw_range,       /* Range info */
+                    int                has_range)
 {
-  int  ielen = iebuf[1] + 2;
-  int  i;
-
-  if(ielen > buflen)
-    ielen = buflen;
-
-  printf("Unknown: ");
-  for(i = 0; i < ielen; i++)
-    printf("%02X", iebuf[i]);
-  printf("\n");
-}
+  char         buffer[128];    /* Temporary buffer */
 
-/*-----------------------------------------------------------------*/
-/*
- * Display the cipher type for the value passed in.
- *
- */
-static inline void 
-iw_print_ie_cipher(unsigned char       csuite)
-{
-  switch (csuite)
+  /* Now, let's decode the event */
+  switch(event->cmd)
     {
-    case 0x00:
-      printf("None or same as Group ");
+    case SIOCGIWAP:
+      printf("          Cell %02d - Address: %s\n", state->ap_num,
+            iw_saether_ntop(&event->u.ap_addr, buffer));
+      state->ap_num++;
       break;
-    case 0x01:
-      printf("WEP-40 ");
+    case SIOCGIWNWID:
+      if(event->u.nwid.disabled)
+       printf("                    NWID:off/any\n");
+      else
+       printf("                    NWID:%X\n", event->u.nwid.value);
       break;
-    case 0x02:
-      printf("TKIP ");
+    case SIOCGIWFREQ:
+      {
+       double          freq;                   /* Frequency/channel */
+       int             channel = -1;           /* Converted to channel */
+       freq = iw_freq2float(&(event->u.freq));
+       /* Convert to channel if possible */
+       if(has_range)
+         channel = iw_freq_to_channel(freq, iw_range);
+       iw_print_freq(buffer, sizeof(buffer),
+                     freq, channel, event->u.freq.flags);
+       printf("                    %s\n", buffer);
+      }
       break;
-    case 0x03:
-      printf("WRAP ");
+    case SIOCGIWMODE:
+      /* Note : event->u.mode is unsigned, no need to check <= 0 */
+      if(event->u.mode >= IW_NUM_OPER_MODE)
+       event->u.mode = IW_NUM_OPER_MODE;
+      printf("                    Mode:%s\n",
+            iw_operation_mode[event->u.mode]);
       break;
-    case 0x04:
-      printf("CCMP ");
-      break;
-    case 0x05:
-      printf("WEP-104 ");
-      break;
-    default:
-      printf("Unknown ");
-      break;
-    }
- }
-/*------------------------------------------------------------------*/
-/*
- * Parse, and display the results of a WPA or WPA2 IE.
- *
- */
-static inline void 
-iw_print_ie_wpa(unsigned char *        iebuf,
-               int             buflen)
-{
-  int                  ielen = iebuf[1] + 2;
-  int                  offset = 2;     /* Skip the IE id, and the length. */
-  unsigned char                wpa1_oui[3] = {0x00, 0x50, 0xf2};
-  unsigned char                wpa2_oui[3] = {0x00, 0x0f, 0xac};
-  unsigned char *      wpa_oui;
-  int                  i;
-  uint16_t             ver = 0;
-  uint16_t             cnt = 0;
-
-  if(ielen > buflen)
-    ielen = buflen;
-
-  switch(iebuf[0])
-    {
-    case 0x30:         /* WPA2 */
-      /* Check if we have enough data */
-      if(ielen < 4)
-       {
-         iw_print_ie_unknown(iebuf, buflen);
-         return;
-       }
-
-      wpa_oui = wpa2_oui;
-      break;
-
-    case 0xdd:         /* WPA or else */
-      wpa_oui = wpa1_oui;
-      /* Not all IEs that start with 0xdd are WPA. 
-       * So check that the OUI is valid. */
-      if((ielen < 8)
-        || ((memcmp(&iebuf[offset], wpa_oui, 3) != 0)
-            && (iebuf[offset+3] == 0x01)))
-       {
-         iw_print_ie_unknown(iebuf, buflen);
-         return;
-       }
-
-       offset += 4;
-       break;
-
-    default:
-      return;
-    }
-  
-  /* Pick version number (little endian) */
-  ver = iebuf[offset] | (iebuf[offset + 1] << 8);
-  offset += 2;
-
-  if(iebuf[0] == 0xdd)
-    printf("WPA Version %d\n", ver);
-  if(iebuf[0] == 0x30)
-    printf("IEEE 802.11i/WPA2 Version %d\n", ver);
-
-  /* From here, everything is technically optional. */
-
-  /* Check if we are done */
-  if(ielen < (offset + 4))
-    {
-      /* We have a short IE.  So we should assume TKIP/TKIP. */
-      printf("                        Group Cipher : TKIP\n");
-      printf("                        Pairwise Cipher : TKIP\n");
-      return;
-    }
-  /* Next we have our group cipher. */
-  if(memcmp(&iebuf[offset], wpa_oui, 3) != 0)
-    {
-      printf("                        Group Cipher : Proprietary\n");
-    }
-  else
-    {
-      printf("                        Group Cipher : ");
-      iw_print_ie_cipher(iebuf[offset+3]);
-      printf("\n");
-    }
-  offset += 4;
-
-  /* Check if we are done */
-  if(ielen < (offset + 2))
-    {
-      /* We don't have a pairwise cipher, or auth method. Assume TKIP. */
-      printf("                        Pairwise Ciphers (1) : TKIP\n");
-      return;
-    }
-
-  /* Otherwise, we have some number of pairwise ciphers. */
-  cnt = iebuf[offset] | (iebuf[offset + 1] << 8);
-  offset += 2;
-  printf("                        Pairwise Ciphers (%d) : ", cnt);
-
-  if(ielen < (offset + 4*cnt))
-    return;
-
-  for(i = 0; i < cnt; i++)
-    {
-      if(memcmp(&iebuf[offset], wpa_oui, 3) != 0)
-       {
-         printf("Proprietary  ");
-       }
-      else
-       {
-         iw_print_ie_cipher(iebuf[offset+3]);
-       }
-      offset+=4;
-    }
-  printf("\n");
-  /* Check if we are done */
-  if(ielen < (offset + 2))
-    return;
-
-  /* Now, we have authentication suites. */
-  cnt = iebuf[offset] | (iebuf[offset + 1] << 8);
-  offset += 2;
-  printf("                        Authentication Suites (%d) : ", cnt);
-
-  if(ielen < (offset + 4*cnt))
-    return;
-
-  for(i = 0; i < cnt; i++)
-    {
-      if(memcmp(&iebuf[offset], wpa_oui, 3) != 0)
-       {
-         printf("Proprietary  ");
-       }
-      else
-       {
-         switch(iebuf[offset+3])
-           {
-           case 0x00:
-             printf("Reserved  ");
-             break;
-
-           case 0x01:
-             printf("802.1X  ");
-             break;
-
-           case 0x02:
-             printf("PSK  ");
-             break;
-
-           default:
-             printf("Unknown  ");
-             break;
-           }
-       }
-       offset+=4;
-     }
-  printf("\n");
-  /* Check if we are done */
-  if(ielen < (offset + 1))
-    return;
-
-  /* Otherwise, we have capabilities bytes.
-   * For now, we only care about preauth which is in bit position 1 of the
-   * first byte.  (But, preauth with WPA version 1 isn't supposed to be 
-   * allowed.) 8-) */
-  if(iebuf[offset] & 0x01)
-    {
-      printf("                       Preauthentication Supported\n");
-    }
-}
-/*------------------------------------------------------------------*/
-/*
- * Process a generic IE and display the info in human readable form
- * for some of the most interesting ones.
- * For now, we only decode the WPA IEs.
- */
-static inline void
-iw_print_gen_ie(unsigned char *        buffer,
-               int             buflen)
-{
-  int offset = 0;
-
-  /* Loop on each IE, each IE is minimum 2 bytes */
-  while(offset <= (buflen - 2))
-    {
-      printf("                    IE: ");
-
-      /* Check IE type */
-      switch(buffer[offset])
-       {
-       case 0xdd:      /* WPA1 (and other) */
-       case 0x30:      /* WPA2 */
-         iw_print_ie_wpa(buffer + offset, buflen);
-         break;
-       default:
-         iw_print_ie_unknown(buffer + offset, buflen);
-       }
-      /* Skip over this IE to the next one in the list. */
-      offset += buffer[offset+1] + 2;
-    }
-}
-
-/*------------------------------------------------------------------*/
-/*
- * Print one element from the scanning results
- */
-static inline void
-print_scanning_token(struct stream_descr *     stream, /* Stream of events */
-                    struct iw_event *          event,  /* Extracted token */
-                    struct iwscan_state *      state,
-                    struct iw_range *  iw_range,       /* Range info */
-                    int                has_range)
-{
-  char         buffer[128];    /* Temporary buffer */
-
-  /* Now, let's decode the event */
-  switch(event->cmd)
-    {
-    case SIOCGIWAP:
-      printf("          Cell %02d - Address: %s\n", state->ap_num,
-            iw_saether_ntop(&event->u.ap_addr, buffer));
-      state->ap_num++;
-      break;
-    case SIOCGIWNWID:
-      if(event->u.nwid.disabled)
-       printf("                    NWID:off/any\n");
-      else
-       printf("                    NWID:%X\n", event->u.nwid.value);
-      break;
-    case SIOCGIWFREQ:
-      {
-       double          freq;                   /* Frequency/channel */
-       int             channel = -1;           /* Converted to channel */
-       freq = iw_freq2float(&(event->u.freq));
-       /* Convert to channel if possible */
-       if(has_range)
-         channel = iw_freq_to_channel(freq, iw_range);
-       iw_print_freq(buffer, sizeof(buffer),
-                     freq, channel, event->u.freq.flags);
-       printf("                    %s\n", buffer);
-      }
-      break;
-    case SIOCGIWMODE:
-      printf("                    Mode:%s\n",
-            iw_operation_mode[event->u.mode]);
-      break;
-    case SIOCGIWNAME:
-      printf("                    Protocol:%-1.16s\n", event->u.name);
+    case SIOCGIWNAME:
+      printf("                    Protocol:%-1.16s\n", event->u.name);
       break;
     case SIOCGIWESSID:
       {
@@ -1164,230 +554,1111 @@ print_scanning_token(struct stream_descr *    stream, /* Stream of events */
       /* Check for termination */
       if(stream->value == NULL)
        {
-         printf("\n");
-         state->val_index = 0;
+         printf("\n");
+         state->val_index = 0;
+       }
+      else
+       state->val_index++;
+      break;
+    case SIOCGIWMODUL:
+      {
+       unsigned int    modul = event->u.param.value;
+       int             i;
+       int             n = 0;
+       printf("                    Modulations :");
+       for(i = 0; i < IW_SIZE_MODUL_LIST; i++)
+         {
+           if((modul & iw_modul_list[i].mask) == iw_modul_list[i].mask)
+             {
+               if((n++ % 8) == 7)
+                 printf("\n                        ");
+               else
+                 printf(" ; ");
+               printf("%s", iw_modul_list[i].cmd);
+             }
+         }
+       printf("\n");
+      }
+      break;
+    case IWEVQUAL:
+      iw_print_stats(buffer, sizeof(buffer),
+                    &event->u.qual, iw_range, has_range);
+      printf("                    %s\n", buffer);
+      break;
+#ifndef WE_ESSENTIAL
+    case IWEVGENIE:
+      /* Informations Elements are complex, let's do only some of them */
+      iw_print_gen_ie(event->u.data.pointer, event->u.data.length);
+      break;
+#endif /* WE_ESSENTIAL */
+    case IWEVCUSTOM:
+      {
+       char custom[IW_CUSTOM_MAX+1];
+       if((event->u.data.pointer) && (event->u.data.length))
+         memcpy(custom, event->u.data.pointer, event->u.data.length);
+       custom[event->u.data.length] = '\0';
+       printf("                    Extra:%s\n", custom);
+      }
+      break;
+    default:
+      printf("                    (Unknown Wireless Token 0x%04X)\n",
+            event->cmd);
+   }   /* switch(event->cmd) */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Perform a scanning on one device
+ */
+static int
+print_scanning_info(int                skfd,
+                   char *      ifname,
+                   char *      args[],         /* Command line args */
+                   int         count)          /* Args count */
+{
+  struct iwreq         wrq;
+  struct iw_scan_req    scanopt;               /* Options for 'set' */
+  int                  scanflags = 0;          /* Flags for scan */
+  unsigned char *      buffer = NULL;          /* Results */
+  int                  buflen = IW_SCAN_MAX_DATA; /* Min for compat WE<17 */
+  struct iw_range      range;
+  int                  has_range;
+  struct timeval       tv;                             /* Select timeout */
+  int                  timeout = 15000000;             /* 15s */
+
+  /* Avoid "Unused parameter" warning */
+  args = args; count = count;
+
+  /* Debugging stuff */
+  if((IW_EV_LCP_PK2_LEN != IW_EV_LCP_PK_LEN) || (IW_EV_POINT_PK2_LEN != IW_EV_POINT_PK_LEN))
+    {
+      fprintf(stderr, "*** Please report to jt@hpl.hp.com your platform details\n");
+      fprintf(stderr, "*** and the following line :\n");
+      fprintf(stderr, "*** IW_EV_LCP_PK2_LEN = %zu ; IW_EV_POINT_PK2_LEN = %zu\n\n",
+             IW_EV_LCP_PK2_LEN, IW_EV_POINT_PK2_LEN);
+    }
+
+  /* Get range stuff */
+  has_range = (iw_get_range_info(skfd, ifname, &range) >= 0);
+
+  /* Check if the interface could support scanning. */
+  if((!has_range) || (range.we_version_compiled < 14))
+    {
+      fprintf(stderr, "%-8.16s  Interface doesn't support scanning.\n\n",
+             ifname);
+      return(-1);
+    }
+
+  /* Init timeout value -> 250ms between set and first get */
+  tv.tv_sec = 0;
+  tv.tv_usec = 250000;
+
+  /* Clean up set args */
+  memset(&scanopt, 0, sizeof(scanopt));
+
+  /* Parse command line arguments and extract options.
+   * Note : when we have enough options, we should use the parser
+   * from iwconfig... */
+  while(count > 0)
+    {
+      /* One arg is consumed (the option name) */
+      count--;
+      
+      /*
+       * Check for Active Scan (scan with specific essid)
+       */
+      if(!strncmp(args[0], "essid", 5))
+       {
+         if(count < 1)
+           {
+             fprintf(stderr, "Too few arguments for scanning option [%s]\n",
+                     args[0]);
+             return(-1);
+           }
+         args++;
+         count--;
+
+         /* Store the ESSID in the scan options */
+         scanopt.essid_len = strlen(args[0]);
+         memcpy(scanopt.essid, args[0], scanopt.essid_len);
+         /* Initialise BSSID as needed */
+         if(scanopt.bssid.sa_family == 0)
+           {
+             scanopt.bssid.sa_family = ARPHRD_ETHER;
+             memset(scanopt.bssid.sa_data, 0xff, ETH_ALEN);
+           }
+         /* Scan only this ESSID */
+         scanflags |= IW_SCAN_THIS_ESSID;
+       }
+      else
+       /* Check for last scan result (do not trigger scan) */
+       if(!strncmp(args[0], "last", 4))
+         {
+           /* Hack */
+           scanflags |= IW_SCAN_HACK;
+         }
+       else
+         {
+           fprintf(stderr, "Invalid scanning option [%s]\n", args[0]);
+           return(-1);
+         }
+
+      /* Next arg */
+      args++;
+    }
+
+  /* Check if we have scan options */
+  if(scanflags)
+    {
+      wrq.u.data.pointer = (caddr_t) &scanopt;
+      wrq.u.data.length = sizeof(scanopt);
+      wrq.u.data.flags = scanflags;
+    }
+  else
+    {
+      wrq.u.data.pointer = NULL;
+      wrq.u.data.flags = 0;
+      wrq.u.data.length = 0;
+    }
+
+  /* If only 'last' was specified on command line, don't trigger a scan */
+  if(scanflags == IW_SCAN_HACK)
+    {
+      /* Skip waiting */
+      tv.tv_usec = 0;
+    }
+  else
+    {
+      /* Initiate Scanning */
+      if(iw_set_ext(skfd, ifname, SIOCSIWSCAN, &wrq) < 0)
+       {
+         if((errno != EPERM) || (scanflags != 0))
+           {
+             fprintf(stderr, "%-8.16s  Interface doesn't support scanning : %s\n\n",
+                     ifname, strerror(errno));
+             return(-1);
+           }
+         /* If we don't have the permission to initiate the scan, we may
+          * still have permission to read left-over results.
+          * But, don't wait !!! */
+#if 0
+         /* Not cool, it display for non wireless interfaces... */
+         fprintf(stderr, "%-8.16s  (Could not trigger scanning, just reading left-over results)\n", ifname);
+#endif
+         tv.tv_usec = 0;
+       }
+    }
+  timeout -= tv.tv_usec;
+
+  /* Forever */
+  while(1)
+    {
+      fd_set           rfds;           /* File descriptors for select */
+      int              last_fd;        /* Last fd */
+      int              ret;
+
+      /* Guess what ? We must re-generate rfds each time */
+      FD_ZERO(&rfds);
+      last_fd = -1;
+
+      /* In here, add the rtnetlink fd in the list */
+
+      /* Wait until something happens */
+      ret = select(last_fd + 1, &rfds, NULL, NULL, &tv);
+
+      /* Check if there was an error */
+      if(ret < 0)
+       {
+         if(errno == EAGAIN || errno == EINTR)
+           continue;
+         fprintf(stderr, "Unhandled signal - exiting...\n");
+         return(-1);
+       }
+
+      /* Check if there was a timeout */
+      if(ret == 0)
+       {
+         unsigned char *       newbuf;
+
+       realloc:
+         /* (Re)allocate the buffer - realloc(NULL, len) == malloc(len) */
+         newbuf = realloc(buffer, buflen);
+         if(newbuf == NULL)
+           {
+             if(buffer)
+               free(buffer);
+             fprintf(stderr, "%s: Allocation failed\n", __FUNCTION__);
+             return(-1);
+           }
+         buffer = newbuf;
+
+         /* Try to read the results */
+         wrq.u.data.pointer = buffer;
+         wrq.u.data.flags = 0;
+         wrq.u.data.length = buflen;
+         if(iw_get_ext(skfd, ifname, SIOCGIWSCAN, &wrq) < 0)
+           {
+             /* Check if buffer was too small (WE-17 only) */
+             if((errno == E2BIG) && (range.we_version_compiled > 16))
+               {
+                 /* Some driver may return very large scan results, either
+                  * because there are many cells, or because they have many
+                  * large elements in cells (like IWEVCUSTOM). Most will
+                  * only need the regular sized buffer. We now use a dynamic
+                  * allocation of the buffer to satisfy everybody. Of course,
+                  * as we don't know in advance the size of the array, we try
+                  * various increasing sizes. Jean II */
+
+                 /* Check if the driver gave us any hints. */
+                 if(wrq.u.data.length > buflen)
+                   buflen = wrq.u.data.length;
+                 else
+                   buflen *= 2;
+
+                 /* Try again */
+                 goto realloc;
+               }
+
+             /* Check if results not available yet */
+             if(errno == EAGAIN)
+               {
+                 /* Restart timer for only 100ms*/
+                 tv.tv_sec = 0;
+                 tv.tv_usec = 100000;
+                 timeout -= tv.tv_usec;
+                 if(timeout > 0)
+                   continue;   /* Try again later */
+               }
+
+             /* Bad error */
+             free(buffer);
+             fprintf(stderr, "%-8.16s  Failed to read scan data : %s\n\n",
+                     ifname, strerror(errno));
+             return(-2);
+           }
+         else
+           /* We have the results, go to process them */
+           break;
+       }
+
+      /* In here, check if event and event type
+       * if scan event, read results. All errors bad & no reset timeout */
+    }
+
+  if(wrq.u.data.length)
+    {
+      struct iw_event          iwe;
+      struct stream_descr      stream;
+      struct iwscan_state      state = { .ap_num = 1, .val_index = 0 };
+      int                      ret;
+      
+#ifdef DEBUG
+      /* Debugging code. In theory useless, because it's debugged ;-) */
+      int      i;
+      printf("Scan result %d [%02X", wrq.u.data.length, buffer[0]);
+      for(i = 1; i < wrq.u.data.length; i++)
+       printf(":%02X", buffer[i]);
+      printf("]\n");
+#endif
+      printf("%-8.16s  Scan completed :\n", ifname);
+      iw_init_event_stream(&stream, (char *) buffer, wrq.u.data.length);
+      do
+       {
+         /* Extract an event and print it */
+         ret = iw_extract_event_stream(&stream, &iwe,
+                                       range.we_version_compiled);
+         if(ret > 0)
+           print_scanning_token(&stream, &iwe, &state,
+                                &range, has_range);
+       }
+      while(ret > 0);
+      printf("\n");
+    }
+  else
+    printf("%-8.16s  No scan results\n\n", ifname);
+
+  free(buffer);
+  return(0);
+}
+
+/*********************** FREQUENCIES/CHANNELS ***********************/
+
+/*------------------------------------------------------------------*/
+/*
+ * Print the number of channels and available frequency for the device
+ */
+static int
+print_freq_info(int            skfd,
+               char *          ifname,
+               char *          args[],         /* Command line args */
+               int             count)          /* Args count */
+{
+  struct iwreq         wrq;
+  struct iw_range      range;
+  double               freq;
+  int                  k;
+  int                  channel;
+  char                 buffer[128];    /* Temporary buffer */
+
+  /* Avoid "Unused parameter" warning */
+  args = args; count = count;
+
+  /* Get list of frequencies / channels */
+  if(iw_get_range_info(skfd, ifname, &range) < 0)
+      fprintf(stderr, "%-8.16s  no frequency information.\n\n",
+                     ifname);
+  else
+    {
+      if(range.num_frequency > 0)
+       {
+         printf("%-8.16s  %d channels in total; available frequencies :\n",
+                ifname, range.num_channels);
+         /* Print them all */
+         for(k = 0; k < range.num_frequency; k++)
+           {
+             freq = iw_freq2float(&(range.freq[k]));
+             iw_print_freq_value(buffer, sizeof(buffer), freq);
+             printf("          Channel %.2d : %s\n",
+                    range.freq[k].i, buffer);
+           }
+       }
+      else
+       printf("%-8.16s  %d channels\n",
+              ifname, range.num_channels);
+
+      /* Get current frequency / channel and display it */
+      if(iw_get_ext(skfd, ifname, SIOCGIWFREQ, &wrq) >= 0)
+       {
+         freq = iw_freq2float(&(wrq.u.freq));
+         channel = iw_freq_to_channel(freq, &range);
+         iw_print_freq(buffer, sizeof(buffer),
+                       freq, channel, wrq.u.freq.flags);
+         printf("          Current %s\n\n", buffer);
+       }
+    }
+  return(0);
+}
+
+/***************************** BITRATES *****************************/
+
+/*------------------------------------------------------------------*/
+/*
+ * Print the number of available bitrates for the device
+ */
+static int
+print_bitrate_info(int         skfd,
+                  char *       ifname,
+                  char *       args[],         /* Command line args */
+                  int          count)          /* Args count */
+{
+  struct iwreq         wrq;
+  struct iw_range      range;
+  int                  k;
+  char                 buffer[128];
+
+  /* Avoid "Unused parameter" warning */
+  args = args; count = count;
+
+  /* Extract range info */
+  if(iw_get_range_info(skfd, ifname, &range) < 0)
+      fprintf(stderr, "%-8.16s  no bit-rate information.\n\n",
+                     ifname);
+  else
+    {
+      if((range.num_bitrates > 0) && (range.num_bitrates <= IW_MAX_BITRATES))
+       {
+         printf("%-8.16s  %d available bit-rates :\n",
+                ifname, range.num_bitrates);
+         /* Print them all */
+         for(k = 0; k < range.num_bitrates; k++)
+           {
+             iw_print_bitrate(buffer, sizeof(buffer), range.bitrate[k]);
+             /* Maybe this should be %10s */
+             printf("\t  %s\n", buffer);
+           }
+       }
+      else
+       printf("%-8.16s  unknown bit-rate information.\n", ifname);
+
+      /* Get current bit rate */
+      if(iw_get_ext(skfd, ifname, SIOCGIWRATE, &wrq) >= 0)
+       {
+         iw_print_bitrate(buffer, sizeof(buffer), wrq.u.bitrate.value);
+         printf("          Current Bit Rate%c%s\n",
+                (wrq.u.bitrate.fixed ? '=' : ':'), buffer);
+       }
+
+      /* Try to get the broadcast bitrate if it exist... */
+      if(range.bitrate_capa & IW_BITRATE_BROADCAST)
+       {
+         wrq.u.bitrate.flags = IW_BITRATE_BROADCAST;
+         if(iw_get_ext(skfd, ifname, SIOCGIWRATE, &wrq) >= 0)
+           {
+             iw_print_bitrate(buffer, sizeof(buffer), wrq.u.bitrate.value);
+             printf("          Broadcast Bit Rate%c%s\n",
+                    (wrq.u.bitrate.fixed ? '=' : ':'), buffer);
+           }
+       }
+
+      printf("\n");
+    }
+  return(0);
+}
+
+/************************* ENCRYPTION KEYS *************************/
+
+/*------------------------------------------------------------------*/
+/*
+ * Print all the available encryption keys for the device
+ */
+static int
+print_keys_info(int            skfd,
+               char *          ifname,
+               char *          args[],         /* Command line args */
+               int             count)          /* Args count */
+{
+  struct iwreq         wrq;
+  struct iw_range      range;
+  unsigned char                key[IW_ENCODING_TOKEN_MAX];
+  unsigned int         k;
+  char                 buffer[128];
+
+  /* Avoid "Unused parameter" warning */
+  args = args; count = count;
+
+  /* Extract range info */
+  if(iw_get_range_info(skfd, ifname, &range) < 0)
+      fprintf(stderr, "%-8.16s  no encryption keys information.\n\n",
+                     ifname);
+  else
+    {
+      printf("%-8.16s  ", ifname);
+      /* Print key sizes */
+      if((range.num_encoding_sizes > 0) &&
+        (range.num_encoding_sizes < IW_MAX_ENCODING_SIZES))
+       {
+         printf("%d key sizes : %d", range.num_encoding_sizes,
+                range.encoding_size[0] * 8);
+         /* Print them all */
+         for(k = 1; k < range.num_encoding_sizes; k++)
+           printf(", %d", range.encoding_size[k] * 8);
+         printf("bits\n          ");
+       }
+      /* Print the keys and associate mode */
+      printf("%d keys available :\n", range.max_encoding_tokens);
+      for(k = 1; k <= range.max_encoding_tokens; k++)
+       {
+         wrq.u.data.pointer = (caddr_t) key;
+         wrq.u.data.length = IW_ENCODING_TOKEN_MAX;
+         wrq.u.data.flags = k;
+         if(iw_get_ext(skfd, ifname, SIOCGIWENCODE, &wrq) < 0)
+           {
+             fprintf(stderr, "Error reading wireless keys (SIOCGIWENCODE): %s\n", strerror(errno));
+             break;
+           }
+         if((wrq.u.data.flags & IW_ENCODE_DISABLED) ||
+            (wrq.u.data.length == 0))
+           printf("\t\t[%d]: off\n", k);
+         else
+           {
+             /* Display the key */
+             iw_print_key(buffer, sizeof(buffer),
+                          key, wrq.u.data.length, wrq.u.data.flags);
+             printf("\t\t[%d]: %s", k, buffer);
+
+             /* Other info... */
+             printf(" (%d bits)", wrq.u.data.length * 8);
+             printf("\n");
+           }
+       }
+      /* Print current key index and mode */
+      wrq.u.data.pointer = (caddr_t) key;
+      wrq.u.data.length = IW_ENCODING_TOKEN_MAX;
+      wrq.u.data.flags = 0;    /* Set index to zero to get current */
+      if(iw_get_ext(skfd, ifname, SIOCGIWENCODE, &wrq) >= 0)
+       {
+         /* Note : if above fails, we have already printed an error
+          * message int the loop above */
+         printf("          Current Transmit Key: [%d]\n",
+                wrq.u.data.flags & IW_ENCODE_INDEX);
+         if(wrq.u.data.flags & IW_ENCODE_RESTRICTED)
+           printf("          Security mode:restricted\n");
+         if(wrq.u.data.flags & IW_ENCODE_OPEN)
+           printf("          Security mode:open\n");
+       }
+
+      printf("\n\n");
+    }
+  return(0);
+}
+
+/************************* POWER MANAGEMENT *************************/
+
+/*------------------------------------------------------------------*/
+/*
+ * Print Power Management info for each device
+ */
+static int
+get_pm_value(int               skfd,
+            char *             ifname,
+            struct iwreq *     pwrq,
+            int                flags,
+            char *             buffer,
+            int                buflen,
+            int                we_version_compiled)
+{
+  /* Get Another Power Management value */
+  pwrq->u.power.flags = flags;
+  if(iw_get_ext(skfd, ifname, SIOCGIWPOWER, pwrq) >= 0)
+    {
+      /* Let's check the value and its type */
+      if(pwrq->u.power.flags & IW_POWER_TYPE)
+       {
+         iw_print_pm_value(buffer, buflen,
+                           pwrq->u.power.value, pwrq->u.power.flags,
+                           we_version_compiled);
+         printf("\n                 %s", buffer);
+       }
+    }
+  return(pwrq->u.power.flags);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Print Power Management range for each type
+ */
+static void
+print_pm_value_range(char *            name,
+                    int                mask,
+                    int                iwr_flags,
+                    int                iwr_min,
+                    int                iwr_max,
+                    char *             buffer,
+                    int                buflen,
+                    int                we_version_compiled)
+{
+  if(iwr_flags & mask)
+    {
+      int      flags = (iwr_flags & ~(IW_POWER_MIN | IW_POWER_MAX));
+      /* Display if auto or fixed */
+      printf("%s %s ; ",
+            (iwr_flags & IW_POWER_MIN) ? "Auto " : "Fixed",
+            name);
+      /* Print the range */
+      iw_print_pm_value(buffer, buflen,
+                       iwr_min, flags | IW_POWER_MIN,
+                       we_version_compiled);
+      printf("%s\n                          ", buffer);
+      iw_print_pm_value(buffer, buflen,
+                       iwr_max, flags | IW_POWER_MAX,
+                       we_version_compiled);
+      printf("%s\n          ", buffer);
+    }
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Power Management types of values
+ */
+static const unsigned int pm_type_flags[] = {
+  IW_POWER_PERIOD,
+  IW_POWER_TIMEOUT,
+  IW_POWER_SAVING,
+};
+static const int pm_type_flags_size = (sizeof(pm_type_flags)/sizeof(pm_type_flags[0]));
+
+/*------------------------------------------------------------------*/
+/*
+ * Print Power Management info for each device
+ */
+static int
+print_pm_info(int              skfd,
+             char *            ifname,
+             char *            args[],         /* Command line args */
+             int               count)          /* Args count */
+{
+  struct iwreq         wrq;
+  struct iw_range      range;
+  char                 buffer[128];
+
+  /* Avoid "Unused parameter" warning */
+  args = args; count = count;
+
+  /* Extract range info */
+  if((iw_get_range_info(skfd, ifname, &range) < 0) ||
+     (range.we_version_compiled < 10))
+      fprintf(stderr, "%-8.16s  no power management information.\n\n",
+                     ifname);
+  else
+    {
+      printf("%-8.16s  ", ifname);
+
+      /* Display modes availables */
+      if(range.pm_capa & IW_POWER_MODE)
+       {
+         printf("Supported modes :\n          ");
+         if(range.pm_capa & (IW_POWER_UNICAST_R | IW_POWER_MULTICAST_R))
+           printf("\t\to Receive all packets (unicast & multicast)\n          ");
+         if(range.pm_capa & IW_POWER_UNICAST_R)
+           printf("\t\to Receive Unicast only (discard multicast)\n          ");
+         if(range.pm_capa & IW_POWER_MULTICAST_R)
+           printf("\t\to Receive Multicast only (discard unicast)\n          ");
+         if(range.pm_capa & IW_POWER_FORCE_S)
+           printf("\t\to Force sending using Power Management\n          ");
+         if(range.pm_capa & IW_POWER_REPEATER)
+           printf("\t\to Repeat multicast\n          ");
+       }
+      /* Display min/max period availables */
+      print_pm_value_range("period ", IW_POWER_PERIOD,
+                          range.pmp_flags, range.min_pmp, range.max_pmp,
+                          buffer, sizeof(buffer), range.we_version_compiled);
+      /* Display min/max timeout availables */
+      print_pm_value_range("timeout", IW_POWER_TIMEOUT,
+                          range.pmt_flags, range.min_pmt, range.max_pmt,
+                          buffer, sizeof(buffer), range.we_version_compiled);
+      /* Display min/max saving availables */
+      print_pm_value_range("saving ", IW_POWER_SAVING,
+                          range.pms_flags, range.min_pms, range.max_pms,
+                          buffer, sizeof(buffer), range.we_version_compiled);
+
+      /* Get current Power Management settings */
+      wrq.u.power.flags = 0;
+      if(iw_get_ext(skfd, ifname, SIOCGIWPOWER, &wrq) >= 0)
+       {
+         int   flags = wrq.u.power.flags;
+
+         /* Is it disabled ? */
+         if(wrq.u.power.disabled)
+           printf("Current mode:off\n");
+         else
+           {
+             unsigned int      pm_type = 0;
+             unsigned int      pm_mask = 0;
+             unsigned int      remain_mask = range.pm_capa & IW_POWER_TYPE;
+             int               i = 0;
+
+             /* Let's check the mode */
+             iw_print_pm_mode(buffer, sizeof(buffer), flags);
+             printf("Current %s", buffer);
+
+             /* Let's check if nothing (simply on) */
+             if((flags & IW_POWER_MODE) == IW_POWER_ON)
+               printf("mode:on");
+
+             /* Let's check the value and its type */
+             if(wrq.u.power.flags & IW_POWER_TYPE)
+               {
+                 iw_print_pm_value(buffer, sizeof(buffer),
+                                   wrq.u.power.value, wrq.u.power.flags,
+                                   range.we_version_compiled);
+                 printf("\n                 %s", buffer);
+               }
+
+             while(1)
+               {
+                 /* Deal with min/max for the current value */
+                 pm_mask = 0;
+                 /* If we have been returned a MIN value, ask for the MAX */
+                 if(flags & IW_POWER_MIN)
+                   pm_mask = IW_POWER_MAX;
+                 /* If we have been returned a MAX value, ask for the MIN */
+                 if(flags & IW_POWER_MAX)
+                   pm_mask = IW_POWER_MIN;
+                 /* If we have something to ask for... */
+                 if(pm_mask)
+                   {
+                     pm_mask |= pm_type;
+                     get_pm_value(skfd, ifname, &wrq, pm_mask,
+                                  buffer, sizeof(buffer),
+                                  range.we_version_compiled);
+                   }
+
+                 /* Remove current type from mask */
+                 remain_mask &= ~(wrq.u.power.flags);
+
+                 /* Check what other types we still have to read */
+                 while(i < pm_type_flags_size)
+                   {
+                     pm_type = remain_mask & pm_type_flags[i];
+                     if(pm_type)
+                       break;
+                     i++;
+                   }
+                 /* Nothing anymore : exit the loop */
+                 if(!pm_type)
+                   break;
+
+                 /* Ask for this other type of value */
+                 flags = get_pm_value(skfd, ifname, &wrq, pm_type,
+                                      buffer, sizeof(buffer),
+                                      range.we_version_compiled);
+                 /* Loop back for min/max */
+               }
+             printf("\n");
+           }
+       }
+      printf("\n");
+    }
+  return(0);
+}
+
+#ifndef WE_ESSENTIAL
+/************************** TRANSMIT POWER **************************/
+
+/*------------------------------------------------------------------*/
+/*
+ * Print the number of available transmit powers for the device
+ */
+static int
+print_txpower_info(int         skfd,
+                  char *       ifname,
+                  char *       args[],         /* Command line args */
+                  int          count)          /* Args count */
+{
+  struct iwreq         wrq;
+  struct iw_range      range;
+  int                  dbm;
+  int                  mwatt;
+  int                  k;
+
+  /* Avoid "Unused parameter" warning */
+  args = args; count = count;
+
+  /* Extract range info */
+  if((iw_get_range_info(skfd, ifname, &range) < 0) ||
+     (range.we_version_compiled < 10))
+      fprintf(stderr, "%-8.16s  no transmit-power information.\n\n",
+                     ifname);
+  else
+    {
+      if((range.num_txpower <= 0) || (range.num_txpower > IW_MAX_TXPOWER))
+       printf("%-8.16s  unknown transmit-power information.\n\n", ifname);
+      else
+       {
+         printf("%-8.16s  %d available transmit-powers :\n",
+                ifname, range.num_txpower);
+         /* Print them all */
+         for(k = 0; k < range.num_txpower; k++)
+           {
+             /* Check for relative values */
+             if(range.txpower_capa & IW_TXPOW_RELATIVE)
+               {
+                 printf("\t  %d (no units)\n", range.txpower[k]);
+               }
+             else
+               {
+                 if(range.txpower_capa & IW_TXPOW_MWATT)
+                   {
+                     dbm = iw_mwatt2dbm(range.txpower[k]);
+                     mwatt = range.txpower[k];
+                   }
+                 else
+                   {
+                     dbm = range.txpower[k];
+                     mwatt = iw_dbm2mwatt(range.txpower[k]);
+                   }
+                 printf("\t  %d dBm  \t(%d mW)\n", dbm, mwatt);
+               }
+           }
+       }
+
+      /* Get current Transmit Power */
+      if(iw_get_ext(skfd, ifname, SIOCGIWTXPOW, &wrq) >= 0)
+       {
+         printf("          Current Tx-Power");
+         /* Disabled ? */
+         if(wrq.u.txpower.disabled)
+           printf(":off\n\n");
+         else
+           {
+             /* Fixed ? */
+             if(wrq.u.txpower.fixed)
+               printf("=");
+             else
+               printf(":");
+             /* Check for relative values */
+             if(wrq.u.txpower.flags & IW_TXPOW_RELATIVE)
+               {
+                 /* I just hate relative value, because they are
+                  * driver specific, so not very meaningfull to apps.
+                  * But, we have to support that, because
+                  * this is the way hardware is... */
+                 printf("\t  %d (no units)\n", wrq.u.txpower.value);
+               }
+             else
+               {
+                 if(wrq.u.txpower.flags & IW_TXPOW_MWATT)
+                   {
+                     dbm = iw_mwatt2dbm(wrq.u.txpower.value);
+                     mwatt = wrq.u.txpower.value;
+                   }
+                 else
+                   {
+                     dbm = wrq.u.txpower.value;
+                     mwatt = iw_dbm2mwatt(wrq.u.txpower.value);
+                   }
+                 printf("%d dBm  \t(%d mW)\n\n", dbm, mwatt);
+               }
+           }
+       }
+    }
+  return(0);
+}
+
+/*********************** RETRY LIMIT/LIFETIME ***********************/
+
+/*------------------------------------------------------------------*/
+/*
+ * Print one retry value
+ */
+static int
+get_retry_value(int            skfd,
+               char *          ifname,
+               struct iwreq *  pwrq,
+               int             flags,
+               char *          buffer,
+               int             buflen,
+               int             we_version_compiled)
+{
+  /* Get Another retry value */
+  pwrq->u.retry.flags = flags;
+  if(iw_get_ext(skfd, ifname, SIOCGIWRETRY, pwrq) >= 0)
+    {
+      /* Let's check the value and its type */
+      if(pwrq->u.retry.flags & IW_RETRY_TYPE)
+       {
+         iw_print_retry_value(buffer, buflen,
+                              pwrq->u.retry.value, pwrq->u.retry.flags,
+                              we_version_compiled);
+         printf("%s\n                 ", buffer);
        }
-      else
-       state->val_index++;
-      break;
-    case IWEVQUAL:
-      {
-       iw_print_stats(buffer, sizeof(buffer),
-                      &event->u.qual, iw_range, has_range);
-       printf("                    %s\n", buffer);
-       break;
-      }
-    case IWEVGENIE:
-      /* Informations Elements are complex, let's do only some of them */
-      iw_print_gen_ie(event->u.data.pointer, event->u.data.length);
-      break;
-    case IWEVCUSTOM:
-      {
-       char custom[IW_CUSTOM_MAX+1];
-       if((event->u.data.pointer) && (event->u.data.length))
-         memcpy(custom, event->u.data.pointer, event->u.data.length);
-       custom[event->u.data.length] = '\0';
-       printf("                    Extra:%s\n", custom);
-      }
-      break;
-    default:
-      printf("                    (Unknown Wireless Token 0x%04X)\n",
-            event->cmd);
-   }   /* switch(event->cmd) */
+    }
+  return(pwrq->u.retry.flags);
 }
 
 /*------------------------------------------------------------------*/
 /*
- * Perform a scanning on one device
+ * Print Power Management range for each type
+ */
+static void
+print_retry_value_range(char *         name,
+                       int             mask,
+                       int             iwr_flags,
+                       int             iwr_min,
+                       int             iwr_max,
+                       char *          buffer,
+                       int             buflen,
+                       int             we_version_compiled)
+{
+  if(iwr_flags & mask)
+    {
+      int      flags = (iwr_flags & ~(IW_RETRY_MIN | IW_RETRY_MAX));
+      /* Display if auto or fixed */
+      printf("%s %s ; ",
+            (iwr_flags & IW_POWER_MIN) ? "Auto " : "Fixed",
+            name);
+      /* Print the range */
+      iw_print_retry_value(buffer, buflen,
+                          iwr_min, flags | IW_POWER_MIN,
+                          we_version_compiled);
+      printf("%s\n                           ", buffer);
+      iw_print_retry_value(buffer, buflen,
+                          iwr_max, flags | IW_POWER_MAX,
+                          we_version_compiled);
+      printf("%s\n          ", buffer);
+    }
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Print Retry info for each device
  */
 static int
-print_scanning_info(int                skfd,
-                   char *      ifname,
-                   char *      args[],         /* Command line args */
-                   int         count)          /* Args count */
+print_retry_info(int           skfd,
+                char *         ifname,
+                char *         args[],         /* Command line args */
+                int            count)          /* Args count */
 {
   struct iwreq         wrq;
-  unsigned char *      buffer = NULL;          /* Results */
-  int                  buflen = IW_SCAN_MAX_DATA; /* Min for compat WE<17 */
   struct iw_range      range;
-  int                  has_range;
-  struct timeval       tv;                             /* Select timeout */
-  int                  timeout = 15000000;             /* 15s */
+  char                 buffer[128];
 
   /* Avoid "Unused parameter" warning */
   args = args; count = count;
 
-  /* Get range stuff */
-  has_range = (iw_get_range_info(skfd, ifname, &range) >= 0);
-
-  /* Check if the interface could support scanning. */
-  if((!has_range) || (range.we_version_compiled < 14))
+  /* Extract range info */
+  if((iw_get_range_info(skfd, ifname, &range) < 0) ||
+     (range.we_version_compiled < 11))
+    fprintf(stderr, "%-8.16s  no retry limit/lifetime information.\n\n",
+           ifname);
+  else
     {
-      fprintf(stderr, "%-8.16s  Interface doesn't support scanning.\n\n",
-             ifname);
-      return(-1);
-    }
-
-  /* Init timeout value -> 250ms*/
-  tv.tv_sec = 0;
-  tv.tv_usec = 250000;
+      printf("%-8.16s  ", ifname);
 
-  /*
-   * Here we should look at the command line args and set the IW_SCAN_ flags
-   * properly
-   */
-  wrq.u.data.pointer = NULL;           /* Later */
-  wrq.u.data.flags = 0;
-  wrq.u.data.length = 0;
+      /* Display min/max limit availables */
+      print_retry_value_range("limit   ", IW_RETRY_LIMIT, range.retry_flags,
+                             range.min_retry, range.max_retry,
+                             buffer, sizeof(buffer),
+                             range.we_version_compiled);
+      /* Display min/max lifetime availables */
+      print_retry_value_range("lifetime", IW_RETRY_LIFETIME, 
+                             range.r_time_flags,
+                             range.min_r_time, range.max_r_time,
+                             buffer, sizeof(buffer),
+                             range.we_version_compiled);
 
-  /* Initiate Scanning */
-  if(iw_set_ext(skfd, ifname, SIOCSIWSCAN, &wrq) < 0)
-    {
-      if(errno != EPERM)
+      /* Get current retry settings */
+      wrq.u.retry.flags = 0;
+      if(iw_get_ext(skfd, ifname, SIOCGIWRETRY, &wrq) >= 0)
        {
-         fprintf(stderr, "%-8.16s  Interface doesn't support scanning : %s\n\n",
-                 ifname, strerror(errno));
-         return(-1);
-       }
-      /* If we don't have the permission to initiate the scan, we may
-       * still have permission to read left-over results.
-       * But, don't wait !!! */
-#if 0
-      /* Not cool, it display for non wireless interfaces... */
-      fprintf(stderr, "%-8.16s  (Could not trigger scanning, just reading left-over results)\n", ifname);
-#endif
-      tv.tv_usec = 0;
-    }
-  timeout -= tv.tv_usec;
-
-  /* Forever */
-  while(1)
-    {
-      fd_set           rfds;           /* File descriptors for select */
-      int              last_fd;        /* Last fd */
-      int              ret;
-
-      /* Guess what ? We must re-generate rfds each time */
-      FD_ZERO(&rfds);
-      last_fd = -1;
+         int   flags = wrq.u.retry.flags;
 
-      /* In here, add the rtnetlink fd in the list */
+         /* Is it disabled ? */
+         if(wrq.u.retry.disabled)
+           printf("Current mode:off\n          ");
+         else
+           {
+             unsigned int      retry_type = 0;
+             unsigned int      retry_mask = 0;
+             unsigned int      remain_mask = range.retry_capa & IW_RETRY_TYPE;
 
-      /* Wait until something happens */
-      ret = select(last_fd + 1, &rfds, NULL, NULL, &tv);
+             /* Let's check the mode */
+             printf("Current mode:on\n                 ");
 
-      /* Check if there was an error */
-      if(ret < 0)
-       {
-         if(errno == EAGAIN || errno == EINTR)
-           continue;
-         fprintf(stderr, "Unhandled signal - exiting...\n");
-         return(-1);
-       }
+             /* Let's check the value and its type */
+             if(wrq.u.retry.flags & IW_RETRY_TYPE)
+               {
+                 iw_print_retry_value(buffer, sizeof(buffer),
+                                      wrq.u.retry.value, wrq.u.retry.flags,
+                                      range.we_version_compiled);
+                 printf("%s\n                 ", buffer);
+               }
 
-      /* Check if there was a timeout */
-      if(ret == 0)
-       {
-         unsigned char *       newbuf;
+             while(1)
+               {
+                 /* Deal with min/max/short/long for the current value */
+                 retry_mask = 0;
+                 /* If we have been returned a MIN value, ask for the MAX */
+                 if(flags & IW_RETRY_MIN)
+                   retry_mask = IW_RETRY_MAX;
+                 /* If we have been returned a MAX value, ask for the MIN */
+                 if(flags & IW_RETRY_MAX)
+                   retry_mask = IW_RETRY_MIN;
+                 /* Same for SHORT and LONG */
+                 if(flags & IW_RETRY_SHORT)
+                   retry_mask = IW_RETRY_LONG;
+                 if(flags & IW_RETRY_LONG)
+                   retry_mask = IW_RETRY_SHORT;
+                 /* If we have something to ask for... */
+                 if(retry_mask)
+                   {
+                     retry_mask |= retry_type;
+                     get_retry_value(skfd, ifname, &wrq, retry_mask,
+                                     buffer, sizeof(buffer),
+                                     range.we_version_compiled);
+                   }
 
-       realloc:
-         /* (Re)allocate the buffer - realloc(NULL, len) == malloc(len) */
-         newbuf = realloc(buffer, buflen);
-         if(newbuf == NULL)
-           {
-             if(buffer)
-               free(buffer);
-             fprintf(stderr, "%s: Allocation failed\n", __FUNCTION__);
-             return(-1);
+                 /* And if we have both a limit and a lifetime,
+                  * ask the other one */
+                 remain_mask &= ~(wrq.u.retry.flags);
+                 retry_type = remain_mask;
+                 /* Nothing anymore : exit the loop */
+                 if(!retry_type)
+                   break;
+
+                 /* Ask for this other type of value */
+                 flags = get_retry_value(skfd, ifname, &wrq, retry_type,
+                                         buffer, sizeof(buffer),
+                                         range.we_version_compiled);
+                 /* Loop back for min/max/short/long */
+               }
            }
-         buffer = newbuf;
+       }
+      printf("\n");
+    }
+  return(0);
+}
 
-         /* Try to read the results */
-         wrq.u.data.pointer = buffer;
-         wrq.u.data.flags = 0;
-         wrq.u.data.length = buflen;
-         if(iw_get_ext(skfd, ifname, SIOCGIWSCAN, &wrq) < 0)
-           {
-             /* Check if buffer was too small (WE-17 only) */
-             if((errno == E2BIG) && (range.we_version_compiled > 16))
-               {
-                 /* Some driver may return very large scan results, either
-                  * because there are many cells, or because they have many
-                  * large elements in cells (like IWEVCUSTOM). Most will
-                  * only need the regular sized buffer. We now use a dynamic
-                  * allocation of the buffer to satisfy everybody. Of course,
-                  * as we don't know in advance the size of the array, we try
-                  * various increasing sizes. Jean II */
+/************************ ACCESS POINT LIST ************************/
+/*
+ * Note : now that we have scanning support, this is depracted and
+ * won't survive long. Actually, next version it's out !
+ */
 
-                 /* Check if the driver gave us any hints. */
-                 if(wrq.u.data.length > buflen)
-                   buflen = wrq.u.data.length;
-                 else
-                   buflen *= 2;
+/*------------------------------------------------------------------*/
+/*
+ * Display the list of ap addresses and the associated stats
+ * Exacly the same as the spy list, only with different IOCTL and messages
+ */
+static int
+print_ap_info(int      skfd,
+             char *    ifname,
+             char *    args[],         /* Command line args */
+             int       count)          /* Args count */
+{
+  struct iwreq         wrq;
+  char         buffer[(sizeof(struct iw_quality) +
+                       sizeof(struct sockaddr)) * IW_MAX_AP];
+  char         temp[128];
+  struct sockaddr *    hwa;
+  struct iw_quality *  qual;
+  iwrange      range;
+  int          has_range = 0;
+  int          has_qual = 0;
+  int          n;
+  int          i;
 
-                 /* Try again */
-                 goto realloc;
-               }
+  /* Avoid "Unused parameter" warning */
+  args = args; count = count;
 
-             /* Check if results not available yet */
-             if(errno == EAGAIN)
-               {
-                 /* Restart timer for only 100ms*/
-                 tv.tv_sec = 0;
-                 tv.tv_usec = 100000;
-                 timeout -= tv.tv_usec;
-                 if(timeout > 0)
-                   continue;   /* Try again later */
-               }
+  /* Collect stats */
+  wrq.u.data.pointer = (caddr_t) buffer;
+  wrq.u.data.length = IW_MAX_AP;
+  wrq.u.data.flags = 0;
+  if(iw_get_ext(skfd, ifname, SIOCGIWAPLIST, &wrq) < 0)
+    {
+      fprintf(stderr, "%-8.16s  Interface doesn't have a list of Peers/Access-Points\n\n", ifname);
+      return(-1);
+    }
 
-             /* Bad error */
-             free(buffer);
-             fprintf(stderr, "%-8.16s  Failed to read scan data : %s\n\n",
-                     ifname, strerror(errno));
-             return(-2);
-           }
-         else
-           /* We have the results, go to process them */
-           break;
-       }
+  /* Number of addresses */
+  n = wrq.u.data.length;
+  has_qual = wrq.u.data.flags;
 
-      /* In here, check if event and event type
-       * if scan event, read results. All errors bad & no reset timeout */
+  /* The two lists */
+  hwa = (struct sockaddr *) buffer;
+  qual = (struct iw_quality *) (buffer + (sizeof(struct sockaddr) * n));
+
+  /* Check if we have valid mac address type */
+  if(iw_check_mac_addr_type(skfd, ifname) < 0)
+    {
+      fprintf(stderr, "%-8.16s  Interface doesn't support MAC addresses\n\n", ifname);
+      return(-2);
     }
 
-  if(wrq.u.data.length)
+  /* Get range info if we can */
+  if(iw_get_range_info(skfd, ifname, &(range)) >= 0)
+    has_range = 1;
+
+  /* Display it */
+  if(n == 0)
+    printf("%-8.16s  No Peers/Access-Point in range\n", ifname);
+  else
+    printf("%-8.16s  Peers/Access-Points in range:\n", ifname);
+  for(i = 0; i < n; i++)
     {
-      struct iw_event          iwe;
-      struct stream_descr      stream;
-      struct iwscan_state      state = { .ap_num = 1, .val_index = 0 };
-      int                      ret;
-      
-#if 0
-      /* Debugging code. In theory useless, because it's debugged ;-) */
-      int      i;
-      printf("Scan result %d [%02X", wrq.u.data.length, buffer[0]);
-      for(i = 1; i < wrq.u.data.length; i++)
-       printf(":%02X", buffer[i]);
-      printf("]\n");
-#endif
-      printf("%-8.16s  Scan completed :\n", ifname);
-      iw_init_event_stream(&stream, (char *) buffer, wrq.u.data.length);
-      do
+      if(has_qual)
        {
-         /* Extract an event and print it */
-         ret = iw_extract_event_stream(&stream, &iwe,
-                                       range.we_version_compiled);
-         if(ret > 0)
-           print_scanning_token(&stream, &iwe, &state,
-                                &range, has_range);
+         /* Print stats for this address */
+         printf("    %s : ", iw_saether_ntop(&hwa[i], temp));
+         iw_print_stats(temp, sizeof(buffer), &qual[i], &range, has_range);
+         printf("%s\n", temp);
        }
-      while(ret > 0);
-      printf("\n");
+      else
+       /* Only print the address */
+       printf("    %s\n", iw_saether_ntop(&hwa[i], temp));
     }
-  else
-    printf("%-8.16s  No scan results\n", ifname);
-
-  free(buffer);
+  printf("\n");
   return(0);
 }
 
@@ -1419,7 +1690,7 @@ static const char *       event_capa_evt[] =
 
 /*------------------------------------------------------------------*/
 /*
- * Print the number of available transmit powers for the device
+ * Print the event capability for the device
  */
 static int
 print_event_capa_info(int              skfd,
@@ -1440,7 +1711,7 @@ print_event_capa_info(int         skfd,
                      ifname);
   else
     {
-#if 0
+#ifdef DEBUG
       /* Debugging ;-) */
       for(cmd = 0x8B00; cmd < 0x8C0F; cmd++)
        {
@@ -1473,6 +1744,296 @@ print_event_capa_info(int               skfd,
   return(0);
 }
 
+/*************************** WPA SUPPORT ***************************/
+
+/*------------------------------------------------------------------*/
+/*
+ * Print the authentication parameters for the device
+ */
+static int
+print_auth_info(int            skfd,
+               char *          ifname,
+               char *          args[],         /* Command line args */
+               int             count)          /* Args count */
+{
+  struct iwreq         wrq;
+  struct iw_range      range;
+  unsigned int         k;
+
+  /* Avoid "Unused parameter" warning */
+  args = args; count = count;
+
+  /* Extract range info */
+  if((iw_get_range_info(skfd, ifname, &range) < 0) ||
+     (range.we_version_compiled < 18))
+      fprintf(stderr, "%-8.16s  no authentication information.\n\n",
+                     ifname);
+  else
+    {
+      /* Print WPA/802.1x/802.11i security parameters */
+      if(!range.enc_capa)
+       {
+       printf("%-8.16s  unknown authentication information.\n\n", ifname);
+       }
+      else
+       {
+         /* Display advanced encryption capabilities */
+         printf("%-8.16s  Authentication capabilities :", ifname);
+         iw_print_mask_name(range.enc_capa,
+                            iw_auth_capa_name, IW_AUTH_CAPA_NUM,
+                                "\n\t\t");
+         printf("\n");
+
+         /* Extract all auth settings */
+         for(k = 0; k < IW_AUTH_SETTINGS_NUM; k++)
+           { 
+             wrq.u.param.flags = iw_auth_settings[k].value;
+             if(iw_get_ext(skfd, ifname, SIOCGIWAUTH, &wrq) >= 0)
+               {
+                 printf("          Current %s :", iw_auth_settings[k].label);
+                 if(iw_auth_settings[k].names != NULL)
+                   iw_print_mask_name(wrq.u.param.value,
+                                      iw_auth_settings[k].names,
+                                      iw_auth_settings[k].num_names,
+                                      "\n\t\t");
+                 else
+                   printf((wrq.u.param.value) ? " yes" : " no");
+                 printf("\n");
+               }
+           }
+       }
+
+      printf("\n\n");
+    }
+  return(0);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Print all the available wpa keys for the device
+ */
+static int
+print_wpakeys_info(int         skfd,
+                  char *       ifname,
+                  char *       args[],         /* Command line args */
+                  int          count)          /* Args count */
+{
+  struct iwreq         wrq;
+  struct iw_range      range;
+  unsigned char         extbuf[IW_EXTKEY_SIZE];
+  struct iw_encode_ext  *extinfo;
+  unsigned int         k;
+  char                 buffer[128];
+
+  /* Avoid "Unused parameter" warning */
+  args = args; count = count;
+
+  /* This always point to the same place */
+  extinfo = (struct iw_encode_ext *) extbuf;
+
+  /* Extract range info */
+  if(iw_get_range_info(skfd, ifname, &range) < 0)
+      fprintf(stderr, "%-8.16s  no wpa key information.\n\n",
+                     ifname);
+  else
+    {
+      printf("%-8.16s  ", ifname);
+      /* Print key sizes */
+      if((range.num_encoding_sizes > 0) &&
+        (range.num_encoding_sizes < IW_MAX_ENCODING_SIZES))
+       {
+         printf("%d key sizes : %d", range.num_encoding_sizes,
+                range.encoding_size[0] * 8);
+         /* Print them all */
+         for(k = 1; k < range.num_encoding_sizes; k++)
+           printf(", %d", range.encoding_size[k] * 8);
+         printf("bits\n          ");
+       }
+
+      /* Print the keys */
+      printf("%d keys available :\n", range.max_encoding_tokens);
+      for(k = 1; k <= range.max_encoding_tokens; k++)
+       {
+         /* Cleanup. Driver may not fill everything */
+         memset(extbuf, '\0', IW_EXTKEY_SIZE);
+
+         /* Get whole struct containing one WPA key */
+         wrq.u.data.pointer = (caddr_t) extbuf;
+         wrq.u.data.length = IW_EXTKEY_SIZE;
+