1 /*
2  * Copyright 2007 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 		if (ic->ic_flags & IEEE80211_F_WPA)
574 			*ow_encryp = WL_ENC_WPA;
575 		break;
576 	case WLAN_SET_PARAM:
577 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_encrypt: "
578 		    "set encryption=%u\n", *iw_encryp);
579 		flags = ic->ic_flags;
580 		if (*iw_encryp == WL_NOENCRYPTION)
581 			flags &= ~IEEE80211_F_PRIVACY;
582 		else
583 			flags |= IEEE80211_F_PRIVACY;
584 
585 		if (ic->ic_flags != flags) {
586 			ic->ic_flags = flags;
587 			err = ENETRESET;
588 		}
589 		break;
590 	default:
591 		ieee80211_err("wifi_cfg_encrypt: unknown command %x\n", cmd);
592 		outp->wldp_result = WL_NOTSUPPORTED;
593 		err = EINVAL;
594 		break;
595 	}
596 
597 	freemsg(*mp);
598 	*mp = omp;
599 	return (err);
600 }
601 
602 static int
603 wifi_cfg_bsstype(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
604 {
605 	mblk_t *omp;
606 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
607 	wldp_t *outp;
608 	wl_bss_type_t *iw_opmode = (wl_bss_type_t *)inp->wldp_buf;
609 	wl_bss_type_t *ow_opmode;
610 	int err = 0;
611 
612 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_bss_type_t))) == NULL)
613 		return (ENOMEM);
614 	outp = (wldp_t *)omp->b_rptr;
615 	ow_opmode = (wl_bss_type_t *)outp->wldp_buf;
616 
617 	switch (cmd) {
618 	case WLAN_GET_PARAM:
619 		switch (ic->ic_opmode) {
620 		case IEEE80211_M_STA:
621 			*ow_opmode = WL_BSS_BSS;
622 			break;
623 		case IEEE80211_M_IBSS:
624 			*ow_opmode = WL_BSS_IBSS;
625 			break;
626 		default:
627 			*ow_opmode = WL_BSS_ANY;
628 			break;
629 		}
630 		break;
631 	case  WLAN_SET_PARAM:
632 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_bsstype: "
633 		    "set bsstype=%u\n", *iw_opmode);
634 		switch (*iw_opmode) {
635 		case WL_BSS_BSS:
636 			ic->ic_flags &= ~IEEE80211_F_IBSSON;
637 			ic->ic_opmode = IEEE80211_M_STA;
638 			err = ENETRESET;
639 			break;
640 		case WL_BSS_IBSS:
641 			if ((ic->ic_caps & IEEE80211_C_IBSS) == 0) {
642 				outp->wldp_result = WL_LACK_FEATURE;
643 				err = ENOTSUP;
644 				break;
645 			}
646 
647 			if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) {
648 				ic->ic_flags |= IEEE80211_F_IBSSON;
649 				ic->ic_opmode = IEEE80211_M_IBSS;
650 				err = ENETRESET;
651 			}
652 			break;
653 		default:
654 			ieee80211_err("wifi_cfg_bsstype: "
655 			    "unknown opmode %u\n", *iw_opmode);
656 			outp->wldp_result = WL_NOTSUPPORTED;
657 			err = EINVAL;
658 			break;
659 		}
660 		break;
661 	default:
662 		ieee80211_err("wifi_cfg_bsstype: unknown command %x\n", cmd);
663 		outp->wldp_result = WL_NOTSUPPORTED;
664 		err = EINVAL;
665 		break;
666 	}
667 
668 	freemsg(*mp);
669 	*mp = omp;
670 	return (err);
671 }
672 
673 static int
674 wifi_cfg_linkstatus(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
675 {
676 	mblk_t *omp;
677 	wldp_t *outp;
678 	wl_linkstatus_t *ow_linkstat;
679 	int err = 0;
680 
681 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_linkstatus_t))) == NULL)
682 		return (ENOMEM);
683 	outp = (wldp_t *)omp->b_rptr;
684 	ow_linkstat = (wl_linkstatus_t *)outp->wldp_buf;
685 
686 	switch (cmd) {
687 	case WLAN_GET_PARAM:
688 		*ow_linkstat = (ic->ic_state == IEEE80211_S_RUN) ?
689 		    WL_CONNECTED : WL_NOTCONNECTED;
690 		if ((ic->ic_flags & IEEE80211_F_WPA) &&
691 		    (ieee80211_crypto_getciphertype(ic) != WIFI_SEC_WPA)) {
692 			*ow_linkstat = WL_NOTCONNECTED;
693 		}
694 		break;
695 	case WLAN_SET_PARAM:
696 		outp->wldp_result = WL_READONLY;
697 		err = EINVAL;
698 		break;
699 	default:
700 		ieee80211_err("wifi_cfg_linkstatus: unknown command %x\n", cmd);
701 		outp->wldp_result = WL_NOTSUPPORTED;
702 		err = EINVAL;
703 		break;
704 	}
705 
706 	freemsg(*mp);
707 	*mp = omp;
708 	return (err);
709 }
710 
711 static int
712 wifi_cfg_suprates(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
713 {
714 	mblk_t *omp;
715 	wldp_t *outp;
716 	wl_rates_t *ow_rates;
717 	const struct ieee80211_rateset *srs;
718 	uint8_t srates, *drates;
719 	int err, buflen, i, j, k, l;
720 
721 	err = 0;
722 	/* rate value (wl_rates_rates) is of type char */
723 	buflen = offsetof(wl_rates_t, wl_rates_rates) +
724 	    sizeof (char) * IEEE80211_MODE_MAX * IEEE80211_RATE_MAXSIZE;
725 	if ((omp = wifi_getoutmsg(*mp, cmd, buflen)) == NULL)
726 		return (ENOMEM);
727 	outp = (wldp_t *)omp->b_rptr;
728 	ow_rates = (wl_rates_t *)outp->wldp_buf;
729 
730 	switch (cmd) {
731 	case WLAN_GET_PARAM:
732 		/* all rates supported by the device */
733 		ow_rates->wl_rates_num = 0;
734 		drates = (uint8_t *)ow_rates->wl_rates_rates;
735 		for (i = 0; i < IEEE80211_MODE_MAX; i++) {
736 			srs = &ic->ic_sup_rates[i];
737 			if (srs->ir_nrates == 0)
738 				continue;
739 
740 			for (j = 0; j < srs->ir_nrates; j++) {
741 				srates = IEEE80211_RV(srs->ir_rates[j]);
742 				/* sort and skip duplicated rates */
743 				for (k = 0; k < ow_rates->wl_rates_num; k++) {
744 					if (srates <= drates[k])
745 						break;
746 				}
747 				if (srates == drates[k])
748 					continue;	/* duplicate, skip */
749 				/* sort */
750 				for (l = ow_rates->wl_rates_num; l > k; l--)
751 					drates[l] = drates[l-1];
752 				drates[k] = srates;
753 				ow_rates->wl_rates_num++;
754 			}
755 		}
756 		break;
757 	case WLAN_SET_PARAM:
758 		outp->wldp_result = WL_READONLY;
759 		err = EINVAL;
760 		break;
761 	default:
762 		ieee80211_err("wifi_cfg_suprates: unknown command %x\n", cmd);
763 		outp->wldp_result = WL_NOTSUPPORTED;
764 		err = EINVAL;
765 		break;
766 	}
767 
768 	freemsg(*mp);
769 	*mp = omp;
770 	return (err);
771 }
772 
773 static int
774 wifi_cfg_desrates(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
775 {
776 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
777 	wl_rates_t *iw_rates = (wl_rates_t *)inp->wldp_buf;
778 	mblk_t *omp;
779 	wldp_t *outp;
780 	wl_rates_t *ow_rates;
781 	struct ieee80211_node *in = ic->ic_bss;
782 	struct ieee80211_rateset *rs = &in->in_rates;
783 	uint8_t drate, srate;
784 	int err, i, j;
785 	boolean_t found;
786 
787 	err = 0;
788 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_rates_t))) == NULL)
789 		return (ENOMEM);
790 	outp = (wldp_t *)omp->b_rptr;
791 	ow_rates = (wl_rates_t *)outp->wldp_buf;
792 
793 	srate = rs->ir_rates[in->in_txrate] & IEEE80211_RATE_VAL;
794 	switch (cmd) {
795 	case  WLAN_GET_PARAM:
796 		ow_rates->wl_rates_num = 1;
797 		ow_rates->wl_rates_rates[0] =
798 		    (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) ?
799 		    srate : ic->ic_fixed_rate;
800 		break;
801 	case  WLAN_SET_PARAM:
802 		drate = iw_rates->wl_rates_rates[0];
803 		if (ic->ic_fixed_rate == drate)
804 			break;
805 
806 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_desrates: "
807 		    "set desired rate=%u\n", drate);
808 
809 		if (drate == 0) {	/* reset */
810 			ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
811 			if (ic->ic_state == IEEE80211_S_RUN) {
812 				IEEE80211_UNLOCK(ic);
813 				ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
814 				IEEE80211_LOCK(ic);
815 			}
816 			break;
817 		}
818 
819 		/*
820 		 * Set desired rate. the desired rate is for data transfer
821 		 * and usually is checked and used when driver changes to
822 		 * RUN state.
823 		 * If the driver is in AUTH | ASSOC | RUN state, desired
824 		 * rate is checked against rates supported by current ESS.
825 		 * If it's supported and current state is AUTH|ASSOC, nothing
826 		 * needs to be doen by driver since the desired rate will
827 		 * be enabled when the device changes to RUN state. And
828 		 * when current state is RUN, Re-associate with the ESS to
829 		 * enable the desired rate.
830 		 */
831 		if (ic->ic_state != IEEE80211_S_INIT &&
832 		    ic->ic_state != IEEE80211_S_SCAN) {
833 			/* check if the rate is supported by current ESS */
834 			for (i = 0; i < rs->ir_nrates; i++) {
835 				if (drate == IEEE80211_RV(rs->ir_rates[i]))
836 					break;
837 			}
838 			if (i < rs->ir_nrates) {	/* supported */
839 				ic->ic_fixed_rate = drate;
840 				if (ic->ic_state == IEEE80211_S_RUN) {
841 					IEEE80211_UNLOCK(ic);
842 					ieee80211_new_state(ic,
843 					    IEEE80211_S_ASSOC, 0);
844 					IEEE80211_LOCK(ic);
845 				}
846 				break;
847 			}
848 		}
849 
850 		/* check the rate is supported by device */
851 		found = B_FALSE;
852 		for (i = 0; i < IEEE80211_MODE_MAX; i++) {
853 			rs = &ic->ic_sup_rates[i];
854 			for (j = 0; j < rs->ir_nrates; j++) {
855 				if (drate == IEEE80211_RV(rs->ir_rates[j])) {
856 					found = B_TRUE;
857 					break;
858 				}
859 			}
860 			if (found)
861 				break;
862 		}
863 		if (!found) {
864 			ieee80211_err("wifi_cfg_desrates: "
865 			    "invalid rate %d\n", drate);
866 			outp->wldp_result = WL_NOTSUPPORTED;
867 			err = EINVAL;
868 			break;
869 		}
870 		ic->ic_fixed_rate = drate;
871 		if (ic->ic_state != IEEE80211_S_SCAN)
872 			err = ENETRESET;	/* restart */
873 		break;
874 	default:
875 		ieee80211_err("wifi_cfg_desrates: unknown command %x\n", cmd);
876 		outp->wldp_result = WL_NOTSUPPORTED;
877 		err = EINVAL;
878 		break;
879 	}
880 
881 	freemsg(*mp);
882 	*mp = omp;
883 	return (err);
884 }
885 
886 /*
887  * Rescale device's RSSI value to (0, 15) as required by WiFi
888  * driver IOCTLs (PSARC/2003/722)
889  */
890 static wl_rssi_t
891 wifi_getrssi(struct ieee80211_node *in)
892 {
893 	struct ieee80211com *ic = in->in_ic;
894 	wl_rssi_t rssi, max_rssi;
895 
896 	rssi = ic->ic_node_getrssi(in);
897 	max_rssi = (ic->ic_maxrssi == 0) ? IEEE80211_MAXRSSI : ic->ic_maxrssi;
898 	if (rssi == 0)
899 		rssi = 0;
900 	else if (rssi >= max_rssi)
901 		rssi = MAX_RSSI;
902 	else
903 		rssi = rssi * MAX_RSSI / max_rssi + 1;
904 
905 	return (rssi);
906 }
907 
908 static int
909 wifi_cfg_rssi(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
910 {
911 	mblk_t *omp;
912 	wldp_t *outp;
913 	wl_rssi_t *ow_rssi;
914 	int err = 0;
915 
916 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_rssi_t))) == NULL)
917 		return (ENOMEM);
918 	outp = (wldp_t *)omp->b_rptr;
919 	ow_rssi = (wl_rssi_t *)outp->wldp_buf;
920 
921 	switch (cmd) {
922 	case  WLAN_GET_PARAM:
923 		*ow_rssi = wifi_getrssi(ic->ic_bss);
924 		break;
925 	case  WLAN_SET_PARAM:
926 		outp->wldp_result = WL_READONLY;
927 		err = EINVAL;
928 		break;
929 	default:
930 		ieee80211_err("wifi_cfg_rssi: unknown command %x\n", cmd);
931 		outp->wldp_result = WL_NOTSUPPORTED;
932 		return (EINVAL);
933 	}
934 
935 	freemsg(*mp);
936 	*mp = omp;
937 	return (err);
938 }
939 
940 /*
941  * maximum scan wait time in second.
942  * Time spent on scaning one channel is usually 100~200ms. The maximum
943  * number of channels defined in wifi_ioctl.h is 99 (MAX_CHANNEL_NUM).
944  * As a result the maximum total scan time is defined as below in ms.
945  */
946 #define	WAIT_SCAN_MAX	(200 * MAX_CHANNEL_NUM)
947 
948 static void
949 wifi_wait_scan(struct ieee80211com *ic)
950 {
951 	ieee80211_impl_t *im = ic->ic_private;
952 
953 	while ((ic->ic_flags & (IEEE80211_F_SCAN | IEEE80211_F_ASCAN)) != 0) {
954 		if (cv_timedwait_sig(&im->im_scan_cv, &ic->ic_genlock,
955 		    ddi_get_lbolt() + drv_usectohz(WAIT_SCAN_MAX * 1000)) !=
956 		    0) {
957 			break;
958 		}
959 	}
960 }
961 
962 #define	WIFI_HAVE_CAP(in, flag)	(((in)->in_capinfo & (flag)) ? 1 : 0)
963 
964 /*
965  * Callback function used by ieee80211_iterate_nodes() in
966  * wifi_cfg_esslist() to get info of each node in a node table
967  *    arg  output buffer, pointer to wl_ess_list_t
968  *    in   each node in the node table
969  */
970 static void
971 wifi_read_ap(void *arg, struct ieee80211_node *in)
972 {
973 	wl_ess_list_t *aps = arg;
974 	ieee80211com_t *ic = in->in_ic;
975 	struct ieee80211_channel *chan = in->in_chan;
976 	struct ieee80211_rateset *rates = &(in->in_rates);
977 	wl_ess_conf_t *conf;
978 	uint8_t *end;
979 	uint_t i, nrates;
980 
981 	end = (uint8_t *)aps - WIFI_BUF_OFFSET + MAX_BUF_LEN -
982 	    sizeof (wl_ess_list_t);
983 	conf = &aps->wl_ess_list_ess[aps->wl_ess_list_num];
984 	if ((uint8_t *)conf > end)
985 		return;
986 
987 	/* skip newly allocated NULL bss node */
988 	if (IEEE80211_ADDR_EQ(in->in_macaddr, ic->ic_macaddr))
989 		return;
990 
991 	conf->wl_ess_conf_essid.wl_essid_length = in->in_esslen;
992 	bcopy(in->in_essid, conf->wl_ess_conf_essid.wl_essid_essid,
993 	    in->in_esslen);
994 	bcopy(in->in_bssid, conf->wl_ess_conf_bssid, IEEE80211_ADDR_LEN);
995 	conf->wl_ess_conf_wepenabled =
996 	    (in->in_capinfo & IEEE80211_CAPINFO_PRIVACY ?
997 	    WL_ENC_WEP : WL_NOENCRYPTION);
998 	conf->wl_ess_conf_bsstype =
999 	    (in->in_capinfo & IEEE80211_CAPINFO_ESS ?
1000 	    WL_BSS_BSS : WL_BSS_IBSS);
1001 	conf->wl_ess_conf_sl = wifi_getrssi(in);
1002 	conf->wl_ess_conf_reserved[0] = (in->in_wpa_ie == NULL? 0 : 1);
1003 
1004 	/* physical (FH, DS, ERP) parameters */
1005 	if (IEEE80211_IS_CHAN_A(chan) || IEEE80211_IS_CHAN_T(chan)) {
1006 		wl_ofdm_t *ofdm =
1007 		    (wl_ofdm_t *)&((conf->wl_phy_conf).wl_phy_ofdm_conf);
1008 		ofdm->wl_ofdm_subtype = WL_OFDM;
1009 		ofdm->wl_ofdm_frequency = chan->ich_freq;
1010 	} else {
1011 		switch (in->in_phytype) {
1012 		case IEEE80211_T_FH: {
1013 			wl_fhss_t *fhss = (wl_fhss_t *)
1014 			    &((conf->wl_phy_conf).wl_phy_fhss_conf);
1015 
1016 			fhss->wl_fhss_subtype = WL_FHSS;
1017 			fhss->wl_fhss_channel = ieee80211_chan2ieee(ic, chan);
1018 			fhss->wl_fhss_dwelltime = in->in_fhdwell;
1019 			break;
1020 		}
1021 		case IEEE80211_T_DS: {
1022 			wl_dsss_t *dsss = (wl_dsss_t *)
1023 			    &((conf->wl_phy_conf).wl_phy_dsss_conf);
1024 
1025 			dsss->wl_dsss_subtype = WL_DSSS;
1026 			dsss->wl_dsss_channel = ieee80211_chan2ieee(ic, chan);
1027 			dsss->wl_dsss_have_short_preamble = WIFI_HAVE_CAP(in,
1028 			    IEEE80211_CAPINFO_SHORT_PREAMBLE);
1029 			dsss->wl_dsss_agility_enabled = WIFI_HAVE_CAP(in,
1030 			    IEEE80211_CAPINFO_CHNL_AGILITY);
1031 			dsss->wl_dsss_have_pbcc = dsss->wl_dsss_pbcc_enable =
1032 			    WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_PBCC);
1033 			break;
1034 		}
1035 		case IEEE80211_T_OFDM: {
1036 			wl_erp_t *erp = (wl_erp_t *)
1037 			    &((conf->wl_phy_conf).wl_phy_erp_conf);
1038 
1039 			erp->wl_erp_subtype = WL_ERP;
1040 			erp->wl_erp_channel = ieee80211_chan2ieee(ic, chan);
1041 			erp->wl_erp_have_short_preamble = WIFI_HAVE_CAP(in,
1042 			    IEEE80211_CAPINFO_SHORT_PREAMBLE);
1043 			erp->wl_erp_have_agility = erp->wl_erp_agility_enabled =
1044 			    WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_CHNL_AGILITY);
1045 			erp->wl_erp_have_pbcc = erp->wl_erp_pbcc_enabled =
1046 			    WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_PBCC);
1047 			erp->wl_erp_dsss_ofdm_enabled =
1048 			    WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_DSSSOFDM);
1049 			erp->wl_erp_sst_enabled = WIFI_HAVE_CAP(in,
1050 			    IEEE80211_CAPINFO_SHORT_SLOTTIME);
1051 			break;
1052 		} /* case IEEE80211_T_OFDM */
1053 		} /* switch in->in_phytype */
1054 	}
1055 
1056 	/* supported rates */
1057 	nrates = MIN(rates->ir_nrates, MAX_SCAN_SUPPORT_RATES);
1058 	/*
1059 	 * The number of supported rates might exceed
1060 	 * MAX_SCAN_SUPPORT_RATES. Fill in highest rates
1061 	 * first so userland command could properly show
1062 	 * maximum speed of AP
1063 	 */
1064 	for (i = 0; i < nrates; i++) {
1065 		conf->wl_supported_rates[i] =
1066 		    rates->ir_rates[rates->ir_nrates - i - 1];
1067 	}
1068 
1069 	aps->wl_ess_list_num++;
1070 }
1071 
1072 static int
1073 wifi_cfg_esslist(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1074 {
1075 	mblk_t *omp;
1076 	wldp_t *outp;
1077 	wl_ess_list_t *ow_aps;
1078 	int err = 0;
1079 
1080 	if ((omp = wifi_getoutmsg(*mp, cmd, MAX_BUF_LEN - WIFI_BUF_OFFSET)) ==
1081 	    NULL) {
1082 		return (ENOMEM);
1083 	}
1084 	outp = (wldp_t *)omp->b_rptr;
1085 	ow_aps = (wl_ess_list_t *)outp->wldp_buf;
1086 
1087 	switch (cmd) {
1088 	case WLAN_GET_PARAM:
1089 		ow_aps->wl_ess_list_num = 0;
1090 		ieee80211_iterate_nodes(&ic->ic_scan, wifi_read_ap, ow_aps);
1091 		outp->wldp_length = WIFI_BUF_OFFSET +
1092 		    offsetof(wl_ess_list_t, wl_ess_list_ess) +
1093 		    ow_aps->wl_ess_list_num * sizeof (wl_ess_conf_t);
1094 		omp->b_wptr = omp->b_rptr + outp->wldp_length;
1095 		break;
1096 	case WLAN_SET_PARAM:
1097 		outp->wldp_result = WL_READONLY;
1098 		err = EINVAL;
1099 		break;
1100 	default:
1101 		ieee80211_err("wifi_cfg_esslist: unknown command %x\n", cmd);
1102 		outp->wldp_result = WL_NOTSUPPORTED;
1103 		err = EINVAL;
1104 		break;
1105 	}
1106 
1107 	freemsg(*mp);
1108 	*mp = omp;
1109 	return (err);
1110 }
1111 
1112 /*
1113  * Scan the network for all available ESSs.
1114  * IEEE80211_F_SCANONLY is set when current state is INIT. And
1115  * with this flag, after scan the state will be changed back to
1116  * INIT. The reason is at the end of SCAN stage, the STA will
1117  * consequently connect to an AP. Then it looks unreasonable that
1118  * for a disconnected device, A SCAN command causes it connected.
1119  * So the state is changed back to INIT.
1120  */
1121 static int
1122 wifi_cmd_scan(struct ieee80211com *ic, mblk_t *mp)
1123 {
1124 	int ostate = ic->ic_state;
1125 
1126 	/*
1127 	 * Do not scan when current state is RUN. The reason is
1128 	 * when connected, STA is on the same channel as AP. But
1129 	 * to do scan, STA have to switch to each available channel,
1130 	 * send probe request and wait certian time for probe
1131 	 * response/beacon. Then when the STA switches to a channel
1132 	 * different than AP's, as a result it cannot send/receive
1133 	 * data packets to/from the connected WLAN. This eventually
1134 	 * will cause data loss.
1135 	 */
1136 	if (ostate == IEEE80211_S_RUN)
1137 		return (0);
1138 
1139 	IEEE80211_UNLOCK(ic);
1140 
1141 	ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
1142 	IEEE80211_LOCK(ic);
1143 	if (ostate == IEEE80211_S_INIT)
1144 		ic->ic_flags |= IEEE80211_F_SCANONLY;
1145 
1146 	/* Don't wait on WPA mode */
1147 	if ((ic->ic_flags & IEEE80211_F_WPA) == 0) {
1148 		/* wait scan complete */
1149 		wifi_wait_scan(ic);
1150 	}
1151 
1152 	wifi_setupoutmsg(mp, 0);
1153 	return (0);
1154 }
1155 
1156 static void
1157 wifi_loaddefdata(struct ieee80211com *ic)
1158 {
1159 	struct ieee80211_node *in = ic->ic_bss;
1160 	int i;
1161 
1162 	ic->ic_des_esslen = 0;
1163 	bzero(ic->ic_des_essid, IEEE80211_NWID_LEN);
1164 	ic->ic_flags &= ~IEEE80211_F_DESBSSID;
1165 	bzero(ic->ic_des_bssid, IEEE80211_ADDR_LEN);
1166 	bzero(ic->ic_bss->in_bssid, IEEE80211_ADDR_LEN);
1167 	ic->ic_des_chan = IEEE80211_CHAN_ANYC;
1168 	ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
1169 	bzero(ic->ic_nickname, IEEE80211_NWID_LEN);
1170 	in->in_authmode = IEEE80211_AUTH_OPEN;
1171 	ic->ic_flags &= ~IEEE80211_F_PRIVACY;
1172 	ic->ic_flags &= ~IEEE80211_F_WPA;	/* mask WPA mode */
1173 	ic->ic_evq_head = ic->ic_evq_tail = 0;	/* reset Queue */
1174 	ic->ic_def_txkey = 0;
1175 	for (i = 0; i < MAX_NWEPKEYS; i++) {
1176 		ic->ic_nw_keys[i].wk_keylen = 0;
1177 		bzero(ic->ic_nw_keys[i].wk_key, IEEE80211_KEYBUF_SIZE);
1178 	}
1179 	ic->ic_curmode = IEEE80211_MODE_AUTO;
1180 }
1181 
1182 static int
1183 wifi_cmd_loaddefaults(struct ieee80211com *ic, mblk_t *mp)
1184 {
1185 	wifi_loaddefdata(ic);
1186 	wifi_setupoutmsg(mp, 0);
1187 	return (ENETRESET);
1188 }
1189 
1190 static int
1191 wifi_cmd_disassoc(struct ieee80211com *ic, mblk_t *mp)
1192 {
1193 	if (ic->ic_state != IEEE80211_S_INIT) {
1194 		IEEE80211_UNLOCK(ic);
1195 		(void) ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
1196 		IEEE80211_LOCK(ic);
1197 	}
1198 	wifi_loaddefdata(ic);
1199 	wifi_setupoutmsg(mp, 0);
1200 	return (0);
1201 }
1202 
1203 /*
1204  * Get the capabilities of drivers.
1205  */
1206 static int
1207 wifi_cfg_caps(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1208 {
1209 	mblk_t *omp;
1210 	wldp_t *outp;
1211 	wl_capability_t *o_caps;
1212 	int err = 0;
1213 
1214 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_capability_t))) == NULL)
1215 		return (ENOMEM);
1216 	outp = (wldp_t *)omp->b_rptr;
1217 	o_caps = (wl_capability_t *)outp->wldp_buf;
1218 
1219 	switch (cmd) {
1220 	case WLAN_GET_PARAM:
1221 		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_caps: "
1222 		    "ic_caps = %u\n", ic->ic_caps);
1223 		o_caps->caps = ic->ic_caps;
1224 		break;
1225 	case WLAN_SET_PARAM:
1226 		outp->wldp_result = WL_READONLY;
1227 		err = EINVAL;
1228 		break;
1229 	default:
1230 		ieee80211_err("wifi_cfg_caps: unknown command %x\n", cmd);
1231 		outp->wldp_result = WL_NOTSUPPORTED;
1232 		err = EINVAL;
1233 		break;
1234 	}
1235 
1236 	freemsg(*mp);
1237 	*mp = omp;
1238 	return (err);
1239 }
1240 
1241 /*
1242  * Operating on WPA mode.
1243  */
1244 static int
1245 wifi_cfg_wpa(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1246 {
1247 	mblk_t *omp;
1248 	wldp_t *outp;
1249 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
1250 	wl_wpa_t *wpa = (wl_wpa_t *)inp->wldp_buf;
1251 	wl_wpa_t *o_wpa;
1252 	int err = 0;
1253 
1254 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_wpa_t))) == NULL)
1255 		return (ENOMEM);
1256 	outp = (wldp_t *)omp->b_rptr;
1257 	o_wpa = (wl_wpa_t *)outp->wldp_buf;
1258 
1259 	switch (cmd) {
1260 	case WLAN_GET_PARAM:
1261 		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_wpa: "
1262 		    "get wpa=%u\n", wpa->wpa_flag);
1263 		o_wpa->wpa_flag = ((ic->ic_flags & IEEE80211_F_WPA)? 1 : 0);
1264 		break;
1265 	case WLAN_SET_PARAM:
1266 		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_wpa: "
1267 		    "set wpa=%u\n", wpa->wpa_flag);
1268 		if (wpa->wpa_flag > 0) {	/* enable WPA mode */
1269 			ic->ic_flags |= IEEE80211_F_PRIVACY;
1270 			ic->ic_flags |= IEEE80211_F_WPA;
1271 		} else {
1272 			ic->ic_flags &= ~IEEE80211_F_PRIVACY;
1273 			ic->ic_flags &= ~IEEE80211_F_WPA;
1274 		}
1275 		break;
1276 	default:
1277 		ieee80211_err("wifi_cfg_wpa: unknown command %x\n", cmd);
1278 		outp->wldp_result = WL_NOTSUPPORTED;
1279 		err = EINVAL;
1280 		break;
1281 	}
1282 
1283 	freemsg(*mp);
1284 	*mp = omp;
1285 	return (err);
1286 }
1287 
1288 /*
1289  * WPA daemon set the WPA keys.
1290  * The WPA keys are negotiated with APs through wpa service.
1291  */
1292 static int
1293 wifi_cfg_wpakey(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1294 {
1295 	mblk_t *omp;
1296 	wldp_t *outp;
1297 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
1298 	wl_key_t *ik = (wl_key_t *)(inp->wldp_buf);
1299 	struct ieee80211_node *in;
1300 	struct ieee80211_key *wk;
1301 	uint16_t kid;
1302 	int err = 0;
1303 
1304 	if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
1305 		return (ENOMEM);
1306 	outp = (wldp_t *)omp->b_rptr;
1307 
1308 	switch (cmd) {
1309 	case WLAN_GET_PARAM:
1310 		outp->wldp_result = WL_WRITEONLY;
1311 		err = EINVAL;
1312 		break;
1313 	case WLAN_SET_PARAM:
1314 		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_wpakey: "
1315 		    "idx=%d\n", ik->ik_keyix);
1316 		/* NB: cipher support is verified by ieee80211_crypt_newkey */
1317 		/* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */
1318 		if (ik->ik_keylen > sizeof (ik->ik_keydata)) {
1319 			ieee80211_err("wifi_cfg_wpakey: key too long\n");
1320 			outp->wldp_result = WL_NOTSUPPORTED;
1321 			err = EINVAL;
1322 			break;
1323 		}
1324 		kid = ik->ik_keyix;
1325 		if (kid == IEEE80211_KEYIX_NONE || kid >= IEEE80211_WEP_NKID) {
1326 			ieee80211_err("wifi_cfg_wpakey: incorrect keyix\n");
1327 			outp->wldp_result = WL_NOTSUPPORTED;
1328 			err = EINVAL;
1329 			break;
1330 
1331 		} else {
1332 			wk = &ic->ic_nw_keys[kid];
1333 			/*
1334 			 * Global slots start off w/o any assigned key index.
1335 			 * Force one here for consistency with WEPKEY.
1336 			 */
1337 			if (wk->wk_keyix == IEEE80211_KEYIX_NONE)
1338 				wk->wk_keyix = kid;
1339 			/* in = ic->ic_bss; */
1340 			in = NULL;
1341 		}
1342 
1343 		KEY_UPDATE_BEGIN(ic);
1344 		if (ieee80211_crypto_newkey(ic, ik->ik_type,
1345 		    ik->ik_flags, wk)) {
1346 			wk->wk_keylen = ik->ik_keylen;
1347 			/* NB: MIC presence is implied by cipher type */
1348 			if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE)
1349 				wk->wk_keylen = IEEE80211_KEYBUF_SIZE;
1350 			wk->wk_keyrsc = ik->ik_keyrsc;
1351 			wk->wk_keytsc = 0;		/* new key, reset */
1352 			wk->wk_flags |= ik->ik_flags &
1353 			    (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV);
1354 			(void) memset(wk->wk_key, 0, sizeof (wk->wk_key));
1355 			(void) memcpy(wk->wk_key, ik->ik_keydata,
1356 			    ik->ik_keylen);
1357 			if (!ieee80211_crypto_setkey(ic, wk,
1358 			    in != NULL ? in->in_macaddr : ik->ik_macaddr)) {
1359 				err = EIO;
1360 				outp->wldp_result = WL_HW_ERROR;
1361 			} else if ((ik->ik_flags & IEEE80211_KEY_DEFAULT)) {
1362 				ic->ic_def_txkey = kid;
1363 				ieee80211_mac_update(ic);
1364 			}
1365 		} else {
1366 			err = EIO;
1367 			outp->wldp_result = WL_HW_ERROR;
1368 		}
1369 		KEY_UPDATE_END(ic);
1370 		break;
1371 	default:
1372 		ieee80211_err("wifi_cfg_wpakey: unknown command %x\n", cmd);
1373 		outp->wldp_result = WL_NOTSUPPORTED;
1374 		err = EINVAL;
1375 		break;
1376 	}
1377 
1378 	freemsg(*mp);
1379 	*mp = omp;
1380 	return (err);
1381 }
1382 
1383 /*
1384  * Delete obsolete keys - keys are dynamically exchanged between APs
1385  * and wpa daemon.
1386  */
1387 static int
1388 wifi_cfg_delkey(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1389 {
1390 	mblk_t *omp;
1391 	wldp_t *outp;
1392 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
1393 	wl_del_key_t *dk = (wl_del_key_t *)inp->wldp_buf;
1394 	int kid;
1395 	int err = 0;
1396 
1397 	if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
1398 		return (ENOMEM);
1399 	outp = (wldp_t *)omp->b_rptr;
1400 
1401 	switch (cmd) {
1402 	case WLAN_GET_PARAM:
1403 		outp->wldp_result = WL_WRITEONLY;
1404 		err = EINVAL;
1405 		break;
1406 	case WLAN_SET_PARAM:
1407 		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_delkey: "
1408 		    "keyix=%d\n", dk->idk_keyix);
1409 		kid = dk->idk_keyix;
1410 		if (kid == IEEE80211_KEYIX_NONE || kid >= IEEE80211_WEP_NKID) {
1411 			ieee80211_err("wifi_cfg_delkey: incorrect keyix\n");
1412 			outp->wldp_result = WL_NOTSUPPORTED;
1413 			err = EINVAL;
1414 			break;
1415 
1416 		} else {
1417 			(void) ieee80211_crypto_delkey(ic,
1418 			    &ic->ic_nw_keys[kid]);
1419 			ieee80211_mac_update(ic);
1420 		}
1421 		break;
1422 	default:
1423 		ieee80211_err("wifi_cfg_delkey: unknown command %x\n", cmd);
1424 		outp->wldp_result = WL_NOTSUPPORTED;
1425 		err = EINVAL;
1426 		break;
1427 	}
1428 
1429 	freemsg(*mp);
1430 	*mp = omp;
1431 	return (err);
1432 }
1433 
1434 /*
1435  * The OPTIE will be used in the association request.
1436  */
1437 static int
1438 wifi_cfg_setoptie(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1439 {
1440 	mblk_t *omp;
1441 	wldp_t *outp;
1442 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
1443 	wl_wpa_ie_t *ie_in = (wl_wpa_ie_t *)inp->wldp_buf;
1444 	char *ie;
1445 	int err = 0;
1446 
1447 	if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
1448 		return (ENOMEM);
1449 	outp = (wldp_t *)omp->b_rptr;
1450 
1451 	switch (cmd) {
1452 	case WLAN_GET_PARAM:
1453 		outp->wldp_result = WL_WRITEONLY;
1454 		err = EINVAL;
1455 		break;
1456 	case WLAN_SET_PARAM:
1457 		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_setoptie\n");
1458 		/*
1459 		 * NB: Doing this for ap operation could be useful (e.g. for
1460 		 * WPA and/or WME) except that it typically is worthless
1461 		 * without being able to intervene when processing
1462 		 * association response frames--so disallow it for now.
1463 		 */
1464 		if (ic->ic_opmode != IEEE80211_M_STA) {
1465 			ieee80211_err("wifi_cfg_setoptie: opmode err\n");
1466 			err = EINVAL;
1467 			outp->wldp_result = WL_NOTSUPPORTED;
1468 			break;
1469 		}
1470 		if (ie_in->wpa_ie_len > IEEE80211_MAX_OPT_IE) {
1471 			ieee80211_err("wifi_cfg_setoptie: optie too long\n");
1472 			err = EINVAL;
1473 			outp->wldp_result = WL_NOTSUPPORTED;
1474 			break;
1475 		}
1476 
1477 		ie = ieee80211_malloc(ie_in->wpa_ie_len);
1478 		(void) memcpy(ie, ie_in->wpa_ie, ie_in->wpa_ie_len);
1479 		if (ic->ic_opt_ie != NULL)
1480 			ieee80211_free(ic->ic_opt_ie);
1481 		ic->ic_opt_ie = ie;
1482 		ic->ic_opt_ie_len = ie_in->wpa_ie_len;
1483 		break;
1484 	default:
1485 		ieee80211_err("wifi_cfg_setoptie: unknown command %x\n", cmd);
1486 		outp->wldp_result = WL_NOTSUPPORTED;
1487 		err = EINVAL;
1488 		break;
1489 	}
1490 
1491 	freemsg(*mp);
1492 	*mp = omp;
1493 	return (err);
1494 }
1495 
1496 /*
1497  * To be compatible with drivers/tools of OpenSolaris.org,
1498  * we use a different ID to filter out those APs of WPA mode.
1499  */
1500 static int
1501 wifi_cfg_scanresults(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1502 {
1503 	mblk_t *omp;
1504 	wldp_t *outp;
1505 	wl_wpa_ess_t *sr;
1506 	ieee80211_node_t *in;
1507 	ieee80211_node_table_t *nt;
1508 	int len, ap_num = 0;
1509 	int err = 0;
1510 
1511 	if ((omp = wifi_getoutmsg(*mp, cmd, MAX_BUF_LEN - WIFI_BUF_OFFSET)) ==
1512 	    NULL) {
1513 		return (ENOMEM);
1514 	}
1515 	outp = (wldp_t *)omp->b_rptr;
1516 	sr = (wl_wpa_ess_t *)outp->wldp_buf;
1517 	sr->count = 0;
1518 
1519 	switch (cmd) {
1520 	case WLAN_GET_PARAM:
1521 		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_scanresults\n");
1522 		nt = &ic->ic_scan;
1523 		IEEE80211_NODE_LOCK(nt);
1524 		in = list_head(&nt->nt_node);
1525 		while (in != NULL) {
1526 			/* filter out non-WPA APs */
1527 			if (in->in_wpa_ie == NULL) {
1528 				in = list_next(&nt->nt_node, in);
1529 				continue;
1530 			}
1531 			bcopy(in->in_bssid, sr->ess[ap_num].bssid,
1532 			    IEEE80211_ADDR_LEN);
1533 			sr->ess[ap_num].ssid_len = in->in_esslen;
1534 			bcopy(in->in_essid, sr->ess[ap_num].ssid,
1535 			    in->in_esslen);
1536 			sr->ess[ap_num].freq = in->in_chan->ich_freq;
1537 
1538 			len = in->in_wpa_ie[1] + 2;
1539 			bcopy(in->in_wpa_ie, sr->ess[ap_num].wpa_ie, len);
1540 			sr->ess[ap_num].wpa_ie_len = len;
1541 
1542 			ap_num ++;
1543 			in = list_next(&nt->nt_node, in);
1544 		}
1545 		IEEE80211_NODE_UNLOCK(nt);
1546 		sr->count = ap_num;
1547 		outp->wldp_length = WIFI_BUF_OFFSET +
1548 		    offsetof(wl_wpa_ess_t, ess) +
1549 		    sr->count * sizeof (struct wpa_ess);
1550 		omp->b_wptr = omp->b_rptr + outp->wldp_length;
1551 		break;
1552 	case WLAN_SET_PARAM:
1553 		outp->wldp_result = WL_READONLY;
1554 		err = EINVAL;
1555 		break;
1556 	default:
1557 		ieee80211_err("wifi_cfg_scanresults: unknown cmmand %x\n", cmd);
1558 		outp->wldp_result = WL_NOTSUPPORTED;
1559 		err = EINVAL;
1560 		break;
1561 	}
1562 
1563 	freemsg(*mp);
1564 	*mp = omp;
1565 	return (err);
1566 }
1567 
1568 /*
1569  * Manually control the state of AUTH | DEAUTH | DEASSOC | ASSOC
1570  */
1571 static int
1572 wifi_cfg_setmlme(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1573 {
1574 	mblk_t *omp;
1575 	wldp_t *outp;
1576 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
1577 	wl_mlme_t *mlme = (wl_mlme_t *)inp->wldp_buf;
1578 	ieee80211_node_t *in;
1579 	int err = 0;
1580 	uint32_t flags;
1581 
1582 	if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
1583 		return (ENOMEM);
1584 	outp = (wldp_t *)omp->b_rptr;
1585 
1586 	switch (cmd) {
1587 	case WLAN_GET_PARAM:
1588 		outp->wldp_result = WL_WRITEONLY;
1589 		err = EINVAL;
1590 		break;
1591 	case WLAN_SET_PARAM:
1592 		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_setmlme: "
1593 		    "op=%d\n", mlme->im_op);
1594 		switch (mlme->im_op) {
1595 		case IEEE80211_MLME_DISASSOC:
1596 		case IEEE80211_MLME_DEAUTH:
1597 			if (ic->ic_opmode == IEEE80211_M_STA) {
1598 				/*
1599 				 * Mask ic_flags of IEEE80211_F_WPA to disable
1600 				 * ieee80211_notify temporarily.
1601 				 */
1602 				flags = ic->ic_flags;
1603 				ic->ic_flags &= ~IEEE80211_F_WPA;
1604 
1605 				IEEE80211_UNLOCK(ic);
1606 				ieee80211_new_state(ic, IEEE80211_S_INIT,
1607 				    mlme->im_reason);
1608 				IEEE80211_LOCK(ic);
1609 
1610 				ic->ic_flags = flags;
1611 			}
1612 			break;
1613 		case IEEE80211_MLME_ASSOC:
1614 			if (ic->ic_opmode != IEEE80211_M_STA) {
1615 				ieee80211_err("wifi_cfg_setmlme: opmode err\n");
1616 				err = EINVAL;
1617 				outp->wldp_result = WL_NOTSUPPORTED;
1618 				break;
1619 			}
1620 			if (ic->ic_des_esslen != 0) {
1621 			/*
1622 			 * Desired ssid specified; must match both bssid and
1623 			 * ssid to distinguish ap advertising multiple ssid's.
1624 			 */
1625 				in = ieee80211_find_node_with_ssid(&ic->ic_scan,
1626 				    mlme->im_macaddr,
1627 				    ic->ic_des_esslen, ic->ic_des_essid);
1628 			} else {
1629 			/*
1630 			 * Normal case; just match bssid.
1631 			 */
1632 				in = ieee80211_find_node(&ic->ic_scan,
1633 				    mlme->im_macaddr);
1634 			}
1635 			if (in == NULL) {
1636 				ieee80211_err("wifi_cfg_setmlme: "
1637 				    "no matched node\n");
1638 				err = EINVAL;
1639 				outp->wldp_result = WL_NOTSUPPORTED;
1640 				break;
1641 			}
1642 			IEEE80211_UNLOCK(ic);
1643 			ieee80211_sta_join(ic, in);
1644 			IEEE80211_LOCK(ic);
1645 		}
1646 		break;
1647 	default:
1648 		ieee80211_err("wifi_cfg_delkey: unknown command %x\n", cmd);
1649 		outp->wldp_result = WL_NOTSUPPORTED;
1650 		err = EINVAL;
1651 		break;
1652 	}
1653 
1654 	freemsg(*mp);
1655 	*mp = omp;
1656 	return (err);
1657 }
1658 
1659 static int
1660 wifi_cfg_getset(struct ieee80211com *ic, mblk_t **mp, uint32_t cmd)
1661 {
1662 	mblk_t *mp1 = *mp;
1663 	wldp_t *wp = (wldp_t *)mp1->b_rptr;
1664 	int err = 0;
1665 
1666 	ASSERT(ic != NULL && mp1 != NULL);
1667 	IEEE80211_LOCK_ASSERT(ic);
1668 	if (MBLKL(mp1) < WIFI_BUF_OFFSET) {
1669 		ieee80211_err("wifi_cfg_getset: "
1670 		    "invalid input buffer, size=%d\n", MBLKL(mp1));
1671 		return (EINVAL);
1672 	}
1673 
1674 	switch (wp->wldp_id) {
1675 	/* Commands */
1676 	case WL_SCAN:
1677 		err = wifi_cmd_scan(ic, mp1);
1678 		break;
1679 	case WL_LOAD_DEFAULTS:
1680 		err = wifi_cmd_loaddefaults(ic, mp1);
1681 		break;
1682 	case WL_DISASSOCIATE:
1683 		err = wifi_cmd_disassoc(ic, mp1);
1684 		break;
1685 	/* Parameters */
1686 	case WL_ESSID:
1687 		err = wifi_cfg_essid(ic, cmd, mp);
1688 		break;
1689 	case WL_BSSID:
1690 		err = wifi_cfg_bssid(ic, cmd, mp);
1691 		break;
1692 	case WL_NODE_NAME:
1693 		err = wifi_cfg_nodename(ic, cmd, mp);
1694 		break;
1695 	case WL_PHY_CONFIG:
1696 		err = wifi_cfg_phy(ic, cmd, mp);
1697 		break;
1698 	case WL_WEP_KEY_TAB:
1699 		err = wifi_cfg_wepkey(ic, cmd, mp);
1700 		break;
1701 	case WL_WEP_KEY_ID:
1702 		err = wifi_cfg_keyid(ic, cmd, mp);
1703 		break;
1704 	case WL_AUTH_MODE:
1705 		err = wifi_cfg_authmode(ic, cmd, mp);
1706 		break;
1707 	case WL_ENCRYPTION:
1708 		err = wifi_cfg_encrypt(ic, cmd, mp);
1709 		break;
1710 	case WL_BSS_TYPE:
1711 		err = wifi_cfg_bsstype(ic, cmd, mp);
1712 		break;
1713 	case WL_DESIRED_RATES:
1714 		err = wifi_cfg_desrates(ic, cmd, mp);
1715 		break;
1716 	case WL_LINKSTATUS:
1717 		err = wifi_cfg_linkstatus(ic, cmd, mp);
1718 		break;
1719 	case WL_ESS_LIST:
1720 		err = wifi_cfg_esslist(ic, cmd, mp);
1721 		break;
1722 	case WL_SUPPORTED_RATES:
1723 		err = wifi_cfg_suprates(ic, cmd, mp);
1724 		break;
1725 	case WL_RSSI:
1726 		err = wifi_cfg_rssi(ic, cmd, mp);
1727 		break;
1728 	/*
1729 	 * WPA IOCTLs
1730 	 */
1731 	case WL_CAPABILITY:
1732 		err = wifi_cfg_caps(ic, cmd, mp);
1733 		break;
1734 	case WL_WPA:
1735 		err = wifi_cfg_wpa(ic, cmd, mp);
1736 		break;
1737 	case WL_KEY:
1738 		err = wifi_cfg_wpakey(ic, cmd, mp);
1739 		break;
1740 	case WL_DELKEY:
1741 		err = wifi_cfg_delkey(ic, cmd, mp);
1742 		break;
1743 	case WL_SETOPTIE:
1744 		err = wifi_cfg_setoptie(ic, cmd, mp);
1745 		break;
1746 	case WL_SCANRESULTS:
1747 		err = wifi_cfg_scanresults(ic, cmd, mp);
1748 		break;
1749 	case WL_MLME:
1750 		err = wifi_cfg_setmlme(ic, cmd, mp);
1751 		break;
1752 	default:
1753 		wifi_setupoutmsg(mp1, 0);
1754 		wp->wldp_result = WL_LACK_FEATURE;
1755 		err = ENOTSUP;
1756 		break;
1757 	}
1758 
1759 	return (err);
1760 }
1761 
1762 /*
1763  * Typically invoked by drivers in response to requests for
1764  * information or to change settings from the userland.
1765  *
1766  * Return value should be checked by WiFi drivers. Return 0
1767  * on success. Otherwise, return non-zero value to indicate
1768  * the error. Driver should operate as below when the return
1769  * error is:
1770  * ENETRESET	Reset wireless network and re-start to join a
1771  *		WLAN. ENETRESET is returned when a configuration
1772  *		parameter has been changed.
1773  *		When acknowledge a M_IOCTL message, thie error
1774  *		is ignored.
1775  */
1776 int
1777 ieee80211_ioctl(struct ieee80211com *ic, queue_t *wq, mblk_t *mp)
1778 {
1779 	struct iocblk *iocp;
1780 	int32_t cmd, err, len;
1781 	boolean_t need_privilege;
1782 	mblk_t *mp1;
1783 
1784 	if (MBLKL(mp) < sizeof (struct iocblk)) {
1785 		ieee80211_err("ieee80211_ioctl: ioctl buffer too short, %u\n",
1786 		    MBLKL(mp));
1787 		miocnak(wq, mp, 0, EINVAL);
1788 		return (EINVAL);
1789 	}
1790 
1791 	/*
1792 	 * Validate the command
1793 	 */
1794 	iocp = (struct iocblk *)mp->b_rptr;
1795 	iocp->ioc_error = 0;
1796 	cmd = iocp->ioc_cmd;
1797 	need_privilege = B_TRUE;
1798 	switch (cmd) {
1799 	case WLAN_SET_PARAM:
1800 	case WLAN_COMMAND:
1801 		break;
1802 	case WLAN_GET_PARAM:
1803 		need_privilege = B_FALSE;
1804 		break;
1805 	default:
1806 		ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_ioctl(): "
1807 		    "unknown cmd 0x%x\n", cmd);
1808 		miocnak(wq, mp, 0, EINVAL);
1809 		return (EINVAL);
1810 	}
1811 
1812 	if (need_privilege) {
1813 		/*
1814 		 * Check for specific net_config privilege on Solaris 10+.
1815 		 */
1816 		err = secpolicy_net_config(iocp->ioc_cr, B_FALSE);
1817 		if (err != 0) {
1818 			miocnak(wq, mp, 0, err);
1819 			return (err);
1820 		}
1821 	}
1822 
1823 	IEEE80211_LOCK(ic);
1824 
1825 	/* sanity check */
1826 	mp1 = mp->b_cont;
1827 	if (iocp->ioc_count == 0 || iocp->ioc_count < sizeof (wldp_t) ||
1828 	    mp1 == NULL) {
1829 		miocnak(wq, mp, 0, EINVAL);
1830 		IEEE80211_UNLOCK(ic);
1831 		return (EINVAL);
1832 	}
1833 
1834 	/* assuming single data block */
1835 	if (mp1->b_cont != NULL) {
1836 		freemsg(mp1->b_cont);
1837 		mp1->b_cont = NULL;
1838 	}
1839 
1840 	err = wifi_cfg_getset(ic, &mp1, cmd);
1841 	mp->b_cont = mp1;
1842 	IEEE80211_UNLOCK(ic);
1843 
1844 	len = msgdsize(mp1);
1845 	/* ignore ENETRESET when acknowledge the M_IOCTL message */
1846 	if (err == 0 || err == ENETRESET)
1847 		miocack(wq, mp, len, 0);
1848 	else
1849 		miocack(wq, mp, len, err);
1850 
1851 	return (err);
1852 }
1853