fix usb-storage permissions
[openwrt.git] / package / wlcompat / src / wlcompat.c
1 /*
2  * wlcompat.c
3  *
4  * Copyright (C) 2005      Mike Baker
5  * Copyright (C) 2005-2007 Felix Fietkau <nbd@openwrt.org>
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/moduleparam.h>
28 #include <linux/init.h>
29 #include <linux/if_arp.h>
30 #include <linux/wireless.h>
31 #include <linux/timer.h>
32 #include <linux/delay.h>
33 #include <net/iw_handler.h>
34 #include <asm/uaccess.h>
35
36 #include <typedefs.h>
37 #include <bcmutils.h>
38 #include <wlioctl.h>
39
40 static struct net_device *dev;
41 static struct iw_statistics wstats;
42 #ifndef DEBUG
43 static int random = 1;
44 #endif
45 static int last_mode = -1;
46 static int scan_cur = 0;
47 char buf[WLC_IOCTL_MAXLEN];
48
49 /* The frequency of each channel in MHz */
50 const long channel_frequency[] = {
51         2412, 2417, 2422, 2427, 2432, 2437, 2442,
52         2447, 2452, 2457, 2462, 2467, 2472, 2484
53 };
54 #define NUM_CHANNELS ( sizeof(channel_frequency) / sizeof(channel_frequency[0]) )
55
56 #define SCAN_RETRY_MAX  5
57 #define RNG_POLL_FREQ   1
58
59 typedef struct internal_wsec_key {
60         uint8 index;            // 0x00
61         uint8 unknown_1;        // 0x01
62         uint8 type;             // 0x02
63         uint8 unknown_2[7];     // 0x03
64         uint8 len;              // 0x0a
65         uint8 pad[3];
66         char data[32];          // 0x0e
67 } wkey;
68
69
70 #ifdef DEBUG
71 void print_buffer(int len, unsigned char *buf);
72 #endif
73
74 static int wl_ioctl(struct net_device *dev, int cmd, void *buf, int len)
75 {
76         mm_segment_t old_fs = get_fs();
77         struct ifreq ifr;
78         int ret;
79         wl_ioctl_t ioc;
80         ioc.cmd = cmd;
81         ioc.buf = buf;
82         ioc.len = len;
83         strncpy(ifr.ifr_name, dev->name, IFNAMSIZ);
84         ifr.ifr_data = (caddr_t) &ioc;
85         set_fs(KERNEL_DS);
86         ret = dev->do_ioctl(dev,&ifr,SIOCDEVPRIVATE);
87         set_fs (old_fs);
88         return ret;
89 }
90
91 static int
92 wl_iovar_getbuf(struct net_device *dev, char *iovar, void *param, int paramlen, void *bufptr, int buflen)
93 {
94         int err;
95         uint namelen;
96         uint iolen;
97
98         namelen = strlen(iovar) + 1;     /* length of iovar name plus null */
99         iolen = namelen + paramlen;
100
101         /* check for overflow */
102         if (iolen > buflen)
103                 return (BCME_BUFTOOSHORT);
104
105         memcpy(bufptr, iovar, namelen); /* copy iovar name including null */
106         memcpy((int8*)bufptr + namelen, param, paramlen);
107
108         err = wl_ioctl(dev, WLC_GET_VAR, bufptr, buflen);
109
110         return (err);
111 }
112
113 static int
114 wl_iovar_setbuf(struct net_device *dev, char *iovar, void *param, int paramlen, void *bufptr, int buflen)
115 {
116         uint namelen;
117         uint iolen;
118
119         namelen = strlen(iovar) + 1;     /* length of iovar name plus null */
120         iolen = namelen + paramlen;
121
122         /* check for overflow */
123         if (iolen > buflen)
124                 return (BCME_BUFTOOSHORT);
125
126         memcpy(bufptr, iovar, namelen); /* copy iovar name including null */
127         memcpy((int8*)bufptr + namelen, param, paramlen);
128
129         return wl_ioctl(dev, WLC_SET_VAR, bufptr, iolen);
130 }
131
132 static int
133 wl_iovar_set(struct net_device *dev, char *iovar, void *param, int paramlen)
134 {
135         char smbuf[WLC_IOCTL_SMLEN];
136
137         return wl_iovar_setbuf(dev, iovar, param, paramlen, smbuf, sizeof(smbuf));
138 }
139
140 static int
141 wl_iovar_get(struct net_device *dev, char *iovar, void *bufptr, int buflen)
142 {
143         char smbuf[WLC_IOCTL_SMLEN];
144         int ret;
145
146         /* use the return buffer if it is bigger than what we have on the stack */
147         if (buflen > sizeof(smbuf)) {
148                 ret = wl_iovar_getbuf(dev, iovar, NULL, 0, bufptr, buflen);
149         } else {
150                 ret = wl_iovar_getbuf(dev, iovar, NULL, 0, smbuf, sizeof(smbuf));
151                 if (ret == 0)
152                         memcpy(bufptr, smbuf, buflen);
153         }
154
155         return ret;
156 }
157
158 #ifdef notyet
159 /*
160  * format a bsscfg indexed iovar buffer
161  */
162 static int
163 wl_bssiovar_mkbuf(char *iovar, int bssidx, void *param, int paramlen, void *bufptr, int buflen,
164                   int *plen)
165 {
166         char *prefix = "bsscfg:";
167         int8* p;
168         uint prefixlen;
169         uint namelen;
170         uint iolen;
171
172         prefixlen = strlen(prefix);     /* length of bsscfg prefix */
173         namelen = strlen(iovar) + 1;    /* length of iovar name + null */
174         iolen = prefixlen + namelen + sizeof(int) + paramlen;
175
176         /* check for overflow */
177         if (buflen < 0 || iolen > (uint)buflen) {
178                 *plen = 0;
179                 return BCME_BUFTOOSHORT;
180         }
181
182         p = (int8*)bufptr;
183
184         /* copy prefix, no null */
185         memcpy(p, prefix, prefixlen);
186         p += prefixlen;
187
188         /* copy iovar name including null */
189         memcpy(p, iovar, namelen);
190         p += namelen;
191
192         /* bss config index as first param */
193         memcpy(p, &bssidx, sizeof(int32));
194         p += sizeof(int32);
195
196         /* parameter buffer follows */
197         if (paramlen)
198                 memcpy(p, param, paramlen);
199
200         *plen = iolen;
201         return 0;
202 }
203
204 /*
205  * set named & bss indexed driver variable to buffer value
206  */
207 static int
208 wl_bssiovar_setbuf(struct net_device *dev, char *iovar, int bssidx, void *param, int paramlen, void *bufptr,
209                    int buflen)
210 {
211         int err;
212         int iolen;
213
214         err = wl_bssiovar_mkbuf(iovar, bssidx, param, paramlen, bufptr, buflen, &iolen);
215         if (err)
216                 return err;
217
218         return wl_ioctl(dev, WLC_SET_VAR, bufptr, iolen);
219 }
220
221 /*
222  * get named & bss indexed driver variable buffer value
223  */
224 static int
225 wl_bssiovar_getbuf(struct net_device *dev, char *iovar, int bssidx, void *param, int paramlen, void *bufptr,
226                    int buflen)
227 {
228         int err;
229         int iolen;
230
231         err = wl_bssiovar_mkbuf(iovar, bssidx, param, paramlen, bufptr, buflen, &iolen);
232         if (err)
233                 return err;
234
235         return wl_ioctl(dev, WLC_GET_VAR, bufptr, buflen);
236 }
237
238 /*
239  * set named & bss indexed driver variable to buffer value
240  */
241 static int
242 wl_bssiovar_set(struct net_device *dev, char *iovar, int bssidx, void *param, int paramlen)
243 {
244         char smbuf[WLC_IOCTL_SMLEN];
245
246         return wl_bssiovar_setbuf(dev, iovar, bssidx, param, paramlen, smbuf, sizeof(smbuf));
247 }
248
249 /*
250  * get named & bss indexed driver variable buffer value
251  */
252 static int
253 wl_bssiovar_get(struct net_device *dev, char *iovar, int bssidx, void *outbuf, int len)
254 {
255         char smbuf[WLC_IOCTL_SMLEN];
256         int err;
257
258         /* use the return buffer if it is bigger than what we have on the stack */
259         if (len > (int)sizeof(smbuf)) {
260                 err = wl_bssiovar_getbuf(dev, iovar, bssidx, NULL, 0, outbuf, len);
261         } else {
262                 memset(smbuf, 0, sizeof(smbuf));
263                 err = wl_bssiovar_getbuf(dev, iovar, bssidx, NULL, 0, smbuf, sizeof(smbuf));
264                 if (err == 0)
265                         memcpy(outbuf, smbuf, len);
266         }
267
268         return err;
269 }
270 #endif
271
272 int get_primary_key(struct net_device *dev)
273 {
274         int key, val;
275         
276         for (key = val = 0; (key < 4) && (val == 0); key++) {
277                 val = key;
278                 if (wl_ioctl(dev, WLC_GET_KEY_PRIMARY, &val, sizeof(val)) < 0)
279                         return -EINVAL;
280         }
281         return key;
282 }
283
284
285 static int wlcompat_ioctl_getiwrange(struct net_device *dev,
286                                     char *extra)
287 {
288         int i, k;
289         struct iw_range *range;
290
291         range = (struct iw_range *) extra;
292         memset(extra, 0, sizeof(struct iw_range));
293
294         range->we_version_compiled = WIRELESS_EXT;
295         range->we_version_source = WIRELESS_EXT;
296         
297         range->min_nwid = range->max_nwid = 0;
298         
299         range->num_channels = NUM_CHANNELS;
300         k = 0;
301         for (i = 0; i < NUM_CHANNELS; i++) {
302                 range->freq[k].i = i + 1;
303                 range->freq[k].m = channel_frequency[i] * 100000;
304                 range->freq[k].e = 1;
305                 k++;
306                 if (k >= IW_MAX_FREQUENCIES)
307                         break;
308         }
309         range->num_frequency = k;
310         range->sensitivity = 3;
311
312         /* nbd: don't know what this means, but other drivers set it this way */
313         range->pmp_flags = IW_POWER_PERIOD;
314         range->pmt_flags = IW_POWER_TIMEOUT;
315         range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_UNICAST_R;
316
317         range->min_pmp = 0;
318         range->max_pmp = 65535000;
319         range->min_pmt = 0;
320         range->max_pmt = 65535 * 1000;
321
322         range->max_qual.qual = 0;
323         range->max_qual.level = 0;
324         range->max_qual.noise = 0;
325         
326         range->min_rts = 0;
327         if (wl_iovar_get(dev, "rtsthresh", &range->max_rts, sizeof(int)) < 0)
328                 range->max_rts = 2347;
329
330         range->min_frag = 256;
331         
332         if (wl_iovar_get(dev, "fragthresh", &range->max_frag, sizeof(int)) < 0)
333                 range->max_frag = 2346;
334
335         range->txpower_capa = IW_TXPOW_DBM;
336
337         return 0;
338 }
339
340
341 static int wlcompat_set_scan(struct net_device *dev,
342                          struct iw_request_info *info,
343                          union iwreq_data *wrqu,
344                          char *extra)
345 {
346         int ap = 0;
347         wl_scan_params_t params;
348
349         memset(&params, 0, sizeof(params));
350
351         /* use defaults (same parameters as wl scan) */
352         memset(&params.bssid, 0xff, sizeof(params.bssid));
353         params.bss_type = DOT11_BSSTYPE_ANY;
354         params.scan_type = -1;
355         params.nprobes = -1;
356         params.active_time = -1;
357         params.passive_time = -1;
358         params.home_time = -1;
359         
360         /* can only scan in STA mode */
361         wl_ioctl(dev, WLC_GET_AP, &last_mode, sizeof(last_mode));
362         if (last_mode > 0) {
363                 /* switch to ap mode, scan result query will switch back */
364                 wl_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap));
365
366                 /* wait 250 msec after mode change */
367                 set_current_state(TASK_INTERRUPTIBLE);
368                 schedule_timeout(msecs_to_jiffies(250));
369         }
370
371         scan_cur = SCAN_RETRY_MAX;
372         while (scan_cur-- && (wl_ioctl(dev, WLC_SCAN, &params, 64) < 0)) {
373                 /* sometimes the driver takes a few tries... */
374                 set_current_state(TASK_INTERRUPTIBLE);
375                 schedule_timeout(msecs_to_jiffies(250));
376         }
377
378         if (!scan_cur) 
379                 return -EINVAL;
380         
381         scan_cur = 0;
382
383         /* wait at least 2 seconds for results */
384         set_current_state(TASK_INTERRUPTIBLE);
385         schedule_timeout(msecs_to_jiffies(2000));
386         
387         return 0;
388 }
389
390
391 struct iw_statistics *wlcompat_get_wireless_stats(struct net_device *dev)
392 {
393         struct wl_bss_info *bss_info = (struct wl_bss_info *) buf;
394         get_pktcnt_t pkt;
395         unsigned int rssi, noise, ap;
396         
397         memset(&wstats, 0, sizeof(wstats));
398         memset(&pkt, 0, sizeof(pkt));
399         memset(buf, 0, sizeof(buf));
400         bss_info->version = 0x2000;
401         wl_ioctl(dev, WLC_GET_BSS_INFO, bss_info, WLC_IOCTL_MAXLEN);
402         wl_ioctl(dev, WLC_GET_PKTCNTS, &pkt, sizeof(pkt));
403
404         rssi = 0;
405         if ((wl_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap)) < 0) || ap) {
406                 if (wl_ioctl(dev, WLC_GET_PHY_NOISE, &noise, sizeof(noise)) < 0)
407                         noise = 0;
408         } else {
409                 // somehow the structure doesn't fit here
410                 rssi = buf[82];
411                 noise = buf[84];
412         }
413         rssi = (rssi == 0 ? 1 : rssi);
414         wstats.qual.updated = 0x10;
415         if (rssi <= 1) 
416                 wstats.qual.updated |= 0x20;
417         if (noise <= 1)
418                 wstats.qual.updated |= 0x40;
419
420         if ((wstats.qual.updated & 0x60) == 0x60)
421                 return NULL;
422
423         wstats.qual.level = rssi;
424         wstats.qual.noise = noise;
425         wstats.discard.misc = pkt.rx_bad_pkt;
426         wstats.discard.retries = pkt.tx_bad_pkt;
427
428         return &wstats;
429 }
430
431 static int wlcompat_get_scan(struct net_device *dev,
432                          struct iw_request_info *info,
433                          union iwreq_data *wrqu,
434                          char *extra)
435 {
436         wl_scan_results_t *results = (wl_scan_results_t *) buf;
437         wl_bss_info_t *bss_info;
438         char *info_ptr;
439         char *current_ev = extra;
440         char *current_val;
441         char *end_buf = extra + IW_SCAN_MAX_DATA;
442         struct iw_event iwe;
443         int i, j;
444         int rssi, noise;
445         
446         memset(buf, 0, WLC_IOCTL_MAXLEN);
447         results->buflen = WLC_IOCTL_MAXLEN - sizeof(wl_scan_results_t);
448         
449         if (wl_ioctl(dev, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0)
450                 return -EAGAIN;
451         
452         if ((results->count <= 0) && (scan_cur++ < SCAN_RETRY_MAX))
453                 return -EAGAIN;
454         
455         bss_info = &(results->bss_info[0]);
456         info_ptr = (char *) bss_info;
457         for (i = 0; i < results->count; i++) {
458                 /* send the cell address (must be sent first) */
459                 iwe.cmd = SIOCGIWAP;
460                 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
461                 memcpy(&iwe.u.ap_addr.sa_data, &bss_info->BSSID, sizeof(bss_info->BSSID));
462                 current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN);
463                 
464                 /* send the ESSID */
465                 iwe.cmd = SIOCGIWESSID;
466                 iwe.u.data.length = bss_info->SSID_len;
467                 if (iwe.u.data.length > IW_ESSID_MAX_SIZE)
468                         iwe.u.data.length = IW_ESSID_MAX_SIZE;
469                 iwe.u.data.flags = 1;
470                 current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss_info->SSID);
471
472                 /* send mode */
473                 if  (bss_info->capability & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) {
474                         iwe.cmd = SIOCGIWMODE;
475                         if (bss_info->capability & DOT11_CAP_ESS)
476                                 iwe.u.mode = IW_MODE_MASTER;
477                         else if (bss_info->capability & DOT11_CAP_IBSS)
478                                 iwe.u.mode = IW_MODE_ADHOC;
479                         current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_UINT_LEN);
480                 }
481
482                 /* send frequency/channel info */
483                 iwe.cmd = SIOCGIWFREQ;
484                 iwe.u.freq.e = 0;
485                 iwe.u.freq.m = bss_info->chanspec;
486                 current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN);
487
488                 /* add quality statistics */
489                 iwe.cmd = IWEVQUAL;
490                 iwe.u.qual.qual = 0;
491                 iwe.u.qual.level = bss_info->RSSI;
492                 iwe.u.qual.noise = bss_info->phy_noise;
493                 current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN);
494         
495                 /* send encryption capability */
496                 iwe.cmd = SIOCGIWENCODE;
497                 iwe.u.data.pointer = NULL;
498                 iwe.u.data.length = 0;
499                 if (bss_info->capability & DOT11_CAP_PRIVACY)
500                         iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
501                 else
502                         iwe.u.data.flags = IW_ENCODE_DISABLED;
503                 current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, NULL);
504
505                 /* send rate information */
506                 iwe.cmd = SIOCGIWRATE;
507                 current_val = current_ev + IW_EV_LCP_LEN;
508                 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
509                 
510                 for(j = 0 ; j < bss_info->rateset.count ; j++) {
511                         iwe.u.bitrate.value = ((bss_info->rateset.rates[j] & 0x7f) * 500000);
512                         current_val = iwe_stream_add_value(current_ev, current_val, end_buf, &iwe, IW_EV_PARAM_LEN);
513                 }
514                 if((current_val - current_ev) > IW_EV_LCP_LEN)
515                         current_ev = current_val;
516
517                 info_ptr += sizeof(wl_bss_info_t);
518                 if (bss_info->ie_length % 4)
519                         info_ptr += bss_info->ie_length + 4 - (bss_info->ie_length % 4);
520                 else
521                         info_ptr += bss_info->ie_length;
522                 bss_info = (wl_bss_info_t *) info_ptr;
523         }
524         
525         wrqu->data.length = (current_ev - extra);
526         wrqu->data.flags = 0;
527
528         if (last_mode > 0)
529                 /* switch back to ap mode */
530                 wl_ioctl(dev, WLC_SET_AP, &last_mode, sizeof(last_mode));
531         
532         return 0;
533 }
534
535 static int wlcompat_ioctl(struct net_device *dev,
536                          struct iw_request_info *info,
537                          union iwreq_data *wrqu,
538                          char *extra)
539 {
540         switch (info->cmd) {
541                 case SIOCGIWNAME:
542                         strcpy(wrqu->name, "IEEE 802.11-DS");
543                         break;
544                 case SIOCGIWFREQ:
545                 {
546                         channel_info_t ci;
547
548                         if (wl_ioctl(dev,WLC_GET_CHANNEL, &ci, sizeof(ci)) < 0)
549                                 return -EINVAL;
550
551                         wrqu->freq.m = ci.target_channel;
552                         wrqu->freq.e = 0;
553                         break;
554                 }
555                 case SIOCSIWFREQ:
556                 {
557                         if (wrqu->freq.m == -1) {
558                                 wrqu->freq.m = 0;
559                                 if (wl_ioctl(dev, WLC_SET_CHANNEL, &wrqu->freq.m, sizeof(int)) < 0)
560                                         return -EINVAL;
561                         } else {
562                                 if (wrqu->freq.e == 1) {
563                                         int channel = 0;
564                                         int f = wrqu->freq.m / 100000;
565                                         while ((channel < NUM_CHANNELS + 1) && (f != channel_frequency[channel]))
566                                                 channel++;
567                                         
568                                         if (channel == NUM_CHANNELS) // channel not found
569                                                 return -EINVAL;
570
571                                         wrqu->freq.e = 0;
572                                         wrqu->freq.m = channel + 1;
573                                 }
574                                 if ((wrqu->freq.e == 0) && (wrqu->freq.m < 1000)) {
575                                         if (wl_ioctl(dev, WLC_SET_CHANNEL, &wrqu->freq.m, sizeof(int)) < 0)
576                                                 return -EINVAL;
577                                 } else {
578                                         return -EINVAL;
579                                 }
580                         }
581                         break;
582                 }
583                 case SIOCSIWAP:
584                 {
585                         int ap = 0;
586                         int infra = 0;
587                         rw_reg_t reg;
588
589                         memset(&reg, 0, sizeof(reg));
590
591                         if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
592                                 return -EINVAL;
593
594                         if (wl_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap)) < 0)
595                                 return -EINVAL;
596
597                         if (wl_ioctl(dev, WLC_GET_INFRA, &infra, sizeof(infra)) < 0)
598                                 return -EINVAL;
599
600                         if (!infra) 
601                                 wl_ioctl(dev, WLC_SET_BSSID, wrqu->ap_addr.sa_data, 6);
602
603                         if (wl_ioctl(dev, ((ap || !infra) ? WLC_SET_BSSID : WLC_REASSOC), wrqu->ap_addr.sa_data, 6) < 0)
604                                 return -EINVAL;
605
606                         break;
607                 }
608                 case SIOCGIWAP:
609                 {
610                         wrqu->ap_addr.sa_family = ARPHRD_ETHER;
611                         if (wl_ioctl(dev,WLC_GET_BSSID,wrqu->ap_addr.sa_data,6) < 0)
612                                 return -EINVAL;
613                         break;
614                 }
615                 case SIOCGIWESSID:
616                 {
617                         wlc_ssid_t ssid;
618                         
619                         if (wl_ioctl(dev,WLC_GET_SSID, &ssid, sizeof(wlc_ssid_t)) < 0)
620                                 return -EINVAL;
621
622                         wrqu->essid.flags = wrqu->data.flags = 1;
623                         wrqu->essid.length = wrqu->data.length = ssid.SSID_len + 1;
624                         memcpy(extra,ssid.SSID,ssid.SSID_len + 1);
625                         break;
626                 }
627                 case SIOCSIWESSID:
628                 {
629                         wlc_ssid_t ssid;
630                         memset(&ssid, 0, sizeof(ssid));
631                         ssid.SSID_len = strlen(extra);
632                         if (ssid.SSID_len > 32)
633                                 ssid.SSID_len = 32;
634                         memcpy(ssid.SSID, extra, ssid.SSID_len);
635                         if (wl_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid)) < 0)
636                                 return -EINVAL;
637                         break;
638                 }
639                 case SIOCGIWRTS:
640                 {
641                         if (wl_iovar_get(dev, "rtsthresh", &(wrqu->rts.value), sizeof(int)) < 0) 
642                                 return -EINVAL;
643                         break;
644                 }
645                 case SIOCSIWRTS:
646                 {
647                         if (wl_iovar_set(dev, "rtsthresh", &(wrqu->rts.value), sizeof(int)) < 0) 
648                                 return -EINVAL;
649                         break;
650                 }
651                 case SIOCGIWFRAG:
652                 {
653                         if (wl_iovar_get(dev, "fragthresh", &(wrqu->frag.value), sizeof(int)) < 0)
654                                 return -EINVAL;
655                         break;
656                 }
657                 case SIOCSIWFRAG:
658                 {
659                         if (wl_iovar_set(dev, "fragthresh", &(wrqu->frag.value), sizeof(int)) < 0)
660                                 return -EINVAL;
661                         break;
662                 }
663                 case SIOCGIWTXPOW:
664                 {
665                         int radio, override;
666
667                         wl_ioctl(dev, WLC_GET_RADIO, &radio, sizeof(int));
668                         
669                         if (wl_iovar_get(dev, "qtxpower", &(wrqu->txpower.value), sizeof(int)) < 0)
670                                 return -EINVAL;
671                         
672                         override = (wrqu->txpower.value & WL_TXPWR_OVERRIDE) == WL_TXPWR_OVERRIDE;
673                         wrqu->txpower.value &= ~WL_TXPWR_OVERRIDE;
674                         if (!override && (wrqu->txpower.value > 76))
675                                 wrqu->txpower.value = 76;
676                         wrqu->txpower.value /= 4;
677                                 
678                         wrqu->txpower.fixed = 0;
679                         wrqu->txpower.disabled = radio;
680                         wrqu->txpower.flags = IW_TXPOW_DBM;
681                         break;
682                 }
683                 case SIOCSIWTXPOW:
684                 {
685                         /* This is weird: WLC_SET_RADIO with 1 as argument disables the radio */
686                         int radio = wrqu->txpower.disabled;
687
688                         wl_ioctl(dev, WLC_SET_RADIO, &radio, sizeof(int));
689                         
690                         if (!wrqu->txpower.disabled && (wrqu->txpower.value > 0)) {
691                                 int value;
692                                 
693                                 if (wl_iovar_get(dev, "qtxpower", &value, sizeof(int)) < 0)
694                                         return -EINVAL;
695                                 
696                                 value &= WL_TXPWR_OVERRIDE;
697                                 wrqu->txpower.value *= 4;
698                                 wrqu->txpower.value |= value;
699                                 
700                                 if (wrqu->txpower.flags != IW_TXPOW_DBM)
701                                         return -EINVAL;
702                                 
703                                 if (wrqu->txpower.value > 0)
704                                         if (wl_iovar_set(dev, "qtxpower", &(wrqu->txpower.value), sizeof(int)) < 0)
705                                                 return -EINVAL;
706                         }
707                         break;
708                 }
709                 case SIOCSIWENCODE:
710                 {
711                         int val = 0, wep = 1, wrestrict = 1;
712                         int index = (wrqu->data.flags & IW_ENCODE_INDEX) - 1;
713
714                         if (index < 0)
715                                 index = get_primary_key(dev);
716                         
717                         if (wrqu->data.flags & IW_ENCODE_DISABLED) {
718                                 wep = 0;
719                                 if (wl_ioctl(dev, WLC_SET_WSEC, &wep, sizeof(val)) < 0)
720                                         return -EINVAL;
721                                 return 0;
722                         }
723
724                         if (wl_ioctl(dev, WLC_SET_WSEC, &wep, sizeof(val)) < 0)
725                                 return -EINVAL;
726
727                         if (wrqu->data.flags & IW_ENCODE_OPEN)
728                                 wrestrict = 0;
729                         
730                         if (wrqu->data.pointer && (wrqu->data.length > 0) && (wrqu->data.length <= 16)) {
731                                 wl_wsec_key_t key;
732                                 memset(&key, 0, sizeof(key));
733
734                                 key.flags = WL_PRIMARY_KEY;
735                                 key.len = wrqu->data.length;
736                                 key.index = index;
737                                 memcpy(key.data, wrqu->data.pointer, wrqu->data.length);
738
739                                 if (wl_ioctl(dev, WLC_SET_KEY, &key, sizeof(key)) < 0)
740                                         return -EINVAL;
741                         }
742
743                         if (index >= 0)
744                                 wl_ioctl(dev, WLC_SET_KEY_PRIMARY, &index, sizeof(index));
745                         
746                         if (wrestrict >= 0)
747                                 wl_ioctl(dev, WLC_SET_WEP_RESTRICT, &wrestrict, sizeof(wrestrict));
748
749                         break;
750                 }
751                 case SIOCGIWENCODE:
752                 {
753                         int val;
754                         int key = get_primary_key(dev);
755                         int *info_addr; 
756                         wkey *wep_key;
757                         
758                         if (wl_ioctl(dev, WLC_GET_WSEC, &val, sizeof(val)) < 0)
759                                 return -EINVAL;
760                         if (!(val & WEP_ENABLED)) {
761                                 wrqu->data.flags = IW_ENCODE_DISABLED;
762                                 break;
763                         }
764
765                         key = get_primary_key(dev);
766                         wrqu->data.flags = IW_ENCODE_ENABLED;
767
768                         /* the driver apparently doesn't allow us to read the wep key */
769                         wrqu->data.flags |= IW_ENCODE_NOKEY;
770
771                         break;
772                 }
773                 case SIOCGIWRANGE:
774                 {
775                         return wlcompat_ioctl_getiwrange(dev, extra);
776                         break;
777                 }
778                 case SIOCSIWMODE:
779                 {
780                         int ap = -1, infra = -1, passive = 0, wet = 0;
781                         
782                         switch (wrqu->mode) {
783                                 case IW_MODE_MONITOR:
784                                         passive = 1;
785                                         break;
786                                 case IW_MODE_ADHOC:
787                                         infra = 0;
788                                         ap = 0;
789                                         break;
790                                 case IW_MODE_MASTER:
791                                         infra = 1;
792                                         ap = 1;
793                                         break;
794                                 case IW_MODE_INFRA:
795                                         infra = 1;
796                                         ap = 0;
797                                         break;
798                                 case IW_MODE_REPEAT:
799                                         infra = 1;
800                                         ap = 0;
801                                         wet = 1;
802                                         break;
803                                         
804                                 default:
805                                         return -EINVAL;
806                         }
807                         
808                         wl_ioctl(dev, WLC_SET_PASSIVE, &passive, sizeof(passive));
809                         wl_ioctl(dev, WLC_SET_MONITOR, &passive, sizeof(passive));
810                         wl_ioctl(dev, WLC_SET_WET, &wet, sizeof(wet));
811                         if (ap >= 0) 
812                                 wl_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap));
813                         if (infra >= 0)
814                                 wl_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(infra));
815
816                         break;
817                                                 
818                 }
819                 case SIOCGIWMODE:
820                 {
821                         int ap, infra, wet, passive;
822
823                         if (wl_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap)) < 0)
824                                 return -EINVAL;
825                         if (wl_ioctl(dev, WLC_GET_INFRA, &infra, sizeof(infra)) < 0)
826                                 return -EINVAL;
827                         if (wl_ioctl(dev, WLC_GET_PASSIVE, &passive, sizeof(passive)) < 0)
828                                 return -EINVAL;
829                         if (wl_ioctl(dev, WLC_GET_WET, &wet, sizeof(wet)) < 0)
830                                 return -EINVAL;
831
832                         if (passive) {
833                                 wrqu->mode = IW_MODE_MONITOR;
834                         } else if (!infra) {
835                                 wrqu->mode = IW_MODE_ADHOC;
836                         } else {
837                                 if (ap) {
838                                         wrqu->mode = IW_MODE_MASTER;
839                                 } else {
840                                         if (wet) {
841                                                 wrqu->mode = IW_MODE_REPEAT;
842                                         } else {
843                                                 wrqu->mode = IW_MODE_INFRA;
844                                         }
845                                 }
846                         }
847                         break;
848                 }
849                 default:
850                         return -EINVAL;
851                         break;
852         }
853         
854         return 0;
855 }
856
857 static const iw_handler  wlcompat_handler[] = {
858         NULL,                   /* SIOCSIWCOMMIT */
859         wlcompat_ioctl,         /* SIOCGIWNAME */
860         NULL,                   /* SIOCSIWNWID */
861         NULL,                   /* SIOCGIWNWID */
862         wlcompat_ioctl,         /* SIOCSIWFREQ */
863         wlcompat_ioctl,         /* SIOCGIWFREQ */
864         wlcompat_ioctl,         /* SIOCSIWMODE */
865         wlcompat_ioctl,         /* SIOCGIWMODE */
866         NULL,                   /* SIOCSIWSENS */
867         NULL,                   /* SIOCGIWSENS */
868         NULL,                   /* SIOCSIWRANGE, unused */
869         wlcompat_ioctl,         /* SIOCGIWRANGE */
870         NULL,                   /* SIOCSIWPRIV */
871         NULL,                   /* SIOCGIWPRIV */
872         NULL,                   /* SIOCSIWSTATS */
873         NULL,                   /* SIOCGIWSTATS */
874         iw_handler_set_spy,     /* SIOCSIWSPY */
875         iw_handler_get_spy,     /* SIOCGIWSPY */
876         iw_handler_set_thrspy,  /* SIOCSIWTHRSPY */
877         iw_handler_get_thrspy,  /* SIOCGIWTHRSPY */
878         wlcompat_ioctl,         /* SIOCSIWAP */
879         wlcompat_ioctl,         /* SIOCGIWAP */
880         NULL,                   /* -- hole -- */
881         NULL,                   /* SIOCGIWAPLIST */
882         wlcompat_set_scan,      /* SIOCSIWSCAN */
883         wlcompat_get_scan,      /* SIOCGIWSCAN */
884         wlcompat_ioctl,         /* SIOCSIWESSID */
885         wlcompat_ioctl,         /* SIOCGIWESSID */
886         NULL,                   /* SIOCSIWNICKN */
887         NULL,                   /* SIOCGIWNICKN */
888         NULL,                   /* -- hole -- */
889         NULL,                   /* -- hole -- */
890         NULL,                   /* SIOCSIWRATE */
891         NULL,                   /* SIOCGIWRATE */
892         wlcompat_ioctl,         /* SIOCSIWRTS */
893         wlcompat_ioctl,         /* SIOCGIWRTS */
894         wlcompat_ioctl,         /* SIOCSIWFRAG */
895         wlcompat_ioctl,         /* SIOCGIWFRAG */
896         wlcompat_ioctl,         /* SIOCSIWTXPOW */
897         wlcompat_ioctl,         /* SIOCGIWTXPOW */
898         NULL,                   /* SIOCSIWRETRY */
899         NULL,                   /* SIOCGIWRETRY */
900         wlcompat_ioctl,         /* SIOCSIWENCODE */
901         wlcompat_ioctl,         /* SIOCGIWENCODE */
902 };
903
904
905 static const struct iw_handler_def wlcompat_handler_def =
906 {
907         .standard       = (iw_handler *) wlcompat_handler,
908         .num_standard   = sizeof(wlcompat_handler)/sizeof(iw_handler),
909 };
910
911
912 #ifdef DEBUG
913 void print_buffer(int len, unsigned char *buf) {
914         int x;
915         if (buf != NULL) {
916                 for (x=0;x<len && x<180 ;x++) {
917                         if ((x % 4) == 0)
918                                 printk(" ");
919                         printk("%02X",buf[x]);
920                 }
921         } else {
922                 printk(" NULL");
923         }
924         printk("\n");
925
926 }
927 #endif
928 static int (*old_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);
929 static int new_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) {
930         int ret = 0;
931         struct iwreq *iwr = (struct iwreq *) ifr;
932         struct iw_request_info info;
933         
934 #ifdef DEBUG
935         printk("dev: %s ioctl: 0x%04x\n",dev->name,cmd);
936         if (cmd==SIOCDEVPRIVATE) {
937                 wl_ioctl_t *ioc = (wl_ioctl_t *)ifr->ifr_data;
938                 unsigned char *buf = ioc->buf;
939                 printk("   cmd: %d buf: 0x%08x len: %d\n",ioc->cmd,&(ioc->buf),ioc->len);
940                 printk("   send: ->");
941                 print_buffer(ioc->len, buf);
942                 ret = old_ioctl(dev,ifr,cmd);
943                 printk("   recv: ->");
944                 print_buffer(ioc->len, buf);
945                 printk("   ret: %d\n", ret);
946         } else
947 #endif
948         {
949                 ret = old_ioctl(dev,ifr,cmd);
950         }
951         return ret;
952 }
953
954 #ifndef DEBUG
955 static struct timer_list rng_timer;
956 static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED;
957
958 static void rng_timer_tick(unsigned long n)
959 {
960         struct net_device *dev = (struct net_device *) n;
961         unsigned long flags;
962         u16 data[4];
963         int i, ret;
964         
965         ret = 0;
966         spin_lock_irqsave(&rng_lock, flags);
967         for (i = 0; i < 3; i++) {
968                 ret |= wl_iovar_get(dev, "rand", &data[i], sizeof(u16));
969         }
970         spin_unlock_irqrestore(&rng_lock, flags);
971
972         if (!ret)
973                 batch_entropy_store(*((u32 *) &data[0]), *((u32 *) &data[2]), (jiffies % 255));
974
975         mod_timer(&rng_timer, jiffies + (HZ/RNG_POLL_FREQ));
976 }
977 #endif
978
979 static int __init wlcompat_init()
980 {
981         int found = 0, i;
982         char devname[4] = "wl0";
983         
984         while (!found && (dev = dev_get_by_name(devname))) {
985                 if ((dev->wireless_handlers == NULL) && ((wl_ioctl(dev, WLC_GET_MAGIC, &i, sizeof(i)) == 0) && i == WLC_IOCTL_MAGIC))
986                         found = 1;
987                 devname[2]++;
988         }
989         
990         if (!found) {
991                 printk("No Broadcom devices found.\n");
992                 return -ENODEV;
993         }
994                 
995
996         old_ioctl = dev->do_ioctl;
997         dev->do_ioctl = new_ioctl;
998         dev->wireless_handlers = (struct iw_handler_def *)&wlcompat_handler_def;
999         dev->get_wireless_stats = wlcompat_get_wireless_stats;
1000
1001 #ifndef DEBUG
1002         if (random) {
1003                 init_timer(&rng_timer);
1004                 rng_timer.function = rng_timer_tick;
1005                 rng_timer.data = (unsigned long) dev;
1006                 rng_timer_tick((unsigned long) dev);
1007         }
1008 #endif
1009         
1010 #ifdef DEBUG
1011         printk("broadcom driver private data: 0x%08x\n", dev->priv);
1012 #endif
1013         return 0;
1014 }
1015
1016 static void __exit wlcompat_exit()
1017 {
1018 #ifndef DEBUG
1019         if (random)
1020                 del_timer(&rng_timer);
1021 #endif
1022         dev->get_wireless_stats = NULL;
1023         dev->wireless_handlers = NULL;
1024         dev->do_ioctl = old_ioctl;
1025         return;
1026 }
1027
1028 EXPORT_NO_SYMBOLS;
1029 MODULE_AUTHOR("openwrt.org");
1030 MODULE_LICENSE("GPL");
1031
1032 #ifndef DEBUG
1033 module_param(random, int, 0);
1034 #endif
1035 module_init(wlcompat_init);
1036 module_exit(wlcompat_exit);