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