Merge Toastman-RT into Toastman-RT-N
[tomato.git] / release / src / router / httpd / wl.c
blob3e7969b3b201b062cad201a9732f805d5ad22b0a
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, int scan_time)
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 #ifdef WLC_SET_SCAN_CHANNEL_TIME
52 if (scan_time > 0) {
53 // restore original scan channel time
54 wl_ioctl(wif, WLC_SET_SCAN_CHANNEL_TIME, &scan_time, sizeof(scan_time));
56 #endif
57 set_radio(radio, unit);
60 // allow to scan using up to MAX_WLIF_SCAN wireless ifaces
61 #define MAX_WLIF_SCAN 3
63 typedef struct {
64 int unit_filter;
65 char comma;
66 struct {
67 int ap;
68 int radio;
69 int scan_time;
70 } wif[MAX_WLIF_SCAN];
71 } scan_list_t;
73 static int start_scan(int idx, int unit, int subunit, void *param)
75 scan_list_t *rp = param;
76 wl_scan_params_t sp;
77 char *wif;
78 int zero = 0;
79 int retry;
80 #ifdef WLC_SET_SCAN_CHANNEL_TIME
81 int scan_time = 40;
82 #endif
84 if ((idx >= MAX_WLIF_SCAN) || (rp->unit_filter >= 0 && rp->unit_filter != unit)) return 0;
86 wif = nvram_safe_get(wl_nvname("ifname", unit, 0));
88 memset(&sp, 0xff, sizeof(sp)); // most default to -1
89 sp.ssid.SSID_len = 0;
90 sp.bss_type = DOT11_BSSTYPE_ANY; // =2
91 sp.channel_num = 0;
93 if (wl_ioctl(wif, WLC_GET_AP, &(rp->wif[idx].ap), sizeof(rp->wif[idx].ap)) < 0) {
94 // Unable to get AP mode
95 return 0;
97 if (rp->wif[idx].ap > 0) {
98 wl_ioctl(wif, WLC_SET_AP, &zero, sizeof(zero));
101 // set scan type based on the ap mode
102 sp.scan_type = rp->wif[idx].ap ? DOT11_SCANTYPE_PASSIVE : -1 /* default */;
104 #ifdef WLC_SET_SCAN_CHANNEL_TIME
105 // extend scan channel time to get more AP probe resp
106 wl_ioctl(wif, WLC_GET_SCAN_CHANNEL_TIME, &(rp->wif[idx].scan_time), sizeof(rp->wif[idx].scan_time));
107 if (rp->wif[idx].scan_time < scan_time) {
108 wl_ioctl(wif, WLC_SET_SCAN_CHANNEL_TIME, &scan_time, sizeof(scan_time));
110 #endif
112 rp->wif[idx].radio = get_radio(unit);
113 if (!(rp->wif[idx].radio)) set_radio(1, unit);
115 retry = 3 * 10;
116 while (retry--) {
117 if (wl_ioctl(wif, WLC_SCAN, &sp, WL_SCAN_PARAMS_FIXED_SIZE) == 0)
118 return 1;
119 if (retry) usleep(100000);
122 // Unable to start scan
123 wl_restore(wif, unit, rp->wif[idx].ap, rp->wif[idx].radio, rp->wif[idx].scan_time);
124 return 0;
127 static int get_scan_results(int idx, int unit, int subunit, void *param)
129 scan_list_t *rp = param;
131 if ((idx >= MAX_WLIF_SCAN) || (rp->unit_filter >= 0 && rp->unit_filter != unit)) return 0;
133 // get results
135 char *wif;
136 wl_scan_results_t *results;
137 wl_bss_info_t *bssi;
138 int r;
139 int retry;
141 wif = nvram_safe_get(wl_nvname("ifname", unit, 0));
143 results = malloc(WLC_IOCTL_MAXLEN + sizeof(*results));
144 if (!results) {
145 // Not enough memory
146 wl_restore(wif, unit, rp->wif[idx].ap, rp->wif[idx].radio, rp->wif[idx].scan_time);
147 return 0;
149 results->buflen = WLC_IOCTL_MAXLEN;
150 results->version = WL_BSS_INFO_VERSION;
152 // Keep trying to obtain scan results for up to 4 secs
153 // Passive scan may require more time, although 1 extra sec is almost always enough.
154 retry = 4 * 10;
155 r = -1;
156 while (retry--) {
157 r = wl_ioctl(wif, WLC_SCAN_RESULTS, results, WLC_IOCTL_MAXLEN);
158 if (r >= 0) break;
159 usleep(100000);
162 wl_restore(wif, unit, rp->wif[idx].ap, rp->wif[idx].radio, rp->wif[idx].scan_time);
164 if (r < 0) {
165 free(results);
166 // Unable to obtain scan results
167 return 0;
170 // format for javascript
172 int i;
173 int j;
174 int k;
175 char c;
176 char ssid[64];
177 char mac[32];
178 char *ssidj;
179 int channel;
181 bssi = &results->bss_info[0];
182 for (i = 0; i < results->count; ++i) {
184 channel = CHSPEC_CHANNEL(bssi->chanspec);
185 if (CHSPEC_IS40(bssi->chanspec))
186 channel = channel + (CHSPEC_SB_LOWER(bssi->chanspec) ? -2 : 2);
188 j = bssi->SSID_len;
189 if (j < 0) j = 0;
190 if (j > 32) j = 32;
191 if (nvram_get_int("wlx_scrubssid")) {
192 for (k = j - 1; k >= 0; --k) {
193 c = bssi->SSID[k];
194 if (!isprint(c)) c = '?';
195 ssid[k] = c;
198 else {
199 memcpy(ssid, bssi->SSID, j);
201 ssid[j] = 0;
202 ssidj = js_string(ssid);
204 web_printf("%c['%s','%s',%u,%u,%d,%d,[", rp->comma,
205 ether_etoa(bssi->BSSID.octet, mac), ssidj ? ssidj : "",
206 channel,
207 bssi->capability, bssi->RSSI, bssi->phy_noise);
208 rp->comma = ',';
209 free(ssidj);
211 for (j = 0; j < bssi->rateset.count; ++j) {
212 web_printf("%s%u", j ? "," : "", bssi->rateset.rates[j]);
215 web_printf("],%d,%d,%d]",
216 bssi->n_cap,
217 bssi->nbss_cap,
218 bssi->n_cap ? (CHSPEC_IS40(bssi->chanspec) ? 40 : (CHSPEC_IS20(bssi->chanspec) ? 20 : 10)) : 0);
220 bssi = (wl_bss_info_t*)((uint8*)bssi + bssi->length);
222 free(results);
224 return 1;
227 // returns: ['bssid','ssid',channel,capabilities,rssi,noise,[rates,]], or [null,'error message']
228 void asp_wlscan(int argc, char **argv)
230 scan_list_t rp;
232 memset(&rp, 0, sizeof(rp));
233 rp.comma = ' ';
234 rp.unit_filter = (argc > 0) ? atoi(argv[0]) : (-1);
236 web_puts("\nwlscandata = [");
238 // scan
240 if (foreach_wif(0, &rp, start_scan) == 0) {
241 web_puts("[null,'Unable to start scan.']];\n");
242 return;
244 sleep(1);
246 // get results
248 if (foreach_wif(0, &rp, get_scan_results) == 0) {
249 web_puts("[null,'Unable to obtain scan results.']];\n");
250 return;
253 web_puts("];\n");
256 void wo_wlradio(char *url)
258 char *enable;
259 char sunit[10];
261 check_wl_unit(NULL);
263 parse_asp("saved.asp");
264 if (nvram_get_int(wl_nvname("radio", unit, 0))) {
265 if ((enable = webcgi_get("enable")) != NULL) {
266 web_close();
267 sleep(2);
268 sprintf(sunit, "%d", unit);
269 eval("radio", atoi(enable) ? "on" : "off", sunit);
270 return;
275 static int read_noise(int unit)
277 int v;
279 // WLC_GET_PHY_NOISE = 135
280 if (wl_ioctl(nvram_safe_get(wl_nvname("ifname", unit, 0)), 135, &v, sizeof(v)) == 0) {
281 char s[32];
282 sprintf(s, "%d", v);
283 nvram_set(wl_nvname("tnoise", unit, 0), s);
284 return v;
286 return -99;
289 static int get_wlnoise(int client, int unit)
291 int v;
293 if (client) {
294 v = read_noise(unit);
296 else {
297 v = nvram_get_int(wl_nvname("tnoise", unit, 0));
298 if ((v >= 0) || (v < -100)) v = -99;
300 return v;
303 static int print_wlnoise(int idx, int unit, int subunit, void *param)
305 web_printf("%c%d", (idx == 0) ? ' ' : ',', get_wlnoise(wl_client(unit, 0), unit));
306 return 0;
309 void asp_wlnoise(int argc, char **argv)
311 web_puts("\nwlnoise = [");
312 foreach_wif(0, NULL, print_wlnoise);
313 web_puts(" ];\n");
316 void wo_wlmnoise(char *url)
318 int ap;
319 int i;
320 char *wif;
322 check_wl_unit(NULL);
324 parse_asp("mnoise.asp");
325 web_close();
326 sleep(3);
328 int radio = get_radio(unit);
330 wif = nvram_safe_get(wl_nvname("ifname", unit, 0));
331 if (wl_ioctl(wif, WLC_GET_AP, &ap, sizeof(ap)) < 0) return;
333 i = 0;
334 wl_ioctl(wif, WLC_SET_AP, &i, sizeof(i));
336 for (i = 10; i > 0; --i) {
337 sleep(1);
338 read_noise(unit);
341 wl_restore(wif, unit, ap, radio, 0);
344 static int wl_chanfreq(uint ch, int band)
346 if ((band == WLC_BAND_2G && (ch < 1 || ch > 14)) || (ch > 200))
347 return -1;
348 else if ((band == WLC_BAND_2G) && (ch == 14))
349 return 2484;
350 else
351 return ch * 5 + ((band == WLC_BAND_2G) ? 4814 : 10000) / 2;
354 static int not_wlclient(int idx, int unit, int subunit, void *param)
356 return (!wl_client(unit, subunit));
359 // returns '1' if all wireless interfaces are in client mode, '0' otherwise
360 void asp_wlclient(int argc, char **argv)
362 web_puts(foreach_wif(1, NULL, not_wlclient) ? "0" : "1");
365 static int print_wlstats(int idx, int unit, int subunit, void *param)
367 int phytype;
368 int rate, client, nbw;
369 int chanspec, channel, mhz, band, scan;
370 int chanim_enab;
371 int interference;
372 char retbuf[WLC_IOCTL_SMLEN];
373 scb_val_t rssi;
374 char *ifname, *ctrlsb;
376 ifname = nvram_safe_get(wl_nvname("ifname", unit, 0));
377 client = wl_client(unit, 0);
379 /* Get configured phy type */
380 wl_ioctl(ifname, WLC_GET_PHYTYPE, &phytype, sizeof(phytype));
382 if (wl_ioctl(ifname, WLC_GET_RATE, &rate, sizeof(rate)) < 0)
383 rate = 0;
385 if (wl_ioctl(ifname, WLC_GET_BAND, &band, sizeof(band)) < 0)
386 band = nvram_get_int(wl_nvname("nband", unit, 0));
388 channel = nvram_get_int(wl_nvname("channel", unit, 0));
389 scan = 0;
390 interference = -1;
392 if (wl_phytype_n(phytype)) {
393 if (wl_iovar_getint(ifname, "chanspec", &chanspec) != 0) {
394 ctrlsb = nvram_safe_get(wl_nvname("nctrlsb", unit, 0));
395 nbw = nvram_get_int(wl_nvname("nbw", unit, 0));
397 else {
398 channel = CHSPEC_CHANNEL(chanspec);
399 if (CHSPEC_IS40(chanspec))
400 channel = channel + (CHSPEC_SB_LOWER(chanspec) ? -2 : 2);
401 ctrlsb = CHSPEC_SB_LOWER(chanspec) ? "lower" : (CHSPEC_SB_UPPER(chanspec) ? "upper" : "none");
402 nbw = CHSPEC_IS40(chanspec) ? 40 : 20;
405 else {
406 channel_info_t ch;
407 if (wl_ioctl(ifname, WLC_GET_CHANNEL, &ch, sizeof(ch)) == 0) {
408 scan = (ch.scan_channel > 0);
409 channel = (scan) ? ch.scan_channel : ch.hw_channel;
411 ctrlsb = "";
412 nbw = 20;
415 mhz = (channel) ? wl_chanfreq(channel, band) : 0;
416 if (wl_iovar_getint(ifname, "chanim_enab", (int*)(void*)&chanim_enab) != 0)
417 chanim_enab = 0;
418 if (chanim_enab) {
419 if (wl_iovar_getbuf(ifname, "chanim_state", &chanspec, sizeof(chanspec), retbuf, WLC_IOCTL_SMLEN) == 0)
420 interference = *(int*)retbuf;
423 memset(&rssi, 0, sizeof(rssi));
424 if (client) {
425 if (wl_ioctl(ifname, WLC_GET_RSSI, &rssi, sizeof(rssi)) != 0)
426 rssi.val = -100;
429 // [ radio, is_client, channel, freq (mhz), rate, nctrlsb, nbw, rssi, noise, interference ]
430 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",
431 (idx == 0) ? ' ' : ',',
432 get_radio(unit), client, (scan ? '-' : ' '), channel, mhz, rate, ctrlsb, nbw, rssi.val, get_wlnoise(client, unit), interference);
434 return 0;
437 void asp_wlstats(int argc, char **argv)
439 int include_vifs = (argc > 0) ? atoi(argv[0]) : 0;
441 web_puts("\nwlstats = [");
442 foreach_wif(include_vifs, NULL, print_wlstats); // AB multiSSID
443 web_puts("];\n");
446 static void web_print_wlchan(uint chan, int band)
448 int mhz;
449 if ((mhz = wl_chanfreq(chan, band)) > 0)
450 web_printf(",[%d, %d]", chan, mhz);
451 else
452 web_printf(",[%d, 0]", chan);
455 static int _wlchanspecs(char *ifname, char *country, int band, int bw, int ctrlsb)
457 chanspec_t c = 0, *chanspec;
458 int buflen;
459 wl_uint32_list_t *list;
460 int count, i = 0;
462 char *buf = (char *)malloc(WLC_IOCTL_MAXLEN);
463 if (!buf)
464 return 0;
466 strcpy(buf, "chanspecs");
467 buflen = strlen(buf) + 1;
469 c |= (band == WLC_BAND_5G) ? WL_CHANSPEC_BAND_5G : WL_CHANSPEC_BAND_2G;
470 c |= (bw == 20) ? WL_CHANSPEC_BW_20 : WL_CHANSPEC_BW_40;
472 chanspec = (chanspec_t *)(buf + buflen);
473 *chanspec = c;
474 buflen += (sizeof(chanspec_t));
475 strncpy(buf + buflen, country, WLC_CNTRY_BUF_SZ);
476 buflen += WLC_CNTRY_BUF_SZ;
478 /* Add list */
479 list = (wl_uint32_list_t *)(buf + buflen);
480 list->count = WL_NUMCHANSPECS;
481 buflen += sizeof(uint32)*(WL_NUMCHANSPECS + 1);
483 if (wl_ioctl(ifname, WLC_GET_VAR, buf, buflen) < 0) {
484 free((void *)buf);
485 return 0;
488 count = 0;
489 list = (wl_uint32_list_t *)buf;
490 for (i = 0; i < list->count; i++) {
491 c = (chanspec_t)list->element[i];
492 /* Skip upper.. (take only one of the lower or upper) */
493 if (bw == 40 && (CHSPEC_CTL_SB(c) != ctrlsb))
494 continue;
495 /* Create the actual control channel number from sideband */
496 int chan = CHSPEC_CHANNEL(c);
497 if (bw == 40)
498 chan += ((ctrlsb == WL_CHANSPEC_CTL_SB_UPPER) ? 2 : -2);
499 web_print_wlchan(chan, band);
500 count++;
503 free((void *)buf);
504 return count;
507 static void _wlchannels(char *ifname, char *country, int band)
509 int i;
510 wl_channels_in_country_t *cic;
512 cic = (wl_channels_in_country_t *)malloc(WLC_IOCTL_MAXLEN);
513 if (cic) {
514 cic->buflen = WLC_IOCTL_MAXLEN;
515 strcpy(cic->country_abbrev, country);
516 cic->band = band;
518 if (wl_ioctl(ifname, WLC_GET_CHANNELS_IN_COUNTRY, cic, cic->buflen) == 0) {
519 for (i = 0; i < cic->count; i++) {
520 web_print_wlchan(cic->channel[i], band);
523 free((void *)cic);
527 void asp_wlchannels(int argc, char **argv)
529 char buf[WLC_CNTRY_BUF_SZ];
530 int band, phytype, nphy;
531 int bw, ctrlsb, chanspec;
532 char *ifname;
534 // args: unit, nphy[1|0], bw, band, ctrlsb
536 check_wl_unit(argc > 0 ? argv[0] : NULL);
538 ifname = nvram_safe_get(wl_nvname("ifname", unit, 0));
539 wl_ioctl(ifname, WLC_GET_COUNTRY, buf, sizeof(buf));
540 if (wl_ioctl(ifname, WLC_GET_BAND, &band, sizeof(band)) != 0)
541 band = nvram_get_int(wl_nvname("nband", unit, 0));
542 wl_iovar_getint(ifname, "chanspec", &chanspec);
544 if (argc > 1)
545 nphy = atoi(argv[1]);
546 else {
547 wl_ioctl(ifname, WLC_GET_PHYTYPE, &phytype, sizeof(phytype));
548 nphy = wl_phytype_n(phytype);
551 bw = (argc > 2) ? atoi(argv[2]) : 0;
552 bw = bw ? : CHSPEC_IS40(chanspec) ? 40 : 20;
554 if (argc > 3) band = atoi(argv[3]) ? : band;
556 if (argc > 4) {
557 if (strcmp(argv[4], "upper") == 0)
558 ctrlsb = WL_CHANSPEC_CTL_SB_UPPER;
559 else
560 ctrlsb = WL_CHANSPEC_CTL_SB_LOWER;
562 else
563 ctrlsb = CHSPEC_CTL_SB(chanspec);
565 web_puts("\nwl_channels = [\n[0, 0]");
566 if (nphy) {
567 if (!_wlchanspecs(ifname, buf, band, bw, ctrlsb) && band == WLC_BAND_2G && bw == 40)
568 _wlchanspecs(ifname, buf, band, 20, ctrlsb);
570 else
571 _wlchannels(ifname, buf, band);
572 web_puts("];\n");
575 static int print_wlbands(int idx, int unit, int subunit, void *param)
577 char *phytype, *phylist, *ifname;
578 char comma = ' ';
579 int list[WLC_BAND_ALL];
580 int i;
582 ifname = nvram_safe_get(wl_nvname("ifname", unit, 0));
583 phytype = nvram_safe_get(wl_nvname("phytype", unit, 0));
585 web_printf("%c[", (idx == 0) ? ' ' : ',');
587 if (phytype[0] == 'n' ||
588 phytype[0] == 'l' ||
589 phytype[0] == 's' ||
590 phytype[0] == 'c' ||
591 phytype[0] == 'h') {
592 /* Get band list. Assume both the bands in case of error */
593 if (wl_ioctl(ifname, WLC_GET_BANDLIST, list, sizeof(list)) < 0) {
594 for (i = WLC_BAND_5G; i <= WLC_BAND_2G; i++) {
595 web_printf("%c'%d'", comma, i);
596 comma = ',';
599 else {
600 if (list[0] > 2)
601 list[0] = 2;
602 for (i = 1; i <= list[0]; i++) {
603 web_printf("%c'%d'", comma, list[i]);
604 comma = ',';
608 else {
609 /* Get available phy types of the currently selected wireless interface */
610 phylist = nvram_safe_get(wl_nvname("phytypes", unit, 0));
611 for (i = 0; i < strlen(phylist); i++) {
612 web_printf("%c'%d'", comma, phylist[i] == 'a' ? WLC_BAND_5G : WLC_BAND_2G);
613 comma = ',';
617 web_puts("]");
619 return 0;
622 void asp_wlbands(int argc, char **argv)
624 int include_vifs = (argc > 0) ? atoi(argv[0]) : 0;
626 web_puts("\nwl_bands = [");
627 foreach_wif(include_vifs, NULL, print_wlbands); // AB multiSSID
628 web_puts(" ];\n");
631 static int print_wif(int idx, int unit, int subunit, void *param)
633 char unit_str[] = "000000";
634 char *ssidj;
636 if (subunit > 0)
637 snprintf(unit_str, sizeof(unit_str), "%d.%d", unit, subunit);
638 else
639 snprintf(unit_str, sizeof(unit_str), "%d", unit);
641 // [ifname, unitstr, unit, subunit, ssid, hwaddr]
642 ssidj = js_string(nvram_safe_get(wl_nvname("ssid", unit, subunit)));
643 web_printf("%c['%s','%s',%d,%d,'%s','%s']", (idx == 0) ? ' ' : ',',
644 nvram_safe_get(wl_nvname("ifname", unit, subunit)),
645 unit_str, unit, subunit, ssidj,
646 // assume the slave inteface MAC address is the same as the primary interface
647 // nvram_safe_get(wl_nvname("hwaddr", unit, 0))
648 // // virtual inteface MAC address
649 nvram_safe_get(wl_nvname("hwaddr", unit, subunit)) // AB multiSSID
651 free(ssidj);
653 return 0;
656 void asp_wlifaces(int argc, char **argv)
658 int include_vifs = (argc > 0) ? atoi(argv[0]) : 0;
660 web_puts("\nwl_ifaces = [");
661 foreach_wif(include_vifs, NULL, print_wif);
662 web_puts("];\n");
665 void asp_wlcountries(int argc, char **argv)
667 char s[128], *p, *code, *country;
668 FILE *f;
669 int i = 0;
671 web_puts("\nwl_countries = [");
672 if ((f = popen("wl country list", "r")) != NULL) {
673 // skip the header line
674 fgets(s, sizeof(s), f);
675 while (fgets(s, sizeof(s), f)) {
676 p = s;
677 if ((code = strsep(&p, " \t\n")) && p) {
678 country = strsep(&p, "\n");
679 if ((country && *country && strcmp(code, country) != 0) ||
680 // special case EU code since the driver may not have a name for it
681 (strcmp(code, "EU") == 0)) {
682 if (!country || *country == 0) country = code;
683 p = js_string(country);
684 web_printf("%c['%s', '%s']", i++ ? ',' : ' ', code, p);
685 free(p);
689 fclose(f);
691 web_puts("];\n");