[PATCH] wireless: WE-20 compatibility for ESSID and NICKN ioctls
[linux-2.6] / net / core / wireless.c
1 /*
2  * This file implement the Wireless Extensions APIs.
3  *
4  * Authors :    Jean Tourrilhes - HPL - <jt@hpl.hp.com>
5  * Copyright (c) 1997-2006 Jean Tourrilhes, All Rights Reserved.
6  *
7  * (As all part of the Linux kernel, this file is GPL)
8  */
9
10 /************************** DOCUMENTATION **************************/
11 /*
12  * API definition :
13  * --------------
14  * See <linux/wireless.h> for details of the APIs and the rest.
15  *
16  * History :
17  * -------
18  *
19  * v1 - 5.12.01 - Jean II
20  *      o Created this file.
21  *
22  * v2 - 13.12.01 - Jean II
23  *      o Move /proc/net/wireless stuff from net/core/dev.c to here
24  *      o Make Wireless Extension IOCTLs go through here
25  *      o Added iw_handler handling ;-)
26  *      o Added standard ioctl description
27  *      o Initial dumb commit strategy based on orinoco.c
28  *
29  * v3 - 19.12.01 - Jean II
30  *      o Make sure we don't go out of standard_ioctl[] in ioctl_standard_call
31  *      o Add event dispatcher function
32  *      o Add event description
33  *      o Propagate events as rtnetlink IFLA_WIRELESS option
34  *      o Generate event on selected SET requests
35  *
36  * v4 - 18.04.02 - Jean II
37  *      o Fix stupid off by one in iw_ioctl_description : IW_ESSID_MAX_SIZE + 1
38  *
39  * v5 - 21.06.02 - Jean II
40  *      o Add IW_PRIV_TYPE_ADDR in priv_type_size (+cleanup)
41  *      o Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes
42  *      o Add IWEVCUSTOM for driver specific event/scanning token
43  *      o Turn on WE_STRICT_WRITE by default + kernel warning
44  *      o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num)
45  *      o Fix off-by-one in test (extra_size <= IFNAMSIZ)
46  *
47  * v6 - 9.01.03 - Jean II
48  *      o Add common spy support : iw_handler_set_spy(), wireless_spy_update()
49  *      o Add enhanced spy support : iw_handler_set_thrspy() and event.
50  *      o Add WIRELESS_EXT version display in /proc/net/wireless
51  *
52  * v6 - 18.06.04 - Jean II
53  *      o Change get_spydata() method for added safety
54  *      o Remove spy #ifdef, they are always on -> cleaner code
55  *      o Allow any size GET request if user specifies length > max
56  *              and if request has IW_DESCR_FLAG_NOMAX flag or is SIOCGIWPRIV
57  *      o Start migrating get_wireless_stats to struct iw_handler_def
58  *      o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus
59  * Based on patch from Pavel Roskin <proski@gnu.org> :
60  *      o Fix kernel data leak to user space in private handler handling
61  *
62  * v7 - 18.3.05 - Jean II
63  *      o Remove (struct iw_point *)->pointer from events and streams
64  *      o Remove spy_offset from struct iw_handler_def
65  *      o Start deprecating dev->get_wireless_stats, output a warning
66  *      o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless
67  *      o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats)
68  *
69  * v8 - 17.02.06 - Jean II
70  *      o RtNetlink requests support (SET/GET)
71  *
72  * v8b - 03.08.06 - Herbert Xu
73  *      o Fix Wireless Event locking issues.
74  *
75  * v9 - 14.3.06 - Jean II
76  *      o Change length in ESSID and NICK to strlen() instead of strlen()+1
77  *      o Make standard_ioctl_num and standard_event_num unsigned
78  *      o Remove (struct net_device *)->get_wireless_stats()
79  */
80
81 /***************************** INCLUDES *****************************/
82
83 #include <linux/module.h>
84 #include <linux/types.h>                /* off_t */
85 #include <linux/netdevice.h>            /* struct ifreq, dev_get_by_name() */
86 #include <linux/proc_fs.h>
87 #include <linux/rtnetlink.h>            /* rtnetlink stuff */
88 #include <linux/seq_file.h>
89 #include <linux/init.h>                 /* for __init */
90 #include <linux/if_arp.h>               /* ARPHRD_ETHER */
91 #include <linux/etherdevice.h>          /* compare_ether_addr */
92 #include <linux/interrupt.h>
93
94 #include <linux/wireless.h>             /* Pretty obvious */
95 #include <net/iw_handler.h>             /* New driver API */
96 #include <net/netlink.h>
97
98 #include <asm/uaccess.h>                /* copy_to_user() */
99
100 /**************************** CONSTANTS ****************************/
101
102 /* Debugging stuff */
103 #undef WE_IOCTL_DEBUG           /* Debug IOCTL API */
104 #undef WE_RTNETLINK_DEBUG       /* Debug RtNetlink API */
105 #undef WE_EVENT_DEBUG           /* Debug Event dispatcher */
106 #undef WE_SPY_DEBUG             /* Debug enhanced spy support */
107
108 /* Options */
109 //CONFIG_NET_WIRELESS_RTNETLINK /* Wireless requests over RtNetlink */
110 #define WE_EVENT_RTNETLINK      /* Propagate events using RtNetlink */
111 #define WE_SET_EVENT            /* Generate an event on some set commands */
112
113 /************************* GLOBAL VARIABLES *************************/
114 /*
115  * You should not use global variables, because of re-entrancy.
116  * On our case, it's only const, so it's OK...
117  */
118 /*
119  * Meta-data about all the standard Wireless Extension request we
120  * know about.
121  */
122 static const struct iw_ioctl_description standard_ioctl[] = {
123         [SIOCSIWCOMMIT  - SIOCIWFIRST] = {
124                 .header_type    = IW_HEADER_TYPE_NULL,
125         },
126         [SIOCGIWNAME    - SIOCIWFIRST] = {
127                 .header_type    = IW_HEADER_TYPE_CHAR,
128                 .flags          = IW_DESCR_FLAG_DUMP,
129         },
130         [SIOCSIWNWID    - SIOCIWFIRST] = {
131                 .header_type    = IW_HEADER_TYPE_PARAM,
132                 .flags          = IW_DESCR_FLAG_EVENT,
133         },
134         [SIOCGIWNWID    - SIOCIWFIRST] = {
135                 .header_type    = IW_HEADER_TYPE_PARAM,
136                 .flags          = IW_DESCR_FLAG_DUMP,
137         },
138         [SIOCSIWFREQ    - SIOCIWFIRST] = {
139                 .header_type    = IW_HEADER_TYPE_FREQ,
140                 .flags          = IW_DESCR_FLAG_EVENT,
141         },
142         [SIOCGIWFREQ    - SIOCIWFIRST] = {
143                 .header_type    = IW_HEADER_TYPE_FREQ,
144                 .flags          = IW_DESCR_FLAG_DUMP,
145         },
146         [SIOCSIWMODE    - SIOCIWFIRST] = {
147                 .header_type    = IW_HEADER_TYPE_UINT,
148                 .flags          = IW_DESCR_FLAG_EVENT,
149         },
150         [SIOCGIWMODE    - SIOCIWFIRST] = {
151                 .header_type    = IW_HEADER_TYPE_UINT,
152                 .flags          = IW_DESCR_FLAG_DUMP,
153         },
154         [SIOCSIWSENS    - SIOCIWFIRST] = {
155                 .header_type    = IW_HEADER_TYPE_PARAM,
156         },
157         [SIOCGIWSENS    - SIOCIWFIRST] = {
158                 .header_type    = IW_HEADER_TYPE_PARAM,
159         },
160         [SIOCSIWRANGE   - SIOCIWFIRST] = {
161                 .header_type    = IW_HEADER_TYPE_NULL,
162         },
163         [SIOCGIWRANGE   - SIOCIWFIRST] = {
164                 .header_type    = IW_HEADER_TYPE_POINT,
165                 .token_size     = 1,
166                 .max_tokens     = sizeof(struct iw_range),
167                 .flags          = IW_DESCR_FLAG_DUMP,
168         },
169         [SIOCSIWPRIV    - SIOCIWFIRST] = {
170                 .header_type    = IW_HEADER_TYPE_NULL,
171         },
172         [SIOCGIWPRIV    - SIOCIWFIRST] = { /* (handled directly by us) */
173                 .header_type    = IW_HEADER_TYPE_POINT,
174                 .token_size     = sizeof(struct iw_priv_args),
175                 .max_tokens     = 16,
176                 .flags          = IW_DESCR_FLAG_NOMAX,
177         },
178         [SIOCSIWSTATS   - SIOCIWFIRST] = {
179                 .header_type    = IW_HEADER_TYPE_NULL,
180         },
181         [SIOCGIWSTATS   - SIOCIWFIRST] = { /* (handled directly by us) */
182                 .header_type    = IW_HEADER_TYPE_POINT,
183                 .token_size     = 1,
184                 .max_tokens     = sizeof(struct iw_statistics),
185                 .flags          = IW_DESCR_FLAG_DUMP,
186         },
187         [SIOCSIWSPY     - SIOCIWFIRST] = {
188                 .header_type    = IW_HEADER_TYPE_POINT,
189                 .token_size     = sizeof(struct sockaddr),
190                 .max_tokens     = IW_MAX_SPY,
191         },
192         [SIOCGIWSPY     - SIOCIWFIRST] = {
193                 .header_type    = IW_HEADER_TYPE_POINT,
194                 .token_size     = sizeof(struct sockaddr) +
195                                   sizeof(struct iw_quality),
196                 .max_tokens     = IW_MAX_SPY,
197         },
198         [SIOCSIWTHRSPY  - SIOCIWFIRST] = {
199                 .header_type    = IW_HEADER_TYPE_POINT,
200                 .token_size     = sizeof(struct iw_thrspy),
201                 .min_tokens     = 1,
202                 .max_tokens     = 1,
203         },
204         [SIOCGIWTHRSPY  - SIOCIWFIRST] = {
205                 .header_type    = IW_HEADER_TYPE_POINT,
206                 .token_size     = sizeof(struct iw_thrspy),
207                 .min_tokens     = 1,
208                 .max_tokens     = 1,
209         },
210         [SIOCSIWAP      - SIOCIWFIRST] = {
211                 .header_type    = IW_HEADER_TYPE_ADDR,
212         },
213         [SIOCGIWAP      - SIOCIWFIRST] = {
214                 .header_type    = IW_HEADER_TYPE_ADDR,
215                 .flags          = IW_DESCR_FLAG_DUMP,
216         },
217         [SIOCSIWMLME    - SIOCIWFIRST] = {
218                 .header_type    = IW_HEADER_TYPE_POINT,
219                 .token_size     = 1,
220                 .min_tokens     = sizeof(struct iw_mlme),
221                 .max_tokens     = sizeof(struct iw_mlme),
222         },
223         [SIOCGIWAPLIST  - SIOCIWFIRST] = {
224                 .header_type    = IW_HEADER_TYPE_POINT,
225                 .token_size     = sizeof(struct sockaddr) +
226                                   sizeof(struct iw_quality),
227                 .max_tokens     = IW_MAX_AP,
228                 .flags          = IW_DESCR_FLAG_NOMAX,
229         },
230         [SIOCSIWSCAN    - SIOCIWFIRST] = {
231                 .header_type    = IW_HEADER_TYPE_POINT,
232                 .token_size     = 1,
233                 .min_tokens     = 0,
234                 .max_tokens     = sizeof(struct iw_scan_req),
235         },
236         [SIOCGIWSCAN    - SIOCIWFIRST] = {
237                 .header_type    = IW_HEADER_TYPE_POINT,
238                 .token_size     = 1,
239                 .max_tokens     = IW_SCAN_MAX_DATA,
240                 .flags          = IW_DESCR_FLAG_NOMAX,
241         },
242         [SIOCSIWESSID   - SIOCIWFIRST] = {
243                 .header_type    = IW_HEADER_TYPE_POINT,
244                 .token_size     = 1,
245                 .max_tokens     = IW_ESSID_MAX_SIZE,
246                 .flags          = IW_DESCR_FLAG_EVENT,
247         },
248         [SIOCGIWESSID   - SIOCIWFIRST] = {
249                 .header_type    = IW_HEADER_TYPE_POINT,
250                 .token_size     = 1,
251                 .max_tokens     = IW_ESSID_MAX_SIZE,
252                 .flags          = IW_DESCR_FLAG_DUMP,
253         },
254         [SIOCSIWNICKN   - SIOCIWFIRST] = {
255                 .header_type    = IW_HEADER_TYPE_POINT,
256                 .token_size     = 1,
257                 .max_tokens     = IW_ESSID_MAX_SIZE,
258         },
259         [SIOCGIWNICKN   - SIOCIWFIRST] = {
260                 .header_type    = IW_HEADER_TYPE_POINT,
261                 .token_size     = 1,
262                 .max_tokens     = IW_ESSID_MAX_SIZE,
263         },
264         [SIOCSIWRATE    - SIOCIWFIRST] = {
265                 .header_type    = IW_HEADER_TYPE_PARAM,
266         },
267         [SIOCGIWRATE    - SIOCIWFIRST] = {
268                 .header_type    = IW_HEADER_TYPE_PARAM,
269         },
270         [SIOCSIWRTS     - SIOCIWFIRST] = {
271                 .header_type    = IW_HEADER_TYPE_PARAM,
272         },
273         [SIOCGIWRTS     - SIOCIWFIRST] = {
274                 .header_type    = IW_HEADER_TYPE_PARAM,
275         },
276         [SIOCSIWFRAG    - SIOCIWFIRST] = {
277                 .header_type    = IW_HEADER_TYPE_PARAM,
278         },
279         [SIOCGIWFRAG    - SIOCIWFIRST] = {
280                 .header_type    = IW_HEADER_TYPE_PARAM,
281         },
282         [SIOCSIWTXPOW   - SIOCIWFIRST] = {
283                 .header_type    = IW_HEADER_TYPE_PARAM,
284         },
285         [SIOCGIWTXPOW   - SIOCIWFIRST] = {
286                 .header_type    = IW_HEADER_TYPE_PARAM,
287         },
288         [SIOCSIWRETRY   - SIOCIWFIRST] = {
289                 .header_type    = IW_HEADER_TYPE_PARAM,
290         },
291         [SIOCGIWRETRY   - SIOCIWFIRST] = {
292                 .header_type    = IW_HEADER_TYPE_PARAM,
293         },
294         [SIOCSIWENCODE  - SIOCIWFIRST] = {
295                 .header_type    = IW_HEADER_TYPE_POINT,
296                 .token_size     = 1,
297                 .max_tokens     = IW_ENCODING_TOKEN_MAX,
298                 .flags          = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
299         },
300         [SIOCGIWENCODE  - SIOCIWFIRST] = {
301                 .header_type    = IW_HEADER_TYPE_POINT,
302                 .token_size     = 1,
303                 .max_tokens     = IW_ENCODING_TOKEN_MAX,
304                 .flags          = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
305         },
306         [SIOCSIWPOWER   - SIOCIWFIRST] = {
307                 .header_type    = IW_HEADER_TYPE_PARAM,
308         },
309         [SIOCGIWPOWER   - SIOCIWFIRST] = {
310                 .header_type    = IW_HEADER_TYPE_PARAM,
311         },
312         [SIOCSIWGENIE   - SIOCIWFIRST] = {
313                 .header_type    = IW_HEADER_TYPE_POINT,
314                 .token_size     = 1,
315                 .max_tokens     = IW_GENERIC_IE_MAX,
316         },
317         [SIOCGIWGENIE   - SIOCIWFIRST] = {
318                 .header_type    = IW_HEADER_TYPE_POINT,
319                 .token_size     = 1,
320                 .max_tokens     = IW_GENERIC_IE_MAX,
321         },
322         [SIOCSIWAUTH    - SIOCIWFIRST] = {
323                 .header_type    = IW_HEADER_TYPE_PARAM,
324         },
325         [SIOCGIWAUTH    - SIOCIWFIRST] = {
326                 .header_type    = IW_HEADER_TYPE_PARAM,
327         },
328         [SIOCSIWENCODEEXT - SIOCIWFIRST] = {
329                 .header_type    = IW_HEADER_TYPE_POINT,
330                 .token_size     = 1,
331                 .min_tokens     = sizeof(struct iw_encode_ext),
332                 .max_tokens     = sizeof(struct iw_encode_ext) +
333                                   IW_ENCODING_TOKEN_MAX,
334         },
335         [SIOCGIWENCODEEXT - SIOCIWFIRST] = {
336                 .header_type    = IW_HEADER_TYPE_POINT,
337                 .token_size     = 1,
338                 .min_tokens     = sizeof(struct iw_encode_ext),
339                 .max_tokens     = sizeof(struct iw_encode_ext) +
340                                   IW_ENCODING_TOKEN_MAX,
341         },
342         [SIOCSIWPMKSA - SIOCIWFIRST] = {
343                 .header_type    = IW_HEADER_TYPE_POINT,
344                 .token_size     = 1,
345                 .min_tokens     = sizeof(struct iw_pmksa),
346                 .max_tokens     = sizeof(struct iw_pmksa),
347         },
348 };
349 static const unsigned standard_ioctl_num = (sizeof(standard_ioctl) /
350                                             sizeof(struct iw_ioctl_description));
351
352 /*
353  * Meta-data about all the additional standard Wireless Extension events
354  * we know about.
355  */
356 static const struct iw_ioctl_description standard_event[] = {
357         [IWEVTXDROP     - IWEVFIRST] = {
358                 .header_type    = IW_HEADER_TYPE_ADDR,
359         },
360         [IWEVQUAL       - IWEVFIRST] = {
361                 .header_type    = IW_HEADER_TYPE_QUAL,
362         },
363         [IWEVCUSTOM     - IWEVFIRST] = {
364                 .header_type    = IW_HEADER_TYPE_POINT,
365                 .token_size     = 1,
366                 .max_tokens     = IW_CUSTOM_MAX,
367         },
368         [IWEVREGISTERED - IWEVFIRST] = {
369                 .header_type    = IW_HEADER_TYPE_ADDR,
370         },
371         [IWEVEXPIRED    - IWEVFIRST] = {
372                 .header_type    = IW_HEADER_TYPE_ADDR, 
373         },
374         [IWEVGENIE      - IWEVFIRST] = {
375                 .header_type    = IW_HEADER_TYPE_POINT,
376                 .token_size     = 1,
377                 .max_tokens     = IW_GENERIC_IE_MAX,
378         },
379         [IWEVMICHAELMICFAILURE  - IWEVFIRST] = {
380                 .header_type    = IW_HEADER_TYPE_POINT, 
381                 .token_size     = 1,
382                 .max_tokens     = sizeof(struct iw_michaelmicfailure),
383         },
384         [IWEVASSOCREQIE - IWEVFIRST] = {
385                 .header_type    = IW_HEADER_TYPE_POINT,
386                 .token_size     = 1,
387                 .max_tokens     = IW_GENERIC_IE_MAX,
388         },
389         [IWEVASSOCRESPIE        - IWEVFIRST] = {
390                 .header_type    = IW_HEADER_TYPE_POINT,
391                 .token_size     = 1,
392                 .max_tokens     = IW_GENERIC_IE_MAX,
393         },
394         [IWEVPMKIDCAND  - IWEVFIRST] = {
395                 .header_type    = IW_HEADER_TYPE_POINT,
396                 .token_size     = 1,
397                 .max_tokens     = sizeof(struct iw_pmkid_cand),
398         },
399 };
400 static const unsigned standard_event_num = (sizeof(standard_event) /
401                                             sizeof(struct iw_ioctl_description));
402
403 /* Size (in bytes) of the various private data types */
404 static const char iw_priv_type_size[] = {
405         0,                              /* IW_PRIV_TYPE_NONE */
406         1,                              /* IW_PRIV_TYPE_BYTE */
407         1,                              /* IW_PRIV_TYPE_CHAR */
408         0,                              /* Not defined */
409         sizeof(__u32),                  /* IW_PRIV_TYPE_INT */
410         sizeof(struct iw_freq),         /* IW_PRIV_TYPE_FLOAT */
411         sizeof(struct sockaddr),        /* IW_PRIV_TYPE_ADDR */
412         0,                              /* Not defined */
413 };
414
415 /* Size (in bytes) of various events */
416 static const int event_type_size[] = {
417         IW_EV_LCP_LEN,                  /* IW_HEADER_TYPE_NULL */
418         0,
419         IW_EV_CHAR_LEN,                 /* IW_HEADER_TYPE_CHAR */
420         0,
421         IW_EV_UINT_LEN,                 /* IW_HEADER_TYPE_UINT */
422         IW_EV_FREQ_LEN,                 /* IW_HEADER_TYPE_FREQ */
423         IW_EV_ADDR_LEN,                 /* IW_HEADER_TYPE_ADDR */
424         0,
425         IW_EV_POINT_LEN,                /* Without variable payload */
426         IW_EV_PARAM_LEN,                /* IW_HEADER_TYPE_PARAM */
427         IW_EV_QUAL_LEN,                 /* IW_HEADER_TYPE_QUAL */
428 };
429
430 /************************ COMMON SUBROUTINES ************************/
431 /*
432  * Stuff that may be used in various place or doesn't fit in one
433  * of the section below.
434  */
435
436 /* ---------------------------------------------------------------- */
437 /*
438  * Return the driver handler associated with a specific Wireless Extension.
439  * Called from various place, so make sure it remains efficient.
440  */
441 static inline iw_handler get_handler(struct net_device *dev,
442                                      unsigned int cmd)
443 {
444         /* Don't "optimise" the following variable, it will crash */
445         unsigned int    index;          /* *MUST* be unsigned */
446
447         /* Check if we have some wireless handlers defined */
448         if(dev->wireless_handlers == NULL)
449                 return NULL;
450
451         /* Try as a standard command */
452         index = cmd - SIOCIWFIRST;
453         if(index < dev->wireless_handlers->num_standard)
454                 return dev->wireless_handlers->standard[index];
455
456         /* Try as a private command */
457         index = cmd - SIOCIWFIRSTPRIV;
458         if(index < dev->wireless_handlers->num_private)
459                 return dev->wireless_handlers->private[index];
460
461         /* Not found */
462         return NULL;
463 }
464
465 /* ---------------------------------------------------------------- */
466 /*
467  * Get statistics out of the driver
468  */
469 static inline struct iw_statistics *get_wireless_stats(struct net_device *dev)
470 {
471         /* New location */
472         if((dev->wireless_handlers != NULL) &&
473            (dev->wireless_handlers->get_wireless_stats != NULL))
474                 return dev->wireless_handlers->get_wireless_stats(dev);
475
476         /* Not found */
477         return (struct iw_statistics *) NULL;
478 }
479
480 /* ---------------------------------------------------------------- */
481 /*
482  * Call the commit handler in the driver
483  * (if exist and if conditions are right)
484  *
485  * Note : our current commit strategy is currently pretty dumb,
486  * but we will be able to improve on that...
487  * The goal is to try to agreagate as many changes as possible
488  * before doing the commit. Drivers that will define a commit handler
489  * are usually those that need a reset after changing parameters, so
490  * we want to minimise the number of reset.
491  * A cool idea is to use a timer : at each "set" command, we re-set the
492  * timer, when the timer eventually fires, we call the driver.
493  * Hopefully, more on that later.
494  *
495  * Also, I'm waiting to see how many people will complain about the
496  * netif_running(dev) test. I'm open on that one...
497  * Hopefully, the driver will remember to do a commit in "open()" ;-)
498  */
499 static inline int call_commit_handler(struct net_device *       dev)
500 {
501         if((netif_running(dev)) &&
502            (dev->wireless_handlers->standard[0] != NULL)) {
503                 /* Call the commit handler on the driver */
504                 return dev->wireless_handlers->standard[0](dev, NULL,
505                                                            NULL, NULL);
506         } else
507                 return 0;               /* Command completed successfully */
508 }
509
510 /* ---------------------------------------------------------------- */
511 /*
512  * Calculate size of private arguments
513  */
514 static inline int get_priv_size(__u16   args)
515 {
516         int     num = args & IW_PRIV_SIZE_MASK;
517         int     type = (args & IW_PRIV_TYPE_MASK) >> 12;
518
519         return num * iw_priv_type_size[type];
520 }
521
522 /* ---------------------------------------------------------------- */
523 /*
524  * Re-calculate the size of private arguments
525  */
526 static inline int adjust_priv_size(__u16                args,
527                                    union iwreq_data *   wrqu)
528 {
529         int     num = wrqu->data.length;
530         int     max = args & IW_PRIV_SIZE_MASK;
531         int     type = (args & IW_PRIV_TYPE_MASK) >> 12;
532
533         /* Make sure the driver doesn't goof up */
534         if (max < num)
535                 num = max;
536
537         return num * iw_priv_type_size[type];
538 }
539
540 /* ---------------------------------------------------------------- */
541 /*
542  * Standard Wireless Handler : get wireless stats
543  *      Allow programatic access to /proc/net/wireless even if /proc
544  *      doesn't exist... Also more efficient...
545  */
546 static int iw_handler_get_iwstats(struct net_device *           dev,
547                                   struct iw_request_info *      info,
548                                   union iwreq_data *            wrqu,
549                                   char *                        extra)
550 {
551         /* Get stats from the driver */
552         struct iw_statistics *stats;
553
554         stats = get_wireless_stats(dev);
555         if (stats != (struct iw_statistics *) NULL) {
556
557                 /* Copy statistics to extra */
558                 memcpy(extra, stats, sizeof(struct iw_statistics));
559                 wrqu->data.length = sizeof(struct iw_statistics);
560
561                 /* Check if we need to clear the updated flag */
562                 if(wrqu->data.flags != 0)
563                         stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
564                 return 0;
565         } else
566                 return -EOPNOTSUPP;
567 }
568
569 /* ---------------------------------------------------------------- */
570 /*
571  * Standard Wireless Handler : get iwpriv definitions
572  * Export the driver private handler definition
573  * They will be picked up by tools like iwpriv...
574  */
575 static int iw_handler_get_private(struct net_device *           dev,
576                                   struct iw_request_info *      info,
577                                   union iwreq_data *            wrqu,
578                                   char *                        extra)
579 {
580         /* Check if the driver has something to export */
581         if((dev->wireless_handlers->num_private_args == 0) ||
582            (dev->wireless_handlers->private_args == NULL))
583                 return -EOPNOTSUPP;
584
585         /* Check if there is enough buffer up there */
586         if(wrqu->data.length < dev->wireless_handlers->num_private_args) {
587                 /* User space can't know in advance how large the buffer
588                  * needs to be. Give it a hint, so that we can support
589                  * any size buffer we want somewhat efficiently... */
590                 wrqu->data.length = dev->wireless_handlers->num_private_args;
591                 return -E2BIG;
592         }
593
594         /* Set the number of available ioctls. */
595         wrqu->data.length = dev->wireless_handlers->num_private_args;
596
597         /* Copy structure to the user buffer. */
598         memcpy(extra, dev->wireless_handlers->private_args,
599                sizeof(struct iw_priv_args) * wrqu->data.length);
600
601         return 0;
602 }
603
604
605 /******************** /proc/net/wireless SUPPORT ********************/
606 /*
607  * The /proc/net/wireless file is a human readable user-space interface
608  * exporting various wireless specific statistics from the wireless devices.
609  * This is the most popular part of the Wireless Extensions ;-)
610  *
611  * This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
612  * The content of the file is basically the content of "struct iw_statistics".
613  */
614
615 #ifdef CONFIG_PROC_FS
616
617 /* ---------------------------------------------------------------- */
618 /*
619  * Print one entry (line) of /proc/net/wireless
620  */
621 static __inline__ void wireless_seq_printf_stats(struct seq_file *seq,
622                                                  struct net_device *dev)
623 {
624         /* Get stats from the driver */
625         struct iw_statistics *stats = get_wireless_stats(dev);
626
627         if (stats) {
628                 seq_printf(seq, "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d "
629                                 "%6d %6d   %6d\n",
630                            dev->name, stats->status, stats->qual.qual,
631                            stats->qual.updated & IW_QUAL_QUAL_UPDATED
632                            ? '.' : ' ',
633                            ((__s32) stats->qual.level) - 
634                            ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
635                            stats->qual.updated & IW_QUAL_LEVEL_UPDATED
636                            ? '.' : ' ',
637                            ((__s32) stats->qual.noise) - 
638                            ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
639                            stats->qual.updated & IW_QUAL_NOISE_UPDATED
640                            ? '.' : ' ',
641                            stats->discard.nwid, stats->discard.code,
642                            stats->discard.fragment, stats->discard.retries,
643                            stats->discard.misc, stats->miss.beacon);
644                 stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
645         }
646 }
647
648 /* ---------------------------------------------------------------- */
649 /*
650  * Print info for /proc/net/wireless (print all entries)
651  */
652 static int wireless_seq_show(struct seq_file *seq, void *v)
653 {
654         if (v == SEQ_START_TOKEN)
655                 seq_printf(seq, "Inter-| sta-|   Quality        |   Discarded "
656                                 "packets               | Missed | WE\n"
657                                 " face | tus | link level noise |  nwid  "
658                                 "crypt   frag  retry   misc | beacon | %d\n",
659                            WIRELESS_EXT);
660         else
661                 wireless_seq_printf_stats(seq, v);
662         return 0;
663 }
664
665 static struct seq_operations wireless_seq_ops = {
666         .start = dev_seq_start,
667         .next  = dev_seq_next,
668         .stop  = dev_seq_stop,
669         .show  = wireless_seq_show,
670 };
671
672 static int wireless_seq_open(struct inode *inode, struct file *file)
673 {
674         return seq_open(file, &wireless_seq_ops);
675 }
676
677 static struct file_operations wireless_seq_fops = {
678         .owner   = THIS_MODULE,
679         .open    = wireless_seq_open,
680         .read    = seq_read,
681         .llseek  = seq_lseek,
682         .release = seq_release,
683 };
684
685 int __init wireless_proc_init(void)
686 {
687         /* Create /proc/net/wireless entry */
688         if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops))
689                 return -ENOMEM;
690
691         return 0;
692 }
693 #endif  /* CONFIG_PROC_FS */
694
695 /************************** IOCTL SUPPORT **************************/
696 /*
697  * The original user space API to configure all those Wireless Extensions
698  * is through IOCTLs.
699  * In there, we check if we need to call the new driver API (iw_handler)
700  * or just call the driver ioctl handler.
701  */
702
703 /* ---------------------------------------------------------------- */
704 /*
705  * Wrapper to call a standard Wireless Extension handler.
706  * We do various checks and also take care of moving data between
707  * user space and kernel space.
708  */
709 static int ioctl_standard_call(struct net_device *      dev,
710                                struct ifreq *           ifr,
711                                unsigned int             cmd,
712                                iw_handler               handler)
713 {
714         struct iwreq *                          iwr = (struct iwreq *) ifr;
715         const struct iw_ioctl_description *     descr;
716         struct iw_request_info                  info;
717         int                                     ret = -EINVAL;
718
719         /* Get the description of the IOCTL */
720         if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
721                 return -EOPNOTSUPP;
722         descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
723
724 #ifdef WE_IOCTL_DEBUG
725         printk(KERN_DEBUG "%s (WE) : Found standard handler for 0x%04X\n",
726                ifr->ifr_name, cmd);
727         printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
728 #endif  /* WE_IOCTL_DEBUG */
729
730         /* Prepare the call */
731         info.cmd = cmd;
732         info.flags = 0;
733
734         /* Check if we have a pointer to user space data or not */
735         if(descr->header_type != IW_HEADER_TYPE_POINT) {
736
737                 /* No extra arguments. Trivial to handle */
738                 ret = handler(dev, &info, &(iwr->u), NULL);
739
740 #ifdef WE_SET_EVENT
741                 /* Generate an event to notify listeners of the change */
742                 if((descr->flags & IW_DESCR_FLAG_EVENT) &&
743                    ((ret == 0) || (ret == -EIWCOMMIT)))
744                         wireless_send_event(dev, cmd, &(iwr->u), NULL);
745 #endif  /* WE_SET_EVENT */
746         } else {
747                 char *  extra;
748                 int     extra_size;
749                 int     user_length = 0;
750                 int     err;
751                 int     essid_compat = 0;
752
753                 /* Calculate space needed by arguments. Always allocate
754                  * for max space. Easier, and won't last long... */
755                 extra_size = descr->max_tokens * descr->token_size;
756
757                 /* Check need for ESSID compatibility for WE < 21 */
758                 switch (cmd) {
759                 case SIOCSIWESSID:
760                 case SIOCGIWESSID:
761                 case SIOCSIWNICKN:
762                 case SIOCGIWNICKN:
763                         if (iwr->u.data.length == descr->max_tokens + 1)
764                                 essid_compat = 1;
765                         else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
766                                 char essid[IW_ESSID_MAX_SIZE + 1];
767
768                                 err = copy_from_user(essid, iwr->u.data.pointer,
769                                                      iwr->u.data.length *
770                                                      descr->token_size);
771                                 if (err)
772                                         return -EFAULT;
773
774                                 if (essid[iwr->u.data.length - 1] == '\0')
775                                         essid_compat = 1;
776                         }
777                         break;
778                 default:
779                         break;
780                 }
781
782                 iwr->u.data.length -= essid_compat;
783
784                 /* Check what user space is giving us */
785                 if(IW_IS_SET(cmd)) {
786                         /* Check NULL pointer */
787                         if((iwr->u.data.pointer == NULL) &&
788                            (iwr->u.data.length != 0))
789                                 return -EFAULT;
790                         /* Check if number of token fits within bounds */
791                         if(iwr->u.data.length > descr->max_tokens)
792                                 return -E2BIG;
793                         if(iwr->u.data.length < descr->min_tokens)
794                                 return -EINVAL;
795                 } else {
796                         /* Check NULL pointer */
797                         if(iwr->u.data.pointer == NULL)
798                                 return -EFAULT;
799                         /* Save user space buffer size for checking */
800                         user_length = iwr->u.data.length;
801
802                         /* Don't check if user_length > max to allow forward
803                          * compatibility. The test user_length < min is
804                          * implied by the test at the end. */
805
806                         /* Support for very large requests */
807                         if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
808                            (user_length > descr->max_tokens)) {
809                                 /* Allow userspace to GET more than max so
810                                  * we can support any size GET requests.
811                                  * There is still a limit : -ENOMEM. */
812                                 extra_size = user_length * descr->token_size;
813                                 /* Note : user_length is originally a __u16,
814                                  * and token_size is controlled by us,
815                                  * so extra_size won't get negative and
816                                  * won't overflow... */
817                         }
818                 }
819
820 #ifdef WE_IOCTL_DEBUG
821                 printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
822                        dev->name, extra_size);
823 #endif  /* WE_IOCTL_DEBUG */
824
825                 /* Create the kernel buffer */
826                 /*    kzalloc ensures NULL-termination for essid_compat */
827                 extra = kzalloc(extra_size, GFP_KERNEL);
828                 if (extra == NULL) {
829                         return -ENOMEM;
830                 }
831
832                 /* If it is a SET, get all the extra data in here */
833                 if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
834                         err = copy_from_user(extra, iwr->u.data.pointer,
835                                              iwr->u.data.length *
836                                              descr->token_size);
837                         if (err) {
838                                 kfree(extra);
839                                 return -EFAULT;
840                         }
841 #ifdef WE_IOCTL_DEBUG
842                         printk(KERN_DEBUG "%s (WE) : Got %d bytes\n",
843                                dev->name,
844                                iwr->u.data.length * descr->token_size);
845 #endif  /* WE_IOCTL_DEBUG */
846                 }
847
848                 /* Call the handler */
849                 ret = handler(dev, &info, &(iwr->u), extra);
850
851                 iwr->u.data.length += essid_compat;
852
853                 /* If we have something to return to the user */
854                 if (!ret && IW_IS_GET(cmd)) {
855                         /* Check if there is enough buffer up there */
856                         if(user_length < iwr->u.data.length) {
857                                 kfree(extra);
858                                 return -E2BIG;
859                         }
860
861                         err = copy_to_user(iwr->u.data.pointer, extra,
862                                            iwr->u.data.length *
863                                            descr->token_size);
864                         if (err)
865                                 ret =  -EFAULT;                            
866 #ifdef WE_IOCTL_DEBUG
867                         printk(KERN_DEBUG "%s (WE) : Wrote %d bytes\n",
868                                dev->name,
869                                iwr->u.data.length * descr->token_size);
870 #endif  /* WE_IOCTL_DEBUG */
871                 }
872
873 #ifdef WE_SET_EVENT
874                 /* Generate an event to notify listeners of the change */
875                 if((descr->flags & IW_DESCR_FLAG_EVENT) &&
876                    ((ret == 0) || (ret == -EIWCOMMIT))) {
877                         if(descr->flags & IW_DESCR_FLAG_RESTRICT)
878                                 /* If the event is restricted, don't
879                                  * export the payload */
880                                 wireless_send_event(dev, cmd, &(iwr->u), NULL);
881                         else
882                                 wireless_send_event(dev, cmd, &(iwr->u),
883                                                     extra);
884                 }
885 #endif  /* WE_SET_EVENT */
886
887                 /* Cleanup - I told you it wasn't that long ;-) */
888                 kfree(extra);
889         }
890
891         /* Call commit handler if needed and defined */
892         if(ret == -EIWCOMMIT)
893                 ret = call_commit_handler(dev);
894
895         /* Here, we will generate the appropriate event if needed */
896
897         return ret;
898 }
899
900 /* ---------------------------------------------------------------- */
901 /*
902  * Wrapper to call a private Wireless Extension handler.
903  * We do various checks and also take care of moving data between
904  * user space and kernel space.
905  * It's not as nice and slimline as the standard wrapper. The cause
906  * is struct iw_priv_args, which was not really designed for the
907  * job we are going here.
908  *
909  * IMPORTANT : This function prevent to set and get data on the same
910  * IOCTL and enforce the SET/GET convention. Not doing it would be
911  * far too hairy...
912  * If you need to set and get data at the same time, please don't use
913  * a iw_handler but process it in your ioctl handler (i.e. use the
914  * old driver API).
915  */
916 static inline int ioctl_private_call(struct net_device *        dev,
917                                      struct ifreq *             ifr,
918                                      unsigned int               cmd,
919                                      iw_handler         handler)
920 {
921         struct iwreq *                  iwr = (struct iwreq *) ifr;
922         const struct iw_priv_args *     descr = NULL;
923         struct iw_request_info          info;
924         int                             extra_size = 0;
925         int                             i;
926         int                             ret = -EINVAL;
927
928         /* Get the description of the IOCTL */
929         for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
930                 if(cmd == dev->wireless_handlers->private_args[i].cmd) {
931                         descr = &(dev->wireless_handlers->private_args[i]);
932                         break;
933                 }
934
935 #ifdef WE_IOCTL_DEBUG
936         printk(KERN_DEBUG "%s (WE) : Found private handler for 0x%04X\n",
937                ifr->ifr_name, cmd);
938         if(descr) {
939                 printk(KERN_DEBUG "%s (WE) : Name %s, set %X, get %X\n",
940                        dev->name, descr->name,
941                        descr->set_args, descr->get_args);
942         }
943 #endif  /* WE_IOCTL_DEBUG */
944
945         /* Compute the size of the set/get arguments */
946         if(descr != NULL) {
947                 if(IW_IS_SET(cmd)) {
948                         int     offset = 0;     /* For sub-ioctls */
949                         /* Check for sub-ioctl handler */
950                         if(descr->name[0] == '\0')
951                                 /* Reserve one int for sub-ioctl index */
952                                 offset = sizeof(__u32);
953
954                         /* Size of set arguments */
955                         extra_size = get_priv_size(descr->set_args);
956
957                         /* Does it fits in iwr ? */
958                         if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
959                            ((extra_size + offset) <= IFNAMSIZ))
960                                 extra_size = 0;
961                 } else {
962                         /* Size of get arguments */
963                         extra_size = get_priv_size(descr->get_args);
964
965                         /* Does it fits in iwr ? */
966                         if((descr->get_args & IW_PRIV_SIZE_FIXED) &&
967                            (extra_size <= IFNAMSIZ))
968                                 extra_size = 0;
969                 }
970         }
971
972         /* Prepare the call */
973         info.cmd = cmd;
974         info.flags = 0;
975
976         /* Check if we have a pointer to user space data or not. */
977         if(extra_size == 0) {
978                 /* No extra arguments. Trivial to handle */
979                 ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));
980         } else {
981                 char *  extra;
982                 int     err;
983
984                 /* Check what user space is giving us */
985                 if(IW_IS_SET(cmd)) {
986                         /* Check NULL pointer */
987                         if((iwr->u.data.pointer == NULL) &&
988                            (iwr->u.data.length != 0))
989                                 return -EFAULT;
990
991                         /* Does it fits within bounds ? */
992                         if(iwr->u.data.length > (descr->set_args &
993                                                  IW_PRIV_SIZE_MASK))
994                                 return -E2BIG;
995                 } else {
996                         /* Check NULL pointer */
997                         if(iwr->u.data.pointer == NULL)
998                                 return -EFAULT;
999                 }
1000
1001 #ifdef WE_IOCTL_DEBUG
1002                 printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
1003                        dev->name, extra_size);
1004 #endif  /* WE_IOCTL_DEBUG */
1005
1006                 /* Always allocate for max space. Easier, and won't last
1007                  * long... */
1008                 extra = kmalloc(extra_size, GFP_KERNEL);
1009                 if (extra == NULL) {
1010                         return -ENOMEM;
1011                 }
1012
1013                 /* If it is a SET, get all the extra data in here */
1014                 if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
1015                         err = copy_from_user(extra, iwr->u.data.pointer,
1016                                              extra_size);
1017                         if (err) {
1018                                 kfree(extra);
1019                                 return -EFAULT;
1020                         }
1021 #ifdef WE_IOCTL_DEBUG
1022                         printk(KERN_DEBUG "%s (WE) : Got %d elem\n",
1023                                dev->name, iwr->u.data.length);
1024 #endif  /* WE_IOCTL_DEBUG */
1025                 }
1026
1027                 /* Call the handler */
1028                 ret = handler(dev, &info, &(iwr->u), extra);
1029
1030                 /* If we have something to return to the user */
1031                 if (!ret && IW_IS_GET(cmd)) {
1032
1033                         /* Adjust for the actual length if it's variable,
1034                          * avoid leaking kernel bits outside. */
1035                         if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) {
1036                                 extra_size = adjust_priv_size(descr->get_args,
1037                                                               &(iwr->u));
1038                         }
1039
1040                         err = copy_to_user(iwr->u.data.pointer, extra,
1041                                            extra_size);
1042                         if (err)
1043                                 ret =  -EFAULT;                            
1044 #ifdef WE_IOCTL_DEBUG
1045                         printk(KERN_DEBUG "%s (WE) : Wrote %d elem\n",
1046                                dev->name, iwr->u.data.length);
1047 #endif  /* WE_IOCTL_DEBUG */
1048                 }
1049
1050                 /* Cleanup - I told you it wasn't that long ;-) */
1051                 kfree(extra);
1052         }
1053
1054
1055         /* Call commit handler if needed and defined */
1056         if(ret == -EIWCOMMIT)
1057                 ret = call_commit_handler(dev);
1058
1059         return ret;
1060 }
1061
1062 /* ---------------------------------------------------------------- */
1063 /*
1064  * Main IOCTl dispatcher. Called from the main networking code
1065  * (dev_ioctl() in net/core/dev.c).
1066  * Check the type of IOCTL and call the appropriate wrapper...
1067  */
1068 int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
1069 {
1070         struct net_device *dev;
1071         iw_handler      handler;
1072
1073         /* Permissions are already checked in dev_ioctl() before calling us.
1074          * The copy_to/from_user() of ifr is also dealt with in there */
1075
1076         /* Make sure the device exist */
1077         if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL)
1078                 return -ENODEV;
1079
1080         /* A bunch of special cases, then the generic case...
1081          * Note that 'cmd' is already filtered in dev_ioctl() with
1082          * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
1083         switch(cmd) 
1084         {
1085                 case SIOCGIWSTATS:
1086                         /* Get Wireless Stats */
1087                         return ioctl_standard_call(dev,
1088                                                    ifr,
1089                                                    cmd,
1090                                                    &iw_handler_get_iwstats);
1091
1092                 case SIOCGIWPRIV:
1093                         /* Check if we have some wireless handlers defined */
1094                         if(dev->wireless_handlers != NULL) {
1095                                 /* We export to user space the definition of
1096                                  * the private handler ourselves */
1097                                 return ioctl_standard_call(dev,
1098                                                            ifr,
1099                                                            cmd,
1100                                                            &iw_handler_get_private);
1101                         }
1102                         // ## Fall-through for old API ##
1103                 default:
1104                         /* Generic IOCTL */
1105                         /* Basic check */
1106                         if (!netif_device_present(dev))
1107                                 return -ENODEV;
1108                         /* New driver API : try to find the handler */
1109                         handler = get_handler(dev, cmd);
1110                         if(handler != NULL) {
1111                                 /* Standard and private are not the same */
1112                                 if(cmd < SIOCIWFIRSTPRIV)
1113                                         return ioctl_standard_call(dev,
1114                                                                    ifr,
1115                                                                    cmd,
1116                                                                    handler);
1117                                 else
1118                                         return ioctl_private_call(dev,
1119                                                                   ifr,
1120                                                                   cmd,
1121                                                                   handler);
1122                         }
1123                         /* Old driver API : call driver ioctl handler */
1124                         if (dev->do_ioctl) {
1125                                 return dev->do_ioctl(dev, ifr, cmd);
1126                         }
1127                         return -EOPNOTSUPP;
1128         }
1129         /* Not reached */
1130         return -EINVAL;
1131 }
1132
1133 /********************** RTNETLINK REQUEST API **********************/
1134 /*
1135  * The alternate user space API to configure all those Wireless Extensions
1136  * is through RtNetlink.
1137  * This API support only the new driver API (iw_handler).
1138  *
1139  * This RtNetlink API use the same query/reply model as the ioctl API.
1140  * Maximum effort has been done to fit in the RtNetlink model, and
1141  * we support both RtNetlink Set and RtNelink Get operations.
1142  * On the other hand, we don't offer Dump operations because of the
1143  * following reasons :
1144  *      o Large number of parameters, most optional
1145  *      o Large size of some parameters (> 100 bytes)
1146  *      o Each parameters need to be extracted from hardware
1147  *      o Scan requests can take seconds and disable network activity.
1148  * Because of this high cost/overhead, we want to return only the
1149  * parameters the user application is really interested in.
1150  * We could offer partial Dump using the IW_DESCR_FLAG_DUMP flag.
1151  *
1152  * The API uses the standard RtNetlink socket. When the RtNetlink code
1153  * find a IFLA_WIRELESS field in a RtNetlink SET_LINK request,
1154  * it calls here.
1155  */
1156
1157 #ifdef CONFIG_NET_WIRELESS_RTNETLINK
1158 /* ---------------------------------------------------------------- */
1159 /*
1160  * Wrapper to call a standard Wireless Extension GET handler.
1161  * We do various checks and call the handler with the proper args.
1162  */
1163 static int rtnetlink_standard_get(struct net_device *   dev,
1164                                   struct iw_event *     request,
1165                                   int                   request_len,
1166                                   iw_handler            handler,
1167                                   char **               p_buf,
1168                                   int *                 p_len)
1169 {
1170         const struct iw_ioctl_description *     descr = NULL;
1171         unsigned int                            cmd;
1172         union iwreq_data *                      wrqu;
1173         int                                     hdr_len;
1174         struct iw_request_info                  info;
1175         char *                                  buffer = NULL;
1176         int                                     buffer_size = 0;
1177         int                                     ret = -EINVAL;
1178
1179         /* Get the description of the Request */
1180         cmd = request->cmd;
1181         if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
1182                 return -EOPNOTSUPP;
1183         descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
1184
1185 #ifdef WE_RTNETLINK_DEBUG
1186         printk(KERN_DEBUG "%s (WE.r) : Found standard handler for 0x%04X\n",
1187                dev->name, cmd);
1188         printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
1189 #endif  /* WE_RTNETLINK_DEBUG */
1190
1191         /* Check if wrqu is complete */
1192         hdr_len = event_type_size[descr->header_type];
1193         if(request_len < hdr_len) {
1194 #ifdef WE_RTNETLINK_DEBUG
1195                 printk(KERN_DEBUG
1196                        "%s (WE.r) : Wireless request too short (%d)\n",
1197                        dev->name, request_len);
1198 #endif  /* WE_RTNETLINK_DEBUG */
1199                 return -EINVAL;
1200         }
1201
1202         /* Prepare the call */
1203         info.cmd = cmd;
1204         info.flags = 0;
1205
1206         /* Check if we have extra data in the reply or not */
1207         if(descr->header_type != IW_HEADER_TYPE_POINT) {
1208
1209                 /* Create the kernel buffer that we will return.
1210                  * It's at an offset to match the TYPE_POINT case... */
1211                 buffer_size = request_len + IW_EV_POINT_OFF;
1212                 buffer = kmalloc(buffer_size, GFP_KERNEL);
1213                 if (buffer == NULL) {
1214                         return -ENOMEM;
1215                 }
1216                 /* Copy event data */
1217                 memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
1218                 /* Use our own copy of wrqu */
1219                 wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
1220                                              + IW_EV_LCP_LEN);
1221
1222                 /* No extra arguments. Trivial to handle */
1223                 ret = handler(dev, &info, wrqu, NULL);
1224
1225         } else {
1226                 union iwreq_data        wrqu_point;
1227                 char *                  extra = NULL;
1228                 int                     extra_size = 0;
1229
1230                 /* Get a temp copy of wrqu (skip pointer) */
1231                 memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
1232                        ((char *) request) + IW_EV_LCP_LEN,
1233                        IW_EV_POINT_LEN - IW_EV_LCP_LEN);
1234
1235                 /* Calculate space needed by arguments. Always allocate
1236                  * for max space. Easier, and won't last long... */
1237                 extra_size = descr->max_tokens * descr->token_size;
1238                 /* Support for very large requests */
1239                 if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
1240                    (wrqu_point.data.length > descr->max_tokens))
1241                         extra_size = (wrqu_point.data.length
1242                                       * descr->token_size);
1243                 buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
1244 #ifdef WE_RTNETLINK_DEBUG
1245                 printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
1246                        dev->name, extra_size, buffer_size);
1247 #endif  /* WE_RTNETLINK_DEBUG */
1248
1249                 /* Create the kernel buffer that we will return */
1250                 buffer = kmalloc(buffer_size, GFP_KERNEL);
1251                 if (buffer == NULL) {
1252                         return -ENOMEM;
1253                 }
1254
1255                 /* Put wrqu in the right place (just before extra).
1256                  * Leave space for IWE header and dummy pointer...
1257                  * Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
1258                  */
1259                 memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
1260                        ((char *) &wrqu_point) + IW_EV_POINT_OFF,
1261                        IW_EV_POINT_LEN - IW_EV_LCP_LEN);
1262                 wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
1263
1264                 /* Extra comes logically after that. Offset +12 bytes. */
1265                 extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
1266
1267                 /* Call the handler */
1268                 ret = handler(dev, &info, wrqu, extra);
1269
1270                 /* Calculate real returned length */
1271                 extra_size = (wrqu->data.length * descr->token_size);
1272                 /* Re-adjust reply size */
1273                 request->len = extra_size + IW_EV_POINT_LEN;
1274
1275                 /* Put the iwe header where it should, i.e. scrap the
1276                  * dummy pointer. */
1277                 memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
1278
1279 #ifdef WE_RTNETLINK_DEBUG
1280                 printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
1281 #endif  /* WE_RTNETLINK_DEBUG */
1282
1283                 /* Check if there is enough buffer up there */
1284                 if(wrqu_point.data.length < wrqu->data.length)
1285                         ret = -E2BIG;
1286         }
1287
1288         /* Return the buffer to the caller */
1289         if (!ret) {
1290                 *p_buf = buffer;
1291                 *p_len = request->len;
1292         } else {
1293                 /* Cleanup */
1294                 if(buffer)
1295                         kfree(buffer);
1296         }
1297
1298         return ret;
1299 }
1300
1301 /* ---------------------------------------------------------------- */
1302 /*
1303  * Wrapper to call a standard Wireless Extension SET handler.
1304  * We do various checks and call the handler with the proper args.
1305  */
1306 static inline int rtnetlink_standard_set(struct net_device *    dev,
1307                                          struct iw_event *      request,
1308                                          int                    request_len,
1309                                          iw_handler             handler)
1310 {
1311         const struct iw_ioctl_description *     descr = NULL;
1312         unsigned int                            cmd;
1313         union iwreq_data *                      wrqu;
1314         union iwreq_data                        wrqu_point;
1315         int                                     hdr_len;
1316         char *                                  extra = NULL;
1317         int                                     extra_size = 0;
1318         struct iw_request_info                  info;
1319         int                                     ret = -EINVAL;
1320
1321         /* Get the description of the Request */
1322         cmd = request->cmd;
1323         if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
1324                 return -EOPNOTSUPP;
1325         descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
1326
1327 #ifdef WE_RTNETLINK_DEBUG
1328         printk(KERN_DEBUG "%s (WE.r) : Found standard SET handler for 0x%04X\n",
1329                dev->name, cmd);
1330         printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
1331 #endif  /* WE_RTNETLINK_DEBUG */
1332
1333         /* Extract fixed header from request. This is properly aligned. */
1334         wrqu = &request->u;
1335
1336         /* Check if wrqu is complete */
1337         hdr_len = event_type_size[descr->header_type];
1338         if(request_len < hdr_len) {
1339 #ifdef WE_RTNETLINK_DEBUG
1340                 printk(KERN_DEBUG
1341                        "%s (WE.r) : Wireless request too short (%d)\n",
1342                        dev->name, request_len);
1343 #endif  /* WE_RTNETLINK_DEBUG */
1344                 return -EINVAL;
1345         }
1346
1347         /* Prepare the call */
1348         info.cmd = cmd;
1349         info.flags = 0;
1350
1351         /* Check if we have extra data in the request or not */
1352         if(descr->header_type != IW_HEADER_TYPE_POINT) {
1353
1354                 /* No extra arguments. Trivial to handle */
1355                 ret = handler(dev, &info, wrqu, NULL);
1356
1357         } else {
1358                 int     extra_len;
1359
1360                 /* Put wrqu in the right place (skip pointer) */
1361                 memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
1362                        wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
1363                 /* Don't forget about the event code... */
1364                 wrqu = &wrqu_point;
1365
1366                 /* Check if number of token fits within bounds */
1367                 if(wrqu_point.data.length > descr->max_tokens)
1368                         return -E2BIG;
1369                 if(wrqu_point.data.length < descr->min_tokens)
1370                         return -EINVAL;
1371
1372                 /* Real length of payload */
1373                 extra_len = wrqu_point.data.length * descr->token_size;
1374
1375                 /* Check if request is self consistent */
1376                 if((request_len - hdr_len) < extra_len) {
1377 #ifdef WE_RTNETLINK_DEBUG
1378                         printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
1379                                dev->name, extra_size);
1380 #endif  /* WE_RTNETLINK_DEBUG */
1381                         return -EINVAL;
1382                 }
1383
1384 #ifdef WE_RTNETLINK_DEBUG
1385                 printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
1386                        dev->name, extra_size);
1387 #endif  /* WE_RTNETLINK_DEBUG */
1388
1389                 /* Always allocate for max space. Easier, and won't last
1390                  * long... */
1391                 extra_size = descr->max_tokens * descr->token_size;
1392                 extra = kmalloc(extra_size, GFP_KERNEL);
1393                 if (extra == NULL)
1394                         return -ENOMEM;
1395
1396                 /* Copy extra in aligned buffer */
1397                 memcpy(extra, ((char *) request) + hdr_len, extra_len);
1398
1399                 /* Call the handler */
1400                 ret = handler(dev, &info, &wrqu_point, extra);
1401         }
1402
1403 #ifdef WE_SET_EVENT
1404         /* Generate an event to notify listeners of the change */
1405         if((descr->flags & IW_DESCR_FLAG_EVENT) &&
1406            ((ret == 0) || (ret == -EIWCOMMIT))) {
1407                 if(descr->flags & IW_DESCR_FLAG_RESTRICT)
1408                         /* If the event is restricted, don't
1409                          * export the payload */
1410                         wireless_send_event(dev, cmd, wrqu, NULL);
1411                 else
1412                         wireless_send_event(dev, cmd, wrqu, extra);
1413         }
1414 #endif  /* WE_SET_EVENT */
1415
1416         /* Cleanup - I told you it wasn't that long ;-) */
1417         if(extra)
1418                 kfree(extra);
1419
1420         /* Call commit handler if needed and defined */
1421         if(ret == -EIWCOMMIT)
1422                 ret = call_commit_handler(dev);
1423
1424         return ret;
1425 }
1426
1427 /* ---------------------------------------------------------------- */
1428 /*
1429  * Wrapper to call a private Wireless Extension GET handler.
1430  * Same as above...
1431  * It's not as nice and slimline as the standard wrapper. The cause
1432  * is struct iw_priv_args, which was not really designed for the
1433  * job we are going here.
1434  *
1435  * IMPORTANT : This function prevent to set and get data on the same
1436  * IOCTL and enforce the SET/GET convention. Not doing it would be
1437  * far too hairy...
1438  * If you need to set and get data at the same time, please don't use
1439  * a iw_handler but process it in your ioctl handler (i.e. use the
1440  * old driver API).
1441  */
1442 static inline int rtnetlink_private_get(struct net_device *     dev,
1443                                         struct iw_event *       request,
1444                                         int                     request_len,
1445                                         iw_handler              handler,
1446                                         char **                 p_buf,
1447                                         int *                   p_len)
1448 {
1449         const struct iw_priv_args *     descr = NULL;
1450         unsigned int                    cmd;
1451         union iwreq_data *              wrqu;
1452         int                             hdr_len;
1453         struct iw_request_info          info;
1454         int                             extra_size = 0;
1455         int                             i;
1456         char *                          buffer = NULL;
1457         int                             buffer_size = 0;
1458         int                             ret = -EINVAL;
1459
1460         /* Get the description of the Request */
1461         cmd = request->cmd;
1462         for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
1463                 if(cmd == dev->wireless_handlers->private_args[i].cmd) {
1464                         descr = &(dev->wireless_handlers->private_args[i]);
1465                         break;
1466                 }
1467         if(descr == NULL)
1468                 return -EOPNOTSUPP;
1469
1470 #ifdef WE_RTNETLINK_DEBUG
1471         printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
1472                dev->name, cmd);
1473         printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
1474                dev->name, descr->name, descr->set_args, descr->get_args);
1475 #endif  /* WE_RTNETLINK_DEBUG */
1476
1477         /* Compute the max size of the get arguments */
1478         extra_size = get_priv_size(descr->get_args);
1479
1480         /* Does it fits in wrqu ? */
1481         if((descr->get_args & IW_PRIV_SIZE_FIXED) &&
1482            (extra_size <= IFNAMSIZ)) {
1483                 hdr_len = extra_size;
1484                 extra_size = 0;
1485         } else {
1486                 hdr_len = IW_EV_POINT_LEN;
1487         }
1488
1489         /* Check if wrqu is complete */
1490         if(request_len < hdr_len) {
1491 #ifdef WE_RTNETLINK_DEBUG
1492                 printk(KERN_DEBUG
1493                        "%s (WE.r) : Wireless request too short (%d)\n",
1494                        dev->name, request_len);
1495 #endif  /* WE_RTNETLINK_DEBUG */
1496                 return -EINVAL;
1497         }
1498
1499         /* Prepare the call */
1500         info.cmd = cmd;
1501         info.flags = 0;
1502
1503         /* Check if we have a pointer to user space data or not. */
1504         if(extra_size == 0) {
1505
1506                 /* Create the kernel buffer that we will return.
1507                  * It's at an offset to match the TYPE_POINT case... */
1508                 buffer_size = request_len + IW_EV_POINT_OFF;
1509                 buffer = kmalloc(buffer_size, GFP_KERNEL);
1510                 if (buffer == NULL) {
1511                         return -ENOMEM;
1512                 }
1513                 /* Copy event data */
1514                 memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
1515                 /* Use our own copy of wrqu */
1516                 wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
1517                                              + IW_EV_LCP_LEN);
1518
1519                 /* No extra arguments. Trivial to handle */
1520                 ret = handler(dev, &info, wrqu, (char *) wrqu);
1521
1522         } else {
1523                 char *  extra;
1524
1525                 /* Buffer for full reply */
1526                 buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
1527
1528 #ifdef WE_RTNETLINK_DEBUG
1529                 printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
1530                        dev->name, extra_size, buffer_size);
1531 #endif  /* WE_RTNETLINK_DEBUG */
1532
1533                 /* Create the kernel buffer that we will return */
1534                 buffer = kmalloc(buffer_size, GFP_KERNEL);
1535                 if (buffer == NULL) {
1536                         return -ENOMEM;
1537                 }
1538
1539                 /* Put wrqu in the right place (just before extra).
1540                  * Leave space for IWE header and dummy pointer...
1541                  * Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
1542                  */
1543                 memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
1544                        ((char *) request) + IW_EV_LCP_LEN,
1545                        IW_EV_POINT_LEN - IW_EV_LCP_LEN);
1546                 wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
1547
1548                 /* Extra comes logically after that. Offset +12 bytes. */
1549                 extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
1550
1551                 /* Call the handler */
1552                 ret = handler(dev, &info, wrqu, extra);
1553
1554                 /* Adjust for the actual length if it's variable,
1555                  * avoid leaking kernel bits outside. */
1556                 if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
1557                         extra_size = adjust_priv_size(descr->get_args, wrqu);
1558                 /* Re-adjust reply size */
1559                 request->len = extra_size + IW_EV_POINT_LEN;
1560
1561                 /* Put the iwe header where it should, i.e. scrap the
1562                  * dummy pointer. */
1563                 memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
1564
1565 #ifdef WE_RTNETLINK_DEBUG
1566                 printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
1567 #endif  /* WE_RTNETLINK_DEBUG */
1568         }
1569
1570         /* Return the buffer to the caller */
1571         if (!ret) {
1572                 *p_buf = buffer;
1573                 *p_len = request->len;
1574         } else {
1575                 /* Cleanup */
1576                 if(buffer)
1577                         kfree(buffer);
1578         }
1579
1580         return ret;
1581 }
1582
1583 /* ---------------------------------------------------------------- */
1584 /*
1585  * Wrapper to call a private Wireless Extension SET handler.
1586  * Same as above...
1587  * It's not as nice and slimline as the standard wrapper. The cause
1588  * is struct iw_priv_args, which was not really designed for the
1589  * job we are going here.
1590  *
1591  * IMPORTANT : This function prevent to set and get data on the same
1592  * IOCTL and enforce the SET/GET convention. Not doing it would be
1593  * far too hairy...
1594  * If you need to set and get data at the same time, please don't use
1595  * a iw_handler but process it in your ioctl handler (i.e. use the
1596  * old driver API).
1597  */
1598 static inline int rtnetlink_private_set(struct net_device *     dev,
1599                                         struct iw_event *       request,
1600                                         int                     request_len,
1601                                         iw_handler              handler)
1602 {
1603         const struct iw_priv_args *     descr = NULL;
1604         unsigned int                    cmd;
1605         union iwreq_data *              wrqu;
1606         union iwreq_data                wrqu_point;
1607         int                             hdr_len;
1608         char *                          extra = NULL;
1609         int                             extra_size = 0;
1610         int                             offset = 0;     /* For sub-ioctls */
1611         struct iw_request_info          info;
1612         int                             i;
1613         int                             ret = -EINVAL;
1614
1615         /* Get the description of the Request */
1616         cmd = request->cmd;
1617         for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
1618                 if(cmd == dev->wireless_handlers->private_args[i].cmd) {
1619                         descr = &(dev->wireless_handlers->private_args[i]);
1620                         break;
1621                 }
1622         if(descr == NULL)
1623                 return -EOPNOTSUPP;
1624
1625 #ifdef WE_RTNETLINK_DEBUG
1626         printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
1627                ifr->ifr_name, cmd);
1628         printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
1629                dev->name, descr->name, descr->set_args, descr->get_args);
1630 #endif  /* WE_RTNETLINK_DEBUG */
1631
1632         /* Compute the size of the set arguments */
1633         /* Check for sub-ioctl handler */
1634         if(descr->name[0] == '\0')
1635                 /* Reserve one int for sub-ioctl index */
1636                 offset = sizeof(__u32);
1637
1638         /* Size of set arguments */
1639         extra_size = get_priv_size(descr->set_args);
1640
1641         /* Does it fits in wrqu ? */
1642         if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
1643            (extra_size <= IFNAMSIZ)) {
1644                 hdr_len = IW_EV_LCP_LEN + extra_size;
1645                 extra_size = 0;
1646         } else {
1647                 hdr_len = IW_EV_POINT_LEN;
1648         }
1649
1650         /* Extract fixed header from request. This is properly aligned. */
1651         wrqu = &request->u;
1652
1653         /* Check if wrqu is complete */
1654         if(request_len < hdr_len) {
1655 #ifdef WE_RTNETLINK_DEBUG
1656                 printk(KERN_DEBUG
1657                        "%s (WE.r) : Wireless request too short (%d)\n",
1658                        dev->name, request_len);
1659 #endif  /* WE_RTNETLINK_DEBUG */
1660                 return -EINVAL;
1661         }
1662
1663         /* Prepare the call */
1664         info.cmd = cmd;
1665         info.flags = 0;
1666
1667         /* Check if we have a pointer to user space data or not. */
1668         if(extra_size == 0) {
1669
1670                 /* No extra arguments. Trivial to handle */
1671                 ret = handler(dev, &info, wrqu, (char *) wrqu);
1672
1673         } else {
1674                 int     extra_len;
1675
1676                 /* Put wrqu in the right place (skip pointer) */
1677                 memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
1678                        wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
1679
1680                 /* Does it fits within bounds ? */
1681                 if(wrqu_point.data.length > (descr->set_args &
1682                                              IW_PRIV_SIZE_MASK))
1683                         return -E2BIG;
1684
1685                 /* Real length of payload */
1686                 extra_len = adjust_priv_size(descr->set_args, &wrqu_point);
1687
1688                 /* Check if request is self consistent */
1689                 if((request_len - hdr_len) < extra_len) {
1690 #ifdef WE_RTNETLINK_DEBUG
1691                         printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
1692                                dev->name, extra_size);
1693 #endif  /* WE_RTNETLINK_DEBUG */
1694                         return -EINVAL;
1695                 }
1696
1697 #ifdef WE_RTNETLINK_DEBUG
1698                 printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
1699                        dev->name, extra_size);
1700 #endif  /* WE_RTNETLINK_DEBUG */
1701
1702                 /* Always allocate for max space. Easier, and won't last
1703                  * long... */
1704                 extra = kmalloc(extra_size, GFP_KERNEL);
1705                 if (extra == NULL)
1706                         return -ENOMEM;
1707
1708                 /* Copy extra in aligned buffer */
1709                 memcpy(extra, ((char *) request) + hdr_len, extra_len);
1710
1711                 /* Call the handler */
1712                 ret = handler(dev, &info, &wrqu_point, extra);
1713
1714                 /* Cleanup - I told you it wasn't that long ;-) */
1715                 kfree(extra);
1716         }
1717
1718         /* Call commit handler if needed and defined */
1719         if(ret == -EIWCOMMIT)
1720                 ret = call_commit_handler(dev);
1721
1722         return ret;
1723 }
1724
1725 /* ---------------------------------------------------------------- */
1726 /*
1727  * Main RtNetlink dispatcher. Called from the main networking code
1728  * (do_getlink() in net/core/rtnetlink.c).
1729  * Check the type of Request and call the appropriate wrapper...
1730  */
1731 int wireless_rtnetlink_get(struct net_device *  dev,
1732                            char *               data,
1733                            int                  len,
1734                            char **              p_buf,
1735                            int *                p_len)
1736 {
1737         struct iw_event *       request = (struct iw_event *) data;
1738         iw_handler              handler;
1739
1740         /* Check length */
1741         if(len < IW_EV_LCP_LEN) {
1742                 printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
1743                        dev->name, len);
1744                 return -EINVAL;
1745         }
1746
1747         /* ReCheck length (len may have padding) */
1748         if(request->len > len) {
1749                 printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
1750                        dev->name, request->len, len);
1751                 return -EINVAL;
1752         }
1753
1754         /* Only accept GET requests in here */
1755         if(!IW_IS_GET(request->cmd))
1756                 return -EOPNOTSUPP;
1757
1758         /* If command is `get the encoding parameters', check if
1759          * the user has the right to do it */
1760         if (request->cmd == SIOCGIWENCODE ||
1761             request->cmd == SIOCGIWENCODEEXT) {
1762                 if (!capable(CAP_NET_ADMIN))
1763                         return -EPERM;
1764         }
1765
1766         /* Special cases */
1767         if(request->cmd == SIOCGIWSTATS)
1768                 /* Get Wireless Stats */
1769                 return rtnetlink_standard_get(dev,
1770                                               request,
1771                                               request->len,
1772                                               &iw_handler_get_iwstats,
1773                                               p_buf, p_len);
1774         if(request->cmd == SIOCGIWPRIV) {
1775                 /* Check if we have some wireless handlers defined */
1776                 if(dev->wireless_handlers == NULL)
1777                         return -EOPNOTSUPP;
1778                 /* Get Wireless Stats */
1779                 return rtnetlink_standard_get(dev,
1780                                               request,
1781                                               request->len,
1782                                               &iw_handler_get_private,
1783                                               p_buf, p_len);
1784         }
1785
1786         /* Basic check */
1787         if (!netif_device_present(dev))
1788                 return -ENODEV;
1789
1790         /* Try to find the handler */
1791         handler = get_handler(dev, request->cmd);
1792         if(handler != NULL) {
1793                 /* Standard and private are not the same */
1794                 if(request->cmd < SIOCIWFIRSTPRIV)
1795                         return rtnetlink_standard_get(dev,
1796                                                       request,
1797                                                       request->len,
1798                                                       handler,
1799                                                       p_buf, p_len);
1800                 else
1801                         return rtnetlink_private_get(dev,
1802                                                      request,
1803                                                      request->len,
1804                                                      handler,
1805                                                      p_buf, p_len);
1806         }
1807
1808         return -EOPNOTSUPP;
1809 }
1810
1811 /* ---------------------------------------------------------------- */
1812 /*
1813  * Main RtNetlink dispatcher. Called from the main networking code
1814  * (do_setlink() in net/core/rtnetlink.c).
1815  * Check the type of Request and call the appropriate wrapper...
1816  */
1817 int wireless_rtnetlink_set(struct net_device *  dev,
1818                            char *               data,
1819                            int                  len)
1820 {
1821         struct iw_event *       request = (struct iw_event *) data;
1822         iw_handler              handler;
1823
1824         /* Check length */
1825         if(len < IW_EV_LCP_LEN) {
1826                 printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
1827                        dev->name, len);
1828                 return -EINVAL;
1829         }
1830
1831         /* ReCheck length (len may have padding) */
1832         if(request->len > len) {
1833                 printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
1834                        dev->name, request->len, len);
1835                 return -EINVAL;
1836         }
1837
1838         /* Only accept SET requests in here */
1839         if(!IW_IS_SET(request->cmd))
1840                 return -EOPNOTSUPP;
1841
1842         /* Basic check */
1843         if (!netif_device_present(dev))
1844                 return -ENODEV;
1845
1846         /* New driver API : try to find the handler */
1847         handler = get_handler(dev, request->cmd);
1848         if(handler != NULL) {
1849                 /* Standard and private are not the same */
1850                 if(request->cmd < SIOCIWFIRSTPRIV)
1851                         return rtnetlink_standard_set(dev,
1852                                                       request,
1853                                                       request->len,
1854                                                       handler);
1855                 else
1856                         return rtnetlink_private_set(dev,
1857                                                      request,
1858                                                      request->len,
1859                                                      handler);
1860         }
1861
1862         return -EOPNOTSUPP;
1863 }
1864 #endif  /* CONFIG_NET_WIRELESS_RTNETLINK */
1865
1866
1867 /************************* EVENT PROCESSING *************************/
1868 /*
1869  * Process events generated by the wireless layer or the driver.
1870  * Most often, the event will be propagated through rtnetlink
1871  */
1872
1873 #ifdef WE_EVENT_RTNETLINK
1874 /* ---------------------------------------------------------------- */
1875 /*
1876  * Locking...
1877  * ----------
1878  *
1879  * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing
1880  * the locking issue in here and implementing this code !
1881  *
1882  * The issue : wireless_send_event() is often called in interrupt context,
1883  * while the Netlink layer can never be called in interrupt context.
1884  * The fully formed RtNetlink events are queued, and then a tasklet is run
1885  * to feed those to Netlink.
1886  * The skb_queue is interrupt safe, and its lock is not held while calling
1887  * Netlink, so there is no possibility of dealock.
1888  * Jean II
1889  */
1890
1891 static struct sk_buff_head wireless_nlevent_queue;
1892
1893 static int __init wireless_nlevent_init(void)
1894 {
1895         skb_queue_head_init(&wireless_nlevent_queue);
1896         return 0;
1897 }
1898
1899 subsys_initcall(wireless_nlevent_init);
1900
1901 static void wireless_nlevent_process(unsigned long data)
1902 {
1903         struct sk_buff *skb;
1904
1905         while ((skb = skb_dequeue(&wireless_nlevent_queue)))
1906                 rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
1907 }
1908
1909 static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);
1910
1911 /* ---------------------------------------------------------------- */
1912 /*
1913  * Fill a rtnetlink message with our event data.
1914  * Note that we propage only the specified event and don't dump the
1915  * current wireless config. Dumping the wireless config is far too
1916  * expensive (for each parameter, the driver need to query the hardware).
1917  */
1918 static inline int rtnetlink_fill_iwinfo(struct sk_buff *        skb,
1919                                         struct net_device *     dev,
1920                                         int                     type,
1921                                         char *                  event,
1922                                         int                     event_len)
1923 {
1924         struct ifinfomsg *r;
1925         struct nlmsghdr  *nlh;
1926         unsigned char    *b = skb->tail;
1927
1928         nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r));
1929         r = NLMSG_DATA(nlh);
1930         r->ifi_family = AF_UNSPEC;
1931         r->__ifi_pad = 0;
1932         r->ifi_type = dev->type;
1933         r->ifi_index = dev->ifindex;
1934         r->ifi_flags = dev_get_flags(dev);
1935         r->ifi_change = 0;      /* Wireless changes don't affect those flags */
1936
1937         /* Add the wireless events in the netlink packet */
1938         RTA_PUT(skb, IFLA_WIRELESS, event_len, event);
1939
1940         nlh->nlmsg_len = skb->tail - b;
1941         return skb->len;
1942
1943 nlmsg_failure:
1944 rtattr_failure:
1945         skb_trim(skb, b - skb->data);
1946         return -1;
1947 }
1948
1949 /* ---------------------------------------------------------------- */
1950 /*
1951  * Create and broadcast and send it on the standard rtnetlink socket
1952  * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c
1953  * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field
1954  * within a RTM_NEWLINK event.
1955  */
1956 static inline void rtmsg_iwinfo(struct net_device *     dev,
1957                                 char *                  event,
1958                                 int                     event_len)
1959 {
1960         struct sk_buff *skb;
1961         int size = NLMSG_GOODSIZE;
1962
1963         skb = alloc_skb(size, GFP_ATOMIC);
1964         if (!skb)
1965                 return;
1966
1967         if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK,
1968                                   event, event_len) < 0) {
1969                 kfree_skb(skb);
1970                 return;
1971         }
1972         NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
1973         skb_queue_tail(&wireless_nlevent_queue, skb);
1974         tasklet_schedule(&wireless_nlevent_tasklet);
1975 }
1976
1977 #endif  /* WE_EVENT_RTNETLINK */
1978
1979 /* ---------------------------------------------------------------- */
1980 /*
1981  * Main event dispatcher. Called from other parts and drivers.
1982  * Send the event on the appropriate channels.
1983  * May be called from interrupt context.
1984  */
1985 void wireless_send_event(struct net_device *    dev,
1986                          unsigned int           cmd,
1987                          union iwreq_data *     wrqu,
1988                          char *                 extra)
1989 {
1990         const struct iw_ioctl_description *     descr = NULL;
1991         int extra_len = 0;
1992         struct iw_event  *event;                /* Mallocated whole event */
1993         int event_len;                          /* Its size */
1994         int hdr_len;                            /* Size of the event header */
1995         int wrqu_off = 0;                       /* Offset in wrqu */
1996         /* Don't "optimise" the following variable, it will crash */
1997         unsigned        cmd_index;              /* *MUST* be unsigned */
1998
1999         /* Get the description of the Event */
2000         if(cmd <= SIOCIWLAST) {
2001                 cmd_index = cmd - SIOCIWFIRST;
2002                 if(cmd_index < standard_ioctl_num)
2003                         descr = &(standard_ioctl[cmd_index]);
2004         } else {
2005                 cmd_index = cmd - IWEVFIRST;
2006                 if(cmd_index < standard_event_num)
2007                         descr = &(standard_event[cmd_index]);
2008         }
2009         /* Don't accept unknown events */
2010         if(descr == NULL) {
2011                 /* Note : we don't return an error to the driver, because
2012                  * the driver would not know what to do about it. It can't
2013                  * return an error to the user, because the event is not
2014                  * initiated by a user request.
2015                  * The best the driver could do is to log an error message.
2016                  * We will do it ourselves instead...
2017                  */
2018                 printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n",
2019                        dev->name, cmd);
2020                 return;
2021         }
2022 #ifdef WE_EVENT_DEBUG
2023         printk(KERN_DEBUG "%s (WE) : Got event 0x%04X\n",
2024                dev->name, cmd);
2025         printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
2026 #endif  /* WE_EVENT_DEBUG */
2027
2028         /* Check extra parameters and set extra_len */
2029         if(descr->header_type == IW_HEADER_TYPE_POINT) {
2030                 /* Check if number of token fits within bounds */
2031                 if(wrqu->data.length > descr->max_tokens) {
2032                         printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length);
2033                         return;
2034                 }
2035                 if(wrqu->data.length < descr->min_tokens) {
2036                         printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length);
2037                         return;
2038                 }
2039                 /* Calculate extra_len - extra is NULL for restricted events */
2040                 if(extra != NULL)
2041                         extra_len = wrqu->data.length * descr->token_size;
2042                 /* Always at an offset in wrqu */
2043                 wrqu_off = IW_EV_POINT_OFF;
2044 #ifdef WE_EVENT_DEBUG
2045                 printk(KERN_DEBUG "%s (WE) : Event 0x%04X, tokens %d, extra_len %d\n", dev->name, cmd, wrqu->data.length, extra_len);
2046 #endif  /* WE_EVENT_DEBUG */
2047         }
2048
2049         /* Total length of the event */
2050         hdr_len = event_type_size[descr->header_type];
2051         event_len = hdr_len + extra_len;
2052
2053 #ifdef WE_EVENT_DEBUG
2054         printk(KERN_DEBUG "%s (WE) : Event 0x%04X, hdr_len %d, wrqu_off %d, event_len %d\n", dev->name, cmd, hdr_len, wrqu_off, event_len);
2055 #endif  /* WE_EVENT_DEBUG */
2056
2057         /* Create temporary buffer to hold the event */
2058         event = kmalloc(event_len, GFP_ATOMIC);
2059         if(event == NULL)
2060                 return;
2061
2062         /* Fill event */
2063         event->len = event_len;
2064         event->cmd = cmd;
2065         memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
2066         if(extra != NULL)
2067                 memcpy(((char *) event) + hdr_len, extra, extra_len);
2068
2069 #ifdef WE_EVENT_RTNETLINK
2070         /* Send via the RtNetlink event channel */
2071         rtmsg_iwinfo(dev, (char *) event, event_len);
2072 #endif  /* WE_EVENT_RTNETLINK */
2073
2074         /* Cleanup */
2075         kfree(event);
2076
2077         return;         /* Always success, I guess ;-) */
2078 }
2079
2080 /********************** ENHANCED IWSPY SUPPORT **********************/
2081 /*
2082  * In the old days, the driver was handling spy support all by itself.
2083  * Now, the driver can delegate this task to Wireless Extensions.
2084  * It needs to use those standard spy iw_handler in struct iw_handler_def,
2085  * push data to us via wireless_spy_update() and include struct iw_spy_data
2086  * in its private part (and export it in net_device->wireless_data->spy_data).
2087  * One of the main advantage of centralising spy support here is that
2088  * it becomes much easier to improve and extend it without having to touch
2089  * the drivers. One example is the addition of the Spy-Threshold events.
2090  */
2091
2092 /* ---------------------------------------------------------------- */
2093 /*
2094  * Return the pointer to the spy data in the driver.
2095  * Because this is called on the Rx path via wireless_spy_update(),
2096  * we want it to be efficient...
2097  */
2098 static inline struct iw_spy_data * get_spydata(struct net_device *dev)
2099 {
2100         /* This is the new way */
2101         if(dev->wireless_data)
2102                 return(dev->wireless_data->spy_data);
2103         return NULL;
2104 }
2105
2106 /*------------------------------------------------------------------*/
2107 /*
2108  * Standard Wireless Handler : set Spy List
2109  */
2110 int iw_handler_set_spy(struct net_device *      dev,
2111                        struct iw_request_info * info,
2112                        union iwreq_data *       wrqu,
2113                        char *                   extra)
2114 {
2115         struct iw_spy_data *    spydata = get_spydata(dev);
2116         struct sockaddr *       address = (struct sockaddr *) extra;
2117
2118         /* Make sure driver is not buggy or using the old API */
2119         if(!spydata)
2120                 return -EOPNOTSUPP;
2121
2122         /* Disable spy collection while we copy the addresses.
2123          * While we copy addresses, any call to wireless_spy_update()
2124          * will NOP. This is OK, as anyway the addresses are changing. */
2125         spydata->spy_number = 0;
2126
2127         /* We want to operate without locking, because wireless_spy_update()
2128          * most likely will happen in the interrupt handler, and therefore
2129          * have its own locking constraints and needs performance.
2130          * The rtnl_lock() make sure we don't race with the other iw_handlers.
2131          * This make sure wireless_spy_update() "see" that the spy list
2132          * is temporarily disabled. */
2133         wmb();
2134
2135         /* Are there are addresses to copy? */
2136         if(wrqu->data.length > 0) {
2137                 int i;
2138
2139                 /* Copy addresses */
2140                 for(i = 0; i < wrqu->data.length; i++)
2141                         memcpy(spydata->spy_address[i], address[i].sa_data,
2142                                ETH_ALEN);
2143                 /* Reset stats */
2144                 memset(spydata->spy_stat, 0,
2145                        sizeof(struct iw_quality) * IW_MAX_SPY);
2146
2147 #ifdef WE_SPY_DEBUG
2148                 printk(KERN_DEBUG "iw_handler_set_spy() :  wireless_data %p, spydata %p, num %d\n", dev->wireless_data, spydata, wrqu->data.length);
2149                 for (i = 0; i < wrqu->data.length; i++)
2150                         printk(KERN_DEBUG
2151                                "%02X:%02X:%02X:%02X:%02X:%02X \n",
2152                                spydata->spy_address[i][0],
2153                                spydata->spy_address[i][1],
2154                                spydata->spy_address[i][2],
2155                                spydata->spy_address[i][3],
2156                                spydata->spy_address[i][4],
2157                                spydata->spy_address[i][5]);
2158 #endif  /* WE_SPY_DEBUG */
2159         }
2160
2161         /* Make sure above is updated before re-enabling */
2162         wmb();
2163
2164         /* Enable addresses */
2165         spydata->spy_number = wrqu->data.length;
2166
2167         return 0;
2168 }
2169
2170 /*------------------------------------------------------------------*/
2171 /*
2172  * Standard Wireless Handler : get Spy List
2173  */
2174 int iw_handler_get_spy(struct net_device *      dev,
2175                        struct iw_request_info * info,
2176                        union iwreq_data *       wrqu,
2177                        char *                   extra)
2178 {
2179         struct iw_spy_data *    spydata = get_spydata(dev);
2180         struct sockaddr *       address = (struct sockaddr *) extra;
2181         int                     i;
2182
2183         /* Make sure driver is not buggy or using the old API */
2184         if(!spydata)
2185                 return -EOPNOTSUPP;
2186
2187         wrqu->data.length = spydata->spy_number;
2188
2189         /* Copy addresses. */
2190         for(i = 0; i < spydata->spy_number; i++)        {
2191                 memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
2192                 address[i].sa_family = AF_UNIX;
2193         }
2194         /* Copy stats to the user buffer (just after). */
2195         if(spydata->spy_number > 0)
2196                 memcpy(extra  + (sizeof(struct sockaddr) *spydata->spy_number),
2197                        spydata->spy_stat,
2198                        sizeof(struct iw_quality) * spydata->spy_number);
2199         /* Reset updated flags. */
2200         for(i = 0; i < spydata->spy_number; i++)
2201                 spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
2202         return 0;
2203 }
2204
2205 /*------------------------------------------------------------------*/
2206 /*
2207  * Standard Wireless Handler : set spy threshold
2208  */
2209 int iw_handler_set_thrspy(struct net_device *   dev,
2210                           struct iw_request_info *info,
2211                           union iwreq_data *    wrqu,
2212                           char *                extra)
2213 {
2214         struct iw_spy_data *    spydata = get_spydata(dev);
2215         struct iw_thrspy *      threshold = (struct iw_thrspy *) extra;
2216
2217         /* Make sure driver is not buggy or using the old API */
2218         if(!spydata)
2219                 return -EOPNOTSUPP;
2220
2221         /* Just do it */
2222         memcpy(&(spydata->spy_thr_low), &(threshold->low),
2223                2 * sizeof(struct iw_quality));
2224
2225         /* Clear flag */
2226         memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
2227
2228 #ifdef WE_SPY_DEBUG
2229         printk(KERN_DEBUG "iw_handler_set_thrspy() :  low %d ; high %d\n", spydata->spy_thr_low.level, spydata->spy_thr_high.level);
2230 #endif  /* WE_SPY_DEBUG */
2231
2232         return 0;
2233 }
2234
2235 /*------------------------------------------------------------------*/
2236 /*
2237  * Standard Wireless Handler : get spy threshold
2238  */
2239 int iw_handler_get_thrspy(struct net_device *   dev,
2240                           struct iw_request_info *info,
2241                           union iwreq_data *    wrqu,
2242                           char *                extra)
2243 {
2244         struct iw_spy_data *    spydata = get_spydata(dev);
2245         struct iw_thrspy *      threshold = (struct iw_thrspy *) extra;
2246
2247         /* Make sure driver is not buggy or using the old API */
2248         if(!spydata)
2249                 return -EOPNOTSUPP;
2250
2251         /* Just do it */
2252         memcpy(&(threshold->low), &(spydata->spy_thr_low),
2253                2 * sizeof(struct iw_quality));
2254
2255         return 0;
2256 }
2257
2258 /*------------------------------------------------------------------*/
2259 /*
2260  * Prepare and send a Spy Threshold event
2261  */
2262 static void iw_send_thrspy_event(struct net_device *    dev,
2263                                  struct iw_spy_data *   spydata,
2264                                  unsigned char *        address,
2265                                  struct iw_quality *    wstats)
2266 {
2267         union iwreq_data        wrqu;
2268         struct iw_thrspy        threshold;
2269
2270         /* Init */
2271         wrqu.data.length = 1;
2272         wrqu.data.flags = 0;
2273         /* Copy address */
2274         memcpy(threshold.addr.sa_data, address, ETH_ALEN);
2275         threshold.addr.sa_family = ARPHRD_ETHER;
2276         /* Copy stats */
2277         memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
2278         /* Copy also thresholds */
2279         memcpy(&(threshold.low), &(spydata->spy_thr_low),
2280                2 * sizeof(struct iw_quality));
2281
2282 #ifdef WE_SPY_DEBUG
2283         printk(KERN_DEBUG "iw_send_thrspy_event() : address %02X:%02X:%02X:%02X:%02X:%02X, level %d, up = %d\n",
2284                threshold.addr.sa_data[0],
2285                threshold.addr.sa_data[1],
2286                threshold.addr.sa_data[2],
2287                threshold.addr.sa_data[3],
2288                threshold.addr.sa_data[4],
2289                threshold.addr.sa_data[5], threshold.qual.level);
2290 #endif  /* WE_SPY_DEBUG */
2291
2292         /* Send event to user space */
2293         wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
2294 }
2295
2296 /* ---------------------------------------------------------------- */
2297 /*
2298  * Call for the driver to update the spy data.
2299  * For now, the spy data is a simple array. As the size of the array is
2300  * small, this is good enough. If we wanted to support larger number of
2301  * spy addresses, we should use something more efficient...
2302  */
2303 void wireless_spy_update(struct net_device *    dev,
2304                          unsigned char *        address,
2305                          struct iw_quality *    wstats)
2306 {
2307         struct iw_spy_data *    spydata = get_spydata(dev);
2308         int                     i;
2309         int                     match = -1;
2310
2311         /* Make sure driver is not buggy or using the old API */
2312         if(!spydata)
2313                 return;
2314
2315 #ifdef WE_SPY_DEBUG
2316         printk(KERN_DEBUG "wireless_spy_update() :  wireless_data %p, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_data, spydata, address[0], address[1], address[2], address[3], address[4], address[5]);
2317 #endif  /* WE_SPY_DEBUG */
2318
2319         /* Update all records that match */
2320         for(i = 0; i < spydata->spy_number; i++)
2321                 if(!compare_ether_addr(address, spydata->spy_address[i])) {
2322                         memcpy(&(spydata->spy_stat[i]), wstats,
2323                                sizeof(struct iw_quality));
2324                         match = i;
2325                 }
2326
2327         /* Generate an event if we cross the spy threshold.
2328          * To avoid event storms, we have a simple hysteresis : we generate
2329          * event only when we go under the low threshold or above the
2330          * high threshold. */
2331         if(match >= 0) {
2332                 if(spydata->spy_thr_under[match]) {
2333                         if(wstats->level > spydata->spy_thr_high.level) {
2334                                 spydata->spy_thr_under[match] = 0;
2335                                 iw_send_thrspy_event(dev, spydata,
2336                                                      address, wstats);
2337                         }
2338                 } else {
2339                         if(wstats->level < spydata->spy_thr_low.level) {
2340                                 spydata->spy_thr_under[match] = 1;
2341                                 iw_send_thrspy_event(dev, spydata,
2342                                                      address, wstats);
2343                         }
2344                 }
2345         }
2346 }
2347
2348 EXPORT_SYMBOL(iw_handler_get_spy);
2349 EXPORT_SYMBOL(iw_handler_get_thrspy);
2350 EXPORT_SYMBOL(iw_handler_set_spy);
2351 EXPORT_SYMBOL(iw_handler_set_thrspy);
2352 EXPORT_SYMBOL(wireless_send_event);
2353 EXPORT_SYMBOL(wireless_spy_update);