Merge branch 'VLAN-MultiSSID' into Toastman-VLAN
[tomato.git] / release / src / router / httpd / wl.c
blob1ea33e1f7ede4b0903a4438143b11977a9636aea
1 /*
3 Tomato Firmware
4 Copyright (C) 2006-2009 Jonathan Zarate
6 */
8 #include "tomato.h"
10 #include <ctype.h>
11 #include <wlutils.h>
13 #ifndef WL_BSS_INFO_VERSION
14 #error WL_BSS_INFO_VERSION
15 #endif
16 #if WL_BSS_INFO_VERSION < 108
17 #error WL_BSS_INFO_VERSION < 108
18 #endif
21 static int unit = 0;
22 static int subunit = 0;
24 static void check_wl_unit(const char *unitarg)
26 char ifname[12], *wlunit;
27 unit = 0; subunit = 0;
29 wlunit = (unitarg && *unitarg) ? (char *)unitarg :
30 webcgi_safeget("_wl_unit", nvram_safe_get("wl_unit"));
31 snprintf(ifname, sizeof(ifname), "wl%s", wlunit);
32 get_ifname_unit(ifname, &unit, &subunit);
34 _dprintf("check_wl_unit: unitarg: %s, _wl_unit: %s, ifname: %s, unit: %d, subunit: %d\n",
35 unitarg, webcgi_safeget("_wl_unit", nvram_safe_get("wl_unit")), ifname, unit, subunit);
38 static void wl_restore(char *wif, int unit, int ap, int radio)
40 if (ap > 0) {
41 wl_ioctl(wif, WLC_SET_AP, &ap, sizeof(ap));
43 if (!radio) set_radio(1, unit);
44 eval("wl", "-i", wif, "up"); // without this the router may reboot
45 #if WL_BSS_INFO_VERSION >= 108
46 // no idea why this voodoo sequence works to wake up wl -- zzz
47 eval("wl", "-i", wif, "ssid", "");
48 eval("wl", "-i", wif, "ssid", nvram_safe_get(wl_nvname("ssid", unit, 0)));
49 #endif
51 set_radio(radio, unit);
54 // allow to scan using up to MAX_WLIF_SCAN wireless ifaces
55 #define MAX_WLIF_SCAN 3
57 typedef struct {
58 int unit_filter;
59 char comma;
60 struct {
61 int ap;
62 int radio;
63 } wif[MAX_WLIF_SCAN];
64 } scan_list_t;
66 static int start_scan(int idx, int unit, int subunit, void *param)
68 scan_list_t *rp = param;
69 wl_scan_params_t sp;
70 char *wif;
71 int zero = 0;
72 int retry;
74 if ((idx >= MAX_WLIF_SCAN) || (rp->unit_filter >= 0 && rp->unit_filter != unit)) return 0;
76 wif = nvram_safe_get(wl_nvname("ifname", unit, 0));
77 memset(&sp, 0xff, sizeof(sp)); // most default to -1
78 sp.ssid.SSID_len = 0;
79 sp.bss_type = DOT11_BSSTYPE_ANY; // =2
80 sp.channel_num = 0;
82 if (wl_ioctl(wif, WLC_GET_AP, &(rp->wif[idx].ap), sizeof(rp->wif[idx].ap)) < 0) {
83 // Unable to get AP mode
84 return 0;
86 if (rp->wif[idx].ap > 0) {
87 wl_ioctl(wif, WLC_SET_AP, &zero, sizeof(zero));
90 // set scan type based on the ap mode
91 sp.scan_type = rp->wif[idx].ap ? DOT11_SCANTYPE_PASSIVE : -1 /* default */;
93 rp->wif[idx].radio = get_radio(unit);
94 if (!(rp->wif[idx].radio)) set_radio(1, unit);
96 retry = 3 * 10;
97 while (retry--) {
98 if (wl_ioctl(wif, WLC_SCAN, &sp, WL_SCAN_PARAMS_FIXED_SIZE) == 0)
99 return 1;
100 if (retry) usleep(100000);
103 // Unable to start scan
104 wl_restore(wif, unit, rp->wif[idx].ap, rp->wif[idx].radio);
105 return 0;
108 static int get_scan_results(int idx, int unit, int subunit, void *param)
110 scan_list_t *rp = param;
112 if ((idx >= MAX_WLIF_SCAN) || (rp->unit_filter >= 0 && rp->unit_filter != unit)) return 0;
114 // get results
116 char *wif;
117 wl_scan_results_t *results;
118 wl_bss_info_t *bssi;
119 int r;
120 int retry;
122 wif = nvram_safe_get(wl_nvname("ifname", unit, 0));
124 results = malloc(WLC_IOCTL_MAXLEN + sizeof(*results));
125 if (!results) {
126 // Not enough memory
127 wl_restore(wif, unit, rp->wif[idx].ap, rp->wif[idx].radio);
128 return 0;
130 results->buflen = WLC_IOCTL_MAXLEN;
131 results->version = WL_BSS_INFO_VERSION;
133 // Keep trying to obtain scan results for up to 4 secs
134 // Passive scan may require more time, although 1 extra sec is almost always enough.
135 retry = 4 * 10;
136 r = -1;
137 while (retry--) {
138 r = wl_ioctl(wif, WLC_SCAN_RESULTS, results, WLC_IOCTL_MAXLEN);
139 if (r >= 0) break;
140 usleep(100000);
143 wl_restore(wif, unit, rp->wif[idx].ap, rp->wif[idx].radio);
145 if (r < 0) {
146 free(results);
147 // Unable to obtain scan results
148 return 0;
151 // format for javascript
153 int i;
154 int j;
155 int k;
156 char c;
157 char ssid[64];
158 char mac[32];
159 char *ssidj;
160 int channel;
162 bssi = &results->bss_info[0];
163 for (i = 0; i < results->count; ++i) {
165 channel = CHSPEC_CHANNEL(bssi->chanspec);
166 if (CHSPEC_IS40(bssi->chanspec))
167 channel = channel + (CHSPEC_SB_LOWER(bssi->chanspec) ? -2 : 2);
169 j = bssi->SSID_len;
170 if (j < 0) j = 0;
171 if (j > 32) j = 32;
172 if (nvram_get_int("wlx_scrubssid")) {
173 for (k = j - 1; k >= 0; --k) {
174 c = bssi->SSID[k];
175 if (!isprint(c)) c = '?';
176 ssid[k] = c;
179 else {
180 memcpy(ssid, bssi->SSID, j);
182 ssid[j] = 0;
183 ssidj = js_string(ssid);
185 web_printf("%c['%s','%s',%u,%u,%d,%d,[", rp->comma,
186 ether_etoa(bssi->BSSID.octet, mac), ssidj ? ssidj : "",
187 channel,
188 bssi->capability, bssi->RSSI, bssi->phy_noise);
189 rp->comma = ',';
190 free(ssidj);
192 for (j = 0; j < bssi->rateset.count; ++j) {
193 web_printf("%s%u", j ? "," : "", bssi->rateset.rates[j]);
195 web_printf("],%d,%d]", bssi->n_cap, bssi->nbss_cap);
197 bssi = (wl_bss_info_t*)((uint8*)bssi + bssi->length);
199 free(results);
201 return 1;
204 // returns: ['bssid','ssid',channel,capabilities,rssi,noise,[rates,]], or [null,'error message']
205 void asp_wlscan(int argc, char **argv)
207 scan_list_t rp;
209 memset(&rp, 0, sizeof(rp));
210 rp.comma = ' ';
211 rp.unit_filter = (argc > 0) ? atoi(argv[0]) : (-1);
213 web_puts("\nwlscandata = [");
215 // scan
217 if (foreach_wif(0, &rp, start_scan) == 0) {
218 web_puts("[null,'Unable to start scan.']];\n");
219 return;
221 sleep(1);
223 // get results
225 if (foreach_wif(0, &rp, get_scan_results) == 0) {
226 web_puts("[null,'Unable to obtain scan results.']];\n");
227 return;
230 web_puts("];\n");
233 void wo_wlradio(char *url)
235 char *enable;
236 char sunit[10];
238 check_wl_unit(NULL);
240 parse_asp("saved.asp");
241 if (nvram_get_int(wl_nvname("radio", unit, 0))) {
242 if ((enable = webcgi_get("enable")) != NULL) {
243 web_close();
244 sleep(2);
245 sprintf(sunit, "%d", unit);
246 eval("radio", atoi(enable) ? "on" : "off", sunit);
247 return;
252 static int read_noise(int unit)
254 int v;
256 // WLC_GET_PHY_NOISE = 135
257 if (wl_ioctl(nvram_safe_get(wl_nvname("ifname", unit, 0)), 135, &v, sizeof(v)) == 0) {
258 char s[32];
259 sprintf(s, "%d", v);
260 nvram_set(wl_nvname("tnoise", unit, 0), s);
261 return v;
263 return -99;
266 static int get_wlnoise(int client, int unit)
268 int v;
270 if (client) {
271 v = read_noise(unit);
273 else {
274 v = nvram_get_int(wl_nvname("tnoise", unit, 0));
275 if ((v >= 0) || (v < -100)) v = -99;
277 return v;
280 static int print_wlnoise(int idx, int unit, int subunit, void *param)
282 web_printf("%c%d", (idx == 0) ? ' ' : ',', get_wlnoise(wl_client(unit, 0), unit));
283 return 0;
286 void asp_wlnoise(int argc, char **argv)
288 web_puts("\nwlnoise = [");
289 foreach_wif(0, NULL, print_wlnoise);
290 web_puts(" ];\n");
293 void wo_wlmnoise(char *url)
295 int ap;
296 int i;
297 char *wif;
299 check_wl_unit(NULL);
301 parse_asp("mnoise.asp");
302 web_close();
303 sleep(3);
305 int radio = get_radio(unit);
307 wif = nvram_safe_get(wl_nvname("ifname", unit, 0));
308 if (wl_ioctl(wif, WLC_GET_AP, &ap, sizeof(ap)) < 0) return;
310 i = 0;
311 wl_ioctl(wif, WLC_SET_AP, &i, sizeof(i));
313 for (i = 10; i > 0; --i) {
314 sleep(1);
315 read_noise(unit);
318 wl_restore(wif, unit, ap, radio);
321 static int wl_chanfreq(uint ch, int band)
323 if ((band == WLC_BAND_2G && (ch < 1 || ch > 14)) || (ch > 200))
324 return -1;
325 else if ((band == WLC_BAND_2G) && (ch == 14))
326 return 2484;
327 else
328 return ch * 5 + ((band == WLC_BAND_2G) ? 4814 : 10000) / 2;
331 static int not_wlclient(int idx, int unit, int subunit, void *param)
333 return (!wl_client(unit, subunit));
336 // returns '1' if all wireless interfaces are in client mode, '0' otherwise
337 void asp_wlclient(int argc, char **argv)
339 web_puts(foreach_wif(1, NULL, not_wlclient) ? "0" : "1");
342 static int print_wlstats(int idx, int unit, int subunit, void *param)
344 int phytype;
345 int rate, client, nbw;
346 int chanspec, channel, mhz, band, scan;
347 int chanim_enab;
348 int interference;
349 char retbuf[WLC_IOCTL_SMLEN];
350 scb_val_t rssi;
351 char *ifname, *ctrlsb;
353 ifname = nvram_safe_get(wl_nvname("ifname", unit, 0));
354 client = wl_client(unit, 0);
356 /* Get configured phy type */
357 wl_ioctl(ifname, WLC_GET_PHYTYPE, &phytype, sizeof(phytype));
359 if (wl_ioctl(ifname, WLC_GET_RATE, &rate, sizeof(rate)) < 0)
360 rate = 0;
362 if (wl_ioctl(ifname, WLC_GET_BAND, &band, sizeof(band)) < 0)
363 band = nvram_get_int(wl_nvname("nband", unit, 0));
365 channel = nvram_get_int(wl_nvname("channel", unit, 0));
366 scan = 0;
367 interference = -1;
369 if (wl_phytype_n(phytype)) {
370 if (wl_iovar_getint(ifname, "chanspec", &chanspec) != 0) {
371 ctrlsb = nvram_safe_get(wl_nvname("nctrlsb", unit, 0));
372 nbw = nvram_get_int(wl_nvname("nbw", unit, 0));
374 else {
375 channel = CHSPEC_CHANNEL(chanspec);
376 if (CHSPEC_IS40(chanspec))
377 channel = channel + (CHSPEC_SB_LOWER(chanspec) ? -2 : 2);
378 ctrlsb = CHSPEC_SB_LOWER(chanspec) ? "lower" : (CHSPEC_SB_UPPER(chanspec) ? "upper" : "none");
379 nbw = CHSPEC_IS40(chanspec) ? 40 : 20;
382 else {
383 channel_info_t ch;
384 if (wl_ioctl(ifname, WLC_GET_CHANNEL, &ch, sizeof(ch)) == 0) {
385 scan = (ch.scan_channel > 0);
386 channel = (scan) ? ch.scan_channel : ch.hw_channel;
388 ctrlsb = "";
389 nbw = 20;
392 mhz = (channel) ? wl_chanfreq(channel, band) : 0;
393 if (wl_iovar_getint(ifname, "chanim_enab", (int*)(void*)&chanim_enab) != 0)
394 chanim_enab = 0;
395 if (chanim_enab) {
396 if (wl_iovar_getbuf(ifname, "chanim_state", &chanspec, sizeof(chanspec), retbuf, WLC_IOCTL_SMLEN) == 0)
397 interference = *(int*)retbuf;
400 memset(&rssi, 0, sizeof(rssi));
401 if (client) {
402 if (wl_ioctl(ifname, WLC_GET_RSSI, &rssi, sizeof(rssi)) != 0)
403 rssi.val = -100;
406 // [ radio, is_client, channel, freq (mhz), rate, nctrlsb, nbw, rssi, noise, interference ]
407 web_printf("%c{ radio: %d, client: %d, channel: %c%d, mhz: %d, rate: %d, ctrlsb: '%s', nbw: %d, rssi: %d, noise: %d, intf: %d }\n",
408 (idx == 0) ? ' ' : ',',
409 get_radio(unit), client, (scan ? '-' : ' '), channel, mhz, rate, ctrlsb, nbw, rssi.val, get_wlnoise(client, unit), interference);
411 return 0;
414 void asp_wlstats(int argc, char **argv)
416 int include_vifs = (argc > 0) ? atoi(argv[0]) : 0;
418 web_puts("\nwlstats = [");
419 foreach_wif(include_vifs, NULL, print_wlstats); // AB multiSSID
420 web_puts("];\n");
423 static void web_print_wlchan(uint chan, int band)
425 int mhz;
426 if ((mhz = wl_chanfreq(chan, band)) > 0)
427 web_printf(",[%d, %d]", chan, mhz);
428 else
429 web_printf(",[%d, 0]", chan);
432 static int _wlchanspecs(char *ifname, char *country, int band, int bw, int ctrlsb)
434 chanspec_t c = 0, *chanspec;
435 int buflen;
436 wl_uint32_list_t *list;
437 int count, i = 0;
439 char *buf = (char *)malloc(WLC_IOCTL_MAXLEN);
440 if (!buf)
441 return 0;
443 strcpy(buf, "chanspecs");
444 buflen = strlen(buf) + 1;
446 c |= (band == WLC_BAND_5G) ? WL_CHANSPEC_BAND_5G : WL_CHANSPEC_BAND_2G;
447 c |= (bw == 20) ? WL_CHANSPEC_BW_20 : WL_CHANSPEC_BW_40;
449 chanspec = (chanspec_t *)(buf + buflen);
450 *chanspec = c;
451 buflen += (sizeof(chanspec_t));
452 strncpy(buf + buflen, country, WLC_CNTRY_BUF_SZ);
453 buflen += WLC_CNTRY_BUF_SZ;
455 /* Add list */
456 list = (wl_uint32_list_t *)(buf + buflen);
457 list->count = WL_NUMCHANSPECS;
458 buflen += sizeof(uint32)*(WL_NUMCHANSPECS + 1);
460 if (wl_ioctl(ifname, WLC_GET_VAR, buf, buflen) < 0) {
461 free((void *)buf);
462 return 0;
465 count = 0;
466 list = (wl_uint32_list_t *)buf;
467 for (i = 0; i < list->count; i++) {
468 c = (chanspec_t)list->element[i];
469 /* Skip upper.. (take only one of the lower or upper) */
470 if (bw == 40 && (CHSPEC_CTL_SB(c) != ctrlsb))
471 continue;
472 /* Create the actual control channel number from sideband */
473 int chan = CHSPEC_CHANNEL(c);
474 if (bw == 40)
475 chan += ((ctrlsb == WL_CHANSPEC_CTL_SB_UPPER) ? 2 : -2);
476 web_print_wlchan(chan, band);
477 count++;
480 free((void *)buf);
481 return count;
484 static void _wlchannels(char *ifname, char *country, int band)
486 int i;
487 wl_channels_in_country_t *cic;
489 cic = (wl_channels_in_country_t *)malloc(WLC_IOCTL_MAXLEN);
490 if (cic) {
491 cic->buflen = WLC_IOCTL_MAXLEN;
492 strcpy(cic->country_abbrev, country);
493 cic->band = band;
495 if (wl_ioctl(ifname, WLC_GET_CHANNELS_IN_COUNTRY, cic, cic->buflen) == 0) {
496 for (i = 0; i < cic->count; i++) {
497 web_print_wlchan(cic->channel[i], band);
500 free((void *)cic);
504 void asp_wlchannels(int argc, char **argv)
506 char buf[WLC_CNTRY_BUF_SZ];
507 int band, phytype, nphy;
508 int bw, ctrlsb, chanspec;
509 char *ifname;
511 // args: unit, nphy[1|0], bw, band, ctrlsb
513 check_wl_unit(argc > 0 ? argv[0] : NULL);
515 ifname = nvram_safe_get(wl_nvname("ifname", unit, 0));
516 wl_ioctl(ifname, WLC_GET_COUNTRY, buf, sizeof(buf));
517 if (wl_ioctl(ifname, WLC_GET_BAND, &band, sizeof(band)) != 0)
518 band = nvram_get_int(wl_nvname("nband", unit, 0));
519 wl_iovar_getint(ifname, "chanspec", &chanspec);
521 if (argc > 1)
522 nphy = atoi(argv[1]);
523 else {
524 wl_ioctl(ifname, WLC_GET_PHYTYPE, &phytype, sizeof(phytype));
525 nphy = wl_phytype_n(phytype);
528 bw = (argc > 2) ? atoi(argv[2]) : 0;
529 bw = bw ? : CHSPEC_IS40(chanspec) ? 40 : 20;
531 if (argc > 3) band = atoi(argv[3]) ? : band;
533 if (argc > 4) {
534 if (strcmp(argv[4], "upper") == 0)
535 ctrlsb = WL_CHANSPEC_CTL_SB_UPPER;
536 else
537 ctrlsb = WL_CHANSPEC_CTL_SB_LOWER;
539 else
540 ctrlsb = CHSPEC_CTL_SB(chanspec);
542 web_puts("\nwl_channels = [\n[0, 0]");
543 if (nphy) {
544 if (!_wlchanspecs(ifname, buf, band, bw, ctrlsb) && band == WLC_BAND_2G && bw == 40)
545 _wlchanspecs(ifname, buf, band, 20, ctrlsb);
547 else
548 _wlchannels(ifname, buf, band);
549 web_puts("];\n");
552 static int print_wlbands(int idx, int unit, int subunit, void *param)
554 char *phytype, *phylist, *ifname;
555 char comma = ' ';
556 int list[WLC_BAND_ALL];
557 int i;
559 ifname = nvram_safe_get(wl_nvname("ifname", unit, 0));
560 phytype = nvram_safe_get(wl_nvname("phytype", unit, 0));
562 web_printf("%c[", (idx == 0) ? ' ' : ',');
564 if (phytype[0] == 'n' ||
565 phytype[0] == 'l' ||
566 phytype[0] == 's' ||
567 phytype[0] == 'c' ||
568 phytype[0] == 'h') {
569 /* Get band list. Assume both the bands in case of error */
570 if (wl_ioctl(ifname, WLC_GET_BANDLIST, list, sizeof(list)) < 0) {
571 for (i = WLC_BAND_5G; i <= WLC_BAND_2G; i++) {
572 web_printf("%c'%d'", comma, i);
573 comma = ',';
576 else {
577 if (list[0] > 2)
578 list[0] = 2;
579 for (i = 1; i <= list[0]; i++) {
580 web_printf("%c'%d'", comma, list[i]);
581 comma = ',';
585 else {
586 /* Get available phy types of the currently selected wireless interface */
587 phylist = nvram_safe_get(wl_nvname("phytypes", unit, 0));
588 for (i = 0; i < strlen(phylist); i++) {
589 web_printf("%c'%d'", comma, phylist[i] == 'a' ? WLC_BAND_5G : WLC_BAND_2G);
590 comma = ',';
594 web_puts("]");
596 return 0;
599 void asp_wlbands(int argc, char **argv)
601 int include_vifs = (argc > 0) ? atoi(argv[0]) : 0;
603 web_puts("\nwl_bands = [");
604 foreach_wif(include_vifs, NULL, print_wlbands); // AB multiSSID
605 web_puts(" ];\n");
608 static int print_wif(int idx, int unit, int subunit, void *param)
610 char unit_str[] = "000000";
611 char *ssidj;
613 if (subunit > 0)
614 snprintf(unit_str, sizeof(unit_str), "%d.%d", unit, subunit);
615 else
616 snprintf(unit_str, sizeof(unit_str), "%d", unit);
618 // [ifname, unitstr, unit, subunit, ssid, hwaddr]
619 ssidj = js_string(nvram_safe_get(wl_nvname("ssid", unit, subunit)));
620 web_printf("%c['%s','%s',%d,%d,'%s','%s']", (idx == 0) ? ' ' : ',',
621 nvram_safe_get(wl_nvname("ifname", unit, subunit)),
622 unit_str, unit, subunit, ssidj,
623 // assume the slave inteface MAC address is the same as the primary interface
624 // nvram_safe_get(wl_nvname("hwaddr", unit, 0))
625 // // virtual inteface MAC address
626 nvram_safe_get(wl_nvname("hwaddr", unit, subunit)) // AB multiSSID
628 free(ssidj);
630 return 0;
633 void asp_wlifaces(int argc, char **argv)
635 int include_vifs = (argc > 0) ? atoi(argv[0]) : 0;
637 web_puts("\nwl_ifaces = [");
638 foreach_wif(include_vifs, NULL, print_wif);
639 web_puts("];\n");
642 void asp_wlcountries(int argc, char **argv)
644 char s[128], *p, *code, *country;
645 FILE *f;
646 int i = 0;
648 web_puts("\nwl_countries = [");
649 if ((f = popen("wl country list", "r")) != NULL) {
650 // skip the header line
651 fgets(s, sizeof(s), f);
652 while (fgets(s, sizeof(s), f)) {
653 p = s;
654 if ((code = strsep(&p, " \t\n")) && p) {
655 country = strsep(&p, "\n");
656 if ((country && *country && strcmp(code, country) != 0) ||
657 // special case EU code since the driver may not have a name for it
658 (strcmp(code, "EU") == 0)) {
659 if (!country || *country == 0) country = code;
660 p = js_string(country);
661 web_printf("%c['%s', '%s']", i++ ? ',' : ' ', code, p);
662 free(p);
666 fclose(f);
668 web_puts("];\n");