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