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