xref: /illumos-gate/usr/src/uts/common/io/net80211/net80211_ioctl.c (revision 257873cfc1dd3337766407f80397db60a56f2f5a)
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 
35 #include <sys/param.h>
36 #include <sys/types.h>
37 #include <sys/errno.h>
38 #include <sys/strsun.h>
39 #include <sys/policy.h>
40 #include <inet/common.h>
41 #include <inet/nd.h>
42 #include <inet/mi.h>
43 #include <sys/note.h>
44 #include <sys/mac.h>
45 #include <inet/wifi_ioctl.h>
46 #include "net80211_impl.h"
47 
48 static int wl_set_essid(struct ieee80211com *, const void *);
49 static void wl_get_essid(struct ieee80211com *, void *);
50 static int wl_set_bssid(struct ieee80211com *, const void *);
51 static void wl_get_bssid(struct ieee80211com *, void *);
52 static int wl_set_bsstype(struct ieee80211com *, const void *);
53 static void wl_get_bsstype(struct ieee80211com *, void *);
54 static void wl_get_linkstatus(struct ieee80211com *, void *);
55 static int wl_set_desrates(struct ieee80211com *, const void *);
56 static void wl_get_desrates(struct ieee80211com *, void *);
57 static int wl_set_authmode(struct ieee80211com *, const void *);
58 static void wl_get_authmode(struct ieee80211com *, void *);
59 static int wl_set_encrypt(struct ieee80211com *, const void *);
60 static void wl_get_encrypt(struct ieee80211com *, void *);
61 static void wl_get_rssi(struct ieee80211com *, void *);
62 static int wl_set_phy(struct ieee80211com *, const void *);
63 static int wl_get_phy(struct ieee80211com *, void *);
64 static void wl_get_capability(struct ieee80211com *, void *);
65 static int wl_set_wpa(struct ieee80211com *, const void *);
66 static void wl_get_wpa(struct ieee80211com *, void *);
67 static void wl_get_scanresults(struct ieee80211com *, void *);
68 static void wl_get_esslist(struct ieee80211com *, void *);
69 static int wl_set_wepkey(struct ieee80211com *, const void *);
70 static int wl_set_optie(struct ieee80211com *, const void *);
71 static int wl_set_delkey(struct ieee80211com *, const void *);
72 static int wl_set_mlme(struct ieee80211com *, const void *);
73 static int wl_set_wpakey(struct ieee80211com *, const void *);
74 static void wl_get_suprates(struct ieee80211com *, void *);
75 
76 static size_t
77 wifi_strnlen(const char *s, size_t n)
78 {
79 	size_t i;
80 
81 	for (i = 0; i < n && s[i] != '\0'; i++)
82 		/* noop */;
83 	return (i);
84 }
85 
86 /*
87  * Initialize an output message block by copying from an
88  * input message block. The message is of type wldp_t.
89  *    mp     input message block
90  *    buflen length of wldp_buf
91  */
92 static void
93 wifi_setupoutmsg(mblk_t *mp, int buflen)
94 {
95 	wldp_t *wp;
96 
97 	wp = (wldp_t *)mp->b_rptr;
98 	wp->wldp_length = WIFI_BUF_OFFSET + buflen;
99 	wp->wldp_result = WL_SUCCESS;
100 	mp->b_wptr = mp->b_rptr + wp->wldp_length;
101 }
102 
103 /*
104  * Allocate and initialize an output message.
105  */
106 static mblk_t *
107 wifi_getoutmsg(mblk_t *mp, uint32_t cmd, int buflen)
108 {
109 	mblk_t *mp1;
110 	int size;
111 
112 	size = WIFI_BUF_OFFSET;
113 	if (cmd == WLAN_GET_PARAM)
114 		size += buflen;	/* to hold output parameters */
115 	mp1 = allocb(size, BPRI_HI);
116 	if (mp1 == NULL) {
117 		ieee80211_err("wifi_getoutbuf: allocb %d bytes failed!\n",
118 		    size);
119 		return (NULL);
120 	}
121 
122 	bzero(mp1->b_rptr, size);
123 	bcopy(mp->b_rptr, mp1->b_rptr, WIFI_BUF_OFFSET);
124 	wifi_setupoutmsg(mp1, size - WIFI_BUF_OFFSET);
125 
126 	return (mp1);
127 }
128 
129 static int
130 wifi_cfg_essid(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
131 {
132 	mblk_t *omp;
133 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
134 	wldp_t *outp;
135 	wl_essid_t *iw_essid = (wl_essid_t *)inp->wldp_buf;
136 	wl_essid_t *ow_essid;
137 	int err = 0;
138 
139 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_essid_t))) == NULL)
140 		return (ENOMEM);
141 	outp = (wldp_t *)omp->b_rptr;
142 	ow_essid = (wl_essid_t *)outp->wldp_buf;
143 
144 	switch (cmd) {
145 	case WLAN_GET_PARAM:
146 		wl_get_essid(ic, ow_essid);
147 		break;
148 	case WLAN_SET_PARAM:
149 		err = wl_set_essid(ic, iw_essid);
150 		break;
151 	default:
152 		ieee80211_err("wifi_cfg_essid: unknown command %x\n", cmd);
153 		outp->wldp_result = WL_NOTSUPPORTED;
154 		err = EINVAL;
155 		break;
156 	}
157 
158 	freemsg(*mp);
159 	*mp = omp;
160 	return (err);
161 }
162 
163 static int
164 wifi_cfg_bssid(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
165 {
166 	mblk_t *omp;
167 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
168 	wldp_t *outp;
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 		wl_get_bssid(ic, outp->wldp_buf);
178 		break;
179 	case WLAN_SET_PARAM:
180 		err = wl_set_bssid(ic, inp->wldp_buf);
181 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_bssid: "
182 		    "set bssid=%s\n",
183 		    ieee80211_macaddr_sprintf(inp->wldp_buf));
184 		break;
185 	default:
186 		ieee80211_err("wifi_cfg_bssid: unknown command %x\n", cmd);
187 		outp->wldp_result = WL_NOTSUPPORTED;
188 		err = EINVAL;
189 		break;
190 	}
191 
192 	freemsg(*mp);
193 	*mp = omp;
194 	return (err);
195 }
196 
197 static int
198 wifi_cfg_nodename(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
199 {
200 	mblk_t *omp;
201 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
202 	wldp_t *outp;
203 	wl_nodename_t *iw_name = (wl_nodename_t *)inp->wldp_buf;
204 	wl_nodename_t *ow_name;
205 	char *nodename;
206 	int len, err;
207 
208 	err = 0;
209 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_nodename_t))) == NULL)
210 		return (ENOMEM);
211 	outp = (wldp_t *)omp->b_rptr;
212 	ow_name = (wl_nodename_t *)outp->wldp_buf;
213 
214 	switch (cmd) {
215 	case WLAN_GET_PARAM:
216 		len = wifi_strnlen((const char *)ic->ic_nickname,
217 		    IEEE80211_NWID_LEN);
218 		ow_name->wl_nodename_length = len;
219 		bcopy(ic->ic_nickname, ow_name->wl_nodename_name, len);
220 		break;
221 	case WLAN_SET_PARAM:
222 		if (iw_name->wl_nodename_length > IEEE80211_NWID_LEN) {
223 			ieee80211_err("wifi_cfg_nodename: "
224 			    "node name too long, %u\n",
225 			    iw_name->wl_nodename_length);
226 			outp->wldp_result = WL_NOTSUPPORTED;
227 			err = EINVAL;
228 			break;
229 		}
230 		nodename = iw_name->wl_nodename_name;
231 		nodename[IEEE80211_NWID_LEN] = 0;
232 		ieee80211_dbg(IEEE80211_MSG_CONFIG,
233 		    "wifi_cfg_nodename: set nodename %s, len=%d\n",
234 		    nodename, iw_name->wl_nodename_length);
235 
236 		len = iw_name->wl_nodename_length;
237 		if (len > 0)
238 			bcopy(nodename, ic->ic_nickname, len);
239 		if (len < IEEE80211_NWID_LEN)
240 			ic->ic_nickname[len] = 0;
241 		break;
242 	default:
243 		ieee80211_err("wifi_cfg_nodename: unknown command %x\n", cmd);
244 		outp->wldp_result = WL_NOTSUPPORTED;
245 		err = EINVAL;
246 		break;
247 	}
248 
249 	freemsg(*mp);
250 	*mp = omp;
251 	return (err);
252 }
253 
254 static int
255 wifi_cfg_phy(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
256 {
257 	mblk_t *omp;
258 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
259 	wldp_t *outp;
260 	wl_phy_conf_t *iw_phy = (wl_phy_conf_t *)inp->wldp_buf;
261 	wl_phy_conf_t *ow_phy;
262 	int err = 0;
263 
264 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_phy_conf_t))) == NULL)
265 		return (ENOMEM);
266 	outp = (wldp_t *)omp->b_rptr;
267 	ow_phy = (wl_phy_conf_t *)outp->wldp_buf;
268 
269 	switch (cmd) {
270 	case WLAN_GET_PARAM:
271 		err = wl_get_phy(ic, ow_phy);
272 		break;
273 
274 	case WLAN_SET_PARAM:
275 		err = wl_set_phy(ic, iw_phy);
276 		break;
277 
278 	default:
279 		ieee80211_err("wifi_cfg_phy: unknown command %x\n", cmd);
280 		outp->wldp_result = WL_NOTSUPPORTED;
281 		err = EINVAL;
282 		break;
283 	} /* switch (cmd) */
284 
285 	freemsg(*mp);
286 	*mp = omp;
287 	return (err);
288 }
289 
290 static int
291 wifi_cfg_wepkey(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
292 {
293 	mblk_t *omp;
294 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
295 	wldp_t *outp;
296 	wl_wep_key_t *iw_wepkey = (wl_wep_key_t *)inp->wldp_buf;
297 	int err = 0;
298 
299 	if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
300 		return (ENOMEM);
301 	outp = (wldp_t *)omp->b_rptr;
302 
303 	switch (cmd) {
304 	case WLAN_GET_PARAM:
305 		outp->wldp_result = WL_WRITEONLY;
306 		err = EINVAL;
307 		break;
308 	case WLAN_SET_PARAM:
309 		if (inp->wldp_length < sizeof (wl_wep_key_tab_t)) {
310 			ieee80211_err("wifi_cfg_wepkey: "
311 			    "parameter too short, %d, expected %d\n",
312 			    inp->wldp_length, sizeof (wl_wep_key_tab_t));
313 			outp->wldp_result = WL_NOTSUPPORTED;
314 			err = EINVAL;
315 			break;
316 		}
317 
318 		err = wl_set_wepkey(ic, iw_wepkey);
319 		break;
320 	default:
321 		ieee80211_err("wifi_cfg_wepkey: unknown command %x\n", cmd);
322 		outp->wldp_result = WL_NOTSUPPORTED;
323 		err = EINVAL;
324 		break;
325 	}
326 
327 	freemsg(*mp);
328 	*mp = omp;
329 	return (err);
330 }
331 
332 static int
333 wifi_cfg_keyid(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
334 {
335 	mblk_t *omp;
336 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
337 	wldp_t *outp;
338 	wl_wep_key_id_t *iw_kid = (wl_wep_key_id_t *)inp->wldp_buf;
339 	wl_wep_key_id_t *ow_kid;
340 	int err = 0;
341 
342 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_wep_key_id_t))) == NULL)
343 		return (ENOMEM);
344 	outp = (wldp_t *)omp->b_rptr;
345 	ow_kid = (wl_wep_key_id_t *)outp->wldp_buf;
346 
347 	switch (cmd) {
348 	case WLAN_GET_PARAM:
349 		*ow_kid = (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) ?
350 		    0 : ic->ic_def_txkey;
351 		break;
352 	case  WLAN_SET_PARAM:
353 		if (*iw_kid >= MAX_NWEPKEYS) {
354 			ieee80211_err("wifi_cfg_keyid: "
355 			    "keyid too large, %u\n", *iw_kid);
356 			outp->wldp_result = WL_NOTSUPPORTED;
357 			err = EINVAL;
358 		} else {
359 			ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_keyid: "
360 			    "set keyid=%u\n", *iw_kid);
361 			ic->ic_def_txkey = *iw_kid;
362 			err = ENETRESET;
363 		}
364 		break;
365 	default:
366 		ieee80211_err("wifi_cfg_keyid: unknown command %x\n", cmd);
367 		outp->wldp_result = WL_NOTSUPPORTED;
368 		err = EINVAL;
369 		break;
370 	}
371 
372 	freemsg(*mp);
373 	*mp = omp;
374 	return (err);
375 }
376 
377 static int
378 wifi_cfg_authmode(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
379 {
380 	mblk_t *omp;
381 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
382 	wldp_t *outp;
383 	wl_authmode_t *iw_auth = (wl_authmode_t *)inp->wldp_buf;
384 	wl_authmode_t *ow_auth;
385 	int err = 0;
386 
387 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_authmode_t))) == NULL)
388 		return (ENOMEM);
389 	outp = (wldp_t *)omp->b_rptr;
390 	ow_auth = (wl_authmode_t *)outp->wldp_buf;
391 
392 	switch (cmd) {
393 	case WLAN_GET_PARAM:
394 		wl_get_authmode(ic, ow_auth);
395 		break;
396 	case WLAN_SET_PARAM:
397 		err = wl_set_authmode(ic, iw_auth);
398 		break;
399 	default:
400 		ieee80211_err("wifi_cfg_authmode: unknown command %x\n", cmd);
401 		outp->wldp_result = WL_NOTSUPPORTED;
402 		err = EINVAL;
403 		break;
404 	}
405 
406 	freemsg(*mp);
407 	*mp = omp;
408 	return (err);
409 }
410 
411 static int
412 wifi_cfg_encrypt(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
413 {
414 	mblk_t *omp;
415 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
416 	wldp_t *outp;
417 	wl_encryption_t *iw_encryp = (wl_encryption_t *)inp->wldp_buf;
418 	wl_encryption_t *ow_encryp;
419 	int err = 0;
420 
421 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_encryption_t))) == NULL)
422 		return (ENOMEM);
423 	outp = (wldp_t *)omp->b_rptr;
424 	ow_encryp = (wl_encryption_t *)outp->wldp_buf;
425 
426 	switch (cmd) {
427 	case WLAN_GET_PARAM:
428 		wl_get_encrypt(ic, ow_encryp);
429 		break;
430 	case WLAN_SET_PARAM:
431 		err = wl_set_encrypt(ic, iw_encryp);
432 		break;
433 	default:
434 		ieee80211_err("wifi_cfg_encrypt: unknown command %x\n", cmd);
435 		outp->wldp_result = WL_NOTSUPPORTED;
436 		err = EINVAL;
437 		break;
438 	}
439 
440 	freemsg(*mp);
441 	*mp = omp;
442 	return (err);
443 }
444 
445 static int
446 wifi_cfg_bsstype(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
447 {
448 	mblk_t *omp;
449 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
450 	wldp_t *outp;
451 	wl_bss_type_t *iw_opmode = (wl_bss_type_t *)inp->wldp_buf;
452 	wl_bss_type_t *ow_opmode;
453 	int err = 0;
454 
455 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_bss_type_t))) == NULL)
456 		return (ENOMEM);
457 	outp = (wldp_t *)omp->b_rptr;
458 	ow_opmode = (wl_bss_type_t *)outp->wldp_buf;
459 
460 	switch (cmd) {
461 	case WLAN_GET_PARAM:
462 		wl_get_bsstype(ic, ow_opmode);
463 		break;
464 	case  WLAN_SET_PARAM:
465 		err = wl_set_bsstype(ic, iw_opmode);
466 		break;
467 	default:
468 		ieee80211_err("wifi_cfg_bsstype: unknown command %x\n", cmd);
469 		outp->wldp_result = WL_NOTSUPPORTED;
470 		err = EINVAL;
471 		break;
472 	}
473 
474 	freemsg(*mp);
475 	*mp = omp;
476 	return (err);
477 }
478 
479 static int
480 wifi_cfg_linkstatus(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
481 {
482 	mblk_t *omp;
483 	wldp_t *outp;
484 	wl_linkstatus_t *ow_linkstat;
485 	int err = 0;
486 
487 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_linkstatus_t))) == NULL)
488 		return (ENOMEM);
489 	outp = (wldp_t *)omp->b_rptr;
490 	ow_linkstat = (wl_linkstatus_t *)outp->wldp_buf;
491 
492 	switch (cmd) {
493 	case WLAN_GET_PARAM:
494 		wl_get_linkstatus(ic, ow_linkstat);
495 		break;
496 	case WLAN_SET_PARAM:
497 		outp->wldp_result = WL_READONLY;
498 		err = EINVAL;
499 		break;
500 	default:
501 		ieee80211_err("wifi_cfg_linkstatus: unknown command %x\n", cmd);
502 		outp->wldp_result = WL_NOTSUPPORTED;
503 		err = EINVAL;
504 		break;
505 	}
506 
507 	freemsg(*mp);
508 	*mp = omp;
509 	return (err);
510 }
511 
512 static int
513 wifi_cfg_suprates(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
514 {
515 	mblk_t *omp;
516 	wldp_t *outp;
517 	wl_rates_t *ow_rates;
518 	int err, buflen;
519 
520 	err = 0;
521 	/* rate value (wl_rates_rates) is of type char */
522 	buflen = offsetof(wl_rates_t, wl_rates_rates) +
523 	    sizeof (char) * IEEE80211_MODE_MAX * IEEE80211_RATE_MAXSIZE;
524 	if ((omp = wifi_getoutmsg(*mp, cmd, buflen)) == NULL)
525 		return (ENOMEM);
526 	outp = (wldp_t *)omp->b_rptr;
527 	ow_rates = (wl_rates_t *)outp->wldp_buf;
528 
529 	switch (cmd) {
530 	case WLAN_GET_PARAM:
531 		(void) wl_get_suprates(ic, ow_rates);
532 		break;
533 	case WLAN_SET_PARAM:
534 		outp->wldp_result = WL_READONLY;
535 		err = EINVAL;
536 		break;
537 	default:
538 		ieee80211_err("wifi_cfg_suprates: unknown command %x\n", cmd);
539 		outp->wldp_result = WL_NOTSUPPORTED;
540 		err = EINVAL;
541 		break;
542 	}
543 
544 	freemsg(*mp);
545 	*mp = omp;
546 	return (err);
547 }
548 
549 static int
550 wifi_cfg_desrates(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
551 {
552 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
553 	wl_rates_t *iw_rates = (wl_rates_t *)inp->wldp_buf;
554 	mblk_t *omp;
555 	wldp_t *outp;
556 	wl_rates_t *ow_rates;
557 	int err;
558 
559 	err = 0;
560 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_rates_t))) == NULL)
561 		return (ENOMEM);
562 	outp = (wldp_t *)omp->b_rptr;
563 	ow_rates = (wl_rates_t *)outp->wldp_buf;
564 
565 	switch (cmd) {
566 	case  WLAN_GET_PARAM:
567 		wl_get_desrates(ic, ow_rates);
568 		break;
569 	case  WLAN_SET_PARAM:
570 		err = wl_set_desrates(ic, iw_rates);
571 		break;
572 	default:
573 		ieee80211_err("wifi_cfg_desrates: unknown command %x\n", cmd);
574 		outp->wldp_result = WL_NOTSUPPORTED;
575 		err = EINVAL;
576 		break;
577 	}
578 
579 	freemsg(*mp);
580 	*mp = omp;
581 	return (err);
582 }
583 
584 /*
585  * Rescale device's RSSI value to (0, 15) as required by WiFi
586  * driver IOCTLs (PSARC/2003/722)
587  */
588 static wl_rssi_t
589 wifi_getrssi(struct ieee80211_node *in)
590 {
591 	struct ieee80211com *ic = in->in_ic;
592 	wl_rssi_t rssi, max_rssi;
593 
594 	rssi = ic->ic_node_getrssi(in);
595 	max_rssi = (ic->ic_maxrssi == 0) ? IEEE80211_MAXRSSI : ic->ic_maxrssi;
596 	if (rssi == 0)
597 		rssi = 0;
598 	else if (rssi >= max_rssi)
599 		rssi = MAX_RSSI;
600 	else
601 		rssi = rssi * MAX_RSSI / max_rssi + 1;
602 
603 	return (rssi);
604 }
605 
606 static int
607 wifi_cfg_rssi(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
608 {
609 	mblk_t *omp;
610 	wldp_t *outp;
611 	wl_rssi_t *ow_rssi;
612 	int err = 0;
613 
614 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_rssi_t))) == NULL)
615 		return (ENOMEM);
616 	outp = (wldp_t *)omp->b_rptr;
617 	ow_rssi = (wl_rssi_t *)outp->wldp_buf;
618 
619 	switch (cmd) {
620 	case  WLAN_GET_PARAM:
621 		*ow_rssi = wifi_getrssi(ic->ic_bss);
622 		break;
623 	case  WLAN_SET_PARAM:
624 		outp->wldp_result = WL_READONLY;
625 		err = EINVAL;
626 		break;
627 	default:
628 		ieee80211_err("wifi_cfg_rssi: unknown command %x\n", cmd);
629 		outp->wldp_result = WL_NOTSUPPORTED;
630 		return (EINVAL);
631 	}
632 
633 	freemsg(*mp);
634 	*mp = omp;
635 	return (err);
636 }
637 
638 /*
639  * maximum scan wait time in second.
640  * Time spent on scaning one channel is usually 100~200ms. The maximum
641  * number of channels defined in wifi_ioctl.h is 99 (MAX_CHANNEL_NUM).
642  * As a result the maximum total scan time is defined as below in ms.
643  */
644 #define	WAIT_SCAN_MAX	(200 * MAX_CHANNEL_NUM)
645 
646 static void
647 wifi_wait_scan(struct ieee80211com *ic)
648 {
649 	ieee80211_impl_t *im = ic->ic_private;
650 
651 	while ((ic->ic_flags & (IEEE80211_F_SCAN | IEEE80211_F_ASCAN)) != 0) {
652 		if (cv_timedwait_sig(&im->im_scan_cv, &ic->ic_genlock,
653 		    ddi_get_lbolt() + drv_usectohz(WAIT_SCAN_MAX * 1000)) !=
654 		    0) {
655 			break;
656 		}
657 	}
658 }
659 
660 #define	WIFI_HAVE_CAP(in, flag)	(((in)->in_capinfo & (flag)) ? 1 : 0)
661 
662 /*
663  * Callback function used by ieee80211_iterate_nodes() in
664  * wifi_cfg_esslist() to get info of each node in a node table
665  *    arg  output buffer, pointer to wl_ess_list_t
666  *    in   each node in the node table
667  */
668 static void
669 wifi_read_ap(void *arg, struct ieee80211_node *in)
670 {
671 	wl_ess_list_t *aps = arg;
672 	ieee80211com_t *ic = in->in_ic;
673 	struct ieee80211_channel *chan = in->in_chan;
674 	struct ieee80211_rateset *rates = &(in->in_rates);
675 	wl_ess_conf_t *conf;
676 	uint8_t *end;
677 	uint_t i, nrates;
678 
679 	end = (uint8_t *)aps - WIFI_BUF_OFFSET + MAX_BUF_LEN -
680 	    sizeof (wl_ess_list_t);
681 	conf = &aps->wl_ess_list_ess[aps->wl_ess_list_num];
682 	if ((uint8_t *)conf > end)
683 		return;
684 
685 	/* skip newly allocated NULL bss node */
686 	if (IEEE80211_ADDR_EQ(in->in_macaddr, ic->ic_macaddr))
687 		return;
688 
689 	conf->wl_ess_conf_essid.wl_essid_length = in->in_esslen;
690 	bcopy(in->in_essid, conf->wl_ess_conf_essid.wl_essid_essid,
691 	    in->in_esslen);
692 	bcopy(in->in_bssid, conf->wl_ess_conf_bssid, IEEE80211_ADDR_LEN);
693 	conf->wl_ess_conf_wepenabled =
694 	    (in->in_capinfo & IEEE80211_CAPINFO_PRIVACY ?
695 	    WL_ENC_WEP : WL_NOENCRYPTION);
696 	conf->wl_ess_conf_bsstype =
697 	    (in->in_capinfo & IEEE80211_CAPINFO_ESS ?
698 	    WL_BSS_BSS : WL_BSS_IBSS);
699 	conf->wl_ess_conf_sl = wifi_getrssi(in);
700 	conf->wl_ess_conf_reserved[0] = (in->in_wpa_ie == NULL? 0 : 1);
701 
702 	/* physical (FH, DS, ERP) parameters */
703 	if (IEEE80211_IS_CHAN_A(chan) || IEEE80211_IS_CHAN_T(chan)) {
704 		wl_ofdm_t *ofdm =
705 		    (wl_ofdm_t *)&((conf->wl_phy_conf).wl_phy_ofdm_conf);
706 		ofdm->wl_ofdm_subtype = WL_OFDM;
707 		ofdm->wl_ofdm_frequency = chan->ich_freq;
708 	} else {
709 		switch (in->in_phytype) {
710 		case IEEE80211_T_FH: {
711 			wl_fhss_t *fhss = (wl_fhss_t *)
712 			    &((conf->wl_phy_conf).wl_phy_fhss_conf);
713 
714 			fhss->wl_fhss_subtype = WL_FHSS;
715 			fhss->wl_fhss_channel = ieee80211_chan2ieee(ic, chan);
716 			fhss->wl_fhss_dwelltime = in->in_fhdwell;
717 			break;
718 		}
719 		case IEEE80211_T_DS: {
720 			wl_dsss_t *dsss = (wl_dsss_t *)
721 			    &((conf->wl_phy_conf).wl_phy_dsss_conf);
722 
723 			dsss->wl_dsss_subtype = WL_DSSS;
724 			dsss->wl_dsss_channel = ieee80211_chan2ieee(ic, chan);
725 			dsss->wl_dsss_have_short_preamble = WIFI_HAVE_CAP(in,
726 			    IEEE80211_CAPINFO_SHORT_PREAMBLE);
727 			dsss->wl_dsss_agility_enabled = WIFI_HAVE_CAP(in,
728 			    IEEE80211_CAPINFO_CHNL_AGILITY);
729 			dsss->wl_dsss_have_pbcc = dsss->wl_dsss_pbcc_enable =
730 			    WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_PBCC);
731 			break;
732 		}
733 		case IEEE80211_T_OFDM: {
734 			wl_erp_t *erp = (wl_erp_t *)
735 			    &((conf->wl_phy_conf).wl_phy_erp_conf);
736 
737 			erp->wl_erp_subtype = WL_ERP;
738 			erp->wl_erp_channel = ieee80211_chan2ieee(ic, chan);
739 			erp->wl_erp_have_short_preamble = WIFI_HAVE_CAP(in,
740 			    IEEE80211_CAPINFO_SHORT_PREAMBLE);
741 			erp->wl_erp_have_agility = erp->wl_erp_agility_enabled =
742 			    WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_CHNL_AGILITY);
743 			erp->wl_erp_have_pbcc = erp->wl_erp_pbcc_enabled =
744 			    WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_PBCC);
745 			erp->wl_erp_dsss_ofdm_enabled =
746 			    WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_DSSSOFDM);
747 			erp->wl_erp_sst_enabled = WIFI_HAVE_CAP(in,
748 			    IEEE80211_CAPINFO_SHORT_SLOTTIME);
749 			break;
750 		} /* case IEEE80211_T_OFDM */
751 		} /* switch in->in_phytype */
752 	}
753 
754 	/* supported rates */
755 	nrates = MIN(rates->ir_nrates, MAX_SCAN_SUPPORT_RATES);
756 	/*
757 	 * The number of supported rates might exceed
758 	 * MAX_SCAN_SUPPORT_RATES. Fill in highest rates
759 	 * first so userland command could properly show
760 	 * maximum speed of AP
761 	 */
762 	for (i = 0; i < nrates; i++) {
763 		conf->wl_supported_rates[i] =
764 		    rates->ir_rates[rates->ir_nrates - i - 1];
765 	}
766 
767 	aps->wl_ess_list_num++;
768 }
769 
770 static int
771 wifi_cfg_esslist(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
772 {
773 	mblk_t *omp;
774 	wldp_t *outp;
775 	wl_ess_list_t *ow_aps;
776 	int err = 0;
777 
778 	if ((omp = wifi_getoutmsg(*mp, cmd, MAX_BUF_LEN - WIFI_BUF_OFFSET)) ==
779 	    NULL) {
780 		return (ENOMEM);
781 	}
782 	outp = (wldp_t *)omp->b_rptr;
783 	ow_aps = (wl_ess_list_t *)outp->wldp_buf;
784 
785 	switch (cmd) {
786 	case WLAN_GET_PARAM:
787 		ow_aps->wl_ess_list_num = 0;
788 		ieee80211_iterate_nodes(&ic->ic_scan, wifi_read_ap, ow_aps);
789 		outp->wldp_length = WIFI_BUF_OFFSET +
790 		    offsetof(wl_ess_list_t, wl_ess_list_ess) +
791 		    ow_aps->wl_ess_list_num * sizeof (wl_ess_conf_t);
792 		omp->b_wptr = omp->b_rptr + outp->wldp_length;
793 		break;
794 	case WLAN_SET_PARAM:
795 		outp->wldp_result = WL_READONLY;
796 		err = EINVAL;
797 		break;
798 	default:
799 		ieee80211_err("wifi_cfg_esslist: unknown command %x\n", cmd);
800 		outp->wldp_result = WL_NOTSUPPORTED;
801 		err = EINVAL;
802 		break;
803 	}
804 
805 	freemsg(*mp);
806 	*mp = omp;
807 	return (err);
808 }
809 
810 /*
811  * Scan the network for all available ESSs.
812  * IEEE80211_F_SCANONLY is set when current state is INIT. And
813  * with this flag, after scan the state will be changed back to
814  * INIT. The reason is at the end of SCAN stage, the STA will
815  * consequently connect to an AP. Then it looks unreasonable that
816  * for a disconnected device, A SCAN command causes it connected.
817  * So the state is changed back to INIT.
818  */
819 static int
820 wifi_cmd_scan(struct ieee80211com *ic, mblk_t *mp)
821 {
822 	int ostate = ic->ic_state;
823 
824 	/*
825 	 * Do not scan when current state is RUN. The reason is
826 	 * when connected, STA is on the same channel as AP. But
827 	 * to do scan, STA have to switch to each available channel,
828 	 * send probe request and wait certian time for probe
829 	 * response/beacon. Then when the STA switches to a channel
830 	 * different than AP's, as a result it cannot send/receive
831 	 * data packets to/from the connected WLAN. This eventually
832 	 * will cause data loss.
833 	 */
834 	if (ostate == IEEE80211_S_RUN)
835 		return (0);
836 
837 	IEEE80211_UNLOCK(ic);
838 
839 	ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
840 	IEEE80211_LOCK(ic);
841 	if (ostate == IEEE80211_S_INIT)
842 		ic->ic_flags |= IEEE80211_F_SCANONLY;
843 
844 	/* Don't wait on WPA mode */
845 	if ((ic->ic_flags & IEEE80211_F_WPA) == 0) {
846 		/* wait scan complete */
847 		wifi_wait_scan(ic);
848 	}
849 
850 	wifi_setupoutmsg(mp, 0);
851 	return (0);
852 }
853 
854 static void
855 wifi_loaddefdata(struct ieee80211com *ic)
856 {
857 	struct ieee80211_node *in = ic->ic_bss;
858 	int i;
859 
860 	ic->ic_des_esslen = 0;
861 	bzero(ic->ic_des_essid, IEEE80211_NWID_LEN);
862 	ic->ic_flags &= ~IEEE80211_F_DESBSSID;
863 	bzero(ic->ic_des_bssid, IEEE80211_ADDR_LEN);
864 	bzero(ic->ic_bss->in_bssid, IEEE80211_ADDR_LEN);
865 	ic->ic_des_chan = IEEE80211_CHAN_ANYC;
866 	ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
867 	bzero(ic->ic_nickname, IEEE80211_NWID_LEN);
868 	in->in_authmode = IEEE80211_AUTH_OPEN;
869 	ic->ic_flags &= ~IEEE80211_F_PRIVACY;
870 	ic->ic_flags &= ~IEEE80211_F_WPA;	/* mask WPA mode */
871 	ic->ic_evq_head = ic->ic_evq_tail = 0;	/* reset Queue */
872 	ic->ic_def_txkey = 0;
873 	for (i = 0; i < MAX_NWEPKEYS; i++) {
874 		ic->ic_nw_keys[i].wk_keylen = 0;
875 		bzero(ic->ic_nw_keys[i].wk_key, IEEE80211_KEYBUF_SIZE);
876 	}
877 	ic->ic_curmode = IEEE80211_MODE_AUTO;
878 }
879 
880 static int
881 wifi_cmd_loaddefaults(struct ieee80211com *ic, mblk_t *mp)
882 {
883 	wifi_loaddefdata(ic);
884 	wifi_setupoutmsg(mp, 0);
885 	return (ENETRESET);
886 }
887 
888 static int
889 wifi_cmd_disassoc(struct ieee80211com *ic, mblk_t *mp)
890 {
891 	if (ic->ic_state != IEEE80211_S_INIT) {
892 		IEEE80211_UNLOCK(ic);
893 		(void) ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
894 		IEEE80211_LOCK(ic);
895 	}
896 	wifi_loaddefdata(ic);
897 	wifi_setupoutmsg(mp, 0);
898 	return (0);
899 }
900 
901 /*
902  * Get the capabilities of drivers.
903  */
904 static int
905 wifi_cfg_caps(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
906 {
907 	mblk_t *omp;
908 	wldp_t *outp;
909 	wl_capability_t *o_caps;
910 	int err = 0;
911 
912 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_capability_t))) == NULL)
913 		return (ENOMEM);
914 	outp = (wldp_t *)omp->b_rptr;
915 	o_caps = (wl_capability_t *)outp->wldp_buf;
916 
917 	switch (cmd) {
918 	case WLAN_GET_PARAM:
919 		wl_get_capability(ic, o_caps);
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_caps: unknown command %x\n", cmd);
927 		outp->wldp_result = WL_NOTSUPPORTED;
928 		err = EINVAL;
929 		break;
930 	}
931 
932 	freemsg(*mp);
933 	*mp = omp;
934 	return (err);
935 }
936 
937 /*
938  * Operating on WPA mode.
939  */
940 static int
941 wifi_cfg_wpa(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
942 {
943 	mblk_t *omp;
944 	wldp_t *outp;
945 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
946 	wl_wpa_t *wpa = (wl_wpa_t *)inp->wldp_buf;
947 	wl_wpa_t *o_wpa;
948 	int err = 0;
949 
950 	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_wpa_t))) == NULL)
951 		return (ENOMEM);
952 	outp = (wldp_t *)omp->b_rptr;
953 	o_wpa = (wl_wpa_t *)outp->wldp_buf;
954 
955 	switch (cmd) {
956 	case WLAN_GET_PARAM:
957 		wl_get_wpa(ic, o_wpa);
958 		break;
959 	case WLAN_SET_PARAM:
960 		err = wl_set_wpa(ic, wpa);
961 		break;
962 	default:
963 		ieee80211_err("wifi_cfg_wpa: unknown command %x\n", cmd);
964 		outp->wldp_result = WL_NOTSUPPORTED;
965 		err = EINVAL;
966 		break;
967 	}
968 
969 	freemsg(*mp);
970 	*mp = omp;
971 	return (err);
972 }
973 
974 /*
975  * WPA daemon set the WPA keys.
976  * The WPA keys are negotiated with APs through wpa service.
977  */
978 static int
979 wifi_cfg_wpakey(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
980 {
981 	mblk_t *omp;
982 	wldp_t *outp;
983 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
984 	wl_key_t *ik = (wl_key_t *)(inp->wldp_buf);
985 	int err = 0;
986 
987 	if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
988 		return (ENOMEM);
989 	outp = (wldp_t *)omp->b_rptr;
990 
991 	switch (cmd) {
992 	case WLAN_GET_PARAM:
993 		outp->wldp_result = WL_WRITEONLY;
994 		err = EINVAL;
995 		break;
996 	case WLAN_SET_PARAM:
997 		err = wl_set_wpakey(ic, ik);
998 		break;
999 	default:
1000 		ieee80211_err("wifi_cfg_wpakey: unknown command %x\n", cmd);
1001 		outp->wldp_result = WL_NOTSUPPORTED;
1002 		err = EINVAL;
1003 		break;
1004 	}
1005 
1006 	freemsg(*mp);
1007 	*mp = omp;
1008 	return (err);
1009 }
1010 
1011 /*
1012  * Delete obsolete keys - keys are dynamically exchanged between APs
1013  * and wpa daemon.
1014  */
1015 static int
1016 wifi_cfg_delkey(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1017 {
1018 	mblk_t *omp;
1019 	wldp_t *outp;
1020 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
1021 	wl_del_key_t *dk = (wl_del_key_t *)inp->wldp_buf;
1022 	int err = 0;
1023 
1024 	if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
1025 		return (ENOMEM);
1026 	outp = (wldp_t *)omp->b_rptr;
1027 
1028 	switch (cmd) {
1029 	case WLAN_GET_PARAM:
1030 		outp->wldp_result = WL_WRITEONLY;
1031 		err = EINVAL;
1032 		break;
1033 	case WLAN_SET_PARAM:
1034 		err = wl_set_delkey(ic, dk);
1035 		break;
1036 	default:
1037 		ieee80211_err("wifi_cfg_delkey: unknown command %x\n", cmd);
1038 		outp->wldp_result = WL_NOTSUPPORTED;
1039 		err = EINVAL;
1040 		break;
1041 	}
1042 
1043 	freemsg(*mp);
1044 	*mp = omp;
1045 	return (err);
1046 }
1047 
1048 /*
1049  * The OPTIE will be used in the association request.
1050  */
1051 static int
1052 wifi_cfg_setoptie(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1053 {
1054 	mblk_t *omp;
1055 	wldp_t *outp;
1056 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
1057 	wl_wpa_ie_t *ie_in = (wl_wpa_ie_t *)inp->wldp_buf;
1058 	int err = 0;
1059 
1060 	if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
1061 		return (ENOMEM);
1062 	outp = (wldp_t *)omp->b_rptr;
1063 
1064 	switch (cmd) {
1065 	case WLAN_GET_PARAM:
1066 		outp->wldp_result = WL_WRITEONLY;
1067 		err = EINVAL;
1068 		break;
1069 	case WLAN_SET_PARAM:
1070 		if ((err = wl_set_optie(ic, ie_in)) == EINVAL)
1071 			outp->wldp_result = WL_NOTSUPPORTED;
1072 		break;
1073 	default:
1074 		ieee80211_err("wifi_cfg_setoptie: unknown command %x\n", cmd);
1075 		outp->wldp_result = WL_NOTSUPPORTED;
1076 		err = EINVAL;
1077 		break;
1078 	}
1079 
1080 	freemsg(*mp);
1081 	*mp = omp;
1082 	return (err);
1083 }
1084 
1085 /*
1086  * To be compatible with drivers/tools of OpenSolaris.org,
1087  * we use a different ID to filter out those APs of WPA mode.
1088  */
1089 static int
1090 wifi_cfg_scanresults(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1091 {
1092 	mblk_t *omp;
1093 	wldp_t *outp;
1094 	wl_wpa_ess_t *sr;
1095 	ieee80211_node_t *in;
1096 	ieee80211_node_table_t *nt;
1097 	int len, ap_num = 0;
1098 	int err = 0;
1099 
1100 	if ((omp = wifi_getoutmsg(*mp, cmd, MAX_BUF_LEN - WIFI_BUF_OFFSET)) ==
1101 	    NULL) {
1102 		return (ENOMEM);
1103 	}
1104 	outp = (wldp_t *)omp->b_rptr;
1105 	sr = (wl_wpa_ess_t *)outp->wldp_buf;
1106 	sr->count = 0;
1107 
1108 	switch (cmd) {
1109 	case WLAN_GET_PARAM:
1110 		ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_scanresults\n");
1111 		nt = &ic->ic_scan;
1112 		IEEE80211_NODE_LOCK(nt);
1113 		in = list_head(&nt->nt_node);
1114 		while (in != NULL) {
1115 			/* filter out non-WPA APs */
1116 			if (in->in_wpa_ie == NULL) {
1117 				in = list_next(&nt->nt_node, in);
1118 				continue;
1119 			}
1120 			bcopy(in->in_bssid, sr->ess[ap_num].bssid,
1121 			    IEEE80211_ADDR_LEN);
1122 			sr->ess[ap_num].ssid_len = in->in_esslen;
1123 			bcopy(in->in_essid, sr->ess[ap_num].ssid,
1124 			    in->in_esslen);
1125 			sr->ess[ap_num].freq = in->in_chan->ich_freq;
1126 
1127 			len = in->in_wpa_ie[1] + 2;
1128 			bcopy(in->in_wpa_ie, sr->ess[ap_num].wpa_ie, len);
1129 			sr->ess[ap_num].wpa_ie_len = len;
1130 
1131 			ap_num ++;
1132 			in = list_next(&nt->nt_node, in);
1133 		}
1134 		IEEE80211_NODE_UNLOCK(nt);
1135 		sr->count = ap_num;
1136 		outp->wldp_length = WIFI_BUF_OFFSET +
1137 		    offsetof(wl_wpa_ess_t, ess) +
1138 		    sr->count * sizeof (struct wpa_ess);
1139 		omp->b_wptr = omp->b_rptr + outp->wldp_length;
1140 		break;
1141 	case WLAN_SET_PARAM:
1142 		outp->wldp_result = WL_READONLY;
1143 		err = EINVAL;
1144 		break;
1145 	default:
1146 		ieee80211_err("wifi_cfg_scanresults: unknown cmmand %x\n", cmd);
1147 		outp->wldp_result = WL_NOTSUPPORTED;
1148 		err = EINVAL;
1149 		break;
1150 	}
1151 
1152 	freemsg(*mp);
1153 	*mp = omp;
1154 	return (err);
1155 }
1156 
1157 /*
1158  * Manually control the state of AUTH | DEAUTH | DEASSOC | ASSOC
1159  */
1160 static int
1161 wifi_cfg_setmlme(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
1162 {
1163 	mblk_t *omp;
1164 	wldp_t *outp;
1165 	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
1166 	wl_mlme_t *mlme = (wl_mlme_t *)inp->wldp_buf;
1167 	int err = 0;
1168 
1169 	if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
1170 		return (ENOMEM);
1171 	outp = (wldp_t *)omp->b_rptr;
1172 
1173 	switch (cmd) {
1174 	case WLAN_GET_PARAM:
1175 		outp->wldp_result = WL_WRITEONLY;
1176 		err = EINVAL;
1177 		break;
1178 	case WLAN_SET_PARAM:
1179 		err = wl_set_mlme(ic, mlme);
1180 		break;
1181 	default:
1182 		ieee80211_err("wifi_cfg_delkey: unknown command %x\n", cmd);
1183 		outp->wldp_result = WL_NOTSUPPORTED;
1184 		err = EINVAL;
1185 		break;
1186 	}
1187 
1188 	freemsg(*mp);
1189 	*mp = omp;
1190 	return (err);
1191 }
1192 
1193 static int
1194 wifi_cfg_getset(struct ieee80211com *ic, mblk_t **mp, uint32_t cmd)
1195 {
1196 	mblk_t *mp1 = *mp;
1197 	wldp_t *wp = (wldp_t *)mp1->b_rptr;
1198 	int err = 0;
1199 
1200 	ASSERT(ic != NULL && mp1 != NULL);
1201 	IEEE80211_LOCK_ASSERT(ic);
1202 	if (MBLKL(mp1) < WIFI_BUF_OFFSET) {
1203 		ieee80211_err("wifi_cfg_getset: "
1204 		    "invalid input buffer, size=%d\n", MBLKL(mp1));
1205 		return (EINVAL);
1206 	}
1207 
1208 	switch (wp->wldp_id) {
1209 	/* Commands */
1210 	case WL_SCAN:
1211 		err = wifi_cmd_scan(ic, mp1);
1212 		break;
1213 	case WL_LOAD_DEFAULTS:
1214 		err = wifi_cmd_loaddefaults(ic, mp1);
1215 		break;
1216 	case WL_DISASSOCIATE:
1217 		err = wifi_cmd_disassoc(ic, mp1);
1218 		break;
1219 	/* Parameters */
1220 	case WL_ESSID:
1221 		err = wifi_cfg_essid(ic, cmd, mp);
1222 		break;
1223 	case WL_BSSID:
1224 		err = wifi_cfg_bssid(ic, cmd, mp);
1225 		break;
1226 	case WL_NODE_NAME:
1227 		err = wifi_cfg_nodename(ic, cmd, mp);
1228 		break;
1229 	case WL_PHY_CONFIG:
1230 		err = wifi_cfg_phy(ic, cmd, mp);
1231 		break;
1232 	case WL_WEP_KEY_TAB:
1233 		err = wifi_cfg_wepkey(ic, cmd, mp);
1234 		break;
1235 	case WL_WEP_KEY_ID:
1236 		err = wifi_cfg_keyid(ic, cmd, mp);
1237 		break;
1238 	case WL_AUTH_MODE:
1239 		err = wifi_cfg_authmode(ic, cmd, mp);
1240 		break;
1241 	case WL_ENCRYPTION:
1242 		err = wifi_cfg_encrypt(ic, cmd, mp);
1243 		break;
1244 	case WL_BSS_TYPE:
1245 		err = wifi_cfg_bsstype(ic, cmd, mp);
1246 		break;
1247 	case WL_DESIRED_RATES:
1248 		err = wifi_cfg_desrates(ic, cmd, mp);
1249 		break;
1250 	case WL_LINKSTATUS:
1251 		err = wifi_cfg_linkstatus(ic, cmd, mp);
1252 		break;
1253 	case WL_ESS_LIST:
1254 		err = wifi_cfg_esslist(ic, cmd, mp);
1255 		break;
1256 	case WL_SUPPORTED_RATES:
1257 		err = wifi_cfg_suprates(ic, cmd, mp);
1258 		break;
1259 	case WL_RSSI:
1260 		err = wifi_cfg_rssi(ic, cmd, mp);
1261 		break;
1262 	/*
1263 	 * WPA IOCTLs
1264 	 */
1265 	case WL_CAPABILITY:
1266 		err = wifi_cfg_caps(ic, cmd, mp);
1267 		break;
1268 	case WL_WPA:
1269 		err = wifi_cfg_wpa(ic, cmd, mp);
1270 		break;
1271 	case WL_KEY:
1272 		err = wifi_cfg_wpakey(ic, cmd, mp);
1273 		break;
1274 	case WL_DELKEY:
1275 		err = wifi_cfg_delkey(ic, cmd, mp);
1276 		break;
1277 	case WL_SETOPTIE:
1278 		err = wifi_cfg_setoptie(ic, cmd, mp);
1279 		break;
1280 	case WL_SCANRESULTS:
1281 		err = wifi_cfg_scanresults(ic, cmd, mp);
1282 		break;
1283 	case WL_MLME:
1284 		err = wifi_cfg_setmlme(ic, cmd, mp);
1285 		break;
1286 	default:
1287 		wifi_setupoutmsg(mp1, 0);
1288 		wp->wldp_result = WL_LACK_FEATURE;
1289 		err = ENOTSUP;
1290 		break;
1291 	}
1292 
1293 	return (err);
1294 }
1295 
1296 /*
1297  * Typically invoked by drivers in response to requests for
1298  * information or to change settings from the userland.
1299  *
1300  * Return value should be checked by WiFi drivers. Return 0
1301  * on success. Otherwise, return non-zero value to indicate
1302  * the error. Driver should operate as below when the return
1303  * error is:
1304  * ENETRESET	Reset wireless network and re-start to join a
1305  *		WLAN. ENETRESET is returned when a configuration
1306  *		parameter has been changed.
1307  *		When acknowledge a M_IOCTL message, thie error
1308  *		is ignored.
1309  */
1310 int
1311 ieee80211_ioctl(struct ieee80211com *ic, queue_t *wq, mblk_t *mp)
1312 {
1313 	struct iocblk *iocp;
1314 	int32_t cmd, err, len;
1315 	boolean_t need_privilege;
1316 	mblk_t *mp1;
1317 
1318 	if (MBLKL(mp) < sizeof (struct iocblk)) {
1319 		ieee80211_err("ieee80211_ioctl: ioctl buffer too short, %u\n",
1320 		    MBLKL(mp));
1321 		miocnak(wq, mp, 0, EINVAL);
1322 		return (EINVAL);
1323 	}
1324 
1325 	/*
1326 	 * Validate the command
1327 	 */
1328 	iocp = (struct iocblk *)mp->b_rptr;
1329 	iocp->ioc_error = 0;
1330 	cmd = iocp->ioc_cmd;
1331 	need_privilege = B_TRUE;
1332 	switch (cmd) {
1333 	case WLAN_SET_PARAM:
1334 	case WLAN_COMMAND:
1335 		break;
1336 	case WLAN_GET_PARAM:
1337 		need_privilege = B_FALSE;
1338 		break;
1339 	default:
1340 		ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_ioctl(): "
1341 		    "unknown cmd 0x%x\n", cmd);
1342 		miocnak(wq, mp, 0, EINVAL);
1343 		return (EINVAL);
1344 	}
1345 
1346 	if (need_privilege && (err = secpolicy_dl_config(iocp->ioc_cr)) != 0) {
1347 		miocnak(wq, mp, 0, err);
1348 		return (err);
1349 	}
1350 
1351 	IEEE80211_LOCK(ic);
1352 
1353 	/* sanity check */
1354 	mp1 = mp->b_cont;
1355 	if (iocp->ioc_count == 0 || iocp->ioc_count < sizeof (wldp_t) ||
1356 	    mp1 == NULL) {
1357 		miocnak(wq, mp, 0, EINVAL);
1358 		IEEE80211_UNLOCK(ic);
1359 		return (EINVAL);
1360 	}
1361 
1362 	/* assuming single data block */
1363 	if (mp1->b_cont != NULL) {
1364 		freemsg(mp1->b_cont);
1365 		mp1->b_cont = NULL;
1366 	}
1367 
1368 	err = wifi_cfg_getset(ic, &mp1, cmd);
1369 	mp->b_cont = mp1;
1370 	IEEE80211_UNLOCK(ic);
1371 
1372 	len = msgdsize(mp1);
1373 	/* ignore ENETRESET when acknowledge the M_IOCTL message */
1374 	if (err == 0 || err == ENETRESET)
1375 		miocack(wq, mp, len, 0);
1376 	else
1377 		miocack(wq, mp, len, err);
1378 
1379 	return (err);
1380 }
1381 
1382 /*
1383  * The following routines are for brussels support
1384  */
1385 
1386 /*
1387  * MAC_PROP_WL_ESSID
1388  */
1389 static int
1390 wl_set_essid(struct ieee80211com *ic, const void *wldp_buf)
1391 {
1392 	int err = 0;
1393 	char *essid;
1394 	wl_essid_t *iw_essid = (wl_essid_t *)wldp_buf;
1395 
1396 	if (iw_essid->wl_essid_length > IEEE80211_NWID_LEN) {
1397 		ieee80211_err("wl_set_essid: "
1398 		    "essid too long, %u, max %u\n",
1399 		    iw_essid->wl_essid_length, IEEE80211_NWID_LEN);
1400 
1401 		err = EINVAL;
1402 		return (err);
1403 	}
1404 
1405 	essid = iw_essid->wl_essid_essid;
1406 	essid[IEEE80211_NWID_LEN] = 0;
1407 
1408 	ieee80211_dbg(IEEE80211_MSG_CONFIG, "wl_set_essid: "
1409 	    "set essid=%s length=%d\n",
1410 	    essid, iw_essid->wl_essid_length);
1411 
1412 	ic->ic_des_esslen = iw_essid->wl_essid_length;
1413 	if (ic->ic_des_esslen != 0)
1414 		bcopy(essid, ic->ic_des_essid, ic->ic_des_esslen);
1415 	if (ic->ic_des_esslen < IEEE80211_NWID_LEN)
1416 		ic->ic_des_essid[ic->ic_des_esslen] = 0;
1417 
1418 	err = ENETRESET;
1419 
1420 	return (err);
1421 }
1422 
1423 static void
1424 wl_get_essid(struct ieee80211com *ic, void *wldp_buf)
1425 {
1426 	char *essid;
1427 	wl_essid_t ow_essid;
1428 
1429 	essid = (char *)ic->ic_des_essid;
1430 	if (essid[0] == '\0')
1431 		essid = (char *)ic->ic_bss->in_essid;
1432 
1433 	bzero(&ow_essid, sizeof (wl_essid_t));
1434 	ow_essid.wl_essid_length = wifi_strnlen((const char *)essid,
1435 	    IEEE80211_NWID_LEN);
1436 	bcopy(essid, ow_essid.wl_essid_essid,
1437 	    ow_essid.wl_essid_length);
1438 	bcopy(&ow_essid, wldp_buf, sizeof (wl_essid_t));
1439 
1440 }
1441 
1442 /*
1443  * MAC_PROP_WL_BSSID
1444  */
1445 static int
1446 wl_set_bssid(struct ieee80211com *ic, const void* wldp_buf)
1447 {
1448 
1449 	ieee80211_dbg(IEEE80211_MSG_CONFIG, "wl_set_bssid: "
1450 	    "set bssid=%s\n",
1451 	    ieee80211_macaddr_sprintf(wldp_buf));
1452 
1453 	bcopy(wldp_buf, ic->ic_des_bssid, sizeof (wl_bssid_t));
1454 	ic->ic_flags |= IEEE80211_F_DESBSSID;
1455 
1456 	return (ENETRESET);
1457 }
1458 
1459 static void
1460 wl_get_bssid(struct ieee80211com *ic, void *wldp_buf)
1461 {
1462 	uint8_t *bssid;
1463 
1464 	if (ic->ic_flags & IEEE80211_F_DESBSSID)
1465 		bssid = ic->ic_des_bssid;
1466 	else
1467 		bssid = ic->ic_bss->in_bssid;
1468 	bcopy(bssid, wldp_buf, sizeof (wl_bssid_t));
1469 
1470 }
1471 
1472 /*
1473  * MAC_PROP_WL_BSSTYP
1474  */
1475 static int
1476 wl_set_bsstype(struct ieee80211com *ic, const void *wldp_buf)
1477 {
1478 	int err = 0;
1479 	wl_bss_type_t *iw_opmode = (wl_bss_type_t *)wldp_buf;
1480 
1481 	ieee80211_dbg(IEEE80211_MSG_CONFIG, "wl_set_bsstype: "
1482 	    "set bsstype=%u\n", *iw_opmode);
1483 
1484 	switch (*iw_opmode) {
1485 	case WL_BSS_BSS:
1486 		ic->ic_flags &= ~IEEE80211_F_IBSSON;
1487 		ic->ic_opmode = IEEE80211_M_STA;
1488 		err = ENETRESET;
1489 		break;
1490 	case WL_BSS_IBSS:
1491 		if ((ic->ic_caps & IEEE80211_C_IBSS) == 0) {
1492 			err = ENOTSUP;
1493 			break;
1494 		}
1495 		if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) {
1496 			ic->ic_flags |= IEEE80211_F_IBSSON;
1497 			ic->ic_opmode = IEEE80211_M_IBSS;
1498 			err = ENETRESET;
1499 		}
1500 		break;
1501 	default:
1502 		ieee80211_err("wl_set_bsstype: "
1503 		    "unknown opmode\n");
1504 		err = EINVAL;
1505 		break;
1506 	}
1507 	return (err);
1508 }
1509 
1510 static void
1511 wl_get_bsstype(struct ieee80211com *ic, void *wldp_buf)
1512 {
1513 	wl_bss_type_t ow_opmode;
1514 
1515 	switch (ic->ic_opmode) {
1516 	case IEEE80211_M_STA:
1517 		ow_opmode = WL_BSS_BSS;
1518 		break;
1519 	case IEEE80211_M_IBSS:
1520 		ow_opmode = WL_BSS_IBSS;
1521 		break;
1522 	default:
1523 		ow_opmode = WL_BSS_ANY;
1524 		break;
1525 	}
1526 
1527 	bcopy(&ow_opmode, wldp_buf, sizeof (wl_bss_type_t));
1528 }
1529 
1530 /*
1531  * MAC_PROP_WL_LINKSTATUS
1532  */
1533 static void
1534 wl_get_linkstatus(struct ieee80211com *ic, void *wldp_buf)
1535 {
1536 	wl_linkstatus_t ow_linkstat;
1537 
1538 	ow_linkstat = (ic->ic_state == IEEE80211_S_RUN) ?
1539 	    WL_CONNECTED : WL_NOTCONNECTED;
1540 	if ((ic->ic_flags & IEEE80211_F_WPA) &&
1541 	    (ieee80211_crypto_getciphertype(ic) != WIFI_SEC_WPA)) {
1542 		ow_linkstat = WL_NOTCONNECTED;
1543 	}
1544 
1545 	bcopy(&ow_linkstat, wldp_buf, sizeof (wl_linkstatus_t));
1546 }
1547 
1548 /*
1549  * MAC_PROP_WL_DESIRED_RATESa
1550  */
1551 static int
1552 wl_set_desrates(struct ieee80211com *ic, const void *wldp_buf)
1553 {
1554 	int err = 0;
1555 	int i, j;
1556 	uint8_t drate;
1557 	boolean_t isfound;
1558 	wl_rates_t *iw_rates = (wl_rates_t *)wldp_buf;
1559 	struct ieee80211_node *in = ic->ic_bss;
1560 	struct ieee80211_rateset *rs = &in->in_rates;
1561 
1562 	drate = iw_rates->wl_rates_rates[0];
1563 	if (ic->ic_fixed_rate == drate)
1564 		return (err);
1565 
1566 	ieee80211_dbg(IEEE80211_MSG_CONFIG, "wl_set_desrates: "
1567 	    "set desired rate=%u\n", drate);
1568 
1569 	if (drate == 0) {
1570 		ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
1571 		if (ic->ic_state == IEEE80211_S_RUN) {
1572 			IEEE80211_UNLOCK(ic);
1573 			ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
1574 			IEEE80211_LOCK(ic);
1575 		}
1576 		return (err);
1577 	}
1578 
1579 	/*
1580 	 * Set desired rate. The desired rate is for data transfer
1581 	 * and usally is checked and used when driver changes to
1582 	 * RUN state.
1583 	 * If the driver is in AUTH | ASSOC | RUN state, desired
1584 	 * rate is checked anainst rates supported by current ESS.
1585 	 * If it's supported and current state is AUTH|ASSOC, nothing
1586 	 * needs to be done by driver since the desired rate will
1587 	 * be enabled when the device changes to RUN state. And
1588 	 * when current state is RUN, Re-associate with the ESS to
1589 	 * enable the desired rate.
1590 	 */
1591 
1592 	if (ic->ic_state != IEEE80211_S_INIT &&
1593 	    ic->ic_state != IEEE80211_S_SCAN) {
1594 		for (i = 0; i < rs->ir_nrates; i++) {
1595 			if (drate == IEEE80211_RV(rs->ir_rates[i]))
1596 				break;
1597 		}
1598 		/* supported */
1599 		if (i < rs->ir_nrates) {
1600 			ic->ic_fixed_rate = drate;
1601 			if (ic->ic_state == IEEE80211_S_RUN) {
1602 				IEEE80211_UNLOCK(ic);
1603 				ieee80211_new_state(ic,
1604 				    IEEE80211_S_ASSOC, 0);
1605 				IEEE80211_LOCK(ic);
1606 			}
1607 			return (err);
1608 		}
1609 	}
1610 
1611 	/*
1612 	 * In INIT or SCAN state
1613 	 * check if the desired rate is supported by device
1614 	 */
1615 	isfound = B_FALSE;
1616 	for (i = 0; i < IEEE80211_MODE_MAX; i++) {
1617 		rs = &ic->ic_sup_rates[i];
1618 		for (j = 0; j < rs->ir_nrates; j++) {
1619 			if (drate ==  IEEE80211_RV(rs->ir_rates[j])) {
1620 				isfound = B_TRUE;
1621 				break;
1622 			}
1623 		}
1624 		if (isfound)
1625 			break;
1626 	}
1627 	if (!isfound) {
1628 		ieee80211_err("wl_set_desrates: "
1629 		    "invald rate %d\n", drate);
1630 		err = EINVAL;
1631 		return (err);
1632 	}
1633 	ic->ic_fixed_rate = drate;
1634 	if (ic->ic_state != IEEE80211_S_SCAN)
1635 		err = ENETRESET;
1636 
1637 	return (err);
1638 }
1639 
1640 static void
1641 wl_get_desrates(struct ieee80211com *ic, void *wldp_buf)
1642 {
1643 	uint8_t srate;
1644 	wl_rates_t ow_rates;
1645 	struct ieee80211_node *in = ic->ic_bss;
1646 	struct ieee80211_rateset *rs = &in->in_rates;
1647 
1648 	srate = rs->ir_rates[in->in_txrate] & IEEE80211_RATE_VAL;
1649 	ow_rates.wl_rates_num = 1;
1650 	ow_rates.wl_rates_rates[0] =
1651 	    (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) ?
1652 	    srate : ic->ic_fixed_rate;
1653 	bcopy(&ow_rates, wldp_buf, sizeof (wl_rates_t));
1654 
1655 }
1656 
1657 /*
1658  * MAC_PROP_AUTH_MODE
1659  */
1660 static int
1661 wl_set_authmode(struct ieee80211com *ic, const void *wldp_buf)
1662 {
1663 	int err = 0;
1664 	wl_authmode_t *iw_auth = (wl_authmode_t *)wldp_buf;
1665 
1666 	if (*iw_auth == ic->ic_bss->in_authmode)
1667 		return (err);
1668 
1669 	ieee80211_dbg(IEEE80211_MSG_CONFIG, "wl_set_authmode: "
1670 	    "set authmode=%u\n", *iw_auth);
1671 
1672 	switch (*iw_auth) {
1673 	case WL_OPENSYSTEM:
1674 	case WL_SHAREDKEY:
1675 		ic->ic_bss->in_authmode = *iw_auth;
1676 		err = ENETRESET;
1677 		break;
1678 	default:
1679 		ieee80211_err("wl_set_authmode: "
1680 		    "unknown authmode %u\n", *iw_auth);
1681 		err = EINVAL;
1682 		break;
1683 	}
1684 
1685 	return (err);
1686 }
1687 
1688 static void
1689 wl_get_authmode(struct ieee80211com *ic, void *wldp_buf)
1690 {
1691 	wl_authmode_t ow_auth;
1692 
1693 	ow_auth = ic->ic_bss->in_authmode;
1694 	bcopy(&ow_auth, wldp_buf, sizeof (wl_authmode_t));
1695 
1696 }
1697 
1698 /*
1699  * MAC_PROP_WL_ENCRYPTION
1700  */
1701 static int
1702 wl_set_encrypt(struct ieee80211com *ic, const void *wldp_buf)
1703 {
1704 	int err = 0;
1705 	uint32_t flags;
1706 	wl_encryption_t *iw_encryp = (wl_encryption_t *)wldp_buf;
1707 
1708 	ieee80211_dbg(IEEE80211_MSG_CONFIG, "wl_set_encrypt: "
1709 	    "set encryption=%u\n", *iw_encryp);
1710 
1711 	flags = ic->ic_flags;
1712 	if (*iw_encryp == WL_NOENCRYPTION)
1713 		flags &= ~IEEE80211_F_PRIVACY;
1714 	else
1715 		flags |= IEEE80211_F_PRIVACY;
1716 
1717 	if (ic->ic_flags != flags) {
1718 		ic->ic_flags = flags;
1719 		err = ENETRESET;
1720 	}
1721 
1722 	return (err);
1723 }
1724 
1725 static void
1726 wl_get_encrypt(struct ieee80211com *ic, void *wldp_buf)
1727 {
1728 	wl_encryption_t *ow_encryp;
1729 
1730 	ow_encryp = (wl_encryption_t *)wldp_buf;
1731 	*ow_encryp = (ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0;
1732 	if (ic->ic_flags & IEEE80211_F_WPA)
1733 		*ow_encryp = WL_ENC_WPA;
1734 
1735 }
1736 
1737 /*
1738  * MAC_PROP_WL_RSSI
1739  */
1740 static void
1741 wl_get_rssi(struct ieee80211com *ic, void *wldp_buf)
1742 {
1743 	wl_rssi_t *ow_rssi;
1744 
1745 	ow_rssi = (wl_rssi_t *)wldp_buf;
1746 	*ow_rssi = wifi_getrssi(ic->ic_bss);
1747 
1748 }
1749 
1750 /*
1751  * MAC_PROP_WL_PHY_CONFIG
1752  */
1753 
1754 static int
1755 wl_set_phy(struct ieee80211com *ic, const void* wldp_buf)
1756 {
1757 	int err = 0;
1758 	int16_t ch;
1759 	wl_dsss_t *dsss;
1760 	wl_phy_conf_t *iw_phy = (wl_phy_conf_t *)wldp_buf;
1761 
1762 	dsss = (wl_dsss_t *)iw_phy;
1763 	ch = dsss->wl_dsss_channel;
1764 
1765 	ieee80211_dbg(IEEE80211_MSG_CONFIG, "wl_set_phy: "
1766 	    "set channel=%d\n", ch);
1767 
1768 	if (ch == 0 || ch == (int16_t)IEEE80211_CHAN_ANY) {
1769 		ic->ic_des_chan = IEEE80211_CHAN_ANYC;
1770 	} else if ((uint_t)ch > IEEE80211_CHAN_MAX ||
1771 	    ieee80211_isclr(ic->ic_chan_active, ch)) {
1772 		err = EINVAL;
1773 		return (err);
1774 	} else {
1775 		ic->ic_des_chan = ic->ic_ibss_chan =
1776 		    &ic->ic_sup_channels[ch];
1777 	}
1778 
1779 	switch (ic->ic_state) {
1780 	case IEEE80211_S_INIT:
1781 	case IEEE80211_S_SCAN:
1782 		err = ENETRESET;
1783 		break;
1784 	default:
1785 		/*
1786 		 * If hte desired channel has changed (to something
1787 		 * other than any) and we're not already scanning,
1788 		 * then kick the state machine.
1789 		 */
1790 		if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
1791 		    ic->ic_bss->in_chan != ic->ic_des_chan &&
1792 		    (ic->ic_flags & IEEE80211_F_SCAN) == 0)
1793 			err = ENETRESET;
1794 		break;
1795 	}
1796 
1797 	return (err);
1798 }
1799 
1800 static int
1801 wl_get_phy(struct ieee80211com *ic, void *wldp_buf)
1802 {
1803 	int err = 0;
1804 	wl_phy_conf_t *ow_phy;
1805 	struct ieee80211_channel *ch = ic->ic_curchan;
1806 
1807 	ow_phy = (wl_phy_conf_t *)wldp_buf;
1808 	bzero(wldp_buf, sizeof (wl_phy_conf_t));
1809 
1810 	/* get current phy parameters: FH|DS|ERP */
1811 	if (IEEE80211_IS_CHAN_A(ch) || IEEE80211_IS_CHAN_T(ch)) {
1812 		wl_ofdm_t *ofdm = (wl_ofdm_t *)ow_phy;
1813 		ofdm->wl_ofdm_subtype = WL_OFDM;
1814 		ofdm->wl_ofdm_frequency = ch->ich_freq;
1815 	} else {
1816 		switch (ic->ic_phytype) {
1817 		case IEEE80211_T_FH: {
1818 			wl_fhss_t *fhss = (wl_fhss_t *)ow_phy;
1819 			fhss->wl_fhss_subtype = WL_FHSS;
1820 			fhss->wl_fhss_channel =
1821 			    ieee80211_chan2ieee(ic, ch);
1822 			break;
1823 		}
1824 		case IEEE80211_T_DS: {
1825 			wl_dsss_t *dsss = (wl_dsss_t *)ow_phy;
1826 			dsss->wl_dsss_subtype = WL_DSSS;
1827 			dsss->wl_dsss_channel =
1828 			    ieee80211_chan2ieee(ic, ch);
1829 			break;
1830 		}
1831 		case IEEE80211_T_OFDM: {
1832 			wl_erp_t *erp = (wl_erp_t *)ow_phy;
1833 			erp->wl_erp_subtype = WL_ERP;
1834 			erp->wl_erp_channel =
1835 			    ieee80211_chan2ieee(ic, ch);
1836 			break;
1837 		}
1838 		default:
1839 			ieee80211_err("wl_get_phy: "
1840 			    "unknown phy type, %x\n", ic->ic_phytype);
1841 			err = EIO;
1842 			break;
1843 		}
1844 	}
1845 
1846 	return (err);
1847 }
1848 
1849 /*
1850  * MAC_PROP_WL_CAPABILITY
1851  */
1852 static void
1853 wl_get_capability(struct ieee80211com *ic, void *wldp_buf)
1854 {
1855 	wl_capability_t ow_caps;
1856 
1857 	ow_caps.caps = ic->ic_caps;
1858 	bcopy(&ow_caps, wldp_buf, sizeof (wl_capability_t));
1859 
1860 }
1861 
1862 /*
1863  * MAC_PROP_WL_WPA
1864  */
1865 static int
1866 wl_set_wpa(struct ieee80211com *ic, const void *wldp_buf)
1867 {
1868 	int err = 0;
1869 	wl_wpa_t *wpa = (wl_wpa_t *)wldp_buf;
1870 
1871 	ieee80211_dbg(IEEE80211_MSG_BRUSSELS, "wl_set_wpa: "
1872 	    "set wpa=%u\n", wpa->wpa_flag);
1873 
1874 	if (wpa->wpa_flag > 0) {
1875 		/* enable wpa mode */
1876 		ic->ic_flags |= IEEE80211_F_PRIVACY;
1877 		ic->ic_flags |= IEEE80211_F_WPA;
1878 	} else {
1879 		ic->ic_flags &= ~IEEE80211_F_PRIVACY;
1880 		ic->ic_flags &= ~IEEE80211_F_WPA;
1881 	}
1882 
1883 	return (err);
1884 }
1885 
1886 static void
1887 wl_get_wpa(struct ieee80211com *ic, void *wldp_buf)
1888 {
1889 	wl_wpa_t *wpa;
1890 
1891 	wpa = (wl_wpa_t *)wldp_buf;
1892 	wpa->wpa_flag = ((ic->ic_flags & IEEE80211_F_WPA) ? 1 : 0);
1893 
1894 	ieee80211_dbg(IEEE80211_MSG_BRUSSELS, "wl_get_wpa: "
1895 	    "get wpa=%u\n", wpa->wpa_flag);
1896 
1897 }
1898 
1899 /*
1900  * MAC_PROP_WL_SCANRESULTS
1901  */
1902 
1903 static void
1904 wl_get_scanresults(struct ieee80211com *ic, void *wldp_buf)
1905 {
1906 	wl_wpa_ess_t *sr;
1907 	ieee80211_node_t *in;
1908 	ieee80211_node_table_t *nt;
1909 	int ap_num;
1910 	int len;
1911 
1912 	sr = (wl_wpa_ess_t *)wldp_buf;
1913 	sr->count = 0;
1914 	ap_num = 0;
1915 
1916 	ieee80211_dbg(IEEE80211_MSG_WPA, "wl_get_scanrelults\n");
1917 
1918 	nt = &ic->ic_scan;
1919 	IEEE80211_NODE_LOCK(nt);
1920 	in = list_head(&nt->nt_node);
1921 
1922 	while (in != NULL) {
1923 		/* filter out non-wpa APs */
1924 		if (in->in_wpa_ie == NULL) {
1925 			in = list_next(&nt->nt_node, in);
1926 			continue;
1927 		}
1928 		bcopy(in->in_bssid, sr->ess[ap_num].bssid,
1929 		    IEEE80211_ADDR_LEN);
1930 		sr->ess[ap_num].ssid_len = in->in_esslen;
1931 		bcopy(in->in_essid, sr->ess[ap_num].ssid,
1932 		    in->in_esslen);
1933 		sr->ess[ap_num].freq = in->in_chan->ich_freq;
1934 
1935 		len = in->in_wpa_ie[1] + 2;
1936 		bcopy(in->in_wpa_ie, sr->ess[ap_num].wpa_ie, len);
1937 		sr->ess[ap_num].wpa_ie_len = len;
1938 
1939 		ap_num++;
1940 		in = list_next(&nt->nt_node, in);
1941 	}
1942 	IEEE80211_NODE_UNLOCK(nt);
1943 	sr->count = ap_num;
1944 
1945 }
1946 
1947 /*
1948  * MAC_PROP_WL_ESS_LIST
1949  */
1950 static void
1951 wl_get_esslist(struct ieee80211com *ic, void *wldp_buf)
1952 {
1953 	wl_ess_list_t *ess_list;
1954 
1955 	ess_list = (wl_ess_list_t *)wldp_buf;
1956 
1957 	ess_list->wl_ess_list_num = 0;
1958 	ieee80211_iterate_nodes(&ic->ic_scan, wifi_read_ap, ess_list);
1959 
1960 }
1961 
1962 /*
1963  * MAC_PROP_WL_WEP_KEY
1964  */
1965 static int
1966 wl_set_wepkey(struct ieee80211com *ic, const void *wldp_buf)
1967 {
1968 	int	 err = 0;
1969 	uint16_t i;
1970 	uint32_t klen;
1971 	struct ieee80211_key *key;
1972 	wl_wep_key_t *wepkey = (wl_wep_key_t *)wldp_buf;
1973 
1974 	/* set all valid keys */
1975 	for (i = 0; i < MAX_NWEPKEYS; i++) {
1976 		if (wepkey[i].wl_wep_operation != WL_ADD)
1977 			continue;
1978 		klen = wepkey[i].wl_wep_length;
1979 		if (klen > IEEE80211_KEYBUF_SIZE) {
1980 			ieee80211_err("wl_set_wepkey: "
1981 			    "invalid wepkey length, %u\n", klen);
1982 			err = EINVAL;
1983 			continue;  /* continue to set other keys */
1984 		}
1985 		if (klen == 0)
1986 			continue;
1987 
1988 		/*
1989 		 *  Set key contents. Only WEP is supported
1990 		 */
1991 		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wl_set_wepkey: "
1992 		    "set key %u, len=%u\n", i, klen);
1993 		key = &ic->ic_nw_keys[i];
1994 		if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP,
1995 		    IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, key) == 0) {
1996 			ieee80211_err("wl_set_wepkey: "
1997 			    "abort, create key failed. id=%u\n", i);
1998 			err = EIO;
1999 			continue;
2000 		}
2001 
2002 		key->wk_keyix = i;
2003 		key->wk_keylen = (uint8_t)klen;
2004 		key->wk_flags |= IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
2005 		bzero(key->wk_key, IEEE80211_KEYBUF_SIZE);
2006 		bcopy(wepkey[i].wl_wep_key, key->wk_key, klen);
2007 		if (ieee80211_crypto_setkey(ic, key, ic->ic_macaddr)
2008 		    == 0) {
2009 			ieee80211_err("wl_set_wepkey: "
2010 			    "set key failed len=%u\n", klen);
2011 			err = EIO;
2012 		}
2013 	}
2014 	if (err == 0)
2015 		err = ENETRESET;
2016 
2017 	return (err);
2018 }
2019 
2020 /*
2021  * MAC_PROP_WL_SETOPTIE
2022  */
2023 static int
2024 wl_set_optie(struct ieee80211com *ic, const void *wldp_buf)
2025 {
2026 	int err = 0;
2027 	char *ie;
2028 	wl_wpa_ie_t *ie_in = (wl_wpa_ie_t *)wldp_buf;
2029 
2030 	if (ic->ic_opmode != IEEE80211_M_STA) {
2031 		ieee80211_err("wl_set_optie: opmode err\n");
2032 		err = EINVAL;
2033 		return (err);
2034 	}
2035 	if (ie_in->wpa_ie_len > IEEE80211_MAX_OPT_IE) {
2036 
2037 		ieee80211_err("wl_set_optie: optie is too long\n");
2038 
2039 		err = EINVAL;
2040 		return (err);
2041 	}
2042 
2043 	ie = ieee80211_malloc(ie_in->wpa_ie_len);
2044 	(void) memcpy(ie, ie_in->wpa_ie, ie_in->wpa_ie_len);
2045 	if (ic->ic_opt_ie != NULL) {
2046 		ieee80211_dbg(IEEE80211_MSG_BRUSSELS,
2047 		    "wl_set_optie:ic_opt_ie!=NULL\n");
2048 		ieee80211_free(ic->ic_opt_ie);
2049 	}
2050 	ic->ic_opt_ie = ie;
2051 	ic->ic_opt_ie_len = ie_in->wpa_ie_len;
2052 
2053 	return (err);
2054 }
2055 
2056 /*
2057  * MAC_PROP_WL_DELKEY
2058  */
2059 static int
2060 wl_set_delkey(struct ieee80211com *ic, const void *wldp_buf)
2061 {
2062 	int err = 0;
2063 	int kid;
2064 	wl_del_key_t *dk = (wl_del_key_t *)wldp_buf;
2065 
2066 	ieee80211_dbg(IEEE80211_MSG_BRUSSELS, "wl_set_delkey(): "
2067 	    "keyix=%d\n", dk->idk_keyix);
2068 
2069 	kid = dk->idk_keyix;
2070 
2071 	if (kid == IEEE80211_KEYIX_NONE ||
2072 	    kid >= IEEE80211_WEP_NKID) {
2073 		ieee80211_err("wl_set_delkey: incorrect keyix\n");
2074 		err = EINVAL;
2075 		return (err);
2076 	} else {
2077 		(void) ieee80211_crypto_delkey(ic,
2078 		    &ic->ic_nw_keys[kid]);
2079 		ieee80211_mac_update(ic);
2080 	}
2081 
2082 	return (err);
2083 }
2084 
2085 /*
2086  * MAC_PROP_WL_MLME
2087  */
2088 
2089 static int
2090 wl_set_mlme(struct ieee80211com *ic, const void *wldp_buf)
2091 {
2092 	int err = 0;
2093 	uint32_t flags;
2094 	ieee80211_node_t *in;
2095 	wl_mlme_t *mlme = (wl_mlme_t *)wldp_buf;
2096 
2097 	ieee80211_dbg(IEEE80211_MSG_WPA, "wl_set_mlme: "
2098 	    "op=%d\n", mlme->im_op);
2099 
2100 	switch (mlme->im_op) {
2101 	case IEEE80211_MLME_DISASSOC:
2102 	case IEEE80211_MLME_DEAUTH:
2103 		if (ic->ic_opmode == IEEE80211_M_STA) {
2104 			/*
2105 			 * Mask ic_flags of IEEE80211_F_WPA to disable
2106 			 * ieee80211_notify temporarily.
2107 			 */
2108 			flags = ic->ic_flags;
2109 			ic->ic_flags &= ~IEEE80211_F_WPA;
2110 
2111 			IEEE80211_UNLOCK(ic);
2112 			ieee80211_new_state(ic, IEEE80211_S_INIT,
2113 			    mlme->im_reason);
2114 			IEEE80211_LOCK(ic);
2115 
2116 			ic->ic_flags = flags;
2117 		}
2118 		break;
2119 	case IEEE80211_MLME_ASSOC:
2120 		if (ic->ic_opmode != IEEE80211_M_STA) {
2121 			ieee80211_err("wifi_cfg_setmlme: opmode err\n");
2122 			err = EINVAL;
2123 			break;
2124 		}
2125 		if (ic->ic_des_esslen != 0) {
2126 		/*
2127 		 * Desired ssid specified; must match both bssid and
2128 		 * ssid to distinguish ap advertising multiple ssid's.
2129 		 */
2130 			in = ieee80211_find_node_with_ssid(&ic->ic_scan,
2131 			    mlme->im_macaddr,
2132 			    ic->ic_des_esslen,
2133 			    ic->ic_des_essid);
2134 		} else {
2135 		/*
2136 		 * Normal case; just match bssid.
2137 		 */
2138 			in = ieee80211_find_node(&ic->ic_scan,
2139 			    mlme->im_macaddr);
2140 		}
2141 		if (in == NULL) {
2142 			ieee80211_err("wifi_cfg_setmlme: "
2143 			    "no matched node\n");
2144 			err = EINVAL;
2145 			break;
2146 		}
2147 		IEEE80211_UNLOCK(ic);
2148 		ieee80211_sta_join(ic, in);
2149 		IEEE80211_LOCK(ic);
2150 		break;
2151 	default:
2152 		err = EINVAL;
2153 		break;
2154 	}
2155 
2156 	return (err);
2157 }
2158 
2159 /*
2160  * MAC_PROP_WL_WPA_KEY
2161  */
2162 static int
2163 wl_set_wpakey(struct ieee80211com *ic, const void *wldp_buf)
2164 {
2165 	int err = 0;
2166 	uint16_t kid;
2167 	struct ieee80211_node *in;
2168 	struct ieee80211_key *wk;
2169 	wl_key_t *ik = (wl_key_t *)wldp_buf;
2170 
2171 	ieee80211_dbg(IEEE80211_MSG_BRUSSELS, "wl_set_wpakey: "
2172 	    "idx=%d\n", ik->ik_keyix);
2173 
2174 	/*
2175 	 * cipher support is verified by ieee80211_crypt_newkey
2176 	 * this also checks ik->ik_keylen > sizeof(wk->wk_key)
2177 	 */
2178 	if (ik->ik_keylen > sizeof (ik->ik_keydata)) {
2179 		ieee80211_err("wl_set_wpakey: key is too long\n");
2180 		err = EINVAL;
2181 		return (err);
2182 	}
2183 	kid = ik->ik_keyix;
2184 	if (kid == IEEE80211_KEYIX_NONE || kid >= IEEE80211_WEP_NKID) {
2185 		ieee80211_err("wl_set_wpakey: incorrect keyix\n");
2186 		err = EINVAL;
2187 		return (err);
2188 	} else {
2189 		wk = &ic->ic_nw_keys[kid];
2190 		/*
2191 		 * Globle slots start off w/o any assigned key index.
2192 		 * Force one here for consistency with WEPKEY.
2193 		 */
2194 		if (wk->wk_keyix == IEEE80211_KEYIX_NONE)
2195 			wk->wk_keyix = kid;
2196 		in = NULL;
2197 	}
2198 
2199 	KEY_UPDATE_BEGIN(ic);
2200 	if (ieee80211_crypto_newkey(ic, ik->ik_type,
2201 	    ik->ik_flags, wk)) {
2202 		wk->wk_keylen = ik->ik_keylen;
2203 		/* MIC presence is implied by cipher type */
2204 		if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE)
2205 			wk->wk_keylen = IEEE80211_KEYBUF_SIZE;
2206 		wk->wk_keyrsc = ik->ik_keyrsc;
2207 		wk->wk_keytsc = 0;
2208 		wk->wk_flags |= ik->ik_flags &
2209 		    (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV);
2210 		(void) memset(wk->wk_key, 0, sizeof (wk->wk_key));
2211 		(void) memcpy(wk->wk_key, ik->ik_keydata,
2212 		    ik->ik_keylen);
2213 		if (!ieee80211_crypto_setkey(ic, wk,
2214 		    in != NULL ? in->in_macaddr : ik->ik_macaddr)) {
2215 			err = EIO;
2216 		} else if ((ik->ik_flags & IEEE80211_KEY_DEFAULT)) {
2217 			ic->ic_def_txkey = kid;
2218 			ieee80211_mac_update(ic);
2219 		}
2220 	} else {
2221 		err = EIO;
2222 	}
2223 	KEY_UPDATE_END(ic);
2224 
2225 	return (err);
2226 }
2227 
2228 /*
2229  * MAC_PROP_WL_SUP_RATE
2230  */
2231 static void
2232 wl_get_suprates(struct ieee80211com *ic, void *wldp_buf)
2233 {
2234 	int i, j, k, l;
2235 	uint8_t srates;
2236 	uint8_t *drates;
2237 	wl_rates_t *wl_rates;
2238 	const struct ieee80211_rateset *srs;
2239 
2240 	wl_rates = (wl_rates_t *)wldp_buf;
2241 
2242 	wl_rates->wl_rates_num = 0;
2243 	drates = (uint8_t *)wl_rates->wl_rates_rates;
2244 	for (i = 0; i < IEEE80211_MODE_MAX; i++) {
2245 		srs = &ic->ic_sup_rates[i];
2246 		if (srs->ir_nrates == 0)
2247 			continue;
2248 		for (j = 0; j < srs->ir_nrates; j++) {
2249 			srates = IEEE80211_RV(srs->ir_rates[j]);
2250 			/* sort & skip duplicated rates */
2251 			for (k = 0; k < wl_rates->wl_rates_num; k++) {
2252 				if (srates <= drates[k])
2253 					break;
2254 			}
2255 			if (srates == drates[k])
2256 				/* skip duplicated rates */
2257 				continue;
2258 			/* sort */
2259 			for (l = wl_rates->wl_rates_num; l > k; l--)
2260 				drates[l] = drates[l-1];
2261 			drates[k] = srates;
2262 			wl_rates->wl_rates_num++;
2263 		}
2264 	}
2265 
2266 }
2267 
2268 /*
2269  * Typically invoked by drivers in response to request for
2270  * information or to change settings from the userland.
2271  *
2272  * Return value should be checked by WiFI drivers. Return 0
2273  * on success. Otherwise, return non-zero value to indicate
2274  * the error. Driver should operate as below when the return
2275  * error is:
2276  * ENETRESET	Reset wireless network and re-start to join a
2277  * 		WLAN, ENETRESET is returned when a configuration
2278  * 		parameter has been changed.
2279  * 		When acknowledge a M_IOCTL message, this error
2280  * 		is ignored
2281  */
2282 /* ARGSUSED */
2283 int
2284 ieee80211_setprop(void *ic_arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2285     uint_t wldp_length, const void *wldp_buf)
2286 {
2287 	int err = 0;
2288 	struct ieee80211com *ic = ic_arg;
2289 
2290 	ASSERT(ic != NULL);
2291 	IEEE80211_LOCK(ic);
2292 
2293 	switch (wldp_pr_num) {
2294 	/* mac_prop_id */
2295 	case MAC_PROP_WL_ESSID:
2296 		err = wl_set_essid(ic, wldp_buf);
2297 		break;
2298 	case MAC_PROP_WL_BSSID:
2299 		err = wl_set_bssid(ic, wldp_buf);
2300 		break;
2301 	case MAC_PROP_WL_PHY_CONFIG:
2302 		err = wl_set_phy(ic, wldp_buf);
2303 		break;
2304 	case MAC_PROP_WL_KEY_TAB:
2305 		err = wl_set_wepkey(ic, wldp_buf);
2306 		break;
2307 	case MAC_PROP_WL_AUTH_MODE:
2308 		err = wl_set_authmode(ic, wldp_buf);
2309 		break;
2310 	case MAC_PROP_WL_ENCRYPTION:
2311 		err = wl_set_encrypt(ic, wldp_buf);
2312 		break;
2313 	case MAC_PROP_WL_BSSTYPE:
2314 		err = wl_set_bsstype(ic, wldp_buf);
2315 		break;
2316 	case MAC_PROP_WL_DESIRED_RATES:
2317 		err = wl_set_desrates(ic, wldp_buf);
2318 		break;
2319 	case MAC_PROP_WL_WPA:
2320 		err = wl_set_wpa(ic, wldp_buf);
2321 		break;
2322 	case MAC_PROP_WL_KEY:
2323 		err = wl_set_wpakey(ic, wldp_buf);
2324 		break;
2325 	case MAC_PROP_WL_DELKEY:
2326 		err = wl_set_delkey(ic, wldp_buf);
2327 		break;
2328 	case MAC_PROP_WL_SETOPTIE:
2329 		err = wl_set_optie(ic, wldp_buf);
2330 		break;
2331 	case MAC_PROP_WL_MLME:
2332 		err = wl_set_mlme(ic, wldp_buf);
2333 		break;
2334 	case MAC_PROP_WL_LINKSTATUS:
2335 	case MAC_PROP_WL_ESS_LIST:
2336 	case MAC_PROP_WL_SUPPORTED_RATES:
2337 	case MAC_PROP_WL_RSSI:
2338 	case MAC_PROP_WL_CAPABILITY:
2339 	case MAC_PROP_WL_SCANRESULTS:
2340 		ieee80211_err("ieee80211_setprop: opmode err\n");
2341 		err = EINVAL;
2342 		break;
2343 	default:
2344 		ieee80211_err("ieee80211_setprop: opmode not support\n");
2345 		err = ENOTSUP;
2346 		break;
2347 	}
2348 
2349 	IEEE80211_UNLOCK(ic);
2350 
2351 	return (err);
2352 }
2353 
2354 /* ARGSUSED */
2355 int
2356 ieee80211_getprop(void *ic_arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2357     uint_t pr_flags, uint_t wldp_length, void *wldp_buf, uint_t *perm)
2358 {
2359 	int err = 0;
2360 	struct ieee80211com *ic = ic_arg;
2361 
2362 	if (wldp_length == 0) {
2363 		err = EINVAL;
2364 		return (err);
2365 	}
2366 	bzero(wldp_buf, wldp_length);
2367 
2368 	ASSERT(ic != NULL);
2369 	IEEE80211_LOCK(ic);
2370 
2371 	*perm = MAC_PROP_PERM_RW;
2372 
2373 	switch (wldp_pr_num) {
2374 	/* mac_prop_id */
2375 	case MAC_PROP_WL_ESSID:
2376 		wl_get_essid(ic, wldp_buf);
2377 		break;
2378 	case MAC_PROP_WL_BSSID:
2379 		wl_get_bssid(ic, wldp_buf);
2380 		break;
2381 	case MAC_PROP_WL_PHY_CONFIG:
2382 		err = wl_get_phy(ic, wldp_buf);
2383 		break;
2384 	case MAC_PROP_WL_AUTH_MODE:
2385 		wl_get_authmode(ic, wldp_buf);
2386 		break;
2387 	case MAC_PROP_WL_ENCRYPTION:
2388 		wl_get_encrypt(ic, wldp_buf);
2389 		break;
2390 	case MAC_PROP_WL_BSSTYPE:
2391 		wl_get_bsstype(ic, wldp_buf);
2392 		break;
2393 	case MAC_PROP_WL_DESIRED_RATES:
2394 		wl_get_desrates(ic, wldp_buf);
2395 		break;
2396 	case MAC_PROP_WL_LINKSTATUS:
2397 		*perm = MAC_PROP_PERM_READ;
2398 		wl_get_linkstatus(ic, wldp_buf);
2399 		break;
2400 	case MAC_PROP_WL_ESS_LIST:
2401 		*perm = MAC_PROP_PERM_READ;
2402 		wl_get_esslist(ic, wldp_buf);
2403 		break;
2404 	case MAC_PROP_WL_SUPPORTED_RATES:
2405 		*perm = MAC_PROP_PERM_READ;
2406 		wl_get_suprates(ic, wldp_buf);
2407 		break;
2408 	case MAC_PROP_WL_RSSI:
2409 		*perm = MAC_PROP_PERM_READ;
2410 		wl_get_rssi(ic, wldp_buf);
2411 		break;
2412 	case MAC_PROP_WL_CAPABILITY:
2413 		*perm = MAC_PROP_PERM_READ;
2414 		wl_get_capability(ic, wldp_buf);
2415 		break;
2416 	case MAC_PROP_WL_WPA:
2417 		wl_get_wpa(ic, wldp_buf);
2418 		break;
2419 	case MAC_PROP_WL_SCANRESULTS:
2420 		*perm = MAC_PROP_PERM_READ;
2421 		wl_get_scanresults(ic, wldp_buf);
2422 		break;
2423 	case MAC_PROP_WL_KEY_TAB:
2424 	case MAC_PROP_WL_KEY:
2425 	case MAC_PROP_WL_DELKEY:
2426 	case MAC_PROP_WL_SETOPTIE:
2427 	case MAC_PROP_WL_MLME:
2428 		ieee80211_err("ieee80211_setprop: opmode err\n");
2429 		err = EINVAL;
2430 		break;
2431 	default:
2432 		ieee80211_err("ieee80211_setprop: opmode not support\n");
2433 		err = ENOTSUP;
2434 		break;
2435 	}
2436 
2437 	IEEE80211_UNLOCK(ic);
2438 
2439 	return (err);
2440 }
2441