OSDN Git Service

Update to v30-pre9
[android-x86/external-wireless-tools.git] / wireless_tools / iwlist.c
index 0703f3e..90c2a60 100644 (file)
 /*
  *     Wireless Tools
  *
- *             Jean II - HPLB '99 - HPL 99->01
+ *             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-2002 Jean Tourrilhes <jt@hpl.hp.com>
+ *     Copyright (c) 1997-2007 Jean Tourrilhes <jt@hpl.hp.com>
  */
 
-#include "iwlib.h"             /* Header */
+#include "iwlib-private.h"             /* Private header */
 #include <sys/time.h>
 
-/*********************** FREQUENCIES/CHANNELS ***********************/
+/****************************** TYPES ******************************/
+
+/*
+ * Scan state and meta-information, used to decode events...
+ */
+typedef struct iwscan_state
+{
+  /* State */
+  int                  ap_num;         /* Access Point number 1->N */
+  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;
+
+/*
+ * Types of authentication parameters
+ */
+typedef struct iw_auth_descr
+{
+  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;
+
+/**************************** CONSTANTS ****************************/
+
+#define IW_SCAN_HACK           0x8000
+
+#define IW_EXTKEY_SIZE (sizeof(struct iw_encode_ext) + IW_ENCODING_TOKEN_MAX)
+
+/* ------------------------ WPA CAPA NAMES ------------------------ */
+/*
+ * This is the user readable name of a bunch of WPA constants in wireless.h
+ * Maybe this should go in iwlib.c ?
+ */
+
+#ifndef WE_ESSENTIAL
+#define IW_ARRAY_LEN(x) (sizeof(x)/sizeof((x)[0]))
+
+//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)
+
+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)
+
+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)
+
+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)
+
+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)
+
+/* 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)
+
+#endif /* WE_ESSENTIAL */
+
+/************************* WPA SUBROUTINES *************************/
 
+#ifndef WE_ESSENTIAL
 /*------------------------------------------------------------------*/
 /*
- * Print the number of channels and available frequency for the device
+ * Print all names corresponding to a mask.
+ * This may want to be used in iw_print_retry_value() ?
  */
-static int
-print_freq_info(int            skfd,
-               char *          ifname,
-               char *          args[],         /* Command line args */
-               int             count)          /* Args count */
+static void 
+iw_print_mask_name(unsigned int                        mask,
+                  const struct iwmask_name     names[],
+                  const unsigned int           num_names,
+                  const char *                 sep)
 {
-  float                        freq;
-  struct iw_range      range;
-  int                  k;
+  unsigned int i;
 
-  /* Avoid "Unused parameter" warning */
-  args = args; count = count;
+  /* Print out all names for the bitmask */
+  for(i = 0; i < num_names; i++)
+    {
+      if(mask & names[i].mask)
+       {
+         /* Print out */
+         printf("%s%s", sep, names[i].name);
+         /* Remove the bit from the mask */
+         mask &= ~names[i].mask;
+       }
+    }
+  /* If there is unconsumed bits... */
+  if(mask != 0)
+    printf("%sUnknown", sep);
+}
 
-  if(iw_get_range_info(skfd, ifname, &range) < 0)
-      fprintf(stderr, "%-8.8s  no frequency information.\n\n",
-                     ifname);
+/*------------------------------------------------------------------*/
+/*
+ * Print the name corresponding to a value, with overflow check.
+ */
+static void
+iw_print_value_name(unsigned int               value,
+                   const char *                names[],
+                   const unsigned int          num_names)
+{
+  if(value >= num_names)
+    printf(" unknown (%d)", value);
   else
+    printf(" %s", names[value]);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * 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;
+
+  if(ielen > buflen)
+    ielen = buflen;
+
+  printf("Unknown: ");
+  for(i = 0; i < ielen; i++)
+    printf("%02X", iebuf[i]);
+  printf("\n");
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * 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;
+
+#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])
     {
-      if(range.num_frequency > 0)
+    case 0x30:         /* WPA2 */
+      /* Check if we have enough data */
+      if(ielen < 4)
        {
-         printf("%-8.8s  %d channels in total; available frequencies :\n",
-                ifname, range.num_channels);
-         /* Print them all */
-         for(k = 0; k < range.num_frequency; k++)
-           {
-             printf("\t  Channel %.2d : ", range.freq[k].i);
-             freq = iw_freq2float(&(range.freq[k]));
-             if(freq >= GIGA)
-               printf("%g GHz\n", freq / GIGA);
-             else
-               if(freq >= MEGA)
-                 printf("%g MHz\n", freq / MEGA);
-               else
-                 printf("%g kHz\n", freq / KILO);
-           }
-         printf("\n\n");
+         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. Note : offset==2 */
+      if((ielen < 8)
+        || (memcmp(&iebuf[offset], wpa_oui, 3) != 0)
+        || (iebuf[offset + 3] != 0x01))
+       {
+         iw_print_ie_unknown(iebuf, buflen);
+         return;
+       }
+
+      /* Skip the OUI type */
+      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_value_name(iebuf[offset+3],
+                         iw_ie_cypher_name, IW_IE_CYPHER_NUM);
+      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 : 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
-       printf("%-8.8s  %d channels\n\n",
-              ifname, range.num_channels);
+       {
+         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;
+
+  /* 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
+       {
+         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;
+
+  /* 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);
 }
+/*------------------------------------------------------------------*/
+/*
+ * 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;
 
-/************************ ACCESS POINT LIST ************************/
+  /* 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;
+    }
+}
+#endif /* WE_ESSENTIAL */
+
+/***************************** SCANNING *****************************/
+/*
+ * This one behave quite differently from the others
+ *
+ * Note that we don't use the scanning capability of iwlib (functions
+ * iw_process_scan() and iw_scan()). The main reason is that
+ * iw_process_scan() return only a subset of the scan data to the caller,
+ * for example custom elements and bitrates are ommited. Here, we
+ * do the complete job...
+ */
 
 /*------------------------------------------------------------------*/
 /*
- * Display the list of ap addresses and the associated stats
- * Exacly the same as the spy list, only with different IOCTL and messages
+ * 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:
+      /* 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 SIOCGIWNAME:
+      printf("                    Protocol:%-1.16s\n", event->u.name);
+      break;
+    case SIOCGIWESSID:
+      {
+       char essid[4*IW_ESSID_MAX_SIZE+1];
+       memset(essid, '\0', sizeof(essid));
+       if((event->u.essid.pointer) && (event->u.essid.length))
+         iw_essid_escape(essid,
+                         event->u.essid.pointer, event->u.essid.length);
+       if(event->u.essid.flags)
+         {
+           /* Does it have an ESSID index ? */
+           if((event->u.essid.flags & IW_ENCODE_INDEX) > 1)
+             printf("                    ESSID:\"%s\" [%d]\n", essid,
+                    (event->u.essid.flags & IW_ENCODE_INDEX));
+           else
+             printf("                    ESSID:\"%s\"\n", essid);
+         }
+       else
+         printf("                    ESSID:off/any/hidden\n");
+      }
+      break;
+    case SIOCGIWENCODE:
+      {
+       unsigned char   key[IW_ENCODING_TOKEN_MAX];
+       if(event->u.data.pointer)
+         memcpy(key, event->u.data.pointer, event->u.data.length);
+       else
+         event->u.data.flags |= IW_ENCODE_NOKEY;
+       printf("                    Encryption key:");
+       if(event->u.data.flags & IW_ENCODE_DISABLED)
+         printf("off\n");
+       else
+         {
+           /* Display the key */
+           iw_print_key(buffer, sizeof(buffer), key, event->u.data.length,
+                        event->u.data.flags);
+           printf("%s", buffer);
+
+           /* Other info... */
+           if((event->u.data.flags & IW_ENCODE_INDEX) > 1)
+             printf(" [%d]", event->u.data.flags & IW_ENCODE_INDEX);
+           if(event->u.data.flags & IW_ENCODE_RESTRICTED)
+             printf("   Security mode:restricted");
+           if(event->u.data.flags & IW_ENCODE_OPEN)
+             printf("   Security mode:open");
+           printf("\n");
+         }
+      }
+      break;
+    case SIOCGIWRATE:
+      if(state->val_index == 0)
+       printf("                    Bit Rates:");
+      else
+       if((state->val_index % 5) == 0)
+         printf("\n                              ");
+       else
+         printf("; ");
+      iw_print_bitrate(buffer, sizeof(buffer), event->u.bitrate.value);
+      printf("%s", buffer);
+      /* Check for termination */
+      if(stream->value == NULL)
+       {
+         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_ap_info(int      skfd,
-             char *    ifname,
-             char *    args[],         /* Command line args */
-             int       count)          /* Args count */
+print_scanning_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;
+  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;
 
-  /* 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)
+  /* 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, "%-8.8s  Interface doesn't have a list of Access Points\n\n", ifname);
+      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);
     }
 
-  /* Number of addresses */
-  n = wrq.u.data.length;
-  has_qual = wrq.u.data.flags;
+  /* Init timeout value -> 250ms between set and first get */
+  tv.tv_sec = 0;
+  tv.tv_usec = 250000;
 
-  /* The two lists */
-  hwa = (struct sockaddr *) buffer;
-  qual = (struct iw_quality *) (buffer + (sizeof(struct sockaddr) * n));
+  /* Clean up set args */
+  memset(&scanopt, 0, sizeof(scanopt));
 
-  /* Check if we have valid mac address type */
-  if(iw_check_mac_addr_type(skfd, ifname) < 0)
+  /* Parse command line arguments and extract options.
+   * Note : when we have enough options, we should use the parser
+   * from iwconfig... */
+  while(count > 0)
     {
-      fprintf(stderr, "%-8.8s  Interface doesn't support MAC addresses\n\n", ifname);
-      return(-2);
+      /* 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++;
     }
 
-  /* Get range info if we can */
-  if(iw_get_range_info(skfd, ifname, &(range)) >= 0)
-    has_range = 1;
+  /* 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;
+    }
 
-  /* Display it */
-  if(n == 0)
-    printf("%-8.8s  No Access Point in range\n", ifname);
+  /* 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
-    printf("%-8.8s  Access Points in range:\n", ifname);
-  for(i = 0; i < n; i++)
     {
-      if(has_qual)
+      /* Initiate Scanning */
+      if(iw_set_ext(skfd, ifname, SIOCSIWSCAN, &wrq) < 0)
        {
-         /* Print stats for this address */
-         printf("    %s : ", iw_pr_ether(temp, hwa[i].sa_data));
-         iw_print_stats(temp, &qual[i], &range, has_range);
-         printf("%s\n", temp);
+         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)
+                && (buflen < 0xFFFF))
+               {
+                 /* 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;
+
+                 /* wrq.u.data.length is 16 bits so max size is 65535 */
+                 if(buflen > 0xFFFF)
+                   buflen = 0xFFFF;
+
+                 /* 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
-       /* Only print the address */
-       printf("    %s\n", iw_pr_ether(temp, hwa[i].sa_data));
+       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);
+       }
     }
-  printf("\n");
   return(0);
 }
 
@@ -155,6 +957,7 @@ print_bitrate_info(int              skfd,
                   char *       args[],         /* Command line args */
                   int          count)          /* Args count */
 {
+  struct iwreq         wrq;
   struct iw_range      range;
   int                  k;
   char                 buffer[128];
@@ -164,25 +967,46 @@ print_bitrate_info(int            skfd,
 
   /* Extract range info */
   if(iw_get_range_info(skfd, ifname, &range) < 0)
-      fprintf(stderr, "%-8.8s  no bit-rate information.\n\n",
+      fprintf(stderr, "%-8.16s  no bit-rate information.\n\n",
                      ifname);
   else
     {
-      if((range.num_bitrates > 0) && (range.num_bitrates < IW_MAX_BITRATES))
+      if((range.num_bitrates > 0) && (range.num_bitrates <= IW_MAX_BITRATES))
        {
-         printf("%-8.8s  %d available bit-rates :\n",
+         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, range.bitrate[k]);
+             iw_print_bitrate(buffer, sizeof(buffer), range.bitrate[k]);
              /* Maybe this should be %10s */
              printf("\t  %s\n", buffer);
            }
-         printf("\n\n");
        }
       else
-       printf("%-8.8s  No bit-rates ? Please update driver...\n\n", ifname);
+       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);
 }
@@ -191,7 +1015,7 @@ print_bitrate_info(int             skfd,
 
 /*------------------------------------------------------------------*/
 /*
- * Print the number of available encryption key for the device
+ * Print all the available encryption keys for the device
  */
 static int
 print_keys_info(int            skfd,
@@ -202,7 +1026,7 @@ print_keys_info(int                skfd,
   struct iwreq         wrq;
   struct iw_range      range;
   unsigned char                key[IW_ENCODING_TOKEN_MAX];
-  int                  k;
+  unsigned int         k;
   char                 buffer[128];
 
   /* Avoid "Unused parameter" warning */
@@ -210,11 +1034,11 @@ print_keys_info(int              skfd,
 
   /* Extract range info */
   if(iw_get_range_info(skfd, ifname, &range) < 0)
-      fprintf(stderr, "%-8.8s  no encryption keys information.\n\n",
+      fprintf(stderr, "%-8.16s  no encryption keys information.\n\n",
                      ifname);
   else
     {
-      printf("%-8.8s  ", ifname);
+      printf("%-8.16s  ", ifname);
       /* Print key sizes */
       if((range.num_encoding_sizes > 0) &&
         (range.num_encoding_sizes < IW_MAX_ENCODING_SIZES))
@@ -235,7 +1059,7 @@ print_keys_info(int                skfd,
          wrq.u.data.flags = k;
          if(iw_get_ext(skfd, ifname, SIOCGIWENCODE, &wrq) < 0)
            {
-             fprintf(stderr, "SIOCGIWENCODE: %s\n", strerror(errno));
+             fprintf(stderr, "Error reading wireless keys (SIOCGIWENCODE): %s\n", strerror(errno));
              break;
            }
          if((wrq.u.data.flags & IW_ENCODE_DISABLED) ||
@@ -244,7 +1068,8 @@ print_keys_info(int                skfd,
          else
            {
              /* Display the key */
-             iw_print_key(buffer, key, wrq.u.data.length, wrq.u.data.flags);
+             iw_print_key(buffer, sizeof(buffer),
+                          key, wrq.u.data.length, wrq.u.data.flags);
              printf("\t\t[%d]: %s", k, buffer);
 
              /* Other info... */
@@ -252,21 +1077,21 @@ print_keys_info(int              skfd,
              printf("\n");
            }
        }
-      /* Print current key and mode */
+      /* 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)
+      if(iw_get_ext(skfd, ifname, SIOCGIWENCODE, &wrq) >= 0)
        {
-         fprintf(stderr, "SIOCGIWENCODE: %s\n", strerror(errno));
-         return(-1);
+         /* 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("          Current Transmit Key: [%d]\n",
-            wrq.u.data.flags & IW_ENCODE_INDEX);
-      if(wrq.u.data.flags & IW_ENCODE_RESTRICTED)
-       printf("          Encryption mode:restricted\n");
-      if(wrq.u.data.flags & IW_ENCODE_OPEN)
-       printf("          Encryption mode:open\n");
 
       printf("\n\n");
     }
@@ -279,12 +1104,14 @@ print_keys_info(int              skfd,
 /*
  * Print Power Management info for each device
  */
-static inline int
+static int
 get_pm_value(int               skfd,
             char *             ifname,
             struct iwreq *     pwrq,
             int                flags,
-            char *             buffer)
+            char *             buffer,
+            int                buflen,
+            int                we_version_compiled)
 {
   /* Get Another Power Management value */
   pwrq->u.power.flags = flags;
@@ -293,7 +1120,9 @@ get_pm_value(int           skfd,
       /* Let's check the value and its type */
       if(pwrq->u.power.flags & IW_POWER_TYPE)
        {
-         iw_print_pm_value(buffer, pwrq->u.power.value, pwrq->u.power.flags);
+         iw_print_pm_value(buffer, buflen,
+                           pwrq->u.power.value, pwrq->u.power.flags,
+                           we_version_compiled);
          printf("\n                 %s", buffer);
        }
     }
@@ -302,6 +1131,50 @@ get_pm_value(int          skfd,
 
 /*------------------------------------------------------------------*/
 /*
+ * 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
@@ -318,13 +1191,14 @@ print_pm_info(int                skfd,
   args = args; count = count;
 
   /* Extract range info */
-  if(iw_get_range_info(skfd, ifname, &range) < 0)
-      fprintf(stderr, "%-8.8s  no power management information.\n\n",
+  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.8s  ", ifname);
-#if WIRELESS_EXT > 9
+      printf("%-8.16s  ", ifname);
+
       /* Display modes availables */
       if(range.pm_capa & IW_POWER_MODE)
        {
@@ -341,38 +1215,17 @@ print_pm_info(int                skfd,
            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, range.min_pmp, flags | IW_POWER_MIN);
-         printf("%s\n                          ", buffer);
-         iw_print_pm_value(buffer, range.max_pmp, flags | IW_POWER_MAX);
-         printf("%s\n          ", buffer);
-         
-       }
+      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 */
-      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, range.min_pmt, flags | IW_POWER_MIN);
-         printf("%s\n                          ", buffer);
-         iw_print_pm_value(buffer, range.max_pmt, flags | IW_POWER_MAX);
-         printf("%s\n          ", buffer);
-         
-       }
-#endif /* WIRELESS_EXT > 9 */
+      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;
@@ -382,59 +1235,72 @@ print_pm_info(int                skfd,
 
          /* Is it disabled ? */
          if(wrq.u.power.disabled)
-           printf("Current mode:off\n          ");
+           printf("Current mode:off\n");
          else
            {
-             int       pm_mask = 0;
+             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, flags);
+             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");
-             printf("\n                 ");
 
              /* Let's check the value and its type */
              if(wrq.u.power.flags & IW_POWER_TYPE)
                {
-                 iw_print_pm_value(buffer,
-                                   wrq.u.power.value, wrq.u.power.flags);
-                 printf("%s", buffer);
+                 iw_print_pm_value(buffer, sizeof(buffer),
+                                   wrq.u.power.value, wrq.u.power.flags,
+                                   range.we_version_compiled);
+                 printf("\n                 %s", buffer);
                }
 
-             /* 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);
-
-#if WIRELESS_EXT > 9
-             /* 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)
+             while(1)
                {
-                 int   base_mask = pm_mask;
-                 flags = get_pm_value(skfd, ifname, &wrq, pm_mask, buffer);
+                 /* 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 | base_mask;
+                   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 | base_mask;
+                   pm_mask = IW_POWER_MIN;
                  /* If we have something to ask for... */
                  if(pm_mask)
-                   get_pm_value(skfd, ifname, &wrq, pm_mask, buffer);
+                   {
+                     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 */
                }
-#endif /* WIRELESS_EXT > 9 */
+             printf("\n");
            }
        }
       printf("\n");
@@ -442,6 +1308,7 @@ print_pm_info(int          skfd,
   return(0);
 }
 
+#ifndef WE_ESSENTIAL
 /************************** TRANSMIT POWER **************************/
 
 /*------------------------------------------------------------------*/
@@ -454,6 +1321,7 @@ print_txpower_info(int             skfd,
                   char *       args[],         /* Command line args */
                   int          count)          /* Args count */
 {
+  struct iwreq         wrq;
   struct iw_range      range;
   int                  dbm;
   int                  mwatt;
@@ -462,54 +1330,101 @@ print_txpower_info(int          skfd,
   /* Avoid "Unused parameter" warning */
   args = args; count = count;
 
-#if WIRELESS_EXT > 9
   /* Extract range info */
-  if(iw_get_range_info(skfd, ifname, &range) < 0)
-      fprintf(stderr, "%-8.8s  no transmit-power information.\n\n",
+  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))
+      if((range.num_txpower <= 0) || (range.num_txpower > IW_MAX_TXPOWER))
+       printf("%-8.16s  unknown transmit-power information.\n\n", ifname);
+      else
        {
-         printf("%-8.8s  %d available transmit-powers :\n",
+         printf("%-8.16s  %d available transmit-powers :\n",
                 ifname, range.num_txpower);
          /* Print them all */
          for(k = 0; k < range.num_txpower; k++)
            {
-             if(range.txpower_capa & IW_TXPOW_MWATT)
+             /* Check for relative values */
+             if(range.txpower_capa & IW_TXPOW_RELATIVE)
                {
-                 dbm = iw_mwatt2dbm(range.txpower[k]);
-                 mwatt = range.txpower[k];
+                 printf("\t  %d (no units)\n", range.txpower[k]);
                }
              else
                {
-                 dbm = range.txpower[k];
-                 mwatt = iw_dbm2mwatt(range.txpower[k]);
+                 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);
                }
-             printf("\t  %d dBm  \t(%d mW)\n", dbm, mwatt);
            }
-         printf("\n\n");
        }
-      else
-       printf("%-8.8s  No transmit-powers ? Please update driver...\n\n", ifname);
     }
-#endif /* WIRELESS_EXT > 9 */
   return(0);
 }
 
 /*********************** RETRY LIMIT/LIFETIME ***********************/
 
-#if WIRELESS_EXT > 10
 /*------------------------------------------------------------------*/
 /*
  * Print one retry value
  */
-static inline int
+static int
 get_retry_value(int            skfd,
                char *          ifname,
                struct iwreq *  pwrq,
                int             flags,
-               char *          buffer)
+               char *          buffer,
+               int             buflen,
+               int             we_version_compiled)
 {
   /* Get Another retry value */
   pwrq->u.retry.flags = flags;
@@ -518,8 +1433,9 @@ get_retry_value(int                skfd,
       /* Let's check the value and its type */
       if(pwrq->u.retry.flags & IW_RETRY_TYPE)
        {
-         iw_print_retry_value(buffer,
-                              pwrq->u.retry.value, pwrq->u.retry.flags);
+         iw_print_retry_value(buffer, buflen,
+                              pwrq->u.retry.value, pwrq->u.retry.flags,
+                              we_version_compiled);
          printf("%s\n                 ", buffer);
        }
     }
@@ -528,6 +1444,39 @@ get_retry_value(int               skfd,
 
 /*------------------------------------------------------------------*/
 /*
+ * 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
@@ -544,394 +1493,612 @@ print_retry_info(int           skfd,
   args = args; count = count;
 
   /* Extract range info */
-  if(iw_get_range_info(skfd, ifname, &range) < 0)
-      fprintf(stderr, "%-8.8s  no retry limit/lifetime information.\n\n",
-             ifname);
+  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
     {
-      printf("%-8.8s  ", ifname);
+      printf("%-8.16s  ", ifname);
 
       /* Display min/max limit availables */
-      if(range.retry_flags & IW_RETRY_LIMIT)
+      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);
+
+      /* Get current retry settings */
+      wrq.u.retry.flags = 0;
+      if(iw_get_ext(skfd, ifname, SIOCGIWRETRY, &wrq) >= 0)
        {
-         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    ; ");
+         int   flags = wrq.u.retry.flags;
+
+         /* Is it disabled ? */
+         if(wrq.u.retry.disabled)
+           printf("Current mode:off\n          ");
          else
-           printf("Fixed limit    ; ");
-         /* Print the range */
-         iw_print_retry_value(buffer, range.min_retry, flags | IW_RETRY_MIN);
-         printf("%s\n                           ", buffer);
-         iw_print_retry_value(buffer, range.max_retry, flags | IW_RETRY_MAX);
-         printf("%s\n          ", buffer);
-         
+           {
+             unsigned int      retry_type = 0;
+             unsigned int      retry_mask = 0;
+             unsigned int      remain_mask = range.retry_capa & IW_RETRY_TYPE;
+
+             /* 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,
+                                      range.we_version_compiled);
+                 printf("%s\n                 ", buffer);
+               }
+
+             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);
+                   }
+
+                 /* 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 */
+               }
+           }
        }
-      /* Display min/max lifetime availables */
-      if(range.r_time_flags & IW_RETRY_LIFETIME)
+      printf("\n");
+    }
+  return(0);
+}
+
+/************************ ACCESS POINT LIST ************************/
+/*
+ * Note : now that we have scanning support, this is depracted and
+ * won't survive long. Actually, next version it's out !
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * 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;
+
+  /* Avoid "Unused parameter" warning */
+  args = args; count = count;
+
+  /* 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);
+    }
+
+  /* Number of addresses */
+  n = wrq.u.data.length;
+  has_qual = wrq.u.data.flags;
+
+  /* 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);
+    }
+
+  /* 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++)
+    {
+      if(has_qual)
+       {
+         /* 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);
+       }
+      else
+       /* Only print the address */
+       printf("    %s\n", iw_saether_ntop(&hwa[i], temp));
+    }
+  printf("\n");
+  return(0);
+}
+
+/******************** WIRELESS EVENT CAPABILITY ********************/
+
+static const char *    event_capa_req[] =
+{
+  [SIOCSIWNWID - SIOCIWFIRST] = "Set NWID (kernel generated)",
+  [SIOCSIWFREQ - SIOCIWFIRST] = "Set Frequency/Channel (kernel generated)",
+  [SIOCGIWFREQ - SIOCIWFIRST] = "New Frequency/Channel",
+  [SIOCSIWMODE - SIOCIWFIRST] = "Set Mode (kernel generated)",
+  [SIOCGIWTHRSPY - SIOCIWFIRST] = "Spy threshold crossed",
+  [SIOCGIWAP   - SIOCIWFIRST] = "New Access Point/Cell address - roaming",
+  [SIOCGIWSCAN - SIOCIWFIRST] = "Scan request completed",
+  [SIOCSIWESSID        - SIOCIWFIRST] = "Set ESSID (kernel generated)",
+  [SIOCGIWESSID        - SIOCIWFIRST] = "New ESSID",
+  [SIOCGIWRATE - SIOCIWFIRST] = "New bit-rate",
+  [SIOCSIWENCODE - SIOCIWFIRST] = "Set Encoding (kernel generated)",
+  [SIOCGIWPOWER        - SIOCIWFIRST] = NULL,
+};
+
+static const char *    event_capa_evt[] =
+{
+  [IWEVTXDROP  - IWEVFIRST] = "Tx packet dropped - retry exceeded",
+  [IWEVCUSTOM  - IWEVFIRST] = "Custom driver event",
+  [IWEVREGISTERED - IWEVFIRST] = "Registered node",
+  [IWEVEXPIRED - IWEVFIRST] = "Expired node",
+};
+
+static const struct iwmask_name iw_scan_capa_name[] = {
+  { IW_SCAN_CAPA_ESSID,                "ESSID" },
+  { IW_SCAN_CAPA_BSSID,                "BSSID" },
+  { IW_SCAN_CAPA_CHANNEL,      "Channel" },
+  { IW_SCAN_CAPA_MODE,         "Mode" },
+  { IW_SCAN_CAPA_RATE,         "Rate" },
+  { IW_SCAN_CAPA_TYPE,         "Type" },
+  { IW_SCAN_CAPA_TIME,         "Time" },
+};
+#define        IW_SCAN_CAPA_NUM        IW_ARRAY_LEN(iw_scan_capa_name)
+
+/*------------------------------------------------------------------*/
+/*
+ * Print the event capability for the device
+ */
+static int
+print_event_capa_info(int              skfd,
+                     char *            ifname,
+                     char *            args[],         /* Command line args */
+                     int               count)          /* Args count */
+{
+  struct iw_range      range;
+  int                  cmd;
+
+  /* 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 wireless event capability information.\n\n",
+                     ifname);
+  else
+    {
+#ifdef DEBUG
+      /* Debugging ;-) */
+      for(cmd = 0x8B00; cmd < 0x8C0F; cmd++)
+       {
+         int idx = IW_EVENT_CAPA_INDEX(cmd);
+         int mask = IW_EVENT_CAPA_MASK(cmd);
+         printf("0x%X - %d - %X\n", cmd, idx, mask);
+       }
+#endif
+
+      printf("%-8.16s  Wireless Events supported :\n", ifname);
+
+      for(cmd = SIOCIWFIRST; cmd <= SIOCGIWPOWER; cmd++)
+       {
+         int idx = IW_EVENT_CAPA_INDEX(cmd);
+         int mask = IW_EVENT_CAPA_MASK(cmd);
+         if(range.event_capa[idx] & mask)
+           printf("          0x%04X : %s\n",
+                  cmd, event_capa_req[cmd - SIOCIWFIRST]);
+       }
+      for(cmd = IWEVFIRST; cmd <= IWEVEXPIRED; cmd++)
+       {
+         int idx = IW_EVENT_CAPA_INDEX(cmd);
+         int mask = IW_EVENT_CAPA_MASK(cmd);
+         if(range.event_capa[idx] & mask)
+           printf("          0x%04X : %s\n",
+                  cmd, event_capa_evt[cmd - IWEVFIRST]);
+       }
+      printf("\n");
+
+      /* Add Scanning Capacity as a bonus, if available */
+      if(range.scan_capa != 0)
+       {
+         printf("%-8.16s  Scanning capabilities :", ifname);
+         iw_print_mask_name(range.scan_capa,
+                            iw_scan_capa_name, IW_SCAN_CAPA_NUM,
+                            "\n\t\t- ");
+         printf("\n\n");
+       }
+    }
+  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))
        {
-         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, range.min_r_time, flags | IW_RETRY_MIN);
-         printf("%s\n                           ", buffer);
-         iw_print_retry_value(buffer, range.max_r_time, flags | IW_RETRY_MAX);
-         printf("%s\n          ", buffer);
-         
+         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          ");
        }
 
-      /* Get current retry settings */
-      wrq.u.retry.flags = 0;
-      if(iw_get_ext(skfd, ifname, SIOCGIWRETRY, &wrq) >= 0)
+      /* Print the keys */
+      printf("%d keys available :\n", range.max_encoding_tokens);
+      for(k = 1; k <= range.max_encoding_tokens; k++)
        {
-         int   flags = wrq.u.retry.flags;
+         /* Cleanup. Driver may not fill everything */
+         memset(extbuf, '\0', IW_EXTKEY_SIZE);
 
-         /* Is it disabled ? */
-         if(wrq.u.retry.disabled)
-           printf("Current mode:off\n          ");
-         else
+         /* Get whole struct containing one WPA key */
+         wrq.u.data.pointer = (caddr_t) extbuf;
+         wrq.u.data.length = IW_EXTKEY_SIZE;
+         wrq.u.data.flags = k;
+         if(iw_get_ext(skfd, ifname, SIOCGIWENCODEEXT, &wrq) < 0)
            {
-             int       retry_mask = 0;
+             fprintf(stderr, "Error reading wpa keys (SIOCGIWENCODEEXT): %s\n", strerror(errno));
+             break;
+           }
 
-             /* Let's check the mode */
-             printf("Current mode:on\n                 ");
+         /* Sanity check */
+         if(wrq.u.data.length < 
+            (sizeof(struct iw_encode_ext) + extinfo->key_len))
+           break;
 
-             /* Let's check the value and its type */
-             if(wrq.u.retry.flags & IW_RETRY_TYPE)
-               {
-                 iw_print_retry_value(buffer,
-                                      wrq.u.retry.value, wrq.u.retry.flags);
-                 printf("%s", buffer);
-               }
+         /* Check if key is disabled */
+         if((wrq.u.data.flags & IW_ENCODE_DISABLED) ||
+            (extinfo->key_len == 0))
+           printf("\t\t[%d]: off\n", k);
+         else
+           {
+             /* Display the key */
+             iw_print_key(buffer, sizeof(buffer),
+                          extinfo->key, extinfo->key_len, wrq.u.data.flags);
+             printf("\t\t[%d]: %s", k, 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);
-
-             /* 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);
-                 retry_mask = 0;
+             /* Key size */
+             printf(" (%d bits)", extinfo->key_len * 8);
+             printf("\n");
 
-                 /* 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);
-               }
+             /* Other info... */
+             printf("\t\t     Address: %s\n",
+                    iw_saether_ntop(&extinfo->addr, buffer));
+
+             printf("\t\t     Algorithm:");
+             iw_print_value_name(extinfo->alg,
+                                 iw_encode_alg_name, IW_ENCODE_ALG_NUM);
+
+             printf("\n\t\t     Flags: 0x%08x\n", extinfo->ext_flags);
+             if (extinfo->ext_flags & IW_ENCODE_EXT_TX_SEQ_VALID)
+               printf("\t\t        tx-seq-valid\n");
+             if (extinfo->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID)
+               printf("\t\t        rx-seq-valid\n");
+             if (extinfo->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
+               printf("\t\t        group-key\n");
            }
        }
-      printf("\n");
+      /* Print current key index and mode */
+      wrq.u.data.pointer = (caddr_t) extbuf;
+      wrq.u.data.length = IW_EXTKEY_SIZE;
+      wrq.u.data.flags = 0;    /* Set index to zero to get current */
+      if(iw_get_ext(skfd, ifname, SIOCGIWENCODEEXT, &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);
 }
 
-#endif /* WIRELESS_EXT > 10 */
-
-/***************************** SCANNING *****************************/
-/*
- * This one behave quite differently from the others
- */
-#if WIRELESS_EXT > 13
 /*------------------------------------------------------------------*/
 /*
- * Print one element from the scanning results
+ * Print the Generic IE for the device
+ * Note : indentation is broken. We need to fix that.
  */
-static inline int
-print_scanning_token(struct iw_event * event,  /* Extracted token */
-                    int                ap_num, /* AP number */
-                    struct iw_range *  iwrange,        /* Range info */
-                    int                has_range)
+static int
+print_gen_ie_info(int          skfd,
+                 char *        ifname,
+                 char *        args[],         /* Command line args */
+                 int           count)          /* Args count */
 {
-  char         buffer[128];    /* Temporary buffer */
+  struct iwreq         wrq;
+  unsigned char         buf[IW_GENERIC_IE_MAX];
 
-  /* Now, let's decode the event */
-  switch(event->cmd)
-    {
-    case SIOCGIWAP:
-      printf("          Cell %02d - Address: %s\n", ap_num,
-            iw_pr_ether(buffer, event->u.ap_addr.sa_data));
-      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:
-      {
-       float           freq;                   /* Frequency/channel */
-       freq = iw_freq2float(&(event->u.freq));
-       iw_print_freq(buffer, freq);
-       printf("                    %s\n", buffer);
-      }
-      break;
-    case SIOCGIWMODE:
-      printf("                    Mode:%s\n",
-            iw_operation_mode[event->u.mode]);
-      break;
-    case SIOCGIWESSID:
-      {
-       char essid[IW_ESSID_MAX_SIZE+1];
-       if((event->u.essid.pointer) && (event->u.essid.length))
-         memcpy(essid, event->u.essid.pointer, event->u.essid.length);
-       essid[event->u.essid.length] = '\0';
-       if(event->u.essid.flags)
-         {
-           /* Does it have an ESSID index ? */
-           if((event->u.essid.flags & IW_ENCODE_INDEX) > 1)
-             printf("                    ESSID:\"%s\" [%d]\n", essid,
-                    (event->u.essid.flags & IW_ENCODE_INDEX));
-           else
-             printf("                    ESSID:\"%s\"\n", essid);
-         }
-       else
-         printf("                    ESSID:off/any\n");
-      }
-      break;
-    case SIOCGIWENCODE:
-      {
-       unsigned char   key[IW_ENCODING_TOKEN_MAX];
-       if(event->u.data.pointer)
-         memcpy(key, event->u.essid.pointer, event->u.data.length);
-       else
-         event->u.data.flags |= IW_ENCODE_NOKEY;
-       printf("                    Encryption key:");
-       if(event->u.data.flags & IW_ENCODE_DISABLED)
-         printf("off\n");
-       else
-         {
-           /* Display the key */
-           iw_print_key(buffer, key, event->u.data.length,
-                        event->u.data.flags);
-           printf("%s", buffer);
+  /* Avoid "Unused parameter" warning */
+  args = args; count = count;
 
-           /* Other info... */
-           if((event->u.data.flags & IW_ENCODE_INDEX) > 1)
-             printf(" [%d]", event->u.data.flags & IW_ENCODE_INDEX);
-           if(event->u.data.flags & IW_ENCODE_RESTRICTED)
-             printf("   Encryption mode:restricted");
-           if(event->u.data.flags & IW_ENCODE_OPEN)
-             printf("   Encryption mode:open");
-           printf("\n");
-         }
-      }
-      break;
-    case SIOCGIWRATE:
-      iw_print_bitrate(buffer, event->u.bitrate.value);
-      printf("                    Bit Rate:%s\n", buffer);
-      break;
-    case IWEVQUAL:
-      {
-       event->u.qual.updated = 0x0;    /* Not that reliable, disable */
-       iw_print_stats(buffer, &event->u.qual, iwrange, has_range);
-       printf("                    %s\n", buffer);
-       break;
-      }
-    default:
-      printf("                    (Unknown Wireless Token 0x%04X)\n",
-            event->cmd);
-   }   /* switch(event->cmd) */
+  wrq.u.data.pointer = (caddr_t)buf;
+  wrq.u.data.length = IW_GENERIC_IE_MAX;
+  wrq.u.data.flags = 0;
 
-  /* May have changed */
-  return(ap_num);
+  if(iw_get_ext(skfd, ifname, SIOCGIWGENIE, &wrq) < 0)
+    fprintf(stderr, "%-8.16s  no generic IE (%s).\n\n",
+           ifname, strerror(errno));
+  else
+    {
+      fprintf(stderr, "%-8.16s\n", ifname);
+      if(wrq.u.data.length == 0)
+       printf("          empty generic IE\n");
+      else
+       iw_print_gen_ie(buf, wrq.u.data.length);
+      printf("\n");
+    }
+  return(0);
 }
 
+/**************************** MODULATION ****************************/
+
 /*------------------------------------------------------------------*/
 /*
- * Perform a scanning on one device
+ * Print Modulation info for each device
  */
 static int
-print_scanning_info(int                skfd,
-                   char *      ifname,
-                   char *      args[],         /* Command line args */
-                   int         count)          /* Args count */
+print_modul_info(int           skfd,
+                char *         ifname,
+                char *         args[],         /* Command line args */
+                int            count)          /* Args count */
 {
   struct iwreq         wrq;
-  unsigned char                buffer[IW_SCAN_MAX_DATA];       /* Results */
-  struct timeval       tv;                             /* Select timeout */
-  int                  timeout = 5000000;              /* 5s */
+  struct iw_range      range;
 
   /* Avoid "Unused parameter" warning */
   args = args; count = count;
 
-  /* Init timeout value -> 250ms*/
-  tv.tv_sec = 0;
-  tv.tv_usec = 250000;
-
-  /*
-   * Here we should look at the command line args and set the IW_SCAN_ flags
-   * properly
-   */
-  wrq.u.param.flags = IW_SCAN_DEFAULT;
-  wrq.u.param.value = 0;               /* Later */
-
-  /* Initiate Scanning */
-  if(iw_set_ext(skfd, ifname, SIOCSIWSCAN, &wrq) < 0)
+  /* Extract range info */
+  if((iw_get_range_info(skfd, ifname, &range) < 0) ||
+     (range.we_version_compiled < 11))
+    fprintf(stderr, "%-8.16s  no modulation information.\n\n",
+           ifname);
+  else
     {
-      if(errno != EPERM)
+      if(range.modul_capa == 0x0)
+       printf("%-8.16s  unknown modulation information.\n\n", ifname);
+      else
        {
-         fprintf(stderr, "%-8.8s  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.8s  (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;
+         int i;
+         printf("%-8.16s  Modulations available :\n", ifname);
 
-      /* Guess what ? We must re-generate rfds each time */
-      FD_ZERO(&rfds);
-      last_fd = -1;
-
-      /* In here, add the rtnetlink fd in the list */
+         /* Display each modulation available */
+         for(i = 0; i < IW_SIZE_MODUL_LIST; i++)
+           {
+             if((range.modul_capa & iw_modul_list[i].mask)
+                == iw_modul_list[i].mask)
+               printf("              %-8s: %s\n",
+                      iw_modul_list[i].cmd, iw_modul_list[i].verbose);
+           }
 
-      /* Wait until something happens */
-      ret = select(last_fd + 1, &rfds, NULL, NULL, &tv);
+         /* Get current modulations settings */
+         wrq.u.param.flags = 0;
+         if(iw_get_ext(skfd, ifname, SIOCGIWMODUL, &wrq) >= 0)
+           {
+             unsigned int      modul = wrq.u.param.value;
+             int               n = 0;
 
-      /* Check if there was an error */
-      if(ret < 0)
-       {
-         if(errno == EAGAIN || errno == EINTR)
-           continue;
-         fprintf(stderr, "Unhandled signal - exiting...\n");
-         return(-1);
-       }
+             printf("          Current modulations %c",
+                    wrq.u.param.fixed ? '=' : ':');
 
-      /* Check if there was a timeout */
-      if(ret == 0)
-       {
-         /* Try to read the results */
-         wrq.u.data.pointer = buffer;
-         wrq.u.data.flags = 0;
-         wrq.u.data.length = sizeof(buffer);
-         if(iw_get_ext(skfd, ifname, SIOCGIWSCAN, &wrq) < 0)
-           {
-             /* Check if results not available yet */
-             if(errno == EAGAIN)
+             /* Display each modulation enabled */
+             for(i = 0; i < IW_SIZE_MODUL_LIST; i++)
                {
-                 /* Restart timer for only 100ms*/
-                 tv.tv_sec = 0;
-                 tv.tv_usec = 100000;
-                 timeout -= tv.tv_usec;
-                 if(timeout > 0)
-                   continue;   /* Try again later */
+                 if((modul & iw_modul_list[i].mask) == iw_modul_list[i].mask)
+                   {
+                     if((n++ % 8) == 0)
+                       printf("\n              ");
+                     else
+                       printf(" ; ");
+                     printf("%s", iw_modul_list[i].cmd);
+                   }
                }
 
-             /* Bad error */
-             fprintf(stderr, "%-8.8s  Failed to read scan data : %s\n\n",
-                     ifname, strerror(errno));
-             return(-2);
+             printf("\n");
            }
-         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;
-      int                      ap_num = 1;
-      int                      ret;
-      struct iw_range          range;
-      int                      has_range;
-#if 0
-      /* Debugging code. In theory useless, because it's debugged ;-) */
-      int      i;
-      printf("Scan result [%02X", buffer[0]);
-      for(i = 1; i < wrq.u.data.length; i++)
-       printf(":%02X", buffer[i]);
-      printf("]\n");
-#endif
-      has_range = (iw_get_range_info(skfd, ifname, &range) >= 0);
-      printf("%-8.8s  Scan completed :\n", ifname);
-      iw_init_event_stream(&stream, buffer, wrq.u.data.length);
-      do
-       {
-         /* Extract an event and print it */
-         ret = iw_extract_event_stream(&stream, &iwe);
-         if(ret > 0)
-           ap_num = print_scanning_token(&iwe, ap_num, &range, has_range);
+         printf("\n");
        }
-      while(ret > 0);
-      printf("\n");
     }
-  else
-    printf("%-8.8s  No scan results\n", ifname);
-
   return(0);
 }
-#endif /* WIRELESS_EXT > 13 */
+#endif /* WE_ESSENTIAL */
 
 /************************* COMMON UTILITIES *************************/
 /*
- * This section was written by Michael Tokarev <mjt@tls.msk.ru>
- * But modified by me ;-)
+ * This section was initially written by Michael Tokarev <mjt@tls.msk.ru>
+ * but heavily modified by me ;-)
  */
 
-/* command list */
+/*------------------------------------------------------------------*/
+/*
+ * Map command line arguments to the proper procedure...
+ */
 typedef struct iwlist_entry {
-  const char *cmd;
-  iw_enum_handler fn;
-  int min_count;
-  int max_count;
+  const char *         cmd;            /* Command line shorthand */
+  iw_enum_handler      fn;             /* Subroutine */
+  int                  max_count;
+  const char *         argsname;       /* Args as human readable string */
 } iwlist_cmd;
 
 static const struct iwlist_entry iwlist_cmds[] = {
-  { "frequency",       print_freq_info,        0, 0 },
-  { "channel",         print_freq_info,        0, 0 },
-  { "ap",              print_ap_info,          0, 0 },
-  { "accesspoints",    print_ap_info,          0, 0 },
-  { "bitrate",         print_bitrate_info,     0, 0 },
-  { "rate",            print_bitrate_info,     0, 0 },
-  { "encryption",      print_keys_info,        0, 0 },
-  { "key",             print_keys_info,        0, 0 },
-  { "power",           print_pm_info,          0, 0 },
-  { "txpower",         print_txpower_info,     0, 0 },
-#if WIRELESS_EXT > 10
-  { "retry",           print_retry_info,       0, 0 },
-#endif
-#if WIRELESS_EXT > 13
-  { "scanning",                print_scanning_info,    0, 5 },
-#endif
+  { "scanning",                print_scanning_info,    -1, "[essid NNN] [last]" },
+  { "frequency",       print_freq_info,        0, NULL },
+  { "channel",         print_freq_info,        0, NULL },
+  { "bitrate",         print_bitrate_info,     0, NULL },
+  { "rate",            print_bitrate_info,     0, NULL },
+  { "encryption",      print_keys_info,        0, NULL },
+  { "keys",            print_keys_info,        0, NULL },
+  { "power",           print_pm_info,          0, NULL },
+#ifndef WE_ESSENTIAL
+  { "txpower",         print_txpower_info,     0, NULL },
+  { "retry",           print_retry_info,       0, NULL },
+  { "ap",              print_ap_info,          0, NULL },
+  { "accesspoints",    print_ap_info,          0, NULL },
+  { "peers",           print_ap_info,          0, NULL },
+  { "event",           print_event_capa_info,  0, NULL },
+  { "auth",            print_auth_info,        0, NULL },
+  { "wpakeys",         print_wpakeys_info,     0, NULL },
+  { "genie",           print_gen_ie_info,      0, NULL },
+  { "modulation",      print_modul_info,       0, NULL },
+#endif /* WE_ESSENTIAL */
   { NULL, NULL, 0, 0 },
 };
 
@@ -970,13 +2137,13 @@ find_command(const char *        cmd)
 
   if(found == NULL)
     {
-      fprintf(stderr, "iwlist: unknown command `%s'\n", cmd);
+      fprintf(stderr, "iwlist: unknown command `%s' (check 'iwlist --help').\n", cmd);
       return NULL;
     }
 
   if(ambig)
     {
-      fprintf(stderr, "iwlist: command `%s' is ambiguous\n", cmd);
+      fprintf(stderr, "iwlist: command `%s' is ambiguous (check 'iwlist --help').\n", cmd);
       return NULL;
     }
 
@@ -989,12 +2156,17 @@ find_command(const char *        cmd)
  */
 static void iw_usage(int status)
 {
-  FILE* f = status ? stderr : stdout;
-  int i;
+  FILE *               f = status ? stderr : stdout;
+  int                  i;
+
+  for(i = 0; iwlist_cmds[i].cmd != NULL; ++i)
+    {
+      fprintf(f, "%s [interface] %s %s\n",
+             (i ? "             " : "Usage: iwlist"),
+             iwlist_cmds[i].cmd,
+             iwlist_cmds[i].argsname ? iwlist_cmds[i].argsname : "");
+    }
 
-  fprintf(f,   "Usage: iwlist [interface] %s\n", iwlist_cmds[0].cmd);
-  for(i = 1; iwlist_cmds[i].cmd != NULL; ++i)
-    fprintf(f, "              [interface] %s\n", iwlist_cmds[i].cmd);
   exit(status);
 }
 
@@ -1014,14 +2186,18 @@ main(int        argc,
   char **args;                 /* Command arguments */
   int count;                   /* Number of arguments */
   const iwlist_cmd *iwcmd;
+  int goterr = 0;
 
-  if(argc == 1 || argc > 3)
+  if(argc < 2)
     iw_usage(1);
 
-  if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
+  /* Those don't apply to all interfaces */
+  if((argc == 2) && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")))
     iw_usage(0);
+  if((argc == 2) && (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version")))
+    return(iw_print_version_info("iwlist"));
 
-  if (argc == 2)
+  if(argc == 2)
     {
       cmd = argv[1];
       dev = NULL;
@@ -1042,14 +2218,10 @@ main(int        argc,
     return 1;
 
   /* Check arg numbers */
-  if(count < iwcmd->min_count)
-    {
-      fprintf(stderr, "iwlist: command `%s' needs more arguments\n", cmd);
-      return 1;
-    }
-  if(count > iwcmd->max_count)
+  if((iwcmd->max_count >= 0) && (count > iwcmd->max_count))
     {
-      fprintf(stderr, "iwlist: command `%s' needs fewer arguments\n", cmd);
+      fprintf(stderr, "iwlist: command `%s' needs fewer arguments (max %d)\n",
+             iwcmd->cmd, iwcmd->max_count);
       return 1;
     }
 
@@ -1062,12 +2234,12 @@ main(int        argc,
 
   /* do the actual work */
   if (dev)
-    (*iwcmd->fn)(skfd, dev, args, count);
+    goterr = (*iwcmd->fn)(skfd, dev, args, count);
   else
     iw_enum_devices(skfd, iwcmd->fn, args, count);
 
   /* Close the socket. */
-  close(skfd);
+  iw_sockets_close(skfd);
 
-  return 0;
+  return goterr;
 }