1 /*
2  * Copyright 2008 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 #include <sys/param.h>
35 #include <sys/types.h>
36 #include <sys/errno.h>
37 #include <sys/strsun.h>
38 #include <sys/policy.h>
39 #include <inet/common.h>
40 #include <inet/nd.h>
41 #include <inet/mi.h>
42 #include <sys/note.h>
43 #include <sys/mac.h>
44 #include <inet/wifi_ioctl.h>
45 #include "net80211_impl.h"
46 
47 static size_t
48 wifi_strnlen(const char *s, size_t n)
49 {
50 	size_t i;
51 
52 	for (i = 0; i < n && s[i] != '\0'; i++)
53 		/* noop */;
54 	return (i);
55 }
56 
57 /*
58  * Initialize an output message block by copying from an
59  * input message block. The message is of type wldp_t.
60  *    mp     input message block
61  *    buflen length of wldp_buf
62  */
63 static void
64 wifi_setupoutmsg(mblk_t *mp, int buflen)
65 {
66 	wldp_t *wp;
67 
68 	wp = (wldp_t *)mp->b_rptr;
69 	wp->wldp_length = WIFI_BUF_OFFSET + buflen;
70 	wp->wldp_result = WL_SUCCESS;
71 	mp->b_wptr = mp->b_rptr + wp->wldp_length;
72 }
73 
74 /*
75  * Allocate and initialize an output message.
76  */
77 static mblk_t *
78 wifi_getoutmsg(mblk_t *mp, uint32_t cmd, int buflen)
79 {
80 	mblk_t *mp1;
81 	int size;
82 
83 	size = WIFI_BUF_OFFSET;
84 	if (cmd == WLAN_GET_PARAM)
85 		size += buflen;	/* to hold output parameters */
86 	mp1 = allocb(size, BPRI_HI);
87 	if (mp1 == NULL) {
88 		ieee80211_err("wifi_getoutbuf: allocb %d bytes failed!\n",
89 		    size);
90 		return (NULL);
91 	}
92 
93 	bzero(mp1->b_rptr, size);
94 	bcopy(mp->b_rptr, mp1->b_rptr, WIFI_BUF_OFFSET);
95 	wifi_setupoutmsg(mp1, size - WIFI_BUF_OFFSET);
96 
97 	return (mp1);
98 }
99 
100 static int
101 wifi_cfg_essid(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
102 {
103 	mblk_t *omp;
104 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
105 	wldp_t *outp;
106 	wl_essid_t *iw_essid = (wl_essid_t *)inp->wldp_buf;
107 	wl_essid_t *ow_essid;
108 	char *essid;
109 	int err = 0;
110 
111 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_essid_t))) == NULL)
112 		return (ENOMEM);
113 	outp = (wldp_t *)omp->b_rptr;
114 	ow_essid = (wl_essid_t *)outp->wldp_buf;
115 
116 	switch (cmd) {
117 	case WLAN_GET_PARAM:
118 		essid = (char *)ic->ic_des_essid;
119 		if (essid[0] == '\0')
120 			essid = (char *)ic->ic_bss->in_essid;
121 		ow_essid->wl_essid_length = wifi_strnlen((const char *)essid,
122 		    IEEE80211_NWID_LEN);
123 		bcopy(essid, ow_essid->wl_essid_essid,
124 		    ow_essid->wl_essid_length);
125 		break;
126 	case WLAN_SET_PARAM:
127 		if (iw_essid->wl_essid_length > IEEE80211_NWID_LEN) {
128 			ieee80211_err("wifi_cfg_essid: "
129 			    "essid too long, %u, max %u\n",
130 			    iw_essid->wl_essid_length, IEEE80211_NWID_LEN);
131 			outp->wldp_result = WL_NOTSUPPORTED;
132 			err = EINVAL;
133 			break;
134 		}
135 		essid = iw_essid->wl_essid_essid;
136 		essid[IEEE80211_NWID_LEN] = 0;
137 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_essid: "
138 		    "set essid=%s length=%d\n",
139 		    essid, iw_essid->wl_essid_length);
140 
141 		ic->ic_des_esslen = iw_essid->wl_essid_length;
142 		if (ic->ic_des_esslen != 0)
143 			bcopy(essid, ic->ic_des_essid, ic->ic_des_esslen);
144 		if (ic->ic_des_esslen < IEEE80211_NWID_LEN)
145 			ic->ic_des_essid[ic->ic_des_esslen] = 0;
146 		err = ENETRESET;
147 		break;
148 	default:
149 		ieee80211_err("wifi_cfg_essid: unknown command %x\n", cmd);
150 		outp->wldp_result = WL_NOTSUPPORTED;
151 		err = EINVAL;
152 		break;
153 	}
154 
155 	freemsg(*mp);
156 	*mp = omp;
157 	return (err);
158 }
159 
160 static int
161 wifi_cfg_bssid(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
162 {
163 	mblk_t *omp;
164 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
165 	wldp_t *outp;
166 	uint8_t *bssid;
167 	int err = 0;
168 
169 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_bssid_t))) == NULL)
170 		return (ENOMEM);
171 	outp = (wldp_t *)omp->b_rptr;
172 
173 	switch (cmd) {
174 	case  WLAN_GET_PARAM:
175 		if (ic->ic_flags & IEEE80211_F_DESBSSID)
176 			bssid = ic->ic_des_bssid;
177 		else
178 			bssid = ic->ic_bss->in_bssid;
179 		bcopy(bssid, outp->wldp_buf, sizeof (wl_bssid_t));
180 		break;
181 	case WLAN_SET_PARAM:
182 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_bssid: "
183 		    "set bssid=%s\n",
184 		    ieee80211_macaddr_sprintf(inp->wldp_buf));
185 		bcopy(inp->wldp_buf, ic->ic_des_bssid, sizeof (wl_bssid_t));
186 		ic->ic_flags |= IEEE80211_F_DESBSSID;
187 		err = ENETRESET;
188 		break;
189 	default:
190 		ieee80211_err("wifi_cfg_bssid: unknown command %x\n", cmd);
191 		outp->wldp_result = WL_NOTSUPPORTED;
192 		err = EINVAL;
193 		break;
194 	}
195 
196 	freemsg(*mp);
197 	*mp = omp;
198 	return (err);
199 }
200 
201 static int
202 wifi_cfg_nodename(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
203 {
204 	mblk_t *omp;
205 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
206 	wldp_t *outp;
207 	wl_nodename_t *iw_name = (wl_nodename_t *)inp->wldp_buf;
208 	wl_nodename_t *ow_name;
209 	char *nodename;
210 	int len, err;
211 
212 	err = 0;
213 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_nodename_t))) == NULL)
214 		return (ENOMEM);
215 	outp = (wldp_t *)omp->b_rptr;
216 	ow_name = (wl_nodename_t *)outp->wldp_buf;
217 
218 	switch (cmd) {
219 	case WLAN_GET_PARAM:
220 		len = wifi_strnlen((const char *)ic->ic_nickname,
221 		    IEEE80211_NWID_LEN);
222 		ow_name->wl_nodename_length = len;
223 		bcopy(ic->ic_nickname, ow_name->wl_nodename_name, len);
224 		break;
225 	case WLAN_SET_PARAM:
226 		if (iw_name->wl_nodename_length > IEEE80211_NWID_LEN) {
227 			ieee80211_err("wifi_cfg_nodename: "
228 			    "node name too long, %u\n",
229 			    iw_name->wl_nodename_length);
230 			outp->wldp_result = WL_NOTSUPPORTED;
231 			err = EINVAL;
232 			break;
233 		}
234 		nodename = iw_name->wl_nodename_name;
235 		nodename[IEEE80211_NWID_LEN] = 0;
236 		ieee80211_dbg(IEEE80211_MSG_CONFIG,
237 		    "wifi_cfg_nodename: set nodename %s, len=%d\n",
238 		    nodename, iw_name->wl_nodename_length);
239 
240 		len = iw_name->wl_nodename_length;
241 		if (len > 0)
242 			bcopy(nodename, ic->ic_nickname, len);
243 		if (len < IEEE80211_NWID_LEN)
244 			ic->ic_nickname[len] = 0;
245 		break;
246 	default:
247 		ieee80211_err("wifi_cfg_nodename: unknown command %x\n", cmd);
248 		outp->wldp_result = WL_NOTSUPPORTED;
249 		err = EINVAL;
250 		break;
251 	}
252 
253 	freemsg(*mp);
254 	*mp = omp;
255 	return (err);
256 }
257 
258 static int
259 wifi_cfg_phy(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
260 {
261 	mblk_t *omp;
262 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
263 	wldp_t *outp;
264 	wl_phy_conf_t *iw_phy = (wl_phy_conf_t *)inp->wldp_buf;
265 	wl_phy_conf_t *ow_phy;
266 	struct ieee80211_channel *ch = ic->ic_curchan;
267 	int err = 0;
268 
269 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_phy_conf_t))) == NULL)
270 		return (ENOMEM);
271 	outp = (wldp_t *)omp->b_rptr;
272 	ow_phy = (wl_phy_conf_t *)outp->wldp_buf;
273 
274 	switch (cmd) {
275 	case WLAN_GET_PARAM: {
276 		/* get current physical (FH, DS, ERP) parameters */
277 		if (IEEE80211_IS_CHAN_A(ch) || IEEE80211_IS_CHAN_T(ch)) {
278 			wl_ofdm_t *ofdm = (wl_ofdm_t *)ow_phy;
279 
280 			ofdm->wl_ofdm_subtype = WL_OFDM;
281 			ofdm->wl_ofdm_frequency = ch->ich_freq;
282 		} else {
283 			switch (ic->ic_phytype) {
284 			case IEEE80211_T_FH: {
285 				wl_fhss_t *fhss = (wl_fhss_t *)ow_phy;
286 
287 				fhss->wl_fhss_subtype = WL_FHSS;
288 				fhss->wl_fhss_channel =
289 				    ieee80211_chan2ieee(ic, ch);
290 				break;
291 			}
292 			case IEEE80211_T_DS: {
293 				wl_dsss_t *dsss = (wl_dsss_t *)ow_phy;
294 
295 				dsss->wl_dsss_subtype = WL_DSSS;
296 				dsss->wl_dsss_channel =
297 				    ieee80211_chan2ieee(ic, ch);
298 				break;
299 			}
300 			case IEEE80211_T_OFDM: {
301 				wl_erp_t *erp = (wl_erp_t *)ow_phy;
302 
303 				erp->wl_erp_subtype = WL_ERP;
304 				erp->wl_erp_channel =
305 				    ieee80211_chan2ieee(ic, ch);
306 				break;
307 			}
308 			default:
309 				ieee80211_err("wifi_cfg_phy: "
310 				    "unknown phy type, %x\n", ic->ic_phytype);
311 				outp->wldp_result = WL_HW_ERROR;
312 				err = EIO;
313 				break;
314 			} /* switch (ic->ic_phytype) */
315 		}
316 		break;
317 	}
318 
319 	case WLAN_SET_PARAM: {
320 		wl_dsss_t *dsss = (wl_dsss_t *)iw_phy;
321 		int16_t ch = dsss->wl_dsss_channel;
322 
323 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_phy: "
324 		    "set channel=%d\n", ch);
325 		if (ch == 0 || ch == (int16_t)IEEE80211_CHAN_ANY) {
326 			ic->ic_des_chan = IEEE80211_CHAN_ANYC;
327 		} else if ((uint_t)ch > IEEE80211_CHAN_MAX ||
328 		    ieee80211_isclr(ic->ic_chan_active, ch)) {
329 			outp->wldp_result = WL_NOTSUPPORTED;
330 			err = EINVAL;
331 			break;
332 		} else {
333 			ic->ic_des_chan = ic->ic_ibss_chan =
334 			    &ic->ic_sup_channels[ch];
335 		}
336 		switch (ic->ic_state) {
337 		case IEEE80211_S_INIT:
338 		case IEEE80211_S_SCAN:
339 			err = ENETRESET;
340 			break;
341 		default:
342 			/*
343 			 * If the desired channel has changed (to something
344 			 * other than any) and we're not already scanning,
345 			 * then kick the state machine.
346 			 */
347 			if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
348 			    ic->ic_bss->in_chan != ic->ic_des_chan &&
349 			    (ic->ic_flags & IEEE80211_F_SCAN) == 0)
350 				err = ENETRESET;
351 			break;
352 		}
353 		break;
354 	}
355 
356 	default:
357 		ieee80211_err("wifi_cfg_phy: unknown command %x\n", cmd);
358 		outp->wldp_result = WL_NOTSUPPORTED;
359 		err = EINVAL;
360 		break;
361 	} /* switch (cmd) */
362 
363 	freemsg(*mp);
364 	*mp = omp;
365 	return (err);
366 }
367 
368 static int
369 wifi_cfg_wepkey(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
370 {
371 	mblk_t *omp;
372 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
373 	wldp_t *outp;
374 	wl_wep_key_t *iw_wepkey = (wl_wep_key_t *)inp->wldp_buf;
375 	struct ieee80211_key *k;
376 	uint16_t i;
377 	uint32_t klen;
378 	int err = 0;
379 
380 	if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
381 		return (ENOMEM);
382 	outp = (wldp_t *)omp->b_rptr;
383 
384 	switch (cmd) {
385 	case WLAN_GET_PARAM:
386 		outp->wldp_result = WL_WRITEONLY;
387 		err = EINVAL;
388 		break;
389 	case WLAN_SET_PARAM:
390 		if (inp->wldp_length < sizeof (wl_wep_key_tab_t)) {
391 			ieee80211_err("wifi_cfg_wepkey: "
392 			    "parameter too short, %d, expected %d\n",
393 			    inp->wldp_length, sizeof (wl_wep_key_tab_t));
394 			outp->wldp_result = WL_NOTSUPPORTED;
395 			err = EINVAL;
396 			break;
397 		}
398 
399 		/* set all valid keys */
400 		for (i = 0; i < MAX_NWEPKEYS; i++) {
401 			if (iw_wepkey[i].wl_wep_operation != WL_ADD)
402 				continue;
403 			klen = iw_wepkey[i].wl_wep_length;
404 			if (klen > IEEE80211_KEYBUF_SIZE) {
405 				ieee80211_err("wifi_cfg_wepkey: "
406 				    "invalid wepkey length, %u\n", klen);
407 				outp->wldp_result = WL_NOTSUPPORTED;
408 				err = EINVAL;
409 				continue;	/* continue to set other keys */
410 			}
411 			if (klen == 0)
412 				continue;
413 
414 			/*
415 			 * Set key contents. Only WEP is supported
416 			 */
417 			ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_wepkey: "
418 			    "set key %u, len=%u\n", i, klen);
419 			k = &ic->ic_nw_keys[i];
420 			if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP,
421 			    IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, k) == 0) {
422 				ieee80211_err("wifi_cfg_wepkey: "
423 				    "abort, create key failed. id=%u\n", i);
424 				outp->wldp_result = WL_HW_ERROR;
425 				err = EIO;
426 				continue;
427 			}
428 			k->wk_keyix = i;
429 			k->wk_keylen = (uint8_t)klen;
430 			k->wk_flags |= IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
431 			bzero(k->wk_key, IEEE80211_KEYBUF_SIZE);
432 			bcopy(iw_wepkey[i].wl_wep_key, k->wk_key, klen);
433 			if (ieee80211_crypto_setkey(ic, k, ic->ic_macaddr)
434 			    == 0) {
435 				ieee80211_err("wifi_cfg_wepkey: "
436 				    "set key failed len=%u\n", klen);
437 				outp->wldp_result = WL_HW_ERROR;
438 				err = EIO;
439 			}
440 		}
441 		if (err == 0)
442 			err = ENETRESET;
443 		break;
444 	default:
445 		ieee80211_err("wifi_cfg_wepkey: unknown command %x\n", cmd);
446 		outp->wldp_result = WL_NOTSUPPORTED;
447 		err = EINVAL;
448 		break;
449 	}
450 
451 	freemsg(*mp);
452 	*mp = omp;
453 	return (err);
454 }
455 
456 static int
457 wifi_cfg_keyid(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
458 {
459 	mblk_t *omp;
460 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
461 	wldp_t *outp;
462 	wl_wep_key_id_t *iw_kid = (wl_wep_key_id_t *)inp->wldp_buf;
463 	wl_wep_key_id_t *ow_kid;
464 	int err = 0;
465 
466 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_wep_key_id_t))) == NULL)
467 		return (ENOMEM);
468 	outp = (wldp_t *)omp->b_rptr;
469 	ow_kid = (wl_wep_key_id_t *)outp->wldp_buf;
470 
471 	switch (cmd) {
472 	case WLAN_GET_PARAM:
473 		*ow_kid = (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) ?
474 		    0 : ic->ic_def_txkey;
475 		break;
476 	case  WLAN_SET_PARAM:
477 		if (*iw_kid >= MAX_NWEPKEYS) {
478 			ieee80211_err("wifi_cfg_keyid: "
479 			    "keyid too large, %u\n", *iw_kid);
480 			outp->wldp_result = WL_NOTSUPPORTED;
481 			err = EINVAL;
482 		} else {
483 			ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_keyid: "
484 			    "set keyid=%u\n", *iw_kid);
485 			ic->ic_def_txkey = *iw_kid;
486 			err = ENETRESET;
487 		}
488 		break;
489 	default:
490 		ieee80211_err("wifi_cfg_keyid: unknown command %x\n", cmd);
491 		outp->wldp_result = WL_NOTSUPPORTED;
492 		err = EINVAL;
493 		break;
494 	}
495 
496 	freemsg(*mp);
497 	*mp = omp;
498 	return (err);
499 }
500 
501 static int
502 wifi_cfg_authmode(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
503 {
504 	mblk_t *omp;
505 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
506 	wldp_t *outp;
507 	wl_authmode_t *iw_auth = (wl_authmode_t *)inp->wldp_buf;
508 	wl_authmode_t *ow_auth;
509 	int err = 0;
510 
511 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_authmode_t))) == NULL)
512 		return (ENOMEM);
513 	outp = (wldp_t *)omp->b_rptr;
514 	ow_auth = (wl_authmode_t *)outp->wldp_buf;
515 
516 	switch (cmd) {
517 	case WLAN_GET_PARAM:
518 		*ow_auth = ic->ic_bss->in_authmode;
519 		break;
520 	case WLAN_SET_PARAM:
521 		if (*iw_auth == ic->ic_bss->in_authmode)
522 			break;
523 
524 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_authmode: "
525 		    "set authmode=%u\n", *iw_auth);
526 		switch (*iw_auth) {
527 		case WL_OPENSYSTEM:
528 		case WL_SHAREDKEY:
529 			ic->ic_bss->in_authmode = *iw_auth;
530 			err = ENETRESET;
531 			break;
532 		default:
533 			ieee80211_err("wifi_cfg_authmode: "
534 			    "unknown authmode %u\n", *iw_auth);
535 			outp->wldp_result = WL_NOTSUPPORTED;
536 			err = EINVAL;
537 			break;
538 		}
539 		break;
540 	default:
541 		ieee80211_err("wifi_cfg_authmode: unknown command %x\n", cmd);
542 		outp->wldp_result = WL_NOTSUPPORTED;
543 		err = EINVAL;
544 		break;
545 	}
546 
547 	freemsg(*mp);
548 	*mp = omp;
549 	return (err);
550 }
551 
552 static int
553 wifi_cfg_encrypt(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
554 {
555 	mblk_t *omp;
556 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
557 	wldp_t *outp;
558 	wl_encryption_t *iw_encryp = (wl_encryption_t *)inp->wldp_buf;
559 	wl_encryption_t *ow_encryp;
560 	uint32_t flags;
561 	int err = 0;
562 
563 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_encryption_t))) == NULL)
564 		return (ENOMEM);
565 	outp = (wldp_t *)omp->b_rptr;
566 	ow_encryp = (wl_encryption_t *)outp->wldp_buf;
567 
568 	switch (cmd) {
569 	case WLAN_GET_PARAM:
570 		*ow_encryp = (ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0;
571 		if (ic->ic_flags & IEEE80211_F_WPA)
572 			*ow_encryp = WL_ENC_WPA;
573 		break;
574 	case WLAN_SET_PARAM:
575 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_encrypt: "
576 		    "set encryption=%u\n", *iw_encryp);
577 		flags = ic->ic_flags;
578 		if (*iw_encryp == WL_NOENCRYPTION)
579 			flags &= ~IEEE80211_F_PRIVACY;
580 		else
581 			flags |= IEEE80211_F_PRIVACY;
582 
583 		if (ic->ic_flags != flags) {
584 			ic->ic_flags = flags;
585 			err = ENETRESET;
586 		}
587 		break;
588 	default:
589 		ieee80211_err("wifi_cfg_encrypt: unknown command %x\n", cmd);
590 		outp->wldp_result = WL_NOTSUPPORTED;
591 		err = EINVAL;
592 		break;
593 	}
594 
595 	freemsg(*mp);
596 	*mp = omp;
597 	return (err);
598 }
599 
600 static int
601 wifi_cfg_bsstype(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
602 {
603 	mblk_t *omp;
604 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
605 	wldp_t *outp;
606 	wl_bss_type_t *iw_opmode = (wl_bss_type_t *)inp->wldp_buf;
607 	wl_bss_type_t *ow_opmode;
608 	int err = 0;
609 
610 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_bss_type_t))) == NULL)
611 		return (ENOMEM);
612 	outp = (wldp_t *)omp->b_rptr;
613 	ow_opmode = (wl_bss_type_t *)outp->wldp_buf;
614 
615 	switch (cmd) {
616 	case WLAN_GET_PARAM:
617 		switch (ic->ic_opmode) {
618 		case IEEE80211_M_STA:
619 			*ow_opmode = WL_BSS_BSS;
620 			break;
621 		case IEEE80211_M_IBSS:
622 			*ow_opmode = WL_BSS_IBSS;
623 			break;
624 		default:
625 			*ow_opmode = WL_BSS_ANY;
626 			break;
627 		}
628 		break;
629 	case  WLAN_SET_PARAM:
630 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_bsstype: "
631 		    "set bsstype=%u\n", *iw_opmode);
632 		switch (*iw_opmode) {
633 		case WL_BSS_BSS:
634 			ic->ic_flags &= ~IEEE80211_F_IBSSON;
635 			ic->ic_opmode = IEEE80211_M_STA;
636 			err = ENETRESET;
637 			break;
638 		case WL_BSS_IBSS:
639 			if ((ic->ic_caps & IEEE80211_C_IBSS) == 0) {
640 				outp->wldp_result = WL_LACK_FEATURE;
641 				err = ENOTSUP;
642 				break;
643 			}
644 
645 			if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) {
646 				ic->ic_flags |= IEEE80211_F_IBSSON;
647 				ic->ic_opmode = IEEE80211_M_IBSS;
648 				err = ENETRESET;
649 			}
650 			break;
651 		default:
652 			ieee80211_err("wifi_cfg_bsstype: "
653 			    "unknown opmode %u\n", *iw_opmode);
654 			outp->wldp_result = WL_NOTSUPPORTED;
655 			err = EINVAL;
656 			break;
657 		}
658 		break;
659 	default:
660 		ieee80211_err("wifi_cfg_bsstype: unknown command %x\n", cmd);
661 		outp->wldp_result = WL_NOTSUPPORTED;
662 		err = EINVAL;
663 		break;
664 	}
665 
666 	freemsg(*mp);
667 	*mp = omp;
668 	return (err);
669 }
670 
671 static int
672 wifi_cfg_linkstatus(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
673 {
674 	mblk_t *omp;
675 	wldp_t *outp;
676 	wl_linkstatus_t *ow_linkstat;
677 	int err = 0;
678 
679 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_linkstatus_t))) == NULL)
680 		return (ENOMEM);
681 	outp = (wldp_t *)omp->b_rptr;
682 	ow_linkstat = (wl_linkstatus_t *)outp->wldp_buf;
683 
684 	switch (cmd) {
685 	case WLAN_GET_PARAM:
686 		*ow_linkstat = (ic->ic_state == IEEE80211_S_RUN) ?
687 		    WL_CONNECTED : WL_NOTCONNECTED;
688 		if ((ic->ic_flags & IEEE80211_F_WPA) &&
689 		    (ieee80211_crypto_getciphertype(ic) != WIFI_SEC_WPA)) {
690 			*ow_linkstat = WL_NOTCONNECTED;
691 		}
692 		break;
693 	case WLAN_SET_PARAM:
694 		outp->wldp_result = WL_READONLY;
695 		err = EINVAL;
696 		break;
697 	default:
698 		ieee80211_err("wifi_cfg_linkstatus: unknown command %x\n", cmd);
699 		outp->wldp_result = WL_NOTSUPPORTED;
700 		err = EINVAL;
701 		break;
702 	}
703 
704 	freemsg(*mp);
705 	*mp = omp;
706 	return (err);
707 }
708 
709 static int
710 wifi_cfg_suprates(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
711 {
712 	mblk_t *omp;
713 	wldp_t *outp;
714 	wl_rates_t *ow_rates;
715 	const struct ieee80211_rateset *srs;
716 	uint8_t srates, *drates;
717 	int err, buflen, i, j, k, l;
718 
719 	err = 0;
720 	/* rate value (wl_rates_rates) is of type char */
721 	buflen = offsetof(wl_rates_t, wl_rates_rates) +
722 	    sizeof (char) * IEEE80211_MODE_MAX * IEEE80211_RATE_MAXSIZE;
723 	if ((omp = wifi_getoutmsg(*mp, cmd, buflen)) == NULL)
724 		return (ENOMEM);
725 	outp = (wldp_t *)omp->b_rptr;
726 	ow_rates = (wl_rates_t *)outp->wldp_buf;
727 
728 	switch (cmd) {
729 	case WLAN_GET_PARAM:
730 		/* all rates supported by the device */
731 		ow_rates->wl_rates_num = 0;
732 		drates = (uint8_t *)ow_rates->wl_rates_rates;
733 		for (i = 0; i < IEEE80211_MODE_MAX; i++) {
734 			srs = &ic->ic_sup_rates[i];
735 			if (srs->ir_nrates == 0)
736 				continue;
737 
738 			for (j = 0; j < srs->ir_nrates; j++) {
739 				srates = IEEE80211_RV(srs->ir_rates[j]);
740 				/* sort and skip duplicated rates */
741 				for (k = 0; k < ow_rates->wl_rates_num; k++) {
742 					if (srates <= drates[k])
743 						break;
744 				}
745 				if (srates == drates[k])
746 					continue;	/* duplicate, skip */
747 				/* sort */
748 				for (l = ow_rates->wl_rates_num; l > k; l--)
749 					drates[l] = drates[l-1];
750 				drates[k] = srates;
751 				ow_rates->wl_rates_num++;
752 			}
753 		}
754 		break;
755 	case WLAN_SET_PARAM:
756 		outp->wldp_result = WL_READONLY;
757 		err = EINVAL;
758 		break;
759 	default:
760 		ieee80211_err("wifi_cfg_suprates: unknown command %x\n", cmd);
761 		outp->wldp_result = WL_NOTSUPPORTED;
762 		err = EINVAL;
763 		break;
764 	}
765 
766 	freemsg(*mp);
767 	*mp = omp;
768 	return (err);
769 }
770 
771 static int
772 wifi_cfg_desrates(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
773 {
774 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
775 	wl_rates_t *iw_rates = (wl_rates_t *)inp->wldp_buf;
776 	mblk_t *omp;
777 	wldp_t *outp;
778 	wl_rates_t *ow_rates;
779 	struct ieee80211_node *in = ic->ic_bss;
780 	struct ieee80211_rateset *rs = &in->in_rates;
781 	uint8_t drate, srate;
782 	int err, i, j;
783 	boolean_t found;
784 
785 	err = 0;
786 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_rates_t))) == NULL)
787 		return (ENOMEM);
788 	outp = (wldp_t *)omp->b_rptr;
789 	ow_rates = (wl_rates_t *)outp->wldp_buf;
790 
791 	srate = rs->ir_rates[in->in_txrate] & IEEE80211_RATE_VAL;
792 	switch (cmd) {
793 	case  WLAN_GET_PARAM:
794 		ow_rates->wl_rates_num = 1;
795 		ow_rates->wl_rates_rates[0] =
796 		    (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) ?
797 		    srate : ic->ic_fixed_rate;
798 		break;
799 	case  WLAN_SET_PARAM:
800 		drate = iw_rates->wl_rates_rates[0];
801 		if (ic->ic_fixed_rate == drate)
802 			break;
803 
804 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_desrates: "
805 		    "set desired rate=%u\n", drate);
806 
807 		if (drate == 0) {	/* reset */
808 			ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
809 			if (ic->ic_state == IEEE80211_S_RUN) {
810 				IEEE80211_UNLOCK(ic);
811 				ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
812 				IEEE80211_LOCK(ic);
813 			}
814 			break;
815 		}
816 
817 		/*
818 		 * Set desired rate. the desired rate is for data transfer
819 		 * and usually is checked and used when driver changes to
820 		 * RUN state.
821 		 * If the driver is in AUTH | ASSOC | RUN state, desired
822 		 * rate is checked against rates supported by current ESS.
823 		 * If it's supported and current state is AUTH|ASSOC, nothing
824 		 * needs to be doen by driver since the desired rate will
825 		 * be enabled when the device changes to RUN state. And
826 		 * when current state is RUN, Re-associate with the ESS to
827 		 * enable the desired rate.
828 		 */
829 		if (ic->ic_state != IEEE80211_S_INIT &&
830 		    ic->ic_state != IEEE80211_S_SCAN) {
831 			/* check if the rate is supported by current ESS */
832 			for (i = 0; i < rs->ir_nrates; i++) {
833 				if (drate == IEEE80211_RV(rs->ir_rates[i]))
834 					break;
835 			}
836 			if (i < rs->ir_nrates) {	/* supported */
837 				ic->ic_fixed_rate = drate;
838 				if (ic->ic_state == IEEE80211_S_RUN) {
839 					IEEE80211_UNLOCK(ic);
840 					ieee80211_new_state(ic,
841 					    IEEE80211_S_ASSOC, 0);
842 					IEEE80211_LOCK(ic);
843 				}
844 				break;
845 			}
846 		}
847 
848 		/* check the rate is supported by device */
849 		found = B_FALSE;
850 		for (i = 0; i < IEEE80211_MODE_MAX; i++) {
851 			rs = &ic->ic_sup_rates[i];
852 			for (j = 0; j < rs->ir_nrates; j++) {
853 				if (drate == IEEE80211_RV(rs->ir_rates[j])) {
854 					found = B_TRUE;
855 					break;
856 				}
857 			}
858 			if (found)
859 				break;
860 		}
861 		if (!found) {
862 			ieee80211_err("wifi_cfg_desrates: "
863 			    "invalid rate %d\n", drate);
864 			outp->wldp_result = WL_NOTSUPPORTED;
865 			err = EINVAL;
866 			break;
867 		}
868 		ic->ic_fixed_rate = drate;
869 		if (ic->ic_state != IEEE80211_S_SCAN)
870 			err = ENETRESET;	/* restart */
871 		break;
872 	default:
873 		ieee80211_err("wifi_cfg_desrates: unknown command %x\n", cmd);
874 		outp->wldp_result = WL_NOTSUPPORTED;
875 		err = EINVAL;
876 		break;
877 	}
878 
879 	freemsg(*mp);
880 	*mp = omp;
881 	return (err);
882 }
883 
884 /*
885  * Rescale device's RSSI value to (0, 15) as required by WiFi
886  * driver IOCTLs (PSARC/2003/722)
887  */
888 static wl_rssi_t
889 wifi_getrssi(struct ieee80211_node *in)
890 {
891 	struct ieee80211com *ic = in->in_ic;
892 	wl_rssi_t rssi, max_rssi;
893 
894 	rssi = ic->ic_node_getrssi(in);
895 	max_rssi = (ic->ic_maxrssi == 0) ? IEEE80211_MAXRSSI : ic->ic_maxrssi;
896 	if (rssi == 0)
897 		rssi = 0;
898 	else if (rssi >= max_rssi)
899 		rssi = MAX_RSSI;
900 	else
901 		rssi = rssi * MAX_RSSI / max_rssi + 1;
902 
903 	return (rssi);
904 }
905 
906 static int
907 wifi_cfg_rssi(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
908 {
909 	mblk_t *omp;
910 	wldp_t *outp;
911 	wl_rssi_t *ow_rssi;
912 	int err = 0;
913 
914 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_rssi_t))) == NULL)
915 		return (ENOMEM);
916 	outp = (wldp_t *)omp->b_rptr;
917 	ow_rssi = (wl_rssi_t *)outp->wldp_buf;
918 
919 	switch (cmd) {
920 	case  WLAN_GET_PARAM:
921 		*ow_rssi = wifi_getrssi(ic->ic_bss);
922 		break;
923 	case  WLAN_SET_PARAM:
924 		outp->wldp_result = WL_READONLY;
925 		err = EINVAL;
926 		break;
927 	default:
928 		ieee80211_err("wifi_cfg_rssi: unknown command %x\n", cmd);
929 		outp->wldp_result = WL_NOTSUPPORTED;
930 		return (EINVAL);
931 	}
932 
933 	freemsg(*mp);
934 	*mp = omp;
935 	return (err);
936 }
937 
938 /*
939  * maximum scan wait time in second.
940  * Time spent on scaning one channel is usually 100~200ms. The maximum
941  * number of channels defined in wifi_ioctl.h is 99 (MAX_CHANNEL_NUM).
942  * As a result the maximum total scan time is defined as below in ms.
943  */
944 #define	WAIT_SCAN_MAX	(200 * MAX_CHANNEL_NUM)
945 
946 static void
947 wifi_wait_scan(struct ieee80211com *ic)
948 {
949 	ieee80211_impl_t *im = ic->ic_private;
950 
951 	while ((ic->ic_flags & (IEEE80211_F_SCAN | IEEE80211_F_ASCAN)) != 0) {
952 		if (cv_timedwait_sig(&im->im_scan_cv, &ic->ic_genlock,
953 		    ddi_get_lbolt() + drv_usectohz(WAIT_SCAN_MAX * 1000)) !=
954 		    0) {
955 			break;
956 		}
957 	}
958 }
959 
960 #define	WIFI_HAVE_CAP(in, flag)	(((in)->in_capinfo & (flag)) ? 1 : 0)
961 
962 /*
963  * Callback function used by ieee80211_iterate_nodes() in
964  * wifi_cfg_esslist() to get info of each node in a node table
965  *    arg  output buffer, pointer to wl_ess_list_t
966  *    in   each node in the node table
967  */
968 static void
969 wifi_read_ap(void *arg, struct ieee80211_node *in)
970 {
971 	wl_ess_list_t *aps = arg;
972 	ieee80211com_t *ic = in->in_ic;
973 	struct ieee80211_channel *chan = in->in_chan;
974 	struct ieee80211_rateset *rates = &(in->in_rates);
975 	wl_ess_conf_t *conf;
976 	uint8_t *end;
977 	uint_t i, nrates;
978 
979 	end = (uint8_t *)aps - WIFI_BUF_OFFSET + MAX_BUF_LEN -
980 	    sizeof (wl_ess_list_t);
981 	conf = &aps->wl_ess_list_ess[aps->wl_ess_list_num];
982 	if ((uint8_t *)conf > end)
983 		return;
984 
985 	/* skip newly allocated NULL bss node */
986 	if (IEEE80211_ADDR_EQ(in->in_macaddr, ic->ic_macaddr))
987 		return;
988 
989 	conf->wl_ess_conf_essid.wl_essid_length = in->in_esslen;
990 	bcopy(in->in_essid, conf->wl_ess_conf_essid.wl_essid_essid,
991 	    in->in_esslen);
992 	bcopy(in->in_bssid, conf->wl_ess_conf_bssid, IEEE80211_ADDR_LEN);
993 	conf->wl_ess_conf_wepenabled =
994 	    (in->in_capinfo & IEEE80211_CAPINFO_PRIVACY ?
995 	    WL_ENC_WEP : WL_NOENCRYPTION);
996 	conf->wl_ess_conf_bsstype =
997 	    (in->in_capinfo & IEEE80211_CAPINFO_ESS ?
998 	    WL_BSS_BSS : WL_BSS_IBSS);
999 	conf->wl_ess_conf_sl = wifi_getrssi(in);
1000 	conf->wl_ess_conf_reserved[0] = (in->in_wpa_ie == NULL? 0 : 1);
1001 
1002 	/* physical (FH, DS, ERP) parameters */
1003 	if (IEEE80211_IS_CHAN_A(chan) || IEEE80211_IS_CHAN_T(chan)) {
1004 		wl_ofdm_t *ofdm =
1005 		    (wl_ofdm_t *)&((conf->wl_phy_conf).wl_phy_ofdm_conf);
1006 		ofdm->wl_ofdm_subtype = WL_OFDM;
1007 		ofdm->wl_ofdm_frequency = chan->ich_freq;
1008 	} else {
1009 		switch (in->in_phytype) {
1010 		case IEEE80211_T_FH: {
1011 			wl_fhss_t *fhss = (wl_fhss_t *)
1012 			    &((conf->wl_phy_conf).wl_phy_fhss_conf);
1013 
1014 			fhss->wl_fhss_subtype = WL_FHSS;
1015 			fhss->wl_fhss_channel = ieee80211_chan2ieee(ic, chan);
1016 			fhss->wl_fhss_dwelltime = in->in_fhdwell;
1017 			break;
1018 		}
1019 		case IEEE80211_T_DS: {
1020 			wl_dsss_t *dsss = (wl_dsss_t *)
1021 			    &((conf->wl_phy_conf).wl_phy_dsss_conf);
1022 
1023 			dsss->wl_dsss_subtype = WL_DSSS;
1024 			dsss->wl_dsss_channel = ieee80211_chan2ieee(ic, chan);
1025 			dsss->wl_dsss_have_short_preamble = WIFI_HAVE_CAP(in,
1026 			    IEEE80211_CAPINFO_SHORT_PREAMBLE);
1027 			dsss->wl_dsss_agility_enabled = WIFI_HAVE_CAP(in,
1028 			    IEEE80211_CAPINFO_CHNL_AGILITY);
1029 			dsss->wl_dsss_have_pbcc = dsss->wl_dsss_pbcc_enable =
1030 			    WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_PBCC);
1031 			break;
1032 		}
1033 		case IEEE80211_T_OFDM: {
1034 			wl_erp_t *erp = (wl_erp_t *)
1035 			    &((conf->wl_phy_conf).wl_phy_erp_conf);
1036 
1037 			erp->wl_erp_subtype = WL_ERP;
1038 			erp->wl_erp_channel = ieee80211_chan2ieee(ic, chan);
1039 			erp->wl_erp_have_short_preamble = WIFI_HAVE_CAP(in,
1040 			    IEEE80211_CAPINFO_SHORT_PREAMBLE);
1041 			erp->wl_erp_have_agility = erp->wl_erp_agility_enabled =
1042 			    WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_CHNL_AGILITY);
1043 			erp->wl_erp_have_pbcc = erp->wl_erp_pbcc_enabled =
1044 			    WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_PBCC);
1045 			erp->wl_erp_dsss_ofdm_enabled =
1046 			    WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_DSSSOFDM);
1047 			erp->wl_erp_sst_enabled = WIFI_HAVE_CAP(in,
1048 			    IEEE80211_CAPINFO_SHORT_SLOTTIME);
1049 			break;
1050 		} /* case IEEE80211_T_OFDM */
1051 		} /* switch in->in_phytype */
1052 	}
1053 
1054 	/* supported rates */
1055 	nrates = MIN(rates->ir_nrates, MAX_SCAN_SUPPORT_RATES);
1056 	/*
1057 	 * The number of supported rates might exceed
1058 	 * MAX_SCAN_SUPPORT_RATES. Fill in highest rates
1059 	 * first so userland command could properly show
1060 	 * maximum speed of AP
1061 	 */
1062 	for (i = 0; i < nrates; i++) {
1063 		conf->wl_supported_rates[i] =
1064 		    rates->ir_rates[rates->ir_nrates - i - 1];
1065 	}
1066 
1067 	aps->wl_ess_list_num++;
1068 }
1069 
1070 static int
1071 wifi_cfg_esslist(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1072 {
1073 	mblk_t *omp;
1074 	wldp_t *outp;
1075 	wl_ess_list_t *ow_aps;
1076 	int err = 0;
1077 
1078 	if ((omp = wifi_getoutmsg(*mp, cmd, MAX_BUF_LEN - WIFI_BUF_OFFSET)) ==
1079 	    NULL) {
1080 		return (ENOMEM);
1081 	}
1082 	outp = (wldp_t *)omp->b_rptr;
1083 	ow_aps = (wl_ess_list_t *)outp->wldp_buf;
1084 
1085 	switch (cmd) {
1086 	case WLAN_GET_PARAM:
1087 		ow_aps->wl_ess_list_num = 0;
1088 		ieee80211_iterate_nodes(&ic->ic_scan, wifi_read_ap, ow_aps);
1089 		outp->wldp_length = WIFI_BUF_OFFSET +
1090 		    offsetof(wl_ess_list_t, wl_ess_list_ess) +
1091 		    ow_aps->wl_ess_list_num * sizeof (wl_ess_conf_t);
1092 		omp->b_wptr = omp->b_rptr + outp->wldp_length;
1093 		break;
1094 	case WLAN_SET_PARAM:
1095 		outp->wldp_result = WL_READONLY;
1096 		err = EINVAL;
1097 		break;
1098 	default:
1099 		ieee80211_err("wifi_cfg_esslist: unknown command %x\n", cmd);
1100 		outp->wldp_result = WL_NOTSUPPORTED;
1101 		err = EINVAL;
1102 		break;
1103 	}
1104 
1105 	freemsg(*mp);
1106 	*mp = omp;
1107 	return (err);
1108 }
1109 
1110 /*
1111  * Scan the network for all available ESSs.
1112  * IEEE80211_F_SCANONLY is set when current state is INIT. And
1113  * with this flag, after scan the state will be changed back to
1114  * INIT. The reason is at the end of SCAN stage, the STA will
1115  * consequently connect to an AP. Then it looks unreasonable that
1116  * for a disconnected device, A SCAN command causes it connected.
1117  * So the state is changed back to INIT.
1118  */
1119 static int
1120 wifi_cmd_scan(struct ieee80211com *ic, mblk_t *mp)
1121 {
1122 	int ostate = ic->ic_state;
1123 
1124 	/*
1125 	 * Do not scan when current state is RUN. The reason is
1126 	 * when connected, STA is on the same channel as AP. But
1127 	 * to do scan, STA have to switch to each available channel,
1128 	 * send probe request and wait certian time for probe
1129 	 * response/beacon. Then when the STA switches to a channel
1130 	 * different than AP's, as a result it cannot send/receive
1131 	 * data packets to/from the connected WLAN. This eventually
1132 	 * will cause data loss.
1133 	 */
1134 	if (ostate == IEEE80211_S_RUN)
1135 		return (0);
1136 
1137 	IEEE80211_UNLOCK(ic);
1138 
1139 	ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
1140 	IEEE80211_LOCK(ic);
1141 	if (ostate == IEEE80211_S_INIT)
1142 		ic->ic_flags |= IEEE80211_F_SCANONLY;
1143 
1144 	/* Don't wait on WPA mode */
1145 	if ((ic->ic_flags & IEEE80211_F_WPA) == 0) {
1146 		/* wait scan complete */
1147 		wifi_wait_scan(ic);
1148 	}
1149 
1150 	wifi_setupoutmsg(mp, 0);
1151 	return (0);
1152 }
1153 
1154 static void
1155 wifi_loaddefdata(struct ieee80211com *ic)
1156 {
1157 	struct ieee80211_node *in = ic->ic_bss;
1158 	int i;
1159 
1160 	ic->ic_des_esslen = 0;
1161 	bzero(ic->ic_des_essid, IEEE80211_NWID_LEN);
1162 	ic->ic_flags &= ~IEEE80211_F_DESBSSID;
1163 	bzero(ic->ic_des_bssid, IEEE80211_ADDR_LEN);
1164 	bzero(ic->ic_bss->in_bssid, IEEE80211_ADDR_LEN);
1165 	ic->ic_des_chan = IEEE80211_CHAN_ANYC;
1166 	ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
1167 	bzero(ic->ic_nickname, IEEE80211_NWID_LEN);
1168 	in->in_authmode = IEEE80211_AUTH_OPEN;
1169 	ic->ic_flags &= ~IEEE80211_F_PRIVACY;
1170 	ic->ic_flags &= ~IEEE80211_F_WPA;	/* mask WPA mode */
1171 	ic->ic_evq_head = ic->ic_evq_tail = 0;	/* reset Queue */
1172 	ic->ic_def_txkey = 0;
1173 	for (i = 0; i < MAX_NWEPKEYS; i++) {
1174 		ic->ic_nw_keys[i].wk_keylen = 0;
1175 		bzero(ic->ic_nw_keys[i].wk_key, IEEE80211_KEYBUF_SIZE);
1176 	}
1177 	ic->ic_curmode = IEEE80211_MODE_AUTO;
1178 }
1179 
1180 static int
1181 wifi_cmd_loaddefaults(struct ieee80211com *ic, mblk_t *mp)
1182 {
1183 	wifi_loaddefdata(ic);
1184 	wifi_setupoutmsg(mp, 0);
1185 	return (ENETRESET);
1186 }
1187 
1188 static int
1189 wifi_cmd_disassoc(struct ieee80211com *ic, mblk_t *mp)
1190 {
1191 	if (ic->ic_state != IEEE80211_S_INIT) {
1192 		IEEE80211_UNLOCK(ic);
1193 		(void) ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
1194 		IEEE80211_LOCK(ic);
1195 	}
1196 	wifi_loaddefdata(ic);
1197 	wifi_setupoutmsg(mp, 0);
1198 	return (0);
1199 }
1200 
1201 /*
1202  * Get the capabilities of drivers.
1203  */
1204 static int
1205 wifi_cfg_caps(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1206 {
1207 	mblk_t *omp;
1208 	wldp_t *outp;
1209 	wl_capability_t *o_caps;
1210 	int err = 0;
1211 
1212 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_capability_t))) == NULL)
1213 		return (ENOMEM);
1214 	outp = (wldp_t *)omp->b_rptr;
1215 	o_caps = (wl_capability_t *)outp->wldp_buf;
1216 
1217 	switch (cmd) {
1218 	case WLAN_GET_PARAM:
1219 		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_caps: "
1220 		    "ic_caps = %u\n", ic->ic_caps);
1221 		o_caps->caps = ic->ic_caps;
1222 		break;
1223 	case WLAN_SET_PARAM:
1224 		outp->wldp_result = WL_READONLY;
1225 		err = EINVAL;
1226 		break;
1227 	default:
1228 		ieee80211_err("wifi_cfg_caps: unknown command %x\n", cmd);
1229 		outp->wldp_result = WL_NOTSUPPORTED;
1230 		err = EINVAL;
1231 		break;
1232 	}
1233 
1234 	freemsg(*mp);
1235 	*mp = omp;
1236 	return (err);
1237 }
1238 
1239 /*
1240  * Operating on WPA mode.
1241  */
1242 static int
1243 wifi_cfg_wpa(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1244 {
1245 	mblk_t *omp;
1246 	wldp_t *outp;
1247 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
1248 	wl_wpa_t *wpa = (wl_wpa_t *)inp->wldp_buf;
1249 	wl_wpa_t *o_wpa;
1250 	int err = 0;
1251 
1252 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_wpa_t))) == NULL)
1253 		return (ENOMEM);
1254 	outp = (wldp_t *)omp->b_rptr;
1255 	o_wpa = (wl_wpa_t *)outp->wldp_buf;
1256 
1257 	switch (cmd) {
1258 	case WLAN_GET_PARAM:
1259 		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_wpa: "
1260 		    "get wpa=%u\n", wpa->wpa_flag);
1261 		o_wpa->wpa_flag = ((ic->ic_flags & IEEE80211_F_WPA)? 1 : 0);
1262 		break;
1263 	case WLAN_SET_PARAM:
1264 		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_wpa: "
1265 		    "set wpa=%u\n", wpa->wpa_flag);
1266 		if (wpa->wpa_flag > 0) {	/* enable WPA mode */
1267 			ic->ic_flags |= IEEE80211_F_PRIVACY;
1268 			ic->ic_flags |= IEEE80211_F_WPA;
1269 		} else {
1270 			ic->ic_flags &= ~IEEE80211_F_PRIVACY;
1271 			ic->ic_flags &= ~IEEE80211_F_WPA;
1272 		}
1273 		break;
1274 	default:
1275 		ieee80211_err("wifi_cfg_wpa: unknown command %x\n", cmd);
1276 		outp->wldp_result = WL_NOTSUPPORTED;
1277 		err = EINVAL;
1278 		break;
1279 	}
1280 
1281 	freemsg(*mp);
1282 	*mp = omp;
1283 	return (err);
1284 }
1285 
1286 /*
1287  * WPA daemon set the WPA keys.
1288  * The WPA keys are negotiated with APs through wpa service.
1289  */
1290 static int
1291 wifi_cfg_wpakey(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1292 {
1293 	mblk_t *omp;
1294 	wldp_t *outp;
1295 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
1296 	wl_key_t *ik = (wl_key_t *)(inp->wldp_buf);
1297 	struct ieee80211_node *in;
1298 	struct ieee80211_key *wk;
1299 	uint16_t kid;
1300 	int err = 0;
1301 
1302 	if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
1303 		return (ENOMEM);
1304 	outp = (wldp_t *)omp->b_rptr;
1305 
1306 	switch (cmd) {
1307 	case WLAN_GET_PARAM:
1308 		outp->wldp_result = WL_WRITEONLY;
1309 		err = EINVAL;
1310 		break;
1311 	case WLAN_SET_PARAM:
1312 		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_wpakey: "
1313 		    "idx=%d\n", ik->ik_keyix);
1314 		/* NB: cipher support is verified by ieee80211_crypt_newkey */
1315 		/* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */
1316 		if (ik->ik_keylen > sizeof (ik->ik_keydata)) {
1317 			ieee80211_err("wifi_cfg_wpakey: key too long\n");
1318 			outp->wldp_result = WL_NOTSUPPORTED;
1319 			err = EINVAL;
1320 			break;
1321 		}
1322 		kid = ik->ik_keyix;
1323 		if (kid == IEEE80211_KEYIX_NONE || kid >= IEEE80211_WEP_NKID) {
1324 			ieee80211_err("wifi_cfg_wpakey: incorrect keyix\n");
1325 			outp->wldp_result = WL_NOTSUPPORTED;
1326 			err = EINVAL;
1327 			break;
1328 
1329 		} else {
1330 			wk = &ic->ic_nw_keys[kid];
1331 			/*
1332 			 * Global slots start off w/o any assigned key index.
1333 			 * Force one here for consistency with WEPKEY.
1334 			 */
1335 			if (wk->wk_keyix == IEEE80211_KEYIX_NONE)
1336 				wk->wk_keyix = kid;
1337 			/* in = ic->ic_bss; */
1338 			in = NULL;
1339 		}
1340 
1341 		KEY_UPDATE_BEGIN(ic);
1342 		if (ieee80211_crypto_newkey(ic, ik->ik_type,
1343 		    ik->ik_flags, wk)) {
1344 			wk->wk_keylen = ik->ik_keylen;
1345 			/* NB: MIC presence is implied by cipher type */
1346 			if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE)
1347 				wk->wk_keylen = IEEE80211_KEYBUF_SIZE;
1348 			wk->wk_keyrsc = ik->ik_keyrsc;
1349 			wk->wk_keytsc = 0;		/* new key, reset */
1350 			wk->wk_flags |= ik->ik_flags &
1351 			    (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV);
1352 			(void) memset(wk->wk_key, 0, sizeof (wk->wk_key));
1353 			(void) memcpy(wk->wk_key, ik->ik_keydata,
1354 			    ik->ik_keylen);
1355 			if (!ieee80211_crypto_setkey(ic, wk,
1356 			    in != NULL ? in->in_macaddr : ik->ik_macaddr)) {
1357 				err = EIO;
1358 				outp->wldp_result = WL_HW_ERROR;
1359 			} else if ((ik->ik_flags & IEEE80211_KEY_DEFAULT)) {
1360 				ic->ic_def_txkey = kid;
1361 				ieee80211_mac_update(ic);
1362 			}
1363 		} else {
1364 			err = EIO;
1365 			outp->wldp_result = WL_HW_ERROR;
1366 		}
1367 		KEY_UPDATE_END(ic);
1368 		break;
1369 	default:
1370 		ieee80211_err("wifi_cfg_wpakey: unknown command %x\n", cmd);
1371 		outp->wldp_result = WL_NOTSUPPORTED;
1372 		err = EINVAL;
1373 		break;
1374 	}
1375 
1376 	freemsg(*mp);
1377 	*mp = omp;
1378 	return (err);
1379 }
1380 
1381 /*
1382  * Delete obsolete keys - keys are dynamically exchanged between APs
1383  * and wpa daemon.
1384  */
1385 static int
1386 wifi_cfg_delkey(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1387 {
1388 	mblk_t *omp;
1389 	wldp_t *outp;
1390 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
1391 	wl_del_key_t *dk = (wl_del_key_t *)inp->wldp_buf;
1392 	int kid;
1393 	int err = 0;
1394 
1395 	if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
1396 		return (ENOMEM);
1397 	outp = (wldp_t *)omp->b_rptr;
1398 
1399 	switch (cmd) {
1400 	case WLAN_GET_PARAM:
1401 		outp->wldp_result = WL_WRITEONLY;
1402 		err = EINVAL;
1403 		break;
1404 	case WLAN_SET_PARAM:
1405 		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_delkey: "
1406 		    "keyix=%d\n", dk->idk_keyix);
1407 		kid = dk->idk_keyix;
1408 		if (kid == IEEE80211_KEYIX_NONE || kid >= IEEE80211_WEP_NKID) {
1409 			ieee80211_err("wifi_cfg_delkey: incorrect keyix\n");
1410 			outp->wldp_result = WL_NOTSUPPORTED;
1411 			err = EINVAL;
1412 			break;
1413 
1414 		} else {
1415 			(void) ieee80211_crypto_delkey(ic,
1416 			    &ic->ic_nw_keys[kid]);
1417 			ieee80211_mac_update(ic);
1418 		}
1419 		break;
1420 	default:
1421 		ieee80211_err("wifi_cfg_delkey: unknown command %x\n", cmd);
1422 		outp->wldp_result = WL_NOTSUPPORTED;
1423 		err = EINVAL;
1424 		break;
1425 	}
1426 
1427 	freemsg(*mp);
1428 	*mp = omp;
1429 	return (err);
1430 }
1431 
1432 /*
1433  * The OPTIE will be used in the association request.
1434  */
1435 static int
1436 wifi_cfg_setoptie(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1437 {
1438 	mblk_t *omp;
1439 	wldp_t *outp;
1440 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
1441 	wl_wpa_ie_t *ie_in = (wl_wpa_ie_t *)inp->wldp_buf;
1442 	char *ie;
1443 	int err = 0;
1444 
1445 	if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
1446 		return (ENOMEM);
1447 	outp = (wldp_t *)omp->b_rptr;
1448 
1449 	switch (cmd) {
1450 	case WLAN_GET_PARAM:
1451 		outp->wldp_result = WL_WRITEONLY;
1452 		err = EINVAL;
1453 		break;
1454 	case WLAN_SET_PARAM:
1455 		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_setoptie\n");
1456 		/*
1457 		 * NB: Doing this for ap operation could be useful (e.g. for
1458 		 * WPA and/or WME) except that it typically is worthless
1459 		 * without being able to intervene when processing
1460 		 * association response frames--so disallow it for now.
1461 		 */
1462 		if (ic->ic_opmode != IEEE80211_M_STA) {
1463 			ieee80211_err("wifi_cfg_setoptie: opmode err\n");
1464 			err = EINVAL;
1465 			outp->wldp_result = WL_NOTSUPPORTED;
1466 			break;
1467 		}
1468 		if (ie_in->wpa_ie_len > IEEE80211_MAX_OPT_IE) {
1469 			ieee80211_err("wifi_cfg_setoptie: optie too long\n");
1470 			err = EINVAL;
1471 			outp->wldp_result = WL_NOTSUPPORTED;
1472 			break;
1473 		}
1474 
1475 		ie = ieee80211_malloc(ie_in->wpa_ie_len);
1476 		(void) memcpy(ie, ie_in->wpa_ie, ie_in->wpa_ie_len);
1477 		if (ic->ic_opt_ie != NULL)
1478 			ieee80211_free(ic->ic_opt_ie);
1479 		ic->ic_opt_ie = ie;
1480 		ic->ic_opt_ie_len = ie_in->wpa_ie_len;
1481 		break;
1482 	default:
1483 		ieee80211_err("wifi_cfg_setoptie: unknown command %x\n", cmd);
1484 		outp->wldp_result = WL_NOTSUPPORTED;
1485 		err = EINVAL;
1486 		break;
1487 	}
1488 
1489 	freemsg(*mp);
1490 	*mp = omp;
1491 	return (err);
1492 }
1493 
1494 /*
1495  * To be compatible with drivers/tools of OpenSolaris.org,
1496  * we use a different ID to filter out those APs of WPA mode.
1497  */
1498 static int
1499 wifi_cfg_scanresults(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1500 {
1501 	mblk_t *omp;
1502 	wldp_t *outp;
1503 	wl_wpa_ess_t *sr;
1504 	ieee80211_node_t *in;
1505 	ieee80211_node_table_t *nt;
1506 	int len, ap_num = 0;
1507 	int err = 0;
1508 
1509 	if ((omp = wifi_getoutmsg(*mp, cmd, MAX_BUF_LEN - WIFI_BUF_OFFSET)) ==
1510 	    NULL) {
1511 		return (ENOMEM);
1512 	}
1513 	outp = (wldp_t *)omp->b_rptr;
1514 	sr = (wl_wpa_ess_t *)outp->wldp_buf;
1515 	sr->count = 0;
1516 
1517 	switch (cmd) {
1518 	case WLAN_GET_PARAM:
1519 		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_scanresults\n");
1520 		nt = &ic->ic_scan;
1521 		IEEE80211_NODE_LOCK(nt);
1522 		in = list_head(&nt->nt_node);
1523 		while (in != NULL) {
1524 			/* filter out non-WPA APs */
1525 			if (in->in_wpa_ie == NULL) {
1526 				in = list_next(&nt->nt_node, in);
1527 				continue;
1528 			}
1529 			bcopy(in->in_bssid, sr->ess[ap_num].bssid,
1530 			    IEEE80211_ADDR_LEN);
1531 			sr->ess[ap_num].ssid_len = in->in_esslen;
1532 			bcopy(in->in_essid, sr->ess[ap_num].ssid,
1533 			    in->in_esslen);
1534 			sr->ess[ap_num].freq = in->in_chan->ich_freq;
1535 
1536 			len = in->in_wpa_ie[1] + 2;
1537 			bcopy(in->in_wpa_ie, sr->ess[ap_num].wpa_ie, len);
1538 			sr->ess[ap_num].wpa_ie_len = len;
1539 
1540 			ap_num ++;
1541 			in = list_next(&nt->nt_node, in);
1542 		}
1543 		IEEE80211_NODE_UNLOCK(nt);
1544 		sr->count = ap_num;
1545 		outp->wldp_length = WIFI_BUF_OFFSET +
1546 		    offsetof(wl_wpa_ess_t, ess) +
1547 		    sr->count * sizeof (struct wpa_ess);
1548 		omp->b_wptr = omp->b_rptr + outp->wldp_length;
1549 		break;
1550 	case WLAN_SET_PARAM:
1551 		outp->wldp_result = WL_READONLY;
1552 		err = EINVAL;
1553 		break;
1554 	default:
1555 		ieee80211_err("wifi_cfg_scanresults: unknown cmmand %x\n", cmd);
1556 		outp->wldp_result = WL_NOTSUPPORTED;
1557 		err = EINVAL;
1558 		break;
1559 	}
1560 
1561 	freemsg(*mp);
1562 	*mp = omp;
1563 	return (err);
1564 }
1565 
1566 /*
1567  * Manually control the state of AUTH | DEAUTH | DEASSOC | ASSOC
1568  */
1569 static int
1570 wifi_cfg_setmlme(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1571 {
1572 	mblk_t *omp;
1573 	wldp_t *outp;
1574 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
1575 	wl_mlme_t *mlme = (wl_mlme_t *)inp->wldp_buf;
1576 	ieee80211_node_t *in;
1577 	int err = 0;
1578 	uint32_t flags;
1579 
1580 	if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
1581 		return (ENOMEM);
1582 	outp = (wldp_t *)omp->b_rptr;
1583 
1584 	switch (cmd) {
1585 	case WLAN_GET_PARAM:
1586 		outp->wldp_result = WL_WRITEONLY;
1587 		err = EINVAL;
1588 		break;
1589 	case WLAN_SET_PARAM:
1590 		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_setmlme: "
1591 		    "op=%d\n", mlme->im_op);
1592 		switch (mlme->im_op) {
1593 		case IEEE80211_MLME_DISASSOC:
1594 		case IEEE80211_MLME_DEAUTH:
1595 			if (ic->ic_opmode == IEEE80211_M_STA) {
1596 				/*
1597 				 * Mask ic_flags of IEEE80211_F_WPA to disable
1598 				 * ieee80211_notify temporarily.
1599 				 */
1600 				flags = ic->ic_flags;
1601 				ic->ic_flags &= ~IEEE80211_F_WPA;
1602 
1603 				IEEE80211_UNLOCK(ic);
1604 				ieee80211_new_state(ic, IEEE80211_S_INIT,
1605 				    mlme->im_reason);
1606 				IEEE80211_LOCK(ic);
1607 
1608 				ic->ic_flags = flags;
1609 			}
1610 			break;
1611 		case IEEE80211_MLME_ASSOC:
1612 			if (ic->ic_opmode != IEEE80211_M_STA) {
1613 				ieee80211_err("wifi_cfg_setmlme: opmode err\n");
1614 				err = EINVAL;
1615 				outp->wldp_result = WL_NOTSUPPORTED;
1616 				break;
1617 			}
1618 			if (ic->ic_des_esslen != 0) {
1619 			/*
1620 			 * Desired ssid specified; must match both bssid and
1621 			 * ssid to distinguish ap advertising multiple ssid's.
1622 			 */
1623 				in = ieee80211_find_node_with_ssid(&ic->ic_scan,
1624 				    mlme->im_macaddr,
1625 				    ic->ic_des_esslen, ic->ic_des_essid);
1626 			} else {
1627 			/*
1628 			 * Normal case; just match bssid.
1629 			 */
1630 				in = ieee80211_find_node(&ic->ic_scan,
1631 				    mlme->im_macaddr);
1632 			}
1633 			if (in == NULL) {
1634 				ieee80211_err("wifi_cfg_setmlme: "
1635 				    "no matched node\n");
1636 				err = EINVAL;
1637 				outp->wldp_result = WL_NOTSUPPORTED;
1638 				break;
1639 			}
1640 			IEEE80211_UNLOCK(ic);
1641 			ieee80211_sta_join(ic, in);
1642 			IEEE80211_LOCK(ic);
1643 		}
1644 		break;
1645 	default:
1646 		ieee80211_err("wifi_cfg_delkey: unknown command %x\n", cmd);
1647 		outp->wldp_result = WL_NOTSUPPORTED;
1648 		err = EINVAL;
1649 		break;
1650 	}
1651 
1652 	freemsg(*mp);
1653 	*mp = omp;
1654 	return (err);
1655 }
1656 
1657 static int
1658 wifi_cfg_getset(struct ieee80211com *ic, mblk_t **mp, uint32_t cmd)
1659 {
1660 	mblk_t *mp1 = *mp;
1661 	wldp_t *wp = (wldp_t *)mp1->b_rptr;
1662 	int err = 0;
1663 
1664 	ASSERT(ic != NULL && mp1 != NULL);
1665 	IEEE80211_LOCK_ASSERT(ic);
1666 	if (MBLKL(mp1) < WIFI_BUF_OFFSET) {
1667 		ieee80211_err("wifi_cfg_getset: "
1668 		    "invalid input buffer, size=%d\n", MBLKL(mp1));
1669 		return (EINVAL);
1670 	}
1671 
1672 	switch (wp->wldp_id) {
1673 	/* Commands */
1674 	case WL_SCAN:
1675 		err = wifi_cmd_scan(ic, mp1);
1676 		break;
1677 	case WL_LOAD_DEFAULTS:
1678 		err = wifi_cmd_loaddefaults(ic, mp1);
1679 		break;
1680 	case WL_DISASSOCIATE:
1681 		err = wifi_cmd_disassoc(ic, mp1);
1682 		break;
1683 	/* Parameters */
1684 	case WL_ESSID:
1685 		err = wifi_cfg_essid(ic, cmd, mp);
1686 		break;
1687 	case WL_BSSID:
1688 		err = wifi_cfg_bssid(ic, cmd, mp);
1689 		break;
1690 	case WL_NODE_NAME:
1691 		err = wifi_cfg_nodename(ic, cmd, mp);
1692 		break;
1693 	case WL_PHY_CONFIG:
1694 		err = wifi_cfg_phy(ic, cmd, mp);
1695 		break;
1696 	case WL_WEP_KEY_TAB:
1697 		err = wifi_cfg_wepkey(ic, cmd, mp);
1698 		break;
1699 	case WL_WEP_KEY_ID:
1700 		err = wifi_cfg_keyid(ic, cmd, mp);
1701 		break;
1702 	case WL_AUTH_MODE:
1703 		err = wifi_cfg_authmode(ic, cmd, mp);
1704 		break;
1705 	case WL_ENCRYPTION:
1706 		err = wifi_cfg_encrypt(ic, cmd, mp);
1707 		break;
1708 	case WL_BSS_TYPE:
1709 		err = wifi_cfg_bsstype(ic, cmd, mp);
1710 		break;
1711 	case WL_DESIRED_RATES:
1712 		err = wifi_cfg_desrates(ic, cmd, mp);
1713 		break;
1714 	case WL_LINKSTATUS:
1715 		err = wifi_cfg_linkstatus(ic, cmd, mp);
1716 		break;
1717 	case WL_ESS_LIST:
1718 		err = wifi_cfg_esslist(ic, cmd, mp);
1719 		break;
1720 	case WL_SUPPORTED_RATES:
1721 		err = wifi_cfg_suprates(ic, cmd, mp);
1722 		break;
1723 	case WL_RSSI:
1724 		err = wifi_cfg_rssi(ic, cmd, mp);
1725 		break;
1726 	/*
1727 	 * WPA IOCTLs
1728 	 */
1729 	case WL_CAPABILITY:
1730 		err = wifi_cfg_caps(ic, cmd, mp);
1731 		break;
1732 	case WL_WPA:
1733 		err = wifi_cfg_wpa(ic, cmd, mp);
1734 		break;
1735 	case WL_KEY:
1736 		err = wifi_cfg_wpakey(ic, cmd, mp);
1737 		break;
1738 	case WL_DELKEY:
1739 		err = wifi_cfg_delkey(ic, cmd, mp);
1740 		break;
1741 	case WL_SETOPTIE:
1742 		err = wifi_cfg_setoptie(ic, cmd, mp);
1743 		break;
1744 	case WL_SCANRESULTS:
1745 		err = wifi_cfg_scanresults(ic, cmd, mp);
1746 		break;
1747 	case WL_MLME:
1748 		err = wifi_cfg_setmlme(ic, cmd, mp);
1749 		break;
1750 	default:
1751 		wifi_setupoutmsg(mp1, 0);
1752 		wp->wldp_result = WL_LACK_FEATURE;
1753 		err = ENOTSUP;
1754 		break;
1755 	}
1756 
1757 	return (err);
1758 }
1759 
1760 /*
1761  * Typically invoked by drivers in response to requests for
1762  * information or to change settings from the userland.
1763  *
1764  * Return value should be checked by WiFi drivers. Return 0
1765  * on success. Otherwise, return non-zero value to indicate
1766  * the error. Driver should operate as below when the return
1767  * error is:
1768  * ENETRESET	Reset wireless network and re-start to join a
1769  *		WLAN. ENETRESET is returned when a configuration
1770  *		parameter has been changed.
1771  *		When acknowledge a M_IOCTL message, thie error
1772  *		is ignored.
1773  */
1774 int
1775 ieee80211_ioctl(struct ieee80211com *ic, queue_t *wq, mblk_t *mp)
1776 {
1777 	struct iocblk *iocp;
1778 	int32_t cmd, err, len;
1779 	boolean_t need_privilege;
1780 	mblk_t *mp1;
1781 
1782 	if (MBLKL(mp) < sizeof (struct iocblk)) {
1783 		ieee80211_err("ieee80211_ioctl: ioctl buffer too short, %u\n",
1784 		    MBLKL(mp));
1785 		miocnak(wq, mp, 0, EINVAL);
1786 		return (EINVAL);
1787 	}
1788 
1789 	/*
1790 	 * Validate the command
1791 	 */
1792 	iocp = (struct iocblk *)mp->b_rptr;
1793 	iocp->ioc_error = 0;
1794 	cmd = iocp->ioc_cmd;
1795 	need_privilege = B_TRUE;
1796 	switch (cmd) {
1797 	case WLAN_SET_PARAM:
1798 	case WLAN_COMMAND:
1799 		break;
1800 	case WLAN_GET_PARAM:
1801 		need_privilege = B_FALSE;
1802 		break;
1803 	default:
1804 		ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_ioctl(): "
1805 		    "unknown cmd 0x%x\n", cmd);
1806 		miocnak(wq, mp, 0, EINVAL);
1807 		return (EINVAL);
1808 	}
1809 
1810 	if (need_privilege && (err = secpolicy_dl_config(iocp->ioc_cr)) != 0) {
1811 		miocnak(wq, mp, 0, err);
1812 		return (err);
1813 	}
1814 
1815 	IEEE80211_LOCK(ic);
1816 
1817 	/* sanity check */
1818 	mp1 = mp->b_cont;
1819 	if (iocp->ioc_count == 0 || iocp->ioc_count < sizeof (wldp_t) ||
1820 	    mp1 == NULL) {
1821 		miocnak(wq, mp, 0, EINVAL);
1822 		IEEE80211_UNLOCK(ic);
1823 		return (EINVAL);
1824 	}
1825 
1826 	/* assuming single data block */
1827 	if (mp1->b_cont != NULL) {
1828 		freemsg(mp1->b_cont);
1829 		mp1->b_cont = NULL;
1830 	}
1831 
1832 	err = wifi_cfg_getset(ic, &mp1, cmd);
1833 	mp->b_cont = mp1;
1834 	IEEE80211_UNLOCK(ic);
1835 
1836 	len = msgdsize(mp1);
1837 	/* ignore ENETRESET when acknowledge the M_IOCTL message */
1838 	if (err == 0 || err == ENETRESET)
1839 		miocack(wq, mp, len, 0);
1840 	else
1841 		miocack(wq, mp, len, err);
1842 
1843 	return (err);
1844 }
1845