3e869ea7958f8ca957d7c90b4f79e82096e5d35f
[openwrt/svn-archive/archive.git] / package / broadcom-wl / src / wlcompat / 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 & WL_CHANSPEC_CHAN_MASK;
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);