add basic scanning to wlcompat
[openwrt.git] / package / openwrt / wlcompat.c
1 // insert header here
2 // mbm. gpl.
3
4 #include <linux/config.h>
5 #include <linux/module.h>
6 #include <linux/init.h>
7 #include <linux/if_arp.h>
8 #include <asm/uaccess.h>
9 #include <linux/wireless.h>
10
11 #include <net/iw_handler.h>
12 #include <wlioctl.h>
13
14 #define DEBUG
15
16 static struct net_device *dev;
17 char buf[WLC_IOCTL_MAXLEN];
18
19 /* The frequency of each channel in MHz */
20 const long channel_frequency[] = {
21         2412, 2417, 2422, 2427, 2432, 2437, 2442,
22         2447, 2452, 2457, 2462, 2467, 2472, 2484
23 };
24 #define NUM_CHANNELS ( sizeof(channel_frequency) / sizeof(channel_frequency[0]) )
25
26 static int wl_ioctl(struct net_device *dev, int cmd, void *buf, int len)
27 {
28         mm_segment_t old_fs = get_fs();
29         struct ifreq ifr;
30         int ret;
31         wl_ioctl_t ioc;
32         ioc.cmd = cmd;
33         ioc.buf = buf;
34         ioc.len = len;
35         strncpy(ifr.ifr_name, dev->name, IFNAMSIZ);
36         ifr.ifr_data = (caddr_t) &ioc;
37         set_fs(KERNEL_DS);
38         ret = dev->do_ioctl(dev,&ifr,SIOCDEVPRIVATE);
39         set_fs (old_fs);
40         return ret;
41 }
42
43 static int wlcompat_ioctl_getiwrange(struct net_device *dev,
44                                     char *extra)
45 {
46         int i, k;
47         struct iw_range *range;
48
49         range = (struct iw_range *) extra;
50
51         range->we_version_compiled = WIRELESS_EXT;
52         range->we_version_source = WIRELESS_EXT;
53         
54         range->min_nwid = range->max_nwid = 0;
55         
56         range->num_channels = NUM_CHANNELS;
57         k = 0;
58         for (i = 0; i < NUM_CHANNELS; i++) {
59                 range->freq[k].i = i + 1;
60                 range->freq[k].m = channel_frequency[i] * 100000;
61                 range->freq[k].e = 1;
62                 k++;
63                 if (k >= IW_MAX_FREQUENCIES)
64                         break;
65         }
66         range->num_frequency = k;
67         range->sensitivity = 3;
68
69         /* nbd: don't know what this means, but other drivers set it this way */
70         range->pmp_flags = IW_POWER_PERIOD;
71         range->pmt_flags = IW_POWER_TIMEOUT;
72         range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_UNICAST_R;
73
74         range->min_pmp = 0;
75         range->max_pmp = 65535000;
76         range->min_pmt = 0;
77         range->max_pmt = 65535 * 1000;
78
79         range->min_rts = 0;
80         if (wl_ioctl(dev, WLC_GET_RTS, &range->max_rts, sizeof(int)) < 0)
81                 range->max_rts = 2347;
82
83         range->min_frag = 256;
84         
85         if (wl_ioctl(dev, WLC_GET_FRAG, &range->max_frag, sizeof(int)) < 0)
86                 range->max_frag = 2346;
87
88         range->txpower_capa = IW_TXPOW_MWATT;
89
90         return 0;
91 }
92
93
94 static int wlcompat_set_scan(struct net_device *dev,
95                          struct iw_request_info *info,
96                          union iwreq_data *wrqu,
97                          char *extra)
98 {
99         int ap = 0, oldap = 0;
100         wl_scan_params_t params;
101
102         memset(&params, 0, sizeof(params));
103
104         /* use defaults (same parameters as wl scan) */
105         memset(&params.bssid, 0xff, sizeof(params.bssid));
106         params.bss_type = DOT11_BSSTYPE_ANY;
107         params.scan_type = -1;
108         params.nprobes = -1;
109         params.active_time = -1;
110         params.passive_time = -1;
111         params.home_time = -1;
112         
113         /* can only scan in STA mode */
114         wl_ioctl(dev, WLC_GET_AP, &oldap, sizeof(oldap));
115         if (oldap > 0)
116                 wl_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap));
117         
118         if (wl_ioctl(dev, WLC_SCAN, &params, 64) < 0)
119                 return -EINVAL;
120         
121         if (oldap > 0)
122                 wl_ioctl(dev, WLC_SET_AP, &oldap, sizeof(oldap));
123
124         return 0;
125 }
126
127
128 static int wlcompat_get_scan(struct net_device *dev,
129                          struct iw_request_info *info,
130                          union iwreq_data *wrqu,
131                          char *extra)
132 {
133         wl_scan_results_t *results = (wl_scan_results_t *) buf;
134         wl_bss_info_t *bss_info;
135         char *info_ptr;
136         char *current_ev = extra;
137         char *end_buf = extra + IW_SCAN_MAX_DATA;
138         struct iw_event iwe;
139         int i;
140
141         if (wl_ioctl(dev, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0)
142                 return -EAGAIN;
143         
144         bss_info = &(results->bss_info[0]);
145         info_ptr = (char *) bss_info;
146         for (i = 0; i < results->count; i++) {
147
148                 /* send the cell address (must be sent first) */
149                 iwe.cmd = SIOCGIWAP;
150                 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
151                 memcpy(&iwe.u.ap_addr.sa_data, &bss_info->BSSID, sizeof(bss_info->BSSID));
152                 current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN);
153                 
154                 /* send the ESSID */
155                 iwe.cmd = SIOCGIWESSID;
156                 iwe.u.data.length = bss_info->SSID_len;
157                 if (iwe.u.data.length > IW_ESSID_MAX_SIZE)
158                         iwe.u.data.length = IW_ESSID_MAX_SIZE;
159                 iwe.u.data.flags = 1;
160                 current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss_info->SSID);
161
162                 /* send frequency/channel info */
163                 iwe.cmd = SIOCGIWFREQ;
164                 iwe.u.freq.e = 0;
165                 iwe.u.freq.m = bss_info->channel;
166                 current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN);
167
168                 info_ptr += sizeof(wl_bss_info_t);
169                 if (bss_info->ie_length % 4)
170                         info_ptr += bss_info->ie_length + 4 - (bss_info->ie_length % 4);
171                 else
172                         info_ptr += bss_info->ie_length;
173                 bss_info = (wl_bss_info_t *) info_ptr;
174         }
175         
176         wrqu->data.length = (current_ev - extra);
177         wrqu->data.flags = 0;
178
179         return 0;
180 }
181
182 static int wlcompat_ioctl(struct net_device *dev,
183                          struct iw_request_info *info,
184                          union iwreq_data *wrqu,
185                          char *extra)
186 {
187         switch (info->cmd) {
188                 case SIOCGIWNAME:
189                         strcpy(wrqu->name, "IEEE 802.11-DS");
190                         break;
191                 case SIOCGIWFREQ:
192                 {
193                         channel_info_t ci;
194
195                         if (wl_ioctl(dev,WLC_GET_CHANNEL, &ci, sizeof(ci)) < 0)
196                                 return -EINVAL;
197
198                         wrqu->freq.m = ci.target_channel;
199                         wrqu->freq.e = 0;
200                         break;
201                 }
202                 case SIOCSIWFREQ:
203                 {
204                         if (wrqu->freq.e == 1) {
205                                 int channel = 0;
206                                 int f = wrqu->freq.m / 100000;
207                                 while ((channel < NUM_CHANNELS + 1) && (f != channel_frequency[channel]))
208                                         channel++;
209                                 
210                                 if (channel == NUM_CHANNELS) // channel not found
211                                         return -EINVAL;
212
213                                 wrqu->freq.e = 0;
214                                 wrqu->freq.m = channel + 1;
215                         }
216                         if ((wrqu->freq.e == 0) && (wrqu->freq.m < 1000)) {
217                                 if (wl_ioctl(dev, WLC_SET_CHANNEL, &wrqu->freq.m, sizeof(int)) < 0)
218                                         return -EINVAL;
219                         } else {
220                                 return -EINVAL;
221                         }
222                         break;
223                 }
224                 case SIOCGIWAP:
225                 {
226                         wrqu->ap_addr.sa_family = ARPHRD_ETHER;
227                         if (wl_ioctl(dev,WLC_GET_BSSID,wrqu->ap_addr.sa_data,6) < 0)
228                                 return -EINVAL;
229                         break;
230                 }
231                 case SIOCGIWESSID:
232                 {
233                         wlc_ssid_t ssid;
234                         
235                         if (wl_ioctl(dev,WLC_GET_SSID, &ssid, sizeof(wlc_ssid_t)) < 0)
236                                 return -EINVAL;
237
238                         wrqu->essid.flags = wrqu->data.flags = 1;
239                         wrqu->essid.length = wrqu->data.length = ssid.SSID_len + 1;
240                         memcpy(extra,ssid.SSID,ssid.SSID_len + 1);
241                         break;
242                 }
243                 case SIOCSIWESSID:
244                 {
245                         wlc_ssid_t ssid;
246                         memset(&ssid, 0, sizeof(ssid));
247                         ssid.SSID_len = strlen(extra);
248                         if (ssid.SSID_len > WLC_ESSID_MAX_SIZE)
249                                 ssid.SSID_len = WLC_ESSID_MAX_SIZE;
250                         memcpy(ssid.SSID, extra, ssid.SSID_len);
251                         if (wl_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid)) < 0)
252                                 return -EINVAL;
253                         break;
254                 }
255                 case SIOCGIWRTS:
256                 {
257                         if (wl_ioctl(dev,WLC_GET_RTS,&(wrqu->rts.value),sizeof(int)) < 0) 
258                                 return -EINVAL;
259                         break;
260                 }
261                 case SIOCSIWRTS:
262                 {
263                         if (wl_ioctl(dev,WLC_SET_RTS,&(wrqu->rts.value),sizeof(int)) < 0) 
264                                 return -EINVAL;
265                         break;
266                 }
267                 case SIOCGIWFRAG:
268                 {
269                         if (wl_ioctl(dev,WLC_GET_FRAG,&(wrqu->frag.value),sizeof(int)) < 0)
270                                 return -EINVAL;
271                         break;
272                 }
273                 case SIOCSIWFRAG:
274                 {
275                         if (wl_ioctl(dev,WLC_SET_FRAG,&(wrqu->frag.value),sizeof(int)) < 0)
276                                 return -EINVAL;
277                         break;
278                 }
279                 case SIOCGIWTXPOW:
280                 {
281                         wrqu->txpower.value = 0;
282                         if (wl_ioctl(dev,WLC_GET_TXPWR, &(wrqu->txpower.value), sizeof(int)) < 0)
283                                 return -EINVAL;
284                         wrqu->txpower.fixed = 0;
285                         wrqu->txpower.disabled = 0;
286                         wrqu->txpower.flags = IW_TXPOW_MWATT;
287                         break;
288                 }
289                 case SIOCSIWTXPOW:
290                 {
291                         if (wrqu->txpower.flags != IW_TXPOW_MWATT)
292                                 return -EINVAL;
293
294                         if (wl_ioctl(dev, WLC_SET_TXPWR, &wrqu->txpower.value, sizeof(int)) < 0)
295                                 return -EINVAL;
296                 }
297                 case SIOCGIWENCODE:
298                 {
299                         wrqu->data.flags = IW_ENCODE_DISABLED;
300                         break;
301                 }
302                 case SIOCGIWRANGE:
303                 {
304                         return wlcompat_ioctl_getiwrange(dev, extra);
305                         break;
306                 }
307                 case SIOCSIWMODE:
308                 {
309                         int ap = -1, infra = -1, passive = 0, wet = 0;
310                         
311                         switch (wrqu->mode) {
312                                 case IW_MODE_MONITOR:
313                                         passive = 1;
314                                         break;
315                                 case IW_MODE_ADHOC:
316                                         infra = 0;
317                                         ap = 0;
318                                         break;
319                                 case IW_MODE_MASTER:
320                                         infra = 1;
321                                         ap = 1;
322                                         break;
323                                 case IW_MODE_INFRA:
324                                         infra = 1;
325                                         ap = 0;
326                                         break;
327                                 case IW_MODE_REPEAT:
328                                         infra = 1;
329                                         ap = 0;
330                                         wet = 1;
331                                         break;
332                                 default:
333                                         return -EINVAL;
334                         }
335                         
336                         if (wl_ioctl(dev, WLC_SET_PASSIVE, &passive, sizeof(passive)) < 0)
337                                 return -EINVAL;
338                         if (wl_ioctl(dev, WLC_SET_MONITOR, &passive, sizeof(passive)) < 0)
339                                 return -EINVAL;
340                         if (wl_ioctl(dev, WLC_SET_WET, &wet, sizeof(wet)) < 0)
341                                 return -EINVAL;
342                         if (ap >= 0)
343                                 if (wl_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap)) < 0)
344                                         return -EINVAL;
345                         if (infra >= 0)
346                                 if (wl_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(infra)) < 0)
347                                         return -EINVAL;
348
349                         break;
350                                                 
351                 }
352                 case SIOCGIWMODE:
353                 {
354                         int ap, infra, wet, passive;
355
356                         if (wl_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap)) < 0)
357                                 return -EINVAL;
358                         if (wl_ioctl(dev, WLC_GET_INFRA, &infra, sizeof(infra)) < 0)
359                                 return -EINVAL;
360                         if (wl_ioctl(dev, WLC_GET_PASSIVE, &passive, sizeof(passive)) < 0)
361                                 return -EINVAL;
362                         if (wl_ioctl(dev, WLC_GET_WET, &wet, sizeof(wet)) < 0)
363                                 return -EINVAL;
364
365                         if (passive) {
366                                 wrqu->mode = IW_MODE_MONITOR;
367                         } else if (!infra) {
368                                 wrqu->mode = IW_MODE_ADHOC;
369                         } else {
370                                 if (ap) {
371                                         wrqu->mode = IW_MODE_MASTER;
372                                 } else {
373                                         if (wet) {
374                                                 wrqu->mode = IW_MODE_REPEAT;
375                                         } else {
376                                                 wrqu->mode = IW_MODE_INFRA;
377                                         }
378                                 }
379                         }
380                         break;
381                 }
382                 default:
383                 {
384                         return -EINVAL;
385                 }
386         }
387         
388         return 0;
389 }
390
391 static const iw_handler  wlcompat_handler[] = {
392         NULL,                   /* SIOCSIWCOMMIT */
393         wlcompat_ioctl,         /* SIOCGIWNAME */
394         NULL,                   /* SIOCSIWNWID */
395         NULL,                   /* SIOCGIWNWID */
396         wlcompat_ioctl,         /* SIOCSIWFREQ */
397         wlcompat_ioctl,         /* SIOCGIWFREQ */
398         wlcompat_ioctl,         /* SIOCSIWMODE */
399         wlcompat_ioctl,         /* SIOCGIWMODE */
400         NULL,                   /* SIOCSIWSENS */
401         NULL,                   /* SIOCGIWSENS */
402         NULL,                   /* SIOCSIWRANGE, unused */
403         wlcompat_ioctl,         /* SIOCGIWRANGE */
404         NULL,                   /* SIOCSIWPRIV */
405         NULL,                   /* SIOCGIWPRIV */
406         NULL,                   /* SIOCSIWSTATS */
407         NULL,                   /* SIOCGIWSTATS */
408         iw_handler_set_spy,     /* SIOCSIWSPY */
409         iw_handler_get_spy,     /* SIOCGIWSPY */
410         iw_handler_set_thrspy,  /* SIOCSIWTHRSPY */
411         iw_handler_get_thrspy,  /* SIOCGIWTHRSPY */
412         NULL,                   /* SIOCSIWAP */
413         wlcompat_ioctl,         /* SIOCGIWAP */
414         NULL,                   /* -- hole -- */
415         NULL,                   /* SIOCGIWAPLIST */
416         wlcompat_set_scan,      /* SIOCSIWSCAN */
417         wlcompat_get_scan,      /* SIOCGIWSCAN */
418         wlcompat_ioctl,         /* SIOCSIWESSID */
419         wlcompat_ioctl,         /* SIOCGIWESSID */
420         NULL,                   /* SIOCSIWNICKN */
421         NULL,                   /* SIOCGIWNICKN */
422         NULL,                   /* -- hole -- */
423         NULL,                   /* -- hole -- */
424         NULL,                   /* SIOCSIWRATE */
425         NULL,                   /* SIOCGIWRATE */
426         wlcompat_ioctl,         /* SIOCSIWRTS */
427         wlcompat_ioctl,         /* SIOCGIWRTS */
428         wlcompat_ioctl,         /* SIOCSIWFRAG */
429         wlcompat_ioctl,         /* SIOCGIWFRAG */
430         wlcompat_ioctl,         /* SIOCSIWTXPOW */
431         wlcompat_ioctl,         /* SIOCGIWTXPOW */
432         NULL,                   /* SIOCSIWRETRY */
433         NULL,                   /* SIOCGIWRETRY */
434         NULL,                   /* SIOCSIWENCODE */
435         wlcompat_ioctl,         /* SIOCGIWENCODE */
436 };
437
438 static const struct iw_handler_def      wlcompat_handler_def =
439 {
440         .standard       = (iw_handler *) wlcompat_handler,
441         .num_standard   = sizeof(wlcompat_handler)/sizeof(iw_handler),
442         .private        = NULL,
443         .num_private    = 0,
444         .private_args   = NULL, 
445         .num_private_args = 0,
446 };
447
448 #ifdef DEBUG
449 static int (*old_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);
450 static int new_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) {
451         int ret = old_ioctl(dev,ifr,cmd);
452         printk("dev: %s ioctl: 0x%04x\n",dev->name,cmd);
453         if (cmd==SIOCDEVPRIVATE) {
454                 int x;
455                 wl_ioctl_t *ioc = (wl_ioctl_t *)ifr->ifr_data;
456                 unsigned char *buf = ioc->buf;
457                 printk("   cmd: %d buf: 0x%08x len: %d\n",ioc->cmd,&(ioc->buf),ioc->len);
458                 printk("   ->");
459                 for (x=0;x<ioc->len && x<128 ;x++) {
460                         printk("%02X",buf[x]);
461                 }
462                 printk("\n");
463         }
464         return ret;
465 }
466 #endif
467
468 static int __init wlcompat_init()
469 {
470         dev = dev_get_by_name("eth1");
471 #ifdef DEBUG
472         old_ioctl = dev->do_ioctl;
473         dev->do_ioctl = new_ioctl;
474 #endif
475         dev->wireless_handlers = (struct iw_handler_def *)&wlcompat_handler_def;
476         return 0;
477 }
478
479 static void __exit wlcompat_exit()
480 {
481         dev->wireless_handlers = NULL;
482 #ifdef DEBUG
483         dev->do_ioctl = old_ioctl;
484 #endif
485         return;
486 }
487
488 EXPORT_NO_SYMBOLS;
489 MODULE_AUTHOR("openwrt.org");
490 MODULE_LICENSE("GPL");
491
492 module_init(wlcompat_init);
493 module_exit(wlcompat_exit);