2 * no license, extracted from wag54gv2-AU_v1.00.39 GPL
21 #define PHY_TYPE_NULL 0xf
23 /* parts of an idcode: */
24 #define IDCODE_MFG_MASK 0x00000fff
25 #define IDCODE_MFG_SHIFT 0
26 #define IDCODE_ID_MASK 0x0ffff000
27 #define IDCODE_ID_SHIFT 12
28 #define IDCODE_REV_MASK 0xf0000000
29 #define IDCODE_REV_SHIFT 28
31 #define WL_IOCTL(name, cmd, buf, len) ((void) wl_ioctl((name), (cmd), (buf), (len)))
35 wlconf_set_wep_key(char *name
, char *prefix
, int i
)
38 char wl_key
[] = "wlXXXXXXXXXX_keyXXXXXXXXXX";
39 char *keystr
, hex
[] = "XX";
40 unsigned char *data
= key
.data
;
43 memset(&key
, 0, sizeof(key
));
45 sprintf(wl_key
, "%skey%d", prefix
, i
);
46 keystr
= nvram_safe_get(wl_key
);
48 switch (strlen(keystr
)) {
51 key
.len
= strlen(keystr
);
52 strcpy(key
.data
, keystr
);
54 case WEP1_KEY_HEX_SIZE
:
55 case WEP128_KEY_HEX_SIZE
:
56 key
.len
= strlen(keystr
) / 2;
58 strncpy(hex
, keystr
, 2);
59 *data
++ = (unsigned char) strtoul(hex
, NULL
, 16);
68 /* Set current WEP key */
69 if (key
.len
&& i
== atoi(nvram_safe_get(strcat_r(prefix
, "key", wl_key
))))
70 key
.flags
= WSEC_PRIMARY_KEY
;
72 WL_IOCTL(name
, WLC_SET_KEY
, &key
, sizeof(key
));
77 extern struct nvram_tuple router_defaults
[];
79 /* Keep this table in order */
85 { WLC_WW
, ((char *[]) { "Worldwide", "WW", NULL
}), "AU" },
86 { WLC_THA
, ((char *[]) { "Thailand", "THA", NULL
}), "TH" },
87 { WLC_ISR
, ((char *[]) { "Israel", "ISR", NULL
}), "IL" },
88 { WLC_JDN
, ((char *[]) { "Jordan", "JDN", NULL
}), "JO" },
89 { WLC_PRC
, ((char *[]) { "China", "P.R. China", "PRC", NULL
}), "CN" },
90 { WLC_JPN
, ((char *[]) { "Japan", "JPN", NULL
}), "JP" },
91 { WLC_FCC
, ((char *[]) { "USA", "Canada", "ANZ", "New Zealand", "FCC", NULL
}), "US" },
92 { WLC_EUR
, ((char *[]) { "Europe", "EUR", NULL
}), "DE" },
93 { WLC_USL
, ((char *[]) { "USA Low", "USALow", "USL", NULL
}), "US" },
94 { WLC_JPH
, ((char *[]) { "Japan High", "JapanHigh", "JPH", NULL
}), "JP" },
95 { WLC_ALL
, ((char *[]) { "All", "AllTheChannels", NULL
}), "All" },
98 /* validate/restore all per-interface related variables */
100 wlconf_validate_all(char *prefix
, bool restore
)
102 struct nvram_tuple
*t
;
105 for (t
= router_defaults
; t
->name
; t
++) {
106 if (!strncmp(t
->name
, "wl_", 3)) {
107 strcat_r(prefix
, &t
->name
[3], tmp
);
108 if (!restore
&& nvram_get(tmp
))
110 v
= nvram_get(t
->name
);
111 nvram_set(tmp
, v
? v
: t
->value
);
116 /* restore specific per-interface variable */
118 wlconf_restore_var(char *prefix
, char *name
)
120 struct nvram_tuple
*t
;
122 for (t
= router_defaults
; t
->name
; t
++) {
123 if (!strncmp(t
->name
, "wl_", 3) && !strcmp(&t
->name
[3], name
)) {
124 nvram_set(strcat_r(prefix
, name
, tmp
), t
->value
);
132 wlconf_set_wsec(char *ifname
, char *prefix
)
136 strcat_r(prefix
, "wep", tmp
);
137 if (nvram_match(tmp
, "wep") || nvram_match(tmp
, "on") || nvram_match(tmp
, "restricted"))
139 else if (nvram_match(tmp
, "tkip"))
141 else if (nvram_match(tmp
, "aes"))
143 else if (nvram_match(tmp
, "tkip+aes"))
144 val
= TKIP_ENABLED
| AES_ENABLED
;
147 return wl_ioctl(ifname
, WLC_SET_WSEC
, &val
, sizeof(val
));
153 #define WLCONF_DBG(fmt, arg...)
158 sleep_ms(const unsigned int ms
)
165 * The following condition(s) must be met when Auto Channel Selection
167 * - the I/F is up (change radio channel requires it is up?)
168 * - the AP must not be associated (setting SSID to empty should
169 * make sure it for us)
172 wlconf_auto_channel(char *name
)
175 wl_uint32_list_t request
;
177 /* query the phy type */
178 wl_ioctl(name
, WLC_GET_PHYTYPE
, &phytype
, sizeof(phytype
));
179 request
.count
= 0; /* let the ioctl decide */
180 if (!wl_ioctl(name
, WLC_START_CHANNEL_SEL
, &request
, sizeof(request
))) {
181 sleep_ms(phytype
== PHY_TYPE_A
? 1000 : 750);
182 while (wl_ioctl(name
, WLC_GET_CHANNEL_SEL
, &chosen
, sizeof(chosen
)))
185 WLCONF_DBG("interface %s: channel selected %d\n", name
, chosen
);
189 /* PHY type/BAND conversion */
190 #define WLCONF_PHYTYPE2BAND(phy) ((phy) == PHY_TYPE_A ? WLC_BAND_A : WLC_BAND_B)
191 /* PHY type conversion */
192 #define WLCONF_PHYTYPE2STR(phy) ((phy) == PHY_TYPE_A ? "a" : \
193 (phy) == PHY_TYPE_B ? "b" : "g")
194 #define WLCONF_STR2PHYTYPE(phy) ((phy) && (phy)[0] == 'a' ? PHY_TYPE_A : \
195 (phy) && (phy)[0] == 'b' ? PHY_TYPE_B : PHY_TYPE_G)
197 /* configure the specified wireless interface */
201 int restore_defaults
, val
, unit
, phytype
, gmode
= 0, ret
= 0;
202 char tmp
[100], prefix
[] = "wlXXXXXXXXXX_";
203 char var
[80], *next
, phy
[] = "a", *str
;
204 unsigned char buf
[WLC_IOCTL_MAXLEN
];
208 struct maclist
*maclist
;
209 struct ether_addr
*ea
;
214 int ap
, sta
= 0, wet
= 0;
215 char country_code
[4];
217 /* Check interface (fail silently for non-wl interfaces) */
218 if ((ret
= wl_probe(name
)))
221 /* Get MAC address */
222 (void) wl_hwaddr(name
, buf
);
225 WL_IOCTL(name
, WLC_GET_INSTANCE
, &unit
, sizeof(unit
));
226 snprintf(prefix
, sizeof(prefix
), "wl%d_", unit
);
228 /* Restore defaults if per-interface parameters do not exist */
229 restore_defaults
= !nvram_get(strcat_r(prefix
, "ifname", tmp
));
230 wlconf_validate_all(prefix
, restore_defaults
);
231 nvram_set(strcat_r(prefix
, "ifname", tmp
), name
);
232 nvram_set(strcat_r(prefix
, "hwaddr", tmp
), ether_etoa(buf
, eaddr
));
233 snprintf(buf
, sizeof(buf
), "%d", unit
);
234 nvram_set(strcat_r(prefix
, "unit", tmp
), buf
);
237 * Nuke SSID first so that the AP won't be associated when WLC_UP.
238 * This must be done here if Auto Channel Selection is enabled.
240 WL_IOCTL(name
, WLC_GET_UP
, &val
, sizeof(val
));
245 WL_IOCTL(name
, WLC_SET_SSID
, &ssid
, sizeof(ssid
));
247 /* Bring the interface down */
248 WL_IOCTL(name
, WLC_DOWN
, NULL
, sizeof(val
));
251 /* Set mode : AP, STA */
252 ap
= nvram_match(strcat_r(prefix
, "mode", tmp
), "ap");
253 val
= (ap
+ nvram_match(strcat_r(prefix
, "mode", tmp
), "wds")) ? 1 : 0;
254 WL_IOCTL(name
, WLC_SET_AP
, &val
, sizeof(val
));
256 /* Set STA specific parameters */
259 if ((wet
= nvram_match(strcat_r(prefix
, "mode", tmp
), "wet")))
260 WL_IOCTL(name
, WLC_SET_WET
, &wet
, sizeof(wet
));
261 /* Set infra: BSS/IBSS */
262 if (wet
|| (sta
= nvram_match(strcat_r(prefix
, "mode", tmp
), "sta"))) {
263 val
= atoi(nvram_safe_get(strcat_r(prefix
, "infra", tmp
)));
264 WL_IOCTL(name
, WLC_SET_INFRA
, &val
, sizeof(val
));
268 /* Set network type */
269 val
= atoi(nvram_safe_get(strcat_r(prefix
, "closed", tmp
)));
270 WL_IOCTL(name
, WLC_SET_CLOSED
, &val
, sizeof(val
));
272 /* Set up the country code */
273 (void) strcat_r(prefix
, "country_code", tmp
);
274 country
= nvram_get(tmp
);
276 strncpy(country_code
, country
, sizeof(country_code
));
277 WL_IOCTL(name
, WLC_SET_COUNTRY
, country_code
, strlen(country_code
)+1);
280 /* If country_code doesn't exist, check for country to be backward compatible */
281 (void) strcat_r(prefix
, "country", tmp
);
282 country
= nvram_safe_get(tmp
);
283 for (val
= 0; val
< ARRAYSIZE(countries
); val
++) {
285 for (synonym
= countries
[val
].names
; *synonym
; synonym
++)
286 if (!strcmp(country
, *synonym
))
292 /* Get the default country code if undefined or invalid and set the NVRAM */
293 if (val
>= ARRAYSIZE(countries
)) {
294 WL_IOCTL(name
, WLC_GET_COUNTRY
, country_code
, sizeof(country_code
));
297 strncpy(country_code
, countries
[val
].abbr
, sizeof(country_code
));
298 WL_IOCTL(name
, WLC_SET_COUNTRY
, country_code
, strlen(country_code
)+1);
301 /* Add the new NVRAM variable */
302 nvram_set("wl_country_code", country_code
);
303 (void) strcat_r(prefix
, "country_code", tmp
);
304 nvram_set(tmp
, country_code
);
307 /* Set the MAC list */
308 maclist
= (struct maclist
*) buf
;
310 if (!nvram_match(strcat_r(prefix
, "macmode", tmp
), "disabled")) {
312 foreach(var
, nvram_safe_get(strcat_r(prefix
, "maclist", tmp
)), next
) {
313 if ((&ea
[1])->octet
> &buf
[sizeof(buf
)])
315 if (ether_atoe(var
, ea
->octet
)) {
321 WL_IOCTL(name
, WLC_SET_MACLIST
, buf
, sizeof(buf
));
323 /* Set the MAC list mode */
324 (void) strcat_r(prefix
, "macmode", tmp
);
325 if (nvram_match(tmp
, "deny"))
326 val
= WLC_MACMODE_DENY
;
327 else if (nvram_match(tmp
, "allow"))
328 val
= WLC_MACMODE_ALLOW
;
330 val
= WLC_MACMODE_DISABLED
;
331 WL_IOCTL(name
, WLC_SET_MACMODE
, &val
, sizeof(val
));
333 /* Enable or disable the radio */
334 val
= nvram_match(strcat_r(prefix
, "radio", tmp
), "0");
335 WL_IOCTL(name
, WLC_SET_RADIO
, &val
, sizeof(val
));
337 /* Get supported phy types */
338 WL_IOCTL(name
, WLC_GET_PHYLIST
, var
, sizeof(var
));
339 nvram_set(strcat_r(prefix
, "phytypes", tmp
), var
);
342 *(next
= buf
) = '\0';
343 for (i
= 0; i
< strlen(var
); i
++) {
346 val
= WLCONF_STR2PHYTYPE(phy
);
347 val
= WLCONF_PHYTYPE2BAND(val
);
348 WL_IOCTL(name
, WLC_SET_BAND
, &val
, sizeof(val
));
349 /* Get radio ID on this band */
350 WL_IOCTL(name
, WLC_GET_REVINFO
, &rev
, sizeof(rev
));
351 next
+= sprintf(next
, "%sBCM%X", i
? " " : "",
352 (rev
.radiorev
& IDCODE_ID_MASK
) >> IDCODE_ID_SHIFT
);
354 nvram_set(strcat_r(prefix
, "radioids", tmp
), buf
);
357 str
= nvram_get(strcat_r(prefix
, "phytype", tmp
));
358 val
= WLCONF_STR2PHYTYPE(str
);
359 val
= WLCONF_PHYTYPE2BAND(val
);
360 /* Check errors (card may have changed) */
361 if (wl_ioctl(name
, WLC_SET_BAND
, &val
, sizeof(val
))) {
362 /* default band to the first band in band list */
364 val
= WLCONF_STR2PHYTYPE(phy
);
365 val
= WLCONF_PHYTYPE2BAND(val
);
366 WL_IOCTL(name
, WLC_SET_BAND
, &val
, sizeof(val
));
369 /* Get current core revision */
370 WL_IOCTL(name
, WLC_GET_REVINFO
, &rev
, sizeof(rev
));
371 snprintf(buf
, sizeof(buf
), "%d", rev
.corerev
);
372 nvram_set(strcat_r(prefix
, "corerev", tmp
), buf
);
374 /* Get current phy type */
375 WL_IOCTL(name
, WLC_GET_PHYTYPE
, &phytype
, sizeof(phytype
));
376 snprintf(buf
, sizeof(buf
), "%s", WLCONF_PHYTYPE2STR(phytype
));
377 nvram_set(strcat_r(prefix
, "phytype", tmp
), buf
);
379 /* Set channel before setting gmode or rateset */
380 /* Manual Channel Selection - when channel # is not 0 */
381 val
= atoi(nvram_safe_get(strcat_r(prefix
, "channel", tmp
)));
383 if (wl_ioctl(name
, WLC_SET_CHANNEL
, &val
, sizeof(val
))) {
384 /* Use current channel (card may have changed) */
385 WL_IOCTL(name
, WLC_GET_CHANNEL
, &ci
, sizeof(ci
));
386 snprintf(buf
, sizeof(buf
), "%d", ci
.target_channel
);
387 nvram_set(strcat_r(prefix
, "channel", tmp
), buf
);
391 /* Reset to hardware rateset (band may have changed) */
392 WL_IOCTL(name
, WLC_GET_RATESET
, &rs
, sizeof (wl_rateset_t
));
393 WL_IOCTL(name
, WLC_SET_RATESET
, &rs
, sizeof (wl_rateset_t
));
396 if (phytype
== PHY_TYPE_G
) {
400 gmode
= atoi(nvram_safe_get(strcat_r(prefix
, "gmode", tmp
)));
401 if (gmode
== GMODE_AFTERBURNER
) {
402 if (wl_get_int(name
, "abcap", &val
) || !val
) {
404 snprintf(buf
, sizeof(buf
), "%d", gmode
);
408 WL_IOCTL(name
, WLC_SET_GMODE
, &gmode
, sizeof(gmode
));
410 /* Set gmode protection override and control algorithm */
411 if (gmode
!= GMODE_AFTERBURNER
) {
412 int override
= WLC_G_PROTECTION_OFF
;
413 int control
= WLC_G_PROTECTION_CTL_OFF
;
414 strcat_r(prefix
, "gmode_protection", tmp
);
415 if (nvram_match(tmp
, "auto")) {
416 override
= WLC_G_PROTECTION_AUTO
;
417 control
= WLC_G_PROTECTION_CTL_OVERLAP
;
419 WL_IOCTL(name
, WLC_SET_GMODE_PROTECTION_OVERRIDE
, &override
, sizeof(override
));
420 WL_IOCTL(name
, WLC_SET_GMODE_PROTECTION_CONTROL
, &control
, sizeof(control
));
424 /* Get current rateset (gmode may have changed) */
425 WL_IOCTL(name
, WLC_GET_CURR_RATESET
, &rs
, sizeof (wl_rateset_t
));
427 strcat_r(prefix
, "rateset", tmp
);
428 if (nvram_match(tmp
, "all")) {
429 /* Make all rates basic */
430 for (i
= 0; i
< rs
.count
; i
++)
432 } else if (nvram_match(tmp
, "12")) {
433 /* Make 1 and 2 basic */
434 for (i
= 0; i
< rs
.count
; i
++) {
435 if ((rs
.rates
[i
] & 0x7f) == 2 || (rs
.rates
[i
] & 0x7f) == 4)
438 rs
.rates
[i
] &= ~0x80;
443 WL_IOCTL(name
, WLC_SET_RATESET
, &rs
, sizeof (wl_rateset_t
));
445 /* Allow short preamble override for b cards */
446 if (phytype
== PHY_TYPE_B
|| gmode
== 0) {
447 strcat_r(prefix
, "plcphdr", tmp
);
448 if (nvram_match(tmp
, "long"))
451 val
= WLC_PLCP_SHORT
;
452 WL_IOCTL(name
, WLC_SET_PLCPHDR
, &val
, sizeof(val
));
455 /* Set rate in 500 Kbps units */
456 val
= atoi(nvram_safe_get(strcat_r(prefix
, "rate", tmp
))) / 500000;
457 if (wl_ioctl(name
, WLC_SET_RATE
, &val
, sizeof(val
))) {
458 /* Try default rate (card may have changed) */
460 WL_IOCTL(name
, WLC_SET_RATE
, &val
, sizeof(val
));
461 snprintf(buf
, sizeof(buf
), "%d", val
);
462 nvram_set(strcat_r(prefix
, "rate", tmp
), buf
);
465 /* Set fragmentation threshold */
466 val
= atoi(nvram_safe_get(strcat_r(prefix
, "frag", tmp
)));
467 WL_IOCTL(name
, WLC_SET_FRAG
, &val
, sizeof(val
));
469 /* Set RTS threshold */
470 val
= atoi(nvram_safe_get(strcat_r(prefix
, "rts", tmp
)));
471 WL_IOCTL(name
, WLC_SET_RTS
, &val
, sizeof(val
));
473 /* Set DTIM period */
474 val
= atoi(nvram_safe_get(strcat_r(prefix
, "dtim", tmp
)));
475 WL_IOCTL(name
, WLC_SET_DTIMPRD
, &val
, sizeof(val
));
477 /* Set beacon period */
478 val
= atoi(nvram_safe_get(strcat_r(prefix
, "bcn", tmp
)));
479 WL_IOCTL(name
, WLC_SET_BCNPRD
, &val
, sizeof(val
));
481 /* Set lazy WDS mode */
482 val
= atoi(nvram_safe_get(strcat_r(prefix
, "lazywds", tmp
)));
483 WL_IOCTL(name
, WLC_SET_LAZYWDS
, &val
, sizeof(val
));
485 /* Set the WDS list */
486 maclist
= (struct maclist
*) buf
;
489 foreach(var
, nvram_safe_get(strcat_r(prefix
, "wds", tmp
)), next
) {
490 if (ea
->octet
> &buf
[sizeof(buf
)])
492 ether_atoe(var
, ea
->octet
);
496 WL_IOCTL(name
, WLC_SET_WDSLIST
, buf
, sizeof(buf
));
498 /* Set framebursting mode */
499 val
= nvram_match(strcat_r(prefix
, "frameburst", tmp
), "on");
500 WL_IOCTL(name
, WLC_SET_FAKEFRAG
, &val
, sizeof(val
));
502 /* Bring the interface back up */
503 WL_IOCTL(name
, WLC_UP
, NULL
, 0);
506 val
= atoi(nvram_safe_get(strcat_r(prefix
, "antdiv", tmp
)));
507 WL_IOCTL(name
, WLC_SET_ANTDIV
, &val
, sizeof(val
));
509 /* Auto Channel Selection - when channel # is 0 in AP mode */
511 * The following condition(s) must be met in order for
512 * Auto Channel Selection to work.
513 * - the I/F must be up (change radio channel requires it is up?)
514 * - the AP must not be associated (setting SSID to empty should
515 * make sure it for us)
518 if (!(val
= atoi(nvram_safe_get(strcat_r(prefix
, "channel", tmp
))))) {
519 /* select a channel */
520 val
= wlconf_auto_channel(name
);
521 /* switch to the selected channel */
522 WL_IOCTL(name
, WLC_SET_CHANNEL
, &val
, sizeof(val
));
523 /* set the auto channel scan timer in the driver when in auto mode */
524 val
= 15; /* 15 minutes for now */
525 WL_IOCTL(name
, WLC_SET_CS_SCAN_TIMER
, &val
, sizeof(val
));
528 /* reset the channel scan timer in the driver when not in auto mode */
530 WL_IOCTL(name
, WLC_SET_CS_SCAN_TIMER
, &val
, sizeof(val
));
535 for (i
= 1; i
<= WLC_MAX_DEFAULT_KEYS
; i
++)
536 wlconf_set_wep_key(name
, prefix
, i
);
540 * Need to check errors (card may have changed) and change to
541 * defaults since the new chip may not support the requested
542 * encryptions after the card has been changed.
544 if (wlconf_set_wsec(name
, prefix
)) {
545 /* change nvram only, code below will pass them on */
546 wlconf_restore_var(prefix
, "auth_mode");
547 wlconf_restore_var(prefix
, "auth");
548 /* reset wep to default */
549 wlconf_restore_var(prefix
, "wep");
550 wlconf_set_wsec(name
, prefix
);
553 /* Set WPA authentication mode - radius/wpa/psk */
554 strcat_r(prefix
, "auth_mode", tmp
);
555 if (nvram_match(tmp
, "radius"))
556 val
= WPA_AUTH_DISABLED
;
557 else if (nvram_match(tmp
, "wpa"))
558 val
= WPA_AUTH_UNSPECIFIED
;
559 else if (nvram_match(tmp
, "psk"))
561 else /* if (nvram_match(tmp, "disabled")) */
562 val
= WPA_AUTH_DISABLED
;
563 WL_IOCTL(name
, WLC_SET_WPA_AUTH
, &val
, sizeof(val
));
565 /* Set non-WPA authentication mode - open/shared */
566 val
= atoi(nvram_safe_get(strcat_r(prefix
, "auth", tmp
)));
567 WL_IOCTL(name
, WLC_SET_AUTH
, &val
, sizeof(val
));
569 /* Set WEP restrict if WEP is not disabled */
570 val
= nvram_invmatch(strcat_r(prefix
, "wep", tmp
), "off");
571 WL_IOCTL(name
, WLC_SET_WEP_RESTRICT
, &val
, sizeof(val
));
573 /* Set SSID/Join network */
574 if (ap
| sta
| wet
) {
575 strcat_r(prefix
, "ssid", tmp
);
576 ssid
.SSID_len
= strlen(nvram_safe_get(tmp
));
577 if (ssid
.SSID_len
> sizeof(ssid
.SSID
))
578 ssid
.SSID_len
= sizeof(ssid
.SSID
);
579 strncpy(ssid
.SSID
, nvram_safe_get(tmp
), ssid
.SSID_len
);
581 /* A zero length SSID turns off the AP */
585 WL_IOCTL(name
, WLC_SET_SSID
, &ssid
, sizeof(ssid
));
591 wlconf_down(char *name
)
594 unsigned char buf
[WLC_IOCTL_MAXLEN
];
595 struct maclist
*maclist
;
598 /* Check interface (fail silently for non-wl interfaces) */
599 if ((ret
= wl_probe(name
)))
602 /* Bring down the interface */
603 WL_IOCTL(name
, WLC_GET_UP
, &val
, sizeof(val
));
608 WL_IOCTL(name
, WLC_SET_SSID
, &ssid
, sizeof(ssid
));
610 /* Bring the interface down */
611 WL_IOCTL(name
, WLC_DOWN
, NULL
, sizeof(val
));
614 /* Nuke the WDS list */
615 maclist
= (struct maclist
*) buf
;
617 WL_IOCTL(name
, WLC_SET_WDSLIST
, buf
, sizeof(buf
));
623 main(int argc
, char *argv
[])
627 /* Check parameters and branch based on action */
628 if (argc
== 3 && !strcmp(argv
[2], "up"))
629 ret
= wlconf(argv
[1]);
630 else if (argc
== 3 && !strcmp(argv
[2], "down"))
631 ret
= wlconf_down(argv
[1]);
633 fprintf(stderr
, "Usage: wlconf <ifname> up|down\n");
638 fprintf(stderr
, "wlconf: %s failed (%d)\n", argv
[1], ret
);