11a1e1d21SSam Leffler /*- 27535e66aSSam Leffler * Copyright (c) 2001 Atsushi Onoe 31a1e1d21SSam Leffler * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting 41a1e1d21SSam Leffler * All rights reserved. 51a1e1d21SSam Leffler * 61a1e1d21SSam Leffler * Redistribution and use in source and binary forms, with or without 71a1e1d21SSam Leffler * modification, are permitted provided that the following conditions 81a1e1d21SSam Leffler * are met: 91a1e1d21SSam Leffler * 1. Redistributions of source code must retain the above copyright 107535e66aSSam Leffler * notice, this list of conditions and the following disclaimer. 117535e66aSSam Leffler * 2. Redistributions in binary form must reproduce the above copyright 127535e66aSSam Leffler * notice, this list of conditions and the following disclaimer in the 137535e66aSSam Leffler * documentation and/or other materials provided with the distribution. 147535e66aSSam Leffler * 3. The name of the author may not be used to endorse or promote products 157535e66aSSam Leffler * derived from this software without specific prior written permission. 161a1e1d21SSam Leffler * 171a1e1d21SSam Leffler * Alternatively, this software may be distributed under the terms of the 181a1e1d21SSam Leffler * GNU General Public License ("GPL") version 2 as published by the Free 191a1e1d21SSam Leffler * Software Foundation. 201a1e1d21SSam Leffler * 217535e66aSSam Leffler * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 227535e66aSSam Leffler * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 237535e66aSSam Leffler * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 247535e66aSSam Leffler * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 257535e66aSSam Leffler * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 267535e66aSSam Leffler * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 277535e66aSSam Leffler * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 287535e66aSSam Leffler * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 297535e66aSSam Leffler * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 307535e66aSSam Leffler * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 311a1e1d21SSam Leffler */ 321a1e1d21SSam Leffler 331a1e1d21SSam Leffler #include <sys/cdefs.h> 341a1e1d21SSam Leffler __FBSDID("$FreeBSD$"); 351a1e1d21SSam Leffler 361a1e1d21SSam Leffler /* 371a1e1d21SSam Leffler * IEEE 802.11 ioctl support (FreeBSD-specific) 381a1e1d21SSam Leffler */ 391a1e1d21SSam Leffler 401a1e1d21SSam Leffler #include <sys/endian.h> 411a1e1d21SSam Leffler #include <sys/param.h> 421a1e1d21SSam Leffler #include <sys/kernel.h> 431a1e1d21SSam Leffler #include <sys/socket.h> 441a1e1d21SSam Leffler #include <sys/sockio.h> 451a1e1d21SSam Leffler #include <sys/systm.h> 461a1e1d21SSam Leffler 471a1e1d21SSam Leffler #include <net/if.h> 481a1e1d21SSam Leffler #include <net/if_arp.h> 491a1e1d21SSam Leffler #include <net/if_media.h> 501a1e1d21SSam Leffler #include <net/ethernet.h> 511a1e1d21SSam Leffler 521a1e1d21SSam Leffler #include <net80211/ieee80211_var.h> 531a1e1d21SSam Leffler #include <net80211/ieee80211_ioctl.h> 541a1e1d21SSam Leffler 551a1e1d21SSam Leffler #include <dev/wi/if_wavelan_ieee.h> 561a1e1d21SSam Leffler 571a1e1d21SSam Leffler /* 581a1e1d21SSam Leffler * XXX 591a1e1d21SSam Leffler * Wireless LAN specific configuration interface, which is compatible 601a1e1d21SSam Leffler * with wicontrol(8). 611a1e1d21SSam Leffler */ 621a1e1d21SSam Leffler 631a1e1d21SSam Leffler int 641a1e1d21SSam Leffler ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data) 651a1e1d21SSam Leffler { 661a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 671a1e1d21SSam Leffler int i, j, error; 681a1e1d21SSam Leffler struct ifreq *ifr = (struct ifreq *)data; 691a1e1d21SSam Leffler struct wi_req wreq; 701a1e1d21SSam Leffler struct wi_ltv_keys *keys; 711a1e1d21SSam Leffler struct wi_apinfo *ap; 721a1e1d21SSam Leffler struct ieee80211_node *ni; 731a1e1d21SSam Leffler struct ieee80211_rateset *rs; 741a1e1d21SSam Leffler struct wi_sigcache wsc; 751a1e1d21SSam Leffler struct wi_scan_p2_hdr *p2; 761a1e1d21SSam Leffler struct wi_scan_res *res; 771a1e1d21SSam Leffler 781a1e1d21SSam Leffler error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 791a1e1d21SSam Leffler if (error) 801a1e1d21SSam Leffler return error; 811a1e1d21SSam Leffler wreq.wi_len = 0; 821a1e1d21SSam Leffler switch (wreq.wi_type) { 831a1e1d21SSam Leffler case WI_RID_SERIALNO: 841a1e1d21SSam Leffler /* nothing appropriate */ 851a1e1d21SSam Leffler break; 861a1e1d21SSam Leffler case WI_RID_NODENAME: 871a1e1d21SSam Leffler strcpy((char *)&wreq.wi_val[1], hostname); 881a1e1d21SSam Leffler wreq.wi_val[0] = htole16(strlen(hostname)); 891a1e1d21SSam Leffler wreq.wi_len = (1 + strlen(hostname) + 1) / 2; 901a1e1d21SSam Leffler break; 911a1e1d21SSam Leffler case WI_RID_CURRENT_SSID: 921a1e1d21SSam Leffler if (ic->ic_state != IEEE80211_S_RUN) { 931a1e1d21SSam Leffler wreq.wi_val[0] = 0; 941a1e1d21SSam Leffler wreq.wi_len = 1; 951a1e1d21SSam Leffler break; 961a1e1d21SSam Leffler } 971a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_bss->ni_esslen); 981a1e1d21SSam Leffler memcpy(&wreq.wi_val[1], ic->ic_bss->ni_essid, 991a1e1d21SSam Leffler ic->ic_bss->ni_esslen); 1001a1e1d21SSam Leffler wreq.wi_len = (1 + ic->ic_bss->ni_esslen + 1) / 2; 1011a1e1d21SSam Leffler break; 1021a1e1d21SSam Leffler case WI_RID_OWN_SSID: 1031a1e1d21SSam Leffler case WI_RID_DESIRED_SSID: 1041a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_des_esslen); 1051a1e1d21SSam Leffler memcpy(&wreq.wi_val[1], ic->ic_des_essid, ic->ic_des_esslen); 1061a1e1d21SSam Leffler wreq.wi_len = (1 + ic->ic_des_esslen + 1) / 2; 1071a1e1d21SSam Leffler break; 1081a1e1d21SSam Leffler case WI_RID_CURRENT_BSSID: 1091a1e1d21SSam Leffler if (ic->ic_state == IEEE80211_S_RUN) 1101a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_bss->ni_bssid); 1111a1e1d21SSam Leffler else 1121a1e1d21SSam Leffler memset(wreq.wi_val, 0, IEEE80211_ADDR_LEN); 1131a1e1d21SSam Leffler wreq.wi_len = IEEE80211_ADDR_LEN / 2; 1141a1e1d21SSam Leffler break; 1151a1e1d21SSam Leffler case WI_RID_CHANNEL_LIST: 1161a1e1d21SSam Leffler memset(wreq.wi_val, 0, sizeof(wreq.wi_val)); 1171a1e1d21SSam Leffler /* 1181a1e1d21SSam Leffler * Since channel 0 is not available for DS, channel 1 1191a1e1d21SSam Leffler * is assigned to LSB on WaveLAN. 1201a1e1d21SSam Leffler */ 1211a1e1d21SSam Leffler if (ic->ic_phytype == IEEE80211_T_DS) 1221a1e1d21SSam Leffler i = 1; 1231a1e1d21SSam Leffler else 1241a1e1d21SSam Leffler i = 0; 1251a1e1d21SSam Leffler for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) 1261a1e1d21SSam Leffler if (isset(ic->ic_chan_active, i)) { 1271a1e1d21SSam Leffler setbit((u_int8_t *)wreq.wi_val, j); 1281a1e1d21SSam Leffler wreq.wi_len = j / 16 + 1; 1291a1e1d21SSam Leffler } 1301a1e1d21SSam Leffler break; 1311a1e1d21SSam Leffler case WI_RID_OWN_CHNL: 1321a1e1d21SSam Leffler wreq.wi_val[0] = htole16( 1331a1e1d21SSam Leffler ieee80211_chan2ieee(ic, ic->ic_ibss_chan)); 1341a1e1d21SSam Leffler wreq.wi_len = 1; 1351a1e1d21SSam Leffler break; 1361a1e1d21SSam Leffler case WI_RID_CURRENT_CHAN: 1371a1e1d21SSam Leffler wreq.wi_val[0] = htole16( 1381a1e1d21SSam Leffler ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)); 1391a1e1d21SSam Leffler wreq.wi_len = 1; 1401a1e1d21SSam Leffler break; 1411a1e1d21SSam Leffler case WI_RID_COMMS_QUALITY: 1421a1e1d21SSam Leffler wreq.wi_val[0] = 0; /* quality */ 143d1e61976SSam Leffler wreq.wi_val[1] = 144d1e61976SSam Leffler htole16((*ic->ic_node_getrssi)(ic, ic->ic_bss)); 1451a1e1d21SSam Leffler wreq.wi_val[2] = 0; /* noise */ 1461a1e1d21SSam Leffler wreq.wi_len = 3; 1471a1e1d21SSam Leffler break; 1481a1e1d21SSam Leffler case WI_RID_PROMISC: 1491a1e1d21SSam Leffler wreq.wi_val[0] = htole16((ifp->if_flags & IFF_PROMISC) ? 1 : 0); 1501a1e1d21SSam Leffler wreq.wi_len = 1; 1511a1e1d21SSam Leffler break; 1521a1e1d21SSam Leffler case WI_RID_PORTTYPE: 1531a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_opmode); 1541a1e1d21SSam Leffler wreq.wi_len = 1; 1551a1e1d21SSam Leffler break; 1561a1e1d21SSam Leffler case WI_RID_MAC_NODE: 1571a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_myaddr); 1581a1e1d21SSam Leffler wreq.wi_len = IEEE80211_ADDR_LEN / 2; 1591a1e1d21SSam Leffler break; 1601a1e1d21SSam Leffler case WI_RID_TX_RATE: 1611a1e1d21SSam Leffler if (ic->ic_fixed_rate == -1) 1621a1e1d21SSam Leffler wreq.wi_val[0] = 0; /* auto */ 1631a1e1d21SSam Leffler else 1641a1e1d21SSam Leffler wreq.wi_val[0] = htole16( 1651a1e1d21SSam Leffler (ic->ic_sup_rates[ic->ic_curmode].rs_rates[ic->ic_fixed_rate] & 1661a1e1d21SSam Leffler IEEE80211_RATE_VAL) / 2); 1671a1e1d21SSam Leffler wreq.wi_len = 1; 1681a1e1d21SSam Leffler break; 1691a1e1d21SSam Leffler case WI_RID_CUR_TX_RATE: 1701a1e1d21SSam Leffler wreq.wi_val[0] = htole16( 1711a1e1d21SSam Leffler (ic->ic_bss->ni_rates.rs_rates[ic->ic_bss->ni_txrate] & 1721a1e1d21SSam Leffler IEEE80211_RATE_VAL) / 2); 1731a1e1d21SSam Leffler wreq.wi_len = 1; 1741a1e1d21SSam Leffler break; 1751a1e1d21SSam Leffler case WI_RID_RTS_THRESH: 1761a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_rtsthreshold); 1771a1e1d21SSam Leffler wreq.wi_len = 1; 1781a1e1d21SSam Leffler break; 1791a1e1d21SSam Leffler case WI_RID_CREATE_IBSS: 1801a1e1d21SSam Leffler wreq.wi_val[0] = 1811a1e1d21SSam Leffler htole16((ic->ic_flags & IEEE80211_F_IBSSON) ? 1 : 0); 1821a1e1d21SSam Leffler wreq.wi_len = 1; 1831a1e1d21SSam Leffler break; 1841a1e1d21SSam Leffler case WI_RID_MICROWAVE_OVEN: 1851a1e1d21SSam Leffler wreq.wi_val[0] = 0; /* no ... not supported */ 1861a1e1d21SSam Leffler wreq.wi_len = 1; 1871a1e1d21SSam Leffler break; 1881a1e1d21SSam Leffler case WI_RID_ROAMING_MODE: 1891a1e1d21SSam Leffler wreq.wi_val[0] = htole16(1); /* enabled ... not supported */ 1901a1e1d21SSam Leffler wreq.wi_len = 1; 1911a1e1d21SSam Leffler break; 1921a1e1d21SSam Leffler case WI_RID_SYSTEM_SCALE: 1931a1e1d21SSam Leffler wreq.wi_val[0] = htole16(1); /* low density ... not supp */ 1941a1e1d21SSam Leffler wreq.wi_len = 1; 1951a1e1d21SSam Leffler break; 1961a1e1d21SSam Leffler case WI_RID_PM_ENABLED: 1971a1e1d21SSam Leffler wreq.wi_val[0] = 1981a1e1d21SSam Leffler htole16((ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0); 1991a1e1d21SSam Leffler wreq.wi_len = 1; 2001a1e1d21SSam Leffler break; 2011a1e1d21SSam Leffler case WI_RID_MAX_SLEEP: 2021a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_lintval); 2031a1e1d21SSam Leffler wreq.wi_len = 1; 2041a1e1d21SSam Leffler break; 2051a1e1d21SSam Leffler case WI_RID_CUR_BEACON_INT: 2061a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_bss->ni_intval); 2071a1e1d21SSam Leffler wreq.wi_len = 1; 2081a1e1d21SSam Leffler break; 2091a1e1d21SSam Leffler case WI_RID_WEP_AVAIL: 2101a1e1d21SSam Leffler wreq.wi_val[0] = 2111a1e1d21SSam Leffler htole16((ic->ic_caps & IEEE80211_C_WEP) ? 1 : 0); 2121a1e1d21SSam Leffler wreq.wi_len = 1; 2131a1e1d21SSam Leffler break; 2141a1e1d21SSam Leffler case WI_RID_CNFAUTHMODE: 2151a1e1d21SSam Leffler wreq.wi_val[0] = htole16(1); /* TODO: open system only */ 2161a1e1d21SSam Leffler wreq.wi_len = 1; 2171a1e1d21SSam Leffler break; 2181a1e1d21SSam Leffler case WI_RID_ENCRYPTION: 2191a1e1d21SSam Leffler wreq.wi_val[0] = 2201a1e1d21SSam Leffler htole16((ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 0); 2211a1e1d21SSam Leffler wreq.wi_len = 1; 2221a1e1d21SSam Leffler break; 2231a1e1d21SSam Leffler case WI_RID_TX_CRYPT_KEY: 2241a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_wep_txkey); 2251a1e1d21SSam Leffler wreq.wi_len = 1; 2261a1e1d21SSam Leffler break; 2271a1e1d21SSam Leffler case WI_RID_DEFLT_CRYPT_KEYS: 2281a1e1d21SSam Leffler keys = (struct wi_ltv_keys *)&wreq; 2291a1e1d21SSam Leffler /* do not show keys to non-root user */ 2301a1e1d21SSam Leffler error = suser(curthread); 2311a1e1d21SSam Leffler if (error) { 2321a1e1d21SSam Leffler memset(keys, 0, sizeof(*keys)); 2331a1e1d21SSam Leffler error = 0; 2341a1e1d21SSam Leffler break; 2351a1e1d21SSam Leffler } 2361a1e1d21SSam Leffler for (i = 0; i < IEEE80211_WEP_NKID; i++) { 2371a1e1d21SSam Leffler keys->wi_keys[i].wi_keylen = 2381a1e1d21SSam Leffler htole16(ic->ic_nw_keys[i].wk_len); 2391a1e1d21SSam Leffler memcpy(keys->wi_keys[i].wi_keydat, 2401a1e1d21SSam Leffler ic->ic_nw_keys[i].wk_key, ic->ic_nw_keys[i].wk_len); 2411a1e1d21SSam Leffler } 2421a1e1d21SSam Leffler wreq.wi_len = sizeof(*keys) / 2; 2431a1e1d21SSam Leffler break; 2441a1e1d21SSam Leffler case WI_RID_MAX_DATALEN: 2451a1e1d21SSam Leffler wreq.wi_val[0] = htole16(IEEE80211_MAX_LEN); /* TODO: frag */ 2461a1e1d21SSam Leffler wreq.wi_len = 1; 2471a1e1d21SSam Leffler break; 2481a1e1d21SSam Leffler case WI_RID_IFACE_STATS: 2491a1e1d21SSam Leffler /* XXX: should be implemented in lower drivers */ 2501a1e1d21SSam Leffler break; 2511a1e1d21SSam Leffler case WI_RID_READ_APS: 2521a1e1d21SSam Leffler if (ic->ic_opmode != IEEE80211_M_HOSTAP) { 2531a1e1d21SSam Leffler /* 2541a1e1d21SSam Leffler * Don't return results until active scan completes. 2551a1e1d21SSam Leffler */ 2561a1e1d21SSam Leffler if (ic->ic_state == IEEE80211_S_SCAN && 2571a1e1d21SSam Leffler (ic->ic_flags & IEEE80211_F_ASCAN)) { 2581a1e1d21SSam Leffler error = EINPROGRESS; 2591a1e1d21SSam Leffler break; 2601a1e1d21SSam Leffler } 2611a1e1d21SSam Leffler } 2621a1e1d21SSam Leffler i = 0; 2631a1e1d21SSam Leffler ap = (void *)((char *)wreq.wi_val + sizeof(i)); 2641a1e1d21SSam Leffler TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { 2651a1e1d21SSam Leffler if ((caddr_t)(ap + 1) > (caddr_t)(&wreq + 1)) 2661a1e1d21SSam Leffler break; 2671a1e1d21SSam Leffler memset(ap, 0, sizeof(*ap)); 2681a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 2691a1e1d21SSam Leffler IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr); 2701a1e1d21SSam Leffler ap->namelen = ic->ic_des_esslen; 2711a1e1d21SSam Leffler if (ic->ic_des_esslen) 2721a1e1d21SSam Leffler memcpy(ap->name, ic->ic_des_essid, 2731a1e1d21SSam Leffler ic->ic_des_esslen); 2741a1e1d21SSam Leffler } else { 2751a1e1d21SSam Leffler IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid); 2761a1e1d21SSam Leffler ap->namelen = ni->ni_esslen; 2771a1e1d21SSam Leffler if (ni->ni_esslen) 2781a1e1d21SSam Leffler memcpy(ap->name, ni->ni_essid, 2791a1e1d21SSam Leffler ni->ni_esslen); 2801a1e1d21SSam Leffler } 2811a1e1d21SSam Leffler ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan); 282d1e61976SSam Leffler ap->signal = (*ic->ic_node_getrssi)(ic, ni); 2831a1e1d21SSam Leffler ap->capinfo = ni->ni_capinfo; 2841a1e1d21SSam Leffler ap->interval = ni->ni_intval; 2851a1e1d21SSam Leffler rs = &ni->ni_rates; 2861a1e1d21SSam Leffler for (j = 0; j < rs->rs_nrates; j++) { 2871a1e1d21SSam Leffler if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) { 2881a1e1d21SSam Leffler ap->rate = (rs->rs_rates[j] & 2891a1e1d21SSam Leffler IEEE80211_RATE_VAL) * 5; /* XXX */ 2901a1e1d21SSam Leffler } 2911a1e1d21SSam Leffler } 2921a1e1d21SSam Leffler i++; 2931a1e1d21SSam Leffler ap++; 2941a1e1d21SSam Leffler } 2951a1e1d21SSam Leffler memcpy(wreq.wi_val, &i, sizeof(i)); 2961a1e1d21SSam Leffler wreq.wi_len = (sizeof(int) + sizeof(*ap) * i) / 2; 2971a1e1d21SSam Leffler break; 2981a1e1d21SSam Leffler case WI_RID_PRISM2: 2991a1e1d21SSam Leffler wreq.wi_val[0] = 1; /* XXX lie so SCAN_RES can give rates */ 3001a1e1d21SSam Leffler wreq.wi_len = sizeof(u_int16_t) / 2; 3011a1e1d21SSam Leffler break; 3021a1e1d21SSam Leffler case WI_RID_SCAN_RES: /* compatibility interface */ 3031a1e1d21SSam Leffler if (ic->ic_opmode != IEEE80211_M_HOSTAP && 3041a1e1d21SSam Leffler ic->ic_state == IEEE80211_S_SCAN) { 3051a1e1d21SSam Leffler error = EINPROGRESS; 3061a1e1d21SSam Leffler break; 3071a1e1d21SSam Leffler } 3081a1e1d21SSam Leffler /* NB: we use the Prism2 format so we can return rate info */ 3091a1e1d21SSam Leffler p2 = (struct wi_scan_p2_hdr *)wreq.wi_val; 3101a1e1d21SSam Leffler res = (void *)&p2[1]; 3111a1e1d21SSam Leffler i = 0; 3121a1e1d21SSam Leffler TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { 3131a1e1d21SSam Leffler if ((caddr_t)(res + 1) > (caddr_t)(&wreq + 1)) 3141a1e1d21SSam Leffler break; 3151a1e1d21SSam Leffler res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan); 3161a1e1d21SSam Leffler res->wi_noise = 0; 317d1e61976SSam Leffler res->wi_signal = (*ic->ic_node_getrssi)(ic, ni); 3181a1e1d21SSam Leffler IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid); 3191a1e1d21SSam Leffler res->wi_interval = ni->ni_intval; 3201a1e1d21SSam Leffler res->wi_capinfo = ni->ni_capinfo; 3211a1e1d21SSam Leffler res->wi_ssid_len = ni->ni_esslen; 3221a1e1d21SSam Leffler memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN); 3231a1e1d21SSam Leffler /* NB: assumes wi_srates holds <= ni->ni_rates */ 3241a1e1d21SSam Leffler memcpy(res->wi_srates, ni->ni_rates.rs_rates, 3251a1e1d21SSam Leffler sizeof(res->wi_srates)); 3261a1e1d21SSam Leffler if (ni->ni_rates.rs_nrates < 10) 3271a1e1d21SSam Leffler res->wi_srates[ni->ni_rates.rs_nrates] = 0; 3281a1e1d21SSam Leffler res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate]; 3291a1e1d21SSam Leffler res->wi_rsvd = 0; 3301a1e1d21SSam Leffler res++, i++; 3311a1e1d21SSam Leffler } 3321a1e1d21SSam Leffler p2->wi_rsvd = 0; 3331a1e1d21SSam Leffler p2->wi_reason = i; 3341a1e1d21SSam Leffler wreq.wi_len = (sizeof(*p2) + sizeof(*res) * i) / 2; 3351a1e1d21SSam Leffler break; 3361a1e1d21SSam Leffler case WI_RID_READ_CACHE: 3371a1e1d21SSam Leffler i = 0; 3381a1e1d21SSam Leffler TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { 3391a1e1d21SSam Leffler if (i == (WI_MAX_DATALEN/sizeof(struct wi_sigcache))-1) 3401a1e1d21SSam Leffler break; 3411a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wsc.macsrc, ni->ni_macaddr); 3421a1e1d21SSam Leffler memset(&wsc.ipsrc, 0, sizeof(wsc.ipsrc)); 343d1e61976SSam Leffler wsc.signal = (*ic->ic_node_getrssi)(ic, ni); 3441a1e1d21SSam Leffler wsc.noise = 0; 3451a1e1d21SSam Leffler wsc.quality = 0; 3461a1e1d21SSam Leffler memcpy((caddr_t)wreq.wi_val + sizeof(wsc) * i, 3471a1e1d21SSam Leffler &wsc, sizeof(wsc)); 3481a1e1d21SSam Leffler i++; 3491a1e1d21SSam Leffler } 3501a1e1d21SSam Leffler wreq.wi_len = sizeof(wsc) * i / 2; 3511a1e1d21SSam Leffler break; 3521a1e1d21SSam Leffler case WI_RID_SCAN_APS: 3531a1e1d21SSam Leffler error = EINVAL; 3541a1e1d21SSam Leffler break; 3551a1e1d21SSam Leffler default: 3561a1e1d21SSam Leffler error = EINVAL; 3571a1e1d21SSam Leffler break; 3581a1e1d21SSam Leffler } 3591a1e1d21SSam Leffler if (error == 0) { 3601a1e1d21SSam Leffler wreq.wi_len++; 3611a1e1d21SSam Leffler error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); 3621a1e1d21SSam Leffler } 3631a1e1d21SSam Leffler return error; 3641a1e1d21SSam Leffler } 3651a1e1d21SSam Leffler 3661a1e1d21SSam Leffler static int 3671a1e1d21SSam Leffler findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) 3681a1e1d21SSam Leffler { 3691a1e1d21SSam Leffler #define IEEERATE(_ic,_m,_i) \ 3701a1e1d21SSam Leffler ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) 3711a1e1d21SSam Leffler int i, nrates = ic->ic_sup_rates[mode].rs_nrates; 3721a1e1d21SSam Leffler for (i = 0; i < nrates; i++) 3731a1e1d21SSam Leffler if (IEEERATE(ic, mode, i) == rate) 3741a1e1d21SSam Leffler return i; 3751a1e1d21SSam Leffler return -1; 3761a1e1d21SSam Leffler #undef IEEERATE 3771a1e1d21SSam Leffler } 3781a1e1d21SSam Leffler 3791a1e1d21SSam Leffler int 3801a1e1d21SSam Leffler ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) 3811a1e1d21SSam Leffler { 3821a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 3831a1e1d21SSam Leffler int i, j, len, error, rate; 3841a1e1d21SSam Leffler struct ifreq *ifr = (struct ifreq *)data; 3851a1e1d21SSam Leffler struct wi_ltv_keys *keys; 3861a1e1d21SSam Leffler struct wi_req wreq; 3871a1e1d21SSam Leffler u_char chanlist[roundup(IEEE80211_CHAN_MAX, NBBY)]; 3881a1e1d21SSam Leffler 3891a1e1d21SSam Leffler error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 3901a1e1d21SSam Leffler if (error) 3911a1e1d21SSam Leffler return error; 3921a1e1d21SSam Leffler len = wreq.wi_len ? (wreq.wi_len - 1) * 2 : 0; 3931a1e1d21SSam Leffler switch (wreq.wi_type) { 3941a1e1d21SSam Leffler case WI_RID_SERIALNO: 3951a1e1d21SSam Leffler case WI_RID_NODENAME: 3961a1e1d21SSam Leffler return EPERM; 3971a1e1d21SSam Leffler case WI_RID_CURRENT_SSID: 3981a1e1d21SSam Leffler return EPERM; 3991a1e1d21SSam Leffler case WI_RID_OWN_SSID: 4001a1e1d21SSam Leffler case WI_RID_DESIRED_SSID: 4011a1e1d21SSam Leffler if (le16toh(wreq.wi_val[0]) * 2 > len || 4021a1e1d21SSam Leffler le16toh(wreq.wi_val[0]) > IEEE80211_NWID_LEN) { 4031a1e1d21SSam Leffler error = ENOSPC; 4041a1e1d21SSam Leffler break; 4051a1e1d21SSam Leffler } 4061a1e1d21SSam Leffler memset(ic->ic_des_essid, 0, sizeof(ic->ic_des_essid)); 4071a1e1d21SSam Leffler ic->ic_des_esslen = le16toh(wreq.wi_val[0]) * 2; 408b9ee58c4SSam Leffler memcpy(ic->ic_des_essid, &wreq.wi_val[1], ic->ic_des_esslen); 4091a1e1d21SSam Leffler error = ENETRESET; 4101a1e1d21SSam Leffler break; 4111a1e1d21SSam Leffler case WI_RID_CURRENT_BSSID: 4121a1e1d21SSam Leffler return EPERM; 4131a1e1d21SSam Leffler case WI_RID_OWN_CHNL: 4141a1e1d21SSam Leffler if (len != 2) 4151a1e1d21SSam Leffler return EINVAL; 4161a1e1d21SSam Leffler i = le16toh(wreq.wi_val[0]); 4171a1e1d21SSam Leffler if (i < 0 || 4181a1e1d21SSam Leffler i > IEEE80211_CHAN_MAX || 4191a1e1d21SSam Leffler isclr(ic->ic_chan_active, i)) 4201a1e1d21SSam Leffler return EINVAL; 4211a1e1d21SSam Leffler ic->ic_ibss_chan = &ic->ic_channels[i]; 4221a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_SIBSS) 4231a1e1d21SSam Leffler error = ENETRESET; 4241a1e1d21SSam Leffler break; 4251a1e1d21SSam Leffler case WI_RID_CURRENT_CHAN: 4261a1e1d21SSam Leffler return EPERM; 4271a1e1d21SSam Leffler case WI_RID_COMMS_QUALITY: 4281a1e1d21SSam Leffler return EPERM; 4291a1e1d21SSam Leffler case WI_RID_PROMISC: 4301a1e1d21SSam Leffler if (len != 2) 4311a1e1d21SSam Leffler return EINVAL; 4321a1e1d21SSam Leffler if (ifp->if_flags & IFF_PROMISC) { 4331a1e1d21SSam Leffler if (wreq.wi_val[0] == 0) { 4341a1e1d21SSam Leffler ifp->if_flags &= ~IFF_PROMISC; 4351a1e1d21SSam Leffler error = ENETRESET; 4361a1e1d21SSam Leffler } 4371a1e1d21SSam Leffler } else { 4381a1e1d21SSam Leffler if (wreq.wi_val[0] != 0) { 4391a1e1d21SSam Leffler ifp->if_flags |= IFF_PROMISC; 4401a1e1d21SSam Leffler error = ENETRESET; 4411a1e1d21SSam Leffler } 4421a1e1d21SSam Leffler } 4431a1e1d21SSam Leffler break; 4441a1e1d21SSam Leffler case WI_RID_PORTTYPE: 4451a1e1d21SSam Leffler if (len != 2) 4461a1e1d21SSam Leffler return EINVAL; 4471a1e1d21SSam Leffler switch (le16toh(wreq.wi_val[0])) { 4481a1e1d21SSam Leffler case IEEE80211_M_STA: 4491a1e1d21SSam Leffler break; 4501a1e1d21SSam Leffler case IEEE80211_M_IBSS: 4511a1e1d21SSam Leffler if (!(ic->ic_caps & IEEE80211_C_IBSS)) 4521a1e1d21SSam Leffler return EINVAL; 4531a1e1d21SSam Leffler break; 4541a1e1d21SSam Leffler case IEEE80211_M_AHDEMO: 4551a1e1d21SSam Leffler if (ic->ic_phytype != IEEE80211_T_DS || 4561a1e1d21SSam Leffler !(ic->ic_caps & IEEE80211_C_AHDEMO)) 4571a1e1d21SSam Leffler return EINVAL; 4581a1e1d21SSam Leffler break; 4591a1e1d21SSam Leffler case IEEE80211_M_HOSTAP: 4601a1e1d21SSam Leffler if (!(ic->ic_caps & IEEE80211_C_HOSTAP)) 4611a1e1d21SSam Leffler return EINVAL; 4621a1e1d21SSam Leffler break; 4631a1e1d21SSam Leffler default: 4641a1e1d21SSam Leffler return EINVAL; 4651a1e1d21SSam Leffler } 4661a1e1d21SSam Leffler if (le16toh(wreq.wi_val[0]) != ic->ic_opmode) { 4671a1e1d21SSam Leffler ic->ic_opmode = le16toh(wreq.wi_val[0]); 4681a1e1d21SSam Leffler error = ENETRESET; 4691a1e1d21SSam Leffler } 4701a1e1d21SSam Leffler break; 4711a1e1d21SSam Leffler #if 0 4721a1e1d21SSam Leffler case WI_RID_MAC_NODE: 4731a1e1d21SSam Leffler if (len != IEEE80211_ADDR_LEN) 4741a1e1d21SSam Leffler return EINVAL; 4751a1e1d21SSam Leffler IEEE80211_ADDR_COPY(LLADDR(ifp->if_sadl), wreq.wi_val); 4761a1e1d21SSam Leffler /* if_init will copy lladdr into ic_myaddr */ 4771a1e1d21SSam Leffler error = ENETRESET; 4781a1e1d21SSam Leffler break; 4791a1e1d21SSam Leffler #endif 4801a1e1d21SSam Leffler case WI_RID_TX_RATE: 4811a1e1d21SSam Leffler if (len != 2) 4821a1e1d21SSam Leffler return EINVAL; 4831a1e1d21SSam Leffler if (wreq.wi_val[0] == 0) { 4841a1e1d21SSam Leffler /* auto */ 4851a1e1d21SSam Leffler ic->ic_fixed_rate = -1; 4861a1e1d21SSam Leffler break; 4871a1e1d21SSam Leffler } 4881a1e1d21SSam Leffler rate = 2 * le16toh(wreq.wi_val[0]); 4891a1e1d21SSam Leffler if (ic->ic_curmode == IEEE80211_MODE_AUTO) { 4901a1e1d21SSam Leffler /* 4911a1e1d21SSam Leffler * In autoselect mode search for the rate. We take 4921a1e1d21SSam Leffler * the first instance which may not be right, but we 4931a1e1d21SSam Leffler * are limited by the interface. Note that we also 4941a1e1d21SSam Leffler * lock the mode to insure the rate is meaningful 4951a1e1d21SSam Leffler * when it is used. 4961a1e1d21SSam Leffler */ 4971a1e1d21SSam Leffler for (j = IEEE80211_MODE_11A; 4981a1e1d21SSam Leffler j < IEEE80211_MODE_MAX; j++) { 4991a1e1d21SSam Leffler if ((ic->ic_modecaps & (1<<j)) == 0) 5001a1e1d21SSam Leffler continue; 5011a1e1d21SSam Leffler i = findrate(ic, j, rate); 5021a1e1d21SSam Leffler if (i != -1) { 5031a1e1d21SSam Leffler /* lock mode too */ 5041a1e1d21SSam Leffler ic->ic_curmode = j; 5051a1e1d21SSam Leffler goto setrate; 5061a1e1d21SSam Leffler } 5071a1e1d21SSam Leffler } 5081a1e1d21SSam Leffler } else { 5091a1e1d21SSam Leffler i = findrate(ic, ic->ic_curmode, rate); 5101a1e1d21SSam Leffler if (i != -1) 5111a1e1d21SSam Leffler goto setrate; 5121a1e1d21SSam Leffler } 5131a1e1d21SSam Leffler return EINVAL; 5141a1e1d21SSam Leffler setrate: 5151a1e1d21SSam Leffler ic->ic_fixed_rate = i; 5161a1e1d21SSam Leffler error = ENETRESET; 5171a1e1d21SSam Leffler break; 5181a1e1d21SSam Leffler case WI_RID_CUR_TX_RATE: 5191a1e1d21SSam Leffler return EPERM; 5201a1e1d21SSam Leffler case WI_RID_RTS_THRESH: 5211a1e1d21SSam Leffler if (len != 2) 5221a1e1d21SSam Leffler return EINVAL; 5231a1e1d21SSam Leffler if (le16toh(wreq.wi_val[0]) != IEEE80211_MAX_LEN) 5241a1e1d21SSam Leffler return EINVAL; /* TODO: RTS */ 5251a1e1d21SSam Leffler break; 5261a1e1d21SSam Leffler case WI_RID_CREATE_IBSS: 5271a1e1d21SSam Leffler if (len != 2) 5281a1e1d21SSam Leffler return EINVAL; 5291a1e1d21SSam Leffler if (wreq.wi_val[0] != 0) { 5301a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_IBSS) == 0) 5311a1e1d21SSam Leffler return EINVAL; 5321a1e1d21SSam Leffler if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) { 5331a1e1d21SSam Leffler ic->ic_flags |= IEEE80211_F_IBSSON; 5341a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS && 5351a1e1d21SSam Leffler ic->ic_state == IEEE80211_S_SCAN) 5361a1e1d21SSam Leffler error = ENETRESET; 5371a1e1d21SSam Leffler } 5381a1e1d21SSam Leffler } else { 5391a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_IBSSON) { 5401a1e1d21SSam Leffler ic->ic_flags &= ~IEEE80211_F_IBSSON; 5411a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_SIBSS) { 5421a1e1d21SSam Leffler ic->ic_flags &= ~IEEE80211_F_SIBSS; 5431a1e1d21SSam Leffler error = ENETRESET; 5441a1e1d21SSam Leffler } 5451a1e1d21SSam Leffler } 5461a1e1d21SSam Leffler } 5471a1e1d21SSam Leffler break; 5481a1e1d21SSam Leffler case WI_RID_MICROWAVE_OVEN: 5491a1e1d21SSam Leffler if (len != 2) 5501a1e1d21SSam Leffler return EINVAL; 5511a1e1d21SSam Leffler if (wreq.wi_val[0] != 0) 5521a1e1d21SSam Leffler return EINVAL; /* not supported */ 5531a1e1d21SSam Leffler break; 5541a1e1d21SSam Leffler case WI_RID_ROAMING_MODE: 5551a1e1d21SSam Leffler if (len != 2) 5561a1e1d21SSam Leffler return EINVAL; 5571a1e1d21SSam Leffler if (le16toh(wreq.wi_val[0]) != 1) 5581a1e1d21SSam Leffler return EINVAL; /* not supported */ 5591a1e1d21SSam Leffler break; 5601a1e1d21SSam Leffler case WI_RID_SYSTEM_SCALE: 5611a1e1d21SSam Leffler if (len != 2) 5621a1e1d21SSam Leffler return EINVAL; 5631a1e1d21SSam Leffler if (le16toh(wreq.wi_val[0]) != 1) 5641a1e1d21SSam Leffler return EINVAL; /* not supported */ 5651a1e1d21SSam Leffler break; 5661a1e1d21SSam Leffler case WI_RID_PM_ENABLED: 5671a1e1d21SSam Leffler if (len != 2) 5681a1e1d21SSam Leffler return EINVAL; 5691a1e1d21SSam Leffler if (wreq.wi_val[0] != 0) { 5701a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) 5711a1e1d21SSam Leffler return EINVAL; 5721a1e1d21SSam Leffler if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { 5731a1e1d21SSam Leffler ic->ic_flags |= IEEE80211_F_PMGTON; 5741a1e1d21SSam Leffler error = ENETRESET; 5751a1e1d21SSam Leffler } 5761a1e1d21SSam Leffler } else { 5771a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_PMGTON) { 5781a1e1d21SSam Leffler ic->ic_flags &= ~IEEE80211_F_PMGTON; 5791a1e1d21SSam Leffler error = ENETRESET; 5801a1e1d21SSam Leffler } 5811a1e1d21SSam Leffler } 5821a1e1d21SSam Leffler break; 5831a1e1d21SSam Leffler case WI_RID_MAX_SLEEP: 5841a1e1d21SSam Leffler if (len != 2) 5851a1e1d21SSam Leffler return EINVAL; 5861a1e1d21SSam Leffler ic->ic_lintval = le16toh(wreq.wi_val[0]); 5871a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_PMGTON) 5881a1e1d21SSam Leffler error = ENETRESET; 5891a1e1d21SSam Leffler break; 5901a1e1d21SSam Leffler case WI_RID_CUR_BEACON_INT: 5911a1e1d21SSam Leffler return EPERM; 5921a1e1d21SSam Leffler case WI_RID_WEP_AVAIL: 5931a1e1d21SSam Leffler return EPERM; 5941a1e1d21SSam Leffler case WI_RID_CNFAUTHMODE: 5951a1e1d21SSam Leffler if (len != 2) 5961a1e1d21SSam Leffler return EINVAL; 5971a1e1d21SSam Leffler if (le16toh(wreq.wi_val[0]) != 1) 5981a1e1d21SSam Leffler return EINVAL; /* TODO: shared key auth */ 5991a1e1d21SSam Leffler break; 6001a1e1d21SSam Leffler case WI_RID_ENCRYPTION: 6011a1e1d21SSam Leffler if (len != 2) 6021a1e1d21SSam Leffler return EINVAL; 6031a1e1d21SSam Leffler if (wreq.wi_val[0] != 0) { 6041a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_WEP) == 0) 6051a1e1d21SSam Leffler return EINVAL; 6061a1e1d21SSam Leffler if ((ic->ic_flags & IEEE80211_F_WEPON) == 0) { 6071a1e1d21SSam Leffler ic->ic_flags |= IEEE80211_F_WEPON; 6081a1e1d21SSam Leffler error = ENETRESET; 6091a1e1d21SSam Leffler } 6101a1e1d21SSam Leffler } else { 6111a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_WEPON) { 6121a1e1d21SSam Leffler ic->ic_flags &= ~IEEE80211_F_WEPON; 6131a1e1d21SSam Leffler error = ENETRESET; 6141a1e1d21SSam Leffler } 6151a1e1d21SSam Leffler } 6161a1e1d21SSam Leffler break; 6171a1e1d21SSam Leffler case WI_RID_TX_CRYPT_KEY: 6181a1e1d21SSam Leffler if (len != 2) 6191a1e1d21SSam Leffler return EINVAL; 6201a1e1d21SSam Leffler i = le16toh(wreq.wi_val[0]); 6211a1e1d21SSam Leffler if (i >= IEEE80211_WEP_NKID) 6221a1e1d21SSam Leffler return EINVAL; 6231a1e1d21SSam Leffler ic->ic_wep_txkey = i; 6241a1e1d21SSam Leffler break; 6251a1e1d21SSam Leffler case WI_RID_DEFLT_CRYPT_KEYS: 6261a1e1d21SSam Leffler if (len != sizeof(struct wi_ltv_keys)) 6271a1e1d21SSam Leffler return EINVAL; 6281a1e1d21SSam Leffler keys = (struct wi_ltv_keys *)&wreq; 6291a1e1d21SSam Leffler for (i = 0; i < IEEE80211_WEP_NKID; i++) { 6301a1e1d21SSam Leffler len = le16toh(keys->wi_keys[i].wi_keylen); 6311a1e1d21SSam Leffler if (len != 0 && len < IEEE80211_WEP_KEYLEN) 6321a1e1d21SSam Leffler return EINVAL; 6331a1e1d21SSam Leffler if (len > sizeof(ic->ic_nw_keys[i].wk_key)) 6341a1e1d21SSam Leffler return EINVAL; 6351a1e1d21SSam Leffler } 6361a1e1d21SSam Leffler memset(ic->ic_nw_keys, 0, sizeof(ic->ic_nw_keys)); 6371a1e1d21SSam Leffler for (i = 0; i < IEEE80211_WEP_NKID; i++) { 6381a1e1d21SSam Leffler len = le16toh(keys->wi_keys[i].wi_keylen); 6391a1e1d21SSam Leffler ic->ic_nw_keys[i].wk_len = len; 6401a1e1d21SSam Leffler memcpy(ic->ic_nw_keys[i].wk_key, 6411a1e1d21SSam Leffler keys->wi_keys[i].wi_keydat, len); 6421a1e1d21SSam Leffler } 6431a1e1d21SSam Leffler error = ENETRESET; 6441a1e1d21SSam Leffler break; 6451a1e1d21SSam Leffler case WI_RID_MAX_DATALEN: 6461a1e1d21SSam Leffler if (len != 2) 6471a1e1d21SSam Leffler return EINVAL; 6481a1e1d21SSam Leffler len = le16toh(wreq.wi_val[0]); 6491a1e1d21SSam Leffler if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN) 6501a1e1d21SSam Leffler return EINVAL; 6511a1e1d21SSam Leffler if (len != IEEE80211_MAX_LEN) 6521a1e1d21SSam Leffler return EINVAL; /* TODO: fragment */ 6531a1e1d21SSam Leffler ic->ic_fragthreshold = len; 6541a1e1d21SSam Leffler error = ENETRESET; 6551a1e1d21SSam Leffler break; 6561a1e1d21SSam Leffler case WI_RID_IFACE_STATS: 6571a1e1d21SSam Leffler error = EPERM; 6581a1e1d21SSam Leffler break; 6591a1e1d21SSam Leffler case WI_RID_SCAN_REQ: /* XXX wicontrol */ 6601a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) 6611a1e1d21SSam Leffler break; 6621a1e1d21SSam Leffler /* NB: ignore channel list and tx rate parameters */ 663a11c9a5cSSam Leffler error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 6641a1e1d21SSam Leffler break; 6651a1e1d21SSam Leffler case WI_RID_SCAN_APS: 6661a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) 6671a1e1d21SSam Leffler break; 6681a1e1d21SSam Leffler len--; /* XXX: tx rate? */ 6691a1e1d21SSam Leffler /* FALLTHRU */ 6701a1e1d21SSam Leffler case WI_RID_CHANNEL_LIST: 6711a1e1d21SSam Leffler memset(chanlist, 0, sizeof(chanlist)); 6721a1e1d21SSam Leffler /* 6731a1e1d21SSam Leffler * Since channel 0 is not available for DS, channel 1 6741a1e1d21SSam Leffler * is assigned to LSB on WaveLAN. 6751a1e1d21SSam Leffler */ 6761a1e1d21SSam Leffler if (ic->ic_phytype == IEEE80211_T_DS) 6771a1e1d21SSam Leffler i = 1; 6781a1e1d21SSam Leffler else 6791a1e1d21SSam Leffler i = 0; 6801a1e1d21SSam Leffler for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { 6811a1e1d21SSam Leffler if ((j / 8) >= len) 6821a1e1d21SSam Leffler break; 6831a1e1d21SSam Leffler if (isclr((u_int8_t *)wreq.wi_val, j)) 6841a1e1d21SSam Leffler continue; 6851a1e1d21SSam Leffler if (isclr(ic->ic_chan_active, i)) { 6861a1e1d21SSam Leffler if (wreq.wi_type != WI_RID_CHANNEL_LIST) 6871a1e1d21SSam Leffler continue; 6881a1e1d21SSam Leffler if (isclr(ic->ic_chan_avail, i)) 6891a1e1d21SSam Leffler return EPERM; 6901a1e1d21SSam Leffler } 6911a1e1d21SSam Leffler setbit(chanlist, i); 6921a1e1d21SSam Leffler } 6931a1e1d21SSam Leffler memcpy(ic->ic_chan_active, chanlist, 6941a1e1d21SSam Leffler sizeof(ic->ic_chan_active)); 6951a1e1d21SSam Leffler if (isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { 6961a1e1d21SSam Leffler for (i = 0; i <= IEEE80211_CHAN_MAX; i++) 6971a1e1d21SSam Leffler if (isset(chanlist, i)) { 6981a1e1d21SSam Leffler ic->ic_ibss_chan = &ic->ic_channels[i]; 6991a1e1d21SSam Leffler break; 7001a1e1d21SSam Leffler } 7011a1e1d21SSam Leffler } 7021a1e1d21SSam Leffler if (isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan))) 7031a1e1d21SSam Leffler ic->ic_bss->ni_chan = ic->ic_ibss_chan; 7041a1e1d21SSam Leffler if (wreq.wi_type == WI_RID_CHANNEL_LIST) 7051a1e1d21SSam Leffler error = ENETRESET; 7061a1e1d21SSam Leffler else 707a11c9a5cSSam Leffler error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 7081a1e1d21SSam Leffler break; 7091a1e1d21SSam Leffler default: 7101a1e1d21SSam Leffler error = EINVAL; 7111a1e1d21SSam Leffler break; 7121a1e1d21SSam Leffler } 7131a1e1d21SSam Leffler return error; 7141a1e1d21SSam Leffler } 7151a1e1d21SSam Leffler 7161a1e1d21SSam Leffler int 7171a1e1d21SSam Leffler ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 7181a1e1d21SSam Leffler { 7191a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 7201a1e1d21SSam Leffler int error = 0; 7211a1e1d21SSam Leffler u_int kid, len; 7221a1e1d21SSam Leffler struct ieee80211req *ireq; 7231a1e1d21SSam Leffler u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE]; 7241a1e1d21SSam Leffler char tmpssid[IEEE80211_NWID_LEN]; 7251a1e1d21SSam Leffler struct ieee80211_channel *chan; 7261a1e1d21SSam Leffler 7271a1e1d21SSam Leffler switch (cmd) { 7281a1e1d21SSam Leffler case SIOCSIFMEDIA: 7291a1e1d21SSam Leffler case SIOCGIFMEDIA: 7301a1e1d21SSam Leffler error = ifmedia_ioctl(ifp, (struct ifreq *) data, 7311a1e1d21SSam Leffler &ic->ic_media, cmd); 7321a1e1d21SSam Leffler break; 7331a1e1d21SSam Leffler case SIOCG80211: 7341a1e1d21SSam Leffler ireq = (struct ieee80211req *) data; 7351a1e1d21SSam Leffler switch (ireq->i_type) { 7361a1e1d21SSam Leffler case IEEE80211_IOC_SSID: 7371a1e1d21SSam Leffler switch (ic->ic_state) { 7381a1e1d21SSam Leffler case IEEE80211_S_INIT: 7391a1e1d21SSam Leffler case IEEE80211_S_SCAN: 7401a1e1d21SSam Leffler ireq->i_len = ic->ic_des_esslen; 7411a1e1d21SSam Leffler memcpy(tmpssid, ic->ic_des_essid, ireq->i_len); 7421a1e1d21SSam Leffler break; 7431a1e1d21SSam Leffler default: 7441a1e1d21SSam Leffler ireq->i_len = ic->ic_bss->ni_esslen; 7451a1e1d21SSam Leffler memcpy(tmpssid, ic->ic_bss->ni_essid, 7461a1e1d21SSam Leffler ireq->i_len); 7471a1e1d21SSam Leffler break; 7481a1e1d21SSam Leffler } 7491a1e1d21SSam Leffler error = copyout(tmpssid, ireq->i_data, ireq->i_len); 7501a1e1d21SSam Leffler break; 7511a1e1d21SSam Leffler case IEEE80211_IOC_NUMSSIDS: 7521a1e1d21SSam Leffler ireq->i_val = 1; 7531a1e1d21SSam Leffler break; 7541a1e1d21SSam Leffler case IEEE80211_IOC_WEP: 7551a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_WEP) == 0) { 7561a1e1d21SSam Leffler ireq->i_val = IEEE80211_WEP_NOSUP; 7571a1e1d21SSam Leffler } else { 7581a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_WEPON) { 7591a1e1d21SSam Leffler ireq->i_val = 7601a1e1d21SSam Leffler IEEE80211_WEP_MIXED; 7611a1e1d21SSam Leffler } else { 7621a1e1d21SSam Leffler ireq->i_val = 7631a1e1d21SSam Leffler IEEE80211_WEP_OFF; 7641a1e1d21SSam Leffler } 7651a1e1d21SSam Leffler } 7661a1e1d21SSam Leffler break; 7671a1e1d21SSam Leffler case IEEE80211_IOC_WEPKEY: 7681a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_WEP) == 0) { 7691a1e1d21SSam Leffler error = EINVAL; 7701a1e1d21SSam Leffler break; 7711a1e1d21SSam Leffler } 7721a1e1d21SSam Leffler kid = (u_int) ireq->i_val; 7731a1e1d21SSam Leffler if (kid >= IEEE80211_WEP_NKID) { 7741a1e1d21SSam Leffler error = EINVAL; 7751a1e1d21SSam Leffler break; 7761a1e1d21SSam Leffler } 7771a1e1d21SSam Leffler len = (u_int) ic->ic_nw_keys[kid].wk_len; 7781a1e1d21SSam Leffler /* NB: only root can read WEP keys */ 7791a1e1d21SSam Leffler if (suser(curthread)) { 7801a1e1d21SSam Leffler bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len); 7811a1e1d21SSam Leffler } else { 7821a1e1d21SSam Leffler bzero(tmpkey, len); 7831a1e1d21SSam Leffler } 7841a1e1d21SSam Leffler ireq->i_len = len; 7851a1e1d21SSam Leffler error = copyout(tmpkey, ireq->i_data, len); 7861a1e1d21SSam Leffler break; 7871a1e1d21SSam Leffler case IEEE80211_IOC_NUMWEPKEYS: 7881a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_WEP) == 0) 7891a1e1d21SSam Leffler error = EINVAL; 7901a1e1d21SSam Leffler else 7911a1e1d21SSam Leffler ireq->i_val = IEEE80211_WEP_NKID; 7921a1e1d21SSam Leffler break; 7931a1e1d21SSam Leffler case IEEE80211_IOC_WEPTXKEY: 7941a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_WEP) == 0) 7951a1e1d21SSam Leffler error = EINVAL; 7961a1e1d21SSam Leffler else 7971a1e1d21SSam Leffler ireq->i_val = ic->ic_wep_txkey; 7981a1e1d21SSam Leffler break; 7991a1e1d21SSam Leffler case IEEE80211_IOC_AUTHMODE: 8001a1e1d21SSam Leffler ireq->i_val = IEEE80211_AUTH_OPEN; 8011a1e1d21SSam Leffler break; 8021a1e1d21SSam Leffler case IEEE80211_IOC_CHANNEL: 8031a1e1d21SSam Leffler switch (ic->ic_state) { 8041a1e1d21SSam Leffler case IEEE80211_S_INIT: 8051a1e1d21SSam Leffler case IEEE80211_S_SCAN: 8061a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) 8071a1e1d21SSam Leffler chan = ic->ic_des_chan; 8081a1e1d21SSam Leffler else 8091a1e1d21SSam Leffler chan = ic->ic_ibss_chan; 8101a1e1d21SSam Leffler break; 8111a1e1d21SSam Leffler default: 8121a1e1d21SSam Leffler chan = ic->ic_bss->ni_chan; 8131a1e1d21SSam Leffler break; 8141a1e1d21SSam Leffler } 8151a1e1d21SSam Leffler ireq->i_val = ieee80211_chan2ieee(ic, chan); 8161a1e1d21SSam Leffler break; 8171a1e1d21SSam Leffler case IEEE80211_IOC_POWERSAVE: 8181a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_PMGTON) 8191a1e1d21SSam Leffler ireq->i_val = IEEE80211_POWERSAVE_ON; 8201a1e1d21SSam Leffler else 8211a1e1d21SSam Leffler ireq->i_val = IEEE80211_POWERSAVE_OFF; 8221a1e1d21SSam Leffler break; 8231a1e1d21SSam Leffler case IEEE80211_IOC_POWERSAVESLEEP: 8241a1e1d21SSam Leffler ireq->i_val = ic->ic_lintval; 8251a1e1d21SSam Leffler break; 82613604e6bSSam Leffler case IEEE80211_IOC_RTSTHRESHOLD: 8271a1e1d21SSam Leffler ireq->i_val = ic->ic_rtsthreshold; 8281a1e1d21SSam Leffler break; 8291a1e1d21SSam Leffler default: 8301a1e1d21SSam Leffler error = EINVAL; 8311a1e1d21SSam Leffler } 8321a1e1d21SSam Leffler break; 8331a1e1d21SSam Leffler case SIOCS80211: 8341a1e1d21SSam Leffler error = suser(curthread); 8351a1e1d21SSam Leffler if (error) 8361a1e1d21SSam Leffler break; 8371a1e1d21SSam Leffler ireq = (struct ieee80211req *) data; 8381a1e1d21SSam Leffler switch (ireq->i_type) { 8391a1e1d21SSam Leffler case IEEE80211_IOC_SSID: 8401a1e1d21SSam Leffler if (ireq->i_val != 0 || 8411a1e1d21SSam Leffler ireq->i_len > IEEE80211_NWID_LEN) { 8421a1e1d21SSam Leffler error = EINVAL; 8431a1e1d21SSam Leffler break; 8441a1e1d21SSam Leffler } 8451a1e1d21SSam Leffler error = copyin(ireq->i_data, tmpssid, ireq->i_len); 8461a1e1d21SSam Leffler if (error) 8471a1e1d21SSam Leffler break; 8481a1e1d21SSam Leffler memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); 8491a1e1d21SSam Leffler ic->ic_des_esslen = ireq->i_len; 8501a1e1d21SSam Leffler memcpy(ic->ic_des_essid, tmpssid, ireq->i_len); 8511a1e1d21SSam Leffler error = ENETRESET; 8521a1e1d21SSam Leffler break; 8531a1e1d21SSam Leffler case IEEE80211_IOC_WEP: 8541a1e1d21SSam Leffler /* 8551a1e1d21SSam Leffler * These cards only support one mode so 8561a1e1d21SSam Leffler * we just turn wep on if what ever is 8571a1e1d21SSam Leffler * passed in is not OFF. 8581a1e1d21SSam Leffler */ 8591a1e1d21SSam Leffler if (ireq->i_val == IEEE80211_WEP_OFF) { 8601a1e1d21SSam Leffler ic->ic_flags &= ~IEEE80211_F_WEPON; 8611a1e1d21SSam Leffler } else { 8621a1e1d21SSam Leffler ic->ic_flags |= IEEE80211_F_WEPON; 8631a1e1d21SSam Leffler } 8641a1e1d21SSam Leffler error = ENETRESET; 8651a1e1d21SSam Leffler break; 8661a1e1d21SSam Leffler case IEEE80211_IOC_WEPKEY: 8671a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_WEP) == 0) { 8681a1e1d21SSam Leffler error = EINVAL; 8691a1e1d21SSam Leffler break; 8701a1e1d21SSam Leffler } 8711a1e1d21SSam Leffler kid = (u_int) ireq->i_val; 8721a1e1d21SSam Leffler if (kid >= IEEE80211_WEP_NKID) { 8731a1e1d21SSam Leffler error = EINVAL; 8741a1e1d21SSam Leffler break; 8751a1e1d21SSam Leffler } 8761a1e1d21SSam Leffler if (ireq->i_len > sizeof(tmpkey)) { 8771a1e1d21SSam Leffler error = EINVAL; 8781a1e1d21SSam Leffler break; 8791a1e1d21SSam Leffler } 8801a1e1d21SSam Leffler memset(tmpkey, 0, sizeof(tmpkey)); 8811a1e1d21SSam Leffler error = copyin(ireq->i_data, tmpkey, ireq->i_len); 8821a1e1d21SSam Leffler if (error) 8831a1e1d21SSam Leffler break; 8841a1e1d21SSam Leffler memcpy(ic->ic_nw_keys[kid].wk_key, tmpkey, 8851a1e1d21SSam Leffler sizeof(tmpkey)); 8861a1e1d21SSam Leffler ic->ic_nw_keys[kid].wk_len = ireq->i_len; 8871a1e1d21SSam Leffler error = ENETRESET; 8881a1e1d21SSam Leffler break; 8891a1e1d21SSam Leffler case IEEE80211_IOC_WEPTXKEY: 8901a1e1d21SSam Leffler kid = (u_int) ireq->i_val; 8911a1e1d21SSam Leffler if (kid >= IEEE80211_WEP_NKID) { 8921a1e1d21SSam Leffler error = EINVAL; 8931a1e1d21SSam Leffler break; 8941a1e1d21SSam Leffler } 8951a1e1d21SSam Leffler ic->ic_wep_txkey = kid; 8961a1e1d21SSam Leffler error = ENETRESET; 8971a1e1d21SSam Leffler break; 8981a1e1d21SSam Leffler #if 0 8991a1e1d21SSam Leffler case IEEE80211_IOC_AUTHMODE: 9001a1e1d21SSam Leffler sc->wi_authmode = ireq->i_val; 9011a1e1d21SSam Leffler break; 9021a1e1d21SSam Leffler #endif 9031a1e1d21SSam Leffler case IEEE80211_IOC_CHANNEL: 9041a1e1d21SSam Leffler /* XXX 0xffff overflows 16-bit signed */ 9051a1e1d21SSam Leffler if (ireq->i_val == 0 || 9061a1e1d21SSam Leffler ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) 9071a1e1d21SSam Leffler ic->ic_des_chan = IEEE80211_CHAN_ANYC; 9081a1e1d21SSam Leffler else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX || 9091a1e1d21SSam Leffler isclr(ic->ic_chan_active, ireq->i_val)) { 9101a1e1d21SSam Leffler error = EINVAL; 9111a1e1d21SSam Leffler break; 9121a1e1d21SSam Leffler } else 9131a1e1d21SSam Leffler ic->ic_ibss_chan = ic->ic_des_chan = 9141a1e1d21SSam Leffler &ic->ic_channels[ireq->i_val]; 9151a1e1d21SSam Leffler switch (ic->ic_state) { 9161a1e1d21SSam Leffler case IEEE80211_S_INIT: 9171a1e1d21SSam Leffler case IEEE80211_S_SCAN: 9181a1e1d21SSam Leffler error = ENETRESET; 9191a1e1d21SSam Leffler break; 9201a1e1d21SSam Leffler default: 9211a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) { 9221a1e1d21SSam Leffler if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && 9231a1e1d21SSam Leffler ic->ic_bss->ni_chan != ic->ic_des_chan) 9241a1e1d21SSam Leffler error = ENETRESET; 9251a1e1d21SSam Leffler } else { 9261a1e1d21SSam Leffler if (ic->ic_bss->ni_chan != ic->ic_ibss_chan) 9271a1e1d21SSam Leffler error = ENETRESET; 9281a1e1d21SSam Leffler } 9291a1e1d21SSam Leffler break; 9301a1e1d21SSam Leffler } 9311a1e1d21SSam Leffler break; 9321a1e1d21SSam Leffler case IEEE80211_IOC_POWERSAVE: 9331a1e1d21SSam Leffler switch (ireq->i_val) { 9341a1e1d21SSam Leffler case IEEE80211_POWERSAVE_OFF: 9351a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_PMGTON) { 9361a1e1d21SSam Leffler ic->ic_flags &= ~IEEE80211_F_PMGTON; 9371a1e1d21SSam Leffler error = ENETRESET; 9381a1e1d21SSam Leffler } 9391a1e1d21SSam Leffler break; 9401a1e1d21SSam Leffler case IEEE80211_POWERSAVE_ON: 9411a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) 9421a1e1d21SSam Leffler error = EINVAL; 9431a1e1d21SSam Leffler else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { 9441a1e1d21SSam Leffler ic->ic_flags |= IEEE80211_F_PMGTON; 9451a1e1d21SSam Leffler error = ENETRESET; 9461a1e1d21SSam Leffler } 9471a1e1d21SSam Leffler break; 9481a1e1d21SSam Leffler default: 9491a1e1d21SSam Leffler error = EINVAL; 9501a1e1d21SSam Leffler break; 9511a1e1d21SSam Leffler } 9521a1e1d21SSam Leffler break; 9531a1e1d21SSam Leffler case IEEE80211_IOC_POWERSAVESLEEP: 9541a1e1d21SSam Leffler if (ireq->i_val < 0) { 9551a1e1d21SSam Leffler error = EINVAL; 9561a1e1d21SSam Leffler break; 9571a1e1d21SSam Leffler } 9581a1e1d21SSam Leffler ic->ic_lintval = ireq->i_val; 9591a1e1d21SSam Leffler error = ENETRESET; 9601a1e1d21SSam Leffler break; 96113604e6bSSam Leffler case IEEE80211_IOC_RTSTHRESHOLD: 9621a1e1d21SSam Leffler if (!(IEEE80211_RTS_MIN < ireq->i_val && 9631a1e1d21SSam Leffler ireq->i_val < IEEE80211_RTS_MAX)) { 9641a1e1d21SSam Leffler error = EINVAL; 9651a1e1d21SSam Leffler break; 9661a1e1d21SSam Leffler } 9671a1e1d21SSam Leffler ic->ic_rtsthreshold = ireq->i_val; 9681a1e1d21SSam Leffler error = ENETRESET; 9691a1e1d21SSam Leffler break; 9701a1e1d21SSam Leffler default: 9711a1e1d21SSam Leffler error = EINVAL; 9721a1e1d21SSam Leffler break; 9731a1e1d21SSam Leffler } 9741a1e1d21SSam Leffler break; 9751a1e1d21SSam Leffler case SIOCGIFGENERIC: 9761a1e1d21SSam Leffler error = ieee80211_cfgget(ifp, cmd, data); 9771a1e1d21SSam Leffler break; 9781a1e1d21SSam Leffler case SIOCSIFGENERIC: 9791a1e1d21SSam Leffler error = suser(curthread); 9801a1e1d21SSam Leffler if (error) 9811a1e1d21SSam Leffler break; 9821a1e1d21SSam Leffler error = ieee80211_cfgset(ifp, cmd, data); 9831a1e1d21SSam Leffler break; 9841a1e1d21SSam Leffler default: 9851a1e1d21SSam Leffler error = ether_ioctl(ifp, cmd, data); 9861a1e1d21SSam Leffler break; 9871a1e1d21SSam Leffler } 9881a1e1d21SSam Leffler return error; 9891a1e1d21SSam Leffler } 990