summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/net80211/net80211_ioctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/io/net80211/net80211_ioctl.c')
-rw-r--r--usr/src/uts/common/io/net80211/net80211_ioctl.c1359
1 files changed, 1359 insertions, 0 deletions
diff --git a/usr/src/uts/common/io/net80211/net80211_ioctl.c b/usr/src/uts/common/io/net80211/net80211_ioctl.c
new file mode 100644
index 0000000000..3701baa6f7
--- /dev/null
+++ b/usr/src/uts/common/io/net80211/net80211_ioctl.c
@@ -0,0 +1,1359 @@
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/strsun.h>
+#include <sys/policy.h>
+#include <inet/common.h>
+#include <inet/nd.h>
+#include <inet/mi.h>
+#include <sys/note.h>
+#include <sys/mac.h>
+#include <inet/wifi_ioctl.h>
+#include "net80211_impl.h"
+
+static size_t
+wifi_strnlen(const char *s, size_t n)
+{
+ size_t i;
+
+ for (i = 0; i < n && s[i] != '\0'; i++)
+ /* noop */;
+ return (i);
+}
+
+/*
+ * Initialize an output message block by copying from an
+ * input message block. The message is of type wldp_t.
+ * mp input message block
+ * buflen length of wldp_buf
+ */
+static void
+wifi_setupoutmsg(mblk_t *mp, int buflen)
+{
+ wldp_t *wp;
+
+ wp = (wldp_t *)mp->b_rptr;
+ wp->wldp_length = WIFI_BUF_OFFSET + buflen;
+ wp->wldp_result = WL_SUCCESS;
+ mp->b_wptr = mp->b_rptr + wp->wldp_length;
+}
+
+/*
+ * Allocate and initialize an output message.
+ */
+static mblk_t *
+wifi_getoutmsg(mblk_t *mp, uint32_t cmd, int buflen)
+{
+ mblk_t *mp1;
+ int size;
+
+ size = WIFI_BUF_OFFSET;
+ if (cmd == WLAN_GET_PARAM)
+ size += buflen; /* to hold output parameters */
+ mp1 = allocb(size, BPRI_HI);
+ if (mp1 == NULL) {
+ ieee80211_err("wifi_getoutbuf: allocb %d bytes failed!\n",
+ size);
+ return (NULL);
+ }
+
+ bzero(mp1->b_rptr, size);
+ bcopy(mp->b_rptr, mp1->b_rptr, WIFI_BUF_OFFSET);
+ wifi_setupoutmsg(mp1, size - WIFI_BUF_OFFSET);
+
+ return (mp1);
+}
+
+static int
+wifi_cfg_essid(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+ wldp_t *outp;
+ wl_essid_t *iw_essid = (wl_essid_t *)inp->wldp_buf;
+ wl_essid_t *ow_essid;
+ char *essid;
+ int err = 0;
+
+ if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_essid_t))) == NULL)
+ return (ENOMEM);
+ outp = (wldp_t *)omp->b_rptr;
+ ow_essid = (wl_essid_t *)outp->wldp_buf;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ essid = (char *)ic->ic_des_essid;
+ if (essid[0] == '\0')
+ essid = (char *)ic->ic_bss->in_essid;
+ ow_essid->wl_essid_length = wifi_strnlen((const char *)essid,
+ IEEE80211_NWID_LEN);
+ bcopy(essid, ow_essid->wl_essid_essid,
+ ow_essid->wl_essid_length);
+ break;
+ case WLAN_SET_PARAM:
+ if (iw_essid->wl_essid_length > IEEE80211_NWID_LEN) {
+ ieee80211_err("wifi_cfg_essid: "
+ "essid too long, %u, max %u\n",
+ iw_essid->wl_essid_length, IEEE80211_NWID_LEN);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+ essid = iw_essid->wl_essid_essid;
+ essid[IEEE80211_NWID_LEN] = 0;
+ ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_essid: "
+ "set essid=%s length=%d\n",
+ essid, iw_essid->wl_essid_length);
+
+ ic->ic_des_esslen = iw_essid->wl_essid_length;
+ if (ic->ic_des_esslen != 0)
+ bcopy(essid, ic->ic_des_essid, ic->ic_des_esslen);
+ if (ic->ic_des_esslen < IEEE80211_NWID_LEN)
+ ic->ic_des_essid[ic->ic_des_esslen] = 0;
+ err = ENETRESET;
+ break;
+ default:
+ ieee80211_err("wifi_cfg_essid: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+static int
+wifi_cfg_bssid(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+ wldp_t *outp;
+ uint8_t *bssid;
+ int err = 0;
+
+ if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_bssid_t))) == NULL)
+ return (ENOMEM);
+ outp = (wldp_t *)omp->b_rptr;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ if (ic->ic_flags & IEEE80211_F_DESBSSID)
+ bssid = ic->ic_des_bssid;
+ else
+ bssid = ic->ic_bss->in_bssid;
+ bcopy(bssid, outp->wldp_buf, sizeof (wl_bssid_t));
+ break;
+ case WLAN_SET_PARAM:
+ ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_bssid: "
+ "set bssid=%s\n",
+ ieee80211_macaddr_sprintf(inp->wldp_buf));
+ bcopy(inp->wldp_buf, ic->ic_des_bssid, sizeof (wl_bssid_t));
+ ic->ic_flags |= IEEE80211_F_DESBSSID;
+ err = ENETRESET;
+ break;
+ default:
+ ieee80211_err("wifi_cfg_bssid: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+static int
+wifi_cfg_nodename(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+ wldp_t *outp;
+ wl_nodename_t *iw_name = (wl_nodename_t *)inp->wldp_buf;
+ wl_nodename_t *ow_name;
+ char *nodename;
+ int len, err;
+
+ err = 0;
+ if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_nodename_t))) == NULL)
+ return (ENOMEM);
+ outp = (wldp_t *)omp->b_rptr;
+ ow_name = (wl_nodename_t *)outp->wldp_buf;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ len = wifi_strnlen((const char *)ic->ic_nickname,
+ IEEE80211_NWID_LEN);
+ ow_name->wl_nodename_length = len;
+ bcopy(ic->ic_nickname, ow_name->wl_nodename_name, len);
+ break;
+ case WLAN_SET_PARAM:
+ if (iw_name->wl_nodename_length > IEEE80211_NWID_LEN) {
+ ieee80211_err("wifi_cfg_nodename: "
+ "node name too long, %u\n",
+ iw_name->wl_nodename_length);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+ nodename = iw_name->wl_nodename_name;
+ nodename[IEEE80211_NWID_LEN] = 0;
+ ieee80211_dbg(IEEE80211_MSG_CONFIG,
+ "wifi_cfg_nodename: set nodename %s, len=%d\n",
+ nodename, iw_name->wl_nodename_length);
+
+ len = iw_name->wl_nodename_length;
+ if (len > 0)
+ bcopy(nodename, ic->ic_nickname, len);
+ if (len < IEEE80211_NWID_LEN)
+ ic->ic_nickname[len] = 0;
+ break;
+ default:
+ ieee80211_err("wifi_cfg_nodename: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+static int
+wifi_cfg_phy(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+ wldp_t *outp;
+ wl_phy_conf_t *iw_phy = (wl_phy_conf_t *)inp->wldp_buf;
+ wl_phy_conf_t *ow_phy;
+ struct ieee80211_channel *ch = ic->ic_curchan;
+ int err = 0;
+
+ if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_phy_conf_t))) == NULL)
+ return (ENOMEM);
+ outp = (wldp_t *)omp->b_rptr;
+ ow_phy = (wl_phy_conf_t *)outp->wldp_buf;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM: {
+ /* get current physical (FH, DS, ERP) parameters */
+ if (IEEE80211_IS_CHAN_A(ch) || IEEE80211_IS_CHAN_T(ch)) {
+ wl_ofdm_t *ofdm = (wl_ofdm_t *)ow_phy;
+
+ ofdm->wl_ofdm_subtype = WL_OFDM;
+ ofdm->wl_ofdm_frequency = ch->ich_freq;
+ } else {
+ switch (ic->ic_phytype) {
+ case IEEE80211_T_FH: {
+ wl_fhss_t *fhss = (wl_fhss_t *)ow_phy;
+
+ fhss->wl_fhss_subtype = WL_FHSS;
+ fhss->wl_fhss_channel =
+ ieee80211_chan2ieee(ic, ch);
+ break;
+ }
+ case IEEE80211_T_DS: {
+ wl_dsss_t *dsss = (wl_dsss_t *)ow_phy;
+
+ dsss->wl_dsss_subtype = WL_DSSS;
+ dsss->wl_dsss_channel =
+ ieee80211_chan2ieee(ic, ch);
+ break;
+ }
+ case IEEE80211_T_OFDM: {
+ wl_erp_t *erp = (wl_erp_t *)ow_phy;
+
+ erp->wl_erp_subtype = WL_ERP;
+ erp->wl_erp_channel =
+ ieee80211_chan2ieee(ic, ch);
+ break;
+ }
+ default:
+ ieee80211_err("wifi_cfg_phy: "
+ "unknown phy type, %x\n", ic->ic_phytype);
+ outp->wldp_result = WL_HW_ERROR;
+ err = EIO;
+ break;
+ } /* switch (ic->ic_phytype) */
+ }
+ break;
+ }
+
+ case WLAN_SET_PARAM: {
+ wl_dsss_t *dsss = (wl_dsss_t *)iw_phy;
+ int16_t ch = dsss->wl_dsss_channel;
+
+ ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_phy: "
+ "set channel=%d\n", ch);
+ if (ch == 0 || ch == (int16_t)IEEE80211_CHAN_ANY) {
+ ic->ic_des_chan = IEEE80211_CHAN_ANYC;
+ } else if ((uint_t)ch > IEEE80211_CHAN_MAX ||
+ ieee80211_isclr(ic->ic_chan_active, ch)) {
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ } else {
+ ic->ic_des_chan = ic->ic_ibss_chan =
+ &ic->ic_sup_channels[ch];
+ }
+ switch (ic->ic_state) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_SCAN:
+ err = ENETRESET;
+ break;
+ default:
+ /*
+ * If the desired channel has changed (to something
+ * other than any) and we're not already scanning,
+ * then kick the state machine.
+ */
+ if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
+ ic->ic_bss->in_chan != ic->ic_des_chan &&
+ (ic->ic_flags & IEEE80211_F_SCAN) == 0)
+ err = ENETRESET;
+ break;
+ }
+ break;
+ }
+
+ default:
+ ieee80211_err("wifi_cfg_phy: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ } /* switch (cmd) */
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+static int
+wifi_cfg_wepkey(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+ wldp_t *outp;
+ wl_wep_key_t *iw_wepkey = (wl_wep_key_t *)inp->wldp_buf;
+ struct ieee80211_key *k;
+ uint16_t i;
+ uint32_t klen;
+ int err = 0;
+
+ if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
+ return (ENOMEM);
+ outp = (wldp_t *)omp->b_rptr;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ outp->wldp_result = WL_WRITEONLY;
+ err = EINVAL;
+ break;
+ case WLAN_SET_PARAM:
+ if (inp->wldp_length < sizeof (wl_wep_key_tab_t)) {
+ ieee80211_err("wifi_cfg_wepkey: "
+ "parameter too short, %d, expected %d\n",
+ inp->wldp_length, sizeof (wl_wep_key_tab_t));
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ /* set all valid keys */
+ for (i = 0; i < MAX_NWEPKEYS; i++) {
+ if (iw_wepkey[i].wl_wep_operation != WL_ADD)
+ continue;
+ klen = iw_wepkey[i].wl_wep_length;
+ if (klen > IEEE80211_KEYBUF_SIZE) {
+ ieee80211_err("wifi_cfg_wepkey: "
+ "invalid wepkey length, %u\n", klen);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ continue; /* continue to set other keys */
+ }
+ if (klen == 0)
+ continue;
+
+ /*
+ * Set key contents. Only WEP is supported
+ */
+ ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_wepkey: "
+ "set key %u, len=%u\n", i, klen);
+ k = &ic->ic_nw_keys[i];
+ if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP,
+ IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, k) == 0) {
+ ieee80211_err("wifi_cfg_wepkey: "
+ "abort, create key failed. id=%u\n", i);
+ outp->wldp_result = WL_HW_ERROR;
+ err = EIO;
+ continue;
+ }
+ k->wk_keyix = i;
+ k->wk_keylen = (uint8_t)klen;
+ k->wk_flags |= IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
+ bzero(k->wk_key, IEEE80211_KEYBUF_SIZE);
+ bcopy(iw_wepkey[i].wl_wep_key, k->wk_key, klen);
+ if (ieee80211_crypto_setkey(ic, k, ic->ic_macaddr)
+ == 0) {
+ ieee80211_err("wifi_cfg_wepkey: "
+ "set key failed len=%u\n", klen);
+ outp->wldp_result = WL_HW_ERROR;
+ err = EIO;
+ }
+ }
+ if (err == 0)
+ err = ENETRESET;
+ break;
+ default:
+ ieee80211_err("wifi_cfg_wepkey: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+static int
+wifi_cfg_keyid(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+ wldp_t *outp;
+ wl_wep_key_id_t *iw_kid = (wl_wep_key_id_t *)inp->wldp_buf;
+ wl_wep_key_id_t *ow_kid;
+ int err = 0;
+
+ if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_wep_key_id_t))) == NULL)
+ return (ENOMEM);
+ outp = (wldp_t *)omp->b_rptr;
+ ow_kid = (wl_wep_key_id_t *)outp->wldp_buf;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ *ow_kid = (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) ?
+ 0 : ic->ic_def_txkey;
+ break;
+ case WLAN_SET_PARAM:
+ if (*iw_kid >= MAX_NWEPKEYS) {
+ ieee80211_err("wifi_cfg_keyid: "
+ "keyid too large, %u\n", *iw_kid);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ } else {
+ ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_keyid: "
+ "set keyid=%u\n", *iw_kid);
+ ic->ic_def_txkey = *iw_kid;
+ err = ENETRESET;
+ }
+ break;
+ default:
+ ieee80211_err("wifi_cfg_keyid: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+static int
+wifi_cfg_authmode(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+ wldp_t *outp;
+ wl_authmode_t *iw_auth = (wl_authmode_t *)inp->wldp_buf;
+ wl_authmode_t *ow_auth;
+ int err = 0;
+
+ if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_authmode_t))) == NULL)
+ return (ENOMEM);
+ outp = (wldp_t *)omp->b_rptr;
+ ow_auth = (wl_authmode_t *)outp->wldp_buf;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ *ow_auth = ic->ic_bss->in_authmode;
+ break;
+ case WLAN_SET_PARAM:
+ if (*iw_auth == ic->ic_bss->in_authmode)
+ break;
+
+ ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_authmode: "
+ "set authmode=%u\n", *iw_auth);
+ switch (*iw_auth) {
+ case WL_OPENSYSTEM:
+ case WL_SHAREDKEY:
+ ic->ic_bss->in_authmode = *iw_auth;
+ err = ENETRESET;
+ break;
+ default:
+ ieee80211_err("wifi_cfg_authmode: "
+ "unknown authmode %u\n", *iw_auth);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+ break;
+ default:
+ ieee80211_err("wifi_cfg_authmode: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+static int
+wifi_cfg_encrypt(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+ wldp_t *outp;
+ wl_encryption_t *iw_encryp = (wl_encryption_t *)inp->wldp_buf;
+ wl_encryption_t *ow_encryp;
+ uint32_t flags;
+ int err = 0;
+
+ if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_encryption_t))) == NULL)
+ return (ENOMEM);
+ outp = (wldp_t *)omp->b_rptr;
+ ow_encryp = (wl_encryption_t *)outp->wldp_buf;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ *ow_encryp = (ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0;
+ break;
+ case WLAN_SET_PARAM:
+ ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_encrypt: "
+ "set encryption=%u\n", *iw_encryp);
+ flags = ic->ic_flags;
+ if (*iw_encryp == WL_NOENCRYPTION)
+ flags &= ~IEEE80211_F_PRIVACY;
+ else
+ flags |= IEEE80211_F_PRIVACY;
+
+ if (ic->ic_flags != flags) {
+ ic->ic_flags = flags;
+ err = ENETRESET;
+ }
+ break;
+ default:
+ ieee80211_err("wifi_cfg_encrypt: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+static int
+wifi_cfg_bsstype(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+ wldp_t *outp;
+ wl_bss_type_t *iw_opmode = (wl_bss_type_t *)inp->wldp_buf;
+ wl_bss_type_t *ow_opmode;
+ int err = 0;
+
+ if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_bss_type_t))) == NULL)
+ return (ENOMEM);
+ outp = (wldp_t *)omp->b_rptr;
+ ow_opmode = (wl_bss_type_t *)outp->wldp_buf;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ *ow_opmode = WL_BSS_BSS;
+ break;
+ case IEEE80211_M_IBSS:
+ *ow_opmode = WL_BSS_IBSS;
+ break;
+ default:
+ *ow_opmode = WL_BSS_ANY;
+ break;
+ }
+ break;
+ case WLAN_SET_PARAM:
+ ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_bsstype: "
+ "set bsstype=%u\n", *iw_opmode);
+ switch (*iw_opmode) {
+ case WL_BSS_BSS:
+ ic->ic_flags &= ~IEEE80211_F_IBSSON;
+ ic->ic_opmode = IEEE80211_M_STA;
+ err = ENETRESET;
+ break;
+ case WL_BSS_IBSS:
+ if ((ic->ic_caps & IEEE80211_C_IBSS) == 0) {
+ outp->wldp_result = WL_LACK_FEATURE;
+ err = ENOTSUP;
+ break;
+ }
+
+ if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) {
+ ic->ic_flags |= IEEE80211_F_IBSSON;
+ ic->ic_opmode = IEEE80211_M_IBSS;
+ err = ENETRESET;
+ }
+ break;
+ default:
+ ieee80211_err("wifi_cfg_bsstype: "
+ "unknown opmode %u\n", *iw_opmode);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+ break;
+ default:
+ ieee80211_err("wifi_cfg_bsstype: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+static int
+wifi_cfg_linkstatus(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *outp;
+ wl_linkstatus_t *ow_linkstat;
+ int err = 0;
+
+ if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_linkstatus_t))) == NULL)
+ return (ENOMEM);
+ outp = (wldp_t *)omp->b_rptr;
+ ow_linkstat = (wl_linkstatus_t *)outp->wldp_buf;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ *ow_linkstat = (ic->ic_state == IEEE80211_S_RUN) ?
+ WL_CONNECTED : WL_NOTCONNECTED;
+ break;
+ case WLAN_SET_PARAM:
+ outp->wldp_result = WL_READONLY;
+ err = EINVAL;
+ break;
+ default:
+ ieee80211_err("wifi_cfg_linkstatus: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+static int
+wifi_cfg_suprates(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *outp;
+ wl_rates_t *ow_rates;
+ const struct ieee80211_rateset *srs;
+ uint8_t srates, *drates;
+ int err, buflen, i, j, k, l;
+
+ err = 0;
+ /* rate value (wl_rates_rates) is of type char */
+ buflen = offsetof(wl_rates_t, wl_rates_rates) +
+ sizeof (char) * IEEE80211_MODE_MAX * IEEE80211_RATE_MAXSIZE;
+ if ((omp = wifi_getoutmsg(*mp, cmd, buflen)) == NULL)
+ return (ENOMEM);
+ outp = (wldp_t *)omp->b_rptr;
+ ow_rates = (wl_rates_t *)outp->wldp_buf;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ /* all rates supported by the device */
+ ow_rates->wl_rates_num = 0;
+ drates = (uint8_t *)ow_rates->wl_rates_rates;
+ for (i = 0; i < IEEE80211_MODE_MAX; i++) {
+ srs = &ic->ic_sup_rates[i];
+ if (srs->ir_nrates == 0)
+ continue;
+
+ for (j = 0; j < srs->ir_nrates; j++) {
+ srates = IEEE80211_RV(srs->ir_rates[j]);
+ /* sort and skip duplicated rates */
+ for (k = 0; k < ow_rates->wl_rates_num; k++) {
+ if (srates <= drates[k])
+ break;
+ }
+ if (srates == drates[k])
+ continue; /* duplicate, skip */
+ /* sort */
+ for (l = ow_rates->wl_rates_num; l > k; l--)
+ drates[l] = drates[l-1];
+ drates[k] = srates;
+ ow_rates->wl_rates_num++;
+ }
+ }
+ break;
+ case WLAN_SET_PARAM:
+ outp->wldp_result = WL_READONLY;
+ err = EINVAL;
+ break;
+ default:
+ ieee80211_err("wifi_cfg_suprates: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+static int
+wifi_cfg_desrates(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+ wl_rates_t *iw_rates = (wl_rates_t *)inp->wldp_buf;
+ mblk_t *omp;
+ wldp_t *outp;
+ wl_rates_t *ow_rates;
+ struct ieee80211_node *in = ic->ic_bss;
+ struct ieee80211_rateset *rs = &in->in_rates;
+ uint8_t drate, srate;
+ int err, i, j;
+ boolean_t found;
+
+ err = 0;
+ if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_rates_t))) == NULL)
+ return (ENOMEM);
+ outp = (wldp_t *)omp->b_rptr;
+ ow_rates = (wl_rates_t *)outp->wldp_buf;
+
+ srate = rs->ir_rates[in->in_txrate] & IEEE80211_RATE_VAL;
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ ow_rates->wl_rates_num = 1;
+ ow_rates->wl_rates_rates[0] =
+ (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) ?
+ srate : ic->ic_fixed_rate;
+ break;
+ case WLAN_SET_PARAM:
+ drate = iw_rates->wl_rates_rates[0];
+ if (ic->ic_fixed_rate == drate)
+ break;
+
+ ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_desrates: "
+ "set desired rate=%u\n", drate);
+
+ if (drate == 0) { /* reset */
+ ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
+ if (ic->ic_state == IEEE80211_S_RUN) {
+ IEEE80211_UNLOCK(ic);
+ ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
+ IEEE80211_LOCK(ic);
+ }
+ break;
+ }
+
+ /*
+ * Set desired rate. the desired rate is for data transfer
+ * and usually is checked and used when driver changes to
+ * RUN state.
+ * If the driver is in AUTH | ASSOC | RUN state, desired
+ * rate is checked against rates supported by current ESS.
+ * If it's supported and current state is AUTH|ASSOC, nothing
+ * needs to be doen by driver since the desired rate will
+ * be enabled when the device changes to RUN state. And
+ * when current state is RUN, Re-associate with the ESS to
+ * enable the desired rate.
+ */
+ if (ic->ic_state != IEEE80211_S_INIT &&
+ ic->ic_state != IEEE80211_S_SCAN) {
+ /* check if the rate is supported by current ESS */
+ for (i = 0; i < rs->ir_nrates; i++) {
+ if (drate == IEEE80211_RV(rs->ir_rates[i]))
+ break;
+ }
+ if (i < rs->ir_nrates) { /* supported */
+ ic->ic_fixed_rate = drate;
+ if (ic->ic_state == IEEE80211_S_RUN) {
+ IEEE80211_UNLOCK(ic);
+ ieee80211_new_state(ic,
+ IEEE80211_S_ASSOC, 0);
+ IEEE80211_LOCK(ic);
+ }
+ break;
+ }
+ }
+
+ /* check the rate is supported by device */
+ found = B_FALSE;
+ for (i = 0; i < IEEE80211_MODE_MAX; i++) {
+ rs = &ic->ic_sup_rates[i];
+ for (j = 0; j < rs->ir_nrates; j++) {
+ if (drate == IEEE80211_RV(rs->ir_rates[j])) {
+ found = B_TRUE;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+ if (!found) {
+ ieee80211_err("wifi_cfg_desrates: "
+ "invalid rate %d\n", drate);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+ ic->ic_fixed_rate = drate;
+ if (ic->ic_state != IEEE80211_S_SCAN)
+ err = ENETRESET; /* restart */
+ break;
+ default:
+ ieee80211_err("wifi_cfg_desrates: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+/*
+ * Rescale device's RSSI value to (0, 15) as required by WiFi
+ * driver IOCTLs (PSARC/2003/722)
+ */
+static wl_rssi_t
+wifi_getrssi(struct ieee80211_node *in)
+{
+ struct ieee80211com *ic = in->in_ic;
+ wl_rssi_t rssi, max_rssi;
+
+ rssi = ic->ic_node_getrssi(in);
+ max_rssi = (ic->ic_maxrssi == 0) ? IEEE80211_MAXRSSI : ic->ic_maxrssi;
+ if (rssi == 0)
+ rssi = 0;
+ else if (rssi >= max_rssi)
+ rssi = MAX_RSSI;
+ else
+ rssi = rssi * MAX_RSSI / max_rssi + 1;
+
+ return (rssi);
+}
+
+static int
+wifi_cfg_rssi(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *outp;
+ wl_rssi_t *ow_rssi;
+ int err = 0;
+
+ if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_rssi_t))) == NULL)
+ return (ENOMEM);
+ outp = (wldp_t *)omp->b_rptr;
+ ow_rssi = (wl_rssi_t *)outp->wldp_buf;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ *ow_rssi = wifi_getrssi(ic->ic_bss);
+ break;
+ case WLAN_SET_PARAM:
+ outp->wldp_result = WL_READONLY;
+ err = EINVAL;
+ break;
+ default:
+ ieee80211_err("wifi_cfg_rssi: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ return (EINVAL);
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+/*
+ * maximum scan wait time in second.
+ * Time spent on scaning one channel is usually 100~200ms. The maximum
+ * number of channels defined in wifi_ioctl.h is 99 (MAX_CHANNEL_NUM).
+ * As a result the maximum total scan time is defined as below in ms.
+ */
+#define WAIT_SCAN_MAX (200 * MAX_CHANNEL_NUM)
+
+static void
+wifi_wait_scan(struct ieee80211com *ic)
+{
+ ieee80211_impl_t *im = ic->ic_private;
+
+ while ((ic->ic_flags & (IEEE80211_F_SCAN | IEEE80211_F_ASCAN)) != 0) {
+ if (cv_timedwait_sig(&im->im_scan_cv, &ic->ic_genlock,
+ ddi_get_lbolt() + drv_usectohz(WAIT_SCAN_MAX * 1000)) !=
+ 0) {
+ break;
+ }
+ }
+}
+
+#define WIFI_HAVE_CAP(in, flag) (((in)->in_capinfo & (flag)) ? 1 : 0)
+
+/*
+ * Callback function used by ieee80211_iterate_nodes() in
+ * wifi_cfg_esslist() to get info of each node in a node table
+ * arg output buffer, pointer to wl_ess_list_t
+ * in each node in the node table
+ */
+static void
+wifi_read_ap(void *arg, struct ieee80211_node *in)
+{
+ wl_ess_list_t *aps = arg;
+ ieee80211com_t *ic = in->in_ic;
+ struct ieee80211_channel *chan = in->in_chan;
+ struct ieee80211_rateset *rates = &(in->in_rates);
+ wl_ess_conf_t *conf;
+ uint8_t *end;
+ uint_t i, nrates;
+
+ end = (uint8_t *)aps - WIFI_BUF_OFFSET + MAX_BUF_LEN -
+ sizeof (wl_ess_list_t);
+ conf = &aps->wl_ess_list_ess[aps->wl_ess_list_num];
+ if ((uint8_t *)conf > end)
+ return;
+
+ /* skip newly allocated NULL bss node */
+ if (IEEE80211_ADDR_EQ(in->in_macaddr, ic->ic_macaddr))
+ return;
+
+ conf->wl_ess_conf_essid.wl_essid_length = in->in_esslen;
+ bcopy(in->in_essid, conf->wl_ess_conf_essid.wl_essid_essid,
+ in->in_esslen);
+ bcopy(in->in_bssid, conf->wl_ess_conf_bssid, IEEE80211_ADDR_LEN);
+ conf->wl_ess_conf_wepenabled =
+ (in->in_capinfo & IEEE80211_CAPINFO_PRIVACY ?
+ WL_ENC_WEP : WL_NOENCRYPTION);
+ conf->wl_ess_conf_bsstype =
+ (in->in_capinfo & IEEE80211_CAPINFO_ESS ?
+ WL_BSS_BSS : WL_BSS_IBSS);
+ conf->wl_ess_conf_sl = wifi_getrssi(in);
+
+ /* physical (FH, DS, ERP) parameters */
+ if (IEEE80211_IS_CHAN_A(chan) || IEEE80211_IS_CHAN_T(chan)) {
+ wl_ofdm_t *ofdm =
+ (wl_ofdm_t *)&((conf->wl_phy_conf).wl_phy_ofdm_conf);
+ ofdm->wl_ofdm_subtype = WL_OFDM;
+ ofdm->wl_ofdm_frequency = chan->ich_freq;
+ } else {
+ switch (in->in_phytype) {
+ case IEEE80211_T_FH: {
+ wl_fhss_t *fhss = (wl_fhss_t *)
+ &((conf->wl_phy_conf).wl_phy_fhss_conf);
+
+ fhss->wl_fhss_subtype = WL_FHSS;
+ fhss->wl_fhss_channel = ieee80211_chan2ieee(ic, chan);
+ fhss->wl_fhss_dwelltime = in->in_fhdwell;
+ break;
+ }
+ case IEEE80211_T_DS: {
+ wl_dsss_t *dsss = (wl_dsss_t *)
+ &((conf->wl_phy_conf).wl_phy_dsss_conf);
+
+ dsss->wl_dsss_subtype = WL_DSSS;
+ dsss->wl_dsss_channel = ieee80211_chan2ieee(ic, chan);
+ dsss->wl_dsss_have_short_preamble = WIFI_HAVE_CAP(in,
+ IEEE80211_CAPINFO_SHORT_PREAMBLE);
+ dsss->wl_dsss_agility_enabled = WIFI_HAVE_CAP(in,
+ IEEE80211_CAPINFO_CHNL_AGILITY);
+ dsss->wl_dsss_have_pbcc = dsss->wl_dsss_pbcc_enable =
+ WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_PBCC);
+ break;
+ }
+ case IEEE80211_T_OFDM: {
+ wl_erp_t *erp = (wl_erp_t *)
+ &((conf->wl_phy_conf).wl_phy_erp_conf);
+
+ erp->wl_erp_subtype = WL_ERP;
+ erp->wl_erp_channel = ieee80211_chan2ieee(ic, chan);
+ erp->wl_erp_have_short_preamble = WIFI_HAVE_CAP(in,
+ IEEE80211_CAPINFO_SHORT_PREAMBLE);
+ erp->wl_erp_have_agility = erp->wl_erp_agility_enabled =
+ WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_CHNL_AGILITY);
+ erp->wl_erp_have_pbcc = erp->wl_erp_pbcc_enabled =
+ WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_PBCC);
+ erp->wl_erp_dsss_ofdm_enabled =
+ WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_DSSSOFDM);
+ erp->wl_erp_sst_enabled = WIFI_HAVE_CAP(in,
+ IEEE80211_CAPINFO_SHORT_SLOTTIME);
+ break;
+ } /* case IEEE80211_T_OFDM */
+ } /* switch in->in_phytype */
+ }
+
+ /* supported rates */
+ nrates = MIN(rates->ir_nrates, MAX_SCAN_SUPPORT_RATES);
+ /*
+ * The number of supported rates might exceed
+ * MAX_SCAN_SUPPORT_RATES. Fill in highest rates
+ * first so userland command could properly show
+ * maximum speed of AP
+ */
+ for (i = 0; i < nrates; i++) {
+ conf->wl_supported_rates[i] =
+ rates->ir_rates[rates->ir_nrates - i - 1];
+ }
+
+ aps->wl_ess_list_num++;
+}
+
+static int
+wifi_cfg_esslist(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *outp;
+ wl_ess_list_t *ow_aps;
+ int err = 0;
+
+ if ((omp = wifi_getoutmsg(*mp, cmd, MAX_BUF_LEN - WIFI_BUF_OFFSET)) ==
+ NULL) {
+ return (ENOMEM);
+ }
+ outp = (wldp_t *)omp->b_rptr;
+ ow_aps = (wl_ess_list_t *)outp->wldp_buf;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ ow_aps->wl_ess_list_num = 0;
+ ieee80211_iterate_nodes(&ic->ic_scan, wifi_read_ap, ow_aps);
+ outp->wldp_length = WIFI_BUF_OFFSET +
+ offsetof(wl_ess_list_t, wl_ess_list_ess) +
+ ow_aps->wl_ess_list_num * sizeof (wl_ess_conf_t);
+ omp->b_wptr = omp->b_rptr + outp->wldp_length;
+ break;
+ case WLAN_SET_PARAM:
+ outp->wldp_result = WL_READONLY;
+ err = EINVAL;
+ break;
+ default:
+ ieee80211_err("wifi_cfg_esslist: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+/*
+ * Scan the network for all available ESSs.
+ * IEEE80211_F_SCANONLY is set when current state is INIT. And
+ * with this flag, after scan the state will be changed back to
+ * INIT. The reason is at the end of SCAN stage, the STA will
+ * consequently connect to an AP. Then it looks unreasonable that
+ * for a disconnected device, A SCAN command causes it connected.
+ * So the state is changed back to INIT.
+ */
+static int
+wifi_cmd_scan(struct ieee80211com *ic, mblk_t *mp)
+{
+ int ostate = ic->ic_state;
+
+ /*
+ * Do not scan when current state is RUN. The reason is
+ * when connected, STA is on the same channel as AP. But
+ * to do scan, STA have to switch to each available channel,
+ * send probe request and wait certian time for probe
+ * response/beacon. Then when the STA switches to a channel
+ * different than AP's, as a result it cannot send/receive
+ * data packets to/from the connected WLAN. This eventually
+ * will cause data loss.
+ */
+ if (ostate == IEEE80211_S_RUN)
+ return (0);
+
+ IEEE80211_UNLOCK(ic);
+ ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+ IEEE80211_LOCK(ic);
+ if (ostate == IEEE80211_S_INIT)
+ ic->ic_flags |= IEEE80211_F_SCANONLY;
+
+ /* wait scan complete */
+ wifi_wait_scan(ic);
+
+ wifi_setupoutmsg(mp, 0);
+ return (0);
+}
+
+static void
+wifi_loaddefdata(struct ieee80211com *ic)
+{
+ struct ieee80211_node *in = ic->ic_bss;
+ int i;
+
+ ic->ic_des_esslen = 0;
+ bzero(ic->ic_des_essid, IEEE80211_NWID_LEN);
+ ic->ic_flags &= ~IEEE80211_F_DESBSSID;
+ bzero(ic->ic_des_bssid, IEEE80211_ADDR_LEN);
+ bzero(ic->ic_bss->in_bssid, IEEE80211_ADDR_LEN);
+ ic->ic_des_chan = IEEE80211_CHAN_ANYC;
+ ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
+ bzero(ic->ic_nickname, IEEE80211_NWID_LEN);
+ in->in_authmode = IEEE80211_AUTH_OPEN;
+ ic->ic_flags &= ~IEEE80211_F_PRIVACY;
+ ic->ic_def_txkey = 0;
+ for (i = 0; i < MAX_NWEPKEYS; i++) {
+ ic->ic_nw_keys[i].wk_keylen = 0;
+ bzero(ic->ic_nw_keys[i].wk_key, IEEE80211_KEYBUF_SIZE);
+ }
+ ic->ic_curmode = IEEE80211_MODE_AUTO;
+}
+
+static int
+wifi_cmd_loaddefaults(struct ieee80211com *ic, mblk_t *mp)
+{
+ wifi_loaddefdata(ic);
+ wifi_setupoutmsg(mp, 0);
+ return (ENETRESET);
+}
+
+static int
+wifi_cmd_disassoc(struct ieee80211com *ic, mblk_t *mp)
+{
+ if (ic->ic_state != IEEE80211_S_INIT) {
+ IEEE80211_UNLOCK(ic);
+ (void) ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+ IEEE80211_LOCK(ic);
+ }
+ wifi_loaddefdata(ic);
+ wifi_setupoutmsg(mp, 0);
+ return (0);
+}
+
+static int
+wifi_cfg_getset(struct ieee80211com *ic, mblk_t **mp, uint32_t cmd)
+{
+ mblk_t *mp1 = *mp;
+ wldp_t *wp = (wldp_t *)mp1->b_rptr;
+ int err = 0;
+
+ ASSERT(ic != NULL && mp1 != NULL);
+ IEEE80211_LOCK_ASSERT(ic);
+ if (MBLKL(mp1) < WIFI_BUF_OFFSET) {
+ ieee80211_err("wifi_cfg_getset: "
+ "invalid input buffer, size=%d\n", MBLKL(mp1));
+ return (EINVAL);
+ }
+
+ switch (wp->wldp_id) {
+ /* Commands */
+ case WL_SCAN:
+ err = wifi_cmd_scan(ic, mp1);
+ break;
+ case WL_LOAD_DEFAULTS:
+ err = wifi_cmd_loaddefaults(ic, mp1);
+ break;
+ case WL_DISASSOCIATE:
+ err = wifi_cmd_disassoc(ic, mp1);
+ break;
+ /* Parameters */
+ case WL_ESSID:
+ err = wifi_cfg_essid(ic, cmd, mp);
+ break;
+ case WL_BSSID:
+ err = wifi_cfg_bssid(ic, cmd, mp);
+ break;
+ case WL_NODE_NAME:
+ err = wifi_cfg_nodename(ic, cmd, mp);
+ break;
+ case WL_PHY_CONFIG:
+ err = wifi_cfg_phy(ic, cmd, mp);
+ break;
+ case WL_WEP_KEY_TAB:
+ err = wifi_cfg_wepkey(ic, cmd, mp);
+ break;
+ case WL_WEP_KEY_ID:
+ err = wifi_cfg_keyid(ic, cmd, mp);
+ break;
+ case WL_AUTH_MODE:
+ err = wifi_cfg_authmode(ic, cmd, mp);
+ break;
+ case WL_ENCRYPTION:
+ err = wifi_cfg_encrypt(ic, cmd, mp);
+ break;
+ case WL_BSS_TYPE:
+ err = wifi_cfg_bsstype(ic, cmd, mp);
+ break;
+ case WL_DESIRED_RATES:
+ err = wifi_cfg_desrates(ic, cmd, mp);
+ break;
+ case WL_LINKSTATUS:
+ err = wifi_cfg_linkstatus(ic, cmd, mp);
+ break;
+ case WL_ESS_LIST:
+ err = wifi_cfg_esslist(ic, cmd, mp);
+ break;
+ case WL_SUPPORTED_RATES:
+ err = wifi_cfg_suprates(ic, cmd, mp);
+ break;
+ case WL_RSSI:
+ err = wifi_cfg_rssi(ic, cmd, mp);
+ break;
+ default:
+ wifi_setupoutmsg(mp1, 0);
+ wp->wldp_result = WL_LACK_FEATURE;
+ err = ENOTSUP;
+ break;
+ }
+
+ return (err);
+}
+
+/*
+ * Typically invoked by drivers in response to requests for
+ * information or to change settings from the userland.
+ *
+ * Return value should be checked by WiFi drivers. Return 0
+ * on success. Otherwise, return non-zero value to indicate
+ * the error. Driver should operate as below when the return
+ * error is:
+ * ENETRESET Reset wireless network and re-start to join a
+ * WLAN. ENETRESET is returned when a configuration
+ * parameter has been changed.
+ * When acknowledge a M_IOCTL message, thie error
+ * is ignored.
+ */
+int
+ieee80211_ioctl(struct ieee80211com *ic, queue_t *wq, mblk_t *mp)
+{
+ struct iocblk *iocp;
+ int32_t cmd, err, len;
+ boolean_t need_privilege;
+ mblk_t *mp1;
+
+ if (MBLKL(mp) < sizeof (struct iocblk)) {
+ ieee80211_err("ieee80211_ioctl: ioctl buffer too short, %u\n",
+ MBLKL(mp));
+ miocnak(wq, mp, 0, EINVAL);
+ return (EINVAL);
+ }
+
+ /*
+ * Validate the command
+ */
+ iocp = (struct iocblk *)mp->b_rptr;
+ iocp->ioc_error = 0;
+ cmd = iocp->ioc_cmd;
+ need_privilege = B_TRUE;
+ switch (cmd) {
+ case WLAN_SET_PARAM:
+ case WLAN_COMMAND:
+ break;
+ case WLAN_GET_PARAM:
+ need_privilege = B_FALSE;
+ break;
+ default:
+ ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_ioctl(): "
+ "unknown cmd 0x%x\n", cmd);
+ miocnak(wq, mp, 0, EINVAL);
+ return (EINVAL);
+ }
+
+ if (need_privilege) {
+ /*
+ * Check for specific net_config privilege on Solaris 10+.
+ */
+ err = secpolicy_net_config(iocp->ioc_cr, B_FALSE);
+ if (err != 0) {
+ miocnak(wq, mp, 0, err);
+ return (err);
+ }
+ }
+
+ IEEE80211_LOCK(ic);
+
+ /* sanity check */
+ mp1 = mp->b_cont;
+ if (iocp->ioc_count == 0 || iocp->ioc_count < sizeof (wldp_t) ||
+ mp1 == NULL) {
+ miocnak(wq, mp, 0, EINVAL);
+ IEEE80211_UNLOCK(ic);
+ return (EINVAL);
+ }
+
+ /* assuming single data block */
+ if (mp1->b_cont != NULL) {
+ freemsg(mp1->b_cont);
+ mp1->b_cont = NULL;
+ }
+
+ err = wifi_cfg_getset(ic, &mp1, cmd);
+ mp->b_cont = mp1;
+ IEEE80211_UNLOCK(ic);
+
+ len = msgdsize(mp1);
+ /* ignore ENETRESET when acknowledge the M_IOCTL message */
+ if (err == 0 || err == ENETRESET)
+ miocack(wq, mp, len, 0);
+ else
+ miocack(wq, mp, len, err);
+
+ return (err);
+}