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