xref: /titanic_41/usr/src/uts/common/io/net80211/net80211_ioctl.c (revision 3125ebfc35130d243e775dc38a6a59be4df0b137)
1 /*
2  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  * Alternatively, this software may be distributed under the terms of the
19  * GNU General Public License ("GPL") version 2 as published by the Free
20  * Software Foundation.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #pragma ident	"%Z%%M%	%I%	%E% SMI"
35 
36 #include <sys/param.h>
37 #include <sys/types.h>
38 #include <sys/errno.h>
39 #include <sys/strsun.h>
40 #include <sys/policy.h>
41 #include <inet/common.h>
42 #include <inet/nd.h>
43 #include <inet/mi.h>
44 #include <sys/note.h>
45 #include <sys/mac.h>
46 #include <inet/wifi_ioctl.h>
47 #include "net80211_impl.h"
48 
49 static size_t
50 wifi_strnlen(const char *s, size_t n)
51 {
52 	size_t i;
53 
54 	for (i = 0; i < n && s[i] != '\0'; i++)
55 		/* noop */;
56 	return (i);
57 }
58 
59 /*
60  * Initialize an output message block by copying from an
61  * input message block. The message is of type wldp_t.
62  *    mp     input message block
63  *    buflen length of wldp_buf
64  */
65 static void
66 wifi_setupoutmsg(mblk_t *mp, int buflen)
67 {
68 	wldp_t *wp;
69 
70 	wp = (wldp_t *)mp->b_rptr;
71 	wp->wldp_length = WIFI_BUF_OFFSET + buflen;
72 	wp->wldp_result = WL_SUCCESS;
73 	mp->b_wptr = mp->b_rptr + wp->wldp_length;
74 }
75 
76 /*
77  * Allocate and initialize an output message.
78  */
79 static mblk_t *
80 wifi_getoutmsg(mblk_t *mp, uint32_t cmd, int buflen)
81 {
82 	mblk_t *mp1;
83 	int size;
84 
85 	size = WIFI_BUF_OFFSET;
86 	if (cmd == WLAN_GET_PARAM)
87 		size += buflen;	/* to hold output parameters */
88 	mp1 = allocb(size, BPRI_HI);
89 	if (mp1 == NULL) {
90 		ieee80211_err("wifi_getoutbuf: allocb %d bytes failed!\n",
91 		    size);
92 		return (NULL);
93 	}
94 
95 	bzero(mp1->b_rptr, size);
96 	bcopy(mp->b_rptr, mp1->b_rptr, WIFI_BUF_OFFSET);
97 	wifi_setupoutmsg(mp1, size - WIFI_BUF_OFFSET);
98 
99 	return (mp1);
100 }
101 
102 static int
103 wifi_cfg_essid(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
104 {
105 	mblk_t *omp;
106 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
107 	wldp_t *outp;
108 	wl_essid_t *iw_essid = (wl_essid_t *)inp->wldp_buf;
109 	wl_essid_t *ow_essid;
110 	char *essid;
111 	int err = 0;
112 
113 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_essid_t))) == NULL)
114 		return (ENOMEM);
115 	outp = (wldp_t *)omp->b_rptr;
116 	ow_essid = (wl_essid_t *)outp->wldp_buf;
117 
118 	switch (cmd) {
119 	case WLAN_GET_PARAM:
120 		essid = (char *)ic->ic_des_essid;
121 		if (essid[0] == '\0')
122 			essid = (char *)ic->ic_bss->in_essid;
123 		ow_essid->wl_essid_length = wifi_strnlen((const char *)essid,
124 		    IEEE80211_NWID_LEN);
125 		bcopy(essid, ow_essid->wl_essid_essid,
126 		    ow_essid->wl_essid_length);
127 		break;
128 	case WLAN_SET_PARAM:
129 		if (iw_essid->wl_essid_length > IEEE80211_NWID_LEN) {
130 			ieee80211_err("wifi_cfg_essid: "
131 			    "essid too long, %u, max %u\n",
132 			    iw_essid->wl_essid_length, IEEE80211_NWID_LEN);
133 			outp->wldp_result = WL_NOTSUPPORTED;
134 			err = EINVAL;
135 			break;
136 		}
137 		essid = iw_essid->wl_essid_essid;
138 		essid[IEEE80211_NWID_LEN] = 0;
139 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_essid: "
140 		    "set essid=%s length=%d\n",
141 		    essid, iw_essid->wl_essid_length);
142 
143 		ic->ic_des_esslen = iw_essid->wl_essid_length;
144 		if (ic->ic_des_esslen != 0)
145 			bcopy(essid, ic->ic_des_essid, ic->ic_des_esslen);
146 		if (ic->ic_des_esslen < IEEE80211_NWID_LEN)
147 			ic->ic_des_essid[ic->ic_des_esslen] = 0;
148 		err = ENETRESET;
149 		break;
150 	default:
151 		ieee80211_err("wifi_cfg_essid: unknown command %x\n", cmd);
152 		outp->wldp_result = WL_NOTSUPPORTED;
153 		err = EINVAL;
154 		break;
155 	}
156 
157 	freemsg(*mp);
158 	*mp = omp;
159 	return (err);
160 }
161 
162 static int
163 wifi_cfg_bssid(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
164 {
165 	mblk_t *omp;
166 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
167 	wldp_t *outp;
168 	uint8_t *bssid;
169 	int err = 0;
170 
171 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_bssid_t))) == NULL)
172 		return (ENOMEM);
173 	outp = (wldp_t *)omp->b_rptr;
174 
175 	switch (cmd) {
176 	case  WLAN_GET_PARAM:
177 		if (ic->ic_flags & IEEE80211_F_DESBSSID)
178 			bssid = ic->ic_des_bssid;
179 		else
180 			bssid = ic->ic_bss->in_bssid;
181 		bcopy(bssid, outp->wldp_buf, sizeof (wl_bssid_t));
182 		break;
183 	case WLAN_SET_PARAM:
184 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_bssid: "
185 		    "set bssid=%s\n",
186 		    ieee80211_macaddr_sprintf(inp->wldp_buf));
187 		bcopy(inp->wldp_buf, ic->ic_des_bssid, sizeof (wl_bssid_t));
188 		ic->ic_flags |= IEEE80211_F_DESBSSID;
189 		err = ENETRESET;
190 		break;
191 	default:
192 		ieee80211_err("wifi_cfg_bssid: unknown command %x\n", cmd);
193 		outp->wldp_result = WL_NOTSUPPORTED;
194 		err = EINVAL;
195 		break;
196 	}
197 
198 	freemsg(*mp);
199 	*mp = omp;
200 	return (err);
201 }
202 
203 static int
204 wifi_cfg_nodename(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
205 {
206 	mblk_t *omp;
207 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
208 	wldp_t *outp;
209 	wl_nodename_t *iw_name = (wl_nodename_t *)inp->wldp_buf;
210 	wl_nodename_t *ow_name;
211 	char *nodename;
212 	int len, err;
213 
214 	err = 0;
215 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_nodename_t))) == NULL)
216 		return (ENOMEM);
217 	outp = (wldp_t *)omp->b_rptr;
218 	ow_name = (wl_nodename_t *)outp->wldp_buf;
219 
220 	switch (cmd) {
221 	case WLAN_GET_PARAM:
222 		len = wifi_strnlen((const char *)ic->ic_nickname,
223 		    IEEE80211_NWID_LEN);
224 		ow_name->wl_nodename_length = len;
225 		bcopy(ic->ic_nickname, ow_name->wl_nodename_name, len);
226 		break;
227 	case WLAN_SET_PARAM:
228 		if (iw_name->wl_nodename_length > IEEE80211_NWID_LEN) {
229 			ieee80211_err("wifi_cfg_nodename: "
230 			    "node name too long, %u\n",
231 			    iw_name->wl_nodename_length);
232 			outp->wldp_result = WL_NOTSUPPORTED;
233 			err = EINVAL;
234 			break;
235 		}
236 		nodename = iw_name->wl_nodename_name;
237 		nodename[IEEE80211_NWID_LEN] = 0;
238 		ieee80211_dbg(IEEE80211_MSG_CONFIG,
239 		    "wifi_cfg_nodename: set nodename %s, len=%d\n",
240 		    nodename, iw_name->wl_nodename_length);
241 
242 		len = iw_name->wl_nodename_length;
243 		if (len > 0)
244 			bcopy(nodename, ic->ic_nickname, len);
245 		if (len < IEEE80211_NWID_LEN)
246 			ic->ic_nickname[len] = 0;
247 		break;
248 	default:
249 		ieee80211_err("wifi_cfg_nodename: unknown command %x\n", cmd);
250 		outp->wldp_result = WL_NOTSUPPORTED;
251 		err = EINVAL;
252 		break;
253 	}
254 
255 	freemsg(*mp);
256 	*mp = omp;
257 	return (err);
258 }
259 
260 static int
261 wifi_cfg_phy(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
262 {
263 	mblk_t *omp;
264 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
265 	wldp_t *outp;
266 	wl_phy_conf_t *iw_phy = (wl_phy_conf_t *)inp->wldp_buf;
267 	wl_phy_conf_t *ow_phy;
268 	struct ieee80211_channel *ch = ic->ic_curchan;
269 	int err = 0;
270 
271 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_phy_conf_t))) == NULL)
272 		return (ENOMEM);
273 	outp = (wldp_t *)omp->b_rptr;
274 	ow_phy = (wl_phy_conf_t *)outp->wldp_buf;
275 
276 	switch (cmd) {
277 	case WLAN_GET_PARAM: {
278 		/* get current physical (FH, DS, ERP) parameters */
279 		if (IEEE80211_IS_CHAN_A(ch) || IEEE80211_IS_CHAN_T(ch)) {
280 			wl_ofdm_t *ofdm = (wl_ofdm_t *)ow_phy;
281 
282 			ofdm->wl_ofdm_subtype = WL_OFDM;
283 			ofdm->wl_ofdm_frequency = ch->ich_freq;
284 		} else {
285 			switch (ic->ic_phytype) {
286 			case IEEE80211_T_FH: {
287 				wl_fhss_t *fhss = (wl_fhss_t *)ow_phy;
288 
289 				fhss->wl_fhss_subtype = WL_FHSS;
290 				fhss->wl_fhss_channel =
291 				    ieee80211_chan2ieee(ic, ch);
292 				break;
293 			}
294 			case IEEE80211_T_DS: {
295 				wl_dsss_t *dsss = (wl_dsss_t *)ow_phy;
296 
297 				dsss->wl_dsss_subtype = WL_DSSS;
298 				dsss->wl_dsss_channel =
299 				    ieee80211_chan2ieee(ic, ch);
300 				break;
301 			}
302 			case IEEE80211_T_OFDM: {
303 				wl_erp_t *erp = (wl_erp_t *)ow_phy;
304 
305 				erp->wl_erp_subtype = WL_ERP;
306 				erp->wl_erp_channel =
307 				    ieee80211_chan2ieee(ic, ch);
308 				break;
309 			}
310 			default:
311 				ieee80211_err("wifi_cfg_phy: "
312 				    "unknown phy type, %x\n", ic->ic_phytype);
313 				outp->wldp_result = WL_HW_ERROR;
314 				err = EIO;
315 				break;
316 			} /* switch (ic->ic_phytype) */
317 		}
318 		break;
319 	}
320 
321 	case WLAN_SET_PARAM: {
322 		wl_dsss_t *dsss = (wl_dsss_t *)iw_phy;
323 		int16_t ch = dsss->wl_dsss_channel;
324 
325 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_phy: "
326 		    "set channel=%d\n", ch);
327 		if (ch == 0 || ch == (int16_t)IEEE80211_CHAN_ANY) {
328 			ic->ic_des_chan = IEEE80211_CHAN_ANYC;
329 		} else if ((uint_t)ch > IEEE80211_CHAN_MAX ||
330 		    ieee80211_isclr(ic->ic_chan_active, ch)) {
331 			outp->wldp_result = WL_NOTSUPPORTED;
332 			err = EINVAL;
333 			break;
334 		} else {
335 			ic->ic_des_chan = ic->ic_ibss_chan =
336 			    &ic->ic_sup_channels[ch];
337 		}
338 		switch (ic->ic_state) {
339 		case IEEE80211_S_INIT:
340 		case IEEE80211_S_SCAN:
341 			err = ENETRESET;
342 			break;
343 		default:
344 			/*
345 			 * If the desired channel has changed (to something
346 			 * other than any) and we're not already scanning,
347 			 * then kick the state machine.
348 			 */
349 			if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
350 			    ic->ic_bss->in_chan != ic->ic_des_chan &&
351 			    (ic->ic_flags & IEEE80211_F_SCAN) == 0)
352 				err = ENETRESET;
353 			break;
354 		}
355 		break;
356 	}
357 
358 	default:
359 		ieee80211_err("wifi_cfg_phy: unknown command %x\n", cmd);
360 		outp->wldp_result = WL_NOTSUPPORTED;
361 		err = EINVAL;
362 		break;
363 	} /* switch (cmd) */
364 
365 	freemsg(*mp);
366 	*mp = omp;
367 	return (err);
368 }
369 
370 static int
371 wifi_cfg_wepkey(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
372 {
373 	mblk_t *omp;
374 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
375 	wldp_t *outp;
376 	wl_wep_key_t *iw_wepkey = (wl_wep_key_t *)inp->wldp_buf;
377 	struct ieee80211_key *k;
378 	uint16_t i;
379 	uint32_t klen;
380 	int err = 0;
381 
382 	if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
383 		return (ENOMEM);
384 	outp = (wldp_t *)omp->b_rptr;
385 
386 	switch (cmd) {
387 	case WLAN_GET_PARAM:
388 		outp->wldp_result = WL_WRITEONLY;
389 		err = EINVAL;
390 		break;
391 	case WLAN_SET_PARAM:
392 		if (inp->wldp_length < sizeof (wl_wep_key_tab_t)) {
393 			ieee80211_err("wifi_cfg_wepkey: "
394 			    "parameter too short, %d, expected %d\n",
395 			    inp->wldp_length, sizeof (wl_wep_key_tab_t));
396 			outp->wldp_result = WL_NOTSUPPORTED;
397 			err = EINVAL;
398 			break;
399 		}
400 
401 		/* set all valid keys */
402 		for (i = 0; i < MAX_NWEPKEYS; i++) {
403 			if (iw_wepkey[i].wl_wep_operation != WL_ADD)
404 				continue;
405 			klen = iw_wepkey[i].wl_wep_length;
406 			if (klen > IEEE80211_KEYBUF_SIZE) {
407 				ieee80211_err("wifi_cfg_wepkey: "
408 				    "invalid wepkey length, %u\n", klen);
409 				outp->wldp_result = WL_NOTSUPPORTED;
410 				err = EINVAL;
411 				continue;	/* continue to set other keys */
412 			}
413 			if (klen == 0)
414 				continue;
415 
416 			/*
417 			 * Set key contents. Only WEP is supported
418 			 */
419 			ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_wepkey: "
420 			    "set key %u, len=%u\n", i, klen);
421 			k = &ic->ic_nw_keys[i];
422 			if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP,
423 			    IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, k) == 0) {
424 				ieee80211_err("wifi_cfg_wepkey: "
425 				    "abort, create key failed. id=%u\n", i);
426 				outp->wldp_result = WL_HW_ERROR;
427 				err = EIO;
428 				continue;
429 			}
430 			k->wk_keyix = i;
431 			k->wk_keylen = (uint8_t)klen;
432 			k->wk_flags |= IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
433 			bzero(k->wk_key, IEEE80211_KEYBUF_SIZE);
434 			bcopy(iw_wepkey[i].wl_wep_key, k->wk_key, klen);
435 			if (ieee80211_crypto_setkey(ic, k, ic->ic_macaddr)
436 			    == 0) {
437 				ieee80211_err("wifi_cfg_wepkey: "
438 				    "set key failed len=%u\n", klen);
439 				outp->wldp_result = WL_HW_ERROR;
440 				err = EIO;
441 			}
442 		}
443 		if (err == 0)
444 			err = ENETRESET;
445 		break;
446 	default:
447 		ieee80211_err("wifi_cfg_wepkey: unknown command %x\n", cmd);
448 		outp->wldp_result = WL_NOTSUPPORTED;
449 		err = EINVAL;
450 		break;
451 	}
452 
453 	freemsg(*mp);
454 	*mp = omp;
455 	return (err);
456 }
457 
458 static int
459 wifi_cfg_keyid(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
460 {
461 	mblk_t *omp;
462 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
463 	wldp_t *outp;
464 	wl_wep_key_id_t *iw_kid = (wl_wep_key_id_t *)inp->wldp_buf;
465 	wl_wep_key_id_t *ow_kid;
466 	int err = 0;
467 
468 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_wep_key_id_t))) == NULL)
469 		return (ENOMEM);
470 	outp = (wldp_t *)omp->b_rptr;
471 	ow_kid = (wl_wep_key_id_t *)outp->wldp_buf;
472 
473 	switch (cmd) {
474 	case WLAN_GET_PARAM:
475 		*ow_kid = (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) ?
476 			0 : ic->ic_def_txkey;
477 		break;
478 	case  WLAN_SET_PARAM:
479 		if (*iw_kid >= MAX_NWEPKEYS) {
480 			ieee80211_err("wifi_cfg_keyid: "
481 			    "keyid too large, %u\n", *iw_kid);
482 			outp->wldp_result = WL_NOTSUPPORTED;
483 			err = EINVAL;
484 		} else {
485 			ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_keyid: "
486 			    "set keyid=%u\n", *iw_kid);
487 			ic->ic_def_txkey = *iw_kid;
488 			err = ENETRESET;
489 		}
490 		break;
491 	default:
492 		ieee80211_err("wifi_cfg_keyid: unknown command %x\n", cmd);
493 		outp->wldp_result = WL_NOTSUPPORTED;
494 		err = EINVAL;
495 		break;
496 	}
497 
498 	freemsg(*mp);
499 	*mp = omp;
500 	return (err);
501 }
502 
503 static int
504 wifi_cfg_authmode(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
505 {
506 	mblk_t *omp;
507 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
508 	wldp_t *outp;
509 	wl_authmode_t *iw_auth = (wl_authmode_t *)inp->wldp_buf;
510 	wl_authmode_t *ow_auth;
511 	int err = 0;
512 
513 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_authmode_t))) == NULL)
514 		return (ENOMEM);
515 	outp = (wldp_t *)omp->b_rptr;
516 	ow_auth = (wl_authmode_t *)outp->wldp_buf;
517 
518 	switch (cmd) {
519 	case WLAN_GET_PARAM:
520 		*ow_auth = ic->ic_bss->in_authmode;
521 		break;
522 	case WLAN_SET_PARAM:
523 		if (*iw_auth == ic->ic_bss->in_authmode)
524 			break;
525 
526 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_authmode: "
527 		    "set authmode=%u\n", *iw_auth);
528 		switch (*iw_auth) {
529 		case WL_OPENSYSTEM:
530 		case WL_SHAREDKEY:
531 			ic->ic_bss->in_authmode = *iw_auth;
532 			err = ENETRESET;
533 			break;
534 		default:
535 			ieee80211_err("wifi_cfg_authmode: "
536 			    "unknown authmode %u\n", *iw_auth);
537 			outp->wldp_result = WL_NOTSUPPORTED;
538 			err = EINVAL;
539 			break;
540 		}
541 		break;
542 	default:
543 		ieee80211_err("wifi_cfg_authmode: unknown command %x\n", cmd);
544 		outp->wldp_result = WL_NOTSUPPORTED;
545 		err = EINVAL;
546 		break;
547 	}
548 
549 	freemsg(*mp);
550 	*mp = omp;
551 	return (err);
552 }
553 
554 static int
555 wifi_cfg_encrypt(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
556 {
557 	mblk_t *omp;
558 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
559 	wldp_t *outp;
560 	wl_encryption_t *iw_encryp = (wl_encryption_t *)inp->wldp_buf;
561 	wl_encryption_t *ow_encryp;
562 	uint32_t flags;
563 	int err = 0;
564 
565 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_encryption_t))) == NULL)
566 		return (ENOMEM);
567 	outp = (wldp_t *)omp->b_rptr;
568 	ow_encryp = (wl_encryption_t *)outp->wldp_buf;
569 
570 	switch (cmd) {
571 	case WLAN_GET_PARAM:
572 		*ow_encryp = (ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0;
573 		break;
574 	case WLAN_SET_PARAM:
575 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_encrypt: "
576 		    "set encryption=%u\n", *iw_encryp);
577 		flags = ic->ic_flags;
578 		if (*iw_encryp == WL_NOENCRYPTION)
579 			flags &= ~IEEE80211_F_PRIVACY;
580 		else
581 			flags |= IEEE80211_F_PRIVACY;
582 
583 		if (ic->ic_flags != flags) {
584 			ic->ic_flags = flags;
585 			err = ENETRESET;
586 		}
587 		break;
588 	default:
589 		ieee80211_err("wifi_cfg_encrypt: unknown command %x\n", cmd);
590 		outp->wldp_result = WL_NOTSUPPORTED;
591 		err = EINVAL;
592 		break;
593 	}
594 
595 	freemsg(*mp);
596 	*mp = omp;
597 	return (err);
598 }
599 
600 static int
601 wifi_cfg_bsstype(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
602 {
603 	mblk_t *omp;
604 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
605 	wldp_t *outp;
606 	wl_bss_type_t *iw_opmode = (wl_bss_type_t *)inp->wldp_buf;
607 	wl_bss_type_t *ow_opmode;
608 	int err = 0;
609 
610 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_bss_type_t))) == NULL)
611 		return (ENOMEM);
612 	outp = (wldp_t *)omp->b_rptr;
613 	ow_opmode = (wl_bss_type_t *)outp->wldp_buf;
614 
615 	switch (cmd) {
616 	case WLAN_GET_PARAM:
617 		switch (ic->ic_opmode) {
618 		case IEEE80211_M_STA:
619 			*ow_opmode = WL_BSS_BSS;
620 			break;
621 		case IEEE80211_M_IBSS:
622 			*ow_opmode = WL_BSS_IBSS;
623 			break;
624 		default:
625 			*ow_opmode = WL_BSS_ANY;
626 			break;
627 		}
628 		break;
629 	case  WLAN_SET_PARAM:
630 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_bsstype: "
631 		    "set bsstype=%u\n", *iw_opmode);
632 		switch (*iw_opmode) {
633 		case WL_BSS_BSS:
634 			ic->ic_flags &= ~IEEE80211_F_IBSSON;
635 			ic->ic_opmode = IEEE80211_M_STA;
636 			err = ENETRESET;
637 			break;
638 		case WL_BSS_IBSS:
639 			if ((ic->ic_caps & IEEE80211_C_IBSS) == 0) {
640 				outp->wldp_result = WL_LACK_FEATURE;
641 				err = ENOTSUP;
642 				break;
643 			}
644 
645 			if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) {
646 				ic->ic_flags |= IEEE80211_F_IBSSON;
647 				ic->ic_opmode = IEEE80211_M_IBSS;
648 				err = ENETRESET;
649 			}
650 			break;
651 		default:
652 			ieee80211_err("wifi_cfg_bsstype: "
653 			    "unknown opmode %u\n", *iw_opmode);
654 			outp->wldp_result = WL_NOTSUPPORTED;
655 			err = EINVAL;
656 			break;
657 		}
658 		break;
659 	default:
660 		ieee80211_err("wifi_cfg_bsstype: unknown command %x\n", cmd);
661 		outp->wldp_result = WL_NOTSUPPORTED;
662 		err = EINVAL;
663 		break;
664 	}
665 
666 	freemsg(*mp);
667 	*mp = omp;
668 	return (err);
669 }
670 
671 static int
672 wifi_cfg_linkstatus(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
673 {
674 	mblk_t *omp;
675 	wldp_t *outp;
676 	wl_linkstatus_t *ow_linkstat;
677 	int err = 0;
678 
679 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_linkstatus_t))) == NULL)
680 		return (ENOMEM);
681 	outp = (wldp_t *)omp->b_rptr;
682 	ow_linkstat = (wl_linkstatus_t *)outp->wldp_buf;
683 
684 	switch (cmd) {
685 	case WLAN_GET_PARAM:
686 		*ow_linkstat = (ic->ic_state == IEEE80211_S_RUN) ?
687 		    WL_CONNECTED : WL_NOTCONNECTED;
688 		break;
689 	case WLAN_SET_PARAM:
690 		outp->wldp_result = WL_READONLY;
691 		err = EINVAL;
692 		break;
693 	default:
694 		ieee80211_err("wifi_cfg_linkstatus: unknown command %x\n", cmd);
695 		outp->wldp_result = WL_NOTSUPPORTED;
696 		err = EINVAL;
697 		break;
698 	}
699 
700 	freemsg(*mp);
701 	*mp = omp;
702 	return (err);
703 }
704 
705 static int
706 wifi_cfg_suprates(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
707 {
708 	mblk_t *omp;
709 	wldp_t *outp;
710 	wl_rates_t *ow_rates;
711 	const struct ieee80211_rateset *srs;
712 	uint8_t srates, *drates;
713 	int err, buflen, i, j, k, l;
714 
715 	err = 0;
716 	/* rate value (wl_rates_rates) is of type char */
717 	buflen = offsetof(wl_rates_t, wl_rates_rates) +
718 		sizeof (char) * IEEE80211_MODE_MAX * IEEE80211_RATE_MAXSIZE;
719 	if ((omp = wifi_getoutmsg(*mp, cmd, buflen)) == NULL)
720 		return (ENOMEM);
721 	outp = (wldp_t *)omp->b_rptr;
722 	ow_rates = (wl_rates_t *)outp->wldp_buf;
723 
724 	switch (cmd) {
725 	case WLAN_GET_PARAM:
726 		/* all rates supported by the device */
727 		ow_rates->wl_rates_num = 0;
728 		drates = (uint8_t *)ow_rates->wl_rates_rates;
729 		for (i = 0; i < IEEE80211_MODE_MAX; i++) {
730 			srs = &ic->ic_sup_rates[i];
731 			if (srs->ir_nrates == 0)
732 				continue;
733 
734 			for (j = 0; j < srs->ir_nrates; j++) {
735 				srates = IEEE80211_RV(srs->ir_rates[j]);
736 				/* sort and skip duplicated rates */
737 				for (k = 0; k < ow_rates->wl_rates_num; k++) {
738 					if (srates <= drates[k])
739 						break;
740 				}
741 				if (srates == drates[k])
742 					continue;	/* duplicate, skip */
743 				/* sort */
744 				for (l = ow_rates->wl_rates_num; l > k; l--)
745 					drates[l] = drates[l-1];
746 				drates[k] = srates;
747 				ow_rates->wl_rates_num++;
748 			}
749 		}
750 		break;
751 	case WLAN_SET_PARAM:
752 		outp->wldp_result = WL_READONLY;
753 		err = EINVAL;
754 		break;
755 	default:
756 		ieee80211_err("wifi_cfg_suprates: unknown command %x\n", cmd);
757 		outp->wldp_result = WL_NOTSUPPORTED;
758 		err = EINVAL;
759 		break;
760 	}
761 
762 	freemsg(*mp);
763 	*mp = omp;
764 	return (err);
765 }
766 
767 static int
768 wifi_cfg_desrates(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
769 {
770 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
771 	wl_rates_t *iw_rates = (wl_rates_t *)inp->wldp_buf;
772 	mblk_t *omp;
773 	wldp_t *outp;
774 	wl_rates_t *ow_rates;
775 	struct ieee80211_node *in = ic->ic_bss;
776 	struct ieee80211_rateset *rs = &in->in_rates;
777 	uint8_t drate, srate;
778 	int err, i, j;
779 	boolean_t found;
780 
781 	err = 0;
782 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_rates_t))) == NULL)
783 		return (ENOMEM);
784 	outp = (wldp_t *)omp->b_rptr;
785 	ow_rates = (wl_rates_t *)outp->wldp_buf;
786 
787 	srate = rs->ir_rates[in->in_txrate] & IEEE80211_RATE_VAL;
788 	switch (cmd) {
789 	case  WLAN_GET_PARAM:
790 		ow_rates->wl_rates_num = 1;
791 		ow_rates->wl_rates_rates[0] =
792 			(ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) ?
793 			srate : ic->ic_fixed_rate;
794 		break;
795 	case  WLAN_SET_PARAM:
796 		drate = iw_rates->wl_rates_rates[0];
797 		if (ic->ic_fixed_rate == drate)
798 			break;
799 
800 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_desrates: "
801 		    "set desired rate=%u\n", drate);
802 
803 		if (drate == 0) {	/* reset */
804 			ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
805 			if (ic->ic_state == IEEE80211_S_RUN) {
806 				IEEE80211_UNLOCK(ic);
807 				ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
808 				IEEE80211_LOCK(ic);
809 			}
810 			break;
811 		}
812 
813 		/*
814 		 * Set desired rate. the desired rate is for data transfer
815 		 * and usually is checked and used when driver changes to
816 		 * RUN state.
817 		 * If the driver is in AUTH | ASSOC | RUN state, desired
818 		 * rate is checked against rates supported by current ESS.
819 		 * If it's supported and current state is AUTH|ASSOC, nothing
820 		 * needs to be doen by driver since the desired rate will
821 		 * be enabled when the device changes to RUN state. And
822 		 * when current state is RUN, Re-associate with the ESS to
823 		 * enable the desired rate.
824 		 */
825 		if (ic->ic_state != IEEE80211_S_INIT &&
826 		    ic->ic_state != IEEE80211_S_SCAN) {
827 			/* check if the rate is supported by current ESS */
828 			for (i = 0; i < rs->ir_nrates; i++) {
829 				if (drate == IEEE80211_RV(rs->ir_rates[i]))
830 					break;
831 			}
832 			if (i < rs->ir_nrates) {	/* supported */
833 				ic->ic_fixed_rate = drate;
834 				if (ic->ic_state == IEEE80211_S_RUN) {
835 					IEEE80211_UNLOCK(ic);
836 					ieee80211_new_state(ic,
837 					    IEEE80211_S_ASSOC, 0);
838 					IEEE80211_LOCK(ic);
839 				}
840 				break;
841 			}
842 		}
843 
844 		/* check the rate is supported by device */
845 		found = B_FALSE;
846 		for (i = 0; i < IEEE80211_MODE_MAX; i++) {
847 			rs = &ic->ic_sup_rates[i];
848 			for (j = 0; j < rs->ir_nrates; j++) {
849 				if (drate == IEEE80211_RV(rs->ir_rates[j])) {
850 					found = B_TRUE;
851 					break;
852 				}
853 			}
854 			if (found)
855 				break;
856 		}
857 		if (!found) {
858 			ieee80211_err("wifi_cfg_desrates: "
859 			    "invalid rate %d\n", drate);
860 			outp->wldp_result = WL_NOTSUPPORTED;
861 			err = EINVAL;
862 			break;
863 		}
864 		ic->ic_fixed_rate = drate;
865 		if (ic->ic_state != IEEE80211_S_SCAN)
866 			err = ENETRESET;	/* restart */
867 		break;
868 	default:
869 		ieee80211_err("wifi_cfg_desrates: unknown command %x\n", cmd);
870 		outp->wldp_result = WL_NOTSUPPORTED;
871 		err = EINVAL;
872 		break;
873 	}
874 
875 	freemsg(*mp);
876 	*mp = omp;
877 	return (err);
878 }
879 
880 /*
881  * Rescale device's RSSI value to (0, 15) as required by WiFi
882  * driver IOCTLs (PSARC/2003/722)
883  */
884 static wl_rssi_t
885 wifi_getrssi(struct ieee80211_node *in)
886 {
887 	struct ieee80211com *ic = in->in_ic;
888 	wl_rssi_t rssi, max_rssi;
889 
890 	rssi = ic->ic_node_getrssi(in);
891 	max_rssi = (ic->ic_maxrssi == 0) ? IEEE80211_MAXRSSI : ic->ic_maxrssi;
892 	if (rssi == 0)
893 		rssi = 0;
894 	else if (rssi >= max_rssi)
895 		rssi = MAX_RSSI;
896 	else
897 		rssi = rssi * MAX_RSSI / max_rssi + 1;
898 
899 	return (rssi);
900 }
901 
902 static int
903 wifi_cfg_rssi(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
904 {
905 	mblk_t *omp;
906 	wldp_t *outp;
907 	wl_rssi_t *ow_rssi;
908 	int err = 0;
909 
910 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_rssi_t))) == NULL)
911 		return (ENOMEM);
912 	outp = (wldp_t *)omp->b_rptr;
913 	ow_rssi = (wl_rssi_t *)outp->wldp_buf;
914 
915 	switch (cmd) {
916 	case  WLAN_GET_PARAM:
917 		*ow_rssi = wifi_getrssi(ic->ic_bss);
918 		break;
919 	case  WLAN_SET_PARAM:
920 		outp->wldp_result = WL_READONLY;
921 		err = EINVAL;
922 		break;
923 	default:
924 		ieee80211_err("wifi_cfg_rssi: unknown command %x\n", cmd);
925 		outp->wldp_result = WL_NOTSUPPORTED;
926 		return (EINVAL);
927 	}
928 
929 	freemsg(*mp);
930 	*mp = omp;
931 	return (err);
932 }
933 
934 /*
935  * maximum scan wait time in second.
936  * Time spent on scaning one channel is usually 100~200ms. The maximum
937  * number of channels defined in wifi_ioctl.h is 99 (MAX_CHANNEL_NUM).
938  * As a result the maximum total scan time is defined as below in ms.
939  */
940 #define	WAIT_SCAN_MAX	(200 * MAX_CHANNEL_NUM)
941 
942 static void
943 wifi_wait_scan(struct ieee80211com *ic)
944 {
945 	ieee80211_impl_t *im = ic->ic_private;
946 
947 	while ((ic->ic_flags & (IEEE80211_F_SCAN | IEEE80211_F_ASCAN)) != 0) {
948 		if (cv_timedwait_sig(&im->im_scan_cv, &ic->ic_genlock,
949 		    ddi_get_lbolt() + drv_usectohz(WAIT_SCAN_MAX * 1000)) !=
950 		    0) {
951 			break;
952 		}
953 	}
954 }
955 
956 #define	WIFI_HAVE_CAP(in, flag)	(((in)->in_capinfo & (flag)) ? 1 : 0)
957 
958 /*
959  * Callback function used by ieee80211_iterate_nodes() in
960  * wifi_cfg_esslist() to get info of each node in a node table
961  *    arg  output buffer, pointer to wl_ess_list_t
962  *    in   each node in the node table
963  */
964 static void
965 wifi_read_ap(void *arg, struct ieee80211_node *in)
966 {
967 	wl_ess_list_t *aps = arg;
968 	ieee80211com_t *ic = in->in_ic;
969 	struct ieee80211_channel *chan = in->in_chan;
970 	struct ieee80211_rateset *rates = &(in->in_rates);
971 	wl_ess_conf_t *conf;
972 	uint8_t *end;
973 	uint_t i, nrates;
974 
975 	end = (uint8_t *)aps - WIFI_BUF_OFFSET + MAX_BUF_LEN -
976 		sizeof (wl_ess_list_t);
977 	conf = &aps->wl_ess_list_ess[aps->wl_ess_list_num];
978 	if ((uint8_t *)conf > end)
979 		return;
980 
981 	/* skip newly allocated NULL bss node */
982 	if (IEEE80211_ADDR_EQ(in->in_macaddr, ic->ic_macaddr))
983 		return;
984 
985 	conf->wl_ess_conf_essid.wl_essid_length = in->in_esslen;
986 	bcopy(in->in_essid, conf->wl_ess_conf_essid.wl_essid_essid,
987 	    in->in_esslen);
988 	bcopy(in->in_bssid, conf->wl_ess_conf_bssid, IEEE80211_ADDR_LEN);
989 	conf->wl_ess_conf_wepenabled =
990 	    (in->in_capinfo & IEEE80211_CAPINFO_PRIVACY ?
991 	    WL_ENC_WEP : WL_NOENCRYPTION);
992 	conf->wl_ess_conf_bsstype =
993 	    (in->in_capinfo & IEEE80211_CAPINFO_ESS ?
994 	    WL_BSS_BSS : WL_BSS_IBSS);
995 	conf->wl_ess_conf_sl = wifi_getrssi(in);
996 
997 	/* physical (FH, DS, ERP) parameters */
998 	if (IEEE80211_IS_CHAN_A(chan) || IEEE80211_IS_CHAN_T(chan)) {
999 		wl_ofdm_t *ofdm =
1000 		    (wl_ofdm_t *)&((conf->wl_phy_conf).wl_phy_ofdm_conf);
1001 		ofdm->wl_ofdm_subtype = WL_OFDM;
1002 		ofdm->wl_ofdm_frequency = chan->ich_freq;
1003 	} else {
1004 		switch (in->in_phytype) {
1005 		case IEEE80211_T_FH: {
1006 			wl_fhss_t *fhss = (wl_fhss_t *)
1007 			    &((conf->wl_phy_conf).wl_phy_fhss_conf);
1008 
1009 			fhss->wl_fhss_subtype = WL_FHSS;
1010 			fhss->wl_fhss_channel = ieee80211_chan2ieee(ic, chan);
1011 			fhss->wl_fhss_dwelltime = in->in_fhdwell;
1012 			break;
1013 		}
1014 		case IEEE80211_T_DS: {
1015 			wl_dsss_t *dsss = (wl_dsss_t *)
1016 			    &((conf->wl_phy_conf).wl_phy_dsss_conf);
1017 
1018 			dsss->wl_dsss_subtype = WL_DSSS;
1019 			dsss->wl_dsss_channel = ieee80211_chan2ieee(ic, chan);
1020 			dsss->wl_dsss_have_short_preamble = WIFI_HAVE_CAP(in,
1021 			    IEEE80211_CAPINFO_SHORT_PREAMBLE);
1022 			dsss->wl_dsss_agility_enabled = WIFI_HAVE_CAP(in,
1023 			    IEEE80211_CAPINFO_CHNL_AGILITY);
1024 			dsss->wl_dsss_have_pbcc = dsss->wl_dsss_pbcc_enable =
1025 			    WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_PBCC);
1026 			break;
1027 		}
1028 		case IEEE80211_T_OFDM: {
1029 			wl_erp_t *erp = (wl_erp_t *)
1030 			    &((conf->wl_phy_conf).wl_phy_erp_conf);
1031 
1032 			erp->wl_erp_subtype = WL_ERP;
1033 			erp->wl_erp_channel = ieee80211_chan2ieee(ic, chan);
1034 			erp->wl_erp_have_short_preamble = WIFI_HAVE_CAP(in,
1035 			    IEEE80211_CAPINFO_SHORT_PREAMBLE);
1036 			erp->wl_erp_have_agility = erp->wl_erp_agility_enabled =
1037 			    WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_CHNL_AGILITY);
1038 			erp->wl_erp_have_pbcc = erp->wl_erp_pbcc_enabled =
1039 			    WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_PBCC);
1040 			erp->wl_erp_dsss_ofdm_enabled =
1041 			    WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_DSSSOFDM);
1042 			erp->wl_erp_sst_enabled = WIFI_HAVE_CAP(in,
1043 			    IEEE80211_CAPINFO_SHORT_SLOTTIME);
1044 			break;
1045 		} /* case IEEE80211_T_OFDM */
1046 		} /* switch in->in_phytype */
1047 	}
1048 
1049 	/* supported rates */
1050 	nrates = MIN(rates->ir_nrates, MAX_SCAN_SUPPORT_RATES);
1051 	/*
1052 	 * The number of supported rates might exceed
1053 	 * MAX_SCAN_SUPPORT_RATES. Fill in highest rates
1054 	 * first so userland command could properly show
1055 	 * maximum speed of AP
1056 	 */
1057 	for (i = 0; i < nrates; i++) {
1058 		conf->wl_supported_rates[i] =
1059 		    rates->ir_rates[rates->ir_nrates - i - 1];
1060 	}
1061 
1062 	aps->wl_ess_list_num++;
1063 }
1064 
1065 static int
1066 wifi_cfg_esslist(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1067 {
1068 	mblk_t *omp;
1069 	wldp_t *outp;
1070 	wl_ess_list_t *ow_aps;
1071 	int err = 0;
1072 
1073 	if ((omp = wifi_getoutmsg(*mp, cmd, MAX_BUF_LEN - WIFI_BUF_OFFSET)) ==
1074 	    NULL) {
1075 		return (ENOMEM);
1076 	}
1077 	outp = (wldp_t *)omp->b_rptr;
1078 	ow_aps = (wl_ess_list_t *)outp->wldp_buf;
1079 
1080 	switch (cmd) {
1081 	case WLAN_GET_PARAM:
1082 		ow_aps->wl_ess_list_num = 0;
1083 		ieee80211_iterate_nodes(&ic->ic_scan, wifi_read_ap, ow_aps);
1084 		outp->wldp_length = WIFI_BUF_OFFSET +
1085 		    offsetof(wl_ess_list_t, wl_ess_list_ess) +
1086 		    ow_aps->wl_ess_list_num * sizeof (wl_ess_conf_t);
1087 		omp->b_wptr = omp->b_rptr + outp->wldp_length;
1088 		break;
1089 	case WLAN_SET_PARAM:
1090 		outp->wldp_result = WL_READONLY;
1091 		err = EINVAL;
1092 		break;
1093 	default:
1094 		ieee80211_err("wifi_cfg_esslist: unknown command %x\n", cmd);
1095 		outp->wldp_result = WL_NOTSUPPORTED;
1096 		err = EINVAL;
1097 		break;
1098 	}
1099 
1100 	freemsg(*mp);
1101 	*mp = omp;
1102 	return (err);
1103 }
1104 
1105 /*
1106  * Scan the network for all available ESSs.
1107  * IEEE80211_F_SCANONLY is set when current state is INIT. And
1108  * with this flag, after scan the state will be changed back to
1109  * INIT. The reason is at the end of SCAN stage, the STA will
1110  * consequently connect to an AP. Then it looks unreasonable that
1111  * for a disconnected device, A SCAN command causes it connected.
1112  * So the state is changed back to INIT.
1113  */
1114 static int
1115 wifi_cmd_scan(struct ieee80211com *ic, mblk_t *mp)
1116 {
1117 	int ostate = ic->ic_state;
1118 
1119 	/*
1120 	 * Do not scan when current state is RUN. The reason is
1121 	 * when connected, STA is on the same channel as AP. But
1122 	 * to do scan, STA have to switch to each available channel,
1123 	 * send probe request and wait certian time for probe
1124 	 * response/beacon. Then when the STA switches to a channel
1125 	 * different than AP's, as a result it cannot send/receive
1126 	 * data packets to/from the connected WLAN. This eventually
1127 	 * will cause data loss.
1128 	 */
1129 	if (ostate == IEEE80211_S_RUN)
1130 		return (0);
1131 
1132 	IEEE80211_UNLOCK(ic);
1133 	ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
1134 	IEEE80211_LOCK(ic);
1135 	if (ostate == IEEE80211_S_INIT)
1136 		ic->ic_flags |= IEEE80211_F_SCANONLY;
1137 
1138 	/* wait scan complete */
1139 	wifi_wait_scan(ic);
1140 
1141 	wifi_setupoutmsg(mp, 0);
1142 	return (0);
1143 }
1144 
1145 static void
1146 wifi_loaddefdata(struct ieee80211com *ic)
1147 {
1148 	struct ieee80211_node *in = ic->ic_bss;
1149 	int i;
1150 
1151 	ic->ic_des_esslen = 0;
1152 	bzero(ic->ic_des_essid, IEEE80211_NWID_LEN);
1153 	ic->ic_flags &= ~IEEE80211_F_DESBSSID;
1154 	bzero(ic->ic_des_bssid, IEEE80211_ADDR_LEN);
1155 	bzero(ic->ic_bss->in_bssid, IEEE80211_ADDR_LEN);
1156 	ic->ic_des_chan = IEEE80211_CHAN_ANYC;
1157 	ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
1158 	bzero(ic->ic_nickname, IEEE80211_NWID_LEN);
1159 	in->in_authmode = IEEE80211_AUTH_OPEN;
1160 	ic->ic_flags &= ~IEEE80211_F_PRIVACY;
1161 	ic->ic_def_txkey = 0;
1162 	for (i = 0; i < MAX_NWEPKEYS; i++) {
1163 		ic->ic_nw_keys[i].wk_keylen = 0;
1164 		bzero(ic->ic_nw_keys[i].wk_key, IEEE80211_KEYBUF_SIZE);
1165 	}
1166 	ic->ic_curmode = IEEE80211_MODE_AUTO;
1167 }
1168 
1169 static int
1170 wifi_cmd_loaddefaults(struct ieee80211com *ic, mblk_t *mp)
1171 {
1172 	wifi_loaddefdata(ic);
1173 	wifi_setupoutmsg(mp, 0);
1174 	return (ENETRESET);
1175 }
1176 
1177 static int
1178 wifi_cmd_disassoc(struct ieee80211com *ic, mblk_t *mp)
1179 {
1180 	if (ic->ic_state != IEEE80211_S_INIT) {
1181 		IEEE80211_UNLOCK(ic);
1182 		(void) ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
1183 		IEEE80211_LOCK(ic);
1184 	}
1185 	wifi_loaddefdata(ic);
1186 	wifi_setupoutmsg(mp, 0);
1187 	return (0);
1188 }
1189 
1190 static int
1191 wifi_cfg_getset(struct ieee80211com *ic, mblk_t **mp, uint32_t cmd)
1192 {
1193 	mblk_t *mp1 = *mp;
1194 	wldp_t *wp = (wldp_t *)mp1->b_rptr;
1195 	int err = 0;
1196 
1197 	ASSERT(ic != NULL && mp1 != NULL);
1198 	IEEE80211_LOCK_ASSERT(ic);
1199 	if (MBLKL(mp1) < WIFI_BUF_OFFSET) {
1200 		ieee80211_err("wifi_cfg_getset: "
1201 		    "invalid input buffer, size=%d\n", MBLKL(mp1));
1202 		return (EINVAL);
1203 	}
1204 
1205 	switch (wp->wldp_id) {
1206 	/* Commands */
1207 	case WL_SCAN:
1208 		err = wifi_cmd_scan(ic, mp1);
1209 		break;
1210 	case WL_LOAD_DEFAULTS:
1211 		err = wifi_cmd_loaddefaults(ic, mp1);
1212 		break;
1213 	case WL_DISASSOCIATE:
1214 		err = wifi_cmd_disassoc(ic, mp1);
1215 		break;
1216 	/* Parameters */
1217 	case WL_ESSID:
1218 		err = wifi_cfg_essid(ic, cmd, mp);
1219 		break;
1220 	case WL_BSSID:
1221 		err = wifi_cfg_bssid(ic, cmd, mp);
1222 		break;
1223 	case WL_NODE_NAME:
1224 		err = wifi_cfg_nodename(ic, cmd, mp);
1225 		break;
1226 	case WL_PHY_CONFIG:
1227 		err = wifi_cfg_phy(ic, cmd, mp);
1228 		break;
1229 	case WL_WEP_KEY_TAB:
1230 		err = wifi_cfg_wepkey(ic, cmd, mp);
1231 		break;
1232 	case WL_WEP_KEY_ID:
1233 		err = wifi_cfg_keyid(ic, cmd, mp);
1234 		break;
1235 	case WL_AUTH_MODE:
1236 		err = wifi_cfg_authmode(ic, cmd, mp);
1237 		break;
1238 	case WL_ENCRYPTION:
1239 		err = wifi_cfg_encrypt(ic, cmd, mp);
1240 		break;
1241 	case WL_BSS_TYPE:
1242 		err = wifi_cfg_bsstype(ic, cmd, mp);
1243 		break;
1244 	case WL_DESIRED_RATES:
1245 		err = wifi_cfg_desrates(ic, cmd, mp);
1246 		break;
1247 	case WL_LINKSTATUS:
1248 		err = wifi_cfg_linkstatus(ic, cmd, mp);
1249 		break;
1250 	case WL_ESS_LIST:
1251 		err = wifi_cfg_esslist(ic, cmd, mp);
1252 		break;
1253 	case WL_SUPPORTED_RATES:
1254 		err = wifi_cfg_suprates(ic, cmd, mp);
1255 		break;
1256 	case WL_RSSI:
1257 		err = wifi_cfg_rssi(ic, cmd, mp);
1258 		break;
1259 	default:
1260 		wifi_setupoutmsg(mp1, 0);
1261 		wp->wldp_result = WL_LACK_FEATURE;
1262 		err = ENOTSUP;
1263 		break;
1264 	}
1265 
1266 	return (err);
1267 }
1268 
1269 /*
1270  * Typically invoked by drivers in response to requests for
1271  * information or to change settings from the userland.
1272  *
1273  * Return value should be checked by WiFi drivers. Return 0
1274  * on success. Otherwise, return non-zero value to indicate
1275  * the error. Driver should operate as below when the return
1276  * error is:
1277  * ENETRESET	Reset wireless network and re-start to join a
1278  *		WLAN. ENETRESET is returned when a configuration
1279  *		parameter has been changed.
1280  *		When acknowledge a M_IOCTL message, thie error
1281  *		is ignored.
1282  */
1283 int
1284 ieee80211_ioctl(struct ieee80211com *ic, queue_t *wq, mblk_t *mp)
1285 {
1286 	struct iocblk *iocp;
1287 	int32_t cmd, err, len;
1288 	boolean_t need_privilege;
1289 	mblk_t *mp1;
1290 
1291 	if (MBLKL(mp) < sizeof (struct iocblk)) {
1292 		ieee80211_err("ieee80211_ioctl: ioctl buffer too short, %u\n",
1293 		    MBLKL(mp));
1294 		miocnak(wq, mp, 0, EINVAL);
1295 		return (EINVAL);
1296 	}
1297 
1298 	/*
1299 	 * Validate the command
1300 	 */
1301 	iocp = (struct iocblk *)mp->b_rptr;
1302 	iocp->ioc_error = 0;
1303 	cmd = iocp->ioc_cmd;
1304 	need_privilege = B_TRUE;
1305 	switch (cmd) {
1306 	case WLAN_SET_PARAM:
1307 	case WLAN_COMMAND:
1308 		break;
1309 	case WLAN_GET_PARAM:
1310 		need_privilege = B_FALSE;
1311 		break;
1312 	default:
1313 		ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_ioctl(): "
1314 		    "unknown cmd 0x%x\n", cmd);
1315 		miocnak(wq, mp, 0, EINVAL);
1316 		return (EINVAL);
1317 	}
1318 
1319 	if (need_privilege) {
1320 		/*
1321 		 * Check for specific net_config privilege on Solaris 10+.
1322 		 */
1323 		err = secpolicy_net_config(iocp->ioc_cr, B_FALSE);
1324 		if (err != 0) {
1325 			miocnak(wq, mp, 0, err);
1326 			return (err);
1327 		}
1328 	}
1329 
1330 	IEEE80211_LOCK(ic);
1331 
1332 	/* sanity check */
1333 	mp1 = mp->b_cont;
1334 	if (iocp->ioc_count == 0 || iocp->ioc_count < sizeof (wldp_t) ||
1335 	    mp1 == NULL) {
1336 		miocnak(wq, mp, 0, EINVAL);
1337 		IEEE80211_UNLOCK(ic);
1338 		return (EINVAL);
1339 	}
1340 
1341 	/* assuming single data block */
1342 	if (mp1->b_cont != NULL) {
1343 		freemsg(mp1->b_cont);
1344 		mp1->b_cont = NULL;
1345 	}
1346 
1347 	err = wifi_cfg_getset(ic, &mp1, cmd);
1348 	mp->b_cont = mp1;
1349 	IEEE80211_UNLOCK(ic);
1350 
1351 	len = msgdsize(mp1);
1352 	/* ignore ENETRESET when acknowledge the M_IOCTL message */
1353 	if (err == 0 || err == ENETRESET)
1354 		miocack(wq, mp, len, 0);
1355 	else
1356 		miocack(wq, mp, len, err);
1357 
1358 	return (err);
1359 }
1360