add SIOCSIWAP to wlcompat
[openwrt.git] / package / openwrt / wlcompat.c
1 /*
2  * wlcompat.c
3  *
4  * Copyright (C) 2005 Mike Baker,
5  *                    Felix Fietkau <nbd@vd-s.ath.cx>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *
21  * $Id$
22  */
23
24
25 #include <linux/config.h>
26 #include <linux/module.h>
27 #include <linux/init.h>
28 #include <linux/if_arp.h>
29 #include <asm/uaccess.h>
30 #include <linux/wireless.h>
31
32 #include <net/iw_handler.h>
33 #include <wlioctl.h>
34
35 static struct net_device *dev;
36 char buf[WLC_IOCTL_MAXLEN];
37
38 /* The frequency of each channel in MHz */
39 const long channel_frequency[] = {
40         2412, 2417, 2422, 2427, 2432, 2437, 2442,
41         2447, 2452, 2457, 2462, 2467, 2472, 2484
42 };
43 #define NUM_CHANNELS ( sizeof(channel_frequency) / sizeof(channel_frequency[0]) )
44
45 static int wlcompat_private_ioctl(struct net_device *dev,
46                          struct iw_request_info *info,
47                          union iwreq_data *wrqu,
48                          char *extra);
49
50 static int wl_ioctl(struct net_device *dev, int cmd, void *buf, int len)
51 {
52         mm_segment_t old_fs = get_fs();
53         struct ifreq ifr;
54         int ret;
55         wl_ioctl_t ioc;
56         ioc.cmd = cmd;
57         ioc.buf = buf;
58         ioc.len = len;
59         strncpy(ifr.ifr_name, dev->name, IFNAMSIZ);
60         ifr.ifr_data = (caddr_t) &ioc;
61         set_fs(KERNEL_DS);
62         ret = dev->do_ioctl(dev,&ifr,SIOCDEVPRIVATE);
63         set_fs (old_fs);
64         return ret;
65 }
66
67 static int wl_set_val(struct net_device *dev, char *var, void *val, int len)
68 {
69         char buf[128];
70         int buf_len;
71
72         /* check for overflow */
73         if ((buf_len = strlen(var)) + 1 + len > sizeof(buf))
74                 return -1;
75         
76         strcpy(buf, var);
77         buf_len += 1;
78
79         /* append int value onto the end of the name string */
80         memcpy(&buf[buf_len], val, len);
81         buf_len += len;
82
83         return wl_ioctl(dev, WLC_SET_VAR, buf, buf_len);
84 }
85
86 static int wl_get_val(struct net_device *dev, char *var, void *val, int len)
87 {
88         char buf[128];
89         int ret;
90
91         /* check for overflow */
92         if (strlen(var) + 1 > sizeof(buf) || len > sizeof(buf))
93                 return -1;
94         
95         strcpy(buf, var);
96         if ((ret = wl_ioctl(dev, WLC_GET_VAR, buf, sizeof(buf))))
97                 return ret;
98
99         memcpy(val, buf, len);
100         return 0;
101 }
102
103
104 static int wlcompat_ioctl_getiwrange(struct net_device *dev,
105                                     char *extra)
106 {
107         int i, k;
108         struct iw_range *range;
109
110         range = (struct iw_range *) extra;
111
112         range->we_version_compiled = WIRELESS_EXT;
113         range->we_version_source = WIRELESS_EXT;
114         
115         range->min_nwid = range->max_nwid = 0;
116         
117         range->num_channels = NUM_CHANNELS;
118         k = 0;
119         for (i = 0; i < NUM_CHANNELS; i++) {
120                 range->freq[k].i = i + 1;
121                 range->freq[k].m = channel_frequency[i] * 100000;
122                 range->freq[k].e = 1;
123                 k++;
124                 if (k >= IW_MAX_FREQUENCIES)
125                         break;
126         }
127         range->num_frequency = k;
128         range->sensitivity = 3;
129
130         /* nbd: don't know what this means, but other drivers set it this way */
131         range->pmp_flags = IW_POWER_PERIOD;
132         range->pmt_flags = IW_POWER_TIMEOUT;
133         range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_UNICAST_R;
134
135         range->min_pmp = 0;
136         range->max_pmp = 65535000;
137         range->min_pmt = 0;
138         range->max_pmt = 65535 * 1000;
139
140         range->min_rts = 0;
141         if (wl_ioctl(dev, WLC_GET_RTS, &range->max_rts, sizeof(int)) < 0)
142                 range->max_rts = 2347;
143
144         range->min_frag = 256;
145         
146         if (wl_ioctl(dev, WLC_GET_FRAG, &range->max_frag, sizeof(int)) < 0)
147                 range->max_frag = 2346;
148
149         range->txpower_capa = IW_TXPOW_MWATT;
150
151         return 0;
152 }
153
154
155 static int wlcompat_set_scan(struct net_device *dev,
156                          struct iw_request_info *info,
157                          union iwreq_data *wrqu,
158                          char *extra)
159 {
160         int ap = 0, oldap = 0;
161         wl_scan_params_t params;
162
163         memset(&params, 0, sizeof(params));
164
165         /* use defaults (same parameters as wl scan) */
166         memset(&params.bssid, 0xff, sizeof(params.bssid));
167         params.bss_type = DOT11_BSSTYPE_ANY;
168         params.scan_type = -1;
169         params.nprobes = -1;
170         params.active_time = -1;
171         params.passive_time = -1;
172         params.home_time = -1;
173         
174         /* can only scan in STA mode */
175         wl_ioctl(dev, WLC_GET_AP, &oldap, sizeof(oldap));
176         if (oldap > 0)
177                 wl_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap));
178         
179         if (wl_ioctl(dev, WLC_SCAN, &params, 64) < 0)
180                 return -EINVAL;
181         
182         if (oldap > 0)
183                 wl_ioctl(dev, WLC_SET_AP, &oldap, sizeof(oldap));
184
185         return 0;
186 }
187
188
189 static int wlcompat_get_scan(struct net_device *dev,
190                          struct iw_request_info *info,
191                          union iwreq_data *wrqu,
192                          char *extra)
193 {
194         wl_scan_results_t *results = (wl_scan_results_t *) buf;
195         wl_bss_info_t *bss_info;
196         char *info_ptr;
197         char *current_ev = extra;
198         char *current_val;
199         char *end_buf = extra + IW_SCAN_MAX_DATA;
200         struct iw_event iwe;
201         int i, j;
202
203         if (wl_ioctl(dev, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0)
204                 return -EAGAIN;
205         
206         bss_info = &(results->bss_info[0]);
207         info_ptr = (char *) bss_info;
208         for (i = 0; i < results->count; i++) {
209
210                 /* send the cell address (must be sent first) */
211                 iwe.cmd = SIOCGIWAP;
212                 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
213                 memcpy(&iwe.u.ap_addr.sa_data, &bss_info->BSSID, sizeof(bss_info->BSSID));
214                 current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN);
215                 
216                 /* send the ESSID */
217                 iwe.cmd = SIOCGIWESSID;
218                 iwe.u.data.length = bss_info->SSID_len;
219                 if (iwe.u.data.length > IW_ESSID_MAX_SIZE)
220                         iwe.u.data.length = IW_ESSID_MAX_SIZE;
221                 iwe.u.data.flags = 1;
222                 current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss_info->SSID);
223
224                 /* send frequency/channel info */
225                 iwe.cmd = SIOCGIWFREQ;
226                 iwe.u.freq.e = 0;
227                 iwe.u.freq.m = bss_info->channel;
228                 current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN);
229
230                 /* add quality statistics */
231                 iwe.cmd = IWEVQUAL;
232                 iwe.u.qual.level = bss_info->RSSI;
233                 iwe.u.qual.noise = bss_info->phy_noise;
234                 iwe.u.qual.qual = 0;
235                 current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN);
236         
237                 /* send rate information */
238                 iwe.cmd = SIOCGIWRATE;
239                 current_val = current_ev + IW_EV_LCP_LEN;
240                 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
241                 
242                 for(j = 0 ; j < bss_info->rateset.count ; j++) {
243                         iwe.u.bitrate.value = ((bss_info->rateset.rates[j] & 0x7f) * 500000);
244                         current_val = iwe_stream_add_value(current_ev, current_val, end_buf, &iwe, IW_EV_PARAM_LEN);
245                 }
246                 if((current_val - current_ev) > IW_EV_LCP_LEN)
247                         current_ev = current_val;
248
249                 info_ptr += sizeof(wl_bss_info_t);
250                 if (bss_info->ie_length % 4)
251                         info_ptr += bss_info->ie_length + 4 - (bss_info->ie_length % 4);
252                 else
253                         info_ptr += bss_info->ie_length;
254                 bss_info = (wl_bss_info_t *) info_ptr;
255         }
256         
257         wrqu->data.length = (current_ev - extra);
258         wrqu->data.flags = 0;
259
260         return 0;
261 }
262
263 static int wlcompat_ioctl(struct net_device *dev,
264                          struct iw_request_info *info,
265                          union iwreq_data *wrqu,
266                          char *extra)
267 {
268         switch (info->cmd) {
269                 case SIOCGIWNAME:
270                         strcpy(wrqu->name, "IEEE 802.11-DS");
271                         break;
272                 case SIOCGIWFREQ:
273                 {
274                         channel_info_t ci;
275
276                         if (wl_ioctl(dev,WLC_GET_CHANNEL, &ci, sizeof(ci)) < 0)
277                                 return -EINVAL;
278
279                         wrqu->freq.m = ci.target_channel;
280                         wrqu->freq.e = 0;
281                         break;
282                 }
283                 case SIOCSIWFREQ:
284                 {
285                         if (wrqu->freq.e == 1) {
286                                 int channel = 0;
287                                 int f = wrqu->freq.m / 100000;
288                                 while ((channel < NUM_CHANNELS + 1) && (f != channel_frequency[channel]))
289                                         channel++;
290                                 
291                                 if (channel == NUM_CHANNELS) // channel not found
292                                         return -EINVAL;
293
294                                 wrqu->freq.e = 0;
295                                 wrqu->freq.m = channel + 1;
296                         }
297                         if ((wrqu->freq.e == 0) && (wrqu->freq.m < 1000)) {
298                                 if (wl_ioctl(dev, WLC_SET_CHANNEL, &wrqu->freq.m, sizeof(int)) < 0)
299                                         return -EINVAL;
300                         } else {
301                                 return -EINVAL;
302                         }
303                         break;
304                 }
305                 case SIOCSIWAP:
306                 {
307                         if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
308                                 return -EINVAL;
309
310                         if (wl_ioctl(dev,WLC_SET_BSSID,wrqu->ap_addr.sa_data,6) < 0)
311                                 return -EINVAL;
312
313                         break;
314                 }
315                 case SIOCGIWAP:
316                 {
317                         wrqu->ap_addr.sa_family = ARPHRD_ETHER;
318                         if (wl_ioctl(dev,WLC_GET_BSSID,wrqu->ap_addr.sa_data,6) < 0)
319                                 return -EINVAL;
320                         break;
321                 }
322                 case SIOCGIWESSID:
323                 {
324                         wlc_ssid_t ssid;
325                         
326                         if (wl_ioctl(dev,WLC_GET_SSID, &ssid, sizeof(wlc_ssid_t)) < 0)
327                                 return -EINVAL;
328
329                         wrqu->essid.flags = wrqu->data.flags = 1;
330                         wrqu->essid.length = wrqu->data.length = ssid.SSID_len + 1;
331                         memcpy(extra,ssid.SSID,ssid.SSID_len + 1);
332                         break;
333                 }
334                 case SIOCSIWESSID:
335                 {
336                         wlc_ssid_t ssid;
337                         memset(&ssid, 0, sizeof(ssid));
338                         ssid.SSID_len = strlen(extra);
339                         if (ssid.SSID_len > WLC_ESSID_MAX_SIZE)
340                                 ssid.SSID_len = WLC_ESSID_MAX_SIZE;
341                         memcpy(ssid.SSID, extra, ssid.SSID_len);
342                         if (wl_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid)) < 0)
343                                 return -EINVAL;
344                         break;
345                 }
346                 case SIOCGIWRTS:
347                 {
348                         if (wl_ioctl(dev,WLC_GET_RTS,&(wrqu->rts.value),sizeof(int)) < 0) 
349                                 return -EINVAL;
350                         break;
351                 }
352                 case SIOCSIWRTS:
353                 {
354                         if (wl_ioctl(dev,WLC_SET_RTS,&(wrqu->rts.value),sizeof(int)) < 0) 
355                                 return -EINVAL;
356                         break;
357                 }
358                 case SIOCGIWFRAG:
359                 {
360                         if (wl_ioctl(dev,WLC_GET_FRAG,&(wrqu->frag.value),sizeof(int)) < 0)
361                                 return -EINVAL;
362                         break;
363                 }
364                 case SIOCSIWFRAG:
365                 {
366                         if (wl_ioctl(dev,WLC_SET_FRAG,&(wrqu->frag.value),sizeof(int)) < 0)
367                                 return -EINVAL;
368                         break;
369                 }
370                 case SIOCGIWTXPOW:
371                 {
372                         if (wl_get_val(dev, "qtxpower", &(wrqu->txpower.value), sizeof(int)) < 0)
373                                 return -EINVAL;
374                         
375                         wrqu->txpower.value &= ~WL_TXPWR_OVERRIDE;
376                                 
377                         wrqu->txpower.fixed = 0;
378                         wrqu->txpower.disabled = 0;
379                         wrqu->txpower.flags = IW_TXPOW_MWATT;
380                         break;
381                 }
382                 case SIOCSIWTXPOW:
383                 {
384                         int override;
385                         
386                         if (wl_get_val(dev, "qtxpower", &override, sizeof(int)) < 0)
387                                 return -EINVAL;
388                         
389                         wrqu->txpower.value |= override & WL_TXPWR_OVERRIDE;
390                         
391                         if (wrqu->txpower.flags != IW_TXPOW_MWATT)
392                                 return -EINVAL;
393
394                         if (wl_set_val(dev, "qtxpower", &wrqu->txpower.value, sizeof(int)) < 0)
395                                 return -EINVAL;
396                 }
397                 case SIOCGIWENCODE:
398                 {
399                         int val = 0;
400                         if (wl_ioctl(dev, WLC_GET_WEP, &val, sizeof(val)) < 0)
401                                 return -EINVAL;
402                         
403                         if (val > 0) {
404                                 wrqu->data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
405                         } else {
406                                 wrqu->data.flags = IW_ENCODE_DISABLED;
407                         }
408                                 
409                                 
410                         
411                         break;
412                 }
413                 case SIOCGIWRANGE:
414                 {
415                         return wlcompat_ioctl_getiwrange(dev, extra);
416                         break;
417                 }
418                 case SIOCSIWMODE:
419                 {
420                         int ap = -1, infra = -1, passive = 0, wet = 0;
421                         
422                         switch (wrqu->mode) {
423                                 case IW_MODE_MONITOR:
424                                         passive = 1;
425                                         break;
426                                 case IW_MODE_ADHOC:
427                                         infra = 0;
428                                         ap = 0;
429                                         break;
430                                 case IW_MODE_MASTER:
431                                         infra = 1;
432                                         ap = 1;
433                                         break;
434                                 case IW_MODE_INFRA:
435                                         infra = 1;
436                                         ap = 0;
437                                         break;
438                                 case IW_MODE_REPEAT:
439                                         infra = 1;
440                                         ap = 0;
441                                         wet = 1;
442                                         break;
443                                 default:
444                                         return -EINVAL;
445                         }
446                         
447                         if (wl_ioctl(dev, WLC_SET_PASSIVE, &passive, sizeof(passive)) < 0)
448                                 return -EINVAL;
449                         if (wl_ioctl(dev, WLC_SET_MONITOR, &passive, sizeof(passive)) < 0)
450                                 return -EINVAL;
451                         if (wl_ioctl(dev, WLC_SET_WET, &wet, sizeof(wet)) < 0)
452                                 return -EINVAL;
453                         if (ap >= 0)
454                                 if (wl_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap)) < 0)
455                                         return -EINVAL;
456                         if (infra >= 0)
457                                 if (wl_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(infra)) < 0)
458                                         return -EINVAL;
459
460                         break;
461                                                 
462                 }
463                 case SIOCGIWMODE:
464                 {
465                         int ap, infra, wet, passive;
466
467                         if (wl_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap)) < 0)
468                                 return -EINVAL;
469                         if (wl_ioctl(dev, WLC_GET_INFRA, &infra, sizeof(infra)) < 0)
470                                 return -EINVAL;
471                         if (wl_ioctl(dev, WLC_GET_PASSIVE, &passive, sizeof(passive)) < 0)
472                                 return -EINVAL;
473                         if (wl_ioctl(dev, WLC_GET_WET, &wet, sizeof(wet)) < 0)
474                                 return -EINVAL;
475
476                         if (passive) {
477                                 wrqu->mode = IW_MODE_MONITOR;
478                         } else if (!infra) {
479                                 wrqu->mode = IW_MODE_ADHOC;
480                         } else {
481                                 if (ap) {
482                                         wrqu->mode = IW_MODE_MASTER;
483                                 } else {
484                                         if (wet) {
485                                                 wrqu->mode = IW_MODE_REPEAT;
486                                         } else {
487                                                 wrqu->mode = IW_MODE_INFRA;
488                                         }
489                                 }
490                         }
491                         break;
492                 }
493                 default:
494                 {
495                         if (info->cmd >= SIOCIWFIRSTPRIV)
496                                 return wlcompat_private_ioctl(dev, info, wrqu, extra);
497
498                         return -EINVAL;
499                 }
500         }
501         
502         return 0;
503 }
504
505 static const iw_handler  wlcompat_handler[] = {
506         NULL,                   /* SIOCSIWCOMMIT */
507         wlcompat_ioctl,         /* SIOCGIWNAME */
508         NULL,                   /* SIOCSIWNWID */
509         NULL,                   /* SIOCGIWNWID */
510         wlcompat_ioctl,         /* SIOCSIWFREQ */
511         wlcompat_ioctl,         /* SIOCGIWFREQ */
512         wlcompat_ioctl,         /* SIOCSIWMODE */
513         wlcompat_ioctl,         /* SIOCGIWMODE */
514         NULL,                   /* SIOCSIWSENS */
515         NULL,                   /* SIOCGIWSENS */
516         NULL,                   /* SIOCSIWRANGE, unused */
517         wlcompat_ioctl,         /* SIOCGIWRANGE */
518         NULL,                   /* SIOCSIWPRIV */
519         NULL,                   /* SIOCGIWPRIV */
520         NULL,                   /* SIOCSIWSTATS */
521         NULL,                   /* SIOCGIWSTATS */
522         iw_handler_set_spy,     /* SIOCSIWSPY */
523         iw_handler_get_spy,     /* SIOCGIWSPY */
524         iw_handler_set_thrspy,  /* SIOCSIWTHRSPY */
525         iw_handler_get_thrspy,  /* SIOCGIWTHRSPY */
526         wlcompat_ioctl,         /* SIOCSIWAP */
527         wlcompat_ioctl,         /* SIOCGIWAP */
528         NULL,                   /* -- hole -- */
529         NULL,                   /* SIOCGIWAPLIST */
530         wlcompat_set_scan,      /* SIOCSIWSCAN */
531         wlcompat_get_scan,      /* SIOCGIWSCAN */
532         wlcompat_ioctl,         /* SIOCSIWESSID */
533         wlcompat_ioctl,         /* SIOCGIWESSID */
534         NULL,                   /* SIOCSIWNICKN */
535         NULL,                   /* SIOCGIWNICKN */
536         NULL,                   /* -- hole -- */
537         NULL,                   /* -- hole -- */
538         NULL,                   /* SIOCSIWRATE */
539         NULL,                   /* SIOCGIWRATE */
540         wlcompat_ioctl,         /* SIOCSIWRTS */
541         wlcompat_ioctl,         /* SIOCGIWRTS */
542         wlcompat_ioctl,         /* SIOCSIWFRAG */
543         wlcompat_ioctl,         /* SIOCGIWFRAG */
544         wlcompat_ioctl,         /* SIOCSIWTXPOW */
545         wlcompat_ioctl,         /* SIOCGIWTXPOW */
546         NULL,                   /* SIOCSIWRETRY */
547         NULL,                   /* SIOCGIWRETRY */
548         NULL,                   /* SIOCSIWENCODE */
549         wlcompat_ioctl,         /* SIOCGIWENCODE */
550 };
551
552 #define PRIV_SET_MONITOR                SIOCIWFIRSTPRIV + 0
553 #define PRIV_GET_MONITOR                SIOCIWFIRSTPRIV + 1
554 #define PRIV_SET_TXPWR_LIMIT            SIOCIWFIRSTPRIV + 2
555 #define PRIV_GET_TXPWR_LIMIT            SIOCIWFIRSTPRIV + 3
556
557 static const struct iw_priv_args        wlcompat_private_args[] = 
558 {
559         {       PRIV_SET_MONITOR, 
560                 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
561                 0,
562                 "set_monitor"
563         },
564         {       PRIV_GET_MONITOR, 
565                 0,
566                 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
567                 "get_monitor"
568         },
569         {       PRIV_SET_TXPWR_LIMIT, 
570                 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
571                 0,
572                 "set_txpwr_limit"
573         },
574         {       PRIV_GET_TXPWR_LIMIT, 
575                 0,
576                 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
577                 "get_txpwr_limit"
578         }
579 };
580
581 static int wlcompat_private_ioctl(struct net_device *dev,
582                          struct iw_request_info *info,
583                          union iwreq_data *wrqu,
584                          char *extra)
585 {
586         int *value = (int *) wrqu->name;
587
588         switch (info->cmd) {
589                 case PRIV_SET_MONITOR:
590                 {
591                         if (wl_ioctl(dev, WLC_SET_MONITOR, value, sizeof(int)) < 0)
592                                 return -EINVAL;
593
594                         break;
595                 }
596                 case PRIV_GET_MONITOR:
597                 {
598                         if (wl_ioctl(dev, WLC_GET_MONITOR, extra, sizeof(int)) < 0)
599                                 return -EINVAL;
600
601                         break;
602                 }
603                 case PRIV_SET_TXPWR_LIMIT:
604                 {
605                         int val;
606                         
607
608                         if (wl_get_val(dev, "qtxpower", &val, sizeof(int)) < 0)
609                                 return -EINVAL;
610                         
611                         if (*extra > 0)
612                                 val |= WL_TXPWR_OVERRIDE;
613                         else
614                                 val &= ~WL_TXPWR_OVERRIDE;
615                         
616                         if (wl_set_val(dev, "qtxpower", &val, sizeof(int)) < 0)
617                                 return -EINVAL;
618                         
619                         break;
620                 }
621                 case PRIV_GET_TXPWR_LIMIT:
622                 {
623                         if (wl_get_val(dev, "qtxpower", value, sizeof(int)) < 0)
624                                 return -EINVAL;
625
626                         *value = ((*value & WL_TXPWR_OVERRIDE) == WL_TXPWR_OVERRIDE ? 1 : 0);
627
628                         break;
629                 }
630                 default:
631                 {
632                         return -EINVAL;
633                 }
634                         
635         }
636         return 0;
637 }
638
639
640 static const iw_handler wlcompat_private[] = 
641 {
642         wlcompat_private_ioctl,
643         wlcompat_private_ioctl,
644         wlcompat_private_ioctl,
645         wlcompat_private_ioctl
646 };
647
648
649 static const struct iw_handler_def      wlcompat_handler_def =
650 {
651         .standard       = (iw_handler *) wlcompat_handler,
652         .num_standard   = sizeof(wlcompat_handler)/sizeof(iw_handler),
653         .private        = wlcompat_private,
654         .num_private    = sizeof(wlcompat_private)/sizeof(iw_handler),
655         .private_args   = wlcompat_private_args, 
656         .num_private_args = sizeof(wlcompat_private_args)/sizeof(struct iw_priv_args),
657 };
658
659
660 #ifdef DEBUG
661 static int (*old_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);
662 void print_buffer(int len, unsigned char *buf) {
663         int x;
664         if (buf != NULL) {
665                 for (x=0;x<len && x<180 ;x++) {
666                         if ((x % 4) == 0)
667                                 printk(" ");
668                         printk("%02X",buf[x]);
669                 }
670         } else {
671                 printk(" NULL");
672         }
673         printk("\n");
674
675 }
676 static int new_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) {
677         int ret = 0;
678         printk("dev: %s ioctl: 0x%04x\n",dev->name,cmd);
679         if (cmd==SIOCDEVPRIVATE) {
680                 wl_ioctl_t *ioc = (wl_ioctl_t *)ifr->ifr_data;
681                 unsigned char *buf = ioc->buf;
682                 printk("   cmd: %d buf: 0x%08x len: %d\n",ioc->cmd,&(ioc->buf),ioc->len);
683                 printk("   send: ->");
684                 print_buffer(ioc->len, buf);
685                 ret = old_ioctl(dev,ifr,cmd);
686                 printk("   recv: ->");
687                 print_buffer(ioc->len, buf);
688                 printk("   ret: %d\n", ret);
689         } else {
690                 ret = old_ioctl(dev,ifr,cmd);
691         }
692         return ret;
693 }
694 #endif
695
696 static int __init wlcompat_init()
697 {
698         dev = dev_get_by_name("eth1");
699 #ifdef DEBUG
700         old_ioctl = dev->do_ioctl;
701         dev->do_ioctl = new_ioctl;
702 #endif
703         dev->wireless_handlers = (struct iw_handler_def *)&wlcompat_handler_def;
704         return 0;
705 }
706
707 static void __exit wlcompat_exit()
708 {
709         dev->wireless_handlers = NULL;
710 #ifdef DEBUG
711         dev->do_ioctl = old_ioctl;
712 #endif
713         return;
714 }
715
716 EXPORT_NO_SYMBOLS;
717 MODULE_AUTHOR("openwrt.org");
718 MODULE_LICENSE("GPL");
719
720 module_init(wlcompat_init);
721 module_exit(wlcompat_exit);