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