]>
Commit | Line | Data |
---|---|---|
7d77f03c MWP |
1 | diff -u -p linux/include/linux/wireless.19.h linux/include/linux/wireless.h |
2 | --- linux/include/linux/wireless.19.h 2006-02-17 10:49:04.000000000 -0800 | |
3 | +++ linux/include/linux/wireless.h 2006-02-17 17:58:10.000000000 -0800 | |
4 | @@ -1,10 +1,10 @@ | |
5 | /* | |
6 | * This file define a set of standard wireless extensions | |
7 | * | |
8 | - * Version : 19 18.3.05 | |
9 | + * Version : 20 17.2.06 | |
10 | * | |
11 | * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> | |
12 | - * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved. | |
13 | + * Copyright (c) 1997-2006 Jean Tourrilhes, All Rights Reserved. | |
14 | */ | |
15 | ||
16 | #ifndef _LINUX_WIRELESS_H | |
17 | @@ -80,7 +80,7 @@ | |
18 | * (there is some stuff that will be added in the future...) | |
19 | * I just plan to increment with each new version. | |
20 | */ | |
21 | -#define WIRELESS_EXT 19 | |
22 | +#define WIRELESS_EXT 20 | |
23 | ||
24 | /* | |
25 | * Changes : | |
26 | @@ -204,6 +204,10 @@ | |
27 | * - Add IW_QUAL_ALL_UPDATED and IW_QUAL_ALL_INVALID macros | |
28 | * - Add explicit flag to tell stats are in dBm : IW_QUAL_DBM | |
29 | * - Add IW_IOCTL_IDX() and IW_EVENT_IDX() macros | |
30 | + * | |
31 | + * V19 to V20 | |
32 | + * ---------- | |
33 | + * - RtNetlink requests support (SET/GET) | |
34 | */ | |
35 | ||
36 | /**************************** CONSTANTS ****************************/ | |
37 | diff -u -p linux/include/net/iw_handler.19.h linux/include/net/iw_handler.h | |
38 | --- linux/include/net/iw_handler.19.h 2006-02-17 10:49:15.000000000 -0800 | |
39 | +++ linux/include/net/iw_handler.h 2006-02-17 18:01:36.000000000 -0800 | |
40 | @@ -4,7 +4,7 @@ | |
41 | * Version : 7 18.3.05 | |
42 | * | |
43 | * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> | |
44 | - * Copyright (c) 2001-2005 Jean Tourrilhes, All Rights Reserved. | |
45 | + * Copyright (c) 2001-2006 Jean Tourrilhes, All Rights Reserved. | |
46 | */ | |
47 | ||
48 | #ifndef _IW_HANDLER_H | |
49 | @@ -436,6 +436,16 @@ extern int dev_get_wireless_info(char * | |
50 | /* Handle IOCTLs, called in net/core/dev.c */ | |
51 | extern int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd); | |
52 | ||
53 | +/* Handle RtNetlink requests, called in net/core/rtnetlink.c */ | |
54 | +extern int wireless_rtnetlink_set(struct net_device * dev, | |
55 | + char * data, | |
56 | + int len); | |
57 | +extern int wireless_rtnetlink_get(struct net_device * dev, | |
58 | + char * data, | |
59 | + int len, | |
60 | + char ** p_buf, | |
61 | + int * p_len); | |
62 | + | |
63 | /* Second : functions that may be called by driver modules */ | |
64 | ||
65 | /* Send a single event to user space */ | |
66 | diff -u -p linux/drivers/net/wireless/Kconfig.19 linux/drivers/net/wireless/Kconfig | |
67 | --- linux/drivers/net/wireless/Kconfig.19 2006-02-21 13:11:34.000000000 -0800 | |
68 | +++ linux/drivers/net/wireless/Kconfig 2006-02-22 11:48:27.000000000 -0800 | |
69 | @@ -24,6 +24,15 @@ config NET_RADIO | |
70 | the tools from | |
71 | <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>. | |
72 | ||
73 | +config NET_WIRELESS_RTNETLINK | |
74 | + bool "Wireless Extension API over RtNetlink" | |
75 | + ---help--- | |
76 | + Support the Wireless Extension API over the RtNetlink socket | |
77 | + in addition to the traditional ioctl interface (selected above). | |
78 | + | |
79 | + For now, few tools use this facility, but it might grow in the | |
80 | + future. The only downside is that it adds 4.5 kB to your kernel. | |
81 | + | |
82 | # Note : the cards are obsolete (can't buy them anymore), but the drivers | |
83 | # are not, as people are still using them... | |
84 | comment "Obsolete Wireless cards support (pre-802.11)" | |
85 | diff -u -p linux/net/core/rtnetlink.19.c linux/net/core/rtnetlink.c | |
86 | --- linux/net/core/rtnetlink.19.c 2006-02-17 10:49:26.000000000 -0800 | |
87 | +++ linux/net/core/rtnetlink.c 2006-02-21 12:57:07.000000000 -0800 | |
88 | @@ -50,6 +50,10 @@ | |
89 | #include <net/sock.h> | |
90 | #include <net/pkt_sched.h> | |
91 | #include <net/netlink.h> | |
92 | +#ifdef CONFIG_NET_WIRELESS_RTNETLINK | |
93 | +#include <linux/wireless.h> | |
94 | +#include <net/iw_handler.h> | |
95 | +#endif /* CONFIG_NET_WIRELESS_RTNETLINK */ | |
96 | ||
97 | DECLARE_MUTEX(rtnl_sem); | |
98 | ||
99 | @@ -410,6 +414,17 @@ static int do_setlink(struct sk_buff *sk | |
100 | goto out; | |
101 | } | |
102 | ||
103 | +#ifdef CONFIG_NET_WIRELESS_RTNETLINK | |
104 | + if (ida[IFLA_WIRELESS - 1]) { | |
105 | + | |
106 | + /* Call Wireless Extensions. | |
107 | + * Various stuff checked in there... */ | |
108 | + err = wireless_rtnetlink_set(dev, RTA_DATA(ida[IFLA_WIRELESS - 1]), ida[IFLA_WIRELESS - 1]->rta_len); | |
109 | + if (err) | |
110 | + goto out; | |
111 | + } | |
112 | +#endif /* CONFIG_NET_WIRELESS_RTNETLINK */ | |
113 | + | |
114 | err = 0; | |
115 | ||
116 | out: | |
117 | @@ -420,6 +435,83 @@ out: | |
118 | return err; | |
119 | } | |
120 | ||
121 | +#ifdef CONFIG_NET_WIRELESS_RTNETLINK | |
122 | +static int do_getlink(struct sk_buff *in_skb, struct nlmsghdr* in_nlh, void *arg) | |
123 | +{ | |
124 | + struct ifinfomsg *ifm = NLMSG_DATA(in_nlh); | |
125 | + struct rtattr **ida = arg; | |
126 | + struct net_device *dev; | |
127 | + struct ifinfomsg *r; | |
128 | + struct nlmsghdr *nlh; | |
129 | + int err = -ENOBUFS; | |
130 | + struct sk_buff *skb; | |
131 | + unsigned char *b; | |
132 | + char *iw_buf = NULL; | |
133 | + int iw_buf_len = 0; | |
134 | + | |
135 | + if (ifm->ifi_index >= 0) | |
136 | + dev = dev_get_by_index(ifm->ifi_index); | |
137 | + else | |
138 | + return -EINVAL; | |
139 | + if (!dev) | |
140 | + return -ENODEV; | |
141 | + | |
142 | +#ifdef CONFIG_NET_WIRELESS_RTNETLINK | |
143 | + if (ida[IFLA_WIRELESS - 1]) { | |
144 | + | |
145 | + /* Call Wireless Extensions. We need to know the size before | |
146 | + * we can alloc. Various stuff checked in there... */ | |
147 | + err = wireless_rtnetlink_get(dev, RTA_DATA(ida[IFLA_WIRELESS - 1]), ida[IFLA_WIRELESS - 1]->rta_len, &iw_buf, &iw_buf_len); | |
148 | + if (err) | |
149 | + goto out; | |
150 | + } | |
151 | +#endif /* CONFIG_NET_WIRELESS_RTNETLINK */ | |
152 | + | |
153 | + /* Create a skb big enough to include all the data. | |
154 | + * Some requests are way bigger than 4k... Jean II */ | |
155 | + skb = alloc_skb((NLMSG_LENGTH(sizeof(*r))) + (RTA_SPACE(iw_buf_len)), | |
156 | + GFP_KERNEL); | |
157 | + if (!skb) | |
158 | + goto out; | |
159 | + b = skb->tail; | |
160 | + | |
161 | + /* Put in the message the usual good stuff */ | |
162 | + nlh = NLMSG_PUT(skb, NETLINK_CB(in_skb).pid, in_nlh->nlmsg_seq, | |
163 | + RTM_NEWLINK, sizeof(*r)); | |
164 | + r = NLMSG_DATA(nlh); | |
165 | + r->ifi_family = AF_UNSPEC; | |
166 | + r->__ifi_pad = 0; | |
167 | + r->ifi_type = dev->type; | |
168 | + r->ifi_index = dev->ifindex; | |
169 | + r->ifi_flags = dev->flags; | |
170 | + r->ifi_change = 0; | |
171 | + | |
172 | + /* Put the wireless payload if it exist */ | |
173 | + if(iw_buf != NULL) | |
174 | + RTA_PUT(skb, IFLA_WIRELESS, iw_buf_len, | |
175 | + iw_buf + IW_EV_POINT_OFF); | |
176 | + | |
177 | + nlh->nlmsg_len = skb->tail - b; | |
178 | + | |
179 | + /* Needed ? */ | |
180 | + NETLINK_CB(skb).dst_pid = NETLINK_CB(in_skb).pid; | |
181 | + | |
182 | + err = netlink_unicast(rtnl, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT); | |
183 | + if (err > 0) | |
184 | + err = 0; | |
185 | +out: | |
186 | + if(iw_buf != NULL) | |
187 | + kfree(iw_buf); | |
188 | + dev_put(dev); | |
189 | + return err; | |
190 | + | |
191 | +rtattr_failure: | |
192 | +nlmsg_failure: | |
193 | + kfree_skb(skb); | |
194 | + goto out; | |
195 | +} | |
196 | +#endif /* CONFIG_NET_WIRELESS_RTNETLINK */ | |
197 | + | |
198 | static int rtnetlink_dump_all(struct sk_buff *skb, struct netlink_callback *cb) | |
199 | { | |
200 | int idx; | |
201 | @@ -585,7 +677,11 @@ static void rtnetlink_rcv(struct sock *s | |
202 | ||
203 | static struct rtnetlink_link link_rtnetlink_table[RTM_NR_MSGTYPES] = | |
204 | { | |
205 | - [RTM_GETLINK - RTM_BASE] = { .dumpit = rtnetlink_dump_ifinfo }, | |
206 | + [RTM_GETLINK - RTM_BASE] = { | |
207 | +#ifdef CONFIG_NET_WIRELESS_RTNETLINK | |
208 | + .doit = do_getlink, | |
209 | +#endif /* CONFIG_NET_WIRELESS_RTNETLINK */ | |
210 | + .dumpit = rtnetlink_dump_ifinfo }, | |
211 | [RTM_SETLINK - RTM_BASE] = { .doit = do_setlink }, | |
212 | [RTM_GETADDR - RTM_BASE] = { .dumpit = rtnetlink_dump_all }, | |
213 | [RTM_GETROUTE - RTM_BASE] = { .dumpit = rtnetlink_dump_all }, | |
214 | diff -u -p linux/net/core/wireless.19.c linux/net/core/wireless.c | |
215 | --- linux/net/core/wireless.19.c 2006-02-17 10:49:37.000000000 -0800 | |
216 | +++ linux/net/core/wireless.c 2006-02-22 11:46:52.000000000 -0800 | |
217 | @@ -2,7 +2,7 @@ | |
218 | * This file implement the Wireless Extensions APIs. | |
219 | * | |
220 | * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> | |
221 | - * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved. | |
222 | + * Copyright (c) 1997-2006 Jean Tourrilhes, All Rights Reserved. | |
223 | * | |
224 | * (As all part of the Linux kernel, this file is GPL) | |
225 | */ | |
226 | @@ -65,6 +65,9 @@ | |
227 | * o Start deprecating dev->get_wireless_stats, output a warning | |
228 | * o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless | |
229 | * o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats) | |
230 | + * | |
231 | + * v8 - 17.02.06 - Jean II | |
232 | + * o RtNetlink requests support (SET/GET) | |
233 | */ | |
234 | ||
235 | /***************************** INCLUDES *****************************/ | |
236 | @@ -89,11 +92,13 @@ | |
237 | ||
238 | /* Debugging stuff */ | |
239 | #undef WE_IOCTL_DEBUG /* Debug IOCTL API */ | |
240 | +#undef WE_RTNETLINK_DEBUG /* Debug RtNetlink API */ | |
241 | #undef WE_EVENT_DEBUG /* Debug Event dispatcher */ | |
242 | #undef WE_SPY_DEBUG /* Debug enhanced spy support */ | |
243 | ||
244 | /* Options */ | |
245 | -#define WE_EVENT_NETLINK /* Propagate events using rtnetlink */ | |
246 | +//CONFIG_NET_WIRELESS_RTNETLINK /* Wireless requests over RtNetlink */ | |
247 | +#define WE_EVENT_RTNETLINK /* Propagate events using RtNetlink */ | |
248 | #define WE_SET_EVENT /* Generate an event on some set commands */ | |
249 | ||
250 | /************************* GLOBAL VARIABLES *************************/ | |
251 | @@ -156,13 +161,18 @@ static const struct iw_ioctl_description | |
252 | .header_type = IW_HEADER_TYPE_NULL, | |
253 | }, | |
254 | [SIOCGIWPRIV - SIOCIWFIRST] = { /* (handled directly by us) */ | |
255 | - .header_type = IW_HEADER_TYPE_NULL, | |
256 | + .header_type = IW_HEADER_TYPE_POINT, | |
257 | + .token_size = sizeof(struct iw_priv_args), | |
258 | + .max_tokens = 16, | |
259 | + .flags = IW_DESCR_FLAG_NOMAX, | |
260 | }, | |
261 | [SIOCSIWSTATS - SIOCIWFIRST] = { | |
262 | .header_type = IW_HEADER_TYPE_NULL, | |
263 | }, | |
264 | [SIOCGIWSTATS - SIOCIWFIRST] = { /* (handled directly by us) */ | |
265 | - .header_type = IW_HEADER_TYPE_NULL, | |
266 | + .header_type = IW_HEADER_TYPE_POINT, | |
267 | + .token_size = 1, | |
268 | + .max_tokens = sizeof(struct iw_statistics), | |
269 | .flags = IW_DESCR_FLAG_DUMP, | |
270 | }, | |
271 | [SIOCSIWSPY - SIOCIWFIRST] = { | |
272 | @@ -529,6 +539,70 @@ static inline int adjust_priv_size(__u16 | |
273 | return num * iw_priv_type_size[type]; | |
274 | } | |
275 | ||
276 | +/* ---------------------------------------------------------------- */ | |
277 | +/* | |
278 | + * Standard Wireless Handler : get wireless stats | |
279 | + * Allow programatic access to /proc/net/wireless even if /proc | |
280 | + * doesn't exist... Also more efficient... | |
281 | + */ | |
282 | +static int iw_handler_get_iwstats(struct net_device * dev, | |
283 | + struct iw_request_info * info, | |
284 | + union iwreq_data * wrqu, | |
285 | + char * extra) | |
286 | +{ | |
287 | + /* Get stats from the driver */ | |
288 | + struct iw_statistics *stats; | |
289 | + | |
290 | + stats = get_wireless_stats(dev); | |
291 | + if (stats != (struct iw_statistics *) NULL) { | |
292 | + | |
293 | + /* Copy statistics to extra */ | |
294 | + memcpy(extra, stats, sizeof(struct iw_statistics)); | |
295 | + wrqu->data.length = sizeof(struct iw_statistics); | |
296 | + | |
297 | + /* Check if we need to clear the updated flag */ | |
298 | + if(wrqu->data.flags != 0) | |
299 | + stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; | |
300 | + return 0; | |
301 | + } else | |
302 | + return -EOPNOTSUPP; | |
303 | +} | |
304 | + | |
305 | +/* ---------------------------------------------------------------- */ | |
306 | +/* | |
307 | + * Standard Wireless Handler : get iwpriv definitions | |
308 | + * Export the driver private handler definition | |
309 | + * They will be picked up by tools like iwpriv... | |
310 | + */ | |
311 | +static int iw_handler_get_private(struct net_device * dev, | |
312 | + struct iw_request_info * info, | |
313 | + union iwreq_data * wrqu, | |
314 | + char * extra) | |
315 | +{ | |
316 | + /* Check if the driver has something to export */ | |
317 | + if((dev->wireless_handlers->num_private_args == 0) || | |
318 | + (dev->wireless_handlers->private_args == NULL)) | |
319 | + return -EOPNOTSUPP; | |
320 | + | |
321 | + /* Check if there is enough buffer up there */ | |
322 | + if(wrqu->data.length < dev->wireless_handlers->num_private_args) { | |
323 | + /* User space can't know in advance how large the buffer | |
324 | + * needs to be. Give it a hint, so that we can support | |
325 | + * any size buffer we want somewhat efficiently... */ | |
326 | + wrqu->data.length = dev->wireless_handlers->num_private_args; | |
327 | + return -E2BIG; | |
328 | + } | |
329 | + | |
330 | + /* Set the number of available ioctls. */ | |
331 | + wrqu->data.length = dev->wireless_handlers->num_private_args; | |
332 | + | |
333 | + /* Copy structure to the user buffer. */ | |
334 | + memcpy(extra, dev->wireless_handlers->private_args, | |
335 | + sizeof(struct iw_priv_args) * wrqu->data.length); | |
336 | + | |
337 | + return 0; | |
338 | +} | |
339 | + | |
340 | ||
341 | /******************** /proc/net/wireless SUPPORT ********************/ | |
342 | /* | |
343 | @@ -630,81 +704,14 @@ int __init wireless_proc_init(void) | |
344 | ||
345 | /* ---------------------------------------------------------------- */ | |
346 | /* | |
347 | - * Allow programatic access to /proc/net/wireless even if /proc | |
348 | - * doesn't exist... Also more efficient... | |
349 | - */ | |
350 | -static inline int dev_iwstats(struct net_device *dev, struct ifreq *ifr) | |
351 | -{ | |
352 | - /* Get stats from the driver */ | |
353 | - struct iw_statistics *stats; | |
354 | - | |
355 | - stats = get_wireless_stats(dev); | |
356 | - if (stats != (struct iw_statistics *) NULL) { | |
357 | - struct iwreq * wrq = (struct iwreq *)ifr; | |
358 | - | |
359 | - /* Copy statistics to the user buffer */ | |
360 | - if(copy_to_user(wrq->u.data.pointer, stats, | |
361 | - sizeof(struct iw_statistics))) | |
362 | - return -EFAULT; | |
363 | - | |
364 | - /* Check if we need to clear the updated flag */ | |
365 | - if(wrq->u.data.flags != 0) | |
366 | - stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; | |
367 | - return 0; | |
368 | - } else | |
369 | - return -EOPNOTSUPP; | |
370 | -} | |
371 | - | |
372 | -/* ---------------------------------------------------------------- */ | |
373 | -/* | |
374 | - * Export the driver private handler definition | |
375 | - * They will be picked up by tools like iwpriv... | |
376 | - */ | |
377 | -static inline int ioctl_export_private(struct net_device * dev, | |
378 | - struct ifreq * ifr) | |
379 | -{ | |
380 | - struct iwreq * iwr = (struct iwreq *) ifr; | |
381 | - | |
382 | - /* Check if the driver has something to export */ | |
383 | - if((dev->wireless_handlers->num_private_args == 0) || | |
384 | - (dev->wireless_handlers->private_args == NULL)) | |
385 | - return -EOPNOTSUPP; | |
386 | - | |
387 | - /* Check NULL pointer */ | |
388 | - if(iwr->u.data.pointer == NULL) | |
389 | - return -EFAULT; | |
390 | - | |
391 | - /* Check if there is enough buffer up there */ | |
392 | - if(iwr->u.data.length < dev->wireless_handlers->num_private_args) { | |
393 | - /* User space can't know in advance how large the buffer | |
394 | - * needs to be. Give it a hint, so that we can support | |
395 | - * any size buffer we want somewhat efficiently... */ | |
396 | - iwr->u.data.length = dev->wireless_handlers->num_private_args; | |
397 | - return -E2BIG; | |
398 | - } | |
399 | - | |
400 | - /* Set the number of available ioctls. */ | |
401 | - iwr->u.data.length = dev->wireless_handlers->num_private_args; | |
402 | - | |
403 | - /* Copy structure to the user buffer. */ | |
404 | - if (copy_to_user(iwr->u.data.pointer, | |
405 | - dev->wireless_handlers->private_args, | |
406 | - sizeof(struct iw_priv_args) * iwr->u.data.length)) | |
407 | - return -EFAULT; | |
408 | - | |
409 | - return 0; | |
410 | -} | |
411 | - | |
412 | -/* ---------------------------------------------------------------- */ | |
413 | -/* | |
414 | * Wrapper to call a standard Wireless Extension handler. | |
415 | * We do various checks and also take care of moving data between | |
416 | * user space and kernel space. | |
417 | */ | |
418 | -static inline int ioctl_standard_call(struct net_device * dev, | |
419 | - struct ifreq * ifr, | |
420 | - unsigned int cmd, | |
421 | - iw_handler handler) | |
422 | +static int ioctl_standard_call(struct net_device * dev, | |
423 | + struct ifreq * ifr, | |
424 | + unsigned int cmd, | |
425 | + iw_handler handler) | |
426 | { | |
427 | struct iwreq * iwr = (struct iwreq *) ifr; | |
428 | const struct iw_ioctl_description * descr; | |
429 | @@ -1048,14 +1055,20 @@ int wireless_process_ioctl(struct ifreq | |
430 | { | |
431 | case SIOCGIWSTATS: | |
432 | /* Get Wireless Stats */ | |
433 | - return dev_iwstats(dev, ifr); | |
434 | + return ioctl_standard_call(dev, | |
435 | + ifr, | |
436 | + cmd, | |
437 | + &iw_handler_get_iwstats); | |
438 | ||
439 | case SIOCGIWPRIV: | |
440 | /* Check if we have some wireless handlers defined */ | |
441 | if(dev->wireless_handlers != NULL) { | |
442 | /* We export to user space the definition of | |
443 | * the private handler ourselves */ | |
444 | - return ioctl_export_private(dev, ifr); | |
445 | + return ioctl_standard_call(dev, | |
446 | + ifr, | |
447 | + cmd, | |
448 | + &iw_handler_get_private); | |
449 | } | |
450 | // ## Fall-through for old API ## | |
451 | default: | |
452 | @@ -1088,16 +1101,739 @@ int wireless_process_ioctl(struct ifreq | |
453 | return -EINVAL; | |
454 | } | |
455 | ||
456 | +/********************** RTNETLINK REQUEST API **********************/ | |
457 | +/* | |
458 | + * The alternate user space API to configure all those Wireless Extensions | |
459 | + * is through RtNetlink. | |
460 | + * This API support only the new driver API (iw_handler). | |
461 | + * | |
462 | + * This RtNetlink API use the same query/reply model as the ioctl API. | |
463 | + * Maximum effort has been done to fit in the RtNetlink model, and | |
464 | + * we support both RtNetlink Set and RtNelink Get operations. | |
465 | + * On the other hand, we don't offer Dump operations because of the | |
466 | + * following reasons : | |
467 | + * o Large number of parameters, most optional | |
468 | + * o Large size of some parameters (> 100 bytes) | |
469 | + * o Each parameters need to be extracted from hardware | |
470 | + * o Scan requests can take seconds and disable network activity. | |
471 | + * Because of this high cost/overhead, we want to return only the | |
472 | + * parameters the user application is really interested in. | |
473 | + * We could offer partial Dump using the IW_DESCR_FLAG_DUMP flag. | |
474 | + * | |
475 | + * The API uses the standard RtNetlink socket. When the RtNetlink code | |
476 | + * find a IFLA_WIRELESS field in a RtNetlink SET_LINK request, | |
477 | + * it calls here. | |
478 | + */ | |
479 | + | |
480 | +#ifdef CONFIG_NET_WIRELESS_RTNETLINK | |
481 | +/* ---------------------------------------------------------------- */ | |
482 | +/* | |
483 | + * Wrapper to call a standard Wireless Extension GET handler. | |
484 | + * We do various checks and call the handler with the proper args. | |
485 | + */ | |
486 | +static int rtnetlink_standard_get(struct net_device * dev, | |
487 | + struct iw_event * request, | |
488 | + int request_len, | |
489 | + iw_handler handler, | |
490 | + char ** p_buf, | |
491 | + int * p_len) | |
492 | +{ | |
493 | + const struct iw_ioctl_description * descr = NULL; | |
494 | + unsigned int cmd; | |
495 | + union iwreq_data * wrqu; | |
496 | + int hdr_len; | |
497 | + struct iw_request_info info; | |
498 | + char * buffer = NULL; | |
499 | + int buffer_size = 0; | |
500 | + int ret = -EINVAL; | |
501 | + | |
502 | + /* Get the description of the Request */ | |
503 | + cmd = request->cmd; | |
504 | + if((cmd - SIOCIWFIRST) >= standard_ioctl_num) | |
505 | + return -EOPNOTSUPP; | |
506 | + descr = &(standard_ioctl[cmd - SIOCIWFIRST]); | |
507 | + | |
508 | +#ifdef WE_RTNETLINK_DEBUG | |
509 | + printk(KERN_DEBUG "%s (WE.r) : Found standard handler for 0x%04X\n", | |
510 | + dev->name, cmd); | |
511 | + 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); | |
512 | +#endif /* WE_RTNETLINK_DEBUG */ | |
513 | + | |
514 | + /* Check if wrqu is complete */ | |
515 | + hdr_len = event_type_size[descr->header_type]; | |
516 | + if(request_len < hdr_len) { | |
517 | +#ifdef WE_RTNETLINK_DEBUG | |
518 | + printk(KERN_DEBUG | |
519 | + "%s (WE.r) : Wireless request too short (%d)\n", | |
520 | + dev->name, request_len); | |
521 | +#endif /* WE_RTNETLINK_DEBUG */ | |
522 | + return -EINVAL; | |
523 | + } | |
524 | + | |
525 | + /* Prepare the call */ | |
526 | + info.cmd = cmd; | |
527 | + info.flags = 0; | |
528 | + | |
529 | + /* Check if we have extra data in the reply or not */ | |
530 | + if(descr->header_type != IW_HEADER_TYPE_POINT) { | |
531 | + | |
532 | + /* Create the kernel buffer that we will return. | |
533 | + * It's at an offset to match the TYPE_POINT case... */ | |
534 | + buffer_size = request_len + IW_EV_POINT_OFF; | |
535 | + buffer = kmalloc(buffer_size, GFP_KERNEL); | |
536 | + if (buffer == NULL) { | |
537 | + return -ENOMEM; | |
538 | + } | |
539 | + /* Copy event data */ | |
540 | + memcpy(buffer + IW_EV_POINT_OFF, request, request_len); | |
541 | + /* Use our own copy of wrqu */ | |
542 | + wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF | |
543 | + + IW_EV_LCP_LEN); | |
544 | + | |
545 | + /* No extra arguments. Trivial to handle */ | |
546 | + ret = handler(dev, &info, wrqu, NULL); | |
547 | + | |
548 | + } else { | |
549 | + union iwreq_data wrqu_point; | |
550 | + char * extra = NULL; | |
551 | + int extra_size = 0; | |
552 | + | |
553 | + /* Get a temp copy of wrqu (skip pointer) */ | |
554 | + memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF, | |
555 | + ((char *) request) + IW_EV_LCP_LEN, | |
556 | + IW_EV_POINT_LEN - IW_EV_LCP_LEN); | |
557 | + | |
558 | + /* Calculate space needed by arguments. Always allocate | |
559 | + * for max space. Easier, and won't last long... */ | |
560 | + extra_size = descr->max_tokens * descr->token_size; | |
561 | + /* Support for very large requests */ | |
562 | + if((descr->flags & IW_DESCR_FLAG_NOMAX) && | |
563 | + (wrqu_point.data.length > descr->max_tokens)) | |
564 | + extra_size = (wrqu_point.data.length | |
565 | + * descr->token_size); | |
566 | + buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF; | |
567 | +#ifdef WE_RTNETLINK_DEBUG | |
568 | + printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n", | |
569 | + dev->name, extra_size, buffer_size); | |
570 | +#endif /* WE_RTNETLINK_DEBUG */ | |
571 | + | |
572 | + /* Create the kernel buffer that we will return */ | |
573 | + buffer = kmalloc(buffer_size, GFP_KERNEL); | |
574 | + if (buffer == NULL) { | |
575 | + return -ENOMEM; | |
576 | + } | |
577 | + | |
578 | + /* Put wrqu in the right place (just before extra). | |
579 | + * Leave space for IWE header and dummy pointer... | |
580 | + * Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned... | |
581 | + */ | |
582 | + memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF, | |
583 | + ((char *) &wrqu_point) + IW_EV_POINT_OFF, | |
584 | + IW_EV_POINT_LEN - IW_EV_LCP_LEN); | |
585 | + wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN); | |
586 | + | |
587 | + /* Extra comes logically after that. Offset +12 bytes. */ | |
588 | + extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN; | |
589 | + | |
590 | + /* Call the handler */ | |
591 | + ret = handler(dev, &info, wrqu, extra); | |
592 | + | |
593 | + /* Calculate real returned length */ | |
594 | + extra_size = (wrqu->data.length * descr->token_size); | |
595 | + /* Re-adjust reply size */ | |
596 | + request->len = extra_size + IW_EV_POINT_LEN; | |
597 | + | |
598 | + /* Put the iwe header where it should, i.e. scrap the | |
599 | + * dummy pointer. */ | |
600 | + memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN); | |
601 | + | |
602 | +#ifdef WE_RTNETLINK_DEBUG | |
603 | + 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); | |
604 | +#endif /* WE_RTNETLINK_DEBUG */ | |
605 | + | |
606 | + /* Check if there is enough buffer up there */ | |
607 | + if(wrqu_point.data.length < wrqu->data.length) | |
608 | + ret = -E2BIG; | |
609 | + } | |
610 | + | |
611 | + /* Return the buffer to the caller */ | |
612 | + if (!ret) { | |
613 | + *p_buf = buffer; | |
614 | + *p_len = request->len; | |
615 | + } else { | |
616 | + /* Cleanup */ | |
617 | + if(buffer) | |
618 | + kfree(buffer); | |
619 | + } | |
620 | + | |
621 | + return ret; | |
622 | +} | |
623 | + | |
624 | +/* ---------------------------------------------------------------- */ | |
625 | +/* | |
626 | + * Wrapper to call a standard Wireless Extension SET handler. | |
627 | + * We do various checks and call the handler with the proper args. | |
628 | + */ | |
629 | +static inline int rtnetlink_standard_set(struct net_device * dev, | |
630 | + struct iw_event * request, | |
631 | + int request_len, | |
632 | + iw_handler handler) | |
633 | +{ | |
634 | + const struct iw_ioctl_description * descr = NULL; | |
635 | + unsigned int cmd; | |
636 | + union iwreq_data * wrqu; | |
637 | + union iwreq_data wrqu_point; | |
638 | + int hdr_len; | |
639 | + char * extra = NULL; | |
640 | + int extra_size = 0; | |
641 | + struct iw_request_info info; | |
642 | + int ret = -EINVAL; | |
643 | + | |
644 | + /* Get the description of the Request */ | |
645 | + cmd = request->cmd; | |
646 | + if((cmd - SIOCIWFIRST) >= standard_ioctl_num) | |
647 | + return -EOPNOTSUPP; | |
648 | + descr = &(standard_ioctl[cmd - SIOCIWFIRST]); | |
649 | + | |
650 | +#ifdef WE_RTNETLINK_DEBUG | |
651 | + printk(KERN_DEBUG "%s (WE.r) : Found standard SET handler for 0x%04X\n", | |
652 | + dev->name, cmd); | |
653 | + 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); | |
654 | +#endif /* WE_RTNETLINK_DEBUG */ | |
655 | + | |
656 | + /* Extract fixed header from request. This is properly aligned. */ | |
657 | + wrqu = &request->u; | |
658 | + | |
659 | + /* Check if wrqu is complete */ | |
660 | + hdr_len = event_type_size[descr->header_type]; | |
661 | + if(request_len < hdr_len) { | |
662 | +#ifdef WE_RTNETLINK_DEBUG | |
663 | + printk(KERN_DEBUG | |
664 | + "%s (WE.r) : Wireless request too short (%d)\n", | |
665 | + dev->name, request_len); | |
666 | +#endif /* WE_RTNETLINK_DEBUG */ | |
667 | + return -EINVAL; | |
668 | + } | |
669 | + | |
670 | + /* Prepare the call */ | |
671 | + info.cmd = cmd; | |
672 | + info.flags = 0; | |
673 | + | |
674 | + /* Check if we have extra data in the request or not */ | |
675 | + if(descr->header_type != IW_HEADER_TYPE_POINT) { | |
676 | + | |
677 | + /* No extra arguments. Trivial to handle */ | |
678 | + ret = handler(dev, &info, wrqu, NULL); | |
679 | + | |
680 | + } else { | |
681 | + int extra_len; | |
682 | + | |
683 | + /* Put wrqu in the right place (skip pointer) */ | |
684 | + memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF, | |
685 | + wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN); | |
686 | + /* Don't forget about the event code... */ | |
687 | + wrqu = &wrqu_point; | |
688 | + | |
689 | + /* Check if number of token fits within bounds */ | |
690 | + if(wrqu_point.data.length > descr->max_tokens) | |
691 | + return -E2BIG; | |
692 | + if(wrqu_point.data.length < descr->min_tokens) | |
693 | + return -EINVAL; | |
694 | + | |
695 | + /* Real length of payload */ | |
696 | + extra_len = wrqu_point.data.length * descr->token_size; | |
697 | + | |
698 | + /* Check if request is self consistent */ | |
699 | + if((request_len - hdr_len) < extra_len) { | |
700 | +#ifdef WE_RTNETLINK_DEBUG | |
701 | + printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n", | |
702 | + dev->name, extra_size); | |
703 | +#endif /* WE_RTNETLINK_DEBUG */ | |
704 | + return -EINVAL; | |
705 | + } | |
706 | + | |
707 | +#ifdef WE_RTNETLINK_DEBUG | |
708 | + printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n", | |
709 | + dev->name, extra_size); | |
710 | +#endif /* WE_RTNETLINK_DEBUG */ | |
711 | + | |
712 | + /* Always allocate for max space. Easier, and won't last | |
713 | + * long... */ | |
714 | + extra_size = descr->max_tokens * descr->token_size; | |
715 | + extra = kmalloc(extra_size, GFP_KERNEL); | |
716 | + if (extra == NULL) | |
717 | + return -ENOMEM; | |
718 | + | |
719 | + /* Copy extra in aligned buffer */ | |
720 | + memcpy(extra, ((char *) request) + hdr_len, extra_len); | |
721 | + | |
722 | + /* Call the handler */ | |
723 | + ret = handler(dev, &info, &wrqu_point, extra); | |
724 | + } | |
725 | + | |
726 | +#ifdef WE_SET_EVENT | |
727 | + /* Generate an event to notify listeners of the change */ | |
728 | + if((descr->flags & IW_DESCR_FLAG_EVENT) && | |
729 | + ((ret == 0) || (ret == -EIWCOMMIT))) { | |
730 | + if(descr->flags & IW_DESCR_FLAG_RESTRICT) | |
731 | + /* If the event is restricted, don't | |
732 | + * export the payload */ | |
733 | + wireless_send_event(dev, cmd, wrqu, NULL); | |
734 | + else | |
735 | + wireless_send_event(dev, cmd, wrqu, extra); | |
736 | + } | |
737 | +#endif /* WE_SET_EVENT */ | |
738 | + | |
739 | + /* Cleanup - I told you it wasn't that long ;-) */ | |
740 | + if(extra) | |
741 | + kfree(extra); | |
742 | + | |
743 | + /* Call commit handler if needed and defined */ | |
744 | + if(ret == -EIWCOMMIT) | |
745 | + ret = call_commit_handler(dev); | |
746 | + | |
747 | + return ret; | |
748 | +} | |
749 | + | |
750 | +/* ---------------------------------------------------------------- */ | |
751 | +/* | |
752 | + * Wrapper to call a private Wireless Extension GET handler. | |
753 | + * Same as above... | |
754 | + * It's not as nice and slimline as the standard wrapper. The cause | |
755 | + * is struct iw_priv_args, which was not really designed for the | |
756 | + * job we are going here. | |
757 | + * | |
758 | + * IMPORTANT : This function prevent to set and get data on the same | |
759 | + * IOCTL and enforce the SET/GET convention. Not doing it would be | |
760 | + * far too hairy... | |
761 | + * If you need to set and get data at the same time, please don't use | |
762 | + * a iw_handler but process it in your ioctl handler (i.e. use the | |
763 | + * old driver API). | |
764 | + */ | |
765 | +static inline int rtnetlink_private_get(struct net_device * dev, | |
766 | + struct iw_event * request, | |
767 | + int request_len, | |
768 | + iw_handler handler, | |
769 | + char ** p_buf, | |
770 | + int * p_len) | |
771 | +{ | |
772 | + const struct iw_priv_args * descr = NULL; | |
773 | + unsigned int cmd; | |
774 | + union iwreq_data * wrqu; | |
775 | + int hdr_len; | |
776 | + struct iw_request_info info; | |
777 | + int extra_size = 0; | |
778 | + int i; | |
779 | + char * buffer = NULL; | |
780 | + int buffer_size = 0; | |
781 | + int ret = -EINVAL; | |
782 | + | |
783 | + /* Get the description of the Request */ | |
784 | + cmd = request->cmd; | |
785 | + for(i = 0; i < dev->wireless_handlers->num_private_args; i++) | |
786 | + if(cmd == dev->wireless_handlers->private_args[i].cmd) { | |
787 | + descr = &(dev->wireless_handlers->private_args[i]); | |
788 | + break; | |
789 | + } | |
790 | + if(descr == NULL) | |
791 | + return -EOPNOTSUPP; | |
792 | + | |
793 | +#ifdef WE_RTNETLINK_DEBUG | |
794 | + printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n", | |
795 | + dev->name, cmd); | |
796 | + printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n", | |
797 | + dev->name, descr->name, descr->set_args, descr->get_args); | |
798 | +#endif /* WE_RTNETLINK_DEBUG */ | |
799 | + | |
800 | + /* Compute the max size of the get arguments */ | |
801 | + extra_size = get_priv_size(descr->get_args); | |
802 | + | |
803 | + /* Does it fits in wrqu ? */ | |
804 | + if((descr->get_args & IW_PRIV_SIZE_FIXED) && | |
805 | + (extra_size <= IFNAMSIZ)) { | |
806 | + hdr_len = extra_size; | |
807 | + extra_size = 0; | |
808 | + } else { | |
809 | + hdr_len = IW_EV_POINT_LEN; | |
810 | + } | |
811 | + | |
812 | + /* Check if wrqu is complete */ | |
813 | + if(request_len < hdr_len) { | |
814 | +#ifdef WE_RTNETLINK_DEBUG | |
815 | + printk(KERN_DEBUG | |
816 | + "%s (WE.r) : Wireless request too short (%d)\n", | |
817 | + dev->name, request_len); | |
818 | +#endif /* WE_RTNETLINK_DEBUG */ | |
819 | + return -EINVAL; | |
820 | + } | |
821 | + | |
822 | + /* Prepare the call */ | |
823 | + info.cmd = cmd; | |
824 | + info.flags = 0; | |
825 | + | |
826 | + /* Check if we have a pointer to user space data or not. */ | |
827 | + if(extra_size == 0) { | |
828 | + | |
829 | + /* Create the kernel buffer that we will return. | |
830 | + * It's at an offset to match the TYPE_POINT case... */ | |
831 | + buffer_size = request_len + IW_EV_POINT_OFF; | |
832 | + buffer = kmalloc(buffer_size, GFP_KERNEL); | |
833 | + if (buffer == NULL) { | |
834 | + return -ENOMEM; | |
835 | + } | |
836 | + /* Copy event data */ | |
837 | + memcpy(buffer + IW_EV_POINT_OFF, request, request_len); | |
838 | + /* Use our own copy of wrqu */ | |
839 | + wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF | |
840 | + + IW_EV_LCP_LEN); | |
841 | + | |
842 | + /* No extra arguments. Trivial to handle */ | |
843 | + ret = handler(dev, &info, wrqu, (char *) wrqu); | |
844 | + | |
845 | + } else { | |
846 | + char * extra; | |
847 | + | |
848 | + /* Buffer for full reply */ | |
849 | + buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF; | |
850 | + | |
851 | +#ifdef WE_RTNETLINK_DEBUG | |
852 | + printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n", | |
853 | + dev->name, extra_size, buffer_size); | |
854 | +#endif /* WE_RTNETLINK_DEBUG */ | |
855 | + | |
856 | + /* Create the kernel buffer that we will return */ | |
857 | + buffer = kmalloc(buffer_size, GFP_KERNEL); | |
858 | + if (buffer == NULL) { | |
859 | + return -ENOMEM; | |
860 | + } | |
861 | + | |
862 | + /* Put wrqu in the right place (just before extra). | |
863 | + * Leave space for IWE header and dummy pointer... | |
864 | + * Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned... | |
865 | + */ | |
866 | + memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF, | |
867 | + ((char *) request) + IW_EV_LCP_LEN, | |
868 | + IW_EV_POINT_LEN - IW_EV_LCP_LEN); | |
869 | + wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN); | |
870 | + | |
871 | + /* Extra comes logically after that. Offset +12 bytes. */ | |
872 | + extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN; | |
873 | + | |
874 | + /* Call the handler */ | |
875 | + ret = handler(dev, &info, wrqu, extra); | |
876 | + | |
877 | + /* Adjust for the actual length if it's variable, | |
878 | + * avoid leaking kernel bits outside. */ | |
879 | + if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) | |
880 | + extra_size = adjust_priv_size(descr->get_args, wrqu); | |
881 | + /* Re-adjust reply size */ | |
882 | + request->len = extra_size + IW_EV_POINT_LEN; | |
883 | + | |
884 | + /* Put the iwe header where it should, i.e. scrap the | |
885 | + * dummy pointer. */ | |
886 | + memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN); | |
887 | + | |
888 | +#ifdef WE_RTNETLINK_DEBUG | |
889 | + 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); | |
890 | +#endif /* WE_RTNETLINK_DEBUG */ | |
891 | + } | |
892 | + | |
893 | + /* Return the buffer to the caller */ | |
894 | + if (!ret) { | |
895 | + *p_buf = buffer; | |
896 | + *p_len = request->len; | |
897 | + } else { | |
898 | + /* Cleanup */ | |
899 | + if(buffer) | |
900 | + kfree(buffer); | |
901 | + } | |
902 | + | |
903 | + return ret; | |
904 | +} | |
905 | + | |
906 | +/* ---------------------------------------------------------------- */ | |
907 | +/* | |
908 | + * Wrapper to call a private Wireless Extension SET handler. | |
909 | + * Same as above... | |
910 | + * It's not as nice and slimline as the standard wrapper. The cause | |
911 | + * is struct iw_priv_args, which was not really designed for the | |
912 | + * job we are going here. | |
913 | + * | |
914 | + * IMPORTANT : This function prevent to set and get data on the same | |
915 | + * IOCTL and enforce the SET/GET convention. Not doing it would be | |
916 | + * far too hairy... | |
917 | + * If you need to set and get data at the same time, please don't use | |
918 | + * a iw_handler but process it in your ioctl handler (i.e. use the | |
919 | + * old driver API). | |
920 | + */ | |
921 | +static inline int rtnetlink_private_set(struct net_device * dev, | |
922 | + struct iw_event * request, | |
923 | + int request_len, | |
924 | + iw_handler handler) | |
925 | +{ | |
926 | + const struct iw_priv_args * descr = NULL; | |
927 | + unsigned int cmd; | |
928 | + union iwreq_data * wrqu; | |
929 | + union iwreq_data wrqu_point; | |
930 | + int hdr_len; | |
931 | + char * extra = NULL; | |
932 | + int extra_size = 0; | |
933 | + int offset = 0; /* For sub-ioctls */ | |
934 | + struct iw_request_info info; | |
935 | + int i; | |
936 | + int ret = -EINVAL; | |
937 | + | |
938 | + /* Get the description of the Request */ | |
939 | + cmd = request->cmd; | |
940 | + for(i = 0; i < dev->wireless_handlers->num_private_args; i++) | |
941 | + if(cmd == dev->wireless_handlers->private_args[i].cmd) { | |
942 | + descr = &(dev->wireless_handlers->private_args[i]); | |
943 | + break; | |
944 | + } | |
945 | + if(descr == NULL) | |
946 | + return -EOPNOTSUPP; | |
947 | + | |
948 | +#ifdef WE_RTNETLINK_DEBUG | |
949 | + printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n", | |
950 | + ifr->ifr_name, cmd); | |
951 | + printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n", | |
952 | + dev->name, descr->name, descr->set_args, descr->get_args); | |
953 | +#endif /* WE_RTNETLINK_DEBUG */ | |
954 | + | |
955 | + /* Compute the size of the set arguments */ | |
956 | + /* Check for sub-ioctl handler */ | |
957 | + if(descr->name[0] == '\0') | |
958 | + /* Reserve one int for sub-ioctl index */ | |
959 | + offset = sizeof(__u32); | |
960 | + | |
961 | + /* Size of set arguments */ | |
962 | + extra_size = get_priv_size(descr->set_args); | |
963 | + | |
964 | + /* Does it fits in wrqu ? */ | |
965 | + if((descr->set_args & IW_PRIV_SIZE_FIXED) && | |
966 | + (extra_size <= IFNAMSIZ)) { | |
967 | + hdr_len = IW_EV_LCP_LEN + extra_size; | |
968 | + extra_size = 0; | |
969 | + } else { | |
970 | + hdr_len = IW_EV_POINT_LEN; | |
971 | + } | |
972 | + | |
973 | + /* Extract fixed header from request. This is properly aligned. */ | |
974 | + wrqu = &request->u; | |
975 | + | |
976 | + /* Check if wrqu is complete */ | |
977 | + if(request_len < hdr_len) { | |
978 | +#ifdef WE_RTNETLINK_DEBUG | |
979 | + printk(KERN_DEBUG | |
980 | + "%s (WE.r) : Wireless request too short (%d)\n", | |
981 | + dev->name, request_len); | |
982 | +#endif /* WE_RTNETLINK_DEBUG */ | |
983 | + return -EINVAL; | |
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 | + | |
993 | + /* No extra arguments. Trivial to handle */ | |
994 | + ret = handler(dev, &info, wrqu, (char *) wrqu); | |
995 | + | |
996 | + } else { | |
997 | + int extra_len; | |
998 | + | |
999 | + /* Put wrqu in the right place (skip pointer) */ | |
1000 | + memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF, | |
1001 | + wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN); | |
1002 | + | |
1003 | + /* Does it fits within bounds ? */ | |
1004 | + if(wrqu_point.data.length > (descr->set_args & | |
1005 | + IW_PRIV_SIZE_MASK)) | |
1006 | + return -E2BIG; | |
1007 | + | |
1008 | + /* Real length of payload */ | |
1009 | + extra_len = adjust_priv_size(descr->set_args, &wrqu_point); | |
1010 | + | |
1011 | + /* Check if request is self consistent */ | |
1012 | + if((request_len - hdr_len) < extra_len) { | |
1013 | +#ifdef WE_RTNETLINK_DEBUG | |
1014 | + printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n", | |
1015 | + dev->name, extra_size); | |
1016 | +#endif /* WE_RTNETLINK_DEBUG */ | |
1017 | + return -EINVAL; | |
1018 | + } | |
1019 | + | |
1020 | +#ifdef WE_RTNETLINK_DEBUG | |
1021 | + printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n", | |
1022 | + dev->name, extra_size); | |
1023 | +#endif /* WE_RTNETLINK_DEBUG */ | |
1024 | + | |
1025 | + /* Always allocate for max space. Easier, and won't last | |
1026 | + * long... */ | |
1027 | + extra = kmalloc(extra_size, GFP_KERNEL); | |
1028 | + if (extra == NULL) | |
1029 | + return -ENOMEM; | |
1030 | + | |
1031 | + /* Copy extra in aligned buffer */ | |
1032 | + memcpy(extra, ((char *) request) + hdr_len, extra_len); | |
1033 | + | |
1034 | + /* Call the handler */ | |
1035 | + ret = handler(dev, &info, &wrqu_point, extra); | |
1036 | + | |
1037 | + /* Cleanup - I told you it wasn't that long ;-) */ | |
1038 | + kfree(extra); | |
1039 | + } | |
1040 | + | |
1041 | + /* Call commit handler if needed and defined */ | |
1042 | + if(ret == -EIWCOMMIT) | |
1043 | + ret = call_commit_handler(dev); | |
1044 | + | |
1045 | + return ret; | |
1046 | +} | |
1047 | + | |
1048 | +/* ---------------------------------------------------------------- */ | |
1049 | +/* | |
1050 | + * Main RtNetlink dispatcher. Called from the main networking code | |
1051 | + * (do_getlink() in net/core/rtnetlink.c). | |
1052 | + * Check the type of Request and call the appropriate wrapper... | |
1053 | + */ | |
1054 | +int wireless_rtnetlink_get(struct net_device * dev, | |
1055 | + char * data, | |
1056 | + int len, | |
1057 | + char ** p_buf, | |
1058 | + int * p_len) | |
1059 | +{ | |
1060 | + struct iw_event * request = (struct iw_event *) data; | |
1061 | + iw_handler handler; | |
1062 | + | |
1063 | + /* Check length */ | |
1064 | + if(len < IW_EV_LCP_LEN) { | |
1065 | + printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n", | |
1066 | + dev->name, len); | |
1067 | + return -EINVAL; | |
1068 | + } | |
1069 | + | |
1070 | + /* ReCheck length (len may have padding) */ | |
1071 | + if(request->len > len) { | |
1072 | + printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n", | |
1073 | + dev->name, request->len, len); | |
1074 | + return -EINVAL; | |
1075 | + } | |
1076 | + | |
1077 | + /* Only accept GET requests in here */ | |
1078 | + if(!IW_IS_GET(request->cmd)) | |
1079 | + return -EOPNOTSUPP; | |
1080 | + | |
1081 | + /* Special cases */ | |
1082 | + if(request->cmd == SIOCGIWSTATS) | |
1083 | + /* Get Wireless Stats */ | |
1084 | + return rtnetlink_standard_get(dev, | |
1085 | + request, | |
1086 | + request->len, | |
1087 | + &iw_handler_get_iwstats, | |
1088 | + p_buf, p_len); | |
1089 | + if(request->cmd == SIOCGIWPRIV) { | |
1090 | + /* Check if we have some wireless handlers defined */ | |
1091 | + if(dev->wireless_handlers == NULL) | |
1092 | + return -EOPNOTSUPP; | |
1093 | + /* Get Wireless Stats */ | |
1094 | + return rtnetlink_standard_get(dev, | |
1095 | + request, | |
1096 | + request->len, | |
1097 | + &iw_handler_get_private, | |
1098 | + p_buf, p_len); | |
1099 | + } | |
1100 | + | |
1101 | + /* Basic check */ | |
1102 | + if (!netif_device_present(dev)) | |
1103 | + return -ENODEV; | |
1104 | + | |
1105 | + /* Try to find the handler */ | |
1106 | + handler = get_handler(dev, request->cmd); | |
1107 | + if(handler != NULL) { | |
1108 | + /* Standard and private are not the same */ | |
1109 | + if(request->cmd < SIOCIWFIRSTPRIV) | |
1110 | + return rtnetlink_standard_get(dev, | |
1111 | + request, | |
1112 | + request->len, | |
1113 | + handler, | |
1114 | + p_buf, p_len); | |
1115 | + else | |
1116 | + return rtnetlink_private_get(dev, | |
1117 | + request, | |
1118 | + request->len, | |
1119 | + handler, | |
1120 | + p_buf, p_len); | |
1121 | + } | |
1122 | + | |
1123 | + return -EOPNOTSUPP; | |
1124 | +} | |
1125 | + | |
1126 | +/* ---------------------------------------------------------------- */ | |
1127 | +/* | |
1128 | + * Main RtNetlink dispatcher. Called from the main networking code | |
1129 | + * (do_setlink() in net/core/rtnetlink.c). | |
1130 | + * Check the type of Request and call the appropriate wrapper... | |
1131 | + */ | |
1132 | +int wireless_rtnetlink_set(struct net_device * dev, | |
1133 | + char * data, | |
1134 | + int len) | |
1135 | +{ | |
1136 | + struct iw_event * request = (struct iw_event *) data; | |
1137 | + iw_handler handler; | |
1138 | + | |
1139 | + /* Check length */ | |
1140 | + if(len < IW_EV_LCP_LEN) { | |
1141 | + printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n", | |
1142 | + dev->name, len); | |
1143 | + return -EINVAL; | |
1144 | + } | |
1145 | + | |
1146 | + /* ReCheck length (len may have padding) */ | |
1147 | + if(request->len > len) { | |
1148 | + printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n", | |
1149 | + dev->name, request->len, len); | |
1150 | + return -EINVAL; | |
1151 | + } | |
1152 | + | |
1153 | + /* Only accept SET requests in here */ | |
1154 | + if(!IW_IS_SET(request->cmd)) | |
1155 | + return -EOPNOTSUPP; | |
1156 | + | |
1157 | + /* Basic check */ | |
1158 | + if (!netif_device_present(dev)) | |
1159 | + return -ENODEV; | |
1160 | + | |
1161 | + /* New driver API : try to find the handler */ | |
1162 | + handler = get_handler(dev, request->cmd); | |
1163 | + if(handler != NULL) { | |
1164 | + /* Standard and private are not the same */ | |
1165 | + if(request->cmd < SIOCIWFIRSTPRIV) | |
1166 | + return rtnetlink_standard_set(dev, | |
1167 | + request, | |
1168 | + request->len, | |
1169 | + handler); | |
1170 | + else | |
1171 | + return rtnetlink_private_set(dev, | |
1172 | + request, | |
1173 | + request->len, | |
1174 | + handler); | |
1175 | + } | |
1176 | + | |
1177 | + return -EOPNOTSUPP; | |
1178 | +} | |
1179 | +#endif /* CONFIG_NET_WIRELESS_RTNETLINK */ | |
1180 | + | |
1181 | + | |
1182 | /************************* EVENT PROCESSING *************************/ | |
1183 | /* | |
1184 | * Process events generated by the wireless layer or the driver. | |
1185 | * Most often, the event will be propagated through rtnetlink | |
1186 | */ | |
1187 | ||
1188 | -#ifdef WE_EVENT_NETLINK | |
1189 | -/* "rtnl" is defined in net/core/rtnetlink.c, but we need it here. | |
1190 | - * It is declared in <linux/rtnetlink.h> */ | |
1191 | - | |
1192 | +#ifdef WE_EVENT_RTNETLINK | |
1193 | /* ---------------------------------------------------------------- */ | |
1194 | /* | |
1195 | * Fill a rtnetlink message with our event data. | |
1196 | @@ -1121,12 +1857,11 @@ static inline int rtnetlink_fill_iwinfo( | |
1197 | r->__ifi_pad = 0; | |
1198 | r->ifi_type = dev->type; | |
1199 | r->ifi_index = dev->ifindex; | |
1200 | - r->ifi_flags = dev->flags; | |
1201 | + r->ifi_flags = dev_get_flags(dev); | |
1202 | r->ifi_change = 0; /* Wireless changes don't affect those flags */ | |
1203 | ||
1204 | /* Add the wireless events in the netlink packet */ | |
1205 | - RTA_PUT(skb, IFLA_WIRELESS, | |
1206 | - event_len, event); | |
1207 | + RTA_PUT(skb, IFLA_WIRELESS, event_len, event); | |
1208 | ||
1209 | nlh->nlmsg_len = skb->tail - b; | |
1210 | return skb->len; | |
1211 | @@ -1163,7 +1898,7 @@ static inline void rtmsg_iwinfo(struct n | |
1212 | NETLINK_CB(skb).dst_group = RTNLGRP_LINK; | |
1213 | netlink_broadcast(rtnl, skb, 0, RTNLGRP_LINK, GFP_ATOMIC); | |
1214 | } | |
1215 | -#endif /* WE_EVENT_NETLINK */ | |
1216 | +#endif /* WE_EVENT_RTNETLINK */ | |
1217 | ||
1218 | /* ---------------------------------------------------------------- */ | |
1219 | /* | |
1220 | @@ -1255,10 +1990,10 @@ void wireless_send_event(struct net_devi | |
1221 | if(extra != NULL) | |
1222 | memcpy(((char *) event) + hdr_len, extra, extra_len); | |
1223 | ||
1224 | -#ifdef WE_EVENT_NETLINK | |
1225 | - /* rtnetlink event channel */ | |
1226 | +#ifdef WE_EVENT_RTNETLINK | |
1227 | + /* Send via the RtNetlink event channel */ | |
1228 | rtmsg_iwinfo(dev, (char *) event, event_len); | |
1229 | -#endif /* WE_EVENT_NETLINK */ | |
1230 | +#endif /* WE_EVENT_RTNETLINK */ | |
1231 | ||
1232 | /* Cleanup */ | |
1233 | kfree(event); |