aboutsummaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2007-04-26 23:42:51 -0400
committerDavid S. Miller <davem@davemloft.net>2007-04-26 23:42:51 -0400
commit11433ee450eb4a320f46ce5ed51410b52803ffcc (patch)
tree455f356778339e9015d445a020d60c0e0d095672 /net/core
parent39bf09493042200b967cdf2ee6e3f670b7963903 (diff)
[WEXT]: Move to net/wireless
This patch moves dev/core/wireless.c to net/wireless/wext.c. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core')
-rw-r--r--net/core/Makefile1
-rw-r--r--net/core/dev.c2
-rw-r--r--net/core/wireless.c1633
3 files changed, 1 insertions, 1635 deletions
diff --git a/net/core/Makefile b/net/core/Makefile
index 73272d506e93..4751613e1b59 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -13,7 +13,6 @@ obj-y += dev.o ethtool.o dev_mcast.o dst.o netevent.o \
13obj-$(CONFIG_XFRM) += flow.o 13obj-$(CONFIG_XFRM) += flow.o
14obj-$(CONFIG_SYSFS) += net-sysfs.o 14obj-$(CONFIG_SYSFS) += net-sysfs.o
15obj-$(CONFIG_NET_PKTGEN) += pktgen.o 15obj-$(CONFIG_NET_PKTGEN) += pktgen.o
16obj-$(CONFIG_WIRELESS_EXT) += wireless.o
17obj-$(CONFIG_NETPOLL) += netpoll.o 16obj-$(CONFIG_NETPOLL) += netpoll.o
18obj-$(CONFIG_NET_DMA) += user_dma.o 17obj-$(CONFIG_NET_DMA) += user_dma.o
19obj-$(CONFIG_FIB_RULES) += fib_rules.o 18obj-$(CONFIG_FIB_RULES) += fib_rules.o
diff --git a/net/core/dev.c b/net/core/dev.c
index d82d00f5451f..700e4b5081b6 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2936,7 +2936,7 @@ int dev_ioctl(unsigned int cmd, void __user *arg)
2936 } 2936 }
2937 dev_load(ifr.ifr_name); 2937 dev_load(ifr.ifr_name);
2938 rtnl_lock(); 2938 rtnl_lock();
2939 /* Follow me in net/core/wireless.c */ 2939 /* Follow me in net/wireless/wext.c */
2940 ret = wireless_process_ioctl(&ifr, cmd); 2940 ret = wireless_process_ioctl(&ifr, cmd);
2941 rtnl_unlock(); 2941 rtnl_unlock();
2942 if (IW_IS_GET(cmd) && 2942 if (IW_IS_GET(cmd) &&
diff --git a/net/core/wireless.c b/net/core/wireless.c
deleted file mode 100644
index fba295e05e7a..000000000000
--- a/net/core/wireless.c
+++ /dev/null
@@ -1,1633 +0,0 @@
1/*
2 * This file implement the Wireless Extensions APIs.
3 *
4 * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
5 * Copyright (c) 1997-2007 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 * v10 - 16.3.07 - Jean II
81 * o Prevent leaking of kernel space in stream on 64 bits.
82 */
83
84/***************************** INCLUDES *****************************/
85
86#include <linux/module.h>
87#include <linux/types.h> /* off_t */
88#include <linux/netdevice.h> /* struct ifreq, dev_get_by_name() */
89#include <linux/proc_fs.h>
90#include <linux/rtnetlink.h> /* rtnetlink stuff */
91#include <linux/seq_file.h>
92#include <linux/init.h> /* for __init */
93#include <linux/if_arp.h> /* ARPHRD_ETHER */
94#include <linux/etherdevice.h> /* compare_ether_addr */
95#include <linux/interrupt.h>
96
97#include <linux/wireless.h> /* Pretty obvious */
98#include <net/iw_handler.h> /* New driver API */
99#include <net/netlink.h>
100
101#include <asm/uaccess.h> /* copy_to_user() */
102
103/**************************** CONSTANTS ****************************/
104
105/* Debugging stuff */
106#undef WE_IOCTL_DEBUG /* Debug IOCTL API */
107#undef WE_EVENT_DEBUG /* Debug Event dispatcher */
108#undef WE_SPY_DEBUG /* Debug enhanced spy support */
109
110/* Options */
111#define WE_EVENT_RTNETLINK /* Propagate events using RtNetlink */
112#define WE_SET_EVENT /* Generate an event on some set commands */
113
114/************************* GLOBAL VARIABLES *************************/
115/*
116 * You should not use global variables, because of re-entrancy.
117 * On our case, it's only const, so it's OK...
118 */
119/*
120 * Meta-data about all the standard Wireless Extension request we
121 * know about.
122 */
123static const struct iw_ioctl_description standard_ioctl[] = {
124 [SIOCSIWCOMMIT - SIOCIWFIRST] = {
125 .header_type = IW_HEADER_TYPE_NULL,
126 },
127 [SIOCGIWNAME - SIOCIWFIRST] = {
128 .header_type = IW_HEADER_TYPE_CHAR,
129 .flags = IW_DESCR_FLAG_DUMP,
130 },
131 [SIOCSIWNWID - SIOCIWFIRST] = {
132 .header_type = IW_HEADER_TYPE_PARAM,
133 .flags = IW_DESCR_FLAG_EVENT,
134 },
135 [SIOCGIWNWID - SIOCIWFIRST] = {
136 .header_type = IW_HEADER_TYPE_PARAM,
137 .flags = IW_DESCR_FLAG_DUMP,
138 },
139 [SIOCSIWFREQ - SIOCIWFIRST] = {
140 .header_type = IW_HEADER_TYPE_FREQ,
141 .flags = IW_DESCR_FLAG_EVENT,
142 },
143 [SIOCGIWFREQ - SIOCIWFIRST] = {
144 .header_type = IW_HEADER_TYPE_FREQ,
145 .flags = IW_DESCR_FLAG_DUMP,
146 },
147 [SIOCSIWMODE - SIOCIWFIRST] = {
148 .header_type = IW_HEADER_TYPE_UINT,
149 .flags = IW_DESCR_FLAG_EVENT,
150 },
151 [SIOCGIWMODE - SIOCIWFIRST] = {
152 .header_type = IW_HEADER_TYPE_UINT,
153 .flags = IW_DESCR_FLAG_DUMP,
154 },
155 [SIOCSIWSENS - SIOCIWFIRST] = {
156 .header_type = IW_HEADER_TYPE_PARAM,
157 },
158 [SIOCGIWSENS - SIOCIWFIRST] = {
159 .header_type = IW_HEADER_TYPE_PARAM,
160 },
161 [SIOCSIWRANGE - SIOCIWFIRST] = {
162 .header_type = IW_HEADER_TYPE_NULL,
163 },
164 [SIOCGIWRANGE - SIOCIWFIRST] = {
165 .header_type = IW_HEADER_TYPE_POINT,
166 .token_size = 1,
167 .max_tokens = sizeof(struct iw_range),
168 .flags = IW_DESCR_FLAG_DUMP,
169 },
170 [SIOCSIWPRIV - SIOCIWFIRST] = {
171 .header_type = IW_HEADER_TYPE_NULL,
172 },
173 [SIOCGIWPRIV - SIOCIWFIRST] = { /* (handled directly by us) */
174 .header_type = IW_HEADER_TYPE_POINT,
175 .token_size = sizeof(struct iw_priv_args),
176 .max_tokens = 16,
177 .flags = IW_DESCR_FLAG_NOMAX,
178 },
179 [SIOCSIWSTATS - SIOCIWFIRST] = {
180 .header_type = IW_HEADER_TYPE_NULL,
181 },
182 [SIOCGIWSTATS - SIOCIWFIRST] = { /* (handled directly by us) */
183 .header_type = IW_HEADER_TYPE_POINT,
184 .token_size = 1,
185 .max_tokens = sizeof(struct iw_statistics),
186 .flags = IW_DESCR_FLAG_DUMP,
187 },
188 [SIOCSIWSPY - SIOCIWFIRST] = {
189 .header_type = IW_HEADER_TYPE_POINT,
190 .token_size = sizeof(struct sockaddr),
191 .max_tokens = IW_MAX_SPY,
192 },
193 [SIOCGIWSPY - SIOCIWFIRST] = {
194 .header_type = IW_HEADER_TYPE_POINT,
195 .token_size = sizeof(struct sockaddr) +
196 sizeof(struct iw_quality),
197 .max_tokens = IW_MAX_SPY,
198 },
199 [SIOCSIWTHRSPY - SIOCIWFIRST] = {
200 .header_type = IW_HEADER_TYPE_POINT,
201 .token_size = sizeof(struct iw_thrspy),
202 .min_tokens = 1,
203 .max_tokens = 1,
204 },
205 [SIOCGIWTHRSPY - SIOCIWFIRST] = {
206 .header_type = IW_HEADER_TYPE_POINT,
207 .token_size = sizeof(struct iw_thrspy),
208 .min_tokens = 1,
209 .max_tokens = 1,
210 },
211 [SIOCSIWAP - SIOCIWFIRST] = {
212 .header_type = IW_HEADER_TYPE_ADDR,
213 },
214 [SIOCGIWAP - SIOCIWFIRST] = {
215 .header_type = IW_HEADER_TYPE_ADDR,
216 .flags = IW_DESCR_FLAG_DUMP,
217 },
218 [SIOCSIWMLME - SIOCIWFIRST] = {
219 .header_type = IW_HEADER_TYPE_POINT,
220 .token_size = 1,
221 .min_tokens = sizeof(struct iw_mlme),
222 .max_tokens = sizeof(struct iw_mlme),
223 },
224 [SIOCGIWAPLIST - SIOCIWFIRST] = {
225 .header_type = IW_HEADER_TYPE_POINT,
226 .token_size = sizeof(struct sockaddr) +
227 sizeof(struct iw_quality),
228 .max_tokens = IW_MAX_AP,
229 .flags = IW_DESCR_FLAG_NOMAX,
230 },
231 [SIOCSIWSCAN - SIOCIWFIRST] = {
232 .header_type = IW_HEADER_TYPE_POINT,
233 .token_size = 1,
234 .min_tokens = 0,
235 .max_tokens = sizeof(struct iw_scan_req),
236 },
237 [SIOCGIWSCAN - SIOCIWFIRST] = {
238 .header_type = IW_HEADER_TYPE_POINT,
239 .token_size = 1,
240 .max_tokens = IW_SCAN_MAX_DATA,
241 .flags = IW_DESCR_FLAG_NOMAX,
242 },
243 [SIOCSIWESSID - SIOCIWFIRST] = {
244 .header_type = IW_HEADER_TYPE_POINT,
245 .token_size = 1,
246 .max_tokens = IW_ESSID_MAX_SIZE,
247 .flags = IW_DESCR_FLAG_EVENT,
248 },
249 [SIOCGIWESSID - SIOCIWFIRST] = {
250 .header_type = IW_HEADER_TYPE_POINT,
251 .token_size = 1,
252 .max_tokens = IW_ESSID_MAX_SIZE,
253 .flags = IW_DESCR_FLAG_DUMP,
254 },
255 [SIOCSIWNICKN - SIOCIWFIRST] = {
256 .header_type = IW_HEADER_TYPE_POINT,
257 .token_size = 1,
258 .max_tokens = IW_ESSID_MAX_SIZE,
259 },
260 [SIOCGIWNICKN - SIOCIWFIRST] = {
261 .header_type = IW_HEADER_TYPE_POINT,
262 .token_size = 1,
263 .max_tokens = IW_ESSID_MAX_SIZE,
264 },
265 [SIOCSIWRATE - SIOCIWFIRST] = {
266 .header_type = IW_HEADER_TYPE_PARAM,
267 },
268 [SIOCGIWRATE - SIOCIWFIRST] = {
269 .header_type = IW_HEADER_TYPE_PARAM,
270 },
271 [SIOCSIWRTS - SIOCIWFIRST] = {
272 .header_type = IW_HEADER_TYPE_PARAM,
273 },
274 [SIOCGIWRTS - SIOCIWFIRST] = {
275 .header_type = IW_HEADER_TYPE_PARAM,
276 },
277 [SIOCSIWFRAG - SIOCIWFIRST] = {
278 .header_type = IW_HEADER_TYPE_PARAM,
279 },
280 [SIOCGIWFRAG - SIOCIWFIRST] = {
281 .header_type = IW_HEADER_TYPE_PARAM,
282 },
283 [SIOCSIWTXPOW - SIOCIWFIRST] = {
284 .header_type = IW_HEADER_TYPE_PARAM,
285 },
286 [SIOCGIWTXPOW - SIOCIWFIRST] = {
287 .header_type = IW_HEADER_TYPE_PARAM,
288 },
289 [SIOCSIWRETRY - SIOCIWFIRST] = {
290 .header_type = IW_HEADER_TYPE_PARAM,
291 },
292 [SIOCGIWRETRY - SIOCIWFIRST] = {
293 .header_type = IW_HEADER_TYPE_PARAM,
294 },
295 [SIOCSIWENCODE - SIOCIWFIRST] = {
296 .header_type = IW_HEADER_TYPE_POINT,
297 .token_size = 1,
298 .max_tokens = IW_ENCODING_TOKEN_MAX,
299 .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
300 },
301 [SIOCGIWENCODE - SIOCIWFIRST] = {
302 .header_type = IW_HEADER_TYPE_POINT,
303 .token_size = 1,
304 .max_tokens = IW_ENCODING_TOKEN_MAX,
305 .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
306 },
307 [SIOCSIWPOWER - SIOCIWFIRST] = {
308 .header_type = IW_HEADER_TYPE_PARAM,
309 },
310 [SIOCGIWPOWER - SIOCIWFIRST] = {
311 .header_type = IW_HEADER_TYPE_PARAM,
312 },
313 [SIOCSIWGENIE - SIOCIWFIRST] = {
314 .header_type = IW_HEADER_TYPE_POINT,
315 .token_size = 1,
316 .max_tokens = IW_GENERIC_IE_MAX,
317 },
318 [SIOCGIWGENIE - SIOCIWFIRST] = {
319 .header_type = IW_HEADER_TYPE_POINT,
320 .token_size = 1,
321 .max_tokens = IW_GENERIC_IE_MAX,
322 },
323 [SIOCSIWAUTH - SIOCIWFIRST] = {
324 .header_type = IW_HEADER_TYPE_PARAM,
325 },
326 [SIOCGIWAUTH - SIOCIWFIRST] = {
327 .header_type = IW_HEADER_TYPE_PARAM,
328 },
329 [SIOCSIWENCODEEXT - SIOCIWFIRST] = {
330 .header_type = IW_HEADER_TYPE_POINT,
331 .token_size = 1,
332 .min_tokens = sizeof(struct iw_encode_ext),
333 .max_tokens = sizeof(struct iw_encode_ext) +
334 IW_ENCODING_TOKEN_MAX,
335 },
336 [SIOCGIWENCODEEXT - SIOCIWFIRST] = {
337 .header_type = IW_HEADER_TYPE_POINT,
338 .token_size = 1,
339 .min_tokens = sizeof(struct iw_encode_ext),
340 .max_tokens = sizeof(struct iw_encode_ext) +
341 IW_ENCODING_TOKEN_MAX,
342 },
343 [SIOCSIWPMKSA - SIOCIWFIRST] = {
344 .header_type = IW_HEADER_TYPE_POINT,
345 .token_size = 1,
346 .min_tokens = sizeof(struct iw_pmksa),
347 .max_tokens = sizeof(struct iw_pmksa),
348 },
349};
350static const unsigned standard_ioctl_num = ARRAY_SIZE(standard_ioctl);
351
352/*
353 * Meta-data about all the additional standard Wireless Extension events
354 * we know about.
355 */
356static 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};
400static const unsigned standard_event_num = ARRAY_SIZE(standard_event);
401
402/* Size (in bytes) of the various private data types */
403static const char iw_priv_type_size[] = {
404 0, /* IW_PRIV_TYPE_NONE */
405 1, /* IW_PRIV_TYPE_BYTE */
406 1, /* IW_PRIV_TYPE_CHAR */
407 0, /* Not defined */
408 sizeof(__u32), /* IW_PRIV_TYPE_INT */
409 sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */
410 sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */
411 0, /* Not defined */
412};
413
414/* Size (in bytes) of various events */
415static const int event_type_size[] = {
416 IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */
417 0,
418 IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */
419 0,
420 IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */
421 IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */
422 IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */
423 0,
424 IW_EV_POINT_LEN, /* Without variable payload */
425 IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */
426 IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */
427};
428
429/* Size (in bytes) of various events, as packed */
430static const int event_type_pk_size[] = {
431 IW_EV_LCP_PK_LEN, /* IW_HEADER_TYPE_NULL */
432 0,
433 IW_EV_CHAR_PK_LEN, /* IW_HEADER_TYPE_CHAR */
434 0,
435 IW_EV_UINT_PK_LEN, /* IW_HEADER_TYPE_UINT */
436 IW_EV_FREQ_PK_LEN, /* IW_HEADER_TYPE_FREQ */
437 IW_EV_ADDR_PK_LEN, /* IW_HEADER_TYPE_ADDR */
438 0,
439 IW_EV_POINT_PK_LEN, /* Without variable payload */
440 IW_EV_PARAM_PK_LEN, /* IW_HEADER_TYPE_PARAM */
441 IW_EV_QUAL_PK_LEN, /* IW_HEADER_TYPE_QUAL */
442};
443
444/************************ COMMON SUBROUTINES ************************/
445/*
446 * Stuff that may be used in various place or doesn't fit in one
447 * of the section below.
448 */
449
450/* ---------------------------------------------------------------- */
451/*
452 * Return the driver handler associated with a specific Wireless Extension.
453 * Called from various place, so make sure it remains efficient.
454 */
455static inline iw_handler get_handler(struct net_device *dev,
456 unsigned int cmd)
457{
458 /* Don't "optimise" the following variable, it will crash */
459 unsigned int index; /* *MUST* be unsigned */
460
461 /* Check if we have some wireless handlers defined */
462 if (dev->wireless_handlers == NULL)
463 return NULL;
464
465 /* Try as a standard command */
466 index = cmd - SIOCIWFIRST;
467 if (index < dev->wireless_handlers->num_standard)
468 return dev->wireless_handlers->standard[index];
469
470 /* Try as a private command */
471 index = cmd - SIOCIWFIRSTPRIV;
472 if (index < dev->wireless_handlers->num_private)
473 return dev->wireless_handlers->private[index];
474
475 /* Not found */
476 return NULL;
477}
478
479/* ---------------------------------------------------------------- */
480/*
481 * Get statistics out of the driver
482 */
483static inline struct iw_statistics *get_wireless_stats(struct net_device *dev)
484{
485 /* New location */
486 if ((dev->wireless_handlers != NULL) &&
487 (dev->wireless_handlers->get_wireless_stats != NULL))
488 return dev->wireless_handlers->get_wireless_stats(dev);
489
490 /* Not found */
491 return (struct iw_statistics *) NULL;
492}
493
494/* ---------------------------------------------------------------- */
495/*
496 * Call the commit handler in the driver
497 * (if exist and if conditions are right)
498 *
499 * Note : our current commit strategy is currently pretty dumb,
500 * but we will be able to improve on that...
501 * The goal is to try to agreagate as many changes as possible
502 * before doing the commit. Drivers that will define a commit handler
503 * are usually those that need a reset after changing parameters, so
504 * we want to minimise the number of reset.
505 * A cool idea is to use a timer : at each "set" command, we re-set the
506 * timer, when the timer eventually fires, we call the driver.
507 * Hopefully, more on that later.
508 *
509 * Also, I'm waiting to see how many people will complain about the
510 * netif_running(dev) test. I'm open on that one...
511 * Hopefully, the driver will remember to do a commit in "open()" ;-)
512 */
513static inline int call_commit_handler(struct net_device * dev)
514{
515 if ((netif_running(dev)) &&
516 (dev->wireless_handlers->standard[0] != NULL)) {
517 /* Call the commit handler on the driver */
518 return dev->wireless_handlers->standard[0](dev, NULL,
519 NULL, NULL);
520 } else
521 return 0; /* Command completed successfully */
522}
523
524/* ---------------------------------------------------------------- */
525/*
526 * Calculate size of private arguments
527 */
528static inline int get_priv_size(__u16 args)
529{
530 int num = args & IW_PRIV_SIZE_MASK;
531 int type = (args & IW_PRIV_TYPE_MASK) >> 12;
532
533 return num * iw_priv_type_size[type];
534}
535
536/* ---------------------------------------------------------------- */
537/*
538 * Re-calculate the size of private arguments
539 */
540static inline int adjust_priv_size(__u16 args,
541 union iwreq_data * wrqu)
542{
543 int num = wrqu->data.length;
544 int max = args & IW_PRIV_SIZE_MASK;
545 int type = (args & IW_PRIV_TYPE_MASK) >> 12;
546
547 /* Make sure the driver doesn't goof up */
548 if (max < num)
549 num = max;
550
551 return num * iw_priv_type_size[type];
552}
553
554/* ---------------------------------------------------------------- */
555/*
556 * Standard Wireless Handler : get wireless stats
557 * Allow programatic access to /proc/net/wireless even if /proc
558 * doesn't exist... Also more efficient...
559 */
560static int iw_handler_get_iwstats(struct net_device * dev,
561 struct iw_request_info * info,
562 union iwreq_data * wrqu,
563 char * extra)
564{
565 /* Get stats from the driver */
566 struct iw_statistics *stats;
567
568 stats = get_wireless_stats(dev);
569 if (stats != (struct iw_statistics *) NULL) {
570
571 /* Copy statistics to extra */
572 memcpy(extra, stats, sizeof(struct iw_statistics));
573 wrqu->data.length = sizeof(struct iw_statistics);
574
575 /* Check if we need to clear the updated flag */
576 if (wrqu->data.flags != 0)
577 stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
578 return 0;
579 } else
580 return -EOPNOTSUPP;
581}
582
583/* ---------------------------------------------------------------- */
584/*
585 * Standard Wireless Handler : get iwpriv definitions
586 * Export the driver private handler definition
587 * They will be picked up by tools like iwpriv...
588 */
589static int iw_handler_get_private(struct net_device * dev,
590 struct iw_request_info * info,
591 union iwreq_data * wrqu,
592 char * extra)
593{
594 /* Check if the driver has something to export */
595 if ((dev->wireless_handlers->num_private_args == 0) ||
596 (dev->wireless_handlers->private_args == NULL))
597 return -EOPNOTSUPP;
598
599 /* Check if there is enough buffer up there */
600 if (wrqu->data.length < dev->wireless_handlers->num_private_args) {
601 /* User space can't know in advance how large the buffer
602 * needs to be. Give it a hint, so that we can support
603 * any size buffer we want somewhat efficiently... */
604 wrqu->data.length = dev->wireless_handlers->num_private_args;
605 return -E2BIG;
606 }
607
608 /* Set the number of available ioctls. */
609 wrqu->data.length = dev->wireless_handlers->num_private_args;
610
611 /* Copy structure to the user buffer. */
612 memcpy(extra, dev->wireless_handlers->private_args,
613 sizeof(struct iw_priv_args) * wrqu->data.length);
614
615 return 0;
616}
617
618
619/******************** /proc/net/wireless SUPPORT ********************/
620/*
621 * The /proc/net/wireless file is a human readable user-space interface
622 * exporting various wireless specific statistics from the wireless devices.
623 * This is the most popular part of the Wireless Extensions ;-)
624 *
625 * This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
626 * The content of the file is basically the content of "struct iw_statistics".
627 */
628
629#ifdef CONFIG_PROC_FS
630
631/* ---------------------------------------------------------------- */
632/*
633 * Print one entry (line) of /proc/net/wireless
634 */
635static __inline__ void wireless_seq_printf_stats(struct seq_file *seq,
636 struct net_device *dev)
637{
638 /* Get stats from the driver */
639 struct iw_statistics *stats = get_wireless_stats(dev);
640
641 if (stats) {
642 seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d "
643 "%6d %6d %6d\n",
644 dev->name, stats->status, stats->qual.qual,
645 stats->qual.updated & IW_QUAL_QUAL_UPDATED
646 ? '.' : ' ',
647 ((__s32) stats->qual.level) -
648 ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
649 stats->qual.updated & IW_QUAL_LEVEL_UPDATED
650 ? '.' : ' ',
651 ((__s32) stats->qual.noise) -
652 ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
653 stats->qual.updated & IW_QUAL_NOISE_UPDATED
654 ? '.' : ' ',
655 stats->discard.nwid, stats->discard.code,
656 stats->discard.fragment, stats->discard.retries,
657 stats->discard.misc, stats->miss.beacon);
658 stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
659 }
660}
661
662/* ---------------------------------------------------------------- */
663/*
664 * Print info for /proc/net/wireless (print all entries)
665 */
666static int wireless_seq_show(struct seq_file *seq, void *v)
667{
668 if (v == SEQ_START_TOKEN)
669 seq_printf(seq, "Inter-| sta-| Quality | Discarded "
670 "packets | Missed | WE\n"
671 " face | tus | link level noise | nwid "
672 "crypt frag retry misc | beacon | %d\n",
673 WIRELESS_EXT);
674 else
675 wireless_seq_printf_stats(seq, v);
676 return 0;
677}
678
679static const struct seq_operations wireless_seq_ops = {
680 .start = dev_seq_start,
681 .next = dev_seq_next,
682 .stop = dev_seq_stop,
683 .show = wireless_seq_show,
684};
685
686static int wireless_seq_open(struct inode *inode, struct file *file)
687{
688 return seq_open(file, &wireless_seq_ops);
689}
690
691static const struct file_operations wireless_seq_fops = {
692 .owner = THIS_MODULE,
693 .open = wireless_seq_open,
694 .read = seq_read,
695 .llseek = seq_lseek,
696 .release = seq_release,
697};
698
699int __init wireless_proc_init(void)
700{
701 /* Create /proc/net/wireless entry */
702 if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops))
703 return -ENOMEM;
704
705 return 0;
706}
707#endif /* CONFIG_PROC_FS */
708
709/************************** IOCTL SUPPORT **************************/
710/*
711 * The original user space API to configure all those Wireless Extensions
712 * is through IOCTLs.
713 * In there, we check if we need to call the new driver API (iw_handler)
714 * or just call the driver ioctl handler.
715 */
716
717/* ---------------------------------------------------------------- */
718/*
719 * Wrapper to call a standard Wireless Extension handler.
720 * We do various checks and also take care of moving data between
721 * user space and kernel space.
722 */
723static int ioctl_standard_call(struct net_device * dev,
724 struct ifreq * ifr,
725 unsigned int cmd,
726 iw_handler handler)
727{
728 struct iwreq * iwr = (struct iwreq *) ifr;
729 const struct iw_ioctl_description * descr;
730 struct iw_request_info info;
731 int ret = -EINVAL;
732
733 /* Get the description of the IOCTL */
734 if ((cmd - SIOCIWFIRST) >= standard_ioctl_num)
735 return -EOPNOTSUPP;
736 descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
737
738#ifdef WE_IOCTL_DEBUG
739 printk(KERN_DEBUG "%s (WE) : Found standard handler for 0x%04X\n",
740 ifr->ifr_name, cmd);
741 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);
742#endif /* WE_IOCTL_DEBUG */
743
744 /* Prepare the call */
745 info.cmd = cmd;
746 info.flags = 0;
747
748 /* Check if we have a pointer to user space data or not */
749 if (descr->header_type != IW_HEADER_TYPE_POINT) {
750
751 /* No extra arguments. Trivial to handle */
752 ret = handler(dev, &info, &(iwr->u), NULL);
753
754#ifdef WE_SET_EVENT
755 /* Generate an event to notify listeners of the change */
756 if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
757 ((ret == 0) || (ret == -EIWCOMMIT)))
758 wireless_send_event(dev, cmd, &(iwr->u), NULL);
759#endif /* WE_SET_EVENT */
760 } else {
761 char * extra;
762 int extra_size;
763 int user_length = 0;
764 int err;
765 int essid_compat = 0;
766
767 /* Calculate space needed by arguments. Always allocate
768 * for max space. Easier, and won't last long... */
769 extra_size = descr->max_tokens * descr->token_size;
770
771 /* Check need for ESSID compatibility for WE < 21 */
772 switch (cmd) {
773 case SIOCSIWESSID:
774 case SIOCGIWESSID:
775 case SIOCSIWNICKN:
776 case SIOCGIWNICKN:
777 if (iwr->u.data.length == descr->max_tokens + 1)
778 essid_compat = 1;
779 else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
780 char essid[IW_ESSID_MAX_SIZE + 1];
781
782 err = copy_from_user(essid, iwr->u.data.pointer,
783 iwr->u.data.length *
784 descr->token_size);
785 if (err)
786 return -EFAULT;
787
788 if (essid[iwr->u.data.length - 1] == '\0')
789 essid_compat = 1;
790 }
791 break;
792 default:
793 break;
794 }
795
796 iwr->u.data.length -= essid_compat;
797
798 /* Check what user space is giving us */
799 if (IW_IS_SET(cmd)) {
800 /* Check NULL pointer */
801 if ((iwr->u.data.pointer == NULL) &&
802 (iwr->u.data.length != 0))
803 return -EFAULT;
804 /* Check if number of token fits within bounds */
805 if (iwr->u.data.length > descr->max_tokens)
806 return -E2BIG;
807 if (iwr->u.data.length < descr->min_tokens)
808 return -EINVAL;
809 } else {
810 /* Check NULL pointer */
811 if (iwr->u.data.pointer == NULL)
812 return -EFAULT;
813 /* Save user space buffer size for checking */
814 user_length = iwr->u.data.length;
815
816 /* Don't check if user_length > max to allow forward
817 * compatibility. The test user_length < min is
818 * implied by the test at the end. */
819
820 /* Support for very large requests */
821 if ((descr->flags & IW_DESCR_FLAG_NOMAX) &&
822 (user_length > descr->max_tokens)) {
823 /* Allow userspace to GET more than max so
824 * we can support any size GET requests.
825 * There is still a limit : -ENOMEM. */
826 extra_size = user_length * descr->token_size;
827 /* Note : user_length is originally a __u16,
828 * and token_size is controlled by us,
829 * so extra_size won't get negative and
830 * won't overflow... */
831 }
832 }
833
834#ifdef WE_IOCTL_DEBUG
835 printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
836 dev->name, extra_size);
837#endif /* WE_IOCTL_DEBUG */
838
839 /* Create the kernel buffer */
840 /* kzalloc ensures NULL-termination for essid_compat */
841 extra = kzalloc(extra_size, GFP_KERNEL);
842 if (extra == NULL) {
843 return -ENOMEM;
844 }
845
846 /* If it is a SET, get all the extra data in here */
847 if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
848 err = copy_from_user(extra, iwr->u.data.pointer,
849 iwr->u.data.length *
850 descr->token_size);
851 if (err) {
852 kfree(extra);
853 return -EFAULT;
854 }
855#ifdef WE_IOCTL_DEBUG
856 printk(KERN_DEBUG "%s (WE) : Got %d bytes\n",
857 dev->name,
858 iwr->u.data.length * descr->token_size);
859#endif /* WE_IOCTL_DEBUG */
860 }
861
862 /* Call the handler */
863 ret = handler(dev, &info, &(iwr->u), extra);
864
865 iwr->u.data.length += essid_compat;
866
867 /* If we have something to return to the user */
868 if (!ret && IW_IS_GET(cmd)) {
869 /* Check if there is enough buffer up there */
870 if (user_length < iwr->u.data.length) {
871 kfree(extra);
872 return -E2BIG;
873 }
874
875 err = copy_to_user(iwr->u.data.pointer, extra,
876 iwr->u.data.length *
877 descr->token_size);
878 if (err)
879 ret = -EFAULT;
880#ifdef WE_IOCTL_DEBUG
881 printk(KERN_DEBUG "%s (WE) : Wrote %d bytes\n",
882 dev->name,
883 iwr->u.data.length * descr->token_size);
884#endif /* WE_IOCTL_DEBUG */
885 }
886
887#ifdef WE_SET_EVENT
888 /* Generate an event to notify listeners of the change */
889 if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
890 ((ret == 0) || (ret == -EIWCOMMIT))) {
891 if (descr->flags & IW_DESCR_FLAG_RESTRICT)
892 /* If the event is restricted, don't
893 * export the payload */
894 wireless_send_event(dev, cmd, &(iwr->u), NULL);
895 else
896 wireless_send_event(dev, cmd, &(iwr->u),
897 extra);
898 }
899#endif /* WE_SET_EVENT */
900
901 /* Cleanup - I told you it wasn't that long ;-) */
902 kfree(extra);
903 }
904
905 /* Call commit handler if needed and defined */
906 if (ret == -EIWCOMMIT)
907 ret = call_commit_handler(dev);
908
909 /* Here, we will generate the appropriate event if needed */
910
911 return ret;
912}
913
914/* ---------------------------------------------------------------- */
915/*
916 * Wrapper to call a private Wireless Extension handler.
917 * We do various checks and also take care of moving data between
918 * user space and kernel space.
919 * It's not as nice and slimline as the standard wrapper. The cause
920 * is struct iw_priv_args, which was not really designed for the
921 * job we are going here.
922 *
923 * IMPORTANT : This function prevent to set and get data on the same
924 * IOCTL and enforce the SET/GET convention. Not doing it would be
925 * far too hairy...
926 * If you need to set and get data at the same time, please don't use
927 * a iw_handler but process it in your ioctl handler (i.e. use the
928 * old driver API).
929 */
930static inline int ioctl_private_call(struct net_device * dev,
931 struct ifreq * ifr,
932 unsigned int cmd,
933 iw_handler handler)
934{
935 struct iwreq * iwr = (struct iwreq *) ifr;
936 const struct iw_priv_args * descr = NULL;
937 struct iw_request_info info;
938 int extra_size = 0;
939 int i;
940 int ret = -EINVAL;
941
942 /* Get the description of the IOCTL */
943 for (i = 0; i < dev->wireless_handlers->num_private_args; i++)
944 if (cmd == dev->wireless_handlers->private_args[i].cmd) {
945 descr = &(dev->wireless_handlers->private_args[i]);
946 break;
947 }
948
949#ifdef WE_IOCTL_DEBUG
950 printk(KERN_DEBUG "%s (WE) : Found private handler for 0x%04X\n",
951 ifr->ifr_name, cmd);
952 if (descr) {
953 printk(KERN_DEBUG "%s (WE) : Name %s, set %X, get %X\n",
954 dev->name, descr->name,
955 descr->set_args, descr->get_args);
956 }
957#endif /* WE_IOCTL_DEBUG */
958
959 /* Compute the size of the set/get arguments */
960 if (descr != NULL) {
961 if (IW_IS_SET(cmd)) {
962 int offset = 0; /* For sub-ioctls */
963 /* Check for sub-ioctl handler */
964 if (descr->name[0] == '\0')
965 /* Reserve one int for sub-ioctl index */
966 offset = sizeof(__u32);
967
968 /* Size of set arguments */
969 extra_size = get_priv_size(descr->set_args);
970
971 /* Does it fits in iwr ? */
972 if ((descr->set_args & IW_PRIV_SIZE_FIXED) &&
973 ((extra_size + offset) <= IFNAMSIZ))
974 extra_size = 0;
975 } else {
976 /* Size of get arguments */
977 extra_size = get_priv_size(descr->get_args);
978
979 /* Does it fits in iwr ? */
980 if ((descr->get_args & IW_PRIV_SIZE_FIXED) &&
981 (extra_size <= IFNAMSIZ))
982 extra_size = 0;
983 }
984 }
985
986 /* Prepare the call */
987 info.cmd = cmd;
988 info.flags = 0;
989
990 /* Check if we have a pointer to user space data or not. */
991 if (extra_size == 0) {
992 /* No extra arguments. Trivial to handle */
993 ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));
994 } else {
995 char * extra;
996 int err;
997
998 /* Check what user space is giving us */
999 if (IW_IS_SET(cmd)) {
1000 /* Check NULL pointer */
1001 if ((iwr->u.data.pointer == NULL) &&
1002 (iwr->u.data.length != 0))
1003 return -EFAULT;
1004
1005 /* Does it fits within bounds ? */
1006 if (iwr->u.data.length > (descr->set_args &
1007 IW_PRIV_SIZE_MASK))
1008 return -E2BIG;
1009 } else {
1010 /* Check NULL pointer */
1011 if (iwr->u.data.pointer == NULL)
1012 return -EFAULT;
1013 }
1014
1015#ifdef WE_IOCTL_DEBUG
1016 printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
1017 dev->name, extra_size);
1018#endif /* WE_IOCTL_DEBUG */
1019
1020 /* Always allocate for max space. Easier, and won't last
1021 * long... */
1022 extra = kmalloc(extra_size, GFP_KERNEL);
1023 if (extra == NULL) {
1024 return -ENOMEM;
1025 }
1026
1027 /* If it is a SET, get all the extra data in here */
1028 if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
1029 err = copy_from_user(extra, iwr->u.data.pointer,
1030 extra_size);
1031 if (err) {
1032 kfree(extra);
1033 return -EFAULT;
1034 }
1035#ifdef WE_IOCTL_DEBUG
1036 printk(KERN_DEBUG "%s (WE) : Got %d elem\n",
1037 dev->name, iwr->u.data.length);
1038#endif /* WE_IOCTL_DEBUG */
1039 }
1040
1041 /* Call the handler */
1042 ret = handler(dev, &info, &(iwr->u), extra);
1043
1044 /* If we have something to return to the user */
1045 if (!ret && IW_IS_GET(cmd)) {
1046
1047 /* Adjust for the actual length if it's variable,
1048 * avoid leaking kernel bits outside. */
1049 if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) {
1050 extra_size = adjust_priv_size(descr->get_args,
1051 &(iwr->u));
1052 }
1053
1054 err = copy_to_user(iwr->u.data.pointer, extra,
1055 extra_size);
1056 if (err)
1057 ret = -EFAULT;
1058#ifdef WE_IOCTL_DEBUG
1059 printk(KERN_DEBUG "%s (WE) : Wrote %d elem\n",
1060 dev->name, iwr->u.data.length);
1061#endif /* WE_IOCTL_DEBUG */
1062 }
1063
1064 /* Cleanup - I told you it wasn't that long ;-) */
1065 kfree(extra);
1066 }
1067
1068
1069 /* Call commit handler if needed and defined */
1070 if (ret == -EIWCOMMIT)
1071 ret = call_commit_handler(dev);
1072
1073 return ret;
1074}
1075
1076/* ---------------------------------------------------------------- */
1077/*
1078 * Main IOCTl dispatcher. Called from the main networking code
1079 * (dev_ioctl() in net/core/dev.c).
1080 * Check the type of IOCTL and call the appropriate wrapper...
1081 */
1082int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
1083{
1084 struct net_device *dev;
1085 iw_handler handler;
1086
1087 /* Permissions are already checked in dev_ioctl() before calling us.
1088 * The copy_to/from_user() of ifr is also dealt with in there */
1089
1090 /* Make sure the device exist */
1091 if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL)
1092 return -ENODEV;
1093
1094 /* A bunch of special cases, then the generic case...
1095 * Note that 'cmd' is already filtered in dev_ioctl() with
1096 * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
1097 switch (cmd) {
1098 case SIOCGIWSTATS:
1099 /* Get Wireless Stats */
1100 return ioctl_standard_call(dev,
1101 ifr,
1102 cmd,
1103 &iw_handler_get_iwstats);
1104
1105 case SIOCGIWPRIV:
1106 /* Check if we have some wireless handlers defined */
1107 if (dev->wireless_handlers != NULL) {
1108 /* We export to user space the definition of
1109 * the private handler ourselves */
1110 return ioctl_standard_call(dev,
1111 ifr,
1112 cmd,
1113 &iw_handler_get_private);
1114 }
1115 // ## Fall-through for old API ##
1116 default:
1117 /* Generic IOCTL */
1118 /* Basic check */
1119 if (!netif_device_present(dev))
1120 return -ENODEV;
1121 /* New driver API : try to find the handler */
1122 handler = get_handler(dev, cmd);
1123 if (handler != NULL) {
1124 /* Standard and private are not the same */
1125 if (cmd < SIOCIWFIRSTPRIV)
1126 return ioctl_standard_call(dev,
1127 ifr,
1128 cmd,
1129 handler);
1130 else
1131 return ioctl_private_call(dev,
1132 ifr,
1133 cmd,
1134 handler);
1135 }
1136 /* Old driver API : call driver ioctl handler */
1137 if (dev->do_ioctl) {
1138 return dev->do_ioctl(dev, ifr, cmd);
1139 }
1140 return -EOPNOTSUPP;
1141 }
1142 /* Not reached */
1143 return -EINVAL;
1144}
1145
1146
1147/************************* EVENT PROCESSING *************************/
1148/*
1149 * Process events generated by the wireless layer or the driver.
1150 * Most often, the event will be propagated through rtnetlink
1151 */
1152
1153#ifdef WE_EVENT_RTNETLINK
1154/* ---------------------------------------------------------------- */
1155/*
1156 * Locking...
1157 * ----------
1158 *
1159 * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing
1160 * the locking issue in here and implementing this code !
1161 *
1162 * The issue : wireless_send_event() is often called in interrupt context,
1163 * while the Netlink layer can never be called in interrupt context.
1164 * The fully formed RtNetlink events are queued, and then a tasklet is run
1165 * to feed those to Netlink.
1166 * The skb_queue is interrupt safe, and its lock is not held while calling
1167 * Netlink, so there is no possibility of dealock.
1168 * Jean II
1169 */
1170
1171static struct sk_buff_head wireless_nlevent_queue;
1172
1173static int __init wireless_nlevent_init(void)
1174{
1175 skb_queue_head_init(&wireless_nlevent_queue);
1176 return 0;
1177}
1178
1179subsys_initcall(wireless_nlevent_init);
1180
1181static void wireless_nlevent_process(unsigned long data)
1182{
1183 struct sk_buff *skb;
1184
1185 while ((skb = skb_dequeue(&wireless_nlevent_queue)))
1186 rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
1187}
1188
1189static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);
1190
1191/* ---------------------------------------------------------------- */
1192/*
1193 * Fill a rtnetlink message with our event data.
1194 * Note that we propage only the specified event and don't dump the
1195 * current wireless config. Dumping the wireless config is far too
1196 * expensive (for each parameter, the driver need to query the hardware).
1197 */
1198static inline int rtnetlink_fill_iwinfo(struct sk_buff * skb,
1199 struct net_device * dev,
1200 int type,
1201 char * event,
1202 int event_len)
1203{
1204 struct ifinfomsg *r;
1205 struct nlmsghdr *nlh;
1206 unsigned char *b = skb_tail_pointer(skb);
1207
1208 nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r));
1209 r = NLMSG_DATA(nlh);
1210 r->ifi_family = AF_UNSPEC;
1211 r->__ifi_pad = 0;
1212 r->ifi_type = dev->type;
1213 r->ifi_index = dev->ifindex;
1214 r->ifi_flags = dev_get_flags(dev);
1215 r->ifi_change = 0; /* Wireless changes don't affect those flags */
1216
1217 /* Add the wireless events in the netlink packet */
1218 RTA_PUT(skb, IFLA_WIRELESS, event_len, event);
1219
1220 nlh->nlmsg_len = skb_tail_pointer(skb) - b;
1221 return skb->len;
1222
1223nlmsg_failure:
1224rtattr_failure:
1225 nlmsg_trim(skb, b);
1226 return -1;
1227}
1228
1229/* ---------------------------------------------------------------- */
1230/*
1231 * Create and broadcast and send it on the standard rtnetlink socket
1232 * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c
1233 * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field
1234 * within a RTM_NEWLINK event.
1235 */
1236static inline void rtmsg_iwinfo(struct net_device * dev,
1237 char * event,
1238 int event_len)
1239{
1240 struct sk_buff *skb;
1241 int size = NLMSG_GOODSIZE;
1242
1243 skb = alloc_skb(size, GFP_ATOMIC);
1244 if (!skb)
1245 return;
1246
1247 if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK,
1248 event, event_len) < 0) {
1249 kfree_skb(skb);
1250 return;
1251 }
1252 NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
1253 skb_queue_tail(&wireless_nlevent_queue, skb);
1254 tasklet_schedule(&wireless_nlevent_tasklet);
1255}
1256
1257#endif /* WE_EVENT_RTNETLINK */
1258
1259/* ---------------------------------------------------------------- */
1260/*
1261 * Main event dispatcher. Called from other parts and drivers.
1262 * Send the event on the appropriate channels.
1263 * May be called from interrupt context.
1264 */
1265void wireless_send_event(struct net_device * dev,
1266 unsigned int cmd,
1267 union iwreq_data * wrqu,
1268 char * extra)
1269{
1270 const struct iw_ioctl_description * descr = NULL;
1271 int extra_len = 0;
1272 struct iw_event *event; /* Mallocated whole event */
1273 int event_len; /* Its size */
1274 int hdr_len; /* Size of the event header */
1275 int wrqu_off = 0; /* Offset in wrqu */
1276 /* Don't "optimise" the following variable, it will crash */
1277 unsigned cmd_index; /* *MUST* be unsigned */
1278
1279 /* Get the description of the Event */
1280 if (cmd <= SIOCIWLAST) {
1281 cmd_index = cmd - SIOCIWFIRST;
1282 if (cmd_index < standard_ioctl_num)
1283 descr = &(standard_ioctl[cmd_index]);
1284 } else {
1285 cmd_index = cmd - IWEVFIRST;
1286 if (cmd_index < standard_event_num)
1287 descr = &(standard_event[cmd_index]);
1288 }
1289 /* Don't accept unknown events */
1290 if (descr == NULL) {
1291 /* Note : we don't return an error to the driver, because
1292 * the driver would not know what to do about it. It can't
1293 * return an error to the user, because the event is not
1294 * initiated by a user request.
1295 * The best the driver could do is to log an error message.
1296 * We will do it ourselves instead...
1297 */
1298 printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n",
1299 dev->name, cmd);
1300 return;
1301 }
1302#ifdef WE_EVENT_DEBUG
1303 printk(KERN_DEBUG "%s (WE) : Got event 0x%04X\n",
1304 dev->name, cmd);
1305 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);
1306#endif /* WE_EVENT_DEBUG */
1307
1308 /* Check extra parameters and set extra_len */
1309 if (descr->header_type == IW_HEADER_TYPE_POINT) {
1310 /* Check if number of token fits within bounds */
1311 if (wrqu->data.length > descr->max_tokens) {
1312 printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length);
1313 return;
1314 }
1315 if (wrqu->data.length < descr->min_tokens) {
1316 printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length);
1317 return;
1318 }
1319 /* Calculate extra_len - extra is NULL for restricted events */
1320 if (extra != NULL)
1321 extra_len = wrqu->data.length * descr->token_size;
1322 /* Always at an offset in wrqu */
1323 wrqu_off = IW_EV_POINT_OFF;
1324#ifdef WE_EVENT_DEBUG
1325 printk(KERN_DEBUG "%s (WE) : Event 0x%04X, tokens %d, extra_len %d\n", dev->name, cmd, wrqu->data.length, extra_len);
1326#endif /* WE_EVENT_DEBUG */
1327 }
1328
1329 /* Total length of the event */
1330 hdr_len = event_type_size[descr->header_type];
1331 event_len = hdr_len + extra_len;
1332
1333#ifdef WE_EVENT_DEBUG
1334 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);
1335#endif /* WE_EVENT_DEBUG */
1336
1337 /* Create temporary buffer to hold the event */
1338 event = kmalloc(event_len, GFP_ATOMIC);
1339 if (event == NULL)
1340 return;
1341
1342 /* Fill event */
1343 event->len = event_len;
1344 event->cmd = cmd;
1345 memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
1346 if (extra != NULL)
1347 memcpy(((char *) event) + hdr_len, extra, extra_len);
1348
1349#ifdef WE_EVENT_RTNETLINK
1350 /* Send via the RtNetlink event channel */
1351 rtmsg_iwinfo(dev, (char *) event, event_len);
1352#endif /* WE_EVENT_RTNETLINK */
1353
1354 /* Cleanup */
1355 kfree(event);
1356
1357 return; /* Always success, I guess ;-) */
1358}
1359
1360/********************** ENHANCED IWSPY SUPPORT **********************/
1361/*
1362 * In the old days, the driver was handling spy support all by itself.
1363 * Now, the driver can delegate this task to Wireless Extensions.
1364 * It needs to use those standard spy iw_handler in struct iw_handler_def,
1365 * push data to us via wireless_spy_update() and include struct iw_spy_data
1366 * in its private part (and export it in net_device->wireless_data->spy_data).
1367 * One of the main advantage of centralising spy support here is that
1368 * it becomes much easier to improve and extend it without having to touch
1369 * the drivers. One example is the addition of the Spy-Threshold events.
1370 */
1371
1372/* ---------------------------------------------------------------- */
1373/*
1374 * Return the pointer to the spy data in the driver.
1375 * Because this is called on the Rx path via wireless_spy_update(),
1376 * we want it to be efficient...
1377 */
1378static inline struct iw_spy_data * get_spydata(struct net_device *dev)
1379{
1380 /* This is the new way */
1381 if (dev->wireless_data)
1382 return(dev->wireless_data->spy_data);
1383 return NULL;
1384}
1385
1386/*------------------------------------------------------------------*/
1387/*
1388 * Standard Wireless Handler : set Spy List
1389 */
1390int iw_handler_set_spy(struct net_device * dev,
1391 struct iw_request_info * info,
1392 union iwreq_data * wrqu,
1393 char * extra)
1394{
1395 struct iw_spy_data * spydata = get_spydata(dev);
1396 struct sockaddr * address = (struct sockaddr *) extra;
1397
1398 /* Make sure driver is not buggy or using the old API */
1399 if (!spydata)
1400 return -EOPNOTSUPP;
1401
1402 /* Disable spy collection while we copy the addresses.
1403 * While we copy addresses, any call to wireless_spy_update()
1404 * will NOP. This is OK, as anyway the addresses are changing. */
1405 spydata->spy_number = 0;
1406
1407 /* We want to operate without locking, because wireless_spy_update()
1408 * most likely will happen in the interrupt handler, and therefore
1409 * have its own locking constraints and needs performance.
1410 * The rtnl_lock() make sure we don't race with the other iw_handlers.
1411 * This make sure wireless_spy_update() "see" that the spy list
1412 * is temporarily disabled. */
1413 smp_wmb();
1414
1415 /* Are there are addresses to copy? */
1416 if (wrqu->data.length > 0) {
1417 int i;
1418
1419 /* Copy addresses */
1420 for (i = 0; i < wrqu->data.length; i++)
1421 memcpy(spydata->spy_address[i], address[i].sa_data,
1422 ETH_ALEN);
1423 /* Reset stats */
1424 memset(spydata->spy_stat, 0,
1425 sizeof(struct iw_quality) * IW_MAX_SPY);
1426
1427#ifdef WE_SPY_DEBUG
1428 printk(KERN_DEBUG "iw_handler_set_spy() : wireless_data %p, spydata %p, num %d\n", dev->wireless_data, spydata, wrqu->data.length);
1429 for (i = 0; i < wrqu->data.length; i++)
1430 printk(KERN_DEBUG
1431 "%02X:%02X:%02X:%02X:%02X:%02X \n",
1432 spydata->spy_address[i][0],
1433 spydata->spy_address[i][1],
1434 spydata->spy_address[i][2],
1435 spydata->spy_address[i][3],
1436 spydata->spy_address[i][4],
1437 spydata->spy_address[i][5]);
1438#endif /* WE_SPY_DEBUG */
1439 }
1440
1441 /* Make sure above is updated before re-enabling */
1442 smp_wmb();
1443
1444 /* Enable addresses */
1445 spydata->spy_number = wrqu->data.length;
1446
1447 return 0;
1448}
1449
1450/*------------------------------------------------------------------*/
1451/*
1452 * Standard Wireless Handler : get Spy List
1453 */
1454int iw_handler_get_spy(struct net_device * dev,
1455 struct iw_request_info * info,
1456 union iwreq_data * wrqu,
1457 char * extra)
1458{
1459 struct iw_spy_data * spydata = get_spydata(dev);
1460 struct sockaddr * address = (struct sockaddr *) extra;
1461 int i;
1462
1463 /* Make sure driver is not buggy or using the old API */
1464 if (!spydata)
1465 return -EOPNOTSUPP;
1466
1467 wrqu->data.length = spydata->spy_number;
1468
1469 /* Copy addresses. */
1470 for (i = 0; i < spydata->spy_number; i++) {
1471 memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
1472 address[i].sa_family = AF_UNIX;
1473 }
1474 /* Copy stats to the user buffer (just after). */
1475 if (spydata->spy_number > 0)
1476 memcpy(extra + (sizeof(struct sockaddr) *spydata->spy_number),
1477 spydata->spy_stat,
1478 sizeof(struct iw_quality) * spydata->spy_number);
1479 /* Reset updated flags. */
1480 for (i = 0; i < spydata->spy_number; i++)
1481 spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
1482 return 0;
1483}
1484
1485/*------------------------------------------------------------------*/
1486/*
1487 * Standard Wireless Handler : set spy threshold
1488 */
1489int iw_handler_set_thrspy(struct net_device * dev,
1490 struct iw_request_info *info,
1491 union iwreq_data * wrqu,
1492 char * extra)
1493{
1494 struct iw_spy_data * spydata = get_spydata(dev);
1495 struct iw_thrspy * threshold = (struct iw_thrspy *) extra;
1496
1497 /* Make sure driver is not buggy or using the old API */
1498 if (!spydata)
1499 return -EOPNOTSUPP;
1500
1501 /* Just do it */
1502 memcpy(&(spydata->spy_thr_low), &(threshold->low),
1503 2 * sizeof(struct iw_quality));
1504
1505 /* Clear flag */
1506 memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
1507
1508#ifdef WE_SPY_DEBUG
1509 printk(KERN_DEBUG "iw_handler_set_thrspy() : low %d ; high %d\n", spydata->spy_thr_low.level, spydata->spy_thr_high.level);
1510#endif /* WE_SPY_DEBUG */
1511
1512 return 0;
1513}
1514
1515/*------------------------------------------------------------------*/
1516/*
1517 * Standard Wireless Handler : get spy threshold
1518 */
1519int iw_handler_get_thrspy(struct net_device * dev,
1520 struct iw_request_info *info,
1521 union iwreq_data * wrqu,
1522 char * extra)
1523{
1524 struct iw_spy_data * spydata = get_spydata(dev);
1525 struct iw_thrspy * threshold = (struct iw_thrspy *) extra;
1526
1527 /* Make sure driver is not buggy or using the old API */
1528 if (!spydata)
1529 return -EOPNOTSUPP;
1530
1531 /* Just do it */
1532 memcpy(&(threshold->low), &(spydata->spy_thr_low),
1533 2 * sizeof(struct iw_quality));
1534
1535 return 0;
1536}
1537
1538/*------------------------------------------------------------------*/
1539/*
1540 * Prepare and send a Spy Threshold event
1541 */
1542static void iw_send_thrspy_event(struct net_device * dev,
1543 struct iw_spy_data * spydata,
1544 unsigned char * address,
1545 struct iw_quality * wstats)
1546{
1547 union iwreq_data wrqu;
1548 struct iw_thrspy threshold;
1549
1550 /* Init */
1551 wrqu.data.length = 1;
1552 wrqu.data.flags = 0;
1553 /* Copy address */
1554 memcpy(threshold.addr.sa_data, address, ETH_ALEN);
1555 threshold.addr.sa_family = ARPHRD_ETHER;
1556 /* Copy stats */
1557 memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
1558 /* Copy also thresholds */
1559 memcpy(&(threshold.low), &(spydata->spy_thr_low),
1560 2 * sizeof(struct iw_quality));
1561
1562#ifdef WE_SPY_DEBUG
1563 printk(KERN_DEBUG "iw_send_thrspy_event() : address %02X:%02X:%02X:%02X:%02X:%02X, level %d, up = %d\n",
1564 threshold.addr.sa_data[0],
1565 threshold.addr.sa_data[1],
1566 threshold.addr.sa_data[2],
1567 threshold.addr.sa_data[3],
1568 threshold.addr.sa_data[4],
1569 threshold.addr.sa_data[5], threshold.qual.level);
1570#endif /* WE_SPY_DEBUG */
1571
1572 /* Send event to user space */
1573 wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
1574}
1575
1576/* ---------------------------------------------------------------- */
1577/*
1578 * Call for the driver to update the spy data.
1579 * For now, the spy data is a simple array. As the size of the array is
1580 * small, this is good enough. If we wanted to support larger number of
1581 * spy addresses, we should use something more efficient...
1582 */
1583void wireless_spy_update(struct net_device * dev,
1584 unsigned char * address,
1585 struct iw_quality * wstats)
1586{
1587 struct iw_spy_data * spydata = get_spydata(dev);
1588 int i;
1589 int match = -1;
1590
1591 /* Make sure driver is not buggy or using the old API */
1592 if (!spydata)
1593 return;
1594
1595#ifdef WE_SPY_DEBUG
1596 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]);
1597#endif /* WE_SPY_DEBUG */
1598
1599 /* Update all records that match */
1600 for (i = 0; i < spydata->spy_number; i++)
1601 if (!compare_ether_addr(address, spydata->spy_address[i])) {
1602 memcpy(&(spydata->spy_stat[i]), wstats,
1603 sizeof(struct iw_quality));
1604 match = i;
1605 }
1606
1607 /* Generate an event if we cross the spy threshold.
1608 * To avoid event storms, we have a simple hysteresis : we generate
1609 * event only when we go under the low threshold or above the
1610 * high threshold. */
1611 if (match >= 0) {
1612 if (spydata->spy_thr_under[match]) {
1613 if (wstats->level > spydata->spy_thr_high.level) {
1614 spydata->spy_thr_under[match] = 0;
1615 iw_send_thrspy_event(dev, spydata,
1616 address, wstats);
1617 }
1618 } else {
1619 if (wstats->level < spydata->spy_thr_low.level) {
1620 spydata->spy_thr_under[match] = 1;
1621 iw_send_thrspy_event(dev, spydata,
1622 address, wstats);
1623 }
1624 }
1625 }
1626}
1627
1628EXPORT_SYMBOL(iw_handler_get_spy);
1629EXPORT_SYMBOL(iw_handler_get_thrspy);
1630EXPORT_SYMBOL(iw_handler_set_spy);
1631EXPORT_SYMBOL(iw_handler_set_thrspy);
1632EXPORT_SYMBOL(wireless_send_event);
1633EXPORT_SYMBOL(wireless_spy_update);