cosmetics
[tomato.git] / release / src / router / httpd / wl.c
blob10951282d3e28a3bd8a150817a2e76808e67af8a
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 web_puts("\nwlstats = [");
417 foreach_wif(0, NULL, print_wlstats);
418 web_puts("];\n");
421 static void web_print_wlchan(uint chan, int band)
423 int mhz;
424 if ((mhz = wl_chanfreq(chan, band)) > 0)
425 web_printf(",[%d, %d]", chan, mhz);
426 else
427 web_printf(",[%d, 0]", chan);
430 static int _wlchanspecs(char *ifname, char *country, int band, int bw, int ctrlsb)
432 chanspec_t c = 0, *chanspec;
433 int buflen;
434 wl_uint32_list_t *list;
435 int count, i = 0;
437 char *buf = (char *)malloc(WLC_IOCTL_MAXLEN);
438 if (!buf)
439 return 0;
441 strcpy(buf, "chanspecs");
442 buflen = strlen(buf) + 1;
444 c |= (band == WLC_BAND_5G) ? WL_CHANSPEC_BAND_5G : WL_CHANSPEC_BAND_2G;
445 c |= (bw == 20) ? WL_CHANSPEC_BW_20 : WL_CHANSPEC_BW_40;
447 chanspec = (chanspec_t *)(buf + buflen);
448 *chanspec = c;
449 buflen += (sizeof(chanspec_t));
450 strncpy(buf + buflen, country, WLC_CNTRY_BUF_SZ);
451 buflen += WLC_CNTRY_BUF_SZ;
453 /* Add list */
454 list = (wl_uint32_list_t *)(buf + buflen);
455 list->count = WL_NUMCHANSPECS;
456 buflen += sizeof(uint32)*(WL_NUMCHANSPECS + 1);
458 if (wl_ioctl(ifname, WLC_GET_VAR, buf, buflen) < 0) {
459 free((void *)buf);
460 return 0;
463 count = 0;
464 list = (wl_uint32_list_t *)buf;
465 for (i = 0; i < list->count; i++) {
466 c = (chanspec_t)list->element[i];
467 /* Skip upper.. (take only one of the lower or upper) */
468 if (bw == 40 && (CHSPEC_CTL_SB(c) != ctrlsb))
469 continue;
470 /* Create the actual control channel number from sideband */
471 int chan = CHSPEC_CHANNEL(c);
472 if (bw == 40)
473 chan += ((ctrlsb == WL_CHANSPEC_CTL_SB_UPPER) ? 2 : -2);
474 web_print_wlchan(chan, band);
475 count++;
478 free((void *)buf);
479 return count;
482 static void _wlchannels(char *ifname, char *country, int band)
484 int i;
485 wl_channels_in_country_t *cic;
487 cic = (wl_channels_in_country_t *)malloc(WLC_IOCTL_MAXLEN);
488 if (cic) {
489 cic->buflen = WLC_IOCTL_MAXLEN;
490 strcpy(cic->country_abbrev, country);
491 cic->band = band;
493 if (wl_ioctl(ifname, WLC_GET_CHANNELS_IN_COUNTRY, cic, cic->buflen) == 0) {
494 for (i = 0; i < cic->count; i++) {
495 web_print_wlchan(cic->channel[i], band);
498 free((void *)cic);
502 void asp_wlchannels(int argc, char **argv)
504 char buf[WLC_CNTRY_BUF_SZ];
505 int band, phytype, nphy;
506 int bw, ctrlsb, chanspec;
507 char *ifname;
509 // args: unit, nphy[1|0], bw, band, ctrlsb
511 check_wl_unit(argc > 0 ? argv[0] : NULL);
513 ifname = nvram_safe_get(wl_nvname("ifname", unit, 0));
514 wl_ioctl(ifname, WLC_GET_COUNTRY, buf, sizeof(buf));
515 if (wl_ioctl(ifname, WLC_GET_BAND, &band, sizeof(band)) != 0)
516 band = nvram_get_int(wl_nvname("nband", unit, 0));
517 wl_iovar_getint(ifname, "chanspec", &chanspec);
519 if (argc > 1)
520 nphy = atoi(argv[1]);
521 else {
522 wl_ioctl(ifname, WLC_GET_PHYTYPE, &phytype, sizeof(phytype));
523 nphy = wl_phytype_n(phytype);
526 bw = (argc > 2) ? atoi(argv[2]) : 0;
527 bw = bw ? : CHSPEC_IS40(chanspec) ? 40 : 20;
529 if (argc > 3) band = atoi(argv[3]) ? : band;
531 if (argc > 4) {
532 if (strcmp(argv[4], "upper") == 0)
533 ctrlsb = WL_CHANSPEC_CTL_SB_UPPER;
534 else
535 ctrlsb = WL_CHANSPEC_CTL_SB_LOWER;
537 else
538 ctrlsb = CHSPEC_CTL_SB(chanspec);
540 web_puts("\nwl_channels = [\n[0, 0]");
541 if (nphy) {
542 if (!_wlchanspecs(ifname, buf, band, bw, ctrlsb) && band == WLC_BAND_2G && bw == 40)
543 _wlchanspecs(ifname, buf, band, 20, ctrlsb);
545 else
546 _wlchannels(ifname, buf, band);
547 web_puts("];\n");
550 static int print_wlbands(int idx, int unit, int subunit, void *param)
552 char *phytype, *phylist, *ifname;
553 char comma = ' ';
554 int list[WLC_BAND_ALL];
555 int i;
557 ifname = nvram_safe_get(wl_nvname("ifname", unit, 0));
558 phytype = nvram_safe_get(wl_nvname("phytype", unit, 0));
560 web_printf("%c[", (idx == 0) ? ' ' : ',');
562 if (phytype[0] == 'n' ||
563 phytype[0] == 'l' ||
564 phytype[0] == 's' ||
565 phytype[0] == 'c' ||
566 phytype[0] == 'h') {
567 /* Get band list. Assume both the bands in case of error */
568 if (wl_ioctl(ifname, WLC_GET_BANDLIST, list, sizeof(list)) < 0) {
569 for (i = WLC_BAND_5G; i <= WLC_BAND_2G; i++) {
570 web_printf("%c'%d'", comma, i);
571 comma = ',';
574 else {
575 if (list[0] > 2)
576 list[0] = 2;
577 for (i = 1; i <= list[0]; i++) {
578 web_printf("%c'%d'", comma, list[i]);
579 comma = ',';
583 else {
584 /* Get available phy types of the currently selected wireless interface */
585 phylist = nvram_safe_get(wl_nvname("phytypes", unit, 0));
586 for (i = 0; i < strlen(phylist); i++) {
587 web_printf("%c'%d'", comma, phylist[i] == 'a' ? WLC_BAND_5G : WLC_BAND_2G);
588 comma = ',';
592 web_puts("]");
594 return 0;
597 void asp_wlbands(int argc, char **argv)
599 web_puts("\nwl_bands = [");
600 foreach_wif(0, NULL, print_wlbands);
601 web_puts(" ];\n");
604 static int print_wif(int idx, int unit, int subunit, void *param)
606 char unit_str[] = "000000";
607 char *ssidj;
609 if (subunit > 0)
610 snprintf(unit_str, sizeof(unit_str), "%d.%d", unit, subunit);
611 else
612 snprintf(unit_str, sizeof(unit_str), "%d", unit);
614 // [ifname, unitstr, unit, subunit, ssid, hwaddr]
615 ssidj = js_string(nvram_safe_get(wl_nvname("ssid", unit, subunit)));
616 web_printf("%c['%s','%s',%d,%d,'%s','%s']", (idx == 0) ? ' ' : ',',
617 nvram_safe_get(wl_nvname("ifname", unit, subunit)),
618 unit_str, unit, subunit, ssidj,
619 // assume the slave inteface MAC address is the same as the primary interface
620 nvram_safe_get(wl_nvname("hwaddr", unit, 0))
622 free(ssidj);
624 return 0;
627 void asp_wlifaces(int argc, char **argv)
629 int include_vifs = (argc > 0) ? atoi(argv[0]) : 0;
631 web_puts("\nwl_ifaces = [");
632 foreach_wif(include_vifs, NULL, print_wif);
633 web_puts("];\n");
636 void asp_wlcountries(int argc, char **argv)
638 char s[128], *p, *code, *country;
639 FILE *f;
640 int i = 0;
642 web_puts("\nwl_countries = [");
643 if ((f = popen("wl country list", "r")) != NULL) {
644 // skip the header line
645 fgets(s, sizeof(s), f);
646 while (fgets(s, sizeof(s), f)) {
647 p = s;
648 if ((code = strsep(&p, " \t\n")) && p) {
649 country = strsep(&p, "\n");
650 if ((country && *country && strcmp(code, country) != 0) ||
651 // special case EU code since the driver may not have a name for it
652 (strcmp(code, "EU") == 0)) {
653 if (!country || *country == 0) country = code;
654 p = js_string(country);
655 web_printf("%c['%s', '%s']", i++ ? ',' : ' ', code, p);
656 free(p);
660 fclose(f);
662 web_puts("];\n");