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 40b2e95691SSam Leffler #include "opt_inet.h" 41b2e95691SSam Leffler #include "opt_ipx.h" 42b2e95691SSam Leffler 431a1e1d21SSam Leffler #include <sys/endian.h> 441a1e1d21SSam Leffler #include <sys/param.h> 451a1e1d21SSam Leffler #include <sys/kernel.h> 461a1e1d21SSam Leffler #include <sys/socket.h> 471a1e1d21SSam Leffler #include <sys/sockio.h> 481a1e1d21SSam Leffler #include <sys/systm.h> 491a1e1d21SSam Leffler 501a1e1d21SSam Leffler #include <net/if.h> 511a1e1d21SSam Leffler #include <net/if_arp.h> 521a1e1d21SSam Leffler #include <net/if_media.h> 531a1e1d21SSam Leffler #include <net/ethernet.h> 541a1e1d21SSam Leffler 55b2e95691SSam Leffler #ifdef INET 56b2e95691SSam Leffler #include <netinet/in.h> 57b2e95691SSam Leffler #include <netinet/if_ether.h> 58b2e95691SSam Leffler #endif 59b2e95691SSam Leffler 60b2e95691SSam Leffler #ifdef IPX 61b2e95691SSam Leffler #include <netipx/ipx.h> 62b2e95691SSam Leffler #include <netipx/ipx_if.h> 63b2e95691SSam Leffler #endif 64b2e95691SSam Leffler 651a1e1d21SSam Leffler #include <net80211/ieee80211_var.h> 661a1e1d21SSam Leffler #include <net80211/ieee80211_ioctl.h> 671a1e1d21SSam Leffler 681a1e1d21SSam Leffler #include <dev/wi/if_wavelan_ieee.h> 691a1e1d21SSam Leffler 701a1e1d21SSam Leffler /* 711a1e1d21SSam Leffler * XXX 721a1e1d21SSam Leffler * Wireless LAN specific configuration interface, which is compatible 731a1e1d21SSam Leffler * with wicontrol(8). 741a1e1d21SSam Leffler */ 751a1e1d21SSam Leffler 761a1e1d21SSam Leffler int 771a1e1d21SSam Leffler ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data) 781a1e1d21SSam Leffler { 791a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 801a1e1d21SSam Leffler int i, j, error; 811a1e1d21SSam Leffler struct ifreq *ifr = (struct ifreq *)data; 821a1e1d21SSam Leffler struct wi_req wreq; 831a1e1d21SSam Leffler struct wi_ltv_keys *keys; 841a1e1d21SSam Leffler struct wi_apinfo *ap; 851a1e1d21SSam Leffler struct ieee80211_node *ni; 861a1e1d21SSam Leffler struct ieee80211_rateset *rs; 871a1e1d21SSam Leffler struct wi_sigcache wsc; 881a1e1d21SSam Leffler struct wi_scan_p2_hdr *p2; 891a1e1d21SSam Leffler struct wi_scan_res *res; 901a1e1d21SSam Leffler 911a1e1d21SSam Leffler error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 921a1e1d21SSam Leffler if (error) 931a1e1d21SSam Leffler return error; 941a1e1d21SSam Leffler wreq.wi_len = 0; 951a1e1d21SSam Leffler switch (wreq.wi_type) { 961a1e1d21SSam Leffler case WI_RID_SERIALNO: 971a1e1d21SSam Leffler /* nothing appropriate */ 981a1e1d21SSam Leffler break; 991a1e1d21SSam Leffler case WI_RID_NODENAME: 1001a1e1d21SSam Leffler strcpy((char *)&wreq.wi_val[1], hostname); 1011a1e1d21SSam Leffler wreq.wi_val[0] = htole16(strlen(hostname)); 1021a1e1d21SSam Leffler wreq.wi_len = (1 + strlen(hostname) + 1) / 2; 1031a1e1d21SSam Leffler break; 1041a1e1d21SSam Leffler case WI_RID_CURRENT_SSID: 1051a1e1d21SSam Leffler if (ic->ic_state != IEEE80211_S_RUN) { 1061a1e1d21SSam Leffler wreq.wi_val[0] = 0; 1071a1e1d21SSam Leffler wreq.wi_len = 1; 1081a1e1d21SSam Leffler break; 1091a1e1d21SSam Leffler } 1101a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_bss->ni_esslen); 1111a1e1d21SSam Leffler memcpy(&wreq.wi_val[1], ic->ic_bss->ni_essid, 1121a1e1d21SSam Leffler ic->ic_bss->ni_esslen); 1131a1e1d21SSam Leffler wreq.wi_len = (1 + ic->ic_bss->ni_esslen + 1) / 2; 1141a1e1d21SSam Leffler break; 1151a1e1d21SSam Leffler case WI_RID_OWN_SSID: 1161a1e1d21SSam Leffler case WI_RID_DESIRED_SSID: 1171a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_des_esslen); 1181a1e1d21SSam Leffler memcpy(&wreq.wi_val[1], ic->ic_des_essid, ic->ic_des_esslen); 1191a1e1d21SSam Leffler wreq.wi_len = (1 + ic->ic_des_esslen + 1) / 2; 1201a1e1d21SSam Leffler break; 1211a1e1d21SSam Leffler case WI_RID_CURRENT_BSSID: 1221a1e1d21SSam Leffler if (ic->ic_state == IEEE80211_S_RUN) 1231a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_bss->ni_bssid); 1241a1e1d21SSam Leffler else 1251a1e1d21SSam Leffler memset(wreq.wi_val, 0, IEEE80211_ADDR_LEN); 1261a1e1d21SSam Leffler wreq.wi_len = IEEE80211_ADDR_LEN / 2; 1271a1e1d21SSam Leffler break; 1281a1e1d21SSam Leffler case WI_RID_CHANNEL_LIST: 1291a1e1d21SSam Leffler memset(wreq.wi_val, 0, sizeof(wreq.wi_val)); 1301a1e1d21SSam Leffler /* 1311a1e1d21SSam Leffler * Since channel 0 is not available for DS, channel 1 1321a1e1d21SSam Leffler * is assigned to LSB on WaveLAN. 1331a1e1d21SSam Leffler */ 1341a1e1d21SSam Leffler if (ic->ic_phytype == IEEE80211_T_DS) 1351a1e1d21SSam Leffler i = 1; 1361a1e1d21SSam Leffler else 1371a1e1d21SSam Leffler i = 0; 1381a1e1d21SSam Leffler for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) 1391a1e1d21SSam Leffler if (isset(ic->ic_chan_active, i)) { 1401a1e1d21SSam Leffler setbit((u_int8_t *)wreq.wi_val, j); 1411a1e1d21SSam Leffler wreq.wi_len = j / 16 + 1; 1421a1e1d21SSam Leffler } 1431a1e1d21SSam Leffler break; 1441a1e1d21SSam Leffler case WI_RID_OWN_CHNL: 1451a1e1d21SSam Leffler wreq.wi_val[0] = htole16( 1461a1e1d21SSam Leffler ieee80211_chan2ieee(ic, ic->ic_ibss_chan)); 1471a1e1d21SSam Leffler wreq.wi_len = 1; 1481a1e1d21SSam Leffler break; 1491a1e1d21SSam Leffler case WI_RID_CURRENT_CHAN: 1501a1e1d21SSam Leffler wreq.wi_val[0] = htole16( 1511a1e1d21SSam Leffler ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)); 1521a1e1d21SSam Leffler wreq.wi_len = 1; 1531a1e1d21SSam Leffler break; 1541a1e1d21SSam Leffler case WI_RID_COMMS_QUALITY: 1551a1e1d21SSam Leffler wreq.wi_val[0] = 0; /* quality */ 156d1e61976SSam Leffler wreq.wi_val[1] = 157d1e61976SSam Leffler htole16((*ic->ic_node_getrssi)(ic, ic->ic_bss)); 1581a1e1d21SSam Leffler wreq.wi_val[2] = 0; /* noise */ 1591a1e1d21SSam Leffler wreq.wi_len = 3; 1601a1e1d21SSam Leffler break; 1611a1e1d21SSam Leffler case WI_RID_PROMISC: 1621a1e1d21SSam Leffler wreq.wi_val[0] = htole16((ifp->if_flags & IFF_PROMISC) ? 1 : 0); 1631a1e1d21SSam Leffler wreq.wi_len = 1; 1641a1e1d21SSam Leffler break; 1651a1e1d21SSam Leffler case WI_RID_PORTTYPE: 1661a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_opmode); 1671a1e1d21SSam Leffler wreq.wi_len = 1; 1681a1e1d21SSam Leffler break; 1691a1e1d21SSam Leffler case WI_RID_MAC_NODE: 1701a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_myaddr); 1711a1e1d21SSam Leffler wreq.wi_len = IEEE80211_ADDR_LEN / 2; 1721a1e1d21SSam Leffler break; 1731a1e1d21SSam Leffler case WI_RID_TX_RATE: 1741a1e1d21SSam Leffler if (ic->ic_fixed_rate == -1) 1751a1e1d21SSam Leffler wreq.wi_val[0] = 0; /* auto */ 1761a1e1d21SSam Leffler else 1771a1e1d21SSam Leffler wreq.wi_val[0] = htole16( 1781a1e1d21SSam Leffler (ic->ic_sup_rates[ic->ic_curmode].rs_rates[ic->ic_fixed_rate] & 1791a1e1d21SSam Leffler IEEE80211_RATE_VAL) / 2); 1801a1e1d21SSam Leffler wreq.wi_len = 1; 1811a1e1d21SSam Leffler break; 1821a1e1d21SSam Leffler case WI_RID_CUR_TX_RATE: 1831a1e1d21SSam Leffler wreq.wi_val[0] = htole16( 1841a1e1d21SSam Leffler (ic->ic_bss->ni_rates.rs_rates[ic->ic_bss->ni_txrate] & 1851a1e1d21SSam Leffler IEEE80211_RATE_VAL) / 2); 1861a1e1d21SSam Leffler wreq.wi_len = 1; 1871a1e1d21SSam Leffler break; 1881a1e1d21SSam Leffler case WI_RID_RTS_THRESH: 1891a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_rtsthreshold); 1901a1e1d21SSam Leffler wreq.wi_len = 1; 1911a1e1d21SSam Leffler break; 1921a1e1d21SSam Leffler case WI_RID_CREATE_IBSS: 1931a1e1d21SSam Leffler wreq.wi_val[0] = 1941a1e1d21SSam Leffler htole16((ic->ic_flags & IEEE80211_F_IBSSON) ? 1 : 0); 1951a1e1d21SSam Leffler wreq.wi_len = 1; 1961a1e1d21SSam Leffler break; 1971a1e1d21SSam Leffler case WI_RID_MICROWAVE_OVEN: 1981a1e1d21SSam Leffler wreq.wi_val[0] = 0; /* no ... not supported */ 1991a1e1d21SSam Leffler wreq.wi_len = 1; 2001a1e1d21SSam Leffler break; 2011a1e1d21SSam Leffler case WI_RID_ROAMING_MODE: 2021a1e1d21SSam Leffler wreq.wi_val[0] = htole16(1); /* enabled ... not supported */ 2031a1e1d21SSam Leffler wreq.wi_len = 1; 2041a1e1d21SSam Leffler break; 2051a1e1d21SSam Leffler case WI_RID_SYSTEM_SCALE: 2061a1e1d21SSam Leffler wreq.wi_val[0] = htole16(1); /* low density ... not supp */ 2071a1e1d21SSam Leffler wreq.wi_len = 1; 2081a1e1d21SSam Leffler break; 2091a1e1d21SSam Leffler case WI_RID_PM_ENABLED: 2101a1e1d21SSam Leffler wreq.wi_val[0] = 2111a1e1d21SSam Leffler htole16((ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0); 2121a1e1d21SSam Leffler wreq.wi_len = 1; 2131a1e1d21SSam Leffler break; 2141a1e1d21SSam Leffler case WI_RID_MAX_SLEEP: 2151a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_lintval); 2161a1e1d21SSam Leffler wreq.wi_len = 1; 2171a1e1d21SSam Leffler break; 2181a1e1d21SSam Leffler case WI_RID_CUR_BEACON_INT: 2191a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_bss->ni_intval); 2201a1e1d21SSam Leffler wreq.wi_len = 1; 2211a1e1d21SSam Leffler break; 2221a1e1d21SSam Leffler case WI_RID_WEP_AVAIL: 2231a1e1d21SSam Leffler wreq.wi_val[0] = 2241a1e1d21SSam Leffler htole16((ic->ic_caps & IEEE80211_C_WEP) ? 1 : 0); 2251a1e1d21SSam Leffler wreq.wi_len = 1; 2261a1e1d21SSam Leffler break; 2271a1e1d21SSam Leffler case WI_RID_CNFAUTHMODE: 2281a1e1d21SSam Leffler wreq.wi_val[0] = htole16(1); /* TODO: open system only */ 2291a1e1d21SSam Leffler wreq.wi_len = 1; 2301a1e1d21SSam Leffler break; 2311a1e1d21SSam Leffler case WI_RID_ENCRYPTION: 2321a1e1d21SSam Leffler wreq.wi_val[0] = 2331a1e1d21SSam Leffler htole16((ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 0); 2341a1e1d21SSam Leffler wreq.wi_len = 1; 2351a1e1d21SSam Leffler break; 2361a1e1d21SSam Leffler case WI_RID_TX_CRYPT_KEY: 2371a1e1d21SSam Leffler wreq.wi_val[0] = htole16(ic->ic_wep_txkey); 2381a1e1d21SSam Leffler wreq.wi_len = 1; 2391a1e1d21SSam Leffler break; 2401a1e1d21SSam Leffler case WI_RID_DEFLT_CRYPT_KEYS: 2411a1e1d21SSam Leffler keys = (struct wi_ltv_keys *)&wreq; 2421a1e1d21SSam Leffler /* do not show keys to non-root user */ 2431a1e1d21SSam Leffler error = suser(curthread); 2441a1e1d21SSam Leffler if (error) { 2451a1e1d21SSam Leffler memset(keys, 0, sizeof(*keys)); 2461a1e1d21SSam Leffler error = 0; 2471a1e1d21SSam Leffler break; 2481a1e1d21SSam Leffler } 2491a1e1d21SSam Leffler for (i = 0; i < IEEE80211_WEP_NKID; i++) { 2501a1e1d21SSam Leffler keys->wi_keys[i].wi_keylen = 2511a1e1d21SSam Leffler htole16(ic->ic_nw_keys[i].wk_len); 2521a1e1d21SSam Leffler memcpy(keys->wi_keys[i].wi_keydat, 2531a1e1d21SSam Leffler ic->ic_nw_keys[i].wk_key, ic->ic_nw_keys[i].wk_len); 2541a1e1d21SSam Leffler } 2551a1e1d21SSam Leffler wreq.wi_len = sizeof(*keys) / 2; 2561a1e1d21SSam Leffler break; 2571a1e1d21SSam Leffler case WI_RID_MAX_DATALEN: 2581a1e1d21SSam Leffler wreq.wi_val[0] = htole16(IEEE80211_MAX_LEN); /* TODO: frag */ 2591a1e1d21SSam Leffler wreq.wi_len = 1; 2601a1e1d21SSam Leffler break; 2611a1e1d21SSam Leffler case WI_RID_IFACE_STATS: 2621a1e1d21SSam Leffler /* XXX: should be implemented in lower drivers */ 2631a1e1d21SSam Leffler break; 2641a1e1d21SSam Leffler case WI_RID_READ_APS: 2651a1e1d21SSam Leffler if (ic->ic_opmode != IEEE80211_M_HOSTAP) { 2661a1e1d21SSam Leffler /* 2671a1e1d21SSam Leffler * Don't return results until active scan completes. 2681a1e1d21SSam Leffler */ 2691a1e1d21SSam Leffler if (ic->ic_state == IEEE80211_S_SCAN && 2701a1e1d21SSam Leffler (ic->ic_flags & IEEE80211_F_ASCAN)) { 2711a1e1d21SSam Leffler error = EINPROGRESS; 2721a1e1d21SSam Leffler break; 2731a1e1d21SSam Leffler } 2741a1e1d21SSam Leffler } 2751a1e1d21SSam Leffler i = 0; 2761a1e1d21SSam Leffler ap = (void *)((char *)wreq.wi_val + sizeof(i)); 2771a1e1d21SSam Leffler TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { 2781a1e1d21SSam Leffler if ((caddr_t)(ap + 1) > (caddr_t)(&wreq + 1)) 2791a1e1d21SSam Leffler break; 2801a1e1d21SSam Leffler memset(ap, 0, sizeof(*ap)); 2811a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 2821a1e1d21SSam Leffler IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr); 2831a1e1d21SSam Leffler ap->namelen = ic->ic_des_esslen; 2841a1e1d21SSam Leffler if (ic->ic_des_esslen) 2851a1e1d21SSam Leffler memcpy(ap->name, ic->ic_des_essid, 2861a1e1d21SSam Leffler ic->ic_des_esslen); 2871a1e1d21SSam Leffler } else { 2881a1e1d21SSam Leffler IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid); 2891a1e1d21SSam Leffler ap->namelen = ni->ni_esslen; 2901a1e1d21SSam Leffler if (ni->ni_esslen) 2911a1e1d21SSam Leffler memcpy(ap->name, ni->ni_essid, 2921a1e1d21SSam Leffler ni->ni_esslen); 2931a1e1d21SSam Leffler } 2941a1e1d21SSam Leffler ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan); 295d1e61976SSam Leffler ap->signal = (*ic->ic_node_getrssi)(ic, ni); 2961a1e1d21SSam Leffler ap->capinfo = ni->ni_capinfo; 2971a1e1d21SSam Leffler ap->interval = ni->ni_intval; 2981a1e1d21SSam Leffler rs = &ni->ni_rates; 2991a1e1d21SSam Leffler for (j = 0; j < rs->rs_nrates; j++) { 3001a1e1d21SSam Leffler if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) { 3011a1e1d21SSam Leffler ap->rate = (rs->rs_rates[j] & 3021a1e1d21SSam Leffler IEEE80211_RATE_VAL) * 5; /* XXX */ 3031a1e1d21SSam Leffler } 3041a1e1d21SSam Leffler } 3051a1e1d21SSam Leffler i++; 3061a1e1d21SSam Leffler ap++; 3071a1e1d21SSam Leffler } 3081a1e1d21SSam Leffler memcpy(wreq.wi_val, &i, sizeof(i)); 3091a1e1d21SSam Leffler wreq.wi_len = (sizeof(int) + sizeof(*ap) * i) / 2; 3101a1e1d21SSam Leffler break; 3111a1e1d21SSam Leffler case WI_RID_PRISM2: 3121a1e1d21SSam Leffler wreq.wi_val[0] = 1; /* XXX lie so SCAN_RES can give rates */ 3131a1e1d21SSam Leffler wreq.wi_len = sizeof(u_int16_t) / 2; 3141a1e1d21SSam Leffler break; 3151a1e1d21SSam Leffler case WI_RID_SCAN_RES: /* compatibility interface */ 3161a1e1d21SSam Leffler if (ic->ic_opmode != IEEE80211_M_HOSTAP && 317e249fdbeSAtsushi Onoe ic->ic_state == IEEE80211_S_SCAN && 318e249fdbeSAtsushi Onoe (ic->ic_flags & IEEE80211_F_ASCAN)) { 3191a1e1d21SSam Leffler error = EINPROGRESS; 3201a1e1d21SSam Leffler break; 3211a1e1d21SSam Leffler } 3221a1e1d21SSam Leffler /* NB: we use the Prism2 format so we can return rate info */ 3231a1e1d21SSam Leffler p2 = (struct wi_scan_p2_hdr *)wreq.wi_val; 3241a1e1d21SSam Leffler res = (void *)&p2[1]; 3251a1e1d21SSam Leffler i = 0; 3261a1e1d21SSam Leffler TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { 3271a1e1d21SSam Leffler if ((caddr_t)(res + 1) > (caddr_t)(&wreq + 1)) 3281a1e1d21SSam Leffler break; 3291a1e1d21SSam Leffler res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan); 3301a1e1d21SSam Leffler res->wi_noise = 0; 331d1e61976SSam Leffler res->wi_signal = (*ic->ic_node_getrssi)(ic, ni); 3321a1e1d21SSam Leffler IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid); 3331a1e1d21SSam Leffler res->wi_interval = ni->ni_intval; 3341a1e1d21SSam Leffler res->wi_capinfo = ni->ni_capinfo; 3351a1e1d21SSam Leffler res->wi_ssid_len = ni->ni_esslen; 3361a1e1d21SSam Leffler memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN); 3371a1e1d21SSam Leffler /* NB: assumes wi_srates holds <= ni->ni_rates */ 3381a1e1d21SSam Leffler memcpy(res->wi_srates, ni->ni_rates.rs_rates, 3391a1e1d21SSam Leffler sizeof(res->wi_srates)); 3401a1e1d21SSam Leffler if (ni->ni_rates.rs_nrates < 10) 3411a1e1d21SSam Leffler res->wi_srates[ni->ni_rates.rs_nrates] = 0; 3421a1e1d21SSam Leffler res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate]; 3431a1e1d21SSam Leffler res->wi_rsvd = 0; 3441a1e1d21SSam Leffler res++, i++; 3451a1e1d21SSam Leffler } 3461a1e1d21SSam Leffler p2->wi_rsvd = 0; 3471a1e1d21SSam Leffler p2->wi_reason = i; 3481a1e1d21SSam Leffler wreq.wi_len = (sizeof(*p2) + sizeof(*res) * i) / 2; 3491a1e1d21SSam Leffler break; 3501a1e1d21SSam Leffler case WI_RID_READ_CACHE: 3511a1e1d21SSam Leffler i = 0; 3521a1e1d21SSam Leffler TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { 3531a1e1d21SSam Leffler if (i == (WI_MAX_DATALEN/sizeof(struct wi_sigcache))-1) 3541a1e1d21SSam Leffler break; 3551a1e1d21SSam Leffler IEEE80211_ADDR_COPY(wsc.macsrc, ni->ni_macaddr); 3561a1e1d21SSam Leffler memset(&wsc.ipsrc, 0, sizeof(wsc.ipsrc)); 357d1e61976SSam Leffler wsc.signal = (*ic->ic_node_getrssi)(ic, ni); 3581a1e1d21SSam Leffler wsc.noise = 0; 3591a1e1d21SSam Leffler wsc.quality = 0; 3601a1e1d21SSam Leffler memcpy((caddr_t)wreq.wi_val + sizeof(wsc) * i, 3611a1e1d21SSam Leffler &wsc, sizeof(wsc)); 3621a1e1d21SSam Leffler i++; 3631a1e1d21SSam Leffler } 3641a1e1d21SSam Leffler wreq.wi_len = sizeof(wsc) * i / 2; 3651a1e1d21SSam Leffler break; 3661a1e1d21SSam Leffler case WI_RID_SCAN_APS: 3671a1e1d21SSam Leffler error = EINVAL; 3681a1e1d21SSam Leffler break; 3691a1e1d21SSam Leffler default: 3701a1e1d21SSam Leffler error = EINVAL; 3711a1e1d21SSam Leffler break; 3721a1e1d21SSam Leffler } 3731a1e1d21SSam Leffler if (error == 0) { 3741a1e1d21SSam Leffler wreq.wi_len++; 3751a1e1d21SSam Leffler error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); 3761a1e1d21SSam Leffler } 3771a1e1d21SSam Leffler return error; 3781a1e1d21SSam Leffler } 3791a1e1d21SSam Leffler 3801a1e1d21SSam Leffler static int 3811a1e1d21SSam Leffler findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) 3821a1e1d21SSam Leffler { 3831a1e1d21SSam Leffler #define IEEERATE(_ic,_m,_i) \ 3841a1e1d21SSam Leffler ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) 3851a1e1d21SSam Leffler int i, nrates = ic->ic_sup_rates[mode].rs_nrates; 3861a1e1d21SSam Leffler for (i = 0; i < nrates; i++) 3871a1e1d21SSam Leffler if (IEEERATE(ic, mode, i) == rate) 3881a1e1d21SSam Leffler return i; 3891a1e1d21SSam Leffler return -1; 3901a1e1d21SSam Leffler #undef IEEERATE 3911a1e1d21SSam Leffler } 3921a1e1d21SSam Leffler 39393685685SSam Leffler /* 39493685685SSam Leffler * Prepare to do a user-initiated scan for AP's. If no 39593685685SSam Leffler * current/default channel is setup or the current channel 39693685685SSam Leffler * is invalid then pick the first available channel from 39793685685SSam Leffler * the active list as the place to start the scan. 39893685685SSam Leffler */ 39993685685SSam Leffler static int 40093685685SSam Leffler ieee80211_setupscan(struct ieee80211com *ic) 40193685685SSam Leffler { 40293685685SSam Leffler u_char *chanlist = ic->ic_chan_active; 40393685685SSam Leffler int i; 40493685685SSam Leffler 40593685685SSam Leffler if (ic->ic_ibss_chan == NULL || 40693685685SSam Leffler isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { 40793685685SSam Leffler for (i = 0; i <= IEEE80211_CHAN_MAX; i++) 40893685685SSam Leffler if (isset(chanlist, i)) { 40993685685SSam Leffler ic->ic_ibss_chan = &ic->ic_channels[i]; 41093685685SSam Leffler goto found; 41193685685SSam Leffler } 41293685685SSam Leffler return EINVAL; /* no active channels */ 41393685685SSam Leffler found: 41493685685SSam Leffler ; 41593685685SSam Leffler } 41693685685SSam Leffler if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC || 41793685685SSam Leffler isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan))) 41893685685SSam Leffler ic->ic_bss->ni_chan = ic->ic_ibss_chan; 41993685685SSam Leffler /* 42093685685SSam Leffler * XXX don't permit a scan to be started unless we 42193685685SSam Leffler * know the device is ready. For the moment this means 42293685685SSam Leffler * the device is marked up as this is the required to 42393685685SSam Leffler * initialize the hardware. It would be better to permit 42493685685SSam Leffler * scanning prior to being up but that'll require some 42593685685SSam Leffler * changes to the infrastructure. 42693685685SSam Leffler */ 42793685685SSam Leffler return (ic->ic_if.if_flags & IFF_UP) ? 0 : ENETRESET; 42893685685SSam Leffler } 42993685685SSam Leffler 4301a1e1d21SSam Leffler int 4311a1e1d21SSam Leffler ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data) 4321a1e1d21SSam Leffler { 4331a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 4341a1e1d21SSam Leffler int i, j, len, error, rate; 4351a1e1d21SSam Leffler struct ifreq *ifr = (struct ifreq *)data; 4361a1e1d21SSam Leffler struct wi_ltv_keys *keys; 4371a1e1d21SSam Leffler struct wi_req wreq; 4381a1e1d21SSam Leffler u_char chanlist[roundup(IEEE80211_CHAN_MAX, NBBY)]; 4391a1e1d21SSam Leffler 4401a1e1d21SSam Leffler error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 4411a1e1d21SSam Leffler if (error) 4421a1e1d21SSam Leffler return error; 4431a1e1d21SSam Leffler len = wreq.wi_len ? (wreq.wi_len - 1) * 2 : 0; 4441a1e1d21SSam Leffler switch (wreq.wi_type) { 4451a1e1d21SSam Leffler case WI_RID_SERIALNO: 4461a1e1d21SSam Leffler case WI_RID_NODENAME: 4471a1e1d21SSam Leffler return EPERM; 4481a1e1d21SSam Leffler case WI_RID_CURRENT_SSID: 4491a1e1d21SSam Leffler return EPERM; 4501a1e1d21SSam Leffler case WI_RID_OWN_SSID: 4511a1e1d21SSam Leffler case WI_RID_DESIRED_SSID: 4521a1e1d21SSam Leffler if (le16toh(wreq.wi_val[0]) * 2 > len || 4531a1e1d21SSam Leffler le16toh(wreq.wi_val[0]) > IEEE80211_NWID_LEN) { 4541a1e1d21SSam Leffler error = ENOSPC; 4551a1e1d21SSam Leffler break; 4561a1e1d21SSam Leffler } 4571a1e1d21SSam Leffler memset(ic->ic_des_essid, 0, sizeof(ic->ic_des_essid)); 4581a1e1d21SSam Leffler ic->ic_des_esslen = le16toh(wreq.wi_val[0]) * 2; 459b9ee58c4SSam Leffler memcpy(ic->ic_des_essid, &wreq.wi_val[1], ic->ic_des_esslen); 4601a1e1d21SSam Leffler error = ENETRESET; 4611a1e1d21SSam Leffler break; 4621a1e1d21SSam Leffler case WI_RID_CURRENT_BSSID: 4631a1e1d21SSam Leffler return EPERM; 4641a1e1d21SSam Leffler case WI_RID_OWN_CHNL: 4651a1e1d21SSam Leffler if (len != 2) 4661a1e1d21SSam Leffler return EINVAL; 4671a1e1d21SSam Leffler i = le16toh(wreq.wi_val[0]); 4681a1e1d21SSam Leffler if (i < 0 || 4691a1e1d21SSam Leffler i > IEEE80211_CHAN_MAX || 4701a1e1d21SSam Leffler isclr(ic->ic_chan_active, i)) 4711a1e1d21SSam Leffler return EINVAL; 4721a1e1d21SSam Leffler ic->ic_ibss_chan = &ic->ic_channels[i]; 4731a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_SIBSS) 4741a1e1d21SSam Leffler error = ENETRESET; 4751a1e1d21SSam Leffler break; 4761a1e1d21SSam Leffler case WI_RID_CURRENT_CHAN: 4771a1e1d21SSam Leffler return EPERM; 4781a1e1d21SSam Leffler case WI_RID_COMMS_QUALITY: 4791a1e1d21SSam Leffler return EPERM; 4801a1e1d21SSam Leffler case WI_RID_PROMISC: 4811a1e1d21SSam Leffler if (len != 2) 4821a1e1d21SSam Leffler return EINVAL; 4831a1e1d21SSam Leffler if (ifp->if_flags & IFF_PROMISC) { 4841a1e1d21SSam Leffler if (wreq.wi_val[0] == 0) { 4851a1e1d21SSam Leffler ifp->if_flags &= ~IFF_PROMISC; 4861a1e1d21SSam Leffler error = ENETRESET; 4871a1e1d21SSam Leffler } 4881a1e1d21SSam Leffler } else { 4891a1e1d21SSam Leffler if (wreq.wi_val[0] != 0) { 4901a1e1d21SSam Leffler ifp->if_flags |= IFF_PROMISC; 4911a1e1d21SSam Leffler error = ENETRESET; 4921a1e1d21SSam Leffler } 4931a1e1d21SSam Leffler } 4941a1e1d21SSam Leffler break; 4951a1e1d21SSam Leffler case WI_RID_PORTTYPE: 4961a1e1d21SSam Leffler if (len != 2) 4971a1e1d21SSam Leffler return EINVAL; 4981a1e1d21SSam Leffler switch (le16toh(wreq.wi_val[0])) { 4991a1e1d21SSam Leffler case IEEE80211_M_STA: 5001a1e1d21SSam Leffler break; 5011a1e1d21SSam Leffler case IEEE80211_M_IBSS: 5021a1e1d21SSam Leffler if (!(ic->ic_caps & IEEE80211_C_IBSS)) 5031a1e1d21SSam Leffler return EINVAL; 5041a1e1d21SSam Leffler break; 5051a1e1d21SSam Leffler case IEEE80211_M_AHDEMO: 5061a1e1d21SSam Leffler if (ic->ic_phytype != IEEE80211_T_DS || 5071a1e1d21SSam Leffler !(ic->ic_caps & IEEE80211_C_AHDEMO)) 5081a1e1d21SSam Leffler return EINVAL; 5091a1e1d21SSam Leffler break; 5101a1e1d21SSam Leffler case IEEE80211_M_HOSTAP: 5111a1e1d21SSam Leffler if (!(ic->ic_caps & IEEE80211_C_HOSTAP)) 5121a1e1d21SSam Leffler return EINVAL; 5131a1e1d21SSam Leffler break; 5141a1e1d21SSam Leffler default: 5151a1e1d21SSam Leffler return EINVAL; 5161a1e1d21SSam Leffler } 5171a1e1d21SSam Leffler if (le16toh(wreq.wi_val[0]) != ic->ic_opmode) { 5181a1e1d21SSam Leffler ic->ic_opmode = le16toh(wreq.wi_val[0]); 5191a1e1d21SSam Leffler error = ENETRESET; 5201a1e1d21SSam Leffler } 5211a1e1d21SSam Leffler break; 5221a1e1d21SSam Leffler #if 0 5231a1e1d21SSam Leffler case WI_RID_MAC_NODE: 5241a1e1d21SSam Leffler if (len != IEEE80211_ADDR_LEN) 5251a1e1d21SSam Leffler return EINVAL; 5261a1e1d21SSam Leffler IEEE80211_ADDR_COPY(LLADDR(ifp->if_sadl), wreq.wi_val); 5271a1e1d21SSam Leffler /* if_init will copy lladdr into ic_myaddr */ 5281a1e1d21SSam Leffler error = ENETRESET; 5291a1e1d21SSam Leffler break; 5301a1e1d21SSam Leffler #endif 5311a1e1d21SSam Leffler case WI_RID_TX_RATE: 5321a1e1d21SSam Leffler if (len != 2) 5331a1e1d21SSam Leffler return EINVAL; 5341a1e1d21SSam Leffler if (wreq.wi_val[0] == 0) { 5351a1e1d21SSam Leffler /* auto */ 5361a1e1d21SSam Leffler ic->ic_fixed_rate = -1; 5371a1e1d21SSam Leffler break; 5381a1e1d21SSam Leffler } 5391a1e1d21SSam Leffler rate = 2 * le16toh(wreq.wi_val[0]); 5401a1e1d21SSam Leffler if (ic->ic_curmode == IEEE80211_MODE_AUTO) { 5411a1e1d21SSam Leffler /* 5421a1e1d21SSam Leffler * In autoselect mode search for the rate. We take 5431a1e1d21SSam Leffler * the first instance which may not be right, but we 5441a1e1d21SSam Leffler * are limited by the interface. Note that we also 5451a1e1d21SSam Leffler * lock the mode to insure the rate is meaningful 5461a1e1d21SSam Leffler * when it is used. 5471a1e1d21SSam Leffler */ 5481a1e1d21SSam Leffler for (j = IEEE80211_MODE_11A; 5491a1e1d21SSam Leffler j < IEEE80211_MODE_MAX; j++) { 5501a1e1d21SSam Leffler if ((ic->ic_modecaps & (1<<j)) == 0) 5511a1e1d21SSam Leffler continue; 5521a1e1d21SSam Leffler i = findrate(ic, j, rate); 5531a1e1d21SSam Leffler if (i != -1) { 5541a1e1d21SSam Leffler /* lock mode too */ 5551a1e1d21SSam Leffler ic->ic_curmode = j; 5561a1e1d21SSam Leffler goto setrate; 5571a1e1d21SSam Leffler } 5581a1e1d21SSam Leffler } 5591a1e1d21SSam Leffler } else { 5601a1e1d21SSam Leffler i = findrate(ic, ic->ic_curmode, rate); 5611a1e1d21SSam Leffler if (i != -1) 5621a1e1d21SSam Leffler goto setrate; 5631a1e1d21SSam Leffler } 5641a1e1d21SSam Leffler return EINVAL; 5651a1e1d21SSam Leffler setrate: 5661a1e1d21SSam Leffler ic->ic_fixed_rate = i; 5671a1e1d21SSam Leffler error = ENETRESET; 5681a1e1d21SSam Leffler break; 5691a1e1d21SSam Leffler case WI_RID_CUR_TX_RATE: 5701a1e1d21SSam Leffler return EPERM; 5711a1e1d21SSam Leffler case WI_RID_RTS_THRESH: 5721a1e1d21SSam Leffler if (len != 2) 5731a1e1d21SSam Leffler return EINVAL; 5741a1e1d21SSam Leffler if (le16toh(wreq.wi_val[0]) != IEEE80211_MAX_LEN) 5751a1e1d21SSam Leffler return EINVAL; /* TODO: RTS */ 5761a1e1d21SSam Leffler break; 5771a1e1d21SSam Leffler case WI_RID_CREATE_IBSS: 5781a1e1d21SSam Leffler if (len != 2) 5791a1e1d21SSam Leffler return EINVAL; 5801a1e1d21SSam Leffler if (wreq.wi_val[0] != 0) { 5811a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_IBSS) == 0) 5821a1e1d21SSam Leffler return EINVAL; 5831a1e1d21SSam Leffler if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) { 5841a1e1d21SSam Leffler ic->ic_flags |= IEEE80211_F_IBSSON; 5851a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_IBSS && 5861a1e1d21SSam Leffler ic->ic_state == IEEE80211_S_SCAN) 5871a1e1d21SSam Leffler error = ENETRESET; 5881a1e1d21SSam Leffler } 5891a1e1d21SSam Leffler } else { 5901a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_IBSSON) { 5911a1e1d21SSam Leffler ic->ic_flags &= ~IEEE80211_F_IBSSON; 5921a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_SIBSS) { 5931a1e1d21SSam Leffler ic->ic_flags &= ~IEEE80211_F_SIBSS; 5941a1e1d21SSam Leffler error = ENETRESET; 5951a1e1d21SSam Leffler } 5961a1e1d21SSam Leffler } 5971a1e1d21SSam Leffler } 5981a1e1d21SSam Leffler break; 5991a1e1d21SSam Leffler case WI_RID_MICROWAVE_OVEN: 6001a1e1d21SSam Leffler if (len != 2) 6011a1e1d21SSam Leffler return EINVAL; 6021a1e1d21SSam Leffler if (wreq.wi_val[0] != 0) 6031a1e1d21SSam Leffler return EINVAL; /* not supported */ 6041a1e1d21SSam Leffler break; 6051a1e1d21SSam Leffler case WI_RID_ROAMING_MODE: 6061a1e1d21SSam Leffler if (len != 2) 6071a1e1d21SSam Leffler return EINVAL; 6081a1e1d21SSam Leffler if (le16toh(wreq.wi_val[0]) != 1) 6091a1e1d21SSam Leffler return EINVAL; /* not supported */ 6101a1e1d21SSam Leffler break; 6111a1e1d21SSam Leffler case WI_RID_SYSTEM_SCALE: 6121a1e1d21SSam Leffler if (len != 2) 6131a1e1d21SSam Leffler return EINVAL; 6141a1e1d21SSam Leffler if (le16toh(wreq.wi_val[0]) != 1) 6151a1e1d21SSam Leffler return EINVAL; /* not supported */ 6161a1e1d21SSam Leffler break; 6171a1e1d21SSam Leffler case WI_RID_PM_ENABLED: 6181a1e1d21SSam Leffler if (len != 2) 6191a1e1d21SSam Leffler return EINVAL; 6201a1e1d21SSam Leffler if (wreq.wi_val[0] != 0) { 6211a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) 6221a1e1d21SSam Leffler return EINVAL; 6231a1e1d21SSam Leffler if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { 6241a1e1d21SSam Leffler ic->ic_flags |= IEEE80211_F_PMGTON; 6251a1e1d21SSam Leffler error = ENETRESET; 6261a1e1d21SSam Leffler } 6271a1e1d21SSam Leffler } else { 6281a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_PMGTON) { 6291a1e1d21SSam Leffler ic->ic_flags &= ~IEEE80211_F_PMGTON; 6301a1e1d21SSam Leffler error = ENETRESET; 6311a1e1d21SSam Leffler } 6321a1e1d21SSam Leffler } 6331a1e1d21SSam Leffler break; 6341a1e1d21SSam Leffler case WI_RID_MAX_SLEEP: 6351a1e1d21SSam Leffler if (len != 2) 6361a1e1d21SSam Leffler return EINVAL; 6371a1e1d21SSam Leffler ic->ic_lintval = le16toh(wreq.wi_val[0]); 6381a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_PMGTON) 6391a1e1d21SSam Leffler error = ENETRESET; 6401a1e1d21SSam Leffler break; 6411a1e1d21SSam Leffler case WI_RID_CUR_BEACON_INT: 6421a1e1d21SSam Leffler return EPERM; 6431a1e1d21SSam Leffler case WI_RID_WEP_AVAIL: 6441a1e1d21SSam Leffler return EPERM; 6451a1e1d21SSam Leffler case WI_RID_CNFAUTHMODE: 6461a1e1d21SSam Leffler if (len != 2) 6471a1e1d21SSam Leffler return EINVAL; 6481a1e1d21SSam Leffler if (le16toh(wreq.wi_val[0]) != 1) 6491a1e1d21SSam Leffler return EINVAL; /* TODO: shared key auth */ 6501a1e1d21SSam Leffler break; 6511a1e1d21SSam Leffler case WI_RID_ENCRYPTION: 6521a1e1d21SSam Leffler if (len != 2) 6531a1e1d21SSam Leffler return EINVAL; 6541a1e1d21SSam Leffler if (wreq.wi_val[0] != 0) { 6551a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_WEP) == 0) 6561a1e1d21SSam Leffler return EINVAL; 6571a1e1d21SSam Leffler if ((ic->ic_flags & IEEE80211_F_WEPON) == 0) { 6581a1e1d21SSam Leffler ic->ic_flags |= IEEE80211_F_WEPON; 6591a1e1d21SSam Leffler error = ENETRESET; 6601a1e1d21SSam Leffler } 6611a1e1d21SSam Leffler } else { 6621a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_WEPON) { 6631a1e1d21SSam Leffler ic->ic_flags &= ~IEEE80211_F_WEPON; 6641a1e1d21SSam Leffler error = ENETRESET; 6651a1e1d21SSam Leffler } 6661a1e1d21SSam Leffler } 6671a1e1d21SSam Leffler break; 6681a1e1d21SSam Leffler case WI_RID_TX_CRYPT_KEY: 6691a1e1d21SSam Leffler if (len != 2) 6701a1e1d21SSam Leffler return EINVAL; 6711a1e1d21SSam Leffler i = le16toh(wreq.wi_val[0]); 6721a1e1d21SSam Leffler if (i >= IEEE80211_WEP_NKID) 6731a1e1d21SSam Leffler return EINVAL; 6741a1e1d21SSam Leffler ic->ic_wep_txkey = i; 6751a1e1d21SSam Leffler break; 6761a1e1d21SSam Leffler case WI_RID_DEFLT_CRYPT_KEYS: 6771a1e1d21SSam Leffler if (len != sizeof(struct wi_ltv_keys)) 6781a1e1d21SSam Leffler return EINVAL; 6791a1e1d21SSam Leffler keys = (struct wi_ltv_keys *)&wreq; 6801a1e1d21SSam Leffler for (i = 0; i < IEEE80211_WEP_NKID; i++) { 6811a1e1d21SSam Leffler len = le16toh(keys->wi_keys[i].wi_keylen); 6821a1e1d21SSam Leffler if (len != 0 && len < IEEE80211_WEP_KEYLEN) 6831a1e1d21SSam Leffler return EINVAL; 6841a1e1d21SSam Leffler if (len > sizeof(ic->ic_nw_keys[i].wk_key)) 6851a1e1d21SSam Leffler return EINVAL; 6861a1e1d21SSam Leffler } 6871a1e1d21SSam Leffler memset(ic->ic_nw_keys, 0, sizeof(ic->ic_nw_keys)); 6881a1e1d21SSam Leffler for (i = 0; i < IEEE80211_WEP_NKID; i++) { 6891a1e1d21SSam Leffler len = le16toh(keys->wi_keys[i].wi_keylen); 6901a1e1d21SSam Leffler ic->ic_nw_keys[i].wk_len = len; 6911a1e1d21SSam Leffler memcpy(ic->ic_nw_keys[i].wk_key, 6921a1e1d21SSam Leffler keys->wi_keys[i].wi_keydat, len); 6931a1e1d21SSam Leffler } 6941a1e1d21SSam Leffler error = ENETRESET; 6951a1e1d21SSam Leffler break; 6961a1e1d21SSam Leffler case WI_RID_MAX_DATALEN: 6971a1e1d21SSam Leffler if (len != 2) 6981a1e1d21SSam Leffler return EINVAL; 6991a1e1d21SSam Leffler len = le16toh(wreq.wi_val[0]); 7001a1e1d21SSam Leffler if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN) 7011a1e1d21SSam Leffler return EINVAL; 7021a1e1d21SSam Leffler if (len != IEEE80211_MAX_LEN) 7031a1e1d21SSam Leffler return EINVAL; /* TODO: fragment */ 7041a1e1d21SSam Leffler ic->ic_fragthreshold = len; 7051a1e1d21SSam Leffler error = ENETRESET; 7061a1e1d21SSam Leffler break; 7071a1e1d21SSam Leffler case WI_RID_IFACE_STATS: 7081a1e1d21SSam Leffler error = EPERM; 7091a1e1d21SSam Leffler break; 7101a1e1d21SSam Leffler case WI_RID_SCAN_REQ: /* XXX wicontrol */ 7111a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) 7121a1e1d21SSam Leffler break; 71393685685SSam Leffler error = ieee80211_setupscan(ic); 71493685685SSam Leffler if (error == 0) 715a11c9a5cSSam Leffler error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 7161a1e1d21SSam Leffler break; 7171a1e1d21SSam Leffler case WI_RID_SCAN_APS: 7181a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) 7191a1e1d21SSam Leffler break; 7201a1e1d21SSam Leffler len--; /* XXX: tx rate? */ 7211a1e1d21SSam Leffler /* FALLTHRU */ 7221a1e1d21SSam Leffler case WI_RID_CHANNEL_LIST: 7231a1e1d21SSam Leffler memset(chanlist, 0, sizeof(chanlist)); 7241a1e1d21SSam Leffler /* 7251a1e1d21SSam Leffler * Since channel 0 is not available for DS, channel 1 7261a1e1d21SSam Leffler * is assigned to LSB on WaveLAN. 7271a1e1d21SSam Leffler */ 7281a1e1d21SSam Leffler if (ic->ic_phytype == IEEE80211_T_DS) 7291a1e1d21SSam Leffler i = 1; 7301a1e1d21SSam Leffler else 7311a1e1d21SSam Leffler i = 0; 7321a1e1d21SSam Leffler for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { 7331a1e1d21SSam Leffler if ((j / 8) >= len) 7341a1e1d21SSam Leffler break; 7351a1e1d21SSam Leffler if (isclr((u_int8_t *)wreq.wi_val, j)) 7361a1e1d21SSam Leffler continue; 7371a1e1d21SSam Leffler if (isclr(ic->ic_chan_active, i)) { 7381a1e1d21SSam Leffler if (wreq.wi_type != WI_RID_CHANNEL_LIST) 7391a1e1d21SSam Leffler continue; 7401a1e1d21SSam Leffler if (isclr(ic->ic_chan_avail, i)) 7411a1e1d21SSam Leffler return EPERM; 7421a1e1d21SSam Leffler } 7431a1e1d21SSam Leffler setbit(chanlist, i); 7441a1e1d21SSam Leffler } 7451a1e1d21SSam Leffler memcpy(ic->ic_chan_active, chanlist, 7461a1e1d21SSam Leffler sizeof(ic->ic_chan_active)); 74793685685SSam Leffler error = ieee80211_setupscan(ic); 74893685685SSam Leffler if (wreq.wi_type == WI_RID_CHANNEL_LIST) { 74993685685SSam Leffler /* NB: ignore error from ieee80211_setupscan */ 7501a1e1d21SSam Leffler error = ENETRESET; 75193685685SSam Leffler } else if (error == 0) 752a11c9a5cSSam Leffler error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 7531a1e1d21SSam Leffler break; 7541a1e1d21SSam Leffler default: 7551a1e1d21SSam Leffler error = EINVAL; 7561a1e1d21SSam Leffler break; 7571a1e1d21SSam Leffler } 7581a1e1d21SSam Leffler return error; 7591a1e1d21SSam Leffler } 7601a1e1d21SSam Leffler 7611a1e1d21SSam Leffler int 7621a1e1d21SSam Leffler ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 7631a1e1d21SSam Leffler { 7641a1e1d21SSam Leffler struct ieee80211com *ic = (void *)ifp; 7651a1e1d21SSam Leffler int error = 0; 7661a1e1d21SSam Leffler u_int kid, len; 7671a1e1d21SSam Leffler struct ieee80211req *ireq; 7681be50176SSam Leffler struct ifreq *ifr; 7691a1e1d21SSam Leffler u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE]; 7701a1e1d21SSam Leffler char tmpssid[IEEE80211_NWID_LEN]; 7711a1e1d21SSam Leffler struct ieee80211_channel *chan; 772b2e95691SSam Leffler struct ifaddr *ifa; /* XXX */ 7731a1e1d21SSam Leffler 7741a1e1d21SSam Leffler switch (cmd) { 7751a1e1d21SSam Leffler case SIOCSIFMEDIA: 7761a1e1d21SSam Leffler case SIOCGIFMEDIA: 7771a1e1d21SSam Leffler error = ifmedia_ioctl(ifp, (struct ifreq *) data, 7781a1e1d21SSam Leffler &ic->ic_media, cmd); 7791a1e1d21SSam Leffler break; 7801a1e1d21SSam Leffler case SIOCG80211: 7811a1e1d21SSam Leffler ireq = (struct ieee80211req *) data; 7821a1e1d21SSam Leffler switch (ireq->i_type) { 7831a1e1d21SSam Leffler case IEEE80211_IOC_SSID: 7841a1e1d21SSam Leffler switch (ic->ic_state) { 7851a1e1d21SSam Leffler case IEEE80211_S_INIT: 7861a1e1d21SSam Leffler case IEEE80211_S_SCAN: 7871a1e1d21SSam Leffler ireq->i_len = ic->ic_des_esslen; 7881a1e1d21SSam Leffler memcpy(tmpssid, ic->ic_des_essid, ireq->i_len); 7891a1e1d21SSam Leffler break; 7901a1e1d21SSam Leffler default: 7911a1e1d21SSam Leffler ireq->i_len = ic->ic_bss->ni_esslen; 7921a1e1d21SSam Leffler memcpy(tmpssid, ic->ic_bss->ni_essid, 7931a1e1d21SSam Leffler ireq->i_len); 7941a1e1d21SSam Leffler break; 7951a1e1d21SSam Leffler } 7961a1e1d21SSam Leffler error = copyout(tmpssid, ireq->i_data, ireq->i_len); 7971a1e1d21SSam Leffler break; 7981a1e1d21SSam Leffler case IEEE80211_IOC_NUMSSIDS: 7991a1e1d21SSam Leffler ireq->i_val = 1; 8001a1e1d21SSam Leffler break; 8011a1e1d21SSam Leffler case IEEE80211_IOC_WEP: 8021a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_WEP) == 0) { 8031a1e1d21SSam Leffler ireq->i_val = IEEE80211_WEP_NOSUP; 8041a1e1d21SSam Leffler } else { 8051a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_WEPON) { 8061a1e1d21SSam Leffler ireq->i_val = 8071a1e1d21SSam Leffler IEEE80211_WEP_MIXED; 8081a1e1d21SSam Leffler } else { 8091a1e1d21SSam Leffler ireq->i_val = 8101a1e1d21SSam Leffler IEEE80211_WEP_OFF; 8111a1e1d21SSam Leffler } 8121a1e1d21SSam Leffler } 8131a1e1d21SSam Leffler break; 8141a1e1d21SSam Leffler case IEEE80211_IOC_WEPKEY: 8151a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_WEP) == 0) { 8161a1e1d21SSam Leffler error = EINVAL; 8171a1e1d21SSam Leffler break; 8181a1e1d21SSam Leffler } 8191a1e1d21SSam Leffler kid = (u_int) ireq->i_val; 8201a1e1d21SSam Leffler if (kid >= IEEE80211_WEP_NKID) { 8211a1e1d21SSam Leffler error = EINVAL; 8221a1e1d21SSam Leffler break; 8231a1e1d21SSam Leffler } 8241a1e1d21SSam Leffler len = (u_int) ic->ic_nw_keys[kid].wk_len; 8251a1e1d21SSam Leffler /* NB: only root can read WEP keys */ 8265c8bb90bSBrian Feldman if (suser(curthread) == 0) { 8271a1e1d21SSam Leffler bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len); 8281a1e1d21SSam Leffler } else { 8291a1e1d21SSam Leffler bzero(tmpkey, len); 8301a1e1d21SSam Leffler } 8311a1e1d21SSam Leffler ireq->i_len = len; 8321a1e1d21SSam Leffler error = copyout(tmpkey, ireq->i_data, len); 8331a1e1d21SSam Leffler break; 8341a1e1d21SSam Leffler case IEEE80211_IOC_NUMWEPKEYS: 8351a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_WEP) == 0) 8361a1e1d21SSam Leffler error = EINVAL; 8371a1e1d21SSam Leffler else 8381a1e1d21SSam Leffler ireq->i_val = IEEE80211_WEP_NKID; 8391a1e1d21SSam Leffler break; 8401a1e1d21SSam Leffler case IEEE80211_IOC_WEPTXKEY: 8411a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_WEP) == 0) 8421a1e1d21SSam Leffler error = EINVAL; 8431a1e1d21SSam Leffler else 8441a1e1d21SSam Leffler ireq->i_val = ic->ic_wep_txkey; 8451a1e1d21SSam Leffler break; 8461a1e1d21SSam Leffler case IEEE80211_IOC_AUTHMODE: 8471a1e1d21SSam Leffler ireq->i_val = IEEE80211_AUTH_OPEN; 8481a1e1d21SSam Leffler break; 8491a1e1d21SSam Leffler case IEEE80211_IOC_CHANNEL: 8501a1e1d21SSam Leffler switch (ic->ic_state) { 8511a1e1d21SSam Leffler case IEEE80211_S_INIT: 8521a1e1d21SSam Leffler case IEEE80211_S_SCAN: 8531a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) 8541a1e1d21SSam Leffler chan = ic->ic_des_chan; 8551a1e1d21SSam Leffler else 8561a1e1d21SSam Leffler chan = ic->ic_ibss_chan; 8571a1e1d21SSam Leffler break; 8581a1e1d21SSam Leffler default: 8591a1e1d21SSam Leffler chan = ic->ic_bss->ni_chan; 8601a1e1d21SSam Leffler break; 8611a1e1d21SSam Leffler } 8621a1e1d21SSam Leffler ireq->i_val = ieee80211_chan2ieee(ic, chan); 8631a1e1d21SSam Leffler break; 8641a1e1d21SSam Leffler case IEEE80211_IOC_POWERSAVE: 8651a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_PMGTON) 8661a1e1d21SSam Leffler ireq->i_val = IEEE80211_POWERSAVE_ON; 8671a1e1d21SSam Leffler else 8681a1e1d21SSam Leffler ireq->i_val = IEEE80211_POWERSAVE_OFF; 8691a1e1d21SSam Leffler break; 8701a1e1d21SSam Leffler case IEEE80211_IOC_POWERSAVESLEEP: 8711a1e1d21SSam Leffler ireq->i_val = ic->ic_lintval; 8721a1e1d21SSam Leffler break; 87313604e6bSSam Leffler case IEEE80211_IOC_RTSTHRESHOLD: 8741a1e1d21SSam Leffler ireq->i_val = ic->ic_rtsthreshold; 8751a1e1d21SSam Leffler break; 8762e79ca97SSam Leffler case IEEE80211_IOC_PROTMODE: 8772e79ca97SSam Leffler ireq->i_val = ic->ic_protmode; 8782e79ca97SSam Leffler break; 8792e79ca97SSam Leffler case IEEE80211_IOC_TXPOWER: 8802e79ca97SSam Leffler if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) 8812e79ca97SSam Leffler error = EINVAL; 8822e79ca97SSam Leffler else 8832e79ca97SSam Leffler ireq->i_val = ic->ic_txpower; 8842e79ca97SSam Leffler break; 8851a1e1d21SSam Leffler default: 8861a1e1d21SSam Leffler error = EINVAL; 887b2e95691SSam Leffler break; 8881a1e1d21SSam Leffler } 8891a1e1d21SSam Leffler break; 8901a1e1d21SSam Leffler case SIOCS80211: 8911a1e1d21SSam Leffler error = suser(curthread); 8921a1e1d21SSam Leffler if (error) 8931a1e1d21SSam Leffler break; 8941a1e1d21SSam Leffler ireq = (struct ieee80211req *) data; 8951a1e1d21SSam Leffler switch (ireq->i_type) { 8961a1e1d21SSam Leffler case IEEE80211_IOC_SSID: 8971a1e1d21SSam Leffler if (ireq->i_val != 0 || 8981a1e1d21SSam Leffler ireq->i_len > IEEE80211_NWID_LEN) { 8991a1e1d21SSam Leffler error = EINVAL; 9001a1e1d21SSam Leffler break; 9011a1e1d21SSam Leffler } 9021a1e1d21SSam Leffler error = copyin(ireq->i_data, tmpssid, ireq->i_len); 9031a1e1d21SSam Leffler if (error) 9041a1e1d21SSam Leffler break; 9051a1e1d21SSam Leffler memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN); 9061a1e1d21SSam Leffler ic->ic_des_esslen = ireq->i_len; 9071a1e1d21SSam Leffler memcpy(ic->ic_des_essid, tmpssid, ireq->i_len); 9081a1e1d21SSam Leffler error = ENETRESET; 9091a1e1d21SSam Leffler break; 9101a1e1d21SSam Leffler case IEEE80211_IOC_WEP: 9111a1e1d21SSam Leffler /* 9121a1e1d21SSam Leffler * These cards only support one mode so 9131a1e1d21SSam Leffler * we just turn wep on if what ever is 9141a1e1d21SSam Leffler * passed in is not OFF. 9151a1e1d21SSam Leffler */ 9161a1e1d21SSam Leffler if (ireq->i_val == IEEE80211_WEP_OFF) { 9171a1e1d21SSam Leffler ic->ic_flags &= ~IEEE80211_F_WEPON; 9181a1e1d21SSam Leffler } else { 9191a1e1d21SSam Leffler ic->ic_flags |= IEEE80211_F_WEPON; 9201a1e1d21SSam Leffler } 9211a1e1d21SSam Leffler error = ENETRESET; 9221a1e1d21SSam Leffler break; 9231a1e1d21SSam Leffler case IEEE80211_IOC_WEPKEY: 9241a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_WEP) == 0) { 9251a1e1d21SSam Leffler error = EINVAL; 9261a1e1d21SSam Leffler break; 9271a1e1d21SSam Leffler } 9281a1e1d21SSam Leffler kid = (u_int) ireq->i_val; 9291a1e1d21SSam Leffler if (kid >= IEEE80211_WEP_NKID) { 9301a1e1d21SSam Leffler error = EINVAL; 9311a1e1d21SSam Leffler break; 9321a1e1d21SSam Leffler } 9331a1e1d21SSam Leffler if (ireq->i_len > sizeof(tmpkey)) { 9341a1e1d21SSam Leffler error = EINVAL; 9351a1e1d21SSam Leffler break; 9361a1e1d21SSam Leffler } 9371a1e1d21SSam Leffler memset(tmpkey, 0, sizeof(tmpkey)); 9381a1e1d21SSam Leffler error = copyin(ireq->i_data, tmpkey, ireq->i_len); 9391a1e1d21SSam Leffler if (error) 9401a1e1d21SSam Leffler break; 9411a1e1d21SSam Leffler memcpy(ic->ic_nw_keys[kid].wk_key, tmpkey, 9421a1e1d21SSam Leffler sizeof(tmpkey)); 9431a1e1d21SSam Leffler ic->ic_nw_keys[kid].wk_len = ireq->i_len; 9441a1e1d21SSam Leffler error = ENETRESET; 9451a1e1d21SSam Leffler break; 9461a1e1d21SSam Leffler case IEEE80211_IOC_WEPTXKEY: 9471a1e1d21SSam Leffler kid = (u_int) ireq->i_val; 9481a1e1d21SSam Leffler if (kid >= IEEE80211_WEP_NKID) { 9491a1e1d21SSam Leffler error = EINVAL; 9501a1e1d21SSam Leffler break; 9511a1e1d21SSam Leffler } 9521a1e1d21SSam Leffler ic->ic_wep_txkey = kid; 9531a1e1d21SSam Leffler error = ENETRESET; 9541a1e1d21SSam Leffler break; 9551a1e1d21SSam Leffler #if 0 9561a1e1d21SSam Leffler case IEEE80211_IOC_AUTHMODE: 9571a1e1d21SSam Leffler sc->wi_authmode = ireq->i_val; 9581a1e1d21SSam Leffler break; 9591a1e1d21SSam Leffler #endif 9601a1e1d21SSam Leffler case IEEE80211_IOC_CHANNEL: 9611a1e1d21SSam Leffler /* XXX 0xffff overflows 16-bit signed */ 9621a1e1d21SSam Leffler if (ireq->i_val == 0 || 9631a1e1d21SSam Leffler ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) 9641a1e1d21SSam Leffler ic->ic_des_chan = IEEE80211_CHAN_ANYC; 9651a1e1d21SSam Leffler else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX || 9661a1e1d21SSam Leffler isclr(ic->ic_chan_active, ireq->i_val)) { 9671a1e1d21SSam Leffler error = EINVAL; 9681a1e1d21SSam Leffler break; 9691a1e1d21SSam Leffler } else 9701a1e1d21SSam Leffler ic->ic_ibss_chan = ic->ic_des_chan = 9711a1e1d21SSam Leffler &ic->ic_channels[ireq->i_val]; 9721a1e1d21SSam Leffler switch (ic->ic_state) { 9731a1e1d21SSam Leffler case IEEE80211_S_INIT: 9741a1e1d21SSam Leffler case IEEE80211_S_SCAN: 9751a1e1d21SSam Leffler error = ENETRESET; 9761a1e1d21SSam Leffler break; 9771a1e1d21SSam Leffler default: 9781a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) { 9791a1e1d21SSam Leffler if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && 9801a1e1d21SSam Leffler ic->ic_bss->ni_chan != ic->ic_des_chan) 9811a1e1d21SSam Leffler error = ENETRESET; 9821a1e1d21SSam Leffler } else { 9831a1e1d21SSam Leffler if (ic->ic_bss->ni_chan != ic->ic_ibss_chan) 9841a1e1d21SSam Leffler error = ENETRESET; 9851a1e1d21SSam Leffler } 9861a1e1d21SSam Leffler break; 9871a1e1d21SSam Leffler } 9881a1e1d21SSam Leffler break; 9891a1e1d21SSam Leffler case IEEE80211_IOC_POWERSAVE: 9901a1e1d21SSam Leffler switch (ireq->i_val) { 9911a1e1d21SSam Leffler case IEEE80211_POWERSAVE_OFF: 9921a1e1d21SSam Leffler if (ic->ic_flags & IEEE80211_F_PMGTON) { 9931a1e1d21SSam Leffler ic->ic_flags &= ~IEEE80211_F_PMGTON; 9941a1e1d21SSam Leffler error = ENETRESET; 9951a1e1d21SSam Leffler } 9961a1e1d21SSam Leffler break; 9971a1e1d21SSam Leffler case IEEE80211_POWERSAVE_ON: 9981a1e1d21SSam Leffler if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) 9991a1e1d21SSam Leffler error = EINVAL; 10001a1e1d21SSam Leffler else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) { 10011a1e1d21SSam Leffler ic->ic_flags |= IEEE80211_F_PMGTON; 10021a1e1d21SSam Leffler error = ENETRESET; 10031a1e1d21SSam Leffler } 10041a1e1d21SSam Leffler break; 10051a1e1d21SSam Leffler default: 10061a1e1d21SSam Leffler error = EINVAL; 10071a1e1d21SSam Leffler break; 10081a1e1d21SSam Leffler } 10091a1e1d21SSam Leffler break; 10101a1e1d21SSam Leffler case IEEE80211_IOC_POWERSAVESLEEP: 10111a1e1d21SSam Leffler if (ireq->i_val < 0) { 10121a1e1d21SSam Leffler error = EINVAL; 10131a1e1d21SSam Leffler break; 10141a1e1d21SSam Leffler } 10151a1e1d21SSam Leffler ic->ic_lintval = ireq->i_val; 10161a1e1d21SSam Leffler error = ENETRESET; 10171a1e1d21SSam Leffler break; 101813604e6bSSam Leffler case IEEE80211_IOC_RTSTHRESHOLD: 10191a1e1d21SSam Leffler if (!(IEEE80211_RTS_MIN < ireq->i_val && 10201a1e1d21SSam Leffler ireq->i_val < IEEE80211_RTS_MAX)) { 10211a1e1d21SSam Leffler error = EINVAL; 10221a1e1d21SSam Leffler break; 10231a1e1d21SSam Leffler } 10241a1e1d21SSam Leffler ic->ic_rtsthreshold = ireq->i_val; 10251a1e1d21SSam Leffler error = ENETRESET; 10261a1e1d21SSam Leffler break; 10272e79ca97SSam Leffler case IEEE80211_IOC_PROTMODE: 10282e79ca97SSam Leffler if (ireq->i_val > IEEE80211_PROT_RTSCTS) { 10292e79ca97SSam Leffler error = EINVAL; 10302e79ca97SSam Leffler break; 10312e79ca97SSam Leffler } 10322e79ca97SSam Leffler ic->ic_protmode = ireq->i_val; 10332e79ca97SSam Leffler /* NB: if not operating in 11g this can wait */ 10342e79ca97SSam Leffler if (ic->ic_curmode == IEEE80211_MODE_11G) 10352e79ca97SSam Leffler error = ENETRESET; 10362e79ca97SSam Leffler break; 10372e79ca97SSam Leffler case IEEE80211_IOC_TXPOWER: 10382e79ca97SSam Leffler if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) { 10392e79ca97SSam Leffler error = EINVAL; 10402e79ca97SSam Leffler break; 10412e79ca97SSam Leffler } 10422e79ca97SSam Leffler if (!(IEEE80211_TXPOWER_MIN < ireq->i_val && 10432e79ca97SSam Leffler ireq->i_val < IEEE80211_TXPOWER_MAX)) { 10442e79ca97SSam Leffler error = EINVAL; 10452e79ca97SSam Leffler break; 10462e79ca97SSam Leffler } 10472e79ca97SSam Leffler ic->ic_txpower = ireq->i_val; 10482e79ca97SSam Leffler error = ENETRESET; 10492e79ca97SSam Leffler break; 10501a1e1d21SSam Leffler default: 10511a1e1d21SSam Leffler error = EINVAL; 10521a1e1d21SSam Leffler break; 10531a1e1d21SSam Leffler } 10541a1e1d21SSam Leffler break; 10551a1e1d21SSam Leffler case SIOCGIFGENERIC: 10561a1e1d21SSam Leffler error = ieee80211_cfgget(ifp, cmd, data); 10571a1e1d21SSam Leffler break; 10581a1e1d21SSam Leffler case SIOCSIFGENERIC: 10591a1e1d21SSam Leffler error = suser(curthread); 10601a1e1d21SSam Leffler if (error) 10611a1e1d21SSam Leffler break; 10621a1e1d21SSam Leffler error = ieee80211_cfgset(ifp, cmd, data); 10631a1e1d21SSam Leffler break; 10641be50176SSam Leffler case SIOCG80211STATS: 10651be50176SSam Leffler ifr = (struct ifreq *)data; 10661be50176SSam Leffler copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats)); 10671be50176SSam Leffler break; 10686f161f03SSam Leffler case SIOCSIFMTU: 10696f161f03SSam Leffler ifr = (struct ifreq *)data; 10706f161f03SSam Leffler if (!(IEEE80211_MTU_MIN <= ifr->ifr_mtu && 10716f161f03SSam Leffler ifr->ifr_mtu <= IEEE80211_MTU_MAX)) 10726f161f03SSam Leffler error = EINVAL; 10736f161f03SSam Leffler else 10746f161f03SSam Leffler ifp->if_mtu = ifr->ifr_mtu; 10756f161f03SSam Leffler break; 1076b2e95691SSam Leffler case SIOCSIFADDR: 1077b2e95691SSam Leffler /* 1078b2e95691SSam Leffler * XXX Handle this directly so we can supress if_init calls. 1079b2e95691SSam Leffler * XXX This should be done in ether_ioctl but for the moment 1080b2e95691SSam Leffler * XXX there are too many other parts of the system that 1081b2e95691SSam Leffler * XXX set IFF_UP and so supress if_init being called when 1082b2e95691SSam Leffler * XXX it should be. 1083b2e95691SSam Leffler */ 1084b2e95691SSam Leffler ifa = (struct ifaddr *) data; 1085b2e95691SSam Leffler switch (ifa->ifa_addr->sa_family) { 1086b2e95691SSam Leffler #ifdef INET 1087b2e95691SSam Leffler case AF_INET: 1088b2e95691SSam Leffler if ((ifp->if_flags & IFF_UP) == 0) { 1089b2e95691SSam Leffler ifp->if_flags |= IFF_UP; 1090b2e95691SSam Leffler ifp->if_init(ifp->if_softc); 1091b2e95691SSam Leffler } 1092b2e95691SSam Leffler arp_ifinit(ifp, ifa); 1093b2e95691SSam Leffler break; 1094b2e95691SSam Leffler #endif 1095b2e95691SSam Leffler #ifdef IPX 1096b2e95691SSam Leffler /* 1097b2e95691SSam Leffler * XXX - This code is probably wrong, 1098b2e95691SSam Leffler * but has been copied many times. 1099b2e95691SSam Leffler */ 1100b2e95691SSam Leffler case AF_IPX: { 1101b2e95691SSam Leffler struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr); 1102b2e95691SSam Leffler struct arpcom *ac = (struct arpcom *)ifp; 1103b2e95691SSam Leffler 1104b2e95691SSam Leffler if (ipx_nullhost(*ina)) 1105b2e95691SSam Leffler ina->x_host = *(union ipx_host *) ac->ac_enaddr; 1106b2e95691SSam Leffler else 1107b2e95691SSam Leffler bcopy((caddr_t) ina->x_host.c_host, 1108b2e95691SSam Leffler (caddr_t) ac->ac_enaddr, 1109b2e95691SSam Leffler sizeof(ac->ac_enaddr)); 1110b2e95691SSam Leffler /* fall thru... */ 1111b2e95691SSam Leffler } 1112b2e95691SSam Leffler #endif 1113b2e95691SSam Leffler default: 1114b2e95691SSam Leffler if ((ifp->if_flags & IFF_UP) == 0) { 1115b2e95691SSam Leffler ifp->if_flags |= IFF_UP; 1116b2e95691SSam Leffler ifp->if_init(ifp->if_softc); 1117b2e95691SSam Leffler } 1118b2e95691SSam Leffler break; 1119b2e95691SSam Leffler } 1120b2e95691SSam Leffler break; 11211a1e1d21SSam Leffler default: 11221a1e1d21SSam Leffler error = ether_ioctl(ifp, cmd, data); 11231a1e1d21SSam Leffler break; 11241a1e1d21SSam Leffler } 11251a1e1d21SSam Leffler return error; 11261a1e1d21SSam Leffler } 1127