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