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