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