ieee80211_ioctl.c revision 117040
1116742Ssam/*-
2116904Ssam * Copyright (c) 2001 Atsushi Onoe
3116742Ssam * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
4116742Ssam * All rights reserved.
5116742Ssam *
6116742Ssam * Redistribution and use in source and binary forms, with or without
7116742Ssam * modification, are permitted provided that the following conditions
8116742Ssam * are met:
9116742Ssam * 1. Redistributions of source code must retain the above copyright
10116904Ssam *    notice, this list of conditions and the following disclaimer.
11116904Ssam * 2. Redistributions in binary form must reproduce the above copyright
12116904Ssam *    notice, this list of conditions and the following disclaimer in the
13116904Ssam *    documentation and/or other materials provided with the distribution.
14116904Ssam * 3. The name of the author may not be used to endorse or promote products
15116904Ssam *    derived from this software without specific prior written permission.
16116742Ssam *
17116742Ssam * Alternatively, this software may be distributed under the terms of the
18116742Ssam * GNU General Public License ("GPL") version 2 as published by the Free
19116742Ssam * Software Foundation.
20116742Ssam *
21116904Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22116904Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23116904Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24116904Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25116904Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26116904Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27116904Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28116904Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29116904Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30116904Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31116742Ssam */
32116742Ssam
33116742Ssam#include <sys/cdefs.h>
34116742Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_ioctl.c 117040 2003-06-29 20:12:17Z sam $");
35116742Ssam
36116742Ssam/*
37116742Ssam * IEEE 802.11 ioctl support (FreeBSD-specific)
38116742Ssam */
39116742Ssam
40116742Ssam#include <sys/endian.h>
41116742Ssam#include <sys/param.h>
42116742Ssam#include <sys/kernel.h>
43116742Ssam#include <sys/socket.h>
44116742Ssam#include <sys/sockio.h>
45116742Ssam#include <sys/systm.h>
46116742Ssam
47116742Ssam#include <net/if.h>
48116742Ssam#include <net/if_arp.h>
49116742Ssam#include <net/if_media.h>
50116742Ssam#include <net/ethernet.h>
51116742Ssam
52116742Ssam#include <net80211/ieee80211_var.h>
53116742Ssam#include <net80211/ieee80211_ioctl.h>
54116742Ssam
55116742Ssam#include <dev/wi/if_wavelan_ieee.h>
56116742Ssam
57116742Ssam/*
58116742Ssam * XXX
59116742Ssam * Wireless LAN specific configuration interface, which is compatible
60116742Ssam * with wicontrol(8).
61116742Ssam */
62116742Ssam
63116742Ssamint
64116742Ssamieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data)
65116742Ssam{
66116742Ssam	struct ieee80211com *ic = (void *)ifp;
67116742Ssam	int i, j, error;
68116742Ssam	struct ifreq *ifr = (struct ifreq *)data;
69116742Ssam	struct wi_req wreq;
70116742Ssam	struct wi_ltv_keys *keys;
71116742Ssam	struct wi_apinfo *ap;
72116742Ssam	struct ieee80211_node *ni;
73116742Ssam	struct ieee80211_rateset *rs;
74116742Ssam	struct wi_sigcache wsc;
75116742Ssam	struct wi_scan_p2_hdr *p2;
76116742Ssam	struct wi_scan_res *res;
77116742Ssam
78116742Ssam	error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
79116742Ssam	if (error)
80116742Ssam		return error;
81116742Ssam	wreq.wi_len = 0;
82116742Ssam	switch (wreq.wi_type) {
83116742Ssam	case WI_RID_SERIALNO:
84116742Ssam		/* nothing appropriate */
85116742Ssam		break;
86116742Ssam	case WI_RID_NODENAME:
87116742Ssam		strcpy((char *)&wreq.wi_val[1], hostname);
88116742Ssam		wreq.wi_val[0] = htole16(strlen(hostname));
89116742Ssam		wreq.wi_len = (1 + strlen(hostname) + 1) / 2;
90116742Ssam		break;
91116742Ssam	case WI_RID_CURRENT_SSID:
92116742Ssam		if (ic->ic_state != IEEE80211_S_RUN) {
93116742Ssam			wreq.wi_val[0] = 0;
94116742Ssam			wreq.wi_len = 1;
95116742Ssam			break;
96116742Ssam		}
97116742Ssam		wreq.wi_val[0] = htole16(ic->ic_bss->ni_esslen);
98116742Ssam		memcpy(&wreq.wi_val[1], ic->ic_bss->ni_essid,
99116742Ssam		    ic->ic_bss->ni_esslen);
100116742Ssam		wreq.wi_len = (1 + ic->ic_bss->ni_esslen + 1) / 2;
101116742Ssam		break;
102116742Ssam	case WI_RID_OWN_SSID:
103116742Ssam	case WI_RID_DESIRED_SSID:
104116742Ssam		wreq.wi_val[0] = htole16(ic->ic_des_esslen);
105116742Ssam		memcpy(&wreq.wi_val[1], ic->ic_des_essid, ic->ic_des_esslen);
106116742Ssam		wreq.wi_len = (1 + ic->ic_des_esslen + 1) / 2;
107116742Ssam		break;
108116742Ssam	case WI_RID_CURRENT_BSSID:
109116742Ssam		if (ic->ic_state == IEEE80211_S_RUN)
110116742Ssam			IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_bss->ni_bssid);
111116742Ssam		else
112116742Ssam			memset(wreq.wi_val, 0, IEEE80211_ADDR_LEN);
113116742Ssam		wreq.wi_len = IEEE80211_ADDR_LEN / 2;
114116742Ssam		break;
115116742Ssam	case WI_RID_CHANNEL_LIST:
116116742Ssam		memset(wreq.wi_val, 0, sizeof(wreq.wi_val));
117116742Ssam		/*
118116742Ssam		 * Since channel 0 is not available for DS, channel 1
119116742Ssam		 * is assigned to LSB on WaveLAN.
120116742Ssam		 */
121116742Ssam		if (ic->ic_phytype == IEEE80211_T_DS)
122116742Ssam			i = 1;
123116742Ssam		else
124116742Ssam			i = 0;
125116742Ssam		for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++)
126116742Ssam			if (isset(ic->ic_chan_active, i)) {
127116742Ssam				setbit((u_int8_t *)wreq.wi_val, j);
128116742Ssam				wreq.wi_len = j / 16 + 1;
129116742Ssam			}
130116742Ssam		break;
131116742Ssam	case WI_RID_OWN_CHNL:
132116742Ssam		wreq.wi_val[0] = htole16(
133116742Ssam			ieee80211_chan2ieee(ic, ic->ic_ibss_chan));
134116742Ssam		wreq.wi_len = 1;
135116742Ssam		break;
136116742Ssam	case WI_RID_CURRENT_CHAN:
137116742Ssam		wreq.wi_val[0] = htole16(
138116742Ssam			ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan));
139116742Ssam		wreq.wi_len = 1;
140116742Ssam		break;
141116742Ssam	case WI_RID_COMMS_QUALITY:
142116742Ssam		wreq.wi_val[0] = 0;				/* quality */
143116742Ssam		wreq.wi_val[1] = htole16(ic->ic_bss->ni_rssi);	/* signal */
144116742Ssam		wreq.wi_val[2] = 0;				/* noise */
145116742Ssam		wreq.wi_len = 3;
146116742Ssam		break;
147116742Ssam	case WI_RID_PROMISC:
148116742Ssam		wreq.wi_val[0] = htole16((ifp->if_flags & IFF_PROMISC) ? 1 : 0);
149116742Ssam		wreq.wi_len = 1;
150116742Ssam		break;
151116742Ssam	case WI_RID_PORTTYPE:
152116742Ssam		wreq.wi_val[0] = htole16(ic->ic_opmode);
153116742Ssam		wreq.wi_len = 1;
154116742Ssam		break;
155116742Ssam	case WI_RID_MAC_NODE:
156116742Ssam		IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_myaddr);
157116742Ssam		wreq.wi_len = IEEE80211_ADDR_LEN / 2;
158116742Ssam		break;
159116742Ssam	case WI_RID_TX_RATE:
160116742Ssam		if (ic->ic_fixed_rate == -1)
161116742Ssam			wreq.wi_val[0] = 0;	/* auto */
162116742Ssam		else
163116742Ssam			wreq.wi_val[0] = htole16(
164116742Ssam			    (ic->ic_sup_rates[ic->ic_curmode].rs_rates[ic->ic_fixed_rate] &
165116742Ssam			    IEEE80211_RATE_VAL) / 2);
166116742Ssam		wreq.wi_len = 1;
167116742Ssam		break;
168116742Ssam	case WI_RID_CUR_TX_RATE:
169116742Ssam		wreq.wi_val[0] = htole16(
170116742Ssam		    (ic->ic_bss->ni_rates.rs_rates[ic->ic_bss->ni_txrate] &
171116742Ssam		    IEEE80211_RATE_VAL) / 2);
172116742Ssam		wreq.wi_len = 1;
173116742Ssam		break;
174116742Ssam	case WI_RID_RTS_THRESH:
175116742Ssam		wreq.wi_val[0] = htole16(ic->ic_rtsthreshold);
176116742Ssam		wreq.wi_len = 1;
177116742Ssam		break;
178116742Ssam	case WI_RID_CREATE_IBSS:
179116742Ssam		wreq.wi_val[0] =
180116742Ssam		    htole16((ic->ic_flags & IEEE80211_F_IBSSON) ? 1 : 0);
181116742Ssam		wreq.wi_len = 1;
182116742Ssam		break;
183116742Ssam	case WI_RID_MICROWAVE_OVEN:
184116742Ssam		wreq.wi_val[0] = 0;	/* no ... not supported */
185116742Ssam		wreq.wi_len = 1;
186116742Ssam		break;
187116742Ssam	case WI_RID_ROAMING_MODE:
188116742Ssam		wreq.wi_val[0] = htole16(1);	/* enabled ... not supported */
189116742Ssam		wreq.wi_len = 1;
190116742Ssam		break;
191116742Ssam	case WI_RID_SYSTEM_SCALE:
192116742Ssam		wreq.wi_val[0] = htole16(1);	/* low density ... not supp */
193116742Ssam		wreq.wi_len = 1;
194116742Ssam		break;
195116742Ssam	case WI_RID_PM_ENABLED:
196116742Ssam		wreq.wi_val[0] =
197116742Ssam		    htole16((ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0);
198116742Ssam		wreq.wi_len = 1;
199116742Ssam		break;
200116742Ssam	case WI_RID_MAX_SLEEP:
201116742Ssam		wreq.wi_val[0] = htole16(ic->ic_lintval);
202116742Ssam		wreq.wi_len = 1;
203116742Ssam		break;
204116742Ssam	case WI_RID_CUR_BEACON_INT:
205116742Ssam		wreq.wi_val[0] = htole16(ic->ic_bss->ni_intval);
206116742Ssam		wreq.wi_len = 1;
207116742Ssam		break;
208116742Ssam	case WI_RID_WEP_AVAIL:
209116742Ssam		wreq.wi_val[0] =
210116742Ssam		    htole16((ic->ic_caps & IEEE80211_C_WEP) ? 1 : 0);
211116742Ssam		wreq.wi_len = 1;
212116742Ssam		break;
213116742Ssam	case WI_RID_CNFAUTHMODE:
214116742Ssam		wreq.wi_val[0] = htole16(1);	/* TODO: open system only */
215116742Ssam		wreq.wi_len = 1;
216116742Ssam		break;
217116742Ssam	case WI_RID_ENCRYPTION:
218116742Ssam		wreq.wi_val[0] =
219116742Ssam		    htole16((ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 0);
220116742Ssam		wreq.wi_len = 1;
221116742Ssam		break;
222116742Ssam	case WI_RID_TX_CRYPT_KEY:
223116742Ssam		wreq.wi_val[0] = htole16(ic->ic_wep_txkey);
224116742Ssam		wreq.wi_len = 1;
225116742Ssam		break;
226116742Ssam	case WI_RID_DEFLT_CRYPT_KEYS:
227116742Ssam		keys = (struct wi_ltv_keys *)&wreq;
228116742Ssam		/* do not show keys to non-root user */
229116742Ssam		error = suser(curthread);
230116742Ssam		if (error) {
231116742Ssam			memset(keys, 0, sizeof(*keys));
232116742Ssam			error = 0;
233116742Ssam			break;
234116742Ssam		}
235116742Ssam		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
236116742Ssam			keys->wi_keys[i].wi_keylen =
237116742Ssam			    htole16(ic->ic_nw_keys[i].wk_len);
238116742Ssam			memcpy(keys->wi_keys[i].wi_keydat,
239116742Ssam			    ic->ic_nw_keys[i].wk_key, ic->ic_nw_keys[i].wk_len);
240116742Ssam		}
241116742Ssam		wreq.wi_len = sizeof(*keys) / 2;
242116742Ssam		break;
243116742Ssam	case WI_RID_MAX_DATALEN:
244116742Ssam		wreq.wi_val[0] = htole16(IEEE80211_MAX_LEN);	/* TODO: frag */
245116742Ssam		wreq.wi_len = 1;
246116742Ssam		break;
247116742Ssam	case WI_RID_IFACE_STATS:
248116742Ssam		/* XXX: should be implemented in lower drivers */
249116742Ssam		break;
250116742Ssam	case WI_RID_READ_APS:
251116742Ssam		if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
252116742Ssam			/*
253116742Ssam			 * Don't return results until active scan completes.
254116742Ssam			 */
255116742Ssam			if (ic->ic_state == IEEE80211_S_SCAN &&
256116742Ssam			    (ic->ic_flags & IEEE80211_F_ASCAN)) {
257116742Ssam				error = EINPROGRESS;
258116742Ssam				break;
259116742Ssam			}
260116742Ssam		}
261116742Ssam		i = 0;
262116742Ssam		ap = (void *)((char *)wreq.wi_val + sizeof(i));
263116742Ssam		TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
264116742Ssam			if ((caddr_t)(ap + 1) > (caddr_t)(&wreq + 1))
265116742Ssam				break;
266116742Ssam			memset(ap, 0, sizeof(*ap));
267116742Ssam			if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
268116742Ssam				IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr);
269116742Ssam				ap->namelen = ic->ic_des_esslen;
270116742Ssam				if (ic->ic_des_esslen)
271116742Ssam					memcpy(ap->name, ic->ic_des_essid,
272116742Ssam					    ic->ic_des_esslen);
273116742Ssam			} else {
274116742Ssam				IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid);
275116742Ssam				ap->namelen = ni->ni_esslen;
276116742Ssam				if (ni->ni_esslen)
277116742Ssam					memcpy(ap->name, ni->ni_essid,
278116742Ssam					    ni->ni_esslen);
279116742Ssam			}
280116742Ssam			ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan);
281116742Ssam			ap->signal = ni->ni_rssi;
282116742Ssam			ap->capinfo = ni->ni_capinfo;
283116742Ssam			ap->interval = ni->ni_intval;
284116742Ssam			rs = &ni->ni_rates;
285116742Ssam			for (j = 0; j < rs->rs_nrates; j++) {
286116742Ssam				if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) {
287116742Ssam					ap->rate = (rs->rs_rates[j] &
288116742Ssam					    IEEE80211_RATE_VAL) * 5; /* XXX */
289116742Ssam				}
290116742Ssam			}
291116742Ssam			i++;
292116742Ssam			ap++;
293116742Ssam		}
294116742Ssam		memcpy(wreq.wi_val, &i, sizeof(i));
295116742Ssam		wreq.wi_len = (sizeof(int) + sizeof(*ap) * i) / 2;
296116742Ssam		break;
297116742Ssam	case WI_RID_PRISM2:
298116742Ssam		wreq.wi_val[0] = 1;	/* XXX lie so SCAN_RES can give rates */
299116742Ssam		wreq.wi_len = sizeof(u_int16_t) / 2;
300116742Ssam		break;
301116742Ssam	case WI_RID_SCAN_RES:			/* compatibility interface */
302116742Ssam		if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
303116742Ssam		    ic->ic_state == IEEE80211_S_SCAN) {
304116742Ssam			error = EINPROGRESS;
305116742Ssam			break;
306116742Ssam		}
307116742Ssam		/* NB: we use the Prism2 format so we can return rate info */
308116742Ssam		p2 = (struct wi_scan_p2_hdr *)wreq.wi_val;
309116742Ssam		res = (void *)&p2[1];
310116742Ssam		i = 0;
311116742Ssam		TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
312116742Ssam			if ((caddr_t)(res + 1) > (caddr_t)(&wreq + 1))
313116742Ssam				break;
314116742Ssam			res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan);
315116742Ssam			res->wi_noise = 0;
316116742Ssam			res->wi_signal = ni->ni_rssi;
317116742Ssam			IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid);
318116742Ssam			res->wi_interval = ni->ni_intval;
319116742Ssam			res->wi_capinfo = ni->ni_capinfo;
320116742Ssam			res->wi_ssid_len = ni->ni_esslen;
321116742Ssam			memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN);
322116742Ssam			/* NB: assumes wi_srates holds <= ni->ni_rates */
323116742Ssam			memcpy(res->wi_srates, ni->ni_rates.rs_rates,
324116742Ssam				sizeof(res->wi_srates));
325116742Ssam			if (ni->ni_rates.rs_nrates < 10)
326116742Ssam				res->wi_srates[ni->ni_rates.rs_nrates] = 0;
327116742Ssam			res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate];
328116742Ssam			res->wi_rsvd = 0;
329116742Ssam			res++, i++;
330116742Ssam		}
331116742Ssam		p2->wi_rsvd = 0;
332116742Ssam		p2->wi_reason = i;
333116742Ssam		wreq.wi_len = (sizeof(*p2) + sizeof(*res) * i) / 2;
334116742Ssam		break;
335116742Ssam	case WI_RID_READ_CACHE:
336116742Ssam		i = 0;
337116742Ssam		TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
338116742Ssam			if (i == (WI_MAX_DATALEN/sizeof(struct wi_sigcache))-1)
339116742Ssam				break;
340116742Ssam			IEEE80211_ADDR_COPY(wsc.macsrc, ni->ni_macaddr);
341116742Ssam			memset(&wsc.ipsrc, 0, sizeof(wsc.ipsrc));
342116742Ssam			wsc.signal = ni->ni_rssi;
343116742Ssam			wsc.noise = 0;
344116742Ssam			wsc.quality = 0;
345116742Ssam			memcpy((caddr_t)wreq.wi_val + sizeof(wsc) * i,
346116742Ssam			    &wsc, sizeof(wsc));
347116742Ssam			i++;
348116742Ssam		}
349116742Ssam		wreq.wi_len = sizeof(wsc) * i / 2;
350116742Ssam		break;
351116742Ssam	case WI_RID_SCAN_APS:
352116742Ssam		error = EINVAL;
353116742Ssam		break;
354116742Ssam	default:
355116742Ssam		error = EINVAL;
356116742Ssam		break;
357116742Ssam	}
358116742Ssam	if (error == 0) {
359116742Ssam		wreq.wi_len++;
360116742Ssam		error = copyout(&wreq, ifr->ifr_data, sizeof(wreq));
361116742Ssam	}
362116742Ssam	return error;
363116742Ssam}
364116742Ssam
365116742Ssamstatic int
366116742Ssamfindrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
367116742Ssam{
368116742Ssam#define	IEEERATE(_ic,_m,_i) \
369116742Ssam	((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
370116742Ssam	int i, nrates = ic->ic_sup_rates[mode].rs_nrates;
371116742Ssam	for (i = 0; i < nrates; i++)
372116742Ssam		if (IEEERATE(ic, mode, i) == rate)
373116742Ssam			return i;
374116742Ssam	return -1;
375116742Ssam#undef IEEERATE
376116742Ssam}
377116742Ssam
378116742Ssamint
379116742Ssamieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
380116742Ssam{
381116742Ssam	struct ieee80211com *ic = (void *)ifp;
382116742Ssam	int i, j, len, error, rate;
383116742Ssam	struct ifreq *ifr = (struct ifreq *)data;
384116742Ssam	struct wi_ltv_keys *keys;
385116742Ssam	struct wi_req wreq;
386116742Ssam	u_char chanlist[roundup(IEEE80211_CHAN_MAX, NBBY)];
387116742Ssam
388116742Ssam	error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
389116742Ssam	if (error)
390116742Ssam		return error;
391116742Ssam	len = wreq.wi_len ? (wreq.wi_len - 1) * 2 : 0;
392116742Ssam	switch (wreq.wi_type) {
393116742Ssam	case WI_RID_SERIALNO:
394116742Ssam	case WI_RID_NODENAME:
395116742Ssam		return EPERM;
396116742Ssam	case WI_RID_CURRENT_SSID:
397116742Ssam		return EPERM;
398116742Ssam	case WI_RID_OWN_SSID:
399116742Ssam	case WI_RID_DESIRED_SSID:
400116742Ssam		if (le16toh(wreq.wi_val[0]) * 2 > len ||
401116742Ssam		    le16toh(wreq.wi_val[0]) > IEEE80211_NWID_LEN) {
402116742Ssam			error = ENOSPC;
403116742Ssam			break;
404116742Ssam		}
405116742Ssam		memset(ic->ic_des_essid, 0, sizeof(ic->ic_des_essid));
406116742Ssam		ic->ic_des_esslen = le16toh(wreq.wi_val[0]) * 2;
407117040Ssam		memcpy(ic->ic_des_essid, &wreq.wi_val[1], ic->ic_des_esslen);
408116742Ssam		error = ENETRESET;
409116742Ssam		break;
410116742Ssam	case WI_RID_CURRENT_BSSID:
411116742Ssam		return EPERM;
412116742Ssam	case WI_RID_OWN_CHNL:
413116742Ssam		if (len != 2)
414116742Ssam			return EINVAL;
415116742Ssam		i = le16toh(wreq.wi_val[0]);
416116742Ssam		if (i < 0 ||
417116742Ssam		    i > IEEE80211_CHAN_MAX ||
418116742Ssam		    isclr(ic->ic_chan_active, i))
419116742Ssam			return EINVAL;
420116742Ssam		ic->ic_ibss_chan = &ic->ic_channels[i];
421116742Ssam		if (ic->ic_flags & IEEE80211_F_SIBSS)
422116742Ssam			error = ENETRESET;
423116742Ssam		break;
424116742Ssam	case WI_RID_CURRENT_CHAN:
425116742Ssam		return EPERM;
426116742Ssam	case WI_RID_COMMS_QUALITY:
427116742Ssam		return EPERM;
428116742Ssam	case WI_RID_PROMISC:
429116742Ssam		if (len != 2)
430116742Ssam			return EINVAL;
431116742Ssam		if (ifp->if_flags & IFF_PROMISC) {
432116742Ssam			if (wreq.wi_val[0] == 0) {
433116742Ssam				ifp->if_flags &= ~IFF_PROMISC;
434116742Ssam				error = ENETRESET;
435116742Ssam			}
436116742Ssam		} else {
437116742Ssam			if (wreq.wi_val[0] != 0) {
438116742Ssam				ifp->if_flags |= IFF_PROMISC;
439116742Ssam				error = ENETRESET;
440116742Ssam			}
441116742Ssam		}
442116742Ssam		break;
443116742Ssam	case WI_RID_PORTTYPE:
444116742Ssam		if (len != 2)
445116742Ssam			return EINVAL;
446116742Ssam		switch (le16toh(wreq.wi_val[0])) {
447116742Ssam		case IEEE80211_M_STA:
448116742Ssam			break;
449116742Ssam		case IEEE80211_M_IBSS:
450116742Ssam			if (!(ic->ic_caps & IEEE80211_C_IBSS))
451116742Ssam				return EINVAL;
452116742Ssam			break;
453116742Ssam		case IEEE80211_M_AHDEMO:
454116742Ssam			if (ic->ic_phytype != IEEE80211_T_DS ||
455116742Ssam			    !(ic->ic_caps & IEEE80211_C_AHDEMO))
456116742Ssam				return EINVAL;
457116742Ssam			break;
458116742Ssam		case IEEE80211_M_HOSTAP:
459116742Ssam			if (!(ic->ic_caps & IEEE80211_C_HOSTAP))
460116742Ssam				return EINVAL;
461116742Ssam			break;
462116742Ssam		default:
463116742Ssam			return EINVAL;
464116742Ssam		}
465116742Ssam		if (le16toh(wreq.wi_val[0]) != ic->ic_opmode) {
466116742Ssam			ic->ic_opmode = le16toh(wreq.wi_val[0]);
467116742Ssam			error = ENETRESET;
468116742Ssam		}
469116742Ssam		break;
470116742Ssam#if 0
471116742Ssam	case WI_RID_MAC_NODE:
472116742Ssam		if (len != IEEE80211_ADDR_LEN)
473116742Ssam			return EINVAL;
474116742Ssam		IEEE80211_ADDR_COPY(LLADDR(ifp->if_sadl), wreq.wi_val);
475116742Ssam		/* if_init will copy lladdr into ic_myaddr */
476116742Ssam		error = ENETRESET;
477116742Ssam		break;
478116742Ssam#endif
479116742Ssam	case WI_RID_TX_RATE:
480116742Ssam		if (len != 2)
481116742Ssam			return EINVAL;
482116742Ssam		if (wreq.wi_val[0] == 0) {
483116742Ssam			/* auto */
484116742Ssam			ic->ic_fixed_rate = -1;
485116742Ssam			break;
486116742Ssam		}
487116742Ssam		rate = 2 * le16toh(wreq.wi_val[0]);
488116742Ssam		if (ic->ic_curmode == IEEE80211_MODE_AUTO) {
489116742Ssam			/*
490116742Ssam			 * In autoselect mode search for the rate.  We take
491116742Ssam			 * the first instance which may not be right, but we
492116742Ssam			 * are limited by the interface.  Note that we also
493116742Ssam			 * lock the mode to insure the rate is meaningful
494116742Ssam			 * when it is used.
495116742Ssam			 */
496116742Ssam			for (j = IEEE80211_MODE_11A;
497116742Ssam			     j < IEEE80211_MODE_MAX; j++) {
498116742Ssam				if ((ic->ic_modecaps & (1<<j)) == 0)
499116742Ssam					continue;
500116742Ssam				i = findrate(ic, j, rate);
501116742Ssam				if (i != -1) {
502116742Ssam					/* lock mode too */
503116742Ssam					ic->ic_curmode = j;
504116742Ssam					goto setrate;
505116742Ssam				}
506116742Ssam			}
507116742Ssam		} else {
508116742Ssam			i = findrate(ic, ic->ic_curmode, rate);
509116742Ssam			if (i != -1)
510116742Ssam				goto setrate;
511116742Ssam		}
512116742Ssam		return EINVAL;
513116742Ssam	setrate:
514116742Ssam		ic->ic_fixed_rate = i;
515116742Ssam		error = ENETRESET;
516116742Ssam		break;
517116742Ssam	case WI_RID_CUR_TX_RATE:
518116742Ssam		return EPERM;
519116742Ssam	case WI_RID_RTS_THRESH:
520116742Ssam		if (len != 2)
521116742Ssam			return EINVAL;
522116742Ssam		if (le16toh(wreq.wi_val[0]) != IEEE80211_MAX_LEN)
523116742Ssam			return EINVAL;		/* TODO: RTS */
524116742Ssam		break;
525116742Ssam	case WI_RID_CREATE_IBSS:
526116742Ssam		if (len != 2)
527116742Ssam			return EINVAL;
528116742Ssam		if (wreq.wi_val[0] != 0) {
529116742Ssam			if ((ic->ic_caps & IEEE80211_C_IBSS) == 0)
530116742Ssam				return EINVAL;
531116742Ssam			if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) {
532116742Ssam				ic->ic_flags |= IEEE80211_F_IBSSON;
533116742Ssam				if (ic->ic_opmode == IEEE80211_M_IBSS &&
534116742Ssam				    ic->ic_state == IEEE80211_S_SCAN)
535116742Ssam					error = ENETRESET;
536116742Ssam			}
537116742Ssam		} else {
538116742Ssam			if (ic->ic_flags & IEEE80211_F_IBSSON) {
539116742Ssam				ic->ic_flags &= ~IEEE80211_F_IBSSON;
540116742Ssam				if (ic->ic_flags & IEEE80211_F_SIBSS) {
541116742Ssam					ic->ic_flags &= ~IEEE80211_F_SIBSS;
542116742Ssam					error = ENETRESET;
543116742Ssam				}
544116742Ssam			}
545116742Ssam		}
546116742Ssam		break;
547116742Ssam	case WI_RID_MICROWAVE_OVEN:
548116742Ssam		if (len != 2)
549116742Ssam			return EINVAL;
550116742Ssam		if (wreq.wi_val[0] != 0)
551116742Ssam			return EINVAL;		/* not supported */
552116742Ssam		break;
553116742Ssam	case WI_RID_ROAMING_MODE:
554116742Ssam		if (len != 2)
555116742Ssam			return EINVAL;
556116742Ssam		if (le16toh(wreq.wi_val[0]) != 1)
557116742Ssam			return EINVAL;		/* not supported */
558116742Ssam		break;
559116742Ssam	case WI_RID_SYSTEM_SCALE:
560116742Ssam		if (len != 2)
561116742Ssam			return EINVAL;
562116742Ssam		if (le16toh(wreq.wi_val[0]) != 1)
563116742Ssam			return EINVAL;		/* not supported */
564116742Ssam		break;
565116742Ssam	case WI_RID_PM_ENABLED:
566116742Ssam		if (len != 2)
567116742Ssam			return EINVAL;
568116742Ssam		if (wreq.wi_val[0] != 0) {
569116742Ssam			if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
570116742Ssam				return EINVAL;
571116742Ssam			if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
572116742Ssam				ic->ic_flags |= IEEE80211_F_PMGTON;
573116742Ssam				error = ENETRESET;
574116742Ssam			}
575116742Ssam		} else {
576116742Ssam			if (ic->ic_flags & IEEE80211_F_PMGTON) {
577116742Ssam				ic->ic_flags &= ~IEEE80211_F_PMGTON;
578116742Ssam				error = ENETRESET;
579116742Ssam			}
580116742Ssam		}
581116742Ssam		break;
582116742Ssam	case WI_RID_MAX_SLEEP:
583116742Ssam		if (len != 2)
584116742Ssam			return EINVAL;
585116742Ssam		ic->ic_lintval = le16toh(wreq.wi_val[0]);
586116742Ssam		if (ic->ic_flags & IEEE80211_F_PMGTON)
587116742Ssam			error = ENETRESET;
588116742Ssam		break;
589116742Ssam	case WI_RID_CUR_BEACON_INT:
590116742Ssam		return EPERM;
591116742Ssam	case WI_RID_WEP_AVAIL:
592116742Ssam		return EPERM;
593116742Ssam	case WI_RID_CNFAUTHMODE:
594116742Ssam		if (len != 2)
595116742Ssam			return EINVAL;
596116742Ssam		if (le16toh(wreq.wi_val[0]) != 1)
597116742Ssam			return EINVAL;		/* TODO: shared key auth */
598116742Ssam		break;
599116742Ssam	case WI_RID_ENCRYPTION:
600116742Ssam		if (len != 2)
601116742Ssam			return EINVAL;
602116742Ssam		if (wreq.wi_val[0] != 0) {
603116742Ssam			if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
604116742Ssam				return EINVAL;
605116742Ssam			if ((ic->ic_flags & IEEE80211_F_WEPON) == 0) {
606116742Ssam				ic->ic_flags |= IEEE80211_F_WEPON;
607116742Ssam				error = ENETRESET;
608116742Ssam			}
609116742Ssam		} else {
610116742Ssam			if (ic->ic_flags & IEEE80211_F_WEPON) {
611116742Ssam				ic->ic_flags &= ~IEEE80211_F_WEPON;
612116742Ssam				error = ENETRESET;
613116742Ssam			}
614116742Ssam		}
615116742Ssam		break;
616116742Ssam	case WI_RID_TX_CRYPT_KEY:
617116742Ssam		if (len != 2)
618116742Ssam			return EINVAL;
619116742Ssam		i = le16toh(wreq.wi_val[0]);
620116742Ssam		if (i >= IEEE80211_WEP_NKID)
621116742Ssam			return EINVAL;
622116742Ssam		ic->ic_wep_txkey = i;
623116742Ssam		break;
624116742Ssam	case WI_RID_DEFLT_CRYPT_KEYS:
625116742Ssam		if (len != sizeof(struct wi_ltv_keys))
626116742Ssam			return EINVAL;
627116742Ssam		keys = (struct wi_ltv_keys *)&wreq;
628116742Ssam		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
629116742Ssam			len = le16toh(keys->wi_keys[i].wi_keylen);
630116742Ssam			if (len != 0 && len < IEEE80211_WEP_KEYLEN)
631116742Ssam				return EINVAL;
632116742Ssam			if (len > sizeof(ic->ic_nw_keys[i].wk_key))
633116742Ssam				return EINVAL;
634116742Ssam		}
635116742Ssam		memset(ic->ic_nw_keys, 0, sizeof(ic->ic_nw_keys));
636116742Ssam		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
637116742Ssam			len = le16toh(keys->wi_keys[i].wi_keylen);
638116742Ssam			ic->ic_nw_keys[i].wk_len = len;
639116742Ssam			memcpy(ic->ic_nw_keys[i].wk_key,
640116742Ssam			    keys->wi_keys[i].wi_keydat, len);
641116742Ssam		}
642116742Ssam		error = ENETRESET;
643116742Ssam		break;
644116742Ssam	case WI_RID_MAX_DATALEN:
645116742Ssam		if (len != 2)
646116742Ssam			return EINVAL;
647116742Ssam		len = le16toh(wreq.wi_val[0]);
648116742Ssam		if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN)
649116742Ssam			return EINVAL;
650116742Ssam		if (len != IEEE80211_MAX_LEN)
651116742Ssam			return EINVAL;		/* TODO: fragment */
652116742Ssam		ic->ic_fragthreshold = len;
653116742Ssam		error = ENETRESET;
654116742Ssam		break;
655116742Ssam	case WI_RID_IFACE_STATS:
656116742Ssam		error = EPERM;
657116742Ssam		break;
658116742Ssam	case WI_RID_SCAN_REQ:			/* XXX wicontrol */
659116742Ssam		if (ic->ic_opmode == IEEE80211_M_HOSTAP)
660116742Ssam			break;
661116742Ssam		/* NB: ignore channel list and tx rate parameters */
662116742Ssam		error = ieee80211_new_state(ifp, IEEE80211_S_SCAN, -1);
663116742Ssam		break;
664116742Ssam	case WI_RID_SCAN_APS:
665116742Ssam		if (ic->ic_opmode == IEEE80211_M_HOSTAP)
666116742Ssam			break;
667116742Ssam		len--;			/* XXX: tx rate? */
668116742Ssam		/* FALLTHRU */
669116742Ssam	case WI_RID_CHANNEL_LIST:
670116742Ssam		memset(chanlist, 0, sizeof(chanlist));
671116742Ssam		/*
672116742Ssam		 * Since channel 0 is not available for DS, channel 1
673116742Ssam		 * is assigned to LSB on WaveLAN.
674116742Ssam		 */
675116742Ssam		if (ic->ic_phytype == IEEE80211_T_DS)
676116742Ssam			i = 1;
677116742Ssam		else
678116742Ssam			i = 0;
679116742Ssam		for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) {
680116742Ssam			if ((j / 8) >= len)
681116742Ssam				break;
682116742Ssam			if (isclr((u_int8_t *)wreq.wi_val, j))
683116742Ssam				continue;
684116742Ssam			if (isclr(ic->ic_chan_active, i)) {
685116742Ssam				if (wreq.wi_type != WI_RID_CHANNEL_LIST)
686116742Ssam					continue;
687116742Ssam				if (isclr(ic->ic_chan_avail, i))
688116742Ssam					return EPERM;
689116742Ssam			}
690116742Ssam			setbit(chanlist, i);
691116742Ssam		}
692116742Ssam		memcpy(ic->ic_chan_active, chanlist,
693116742Ssam		    sizeof(ic->ic_chan_active));
694116742Ssam		if (isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) {
695116742Ssam			for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
696116742Ssam				if (isset(chanlist, i)) {
697116742Ssam					ic->ic_ibss_chan = &ic->ic_channels[i];
698116742Ssam					break;
699116742Ssam				}
700116742Ssam		}
701116742Ssam		if (isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)))
702116742Ssam			ic->ic_bss->ni_chan = ic->ic_ibss_chan;
703116742Ssam		if (wreq.wi_type == WI_RID_CHANNEL_LIST)
704116742Ssam			error = ENETRESET;
705116742Ssam		else
706116742Ssam			error = ieee80211_new_state(ifp, IEEE80211_S_SCAN, -1);
707116742Ssam		break;
708116742Ssam	default:
709116742Ssam		error = EINVAL;
710116742Ssam		break;
711116742Ssam	}
712116742Ssam	return error;
713116742Ssam}
714116742Ssam
715116742Ssamint
716116742Ssamieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
717116742Ssam{
718116742Ssam	struct ieee80211com *ic = (void *)ifp;
719116742Ssam	int error = 0;
720116742Ssam	u_int kid, len;
721116742Ssam	struct ieee80211req *ireq;
722116742Ssam	u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE];
723116742Ssam	char tmpssid[IEEE80211_NWID_LEN];
724116742Ssam	struct ieee80211_channel *chan;
725116742Ssam
726116742Ssam	switch (cmd) {
727116742Ssam	case SIOCSIFMEDIA:
728116742Ssam	case SIOCGIFMEDIA:
729116742Ssam		error = ifmedia_ioctl(ifp, (struct ifreq *) data,
730116742Ssam				&ic->ic_media, cmd);
731116742Ssam		break;
732116742Ssam	case SIOCG80211:
733116742Ssam		ireq = (struct ieee80211req *) data;
734116742Ssam		switch (ireq->i_type) {
735116742Ssam		case IEEE80211_IOC_SSID:
736116742Ssam			switch (ic->ic_state) {
737116742Ssam			case IEEE80211_S_INIT:
738116742Ssam			case IEEE80211_S_SCAN:
739116742Ssam				ireq->i_len = ic->ic_des_esslen;
740116742Ssam				memcpy(tmpssid, ic->ic_des_essid, ireq->i_len);
741116742Ssam				break;
742116742Ssam			default:
743116742Ssam				ireq->i_len = ic->ic_bss->ni_esslen;
744116742Ssam				memcpy(tmpssid, ic->ic_bss->ni_essid,
745116742Ssam					ireq->i_len);
746116742Ssam				break;
747116742Ssam			}
748116742Ssam			error = copyout(tmpssid, ireq->i_data, ireq->i_len);
749116742Ssam			break;
750116742Ssam		case IEEE80211_IOC_NUMSSIDS:
751116742Ssam			ireq->i_val = 1;
752116742Ssam			break;
753116742Ssam		case IEEE80211_IOC_WEP:
754116742Ssam			if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
755116742Ssam				ireq->i_val = IEEE80211_WEP_NOSUP;
756116742Ssam			} else {
757116742Ssam				if (ic->ic_flags & IEEE80211_F_WEPON) {
758116742Ssam					ireq->i_val =
759116742Ssam					    IEEE80211_WEP_MIXED;
760116742Ssam				} else {
761116742Ssam					ireq->i_val =
762116742Ssam					    IEEE80211_WEP_OFF;
763116742Ssam				}
764116742Ssam			}
765116742Ssam			break;
766116742Ssam		case IEEE80211_IOC_WEPKEY:
767116742Ssam			if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
768116742Ssam				error = EINVAL;
769116742Ssam				break;
770116742Ssam			}
771116742Ssam			kid = (u_int) ireq->i_val;
772116742Ssam			if (kid >= IEEE80211_WEP_NKID) {
773116742Ssam				error = EINVAL;
774116742Ssam				break;
775116742Ssam			}
776116742Ssam			len = (u_int) ic->ic_nw_keys[kid].wk_len;
777116742Ssam			/* NB: only root can read WEP keys */
778116742Ssam			if (suser(curthread)) {
779116742Ssam				bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
780116742Ssam			} else {
781116742Ssam				bzero(tmpkey, len);
782116742Ssam			}
783116742Ssam			ireq->i_len = len;
784116742Ssam			error = copyout(tmpkey, ireq->i_data, len);
785116742Ssam			break;
786116742Ssam		case IEEE80211_IOC_NUMWEPKEYS:
787116742Ssam			if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
788116742Ssam				error = EINVAL;
789116742Ssam			else
790116742Ssam				ireq->i_val = IEEE80211_WEP_NKID;
791116742Ssam			break;
792116742Ssam		case IEEE80211_IOC_WEPTXKEY:
793116742Ssam			if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
794116742Ssam				error = EINVAL;
795116742Ssam			else
796116742Ssam				ireq->i_val = ic->ic_wep_txkey;
797116742Ssam			break;
798116742Ssam		case IEEE80211_IOC_AUTHMODE:
799116742Ssam			ireq->i_val = IEEE80211_AUTH_OPEN;
800116742Ssam			break;
801116742Ssam		case IEEE80211_IOC_CHANNEL:
802116742Ssam			switch (ic->ic_state) {
803116742Ssam			case IEEE80211_S_INIT:
804116742Ssam			case IEEE80211_S_SCAN:
805116742Ssam				if (ic->ic_opmode == IEEE80211_M_STA)
806116742Ssam					chan = ic->ic_des_chan;
807116742Ssam				else
808116742Ssam					chan = ic->ic_ibss_chan;
809116742Ssam				break;
810116742Ssam			default:
811116742Ssam				chan = ic->ic_bss->ni_chan;
812116742Ssam				break;
813116742Ssam			}
814116742Ssam			ireq->i_val = ieee80211_chan2ieee(ic, chan);
815116742Ssam			break;
816116742Ssam		case IEEE80211_IOC_POWERSAVE:
817116742Ssam			if (ic->ic_flags & IEEE80211_F_PMGTON)
818116742Ssam				ireq->i_val = IEEE80211_POWERSAVE_ON;
819116742Ssam			else
820116742Ssam				ireq->i_val = IEEE80211_POWERSAVE_OFF;
821116742Ssam			break;
822116742Ssam		case IEEE80211_IOC_POWERSAVESLEEP:
823116742Ssam			ireq->i_val = ic->ic_lintval;
824116742Ssam			break;
825116742Ssam		case IEEE80211_IOCT_RTSTHRESHOLD:
826116742Ssam			ireq->i_val = ic->ic_rtsthreshold;
827116742Ssam			break;
828116742Ssam		default:
829116742Ssam			error = EINVAL;
830116742Ssam		}
831116742Ssam		break;
832116742Ssam	case SIOCS80211:
833116742Ssam		error = suser(curthread);
834116742Ssam		if (error)
835116742Ssam			break;
836116742Ssam		ireq = (struct ieee80211req *) data;
837116742Ssam		switch (ireq->i_type) {
838116742Ssam		case IEEE80211_IOC_SSID:
839116742Ssam			if (ireq->i_val != 0 ||
840116742Ssam			    ireq->i_len > IEEE80211_NWID_LEN) {
841116742Ssam				error = EINVAL;
842116742Ssam				break;
843116742Ssam			}
844116742Ssam			error = copyin(ireq->i_data, tmpssid, ireq->i_len);
845116742Ssam			if (error)
846116742Ssam				break;
847116742Ssam			memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN);
848116742Ssam			ic->ic_des_esslen = ireq->i_len;
849116742Ssam			memcpy(ic->ic_des_essid, tmpssid, ireq->i_len);
850116742Ssam			error = ENETRESET;
851116742Ssam			break;
852116742Ssam		case IEEE80211_IOC_WEP:
853116742Ssam			/*
854116742Ssam			 * These cards only support one mode so
855116742Ssam			 * we just turn wep on if what ever is
856116742Ssam			 * passed in is not OFF.
857116742Ssam			 */
858116742Ssam			if (ireq->i_val == IEEE80211_WEP_OFF) {
859116742Ssam				ic->ic_flags &= ~IEEE80211_F_WEPON;
860116742Ssam			} else {
861116742Ssam				ic->ic_flags |= IEEE80211_F_WEPON;
862116742Ssam			}
863116742Ssam			error = ENETRESET;
864116742Ssam			break;
865116742Ssam		case IEEE80211_IOC_WEPKEY:
866116742Ssam			if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
867116742Ssam				error = EINVAL;
868116742Ssam				break;
869116742Ssam			}
870116742Ssam			kid = (u_int) ireq->i_val;
871116742Ssam			if (kid >= IEEE80211_WEP_NKID) {
872116742Ssam				error = EINVAL;
873116742Ssam				break;
874116742Ssam			}
875116742Ssam			if (ireq->i_len > sizeof(tmpkey)) {
876116742Ssam				error = EINVAL;
877116742Ssam				break;
878116742Ssam			}
879116742Ssam			memset(tmpkey, 0, sizeof(tmpkey));
880116742Ssam			error = copyin(ireq->i_data, tmpkey, ireq->i_len);
881116742Ssam			if (error)
882116742Ssam				break;
883116742Ssam			memcpy(ic->ic_nw_keys[kid].wk_key, tmpkey,
884116742Ssam				sizeof(tmpkey));
885116742Ssam			ic->ic_nw_keys[kid].wk_len = ireq->i_len;
886116742Ssam			error = ENETRESET;
887116742Ssam			break;
888116742Ssam		case IEEE80211_IOC_WEPTXKEY:
889116742Ssam			kid = (u_int) ireq->i_val;
890116742Ssam			if (kid >= IEEE80211_WEP_NKID) {
891116742Ssam				error = EINVAL;
892116742Ssam				break;
893116742Ssam			}
894116742Ssam			ic->ic_wep_txkey = kid;
895116742Ssam			error = ENETRESET;
896116742Ssam			break;
897116742Ssam#if 0
898116742Ssam		case IEEE80211_IOC_AUTHMODE:
899116742Ssam			sc->wi_authmode = ireq->i_val;
900116742Ssam			break;
901116742Ssam#endif
902116742Ssam		case IEEE80211_IOC_CHANNEL:
903116742Ssam			/* XXX 0xffff overflows 16-bit signed */
904116742Ssam			if (ireq->i_val == 0 ||
905116742Ssam			    ireq->i_val == (int16_t) IEEE80211_CHAN_ANY)
906116742Ssam				ic->ic_des_chan = IEEE80211_CHAN_ANYC;
907116742Ssam			else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX ||
908116742Ssam			    isclr(ic->ic_chan_active, ireq->i_val)) {
909116742Ssam				error = EINVAL;
910116742Ssam				break;
911116742Ssam			} else
912116742Ssam				ic->ic_ibss_chan = ic->ic_des_chan =
913116742Ssam					&ic->ic_channels[ireq->i_val];
914116742Ssam			switch (ic->ic_state) {
915116742Ssam			case IEEE80211_S_INIT:
916116742Ssam			case IEEE80211_S_SCAN:
917116742Ssam				error = ENETRESET;
918116742Ssam				break;
919116742Ssam			default:
920116742Ssam				if (ic->ic_opmode == IEEE80211_M_STA) {
921116742Ssam					if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
922116742Ssam					    ic->ic_bss->ni_chan != ic->ic_des_chan)
923116742Ssam						error = ENETRESET;
924116742Ssam				} else {
925116742Ssam					if (ic->ic_bss->ni_chan != ic->ic_ibss_chan)
926116742Ssam						error = ENETRESET;
927116742Ssam				}
928116742Ssam				break;
929116742Ssam			}
930116742Ssam			break;
931116742Ssam		case IEEE80211_IOC_POWERSAVE:
932116742Ssam			switch (ireq->i_val) {
933116742Ssam			case IEEE80211_POWERSAVE_OFF:
934116742Ssam				if (ic->ic_flags & IEEE80211_F_PMGTON) {
935116742Ssam					ic->ic_flags &= ~IEEE80211_F_PMGTON;
936116742Ssam					error = ENETRESET;
937116742Ssam				}
938116742Ssam				break;
939116742Ssam			case IEEE80211_POWERSAVE_ON:
940116742Ssam				if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
941116742Ssam					error = EINVAL;
942116742Ssam				else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
943116742Ssam					ic->ic_flags |= IEEE80211_F_PMGTON;
944116742Ssam					error = ENETRESET;
945116742Ssam				}
946116742Ssam				break;
947116742Ssam			default:
948116742Ssam				error = EINVAL;
949116742Ssam				break;
950116742Ssam			}
951116742Ssam			break;
952116742Ssam		case IEEE80211_IOC_POWERSAVESLEEP:
953116742Ssam			if (ireq->i_val < 0) {
954116742Ssam				error = EINVAL;
955116742Ssam				break;
956116742Ssam			}
957116742Ssam			ic->ic_lintval = ireq->i_val;
958116742Ssam			error = ENETRESET;
959116742Ssam			break;
960116742Ssam		case IEEE80211_IOCT_RTSTHRESHOLD:
961116742Ssam			if (!(IEEE80211_RTS_MIN < ireq->i_val &&
962116742Ssam			      ireq->i_val < IEEE80211_RTS_MAX)) {
963116742Ssam				error = EINVAL;
964116742Ssam				break;
965116742Ssam			}
966116742Ssam			ic->ic_rtsthreshold = ireq->i_val;
967116742Ssam			error = ENETRESET;
968116742Ssam			break;
969116742Ssam		default:
970116742Ssam			error = EINVAL;
971116742Ssam			break;
972116742Ssam		}
973116742Ssam		break;
974116742Ssam	case SIOCGIFGENERIC:
975116742Ssam		error = ieee80211_cfgget(ifp, cmd, data);
976116742Ssam		break;
977116742Ssam	case SIOCSIFGENERIC:
978116742Ssam		error = suser(curthread);
979116742Ssam		if (error)
980116742Ssam			break;
981116742Ssam		error = ieee80211_cfgset(ifp, cmd, data);
982116742Ssam		break;
983116742Ssam	default:
984116742Ssam		error = ether_ioctl(ifp, cmd, data);
985116742Ssam		break;
986116742Ssam	}
987116742Ssam	return error;
988116742Ssam}
989