OSDN Git Service

64c2d78569cd8c314cf872775aee340e90c8b160
[android-x86/external-wireless-tools.git] / wireless_tools / iwlib.c
1 /*
2  *      Wireless Tools
3  *
4  *              Jean II - HPLB 97->99 - HPL 99->01
5  *
6  * Common subroutines to all the wireless tools...
7  *
8  * This file is released under the GPL license.
9  *     Copyright (c) 1997-2002 Jean Tourrilhes <jt@hpl.hp.com>
10  */
11
12 /***************************** INCLUDES *****************************/
13
14 #include "iwlib.h"              /* Header */
15
16 /************************ CONSTANTS & MACROS ************************/
17
18 /* Various versions information */
19 /* Recommended Wireless Extension version */
20 #define WE_VERSION      15
21 /* Version of Wireless Tools */
22 #define WT_VERSION      25
23
24 /*
25  * Verify a few things about Wireless Extensions.
26  * I try to maximise backward and forward compatibility, but things are
27  * tricky because I'm fixing bugs and adding new features.
28  * Wireless Tools *must* be compiled with the same version of WE
29  * as the driver. Sometime, the size or layout of some structure changes,
30  * and might produce interesting results.
31  * Wireless Tools will usually compile properly against different
32  * versions of WE, thanks to the zillions of #ifdefs in my code.
33  * Jean II
34  */
35 #if WIRELESS_EXT < 9
36 #error "Wireless Extension v9 or newer required :-("
37 #error "Use Wireless Tools v19 or update your kernel headers !"
38 #endif
39 #if WIRELESS_EXT < WE_VERSION && !defined(WEXT_HEADER)
40 #warning "Wireless Extension earlier than v15 detected,"
41 #warning "Not all tools features will be compiled in !"
42 #warning "No worry, I'll try to make the best of it ;-)"
43 #endif
44 #if WIRELESS_EXT > WE_VERSION && !defined(WEXT_HEADER)
45 #warning "Wireless Extension later than v15 detected,"
46 #warning "Maybe you should get a more recent version"
47 #warning "of the Wireless Tools package !"
48 #endif
49
50 /**************************** VARIABLES ****************************/
51
52 const char * const iw_operation_mode[] = { "Auto",
53                                         "Ad-Hoc",
54                                         "Managed",
55                                         "Master",
56                                         "Repeater",
57                                         "Secondary",
58                                         "Monitor" };
59
60 /* Disable runtime version warning in iw_get_range_info() */
61 int     iw_ignore_version = 0;
62
63 /************************ SOCKET SUBROUTINES *************************/
64
65 /*------------------------------------------------------------------*/
66 /*
67  * Open a socket.
68  * Depending on the protocol present, open the right socket. The socket
69  * will allow us to talk to the driver.
70  */
71 int
72 iw_sockets_open(void)
73 {
74   static const int families[] = {
75     AF_INET, AF_IPX, AF_AX25, AF_APPLETALK
76   };
77   unsigned int  i;
78   int           sock;
79
80   /*
81    * Now pick any (exisiting) useful socket family for generic queries
82    * Note : don't open all the socket, only returns when one matches,
83    * all protocols might not be valid.
84    * Workaround by Jim Kaba <jkaba@sarnoff.com>
85    * Note : in 99% of the case, we will just open the inet_sock.
86    * The remaining 1% case are not fully correct...
87    */
88
89   /* Try all families we support */
90   for(i = 0; i < sizeof(families)/sizeof(int); ++i)
91     {
92       /* Try to open the socket, if success returns it */
93       sock = socket(families[i], SOCK_DGRAM, 0);
94       if(sock >= 0)
95         return sock;
96   }
97
98   return -1;
99 }
100
101 /*------------------------------------------------------------------*/
102 /*
103  * Extract the interface name out of /proc/net/wireless or /proc/net/dev.
104  */
105 static inline char *
106 iw_get_ifname(char *    name,   /* Where to store the name */
107               int       nsize,  /* Size of name buffer */
108               char *    buf)    /* Current position in buffer */
109 {
110   char *        end;
111
112   /* Skip leading spaces */
113   while(isspace(*buf))
114     buf++;
115
116 #ifndef IW_RESTRIC_ENUM
117   /* Get name up to the last ':'. Aliases may contain ':' in them,
118    * but the last one should be the separator */
119   end = strrchr(buf, ':');
120 #else
121   /* Get name up to ": "
122    * Note : we compare to ": " to make sure to process aliased interfaces
123    * properly. Doesn't work on /proc/net/dev, because it doesn't guarantee
124    * a ' ' after the ':'*/
125   end = strstr(buf, ": ");
126 #endif
127
128   /* Not found ??? To big ??? */
129   if((end == NULL) || (((end - buf) + 1) > nsize))
130     return(NULL);
131
132   /* Copy */
133   memcpy(name, buf, (end - buf));
134   name[end - buf] = '\0';
135
136   return(end + 2);
137 }
138
139 /*------------------------------------------------------------------*/
140 /*
141  * Enumerate devices and call specified routine
142  * The new way just use /proc/net/wireless, so get all wireless interfaces,
143  * whether configured or not. This is the default if available.
144  * The old way use SIOCGIFCONF, so get only configured interfaces (wireless
145  * or not).
146  */
147 void
148 iw_enum_devices(int             skfd,
149                 iw_enum_handler fn,
150                 char *          args[],
151                 int             count)
152 {
153   char          buff[1024];
154   FILE *        fh;
155   struct ifconf ifc;
156   struct ifreq *ifr;
157   int           i;
158
159 #ifndef IW_RESTRIC_ENUM
160   /* Check if /proc/net/wireless is available */
161   fh = fopen(PROC_NET_DEV, "r");
162 #else
163   /* Check if /proc/net/wireless is available */
164   fh = fopen(PROC_NET_WIRELESS, "r");
165 #endif
166
167   if(fh != NULL)
168     {
169       /* Success : use data from /proc/net/wireless */
170
171       /* Eat 2 lines of header */
172       fgets(buff, sizeof(buff), fh);
173       fgets(buff, sizeof(buff), fh);
174
175       /* Read each device line */
176       while(fgets(buff, sizeof(buff), fh))
177         {
178           char  name[IFNAMSIZ + 1];
179           char *s;
180
181           /* Extract interface name */
182           s = iw_get_ifname(name, sizeof(name), buff);
183
184           if(!s)
185             /* Failed to parse, complain and continue */
186             fprintf(stderr, "Cannot parse " PROC_NET_WIRELESS "\n");
187           else
188             /* Got it, print info about this interface */
189             (*fn)(skfd, name, args, count);
190         }
191
192       fclose(fh);
193     }
194   else
195     {
196       /* Get list of configured devices using "traditional" way */
197       ifc.ifc_len = sizeof(buff);
198       ifc.ifc_buf = buff;
199       if(ioctl(skfd, SIOCGIFCONF, &ifc) < 0)
200         {
201           fprintf(stderr, "SIOCGIFCONF: %s\n", strerror(errno));
202           return;
203         }
204       ifr = ifc.ifc_req;
205
206       /* Print them */
207       for(i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++)
208         (*fn)(skfd, ifr->ifr_name, args, count);
209     }
210 }
211
212 /*********************** WIRELESS SUBROUTINES ************************/
213
214 /*------------------------------------------------------------------*/
215 /*
216  * Get the range information out of the driver
217  */
218 int
219 iw_get_range_info(int           skfd,
220                   char *        ifname,
221                   iwrange *     range)
222 {
223   struct iwreq          wrq;
224   char                  buffer[sizeof(iwrange) * 2];    /* Large enough */
225
226   /* Cleanup */
227   memset(buffer, 0, sizeof(buffer));
228
229   wrq.u.data.pointer = (caddr_t) buffer;
230   wrq.u.data.length = sizeof(buffer);
231   wrq.u.data.flags = 0;
232   if(iw_get_ext(skfd, ifname, SIOCGIWRANGE, &wrq) < 0)
233     return(-1);
234
235   /* Copy stuff at the right place, ignore extra */
236   memcpy((char *) range, buffer, sizeof(iwrange));
237
238   /* Lots of people have driver and tools out of sync as far as Wireless
239    * Extensions are concerned. It's because /usr/include/linux/wireless.h
240    * and /usr/src/linux/include/linux/wireless.h are different.
241    * We try to catch this stuff here... */
242   if(!iw_ignore_version)
243     {
244       /* For new versions, we can check the version directly, for old versions
245        * we use magic. 300 bytes is a also magic number, don't touch... */
246       if((WIRELESS_EXT > 10) && (wrq.u.data.length >= 300))
247         {
248 #if WIRELESS_EXT > 10
249           /* Version verification - for new versions */
250           if(range->we_version_compiled != WIRELESS_EXT)
251             {
252               fprintf(stderr, "Warning: Driver for device %s has been compiled with version %d\n", ifname, range->we_version_compiled);
253               fprintf(stderr, "of Wireless Extension, while this program is using version %d.\n", WIRELESS_EXT);
254               fprintf(stderr, "Some things may be broken...\n\n");
255             }
256           /* Driver version verification */
257           if(range->we_version_compiled < range->we_version_source)
258             {
259               fprintf(stderr, "Warning: Driver for device %s recommend version %d of Wireless Extension,\n", ifname, range->we_version_source);
260               fprintf(stderr, "but has been compiled with version %d, therefore some driver features\n", range->we_version_compiled);
261               fprintf(stderr, "may not be available...\n\n");
262             }
263 #endif /* WIRELESS_EXT > 10 */
264         }
265       else
266         {
267           /* Version verification - for old versions */
268           if(wrq.u.data.length != sizeof(iwrange))
269             {
270               fprintf(stderr, "Warning: Driver for device %s has been compiled with an ancient version\n", ifname);
271               fprintf(stderr, "of Wireless Extension, while this program is using version %d.\n", WIRELESS_EXT);
272               fprintf(stderr, "Some things may be broken...\n\n");
273             }
274         }
275     }
276   /* Don't complain twice.
277    * In theory, the test apply to each individual driver, but usually
278    * all drivers are compiled from the same kernel, and most often
279    * problem is the system/glibc headers. */
280   iw_ignore_version = 1;
281
282   /* Note : we are only trying to catch compile difference, not source.
283    * If the driver source has not been updated to the latest, it doesn't
284    * matter because the new fields are set to zero */
285
286   return(0);
287 }
288
289 /*------------------------------------------------------------------*/
290 /*
291  * Print the WE versions of the interface.
292  */
293 static int
294 print_iface_version_info(int    skfd,
295                          char * ifname,
296                          char * args[],         /* Command line args */
297                          int    count)          /* Args count */
298 {
299   struct iwreq          wrq;
300   char                  buffer[sizeof(iwrange) * 2];    /* Large enough */
301   struct iw_range *     range;
302
303   /* Avoid "Unused parameter" warning */
304   args = args; count = count;
305
306   /* Cleanup */
307   memset(buffer, 0, sizeof(buffer));
308
309   wrq.u.data.pointer = (caddr_t) buffer;
310   wrq.u.data.length = sizeof(buffer);
311   wrq.u.data.flags = 0;
312   if(iw_get_ext(skfd, ifname, SIOCGIWRANGE, &wrq) < 0)
313     return(-1);
314
315   /* Copy stuff at the right place, ignore extra */
316   range = (struct iw_range *) buffer;
317
318   /* For new versions, we can check the version directly, for old versions
319    * we use magic. 300 bytes is a also magic number, don't touch... */
320   if((WIRELESS_EXT > 10) && (wrq.u.data.length >= 300))
321     {
322 #if WIRELESS_EXT > 10
323       printf("%-8.8s  Recommend Wireless Extension v%d or later,\n",
324              ifname, range->we_version_source);
325       printf("          Currently compiled with Wireless Extension v%d.\n\n",
326              range->we_version_compiled);
327 #endif /* WIRELESS_EXT > 10 */
328     }
329   else
330     {
331 #if 0
332       fprintf(stderr, "%-8.8s  no Wireless Extension version information.\n\n",
333                       ifname);
334 #endif
335     }
336
337
338   return(0);
339 }
340
341 /*------------------------------------------------------------------*/
342 /*
343  * Print the WE versions of the tools.
344  */
345 int
346 iw_print_version_info(char *    toolname)
347 {
348   int skfd;                     /* generic raw socket desc.     */
349
350   /* Create a channel to the NET kernel. */
351   if((skfd = iw_sockets_open()) < 0)
352     {
353       perror("socket");
354       return -1;
355     }
356
357   /* Information about the tools themselves */
358   if(toolname != NULL)
359     printf("%-8.8s  Version %d\n", toolname, WT_VERSION);
360   printf("          Compatible with Wireless Extension v%d or earlier,\n",
361          WE_VERSION);
362   printf("          Currently compiled with Wireless Extension v%d.\n\n",
363          WIRELESS_EXT);
364
365   /* Version for each device */
366   iw_enum_devices(skfd, &print_iface_version_info, NULL, 0);
367
368   close(skfd);
369
370   return 0;
371 }
372
373 /*------------------------------------------------------------------*/
374 /*
375  * Get information about what private ioctls are supported by the driver
376  */
377 int
378 iw_get_priv_info(int            skfd,
379                  char *         ifname,
380                  iwprivargs *   priv,
381                  int            maxpriv)
382 {
383   struct iwreq          wrq;
384
385   /* Ask the driver */
386   wrq.u.data.pointer = (caddr_t) priv;
387   wrq.u.data.length = maxpriv;
388   wrq.u.data.flags = 0;
389   if(iw_get_ext(skfd, ifname, SIOCGIWPRIV, &wrq) < 0)
390     return(-1);
391
392   /* Return the number of ioctls */
393   return(wrq.u.data.length);
394 }
395
396 /*------------------------------------------------------------------*/
397 /*
398  * Get essential wireless config from the device driver
399  * We will call all the classical wireless ioctl on the driver through
400  * the socket to know what is supported and to get the settings...
401  * Note : compare to the version in iwconfig, we extract only
402  * what's *really* needed to configure a device...
403  */
404 int
405 iw_get_basic_config(int                 skfd,
406                     char *              ifname,
407                     wireless_config *   info)
408 {
409   struct iwreq          wrq;
410
411   memset((char *) info, 0, sizeof(struct wireless_config));
412
413   /* Get wireless name */
414   if(iw_get_ext(skfd, ifname, SIOCGIWNAME, &wrq) < 0)
415     /* If no wireless name : no wireless extensions */
416     return(-1);
417   else
418     {
419       strncpy(info->name, wrq.u.name, IFNAMSIZ);
420       info->name[IFNAMSIZ] = '\0';
421     }
422
423   /* Get network ID */
424   if(iw_get_ext(skfd, ifname, SIOCGIWNWID, &wrq) >= 0)
425     {
426       info->has_nwid = 1;
427       memcpy(&(info->nwid), &(wrq.u.nwid), sizeof(iwparam));
428     }
429
430   /* Get frequency / channel */
431   if(iw_get_ext(skfd, ifname, SIOCGIWFREQ, &wrq) >= 0)
432     {
433       info->has_freq = 1;
434       info->freq = iw_freq2float(&(wrq.u.freq));
435     }
436
437   /* Get encryption information */
438   wrq.u.data.pointer = (caddr_t) info->key;
439   wrq.u.data.length = IW_ENCODING_TOKEN_MAX;
440   wrq.u.data.flags = 0;
441   if(iw_get_ext(skfd, ifname, SIOCGIWENCODE, &wrq) >= 0)
442     {
443       info->has_key = 1;
444       info->key_size = wrq.u.data.length;
445       info->key_flags = wrq.u.data.flags;
446     }
447
448   /* Get ESSID */
449   wrq.u.essid.pointer = (caddr_t) info->essid;
450   wrq.u.essid.length = IW_ESSID_MAX_SIZE + 1;
451   wrq.u.essid.flags = 0;
452   if(iw_get_ext(skfd, ifname, SIOCGIWESSID, &wrq) >= 0)
453     {
454       info->has_essid = 1;
455       info->essid_on = wrq.u.data.flags;
456     }
457
458   /* Get operation mode */
459   if(iw_get_ext(skfd, ifname, SIOCGIWMODE, &wrq) >= 0)
460     {
461       info->mode = wrq.u.mode;
462       if((info->mode < 6) && (info->mode >= 0))
463         info->has_mode = 1;
464     }
465
466   return(0);
467 }
468
469 /*------------------------------------------------------------------*/
470 /*
471  * Set essential wireless config in the device driver
472  * We will call all the classical wireless ioctl on the driver through
473  * the socket to know what is supported and to set the settings...
474  * We support only the restricted set as above...
475  */
476 int
477 iw_set_basic_config(int                 skfd,
478                     char *              ifname,
479                     wireless_config *   info)
480 {
481   struct iwreq          wrq;
482   int                   ret = 0;
483
484   /* Get wireless name (check if interface is valid) */
485   if(iw_get_ext(skfd, ifname, SIOCGIWNAME, &wrq) < 0)
486     /* If no wireless name : no wireless extensions */
487     return(-2);
488
489   /* Set Network ID, if available (this is for non-802.11 cards) */
490   if(info->has_nwid)
491     {
492       memcpy(&(wrq.u.nwid), &(info->nwid), sizeof(iwparam));
493       wrq.u.nwid.fixed = 1;     /* Hum... When in Rome... */
494
495       if(iw_set_ext(skfd, ifname, SIOCSIWNWID, &wrq) < 0)
496         {
497           fprintf(stderr, "SIOCSIWNWID: %s\n", strerror(errno));
498           ret = -1;
499         }
500     }
501
502   /* Set frequency / channel */
503   if(info->has_freq)
504     {
505       iw_float2freq(info->freq, &(wrq.u.freq));
506
507       if(iw_set_ext(skfd, ifname, SIOCSIWFREQ, &wrq) < 0)
508         {
509           fprintf(stderr, "SIOCSIWFREQ: %s\n", strerror(errno));
510           ret = -1;
511         }
512     }
513
514   /* Set encryption information */
515   if(info->has_key)
516     {
517       int               flags = info->key_flags;
518
519       /* Check if there is a key index */
520       if((flags & IW_ENCODE_INDEX) > 0)
521         {
522           /* Set the index */
523           wrq.u.data.pointer = (caddr_t) NULL;
524           wrq.u.data.flags = (flags & (IW_ENCODE_INDEX)) | IW_ENCODE_NOKEY;
525           wrq.u.data.length = 0;
526
527           if(iw_set_ext(skfd, ifname, SIOCSIWENCODE, &wrq) < 0)
528             {
529               fprintf(stderr, "SIOCSIWENCODE(%d): %s\n",
530                       errno, strerror(errno));
531               ret = -1;
532             }
533         }
534
535       /* Mask out index to minimise probability of reject when setting key */
536       flags = flags & (~IW_ENCODE_INDEX);
537
538       /* Set the key itself (set current key in this case) */
539       wrq.u.data.pointer = (caddr_t) info->key;
540       wrq.u.data.length = info->key_size;
541       wrq.u.data.flags = flags;
542
543       if(iw_set_ext(skfd, ifname, SIOCSIWENCODE, &wrq) < 0)
544         {
545           fprintf(stderr, "SIOCSIWENCODE(%d): %s\n",
546                   errno, strerror(errno));
547           ret = -1;
548         }
549     }
550
551   /* Set ESSID (extended network), if available */
552   if(info->has_essid)
553     {
554       wrq.u.essid.pointer = (caddr_t) info->essid;
555       wrq.u.essid.length = strlen(info->essid) + 1;
556       wrq.u.data.flags = info->essid_on;
557
558       if(iw_set_ext(skfd, ifname, SIOCSIWESSID, &wrq) < 0)
559         {
560           fprintf(stderr, "SIOCSIWESSID: %s\n", strerror(errno));
561           ret = -1;
562         }
563     }
564
565   /* Set the current mode of operation */
566   if(info->has_mode)
567     {
568       strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
569       wrq.u.mode = info->mode;
570
571       if(iw_get_ext(skfd, ifname, SIOCSIWMODE, &wrq) < 0)
572         {
573           fprintf(stderr, "SIOCSIWMODE: %s\n", strerror(errno));
574           ret = -1;
575         }
576     }
577
578   return(ret);
579 }
580
581 /*********************** PROTOCOL SUBROUTINES ***********************/
582 /*
583  * Fun stuff with protocol identifiers (SIOCGIWNAME).
584  * We assume that drivers are returning sensible values in there,
585  * which is not always the case :-(
586  */
587
588 /*------------------------------------------------------------------*/
589 /*
590  * Compare protocol identifiers.
591  * We don't want to know if the two protocols are the exactly same,
592  * but if they interoperate at some level, and also if they accept the
593  * same type of config (ESSID vs NWID, freq...).
594  * This is supposed to work around the alphabet soup.
595  * Return 1 if protocols are compatible
596  */
597 int
598 iw_protocol_compare(char *      protocol1,
599                     char *      protocol2)
600 {
601   char *        dot11 = "IEEE 802.11";
602   char *        dot11_ds = "Dbg";
603
604   /* If the strings are the same -> easy */
605   if(!strncmp(protocol1, protocol2, IFNAMSIZ))
606     return(1);
607
608   /* Are we dealing with one of the 802.11 variant ? */
609   if( (!strncmp(protocol1, dot11, strlen(dot11))) &&
610       (!strncmp(protocol2, dot11, strlen(dot11))) )
611     {
612       char *    sub1 = protocol1 + strlen(dot11);
613       char *    sub2 = protocol2 + strlen(dot11);
614
615       /* Skip optional separator */
616       if(*sub1 == '-')
617         sub1++;
618       if(*sub2 == '-')
619         sub2++;
620
621       /* Check if they are both 2.4 GHz Direct Sequence compatible */
622       if( (strchr(dot11_ds, *sub1) != NULL) &&
623           (strchr(dot11_ds, *sub2) != NULL) )
624         return(1);
625     }
626   /* Not compatible */
627   return(0);
628 }
629
630 /********************** FREQUENCY SUBROUTINES ***********************/
631 /*
632  * Note : the two functions below are the cause of troubles on
633  * various embeeded platforms, as they are the reason we require
634  * libm (math library).
635  * In this case, please use enable BUILD_NOLIBM in the makefile
636  *
637  * FIXME : check negative mantissa and exponent
638  */
639
640 /*------------------------------------------------------------------*/
641 /*
642  * Convert a floating point the our internal representation of
643  * frequencies.
644  * The kernel doesn't want to hear about floating point, so we use
645  * this custom format instead.
646  */
647 void
648 iw_float2freq(double    in,
649               iwfreq *  out)
650 {
651 #ifdef WE_NOLIBM
652   /* Version without libm : slower */
653   out->e = 0;
654   while(in > 1e9)
655     {
656       in /= 10;
657       out->e++;
658     }
659   out->m = (long) in;
660 #else   /* WE_NOLIBM */
661   /* Version with libm : faster */
662   out->e = (short) (floor(log10(in)));
663   if(out->e > 8)
664     {
665       out->m = ((long) (floor(in / pow(10,out->e - 6)))) * 100;
666       out->e -= 8;
667     }
668   else
669     {
670       out->m = (long) in;
671       out->e = 0;
672     }
673 #endif  /* WE_NOLIBM */
674 }
675
676 /*------------------------------------------------------------------*/
677 /*
678  * Convert our internal representation of frequencies to a floating point.
679  */
680 double
681 iw_freq2float(iwfreq *  in)
682 {
683 #ifdef WE_NOLIBM
684   /* Version without libm : slower */
685   int           i;
686   double        res = (double) in->m;
687   for(i = 0; i < in->e; i++)
688     res *= 10;
689   return(res);
690 #else   /* WE_NOLIBM */
691   /* Version with libm : faster */
692   return ((double) in->m) * pow(10,in->e);
693 #endif  /* WE_NOLIBM */
694 }
695
696 /*------------------------------------------------------------------*/
697 /*
698  * Output a frequency with proper scaling
699  */
700 void
701 iw_print_freq(char *    buffer,
702               double    freq)
703 {
704   if(freq < KILO)
705     sprintf(buffer, "Channel:%g", freq);
706   else
707     {
708       if(freq >= GIGA)
709         sprintf(buffer, "Frequency:%gGHz", freq / GIGA);
710       else
711         {
712           if(freq >= MEGA)
713             sprintf(buffer, "Frequency:%gMHz", freq / MEGA);
714           else
715             sprintf(buffer, "Frequency:%gkHz", freq / KILO);
716         }
717     }
718 }
719
720 /*------------------------------------------------------------------*/
721 /*
722  * Convert a frequency to a channel (negative -> error)
723  */
724 int
725 iw_freq_to_channel(double               freq,
726                    struct iw_range *    range)
727 {
728   double        ref_freq;
729   int           k;
730
731   /* Check if it's a frequency or not already a channel */
732   if(freq < KILO)
733     return(-1);
734
735   /* We compare the frequencies as double to ignore differences
736    * in encoding. Slower, but safer... */
737   for(k = 0; k < range->num_frequency; k++)
738     {
739       ref_freq = iw_freq2float(&(range->freq[k]));
740       if(freq == ref_freq)
741         return(range->freq[k].i);
742     }
743   /* Not found */
744   return(-2);
745 }
746
747 /*********************** BITRATE SUBROUTINES ***********************/
748
749 /*------------------------------------------------------------------*/
750 /*
751  * Output a bitrate with proper scaling
752  */
753 void
754 iw_print_bitrate(char * buffer,
755                  int    bitrate)
756 {
757   double        rate = bitrate;
758
759   if(rate >= GIGA)
760     sprintf(buffer, "%gGb/s", rate / GIGA);
761   else
762     if(rate >= MEGA)
763       sprintf(buffer, "%gMb/s", rate / MEGA);
764     else
765       sprintf(buffer, "%gkb/s", rate / KILO);
766 }
767
768 /************************ POWER SUBROUTINES *************************/
769
770 /*------------------------------------------------------------------*/
771 /*
772  * Convert a value in dBm to a value in milliWatt.
773  */
774 int
775 iw_dbm2mwatt(int        in)
776 {
777   return((int) (floor(pow(10.0, (((double) in) / 10.0)))));
778 }
779
780 /*------------------------------------------------------------------*/
781 /*
782  * Convert a value in milliWatt to a value in dBm.
783  */
784 int
785 iw_mwatt2dbm(int        in)
786 {
787   return((int) (ceil(10.0 * log10((double) in))));
788 }
789
790 /********************** STATISTICS SUBROUTINES **********************/
791
792 /*------------------------------------------------------------------*/
793 /*
794  * Read /proc/net/wireless to get the latest statistics
795  */
796 int
797 iw_get_stats(int        skfd,
798              char *     ifname,
799              iwstats *  stats)
800 {
801 #if WIRELESS_EXT > 11
802   struct iwreq          wrq;
803   wrq.u.data.pointer = (caddr_t) stats;
804   wrq.u.data.length = 0;
805   wrq.u.data.flags = 1;         /* Clear updated flag */
806   strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
807   if(iw_get_ext(skfd, ifname, SIOCGIWSTATS, &wrq) < 0)
808     return(-1);
809
810   return(0);
811 #else /* WIRELESS_EXT > 11 */
812   FILE *        f = fopen(PROC_NET_WIRELESS, "r");
813   char          buf[256];
814   char *        bp;
815   int           t;
816   if(f==NULL)
817     return -1;
818   /* Loop on all devices */
819   while(fgets(buf,255,f))
820     {
821       bp=buf;
822       while(*bp&&isspace(*bp))
823         bp++;
824       /* Is it the good device ? */
825       if(strncmp(bp,ifname,strlen(ifname))==0 && bp[strlen(ifname)]==':')
826         {
827           /* Skip ethX: */
828           bp=strchr(bp,':');
829           bp++;
830           /* -- status -- */
831           bp = strtok(bp, " ");
832           sscanf(bp, "%X", &t);
833           stats->status = (unsigned short) t;
834           /* -- link quality -- */
835           bp = strtok(NULL, " ");
836           if(strchr(bp,'.') != NULL)
837             stats->qual.updated |= 1;
838           sscanf(bp, "%d", &t);
839           stats->qual.qual = (unsigned char) t;
840           /* -- signal level -- */
841           bp = strtok(NULL, " ");
842           if(strchr(bp,'.') != NULL)
843             stats->qual.updated |= 2;
844           sscanf(bp, "%d", &t);
845           stats->qual.level = (unsigned char) t;
846           /* -- noise level -- */
847           bp = strtok(NULL, " ");
848           if(strchr(bp,'.') != NULL)
849             stats->qual.updated += 4;
850           sscanf(bp, "%d", &t);
851           stats->qual.noise = (unsigned char) t;
852           /* -- discarded packets -- */
853           bp = strtok(NULL, " ");
854           sscanf(bp, "%d", &stats->discard.nwid);
855           bp = strtok(NULL, " ");
856           sscanf(bp, "%d", &stats->discard.code);
857           bp = strtok(NULL, " ");
858           sscanf(bp, "%d", &stats->discard.misc);
859           fclose(f);
860           return 0;
861         }
862     }
863   fclose(f);
864   return -1;
865 #endif /* WIRELESS_EXT > 11 */
866 }
867
868 /*------------------------------------------------------------------*/
869 /*
870  * Output the link statistics, taking care of formating
871  */
872 void
873 iw_print_stats(char *           buffer,
874                iwqual *         qual,
875                iwrange *        range,
876                int              has_range)
877 {
878   /* Just do it */
879   if(has_range && (qual->level != 0))
880     {
881       /* If the statistics are in dBm */
882       if(qual->level > range->max_qual.level)
883         {
884           /* Statistics are in dBm (absolute power measurement) */
885           sprintf(buffer,
886                   "Quality:%d/%d  Signal level:%d dBm  Noise level:%d dBm%s",
887                   qual->qual, range->max_qual.qual,
888                   qual->level - 0x100, qual->noise - 0x100,
889                   (qual->updated & 0x7) ? " (updated)" : "");
890         }
891       else
892         {
893           /* Statistics are relative values (0 -> max) */
894           sprintf(buffer,
895                   "Quality:%d/%d  Signal level:%d/%d  Noise level:%d/%d%s",
896                   qual->qual, range->max_qual.qual,
897                   qual->level, range->max_qual.level,
898                   qual->noise, range->max_qual.noise,
899                   (qual->updated & 0x7) ? " (updated)" : "");
900         }
901     }
902   else
903     {
904       /* We can't read the range, so we don't know... */
905       sprintf(buffer, "Quality:%d  Signal level:%d  Noise level:%d%s",
906               qual->qual, qual->level, qual->noise,
907               (qual->updated & 0x7) ? " (updated)" : "");
908     }
909 }
910
911 /*********************** ENCODING SUBROUTINES ***********************/
912
913 /*------------------------------------------------------------------*/
914 /*
915  * Output the encoding key, with a nice formating
916  */
917 void
918 iw_print_key(char *             buffer,
919              unsigned char *    key,
920              int                key_size,
921              int                key_flags)
922 {
923   int   i;
924
925   /* Is the key present ??? */
926   if(key_flags & IW_ENCODE_NOKEY)
927     {
928       /* Nope : print on or dummy */
929       if(key_size <= 0)
930         strcpy(buffer, "on");
931       else
932         {
933           strcpy(buffer, "**");
934           buffer +=2;
935           for(i = 1; i < key_size; i++)
936             {
937               if((i & 0x1) == 0)
938                 strcpy(buffer++, "-");
939               strcpy(buffer, "**");
940               buffer +=2;
941             }
942         }
943     }
944   else
945     {
946       /* Yes : print the key */
947       sprintf(buffer, "%.2X", key[0]);
948       buffer +=2;
949       for(i = 1; i < key_size; i++)
950         {
951           if((i & 0x1) == 0)
952             strcpy(buffer++, "-");
953           sprintf(buffer, "%.2X", key[i]);
954           buffer +=2;
955         }
956     }
957 }
958
959 /*------------------------------------------------------------------*/
960 /*
961  * Convert a passphrase into a key
962  * ### NOT IMPLEMENTED ###
963  * Return size of the key, or 0 (no key) or -1 (error)
964  */
965 int
966 iw_pass_key(char *              input,
967             unsigned char *     key)
968 {
969   input = input; key = key;
970   fprintf(stderr, "Error: Passphrase not implemented\n");
971   return(-1);
972 }
973
974 /*------------------------------------------------------------------*/
975 /*
976  * Parse a key from the command line.
977  * Return size of the key, or 0 (no key) or -1 (error)
978  */
979 int
980 iw_in_key(char *                input,
981           unsigned char *       key)
982 {
983   int           keylen = 0;
984   char *        buff;
985   char *        p;
986   int           temp;
987
988   /* Check the type of key */
989   if(!strncmp(input, "s:", 2))
990     {
991       /* First case : as an ASCII string (Lucent/Agere cards) */
992       keylen = strlen(input + 2);               /* skip "s:" */
993       if(keylen > IW_ENCODING_TOKEN_MAX)
994         keylen = IW_ENCODING_TOKEN_MAX;
995       strncpy(key, input + 2, keylen);
996     }
997   else
998     if(!strncmp(input, "p:", 2))
999       {
1000         /* Second case : as a passphrase (PrismII cards) */
1001         return(iw_pass_key(input + 2, key));            /* skip "p:" */
1002       }
1003     else
1004       {
1005         /* Third case : as hexadecimal digits */
1006         buff = malloc(strlen(input) + 1);
1007         if(buff == NULL)
1008           {
1009             fprintf(stderr, "Malloc failed (string too long ?)\n");
1010             return(-1);
1011           }
1012         /* Preserve original buffer */
1013         strcpy(buff, input);
1014
1015         /* Parse */
1016         p = strtok(buff, "-:;.,");
1017         while((p != (char *) NULL) && (keylen < IW_ENCODING_TOKEN_MAX))
1018           {
1019             if(sscanf(p, "%2X", &temp) != 1)
1020               return(-1);               /* Error */
1021             key[keylen++] = (unsigned char) (temp & 0xFF);
1022             if(strlen(p) > 2)   /* Token not finished yet */
1023               p += 2;
1024             else
1025               p = strtok((char *) NULL, "-:;.,");
1026           }
1027         free(buff);
1028       }
1029
1030   return(keylen);
1031 }
1032
1033 /******************* POWER MANAGEMENT SUBROUTINES *******************/
1034
1035 /*------------------------------------------------------------------*/
1036 /*
1037  * Output a power management value with all attributes...
1038  */
1039 void
1040 iw_print_pm_value(char *        buffer,
1041                   int           value,
1042                   int           flags)
1043 {
1044   /* Modifiers */
1045   if(flags & IW_POWER_MIN)
1046     {
1047       strcpy(buffer, " min");
1048       buffer += 4;
1049     }
1050   if(flags & IW_POWER_MAX)
1051     {
1052       strcpy(buffer, " max");
1053       buffer += 4;
1054     }
1055
1056   /* Type */
1057   if(flags & IW_POWER_TIMEOUT)
1058     {
1059       strcpy(buffer, " timeout:");
1060       buffer += 9;
1061     }
1062   else
1063     {
1064       strcpy(buffer, " period:");
1065       buffer += 8;
1066     }
1067
1068   /* Display value without units */
1069   if(flags & IW_POWER_RELATIVE)
1070     sprintf(buffer, "%g", ((double) value) / MEGA);
1071   else
1072     {
1073       /* Display value with units */
1074       if(value >= (int) MEGA)
1075         sprintf(buffer, "%gs", ((double) value) / MEGA);
1076       else
1077         if(value >= (int) KILO)
1078           sprintf(buffer, "%gms", ((double) value) / KILO);
1079         else
1080           sprintf(buffer, "%dus", value);
1081     }
1082 }
1083
1084 /*------------------------------------------------------------------*/
1085 /*
1086  * Output a power management mode
1087  */
1088 void
1089 iw_print_pm_mode(char * buffer,
1090                  int    flags)
1091 {
1092   /* Print the proper mode... */
1093   switch(flags & IW_POWER_MODE)
1094     {
1095     case IW_POWER_UNICAST_R:
1096       strcpy(buffer, "mode:Unicast only received");
1097       break;
1098     case IW_POWER_MULTICAST_R:
1099       strcpy(buffer, "mode:Multicast only received");
1100       break;
1101     case IW_POWER_ALL_R:
1102       strcpy(buffer, "mode:All packets received");
1103       break;
1104     case IW_POWER_FORCE_S:
1105       strcpy(buffer, "mode:Force sending");
1106       break;
1107     case IW_POWER_REPEATER:
1108       strcpy(buffer, "mode:Repeat multicasts");
1109       break;
1110     default:
1111     }
1112 }
1113
1114 /***************** RETRY LIMIT/LIFETIME SUBROUTINES *****************/
1115
1116 #if WIRELESS_EXT > 10
1117 /*------------------------------------------------------------------*/
1118 /*
1119  * Output a retry value with all attributes...
1120  */
1121 void
1122 iw_print_retry_value(char *     buffer,
1123                      int        value,
1124                      int        flags)
1125 {
1126   /* Modifiers */
1127   if(flags & IW_RETRY_MIN)
1128     {
1129       strcpy(buffer, " min");
1130       buffer += 4;
1131     }
1132   if(flags & IW_RETRY_MAX)
1133     {
1134       strcpy(buffer, " max");
1135       buffer += 4;
1136     }
1137
1138   /* Type lifetime of limit */
1139   if(flags & IW_RETRY_LIFETIME)
1140     {
1141       strcpy(buffer, " lifetime:");
1142       buffer += 10;
1143
1144       /* Display value without units */
1145       if(flags & IW_POWER_RELATIVE)
1146         sprintf(buffer, "%g", ((double) value) / MEGA);
1147       else
1148         {
1149           /* Display value with units */
1150           if(value >= (int) MEGA)
1151             sprintf(buffer, "%gs", ((double) value) / MEGA);
1152           else
1153             if(value >= (int) KILO)
1154               sprintf(buffer, "%gms", ((double) value) / KILO);
1155             else
1156               sprintf(buffer, "%dus", value);
1157         }
1158     }
1159   else
1160     sprintf(buffer, " limit:%d", value);
1161 }
1162 #endif  /* WIRELESS_EXT > 10 */
1163
1164 /************************* TIME SUBROUTINES *************************/
1165
1166 /*------------------------------------------------------------------*/
1167 /*
1168  * Print timestamps
1169  * Inspired from irdadump...
1170  */
1171 void
1172 iw_print_timeval(char *                 buffer,
1173                  const struct timeval * time)
1174 {
1175         int s;
1176
1177         s = (time->tv_sec) % 86400;
1178         sprintf(buffer, "%02d:%02d:%02d.%06u ", 
1179                 s / 3600, (s % 3600) / 60, 
1180                 s % 60, (u_int32_t) time->tv_usec);
1181 }
1182
1183 /*********************** ADDRESS SUBROUTINES ************************/
1184 /*
1185  * This section is mostly a cut & past from net-tools-1.2.0
1186  * manage address display and input...
1187  */
1188
1189 /*------------------------------------------------------------------*/
1190 /*
1191  * Check if interface support the right MAC address type...
1192  */
1193 int
1194 iw_check_mac_addr_type(int              skfd,
1195                        char *           ifname)
1196 {
1197   struct ifreq          ifr;
1198
1199   /* Get the type of hardware address */
1200   strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
1201   if((ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) ||
1202      (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER))
1203     {
1204       /* Deep trouble... */
1205       fprintf(stderr, "Interface %s doesn't support MAC addresses\n",
1206              ifname);
1207       return(-1);
1208     }
1209
1210 #ifdef DEBUG
1211   printf("Hardware : %d - %s\n", ifr.ifr_hwaddr.sa_family,
1212          iw_ether_ntoa((struct ether_addr *) ifr.ifr_hwaddr.sa_data));
1213 #endif
1214
1215   return(0);
1216 }
1217
1218
1219 /*------------------------------------------------------------------*/
1220 /*
1221  * Check if interface support the right interface address type...
1222  */
1223 int
1224 iw_check_if_addr_type(int               skfd,
1225                       char *            ifname)
1226 {
1227   struct ifreq          ifr;
1228
1229   /* Get the type of interface address */
1230   strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
1231   if((ioctl(skfd, SIOCGIFADDR, &ifr) < 0) ||
1232      (ifr.ifr_addr.sa_family !=  AF_INET))
1233     {
1234       /* Deep trouble... */
1235       fprintf(stderr, "Interface %s doesn't support IP addresses\n", ifname);
1236       return(-1);
1237     }
1238
1239 #ifdef DEBUG
1240   printf("Interface : %d - 0x%lX\n", ifr.ifr_addr.sa_family,
1241          *((unsigned long *) ifr.ifr_addr.sa_data));
1242 #endif
1243
1244   return(0);
1245 }
1246
1247 #if 0
1248 /*------------------------------------------------------------------*/
1249 /*
1250  * Check if interface support the right address types...
1251  */
1252 int
1253 iw_check_addr_type(int          skfd,
1254                    char *       ifname)
1255 {
1256   /* Check the interface address type */
1257   if(iw_check_if_addr_type(skfd, ifname) < 0)
1258     return(-1);
1259
1260   /* Check the interface address type */
1261   if(iw_check_mac_addr_type(skfd, ifname) < 0)
1262     return(-1);
1263
1264   return(0);
1265 }
1266 #endif
1267
1268 /*------------------------------------------------------------------*/
1269 /*
1270  * Display an Ethernet address in readable format.
1271  */
1272 void
1273 iw_ether_ntop(const struct ether_addr* eth, char* buf)
1274 {
1275   sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
1276           eth->ether_addr_octet[0], eth->ether_addr_octet[1],
1277           eth->ether_addr_octet[2], eth->ether_addr_octet[3],
1278           eth->ether_addr_octet[4], eth->ether_addr_octet[5]);
1279 }
1280
1281 /*------------------------------------------------------------------*/
1282 /*
1283  * Display an Ethernet address in readable format.
1284  * Same with a static buffer
1285  */
1286 char *
1287 iw_ether_ntoa(const struct ether_addr* eth)
1288 {
1289   static char buf[20];
1290   iw_ether_ntop(eth, buf);
1291   return buf;
1292 }
1293
1294 /*------------------------------------------------------------------*/
1295 /*
1296  * Input an Ethernet address and convert to binary.
1297  */
1298 int
1299 iw_ether_aton(const char *orig, struct ether_addr *eth)
1300 {
1301   const char *bufp;
1302   int i;
1303
1304   i = 0;
1305   for(bufp = orig; *bufp != '\0'; ++bufp) {
1306         unsigned int val;
1307         unsigned char c = *bufp++;
1308         if (isdigit(c)) val = c - '0';
1309         else if (c >= 'a' && c <= 'f') val = c - 'a' + 10;
1310         else if (c >= 'A' && c <= 'F') val = c - 'A' + 10;
1311         else break;
1312
1313         val <<= 4;
1314         c = *bufp++;
1315         if (isdigit(c)) val |= c - '0';
1316         else if (c >= 'a' && c <= 'f') val |= c - 'a' + 10;
1317         else if (c >= 'A' && c <= 'F') val |= c - 'A' + 10;
1318         else break;
1319
1320         eth->ether_addr_octet[i] = (unsigned char) (val & 0377);
1321         if(++i == ETH_ALEN) {
1322                 /* That's it.  Any trailing junk? */
1323                 if (*bufp != '\0') {
1324 #ifdef DEBUG
1325                         fprintf(stderr, "iw_ether_aton(%s): trailing junk!\n", orig);
1326                         errno = EINVAL;
1327                         return(0);
1328 #endif
1329                 }
1330 #ifdef DEBUG
1331                 fprintf(stderr, "iw_ether_aton(%s): %s\n",
1332                         orig, ether_ntoa(eth));
1333 #endif
1334                 return(1);
1335         }
1336         if (*bufp != ':')
1337                 break;
1338   }
1339
1340 #ifdef DEBUG
1341   fprintf(stderr, "iw_ether_aton(%s): invalid ether address!\n", orig);
1342 #endif
1343   errno = EINVAL;
1344   return(0);
1345 }
1346
1347
1348 /*------------------------------------------------------------------*/
1349 /*
1350  * Input an Internet address and convert to binary.
1351  */
1352 int
1353 iw_in_inet(char *name, struct sockaddr *sap)
1354 {
1355   struct hostent *hp;
1356   struct netent *np;
1357   struct sockaddr_in *sin = (struct sockaddr_in *) sap;
1358
1359   /* Grmpf. -FvK */
1360   sin->sin_family = AF_INET;
1361   sin->sin_port = 0;
1362
1363   /* Default is special, meaning 0.0.0.0. */
1364   if (!strcmp(name, "default")) {
1365         sin->sin_addr.s_addr = INADDR_ANY;
1366         return(1);
1367   }
1368
1369   /* Try the NETWORKS database to see if this is a known network. */
1370   if ((np = getnetbyname(name)) != (struct netent *)NULL) {
1371         sin->sin_addr.s_addr = htonl(np->n_net);
1372         strcpy(name, np->n_name);
1373         return(1);
1374   }
1375
1376   /* Always use the resolver (DNS name + IP addresses) */
1377   if ((hp = gethostbyname(name)) == (struct hostent *)NULL) {
1378         errno = h_errno;
1379         return(-1);
1380   }
1381   memcpy((char *) &sin->sin_addr, (char *) hp->h_addr_list[0], hp->h_length);
1382   strcpy(name, hp->h_name);
1383   return(0);
1384 }
1385
1386 /*------------------------------------------------------------------*/
1387 /*
1388  * Input an address and convert to binary.
1389  */
1390 int
1391 iw_in_addr(int          skfd,
1392            char *       ifname,
1393            char *       bufp,
1394            struct sockaddr *sap)
1395 {
1396   /* Check if it is a hardware or IP address */
1397   if(index(bufp, ':') == NULL)
1398     {
1399       struct sockaddr   if_address;
1400       struct arpreq     arp_query;
1401
1402       /* Check if we have valid interface address type */
1403       if(iw_check_if_addr_type(skfd, ifname) < 0)
1404         {
1405           fprintf(stderr, "%-8.8s  Interface doesn't support IP addresses\n", ifname);
1406           return(-1);
1407         }
1408
1409       /* Read interface address */
1410       if(iw_in_inet(bufp, &if_address) < 0)
1411         {
1412           fprintf(stderr, "Invalid interface address %s\n", bufp);
1413           return(-1);
1414         }
1415
1416       /* Translate IP addresses to MAC addresses */
1417       memcpy((char *) &(arp_query.arp_pa),
1418              (char *) &if_address,
1419              sizeof(struct sockaddr));
1420       arp_query.arp_ha.sa_family = 0;
1421       arp_query.arp_flags = 0;
1422       /* The following restrict the search to the interface only */
1423       /* For old kernels which complain, just comment it... */
1424       strncpy(arp_query.arp_dev, ifname, IFNAMSIZ);
1425       if((ioctl(skfd, SIOCGARP, &arp_query) < 0) ||
1426          !(arp_query.arp_flags & ATF_COM))
1427         {
1428           fprintf(stderr, "Arp failed for %s on %s... (%d)\nTry to ping the address before setting it.\n",
1429                   bufp, ifname, errno);
1430           return(-1);
1431         }
1432
1433       /* Store new MAC address */
1434       memcpy((char *) sap,
1435              (char *) &(arp_query.arp_ha),
1436              sizeof(struct sockaddr));
1437
1438 #ifdef DEBUG
1439       printf("IP Address %s => Hw Address = %s\n",
1440              bufp, iw_ether_ntoa((struct ether_addr *) sap->sa_data));
1441 #endif
1442     }
1443   else  /* If it's an hardware address */
1444     {
1445       /* Check if we have valid mac address type */
1446       if(iw_check_mac_addr_type(skfd, ifname) < 0)
1447         {
1448           fprintf(stderr, "%-8.8s  Interface doesn't support MAC addresses\n", ifname);
1449           return(-1);
1450         }
1451
1452       /* Get the hardware address */
1453       if(iw_in_ether(bufp, sap) < 0)
1454         {
1455           fprintf(stderr, "Invalid hardware address %s\n", bufp);
1456           return(-1);
1457         }
1458     }
1459
1460 #ifdef DEBUG
1461   printf("Hw Address = %s\n", iw_ether_ntoa((struct ether_addr *) sap->sa_data));
1462 #endif
1463
1464   return(0);
1465 }
1466
1467 /************************* MISC SUBROUTINES **************************/
1468
1469 /* Size (in bytes) of various events */
1470 static const int priv_type_size[] = {
1471         0,                              /* IW_PRIV_TYPE_NONE */
1472         1,                              /* IW_PRIV_TYPE_BYTE */
1473         1,                              /* IW_PRIV_TYPE_CHAR */
1474         0,                              /* Not defined */
1475         sizeof(__u32),                  /* IW_PRIV_TYPE_INT */
1476         sizeof(struct iw_freq),         /* IW_PRIV_TYPE_FLOAT */
1477         sizeof(struct sockaddr),        /* IW_PRIV_TYPE_ADDR */
1478         0,                              /* Not defined */
1479 };
1480
1481 /*------------------------------------------------------------------*/
1482 /*
1483  * Max size in bytes of an private argument.
1484  */
1485 int
1486 iw_get_priv_size(int    args)
1487 {
1488   int   num = args & IW_PRIV_SIZE_MASK;
1489   int   type = (args & IW_PRIV_TYPE_MASK) >> 12;
1490
1491   return(num * priv_type_size[type]);
1492 }
1493
1494 /************************ EVENT SUBROUTINES ************************/
1495 /*
1496  * The Wireless Extension API 14 and greater define Wireless Events,
1497  * that are used for various events and scanning.
1498  * Those functions help the decoding of events, so are needed only in
1499  * this case.
1500  */
1501 #if WIRELESS_EXT > 13
1502
1503 /* Type of headers we know about (basically union iwreq_data) */
1504 #define IW_HEADER_TYPE_NULL     0       /* Not available */
1505 #define IW_HEADER_TYPE_CHAR     2       /* char [IFNAMSIZ] */
1506 #define IW_HEADER_TYPE_UINT     4       /* __u32 */
1507 #define IW_HEADER_TYPE_FREQ     5       /* struct iw_freq */
1508 #define IW_HEADER_TYPE_ADDR     6       /* struct sockaddr */
1509 #define IW_HEADER_TYPE_POINT    8       /* struct iw_point */
1510 #define IW_HEADER_TYPE_PARAM    9       /* struct iw_param */
1511 #define IW_HEADER_TYPE_QUAL     10      /* struct iw_quality */
1512
1513 /* Headers for the various requests */
1514 static const char standard_ioctl_hdr[] = {
1515         IW_HEADER_TYPE_NULL,    /* SIOCSIWCOMMIT */
1516         IW_HEADER_TYPE_CHAR,    /* SIOCGIWNAME */
1517         IW_HEADER_TYPE_PARAM,   /* SIOCSIWNWID */
1518         IW_HEADER_TYPE_PARAM,   /* SIOCGIWNWID */
1519         IW_HEADER_TYPE_FREQ,    /* SIOCSIWFREQ */
1520         IW_HEADER_TYPE_FREQ,    /* SIOCGIWFREQ */
1521         IW_HEADER_TYPE_UINT,    /* SIOCSIWMODE */
1522         IW_HEADER_TYPE_UINT,    /* SIOCGIWMODE */
1523         IW_HEADER_TYPE_PARAM,   /* SIOCSIWSENS */
1524         IW_HEADER_TYPE_PARAM,   /* SIOCGIWSENS */
1525         IW_HEADER_TYPE_NULL,    /* SIOCSIWRANGE */
1526         IW_HEADER_TYPE_POINT,   /* SIOCGIWRANGE */
1527         IW_HEADER_TYPE_NULL,    /* SIOCSIWPRIV */
1528         IW_HEADER_TYPE_POINT,   /* SIOCGIWPRIV */
1529         IW_HEADER_TYPE_NULL,    /* SIOCSIWSTATS */
1530         IW_HEADER_TYPE_POINT,   /* SIOCGIWSTATS */
1531         IW_HEADER_TYPE_POINT,   /* SIOCSIWSPY */
1532         IW_HEADER_TYPE_POINT,   /* SIOCGIWSPY */
1533         IW_HEADER_TYPE_NULL,    /* -- hole -- */
1534         IW_HEADER_TYPE_NULL,    /* -- hole -- */
1535         IW_HEADER_TYPE_ADDR,    /* SIOCSIWAP */
1536         IW_HEADER_TYPE_ADDR,    /* SIOCGIWAP */
1537         IW_HEADER_TYPE_NULL,    /* -- hole -- */
1538         IW_HEADER_TYPE_POINT,   /* SIOCGIWAPLIST */
1539         IW_HEADER_TYPE_PARAM,   /* SIOCSIWSCAN */
1540         IW_HEADER_TYPE_POINT,   /* SIOCGIWSCAN */
1541         IW_HEADER_TYPE_POINT,   /* SIOCSIWESSID */
1542         IW_HEADER_TYPE_POINT,   /* SIOCGIWESSID */
1543         IW_HEADER_TYPE_POINT,   /* SIOCSIWNICKN */
1544         IW_HEADER_TYPE_POINT,   /* SIOCGIWNICKN */
1545         IW_HEADER_TYPE_NULL,    /* -- hole -- */
1546         IW_HEADER_TYPE_NULL,    /* -- hole -- */
1547         IW_HEADER_TYPE_PARAM,   /* SIOCSIWRATE */
1548         IW_HEADER_TYPE_PARAM,   /* SIOCGIWRATE */
1549         IW_HEADER_TYPE_PARAM,   /* SIOCSIWRTS */
1550         IW_HEADER_TYPE_PARAM,   /* SIOCGIWRTS */
1551         IW_HEADER_TYPE_PARAM,   /* SIOCSIWFRAG */
1552         IW_HEADER_TYPE_PARAM,   /* SIOCGIWFRAG */
1553         IW_HEADER_TYPE_PARAM,   /* SIOCSIWTXPOW */
1554         IW_HEADER_TYPE_PARAM,   /* SIOCGIWTXPOW */
1555         IW_HEADER_TYPE_PARAM,   /* SIOCSIWRETRY */
1556         IW_HEADER_TYPE_PARAM,   /* SIOCGIWRETRY */
1557         IW_HEADER_TYPE_POINT,   /* SIOCSIWENCODE */
1558         IW_HEADER_TYPE_POINT,   /* SIOCGIWENCODE */
1559         IW_HEADER_TYPE_PARAM,   /* SIOCSIWPOWER */
1560         IW_HEADER_TYPE_PARAM,   /* SIOCGIWPOWER */
1561 };
1562 static const unsigned int standard_ioctl_num = sizeof(standard_ioctl_hdr);
1563
1564 /*
1565  * Meta-data about all the additional standard Wireless Extension events
1566  * we know about.
1567  */
1568 static const char       standard_event_hdr[] = {
1569         IW_HEADER_TYPE_ADDR,    /* IWEVTXDROP */
1570         IW_HEADER_TYPE_QUAL,    /* IWEVQUAL */
1571         IW_HEADER_TYPE_POINT,   /* IWEVCUSTOM */
1572         IW_HEADER_TYPE_ADDR,    /* IWEVREGISTERED */
1573         IW_HEADER_TYPE_ADDR,    /* IWEVEXPIRED */
1574 };
1575 static const unsigned int standard_event_num = sizeof(standard_event_hdr);
1576
1577 /* Size (in bytes) of various events */
1578 static const int event_type_size[] = {
1579         IW_EV_LCP_LEN,          /* IW_HEADER_TYPE_NULL */
1580         0,
1581         IW_EV_CHAR_LEN,         /* IW_HEADER_TYPE_CHAR */
1582         0,
1583         IW_EV_UINT_LEN,         /* IW_HEADER_TYPE_UINT */
1584         IW_EV_FREQ_LEN,         /* IW_HEADER_TYPE_FREQ */
1585         IW_EV_ADDR_LEN,         /* IW_HEADER_TYPE_ADDR */
1586         0,
1587         IW_EV_POINT_LEN,        /* Without variable payload */
1588         IW_EV_PARAM_LEN,        /* IW_HEADER_TYPE_PARAM */
1589         IW_EV_QUAL_LEN,         /* IW_HEADER_TYPE_QUAL */
1590 };
1591
1592 /*------------------------------------------------------------------*/
1593 /*
1594  * Initialise the struct stream_descr so that we can extract
1595  * individual events from the event stream.
1596  */
1597 void
1598 iw_init_event_stream(struct stream_descr *      stream, /* Stream of events */
1599                      char *                     data,
1600                      int                        len)
1601 {
1602   /* Cleanup */
1603   memset((char *) stream, '\0', sizeof(struct stream_descr));
1604
1605   /* Set things up */
1606   stream->current = data;
1607   stream->end = data + len;
1608 }
1609
1610 /*------------------------------------------------------------------*/
1611 /*
1612  * Extract the next event from the event stream.
1613  */
1614 int
1615 iw_extract_event_stream(struct stream_descr *   stream, /* Stream of events */
1616                         struct iw_event *       iwe)    /* Extracted event */
1617 {
1618   int           event_type = 0;
1619   unsigned int  event_len = 1;          /* Invalid */
1620   char *        pointer;
1621   /* Don't "optimise" the following variable, it will crash */
1622   unsigned      cmd_index;              /* *MUST* be unsigned */
1623
1624   /* Check for end of stream */
1625   if((stream->current + IW_EV_LCP_LEN) > stream->end)
1626     return(0);
1627
1628 #if 0
1629   printf("DBG - stream->current = %p, stream->value = %p, stream->end = %p\n",
1630          stream->current, stream->value, stream->end);
1631 #endif
1632
1633   /* Extract the event header (to get the event id).
1634    * Note : the event may be unaligned, therefore copy... */
1635   memcpy((char *) iwe, stream->current, IW_EV_LCP_LEN);
1636
1637 #if 0
1638   printf("DBG - iwe->cmd = 0x%X, iwe->len = %d\n",
1639          iwe->cmd, iwe->len);
1640 #endif
1641
1642   /* Check invalid events */
1643   if(iwe->len <= IW_EV_LCP_LEN)
1644     return(-1);
1645
1646   /* Get the type and length of that event */
1647   if(iwe->cmd <= SIOCIWLAST)
1648     {
1649       cmd_index = iwe->cmd - SIOCIWFIRST;
1650       if(cmd_index < standard_ioctl_num)
1651         event_type = standard_ioctl_hdr[cmd_index];
1652     }
1653   else
1654     {
1655       cmd_index = iwe->cmd - IWEVFIRST;
1656       if(cmd_index < standard_event_num)
1657         event_type = standard_event_hdr[cmd_index];
1658     }
1659   /* Unknown events -> event_type=0 => IW_EV_LCP_LEN */
1660   event_len = event_type_size[event_type];
1661
1662   /* Check if we know about this event */
1663   if(event_len <= IW_EV_LCP_LEN)
1664     {
1665       /* Skip to next event */
1666       stream->current += iwe->len;
1667       return(2);
1668     }
1669   event_len -= IW_EV_LCP_LEN;
1670
1671   /* Set pointer on data */
1672   if(stream->value != NULL)
1673     pointer = stream->value;                    /* Next value in event */
1674   else
1675     pointer = stream->current + IW_EV_LCP_LEN;  /* First value in event */
1676
1677 #if 0
1678   printf("DBG - event_type = %d, event_len = %d, pointer = %p\n",
1679          event_type, event_len, pointer);
1680 #endif
1681
1682   /* Copy the rest of the event (at least, fixed part) */
1683   if((pointer + event_len) > stream->end)
1684     {
1685       /* Go to next event */
1686       stream->current += iwe->len;
1687       return(-2);
1688     }
1689   memcpy((char *) iwe + IW_EV_LCP_LEN, pointer, event_len);
1690
1691   /* Skip event in the stream */
1692   pointer += event_len;
1693
1694   /* Special processing for iw_point events */
1695   if(event_type == IW_HEADER_TYPE_POINT)
1696     {
1697       /* Check the length of the payload */
1698       if((iwe->len - (event_len + IW_EV_LCP_LEN)) > 0)
1699         /* Set pointer on variable part (warning : non aligned) */
1700         iwe->u.data.pointer = pointer;
1701       else
1702         /* No data */
1703         iwe->u.data.pointer = NULL;
1704
1705       /* Go to next event */
1706       stream->current += iwe->len;
1707     }
1708   else
1709     {
1710       /* Is there more value in the event ? */
1711       if((pointer + event_len) <= (stream->current + iwe->len))
1712         /* Go to next value */
1713         stream->value = pointer;
1714       else
1715         {
1716           /* Go to next event */
1717           stream->value = NULL;
1718           stream->current += iwe->len;
1719         }
1720     }
1721   return(1);
1722 }
1723
1724 #endif /* WIRELESS_EXT > 13 */