xref: /onnv-gate/usr/src/uts/common/io/pcwl/pcwl.c (revision 6062:f12fdb80179b)
13737Shx147065 /*
2*6062Shx147065  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
33737Shx147065  * Use is subject to license terms.
43737Shx147065  */
53737Shx147065 
63737Shx147065 /*
73737Shx147065  * Copyright (c) 1997, 1998, 1999
83737Shx147065  *      Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
93737Shx147065  *
103737Shx147065  * Redistribution and use in source and binary forms, with or without
113737Shx147065  * modification, are permitted provided that the following conditions
123737Shx147065  * are met:
133737Shx147065  * 1. Redistributions of source code must retain the above copyright
143737Shx147065  *    notice, this list of conditions and the following disclaimer.
153737Shx147065  * 2. Redistributions in binary form must reproduce the above copyright
163737Shx147065  *    notice, this list of conditions and the following disclaimer in the
173737Shx147065  *    documentation and/or other materials provided with the distribution.
183737Shx147065  * 3. All advertising materials mentioning features or use of this software
193737Shx147065  *    must display the following acknowledgement:
203737Shx147065  *      This product includes software developed by Bill Paul.
213737Shx147065  * 4. Neither the name of the author nor the names of any co-contributors
223737Shx147065  *    may be used to endorse or promote products derived from this software
233737Shx147065  *    without specific prior written permission.
243737Shx147065  *
253737Shx147065  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
263737Shx147065  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
273737Shx147065  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
283737Shx147065  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
293737Shx147065  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
303737Shx147065  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
313737Shx147065  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
323737Shx147065  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
333737Shx147065  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
343737Shx147065  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
353737Shx147065  * THE POSSIBILITY OF SUCH DAMAGE.
363737Shx147065  */
373737Shx147065 
383737Shx147065 #pragma ident	"%Z%%M%	%I%	%E% SMI"
393737Shx147065 
403737Shx147065 #include <sys/conf.h>
413737Shx147065 #include <sys/ddi.h>
423737Shx147065 #include <sys/sunddi.h>
433737Shx147065 #include <sys/dlpi.h>
443737Shx147065 #include <sys/ethernet.h>
453737Shx147065 #include <sys/strsun.h>
463737Shx147065 #include <sys/stat.h>
473737Shx147065 #include <sys/byteorder.h>
483737Shx147065 #include <sys/pccard.h>
493737Shx147065 #include <sys/pci.h>
503737Shx147065 #include <sys/policy.h>
513737Shx147065 #include <sys/mac.h>
523737Shx147065 #include <sys/stream.h>
533737Shx147065 #include <inet/common.h>
543737Shx147065 #include <inet/nd.h>
553737Shx147065 #include <inet/mi.h>
563737Shx147065 
573737Shx147065 #include "pcwl.h"
583737Shx147065 #include <sys/mac_wifi.h>
593737Shx147065 #include <inet/wifi_ioctl.h>
603737Shx147065 
613737Shx147065 #ifdef DEBUG
623737Shx147065 #define	PCWL_DBG_BASIC		0x1
633737Shx147065 #define	PCWL_DBG_INFO		0x2
643737Shx147065 #define	PCWL_DBG_SEND		0x4
653737Shx147065 #define	PCWL_DBG_RCV		0x8
663737Shx147065 #define	PCWL_DBG_LINKINFO	0x10
673737Shx147065 uint32_t pcwl_debug = 0;
683737Shx147065 #define	PCWLDBG(x) \
693737Shx147065 	if (pcwl_debug & PCWL_DBG_BASIC) cmn_err x
703737Shx147065 #else
713737Shx147065 #define	PCWLDBG(x)
723737Shx147065 #endif
733737Shx147065 
743737Shx147065 /* for pci card */
753737Shx147065 static ddi_device_acc_attr_t accattr = {
763737Shx147065 		DDI_DEVICE_ATTR_V0,
773737Shx147065 		DDI_NEVERSWAP_ACC,
783737Shx147065 		DDI_STRICTORDER_ACC,
793737Shx147065 		DDI_DEFAULT_ACC
803737Shx147065 };
813737Shx147065 
823737Shx147065 void *pcwl_soft_state_p = NULL;
833737Shx147065 static int pcwl_device_type;
843737Shx147065 
853737Shx147065 mac_callbacks_t pcwl_m_callbacks = {
863737Shx147065 	MC_IOCTL,
873737Shx147065 	pcwl_gstat,
883737Shx147065 	pcwl_start,
893737Shx147065 	pcwl_stop,
903737Shx147065 	pcwl_prom,
913737Shx147065 	pcwl_sdmulti,
923737Shx147065 	pcwl_saddr,
933737Shx147065 	pcwl_tx,
943737Shx147065 	NULL,
953737Shx147065 	pcwl_ioctl
963737Shx147065 };
973737Shx147065 
983737Shx147065 static char *pcwl_name_str = "pcwl";
993737Shx147065 
1003737Shx147065 DDI_DEFINE_STREAM_OPS(pcwl_dev_ops, nulldev, pcwl_probe, pcwl_attach,
1013737Shx147065     pcwl_detach, nodev, NULL, D_MP, NULL);
1023737Shx147065 
1033737Shx147065 extern struct mod_ops mod_driverops;
1043737Shx147065 static struct modldrv modldrv = {
1053737Shx147065 	&mod_driverops,
1063737Shx147065 	"Lucent/PRISM-II 802.11b driver",
1073737Shx147065 	&pcwl_dev_ops
1083737Shx147065 };
1093737Shx147065 
1103737Shx147065 static struct modlinkage modlinkage = {
1113737Shx147065 	MODREV_1, (void *)&modldrv, NULL
1123737Shx147065 	};
1133737Shx147065 
1143737Shx147065 int
1153737Shx147065 _init(void)
1163737Shx147065 {
1173737Shx147065 	int stat;
1183737Shx147065 
1193737Shx147065 	/* Allocate soft state */
1203737Shx147065 	if ((stat = ddi_soft_state_init(&pcwl_soft_state_p,
1213737Shx147065 	    sizeof (pcwl_maci_t), N_PCWL)) != DDI_SUCCESS)
1223737Shx147065 		return (stat);
1233737Shx147065 
1243737Shx147065 	mac_init_ops(&pcwl_dev_ops, "pcwl");
1253737Shx147065 	wl_frame_default.wl_dat[0] = htons(WL_SNAP_WORD0);
1263737Shx147065 	wl_frame_default.wl_dat[1] = htons(WL_SNAP_WORD1);
1273737Shx147065 	stat = mod_install(&modlinkage);
1283737Shx147065 	if (stat != DDI_SUCCESS) {
1293737Shx147065 		mac_fini_ops(&pcwl_dev_ops);
1303737Shx147065 		ddi_soft_state_fini(&pcwl_soft_state_p);
1313737Shx147065 	}
1323737Shx147065 	return (stat);
1333737Shx147065 }
1343737Shx147065 
1353737Shx147065 int
1363737Shx147065 _fini(void)
1373737Shx147065 {
1383737Shx147065 	int stat;
1393737Shx147065 
1403737Shx147065 	if ((stat = mod_remove(&modlinkage)) != DDI_SUCCESS)
1413737Shx147065 		return (stat);
1423737Shx147065 	mac_fini_ops(&pcwl_dev_ops);
1433737Shx147065 	ddi_soft_state_fini(&pcwl_soft_state_p);
1443737Shx147065 
1453737Shx147065 	return (stat);
1463737Shx147065 }
1473737Shx147065 
1483737Shx147065 int
1493737Shx147065 _info(struct modinfo *modinfop)
1503737Shx147065 {
1513737Shx147065 	return (mod_info(&modlinkage, modinfop));
1523737Shx147065 }
1533737Shx147065 
1543737Shx147065 static int
1553737Shx147065 pcwl_probe(dev_info_t *dip)
1563737Shx147065 {
1573737Shx147065 	int len, ret;
1583737Shx147065 	char *buf;
1593737Shx147065 	dev_info_t *pdip = ddi_get_parent(dip);
1603737Shx147065 
1613737Shx147065 	ret = ddi_getlongprop(DDI_DEV_T_ANY, pdip, 0, "device_type",
1623737Shx147065 	    (caddr_t)&buf, &len);
1633737Shx147065 	if (ret != DDI_SUCCESS)
1643737Shx147065 		return (DDI_PROBE_FAILURE);
1653737Shx147065 
1663737Shx147065 	PCWLDBG((CE_NOTE, "pcwl probe: device_type %s\n", buf));
1673737Shx147065 	if ((strcmp(buf, "pccard") == 0) || (strcmp(buf, "pcmcia") == 0)) {
1683737Shx147065 		pcwl_device_type = PCWL_DEVICE_PCCARD;
1693737Shx147065 		ret = DDI_PROBE_SUCCESS;
1703737Shx147065 	} else if (strcmp(buf, "pci") == 0) {
1713737Shx147065 		pcwl_device_type = PCWL_DEVICE_PCI;
1723737Shx147065 		ret = DDI_PROBE_SUCCESS;
1733737Shx147065 	} else {
1743737Shx147065 		ret = DDI_PROBE_FAILURE;
1753737Shx147065 	}
1763737Shx147065 	kmem_free(buf, len);
1773737Shx147065 	return (ret);
1783737Shx147065 }
1793737Shx147065 
1803737Shx147065 static int
1813737Shx147065 pcwl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1823737Shx147065 {
1833737Shx147065 	int ret, i;
1843737Shx147065 	int instance;
1853737Shx147065 	uint16_t stat;
1863737Shx147065 	uint32_t err;
1873737Shx147065 	pcwl_maci_t *pcwl_p;
1883737Shx147065 	wifi_data_t	wd = { 0 };
1893737Shx147065 	mac_register_t	*macp;
1903737Shx147065 	modify_config_t cfgmod;
1913737Shx147065 	char strbuf[256];
1923737Shx147065 
1933737Shx147065 	PCWLDBG((CE_NOTE, "pcwl attach: dip=0x%p cmd=%x\n", (void *)dip, cmd));
1943737Shx147065 	if (cmd != DDI_ATTACH)
1953737Shx147065 		goto attach_fail1;
1963737Shx147065 	/*
1973737Shx147065 	 * Allocate soft state associated with this instance.
1983737Shx147065 	 */
1993737Shx147065 	if (ddi_soft_state_zalloc(pcwl_soft_state_p,
2003737Shx147065 	    ddi_get_instance(dip)) != DDI_SUCCESS) {
2013737Shx147065 		cmn_err(CE_CONT, "pcwl attach: alloc softstate failed\n");
2023737Shx147065 		goto attach_fail1;
2033737Shx147065 	}
2043737Shx147065 	pcwl_p = (pcwl_maci_t *)ddi_get_soft_state(pcwl_soft_state_p,
2053737Shx147065 	    ddi_get_instance(dip));
2063737Shx147065 	pcwl_p->pcwl_device_type = pcwl_device_type;
2073737Shx147065 	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) {
2083737Shx147065 		if (ddi_regs_map_setup(dip, 0,
2093737Shx147065 		    (caddr_t *)&pcwl_p->pcwl_cfg_base, 0, 0,
2103737Shx147065 		    &accattr, &pcwl_p->pcwl_cfg_handle) != DDI_SUCCESS) {
2113737Shx147065 			cmn_err(CE_WARN, "pcwl(pci) attach: pci_regs_map_setup"
2123737Shx147065 			    " failed\n");
2133737Shx147065 			goto attach_fail2;
2143737Shx147065 		}
2153737Shx147065 
2163737Shx147065 		stat = ddi_get16(pcwl_p->pcwl_cfg_handle,
2173737Shx147065 		    (uint16_t *)(pcwl_p->pcwl_cfg_base + PCI_CONF_COMM));
2183737Shx147065 		stat |= (PCI_COMM_IO | PCI_COMM_MAE);
2193737Shx147065 		ddi_put16(pcwl_p->pcwl_cfg_handle,
2203737Shx147065 		    (uint16_t *)(pcwl_p->pcwl_cfg_base + PCI_CONF_COMM), stat);
2213737Shx147065 		stat = ddi_get16(pcwl_p->pcwl_cfg_handle,
2223737Shx147065 		    (uint16_t *)(pcwl_p->pcwl_cfg_base + PCI_CONF_COMM));
2233737Shx147065 		if ((stat & (PCI_COMM_IO | PCI_COMM_MAE)) !=
2243737Shx147065 		    (PCI_COMM_IO | PCI_COMM_MAE)) {
2253737Shx147065 			cmn_err(CE_WARN, "pcwl(pci) attach: pci command"
2263737Shx147065 			    " reg enable failed\n");
2273737Shx147065 			goto attach_fail2a;
2283737Shx147065 		}
2293737Shx147065 
2303737Shx147065 
2313737Shx147065 		if (ddi_regs_map_setup(dip, 1, (caddr_t *)&pcwl_p->pcwl_bar,
2323737Shx147065 		    0, 0, &accattr, (ddi_acc_handle_t *)&pcwl_p->pcwl_handle)
2333737Shx147065 		    != DDI_SUCCESS) {
2343737Shx147065 			cmn_err(CE_WARN, "pcwl(pci) attach: pci_regs_map_setup"
2353737Shx147065 			    " failed\n");
2363737Shx147065 			goto attach_fail2a;
2373737Shx147065 		}
2383737Shx147065 		PCWLDBG((CE_NOTE, "pcwl(pci): regs_map_setup,bar=%p\n",
2393737Shx147065 		    (void *)pcwl_p->pcwl_bar));
2403737Shx147065 
2413737Shx147065 		/*
2423737Shx147065 		 * tricky! copy from freebsd code.
2433737Shx147065 		 */
2443737Shx147065 		PCWL_WRITE(pcwl_p, 0x26, 0x80);
2453737Shx147065 		drv_usecwait(500000);
2463737Shx147065 		PCWL_WRITE(pcwl_p, 0x26, 0x0);
2473737Shx147065 		drv_usecwait(500000);
2483737Shx147065 
2493737Shx147065 		for (i = 0; i < WL_TIMEOUT; i++) {
2503737Shx147065 			PCWL_READ(pcwl_p, 0x0, stat);
2513737Shx147065 			if (stat & WL_CMD_BUSY)
2523737Shx147065 				drv_usecwait(10);
2533737Shx147065 			else
2543737Shx147065 				break;
2553737Shx147065 		}
2563737Shx147065 		if (i == WL_TIMEOUT) {
2573737Shx147065 			cmn_err(CE_WARN, "pcwl(pci) attach: hardware init"
2583737Shx147065 			    " failed\n");
2593737Shx147065 			goto attach_fail3;
2603737Shx147065 		}
2613737Shx147065 
2623737Shx147065 		/*
2633737Shx147065 		 * magic number verification.
2643737Shx147065 		 * tricky! copy from freebsd code.
2653737Shx147065 		 */
2663737Shx147065 		PCWL_WRITE(pcwl_p, 0x28, 0x4a2d);
2673737Shx147065 		PCWL_READ(pcwl_p, 0x28, stat);
2683737Shx147065 		PCWLDBG((CE_NOTE, "pcwl(pci):magic number = %x\n", stat));
2693737Shx147065 		if (stat != 0x4a2d) {
2703737Shx147065 			cmn_err(CE_WARN, "pcwl(pci) attach: magic verify"
2713737Shx147065 			    " failed\n");
2723737Shx147065 			goto attach_fail3;
2733737Shx147065 		}
2743737Shx147065 	}
2753737Shx147065 	pcwl_p->pcwl_dip	= dip;
2763737Shx147065 	pcwl_p->pcwl_flag	= 0;
2773737Shx147065 	pcwl_p->pcwl_socket	= ddi_getprop(DDI_DEV_T_NONE, dip,
2783737Shx147065 	    DDI_PROP_DONTPASS, "socket", -1);
2793737Shx147065 	pcwl_p->pcwl_reschedule_need = B_FALSE;
2803737Shx147065 
2813737Shx147065 	if (ddi_get_iblock_cookie(dip,
2823737Shx147065 	    0, &pcwl_p->pcwl_ib_cookie) != DDI_SUCCESS) {
2833737Shx147065 		cmn_err(CE_WARN, "pcwl attach: get_iblk_cookie failed\n");
2843737Shx147065 		goto attach_fail3;
2853737Shx147065 	}
2863737Shx147065 
2873737Shx147065 	mutex_init(&pcwl_p->pcwl_glock, NULL, MUTEX_DRIVER,
2883737Shx147065 	    pcwl_p->pcwl_ib_cookie);
2893737Shx147065 	mutex_init(&pcwl_p->pcwl_scanlist_lock, NULL, MUTEX_DRIVER,
2903737Shx147065 	    pcwl_p->pcwl_ib_cookie);
2913737Shx147065 	mutex_init(&pcwl_p->pcwl_txring.wl_tx_lock, NULL, MUTEX_DRIVER,
2923737Shx147065 	    pcwl_p->pcwl_ib_cookie);
2933737Shx147065 
2943737Shx147065 	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) {
2953737Shx147065 		if (ret = ddi_add_intr(dip, 0, NULL, NULL,
2963737Shx147065 		    pcwl_intr, (caddr_t)pcwl_p)) {
2973737Shx147065 			cmn_err(CE_NOTE, "pcwl(pci) attach: add intr failed\n");
2983737Shx147065 			goto attach_fail3a;
2993737Shx147065 		}
3003737Shx147065 	} else if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCCARD) {
3013737Shx147065 		if (ret = pcwl_register_cs(dip, pcwl_p)) {
3023737Shx147065 			cmn_err(CE_WARN, "pcwl attach(pccard): "
3033737Shx147065 			    "register_cs err %x\n", ret);
3043737Shx147065 			goto attach_fail3a;
3053737Shx147065 		}
3063737Shx147065 	} else {
3073737Shx147065 		cmn_err(CE_WARN, "pcwl attach: unsupported device type\n");
3083737Shx147065 		goto attach_fail3a;
3093737Shx147065 	}
3103737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
3113737Shx147065 	if (ret = pcwl_reset_backend(pcwl_p)) {
3123737Shx147065 		cmn_err(CE_WARN, "pcwl attach: reset_backend failed %x\n", ret);
3133737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
3143737Shx147065 		goto attach_fail4;
3153737Shx147065 	}
3163737Shx147065 	if (ret = pcwl_get_cap(pcwl_p)) { /* sets macaddr for mac_register */
3173737Shx147065 		cmn_err(CE_WARN, "pcwl attach: get_cap failed %x\n", ret);
3183737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
3193737Shx147065 		goto attach_fail4;
3203737Shx147065 	}
3213737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
3223737Shx147065 	/*
3233737Shx147065 	 * Provide initial settings for the WiFi plugin; whenever this
3243737Shx147065 	 * information changes, we need to call mac_pdata_update()
3253737Shx147065 	 */
3263737Shx147065 	wd.wd_secalloc = WIFI_SEC_NONE;
3273737Shx147065 	wd.wd_opmode = IEEE80211_M_STA;
3283737Shx147065 
3293737Shx147065 	macp = mac_alloc(MAC_VERSION);
3303737Shx147065 	if (macp == NULL) {
3313737Shx147065 		PCWLDBG((CE_NOTE, "pcwl attach: "
3323737Shx147065 		    "MAC version mismatch\n"));
3333737Shx147065 		goto attach_fail4;
3343737Shx147065 	}
3353737Shx147065 
3363737Shx147065 	macp->m_type_ident	= MAC_PLUGIN_IDENT_WIFI;
3373737Shx147065 	macp->m_driver		= pcwl_p;
3383737Shx147065 	macp->m_dip		= dip;
3393737Shx147065 	macp->m_src_addr	= pcwl_p->pcwl_mac_addr;
3403737Shx147065 	macp->m_callbacks	= &pcwl_m_callbacks;
3413737Shx147065 	macp->m_min_sdu		= 0;
3423737Shx147065 	macp->m_max_sdu		= IEEE80211_MTU;
3433737Shx147065 	macp->m_pdata		= &wd;
3443737Shx147065 	macp->m_pdata_size	= sizeof (wd);
3453737Shx147065 
3463737Shx147065 	err = mac_register(macp, &pcwl_p->pcwl_mh);
3473737Shx147065 	mac_free(macp);
3483737Shx147065 	if (err != 0) {
3493737Shx147065 		PCWLDBG((CE_NOTE, "pcwl attach: "
3503737Shx147065 		    "mac_register err\n"));
3513737Shx147065 		goto attach_fail4;
3523737Shx147065 	}
3533737Shx147065 
3543737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
3553737Shx147065 	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCCARD) {
3563737Shx147065 		/*
3573737Shx147065 		 * turn on CS interrupt
3583737Shx147065 		 */
3593737Shx147065 		cfgmod.Attributes = CONF_ENABLE_IRQ_STEERING |
3603737Shx147065 		    CONF_IRQ_CHANGE_VALID;
3613737Shx147065 		cfgmod.Vpp1 = 0;
3623737Shx147065 		cfgmod.Vpp2 = 0;
3633737Shx147065 		(void) csx_ModifyConfiguration(pcwl_p->pcwl_chdl, &cfgmod);
3643737Shx147065 
3653737Shx147065 	}
3663737Shx147065 	if (ret = pcwl_init_nicmem(pcwl_p)) {
3673737Shx147065 		cmn_err(CE_WARN, "pcwl(pccard) attach: pcwl_init_nicmem"
3683737Shx147065 		    " failed %x\n", ret);
3693737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
3703737Shx147065 		goto attach_fail5;
3713737Shx147065 	}
3723737Shx147065 	pcwl_chip_type(pcwl_p);
3733737Shx147065 	if (ret = pcwl_loaddef_rf(pcwl_p)) {
3743737Shx147065 		cmn_err(CE_WARN, "pcwl attach: config_rf failed%x\n", ret);
3753737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
3763737Shx147065 		goto attach_fail5;
3773737Shx147065 	}
3783737Shx147065 	(void) pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0);
3793737Shx147065 	pcwl_stop_locked(pcwl_p);	/* leaves interface down */
3803737Shx147065 	list_create(&pcwl_p->pcwl_scan_list, sizeof (wl_scan_list_t),
3813737Shx147065 	    offsetof(wl_scan_list_t, wl_scan_node));
3823737Shx147065 	pcwl_p->pcwl_scan_num = 0;
3833737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
3843737Shx147065 	pcwl_p->pcwl_scanlist_timeout_id = timeout(pcwl_scanlist_timeout,
3853737Shx147065 	    pcwl_p, drv_usectohz(1000000));
3863737Shx147065 	instance = ddi_get_instance(dip);
3873737Shx147065 	(void) snprintf(strbuf, sizeof (strbuf), "pcwl%d", instance);
3883737Shx147065 	if (ddi_create_minor_node(dip, strbuf, S_IFCHR,
3893737Shx147065 	    instance + 1, DDI_NT_NET_WIFI, 0) != DDI_SUCCESS) {
3903737Shx147065 		goto attach_fail6;
3913737Shx147065 	}
3923737Shx147065 	pcwl_p->pcwl_flag |= PCWL_ATTACHED;
3933737Shx147065 	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) {
3943737Shx147065 		pcwl_p->pcwl_flag |= PCWL_CARD_READY;
3953737Shx147065 	}
3963737Shx147065 	return (DDI_SUCCESS);
3973737Shx147065 attach_fail6:
3983737Shx147065 	if (pcwl_p->pcwl_scanlist_timeout_id != 0) {
3993737Shx147065 		(void) untimeout(pcwl_p->pcwl_scanlist_timeout_id);
4003737Shx147065 		pcwl_p->pcwl_scanlist_timeout_id = 0;
4013737Shx147065 	}
4023737Shx147065 	list_destroy(&pcwl_p->pcwl_scan_list);
4033737Shx147065 attach_fail5:
4043737Shx147065 	(void) mac_unregister(pcwl_p->pcwl_mh);
4053737Shx147065 attach_fail4:
4063737Shx147065 	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) {
4073737Shx147065 		ddi_remove_intr(dip, 0, pcwl_p->pcwl_ib_cookie);
4083737Shx147065 	} else if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCCARD) {
4093737Shx147065 		pcwl_unregister_cs(pcwl_p);
4103737Shx147065 	}
4113737Shx147065 attach_fail3a:
4123737Shx147065 	pcwl_destroy_locks(pcwl_p);
4133737Shx147065 attach_fail3:
4143737Shx147065 	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI)
4153737Shx147065 		ddi_regs_map_free(&pcwl_p->pcwl_handle);
4163737Shx147065 attach_fail2a:
4173737Shx147065 	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI)
4183737Shx147065 		ddi_regs_map_free(&pcwl_p->pcwl_cfg_handle);
4193737Shx147065 attach_fail2:
4203737Shx147065 	ddi_soft_state_free(pcwl_soft_state_p, ddi_get_instance(dip));
4213737Shx147065 attach_fail1:
4223737Shx147065 	return (DDI_FAILURE);
4233737Shx147065 }
4243737Shx147065 
4253737Shx147065 static int
4263737Shx147065 pcwl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4273737Shx147065 {
4283737Shx147065 	pcwl_maci_t *pcwl_p;
4293737Shx147065 	wl_scan_list_t *scan_item0;
4303737Shx147065 	int ret;
4313737Shx147065 	pcwl_p = ddi_get_soft_state(pcwl_soft_state_p, ddi_get_instance(dip));
4323737Shx147065 
4333737Shx147065 	PCWLDBG((CE_NOTE, "pcwl detach: dip=0x%p cmd=%x\n", (void *)dip, cmd));
4343737Shx147065 	if (cmd != DDI_DETACH)
4353737Shx147065 		return (DDI_FAILURE);
4363737Shx147065 	if (!(pcwl_p->pcwl_flag & PCWL_ATTACHED))
4373737Shx147065 		return (DDI_FAILURE);
4383737Shx147065 
4393737Shx147065 	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) {
4403737Shx147065 		mutex_enter(&pcwl_p->pcwl_glock);
4413737Shx147065 		(void) pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0);
4423737Shx147065 		PCWL_DISABLE_INTR(pcwl_p);
4433737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
4443737Shx147065 	}
4453737Shx147065 	if (pcwl_p->pcwl_scanlist_timeout_id != 0) {
4463737Shx147065 		(void) untimeout(pcwl_p->pcwl_scanlist_timeout_id);
4473737Shx147065 		pcwl_p->pcwl_scanlist_timeout_id = 0;
4483737Shx147065 	}
4493737Shx147065 	if (pcwl_p->pcwl_connect_timeout_id != 0) {
4503737Shx147065 		(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
4513737Shx147065 		pcwl_p->pcwl_connect_timeout_id = 0;
4523737Shx147065 	}
4533737Shx147065 	mutex_enter(&pcwl_p->pcwl_scanlist_lock);
4543737Shx147065 	scan_item0 = list_head(&pcwl_p->pcwl_scan_list);
4553737Shx147065 	while (scan_item0) {
4563737Shx147065 		pcwl_delete_scan_item(pcwl_p, scan_item0);
4573737Shx147065 		scan_item0 = list_head(&pcwl_p->pcwl_scan_list);
4583737Shx147065 	}
4593737Shx147065 	list_destroy(&pcwl_p->pcwl_scan_list);
4603737Shx147065 	mutex_exit(&pcwl_p->pcwl_scanlist_lock);
4613737Shx147065 	ret = mac_unregister(pcwl_p->pcwl_mh);
4623737Shx147065 	if (ret != 0)
4633737Shx147065 		return (DDI_FAILURE);
4643737Shx147065 
4653737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
4663737Shx147065 	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) {
4673737Shx147065 		ddi_remove_intr(dip, 0, pcwl_p->pcwl_ib_cookie);
4683737Shx147065 		ddi_regs_map_free(&pcwl_p->pcwl_handle);
4693737Shx147065 		ddi_regs_map_free(&pcwl_p->pcwl_cfg_handle);
4703737Shx147065 	} else if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCCARD) {
4713737Shx147065 		pcwl_unregister_cs(pcwl_p);
4723737Shx147065 	}
4733737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
4743737Shx147065 	pcwl_destroy_locks(pcwl_p);
4753737Shx147065 	ddi_remove_minor_node(dip, NULL);
4763737Shx147065 	ddi_soft_state_free(pcwl_soft_state_p, ddi_get_instance(dip));
4773737Shx147065 	return (DDI_SUCCESS);
4783737Shx147065 }
4793737Shx147065 
4803737Shx147065 /*
4813737Shx147065  * card services and event handlers
4823737Shx147065  */
4833737Shx147065 static int
4843737Shx147065 pcwl_register_cs(dev_info_t *dip, pcwl_maci_t *pcwl_p)
4853737Shx147065 {
4863737Shx147065 	int ret;
4873737Shx147065 	client_reg_t cr;
4883737Shx147065 	client_handle_t chdl; /* uint encoding of socket, function, client */
4893737Shx147065 	get_status_t card_status;
4903737Shx147065 	request_socket_mask_t sock_req;
4913737Shx147065 
4923737Shx147065 	bzero(&cr, sizeof (cr));
4933737Shx147065 	cr.Attributes	= INFO_IO_CLIENT|INFO_CARD_EXCL|INFO_CARD_SHARE;
4943737Shx147065 	cr.EventMask	= CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
4953737Shx147065 	    CS_EVENT_REGISTRATION_COMPLETE | CS_EVENT_CARD_REMOVAL_LOWP |
4963737Shx147065 	    CS_EVENT_CARD_READY | CS_EVENT_PM_RESUME |
4973737Shx147065 	    CS_EVENT_PM_SUSPEND | CS_EVENT_CLIENT_INFO;
4983737Shx147065 	cr.event_callback_args.client_data = pcwl_p;
4993737Shx147065 	cr.Version = CS_VERSION;
5003737Shx147065 	cr.event_handler = (csfunction_t *)pcwl_ev_hdlr;
5013737Shx147065 	cr.dip = dip;
5023737Shx147065 	(void) strcpy(cr.driver_name, pcwl_name_str);
5033737Shx147065 	if (ret = csx_RegisterClient(&chdl, &cr)) {
5043737Shx147065 		cmn_err(CE_WARN, "pcwl: RegisterClient failed %x\n", ret);
5053737Shx147065 		goto regcs_ret;
5063737Shx147065 	}
5073737Shx147065 	pcwl_p->pcwl_chdl = chdl;
5083737Shx147065 
5093737Shx147065 	bzero(&card_status, sizeof (card_status));
5103737Shx147065 	(void) csx_GetStatus(chdl, &card_status);
5113737Shx147065 	PCWLDBG((CE_NOTE,
5123737Shx147065 	    "pcwl: register_cs: Sock=%x CState=%x SState=%x rState=%x\n",
5133737Shx147065 	    card_status.Socket, card_status.CardState,
5143737Shx147065 	    card_status.SocketState, card_status.raw_CardState));
5153737Shx147065 	if (!(card_status.CardState & CS_STATUS_CARD_INSERTED)) {
5163737Shx147065 		/* card is not present, why are we attaching ? */
5173737Shx147065 		ret = CS_NO_CARD;
5183737Shx147065 		goto regcs_unreg;
5193737Shx147065 	}
5203737Shx147065 	cv_init(&pcwl_p->pcwl_cscv, NULL, CV_DRIVER, NULL);
5213737Shx147065 	mutex_init(&pcwl_p->pcwl_cslock, NULL, MUTEX_DRIVER, *cr.iblk_cookie);
5223737Shx147065 	mutex_enter(&pcwl_p->pcwl_cslock);
5233737Shx147065 	if (ret = csx_MapLogSocket(chdl, &pcwl_p->pcwl_log_sock)) {
5243737Shx147065 		cmn_err(CE_WARN, "pcwl: MapLogSocket failed %x\n", ret);
5253737Shx147065 		goto regcs_fail;
5263737Shx147065 	}
5273737Shx147065 	PCWLDBG((CE_NOTE,
5283737Shx147065 	    "pcwl: register_cs: LogSock=%x PhyAdapter=%x PhySock=%x\n",
5293737Shx147065 	    pcwl_p->pcwl_log_sock.LogSocket,
5303737Shx147065 	    pcwl_p->pcwl_log_sock.PhyAdapter,
5313737Shx147065 	    pcwl_p->pcwl_log_sock.PhySocket));
5323737Shx147065 	/* turn on initialization events */
5333737Shx147065 	sock_req.Socket = 0;
5343737Shx147065 	sock_req.EventMask = CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
5353737Shx147065 	    CS_EVENT_REGISTRATION_COMPLETE;
5363737Shx147065 	if (ret = csx_RequestSocketMask(chdl, &sock_req)) {
5373737Shx147065 		cmn_err(CE_WARN, "pcwl: RequestSocketMask failed %x\n", ret);
5383737Shx147065 		goto regcs_fail;
5393737Shx147065 	}
5403737Shx147065 	/* wait for and process card insertion events */
5413737Shx147065 	while (!(pcwl_p->pcwl_flag & PCWL_CARD_READY))
5423737Shx147065 		cv_wait(&pcwl_p->pcwl_cscv, &pcwl_p->pcwl_cslock);
5433737Shx147065 	mutex_exit(&pcwl_p->pcwl_cslock);
5443737Shx147065 
5453737Shx147065 	pcwl_p->pcwl_flag |= PCWL_CS_REGISTERED;
5463737Shx147065 	return (PCWL_SUCCESS);
5473737Shx147065 regcs_fail:
5483737Shx147065 	mutex_destroy(&pcwl_p->pcwl_cslock);
5493737Shx147065 	cv_destroy(&pcwl_p->pcwl_cscv);
5503737Shx147065 regcs_unreg:
5513737Shx147065 	(void) csx_DeregisterClient(chdl);
5523737Shx147065 regcs_ret:
5533737Shx147065 	pcwl_p->pcwl_flag &= ~PCWL_CS_REGISTERED;
5543737Shx147065 	return (ret);
5553737Shx147065 }
5563737Shx147065 
5573737Shx147065 static void
5583737Shx147065 pcwl_unregister_cs(pcwl_maci_t *pcwl_p)
5593737Shx147065 {
5603737Shx147065 	int ret;
5613737Shx147065 	release_socket_mask_t mask;
5623737Shx147065 	mask.Socket = pcwl_p->pcwl_socket;
5633737Shx147065 
5643737Shx147065 	/*
5653737Shx147065 	 * The card service not registered means register_cs function
5663737Shx147065 	 * doesnot return TRUE. Then all the lelated resource has been
5673737Shx147065 	 * released in register_cs.
5683737Shx147065 	 */
5693737Shx147065 	if (!(pcwl_p->pcwl_flag | PCWL_CS_REGISTERED))
5703737Shx147065 		return;
5713737Shx147065 
5723737Shx147065 	if (ret = csx_ReleaseSocketMask(pcwl_p->pcwl_chdl, &mask))
5733737Shx147065 		cmn_err(CE_WARN, "pcwl: ReleaseSocket mask failed %x\n", ret);
5743737Shx147065 
5753737Shx147065 	if (pcwl_p->pcwl_flag & PCWL_CARD_READY) {
5763737Shx147065 		pcwl_card_remove(pcwl_p);
5773737Shx147065 		pcwl_p->pcwl_flag &= ~PCWL_CARD_READY;
5783737Shx147065 	}
5793737Shx147065 	mutex_destroy(&pcwl_p->pcwl_cslock);
5803737Shx147065 	cv_destroy(&pcwl_p->pcwl_cscv);
5813737Shx147065 	if (ret = csx_DeregisterClient(pcwl_p->pcwl_chdl))
5823737Shx147065 		cmn_err(CE_WARN, "pcwl: Deregister failed %x\n", ret);
5833737Shx147065 }
5843737Shx147065 
5853737Shx147065 static void
5863737Shx147065 pcwl_destroy_locks(pcwl_maci_t *pcwl_p)
5873737Shx147065 {
5883737Shx147065 	mutex_destroy(&pcwl_p->pcwl_txring.wl_tx_lock);
5893737Shx147065 	mutex_destroy(&pcwl_p->pcwl_scanlist_lock);
5903737Shx147065 	mutex_destroy(&pcwl_p->pcwl_glock);
5913737Shx147065 }
5923737Shx147065 
5933737Shx147065 static int
5943737Shx147065 pcwl_ev_hdlr(event_t event, int priority, event_callback_args_t *arg)
5953737Shx147065 {
5963737Shx147065 	int ret = CS_SUCCESS;
5973737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg->client_data;
5983737Shx147065 	client_info_t *ci_p = (client_info_t *)&arg->client_info;
5993737Shx147065 
6003737Shx147065 	mutex_enter(&pcwl_p->pcwl_cslock);
6013737Shx147065 	switch (event) {
6023737Shx147065 	case CS_EVENT_CARD_INSERTION:
6033737Shx147065 		ret = pcwl_card_insert(pcwl_p);
6043737Shx147065 		cv_broadcast(&pcwl_p->pcwl_cscv);
6053737Shx147065 		break;
6063737Shx147065 	case CS_EVENT_REGISTRATION_COMPLETE:
6073737Shx147065 		cv_broadcast(&pcwl_p->pcwl_cscv);
6083737Shx147065 		break;
6093737Shx147065 	case CS_EVENT_CARD_REMOVAL:
6103737Shx147065 		if (priority & CS_EVENT_PRI_HIGH)
6113737Shx147065 			break;
6123737Shx147065 		pcwl_card_remove(pcwl_p);
6133737Shx147065 		cv_broadcast(&pcwl_p->pcwl_cscv);
6143737Shx147065 		break;
6153737Shx147065 	case CS_EVENT_CLIENT_INFO:
6163737Shx147065 		if (GET_CLIENT_INFO_SUBSVC(ci_p->Attributes) !=
6173737Shx147065 		    CS_CLIENT_INFO_SUBSVC_CS)
6183737Shx147065 			break;
6193737Shx147065 
6203737Shx147065 		ci_p->Revision = 0x0101;
6213737Shx147065 		ci_p->CSLevel = CS_VERSION;
6223737Shx147065 		ci_p->RevDate = CS_CLIENT_INFO_MAKE_DATE(9, 12, 14);
6233737Shx147065 		(void) strcpy(ci_p->ClientName, PCWL_IDENT_STRING);
6243737Shx147065 		(void) strcpy(ci_p->VendorName, CS_SUN_VENDOR_DESCRIPTION);
6253737Shx147065 		ci_p->Attributes |= CS_CLIENT_INFO_VALID;
6263737Shx147065 		break;
6273737Shx147065 	default:
6283737Shx147065 		ret = CS_UNSUPPORTED_EVENT;
6293737Shx147065 		break;
6303737Shx147065 	}
6313737Shx147065 	mutex_exit(&pcwl_p->pcwl_cslock);
6323737Shx147065 	return (ret);
6333737Shx147065 }
6343737Shx147065 
6353737Shx147065 static int
6363737Shx147065 pcwl_card_insert(pcwl_maci_t *pcwl_p)
6373737Shx147065 {
6383737Shx147065 	int ret, hi, lo;
6393737Shx147065 	tuple_t tuple;
6403737Shx147065 	cisparse_t cisparse;
6413737Shx147065 	io_req_t	io;
6423737Shx147065 	irq_req_t	irq;
6433737Shx147065 	config_req_t	cfg;
6443737Shx147065 	cistpl_config_t config;
6453737Shx147065 	cistpl_cftable_entry_t *tbl_p;
6463737Shx147065 	register client_handle_t chdl = pcwl_p->pcwl_chdl;
6473737Shx147065 
6483737Shx147065 	bzero(&tuple, sizeof (tuple));
6493737Shx147065 	tuple.DesiredTuple = CISTPL_MANFID;
6503737Shx147065 	if (ret = csx_GetFirstTuple(chdl, &tuple)) {
6513737Shx147065 		cmn_err(CE_WARN, "pcwl: get manufacture id failed %x\n", ret);
6523737Shx147065 		goto insert_ret;
6533737Shx147065 	}
6543737Shx147065 	bzero(&cisparse, sizeof (cisparse));
6553737Shx147065 	if (ret = csx_Parse_CISTPL_MANFID(chdl, &tuple, &cisparse.manfid)) {
6563737Shx147065 		cmn_err(CE_WARN, "pcwl: parse manufacture id failed %x\n", ret);
6573737Shx147065 		goto insert_ret;
6583737Shx147065 	}
6593737Shx147065 
6603737Shx147065 	/*
6613737Shx147065 	 * verify manufacture ID
6623737Shx147065 	 */
6633737Shx147065 	PCWLDBG((CE_NOTE, "pcwl insert: manufacturer_id=%x card=%x\n",
6643737Shx147065 	    cisparse.manfid.manf, cisparse.manfid.card));
6653737Shx147065 	bzero(&tuple, sizeof (tuple));
6663737Shx147065 	tuple.DesiredTuple = CISTPL_FUNCID;
6673737Shx147065 	if (ret = csx_GetFirstTuple(chdl, &tuple)) {
6683737Shx147065 		cmn_err(CE_WARN, "pcwl: get function id failed %x\n", ret);
6693737Shx147065 		goto insert_ret;
6703737Shx147065 	}
6713737Shx147065 	bzero(&cisparse, sizeof (cisparse));
6723737Shx147065 	if (ret = csx_Parse_CISTPL_FUNCID(chdl, &tuple, &cisparse.funcid)) {
6733737Shx147065 		cmn_err(CE_WARN, "pcwl: parse function id failed %x\n", ret);
6743737Shx147065 		goto insert_ret;
6753737Shx147065 	}
6763737Shx147065 
6773737Shx147065 	/*
6783737Shx147065 	 * verify function ID
6793737Shx147065 	 */
6803737Shx147065 	PCWLDBG((CE_NOTE, "insert:fun_id=%x\n", cisparse.funcid.function));
6813737Shx147065 	bzero(&tuple, sizeof (tuple));
6823737Shx147065 	tuple.DesiredTuple = CISTPL_CONFIG;
6833737Shx147065 	if (ret = csx_GetFirstTuple(chdl, &tuple)) {
6843737Shx147065 		cmn_err(CE_WARN, "pcwl: get config failed %x\n", ret);
6853737Shx147065 		goto insert_ret;
6863737Shx147065 	}
6873737Shx147065 	bzero(&config, sizeof (config));
6883737Shx147065 	if (ret = csx_Parse_CISTPL_CONFIG(chdl, &tuple, &config)) {
6893737Shx147065 		cmn_err(CE_WARN, "pcwl: parse config failed %x\n", ret);
6903737Shx147065 		goto insert_ret;
6913737Shx147065 	}
6923737Shx147065 	PCWLDBG((CE_NOTE,
6933737Shx147065 	    "pcwl: config present=%x nr=%x hr=%x regs[0]=%x base=%x last=%x\n",
6943737Shx147065 	    config.present, config.nr, config.hr, config.regs[0],
6953737Shx147065 	    config.base, config.last));
6963737Shx147065 	hi = 0;
6973737Shx147065 	lo = (int)-1;		/* really big number */
6983737Shx147065 	tbl_p = &cisparse.cftable;
6993737Shx147065 	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
7003737Shx147065 	for (tbl_p->index = 0; tbl_p->index <= config.hr; ) {
7013737Shx147065 		PCWLDBG((CE_NOTE, "pcwl insert:tuple idx=%x:\n", tbl_p->index));
7023737Shx147065 		if (ret = csx_GetNextTuple(chdl, &tuple)) {
7033737Shx147065 			cmn_err(CE_WARN, "pcwl: get cftable failed %x\n",
7043737Shx147065 			    ret);
7053737Shx147065 			break;
7063737Shx147065 		}
7073737Shx147065 		bzero((caddr_t)&cisparse, sizeof (cisparse));
7083737Shx147065 		if (ret = csx_Parse_CISTPL_CFTABLE_ENTRY(chdl, &tuple, tbl_p)) {
7093737Shx147065 			cmn_err(CE_WARN, "pcwl: parse cftable failed %x\n",
7103737Shx147065 			    ret);
7113737Shx147065 			break;
7123737Shx147065 		}
7133737Shx147065 		if (tbl_p->flags & CISTPL_CFTABLE_TPCE_FS_PWR &&
714*6062Shx147065 		    tbl_p->pd.flags & CISTPL_CFTABLE_TPCE_FS_PWR_VCC) {
7153737Shx147065 			if (tbl_p->pd.pd_vcc.avgI > hi) {
7163737Shx147065 				hi = tbl_p->pd.pd_vcc.avgI;
7173737Shx147065 				pcwl_p->pcwl_config_hi = tbl_p->index;
7183737Shx147065 			}
7193737Shx147065 			if (tbl_p->pd.pd_vcc.avgI < lo) {
7203737Shx147065 				lo = tbl_p->pd.pd_vcc.avgI;
7213737Shx147065 				pcwl_p->pcwl_config = tbl_p->index;
7223737Shx147065 			}
7233737Shx147065 		}
7243737Shx147065 		if (tbl_p->flags & CISTPL_CFTABLE_TPCE_DEFAULT) {
7253737Shx147065 			if (tbl_p->pd.flags & CISTPL_CFTABLE_TPCE_FS_PWR_VCC)
7263737Shx147065 				pcwl_p->pcwl_vcc = tbl_p->pd.pd_vcc.nomV;
7273737Shx147065 			if (tbl_p->flags & CISTPL_CFTABLE_TPCE_FS_IO)
7283737Shx147065 				pcwl_p->pcwl_iodecode = tbl_p->io.addr_lines;
7293737Shx147065 		}
7303737Shx147065 	}
7313737Shx147065 	PCWLDBG((CE_NOTE, "pcwl: insert:cfg_hi=%x cfg=%x vcc=%x iodecode=%x\n",
7323737Shx147065 	    pcwl_p->pcwl_config_hi, pcwl_p->pcwl_config,
7333737Shx147065 	    pcwl_p->pcwl_vcc, pcwl_p->pcwl_iodecode));
7343737Shx147065 	bzero(&io, sizeof (io));
7353737Shx147065 	io.BasePort1.base = 0;
7363737Shx147065 	io.NumPorts1 = 1 << pcwl_p->pcwl_iodecode;
7373737Shx147065 	io.Attributes1 = IO_DATA_PATH_WIDTH_16;
7383737Shx147065 	io.IOAddrLines = pcwl_p->pcwl_iodecode;
7393737Shx147065 	if (ret = csx_RequestIO(chdl, &io)) {
7403737Shx147065 		cmn_err(CE_WARN, "pcwl: RequestIO failed %x\n", ret);
7413737Shx147065 		goto insert_ret;
7423737Shx147065 	}
7433737Shx147065 	pcwl_p->pcwl_port = io.BasePort1.handle;
7443737Shx147065 	if (ret = ddi_add_softintr(DIP(pcwl_p), DDI_SOFTINT_HIGH,
7453737Shx147065 	    &pcwl_p->pcwl_softint_id, &pcwl_p->pcwl_ib_cookie, NULL,
7463737Shx147065 	    pcwl_intr, (caddr_t)pcwl_p)) {
7473737Shx147065 		cmn_err(CE_NOTE, "pcwl(pccard): add softintr failed\n");
7483737Shx147065 		goto insert_ret;
7493737Shx147065 	}
7503737Shx147065 	irq.Attributes = IRQ_TYPE_EXCLUSIVE;
7513737Shx147065 	irq.irq_handler = ddi_intr_hilevel(DIP(pcwl_p), 0) ?
7523737Shx147065 	    (csfunction_t *)pcwl_intr_hi : (csfunction_t *)pcwl_intr;
7533737Shx147065 	irq.irq_handler_arg = pcwl_p;
7543737Shx147065 	if (ret = csx_RequestIRQ(pcwl_p->pcwl_chdl, &irq)) {
7553737Shx147065 		cmn_err(CE_WARN, "pcwl: RequestIRQ failed %x\n", ret);
7563737Shx147065 		goto un_io;
7573737Shx147065 	}
7583737Shx147065 	bzero(&cfg, sizeof (cfg));
7593737Shx147065 	cfg.Attributes = 0; /* not ready for CONF_ENABLE_IRQ_STEERING yet */
7603737Shx147065 	cfg.Vcc = 50;
7613737Shx147065 	cfg.IntType = SOCKET_INTERFACE_MEMORY_AND_IO;
7623737Shx147065 	cfg.ConfigBase = config.base;
7633737Shx147065 	cfg.ConfigIndex = pcwl_p->pcwl_config;
7643737Shx147065 	cfg.Status = CCSR_IO_IS_8;
7653737Shx147065 	cfg.Present = config.present;
7663737Shx147065 	pcwl_p->pcwl_flag |= PCWL_CARD_READY;
7673737Shx147065 	if (ret = csx_RequestConfiguration(chdl, &cfg)) {
7683737Shx147065 		cmn_err(CE_WARN, "pcwl: RequestConfiguration failed %x\n", ret);
7693737Shx147065 		goto un_irq;
7703737Shx147065 	}
7713737Shx147065 	return (CS_SUCCESS);
7723737Shx147065 un_irq:
7733737Shx147065 	(void) csx_ReleaseIRQ(chdl, &irq);
7743737Shx147065 un_io:
7753737Shx147065 	ddi_remove_softintr(pcwl_p->pcwl_softint_id);
7763737Shx147065 	(void) csx_ReleaseIO(chdl, &io);
7773737Shx147065 	pcwl_p->pcwl_port = 0;
7783737Shx147065 insert_ret:
7793737Shx147065 	pcwl_p->pcwl_flag &= ~PCWL_CARD_READY;
7803737Shx147065 	return (ret);
7813737Shx147065 
7823737Shx147065 }
7833737Shx147065 
7843737Shx147065 /*
7853737Shx147065  * assume card is already removed, don't touch the hardware
7863737Shx147065  */
7873737Shx147065 static void
7883737Shx147065 pcwl_card_remove(pcwl_maci_t *pcwl_p)
7893737Shx147065 {
7903737Shx147065 	int ret;
7913737Shx147065 	io_req_t io;
7923737Shx147065 	irq_req_t irq;
7933737Shx147065 
7943737Shx147065 	/*
7953737Shx147065 	 * The card not ready means Insert function doesnot return TRUE.
7963737Shx147065 	 * then the IO and IRQ has been released in Insert
7973737Shx147065 	 */
7983737Shx147065 	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY))
7993737Shx147065 		return;
8003737Shx147065 	if (ret = csx_ReleaseConfiguration(pcwl_p->pcwl_chdl, NULL))
8013737Shx147065 		cmn_err(CE_WARN, "pcwl: ReleaseConfiguration failed %x\n", ret);
8023737Shx147065 
8033737Shx147065 	bzero(&irq, sizeof (irq));
8043737Shx147065 	if (ret = csx_ReleaseIRQ(pcwl_p->pcwl_chdl, &irq))
8053737Shx147065 		cmn_err(CE_WARN, "pcwl: ReleaseIRQ failed %x\n", ret);
8063737Shx147065 
8073737Shx147065 	ddi_remove_softintr(pcwl_p->pcwl_softint_id);
8083737Shx147065 
8093737Shx147065 	bzero(&io, sizeof (io));
8103737Shx147065 	io.BasePort1.handle = pcwl_p->pcwl_port;
8113737Shx147065 	io.NumPorts1 = 16;
8123737Shx147065 	if (ret = csx_ReleaseIO(pcwl_p->pcwl_chdl, &io))
8133737Shx147065 		cmn_err(CE_WARN, "pcwl: ReleaseIO failed %x\n", ret);
8143737Shx147065 
8153737Shx147065 	pcwl_p->pcwl_port = 0;
8163737Shx147065 	pcwl_p->pcwl_flag &= ~PCWL_CARD_READY;
8173737Shx147065 }
8183737Shx147065 
8193737Shx147065 /*
8203737Shx147065  * mac operation interface routines
8213737Shx147065  */
8223737Shx147065 static int
8233737Shx147065 pcwl_start(void *arg)
8243737Shx147065 {
8253737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
8263737Shx147065 
8273737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
8283737Shx147065 	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
8293737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
8303737Shx147065 		return (PCWL_FAIL);
8313737Shx147065 	}
8323737Shx147065 	pcwl_start_locked(pcwl_p);
8333737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
8343737Shx147065 	return (PCWL_SUCCESS);
8353737Shx147065 }
8363737Shx147065 
8373737Shx147065 static void
8383737Shx147065 pcwl_stop(void *arg)
8393737Shx147065 {
8403737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
8413737Shx147065 
8423737Shx147065 	PCWLDBG((CE_NOTE, "pcwl_stop called\n"));
8433737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
8443737Shx147065 	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
8453737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
8463737Shx147065 		return;
8473737Shx147065 	}
8483737Shx147065 
8493737Shx147065 	pcwl_stop_locked(pcwl_p);
8503737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
8513737Shx147065 	if (pcwl_p->pcwl_connect_timeout_id != 0) {
8523737Shx147065 		(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
8533737Shx147065 		pcwl_p->pcwl_connect_timeout_id = 0;
8543737Shx147065 	}
8553737Shx147065 }
8563737Shx147065 
8573737Shx147065 static int
8583737Shx147065 pcwl_saddr(void *arg, const uint8_t *macaddr)
8593737Shx147065 {
8603737Shx147065 	int ret = PCWL_SUCCESS;
8613737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
8623737Shx147065 
8633737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
8643737Shx147065 	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
8653737Shx147065 		ret = PCWL_FAIL;
8663737Shx147065 		goto done;
8673737Shx147065 	}
8683737Shx147065 	ether_copy(macaddr, pcwl_p->pcwl_mac_addr);
8693737Shx147065 	if (pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
8703737Shx147065 		ret = PCWL_FAIL;
8713737Shx147065 		goto done;
8723737Shx147065 	}
8733737Shx147065 	if (pcwl_saddr_locked(pcwl_p)) {
8743737Shx147065 		ret = PCWL_FAIL;
8753737Shx147065 		goto done;
8763737Shx147065 	}
8773737Shx147065 	if (pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
8783737Shx147065 		ret = PCWL_FAIL;
8793737Shx147065 	}
8803737Shx147065 done:
8813737Shx147065 	if (ret)
8823737Shx147065 		cmn_err(CE_WARN, "pcwl set_mac_addr: failed\n");
8833737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
8843737Shx147065 	return (ret);
8853737Shx147065 }
8863737Shx147065 
8873737Shx147065 static int
8883737Shx147065 pcwl_send(pcwl_maci_t *pcwl_p, mblk_t *mblk_p)
8893737Shx147065 {
8903737Shx147065 	int i = 0;
8913737Shx147065 	char *buf, *buf_p;
8923737Shx147065 	wl_frame_t *frm_p;
8933737Shx147065 	uint16_t pkt_len, ret;
8943737Shx147065 	uint16_t xmt_id, ring_idx;
8953737Shx147065 	struct ieee80211_frame *wh;
8963737Shx147065 	struct ieee80211_llc *llc;
8973737Shx147065 
8983737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
8993737Shx147065 	if ((pcwl_p->pcwl_flag & (PCWL_CARD_READY | PCWL_CARD_LINKUP)) !=
9003737Shx147065 	    (PCWL_CARD_READY | PCWL_CARD_LINKUP)) {
9013737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
9023737Shx147065 		freemsg(mblk_p);
9033737Shx147065 		return (PCWL_SUCCESS);		/* drop packet */
9043737Shx147065 	}
9053737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
9063737Shx147065 
9073737Shx147065 	if (pullupmsg(mblk_p, -1) == 0) {
9083737Shx147065 		freemsg(mblk_p);
9093737Shx147065 		return (PCWL_SUCCESS);		/* drop packet */
9103737Shx147065 	}
9113737Shx147065 	wh = (struct ieee80211_frame *)mblk_p->b_rptr;
9123737Shx147065 	llc = (struct ieee80211_llc *)&wh[1];
9133737Shx147065 
9143737Shx147065 	mutex_enter(&pcwl_p->pcwl_txring.wl_tx_lock);
9153737Shx147065 	ring_idx = pcwl_p->pcwl_txring.wl_tx_prod;
9163737Shx147065 	pcwl_p->pcwl_txring.wl_tx_prod = (ring_idx + 1) & (WL_XMT_BUF_NUM - 1);
9173737Shx147065 
9183737Shx147065 	/*
9193737Shx147065 	 * check whether there is a xmt buffer available
9203737Shx147065 	 */
9213737Shx147065 	while ((i < WL_XMT_BUF_NUM) &&
9223737Shx147065 	    (pcwl_p->pcwl_txring.wl_tx_ring[ring_idx])) {
9233737Shx147065 		ring_idx = pcwl_p->pcwl_txring.wl_tx_prod;
9243737Shx147065 		pcwl_p->pcwl_txring.wl_tx_prod =
9253737Shx147065 		    (ring_idx + 1) & (WL_XMT_BUF_NUM - 1);
9263737Shx147065 		i++;
9273737Shx147065 	}
9283737Shx147065 	if (i == WL_XMT_BUF_NUM) {
9293737Shx147065 		mutex_exit(&pcwl_p->pcwl_txring.wl_tx_lock);
9303737Shx147065 		mutex_enter(&pcwl_p->pcwl_glock);
9313737Shx147065 		pcwl_p->pcwl_reschedule_need = B_TRUE;
9323737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
9333737Shx147065 		pcwl_p->pcwl_noxmtbuf++;
9343737Shx147065 		return (PCWL_FAIL);
9353737Shx147065 	}
9363737Shx147065 	xmt_id = pcwl_p->pcwl_txring.wl_tx_fids[ring_idx];
9373737Shx147065 	pcwl_p->pcwl_txring.wl_tx_ring[ring_idx] = xmt_id;
9383737Shx147065 	mutex_exit(&pcwl_p->pcwl_txring.wl_tx_lock);
9393737Shx147065 
9403737Shx147065 	buf = kmem_zalloc(PCWL_NICMEM_SZ, KM_SLEEP);
9413737Shx147065 	buf_p = (ulong_t)buf & 1 ? buf + 1 : buf;
9423737Shx147065 	frm_p = (wl_frame_t *)buf_p;
9433737Shx147065 #ifdef DEBUG
9443737Shx147065 	if (pcwl_debug & PCWL_DBG_SEND) {
9453737Shx147065 		cmn_err(CE_NOTE, "pcwl send: packet");
9463737Shx147065 		for (i = 0; i < mblk_p->b_wptr - mblk_p->b_rptr; i++)
9473737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
9483737Shx147065 			    *((unsigned char *)mblk_p->b_rptr + i));
9493737Shx147065 	}
9503737Shx147065 #endif
9513737Shx147065 	pkt_len = msgdsize(mblk_p);
9523737Shx147065 	if (pkt_len > (PCWL_NICMEM_SZ - sizeof (wl_frame_t))) {
9533737Shx147065 		cmn_err(CE_WARN, "pcwl: send mblk is too long");
9543737Shx147065 		kmem_free(buf, PCWL_NICMEM_SZ);
9553737Shx147065 		freemsg(mblk_p);
9563737Shx147065 		return (PCWL_SUCCESS);		/* drop packet */
9573737Shx147065 	}
9583737Shx147065 	if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) !=
9593737Shx147065 	    IEEE80211_FC1_DIR_TODS) {
9603737Shx147065 		kmem_free(buf, PCWL_NICMEM_SZ);
9613737Shx147065 		freemsg(mblk_p);
9623737Shx147065 		return (PCWL_SUCCESS);		/* drop packet */
9633737Shx147065 	}
9643737Shx147065 	bzero(frm_p, WL_802_11_HDRLEN);
9653737Shx147065 
9663737Shx147065 	frm_p->wl_tx_ctl = WL_TXCNTL_SET;
9673737Shx147065 	bcopy(wh->i_addr3, frm_p->wl_dst_addr, ETHERADDRL); /* dst macaddr */
9683737Shx147065 	bcopy(wh->i_addr2, frm_p->wl_src_addr, ETHERADDRL); /* src macaddr */
9693737Shx147065 	frm_p->wl_len = htons(pkt_len  - sizeof (*wh));
9703737Shx147065 	bcopy(llc, &frm_p->wl_dat[0], pkt_len - sizeof (*wh));
9713737Shx147065 	pkt_len = pkt_len - (sizeof (*wh) + sizeof (*llc)) +
9723737Shx147065 	    WL_802_11_HDRLEN;
9733737Shx147065 	PCWLDBG((CE_NOTE, "send: DIX frmsz=%x pkt_len=%x\n",
9743737Shx147065 	    WL_802_11_HDRLEN, pkt_len));
9753737Shx147065 
9763737Shx147065 	if (pkt_len & 1)	/* round up to 16-bit boundary and pad 0 */
9773737Shx147065 		buf_p[pkt_len++] = 0;
9783737Shx147065 
9793737Shx147065 	ASSERT(pkt_len <= PCWL_NICMEM_SZ);
9803737Shx147065 #ifdef DEBUG
9813737Shx147065 	if (pcwl_debug & PCWL_DBG_SEND) {
9823737Shx147065 		cmn_err(CE_NOTE, "pkt_len = %x\n", pkt_len);
9833737Shx147065 		for (i = 0; i < pkt_len; i++)
9843737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
9853737Shx147065 			    *((unsigned char *)buf + i));
9863737Shx147065 	}
9873737Shx147065 #endif
9883737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
9893737Shx147065 	ret = (WRCH1(pcwl_p, xmt_id, 0, (uint16_t *)buf_p, 0x2e) ||
9903737Shx147065 	    WRPKT(pcwl_p, xmt_id, 0x2e, (uint16_t *)(buf_p + 0x2e),
9913737Shx147065 	    pkt_len - 0x2e));
9923737Shx147065 	if (ret) {
9933737Shx147065 		goto done;
9943737Shx147065 	}
9953737Shx147065 	PCWLDBG((CE_NOTE, "send: xmt_id=%x send=%x\n", xmt_id, pkt_len));
9963737Shx147065 	(void) pcwl_set_cmd(pcwl_p, WL_CMD_TX | WL_RECLAIM, xmt_id);
9973737Shx147065 
9983737Shx147065 done:
9993737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
10003737Shx147065 	kmem_free(buf, PCWL_NICMEM_SZ);
10013737Shx147065 	freemsg(mblk_p);
10023737Shx147065 	return (PCWL_SUCCESS);
10033737Shx147065 }
10043737Shx147065 
10053737Shx147065 static mblk_t *
10063737Shx147065 pcwl_tx(void *arg, mblk_t *mp)
10073737Shx147065 {
10083737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
10093737Shx147065 	mblk_t *next;
10103737Shx147065 
10113737Shx147065 	ASSERT(mp != NULL);
10123737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
10133737Shx147065 	if ((pcwl_p->pcwl_flag & (PCWL_CARD_LINKUP | PCWL_CARD_READY)) !=
10143737Shx147065 	    (PCWL_CARD_LINKUP | PCWL_CARD_READY)) {
10153737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
10163737Shx147065 		return (mp);
10173737Shx147065 	}
10183737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
10193737Shx147065 	while (mp != NULL) {
10203737Shx147065 		next =  mp->b_next;
10213737Shx147065 		mp->b_next = NULL;
10223737Shx147065 
10233737Shx147065 		if (pcwl_send(pcwl_p, mp)) {
10243737Shx147065 			mp->b_next = next;
10253737Shx147065 			break;
10263737Shx147065 		}
10273737Shx147065 		mp = next;
10283737Shx147065 	}
10293737Shx147065 	return (mp);
10303737Shx147065 }
10313737Shx147065 
10323737Shx147065 static int
10333737Shx147065 pcwl_prom(void *arg, boolean_t on)
10343737Shx147065 {
10353737Shx147065 	int ret = PCWL_SUCCESS;
10363737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
10373737Shx147065 
10383737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
10393737Shx147065 	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
10403737Shx147065 		ret = PCWL_FAIL;
10413737Shx147065 		goto done;
10423737Shx147065 	}
10433737Shx147065 
10443737Shx147065 	PCWLDBG((CE_NOTE, "pcwl_prom called %x\n", on));
10453737Shx147065 
10463737Shx147065 	if (on)
10473737Shx147065 		pcwl_p->pcwl_rf.rf_promiscuous = 1;
10483737Shx147065 	else
10493737Shx147065 		pcwl_p->pcwl_rf.rf_promiscuous = 0;
10503737Shx147065 	if (ret = pcwl_fil_ltv(pcwl_p, 2, WL_RID_PROMISC,
10513737Shx147065 	    pcwl_p->pcwl_rf.rf_promiscuous)) {
10523737Shx147065 		ret = PCWL_FAIL;
10533737Shx147065 	}
10543737Shx147065 done:
10553737Shx147065 	if (ret)
10563737Shx147065 		cmn_err(CE_WARN, "pcwl promisc: failed\n");
10573737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
10583737Shx147065 	return (ret);
10593737Shx147065 }
10603737Shx147065 
10613737Shx147065 static int
10623737Shx147065 pcwl_gstat(void *arg, uint_t statitem, uint64_t *val)
10633737Shx147065 {
10643737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
10653737Shx147065 	int ret = PCWL_SUCCESS;
10663737Shx147065 	uint64_t *cntr_p = pcwl_p->pcwl_cntrs_s;
10673737Shx147065 	uint16_t rate = 0;
10683737Shx147065 	uint64_t speed;
10693737Shx147065 
10703737Shx147065 	PCWLDBG((CE_NOTE, "pcwl_gstat called\n"));
10713737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
10723737Shx147065 	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
10733737Shx147065 		ret = PCWL_FAIL;
10743737Shx147065 		goto done;
10753737Shx147065 	}
10763737Shx147065 
10773737Shx147065 	if (pcwl_get_ltv(pcwl_p, 2, WL_RID_CUR_TX_RATE, &rate)) {
10783737Shx147065 		cmn_err(CE_WARN, "pcwl kstat: get speed failed\n");
10793737Shx147065 		ret = PCWL_FAIL;
10803737Shx147065 		goto done;
10813737Shx147065 	}
10823737Shx147065 	switch (pcwl_p->pcwl_chip_type) {
10833737Shx147065 	case PCWL_CHIP_PRISMII:
10843737Shx147065 		switch (rate) {
10853737Shx147065 		case WL_SPEED_1Mbps_P2:		rate = 2;	break;
10863737Shx147065 		case WL_SPEED_2Mbps_P2:		rate = 4;	break;
10873737Shx147065 		case WL_SPEED_55Mbps_P2:	rate = 11;	break;
10883737Shx147065 		case WL_SPEED_11Mbps_P2:	rate = 22;	break;
10893737Shx147065 		default:			rate = 0;	break;
10903737Shx147065 		}
10913737Shx147065 		speed = rate * 500000;
10923737Shx147065 		break;
10933737Shx147065 	case PCWL_CHIP_LUCENT:
10943737Shx147065 	default:
10953737Shx147065 		speed = rate * 1000000;
10963737Shx147065 		if (rate == 6)
10973737Shx147065 			speed = 5500000;
10983737Shx147065 		break;
10993737Shx147065 	}
11003737Shx147065 
11013737Shx147065 	switch (statitem) {
11023737Shx147065 	case MAC_STAT_IFSPEED:
11033737Shx147065 		*val = speed;
11043737Shx147065 		break;
11053737Shx147065 	case MAC_STAT_NOXMTBUF:
11063737Shx147065 		*val = pcwl_p->pcwl_noxmtbuf;
11073737Shx147065 		break;
11083737Shx147065 	case MAC_STAT_NORCVBUF:
11093737Shx147065 		*val = cntr_p[WLC_RX_DISCARDS_NOBUF];
11103737Shx147065 		break;
11113737Shx147065 	case MAC_STAT_IERRORS:
11123737Shx147065 		*val = 0;
11133737Shx147065 		break;
11143737Shx147065 	case MAC_STAT_OERRORS:
11153737Shx147065 		*val = cntr_p[WLC_TX_DISCARDS] +
11163737Shx147065 		    cntr_p[WLC_TX_DISCARDS_WRONG_SA];
11173737Shx147065 		break;
11183737Shx147065 	case MAC_STAT_RBYTES:
11193737Shx147065 		*val = cntr_p[WLC_RX_UNICAST_OCTETS];
11203737Shx147065 		break;
11213737Shx147065 	case MAC_STAT_IPACKETS:
11223737Shx147065 		*val = cntr_p[WLC_RX_UNICAST_FRAMES];
11233737Shx147065 		break;
11243737Shx147065 	case MAC_STAT_OBYTES:
11253737Shx147065 		*val = cntr_p[WLC_TX_UNICAST_OCTETS];
11263737Shx147065 		break;
11273737Shx147065 	case MAC_STAT_OPACKETS:
11283737Shx147065 		*val = cntr_p[WLC_TX_UNICAST_FRAMES];
11293737Shx147065 		break;
11303737Shx147065 	case WIFI_STAT_TX_FAILED:
11313737Shx147065 		*val = cntr_p[WLC_TX_RETRY_LIMIT] +
11323737Shx147065 		    cntr_p[WLC_TX_DEFERRED_XMITS];
11333737Shx147065 		break;
11343737Shx147065 	case WIFI_STAT_TX_RETRANS:
11353737Shx147065 		*val = cntr_p[WLC_TX_SINGLE_RETRIES] +
11363737Shx147065 		    cntr_p[WLC_TX_MULTI_RETRIES];
11373737Shx147065 		break;
11383737Shx147065 	case WIFI_STAT_FCS_ERRORS:
11393737Shx147065 		*val = cntr_p[WLC_RX_FCS_ERRORS];
11403737Shx147065 		break;
11413737Shx147065 	case WIFI_STAT_WEP_ERRORS:
11423737Shx147065 		*val = cntr_p[WLC_RX_WEP_CANT_DECRYPT];
11433737Shx147065 		break;
11443737Shx147065 	case WIFI_STAT_MCAST_TX:
11453737Shx147065 		*val = cntr_p[WLC_TX_MULTICAST_FRAMES];
11463737Shx147065 		break;
11473737Shx147065 	case WIFI_STAT_MCAST_RX:
11483737Shx147065 		*val = cntr_p[WLC_RX_MULTICAST_FRAMES];
11493737Shx147065 		break;
11503737Shx147065 	case WIFI_STAT_TX_FRAGS:
11513737Shx147065 		*val = cntr_p[WLC_TX_FRAGMENTS];
11523737Shx147065 		break;
11533737Shx147065 	case WIFI_STAT_RX_FRAGS:
11543737Shx147065 		*val =	cntr_p[WLC_RX_FRAGMENTS] +
11553737Shx147065 		    cntr_p[WLC_RX_MSG_IN_MSG_FRAGS] +
11563737Shx147065 		    cntr_p[WLC_RX_MSG_IN_BAD_MSG_FRAGS];
11573737Shx147065 		break;
11583737Shx147065 	case WIFI_STAT_RTS_SUCCESS:
11593737Shx147065 	case WIFI_STAT_RTS_FAILURE:
11603737Shx147065 	case WIFI_STAT_ACK_FAILURE:
11613737Shx147065 	case WIFI_STAT_RX_DUPS:
11623737Shx147065 		*val = 0;
11633737Shx147065 		break;
11643737Shx147065 	default:
11653737Shx147065 		ret = ENOTSUP;
11663737Shx147065 	}
11673737Shx147065 done:
11683737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
11693737Shx147065 	return (ret);
11703737Shx147065 }
11713737Shx147065 
11723737Shx147065 static int
11733737Shx147065 pcwl_sdmulti(void *arg, boolean_t add, const uint8_t *eth_p)
11743737Shx147065 {
11753737Shx147065 	int ret = PCWL_SUCCESS;
11763737Shx147065 	uint16_t i;
11773737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
11783737Shx147065 	uint16_t *mc_p = pcwl_p->pcwl_mcast;
11793737Shx147065 
11803737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
11813737Shx147065 	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
11823737Shx147065 		ret = PCWL_FAIL;
11833737Shx147065 		goto done;
11843737Shx147065 	}
11853737Shx147065 
11863737Shx147065 	if (add) { /* enable multicast on eth_p, search for available entries */
11873737Shx147065 		for (i = 0; i < 16; i++, mc_p += (ETHERADDRL >> 1)) {
11883737Shx147065 			if (!ether_cmp(eth_p, mc_p))
11893737Shx147065 				break;
11903737Shx147065 		}
11913737Shx147065 		if (i < 16)	/* already part of the filter */
11923737Shx147065 			goto done;
11933737Shx147065 		mc_p = pcwl_p->pcwl_mcast;	/* reset mc_p for 2nd scan */
11943737Shx147065 		for (i = 0; i < 16; i++, mc_p += (ETHERADDRL >> 1)) {
11953737Shx147065 			PCWLDBG((CE_NOTE, "smulti: mc[%x]=%s\n", i,
11963737Shx147065 			    ether_sprintf((struct ether_addr *)mc_p)));
11973737Shx147065 			if (mc_p[0] == 0 && mc_p[1] == 0 && mc_p[2] == 0)
11983737Shx147065 				break;
11993737Shx147065 		}
12003737Shx147065 		if (i >= 16)	/* can't find a vacant entry */
12013737Shx147065 			goto done;
12023737Shx147065 		ether_copy(eth_p, mc_p);
12033737Shx147065 	} else { /* disable multicast, locate the entry and clear it */
12043737Shx147065 		for (i = 0; i < 16; i++, mc_p += (ETHERADDRL >> 1)) {
12053737Shx147065 			if (!ether_cmp(eth_p, mc_p))
12063737Shx147065 				break;
12073737Shx147065 		}
12083737Shx147065 		if (i >= 16)
12093737Shx147065 			goto done;
12103737Shx147065 		mc_p[0] = 0;
12113737Shx147065 		mc_p[1] = 0;
12123737Shx147065 		mc_p[2] = 0;
12133737Shx147065 	}
12143737Shx147065 	/*
12153737Shx147065 	 * re-blow the entire 16 entries buffer
12163737Shx147065 	 */
12173737Shx147065 	if (i = pcwl_put_ltv(pcwl_p, ETHERADDRL << 4, WL_RID_MCAST,
12183737Shx147065 	    pcwl_p->pcwl_mcast)) {
12193737Shx147065 		ret = PCWL_FAIL;
12203737Shx147065 	}
12213737Shx147065 done:
12223737Shx147065 	if (ret)
12233737Shx147065 		cmn_err(CE_WARN, "pcwl set multi addr: failed\n");
12243737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
12253737Shx147065 	return (ret);
12263737Shx147065 }
12273737Shx147065 
12283737Shx147065 static uint_t
12293737Shx147065 pcwl_intr(caddr_t arg)
12303737Shx147065 {
12313737Shx147065 	uint16_t stat;
12323737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
12333737Shx147065 
12343737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
12353737Shx147065 	if ((pcwl_p->pcwl_flag & (PCWL_CARD_READY | PCWL_CARD_INTREN)) !=
12363737Shx147065 	    (PCWL_CARD_READY | PCWL_CARD_INTREN)) {
12373737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
12383737Shx147065 		return (DDI_INTR_UNCLAIMED);
12393737Shx147065 	}
12403737Shx147065 	PCWL_READ(pcwl_p, WL_EVENT_STAT, stat);
12413737Shx147065 	if (!(stat & WL_INTRS) || stat == WL_EV_ALL) {
12423737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
12433737Shx147065 		return (DDI_INTR_UNCLAIMED);
12443737Shx147065 	}
12453737Shx147065 
12463737Shx147065 	PCWL_WRITE(pcwl_p, WL_INT_EN, 0);
12473737Shx147065 	if (stat & WL_EV_RX) {
12483737Shx147065 		pcwl_rcv(pcwl_p);
12493737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_RX);
12503737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_RX);
12513737Shx147065 	}
12523737Shx147065 	if (stat & WL_EV_TX) {
12533737Shx147065 		if (pcwl_txdone(pcwl_p) == PCWL_SUCCESS) {
12543737Shx147065 			if (pcwl_p->pcwl_reschedule_need == B_TRUE) {
12553737Shx147065 				mutex_exit(&pcwl_p->pcwl_glock);
12563737Shx147065 				mac_tx_update(GLD3(pcwl_p));
12573737Shx147065 				mutex_enter(&pcwl_p->pcwl_glock);
12583737Shx147065 				pcwl_p->pcwl_reschedule_need = B_FALSE;
12593737Shx147065 			}
12603737Shx147065 		}
12613737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX);
12623737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX);
12633737Shx147065 	}
12643737Shx147065 	if (stat & WL_EV_ALLOC) {
12653737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_ALLOC | 0x1000);
12663737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, 0x1000);
12673737Shx147065 	}
12683737Shx147065 	if (stat & WL_EV_INFO) {
12693737Shx147065 		pcwl_infodone(pcwl_p);
12703737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_INFO);
12713737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_INFO);
12723737Shx147065 	}
12733737Shx147065 	if (stat & WL_EV_TX_EXC) {
12743737Shx147065 		if (pcwl_txdone(pcwl_p) == PCWL_SUCCESS) {
12753737Shx147065 			if (pcwl_p->pcwl_reschedule_need == B_TRUE) {
12763737Shx147065 				mutex_exit(&pcwl_p->pcwl_glock);
12773737Shx147065 				mac_tx_update(GLD3(pcwl_p));
12783737Shx147065 				mutex_enter(&pcwl_p->pcwl_glock);
12793737Shx147065 				pcwl_p->pcwl_reschedule_need = B_FALSE;
12803737Shx147065 			}
12813737Shx147065 		}
12823737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX_EXC);
12833737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX_EXC);
12843737Shx147065 	}
12853737Shx147065 	if (stat & WL_EV_INFO_DROP) {
12863737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_INFO_DROP);
12873737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_INFO_DROP);
12883737Shx147065 	}
12893737Shx147065 	PCWL_ENABLE_INTR(pcwl_p);
12903737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
12913737Shx147065 
12923737Shx147065 	return (DDI_INTR_CLAIMED);
12933737Shx147065 }
12943737Shx147065 
12953737Shx147065 static uint_t
12963737Shx147065 pcwl_intr_hi(caddr_t arg)
12973737Shx147065 {
12983737Shx147065 	uint16_t stat;
12993737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
13003737Shx147065 
13013737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
13023737Shx147065 	if ((pcwl_p->pcwl_flag & (PCWL_CARD_READY | PCWL_CARD_INTREN)) !=
13033737Shx147065 	    (PCWL_CARD_READY | PCWL_CARD_INTREN)) {
13043737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
13053737Shx147065 		return (DDI_INTR_UNCLAIMED);
13063737Shx147065 	}
13073737Shx147065 	PCWL_READ(pcwl_p, WL_EVENT_STAT, stat);
13083737Shx147065 	if (!(stat & WL_INTRS) || stat == WL_EV_ALL) {
13093737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
13103737Shx147065 		return (DDI_INTR_UNCLAIMED);
13113737Shx147065 	}
13123737Shx147065 	PCWL_WRITE(pcwl_p, WL_INT_EN, 0); /* disable interrupt without ack */
13133737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
13143737Shx147065 	ddi_trigger_softintr(pcwl_p->pcwl_softint_id);
13153737Shx147065 	return (DDI_INTR_CLAIMED);
13163737Shx147065 }
13173737Shx147065 
13183737Shx147065 /*
13193737Shx147065  * called at interrupt context to retrieve data from card
13203737Shx147065  */
13213737Shx147065 static void
13223737Shx147065 pcwl_rcv(pcwl_maci_t *pcwl_p)
13233737Shx147065 {
13243737Shx147065 	uint16_t id, len, off, ret, frm_ctl;
13253737Shx147065 	wl_frame_t frm;
13263737Shx147065 	mblk_t *mp = allocb(PCWL_NICMEM_SZ, BPRI_MED);
13273737Shx147065 	if (!mp)
13283737Shx147065 		return;
13293737Shx147065 	ASSERT(mp->b_rptr == mp->b_wptr);
13303737Shx147065 
13313737Shx147065 	PCWL_READ(pcwl_p, WL_RX_FID, id);
13323737Shx147065 	PCWL_WRITE(pcwl_p, WL_RX_FID, 0);
13333737Shx147065 	if (id == WL_INVALID_FID) {
13343737Shx147065 		PCWLDBG((CE_NOTE, "pcwl rcv: get rx_fid failed\n"));
13353737Shx147065 		ret = PCWL_FAIL;
13363737Shx147065 		goto done;
13373737Shx147065 	}
13383737Shx147065 	if (ret = RDCH0(pcwl_p, id, 0, (uint16_t *)&frm, sizeof (frm))) {
13393737Shx147065 		PCWLDBG((CE_NOTE, "pcwl rcv: read frm failed %x\n", ret));
13403737Shx147065 		goto done;
13413737Shx147065 	}
13423737Shx147065 	if (frm.wl_status & WL_STAT_ERRSTAT) {
13433737Shx147065 		PCWLDBG((CE_NOTE, "pcwl rcv: errstat %x\n", frm.wl_status));
13443737Shx147065 		ret = frm.wl_status;
13453737Shx147065 		goto done;
13463737Shx147065 	}
13473737Shx147065 	PCWLDBG((CE_NOTE, "pcwl rcv: frame type %x\n", frm.wl_status));
13483737Shx147065 #ifdef DEBUG
13493737Shx147065 	if (pcwl_debug & PCWL_DBG_RCV) {
13503737Shx147065 		int i;
13513737Shx147065 		cmn_err(CE_NOTE, "pcwl rcv: frm header\n");
13523737Shx147065 		for (i = 0; i < WL_802_11_HDRLEN; i++)
13533737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
13543737Shx147065 			    *((uint8_t *)&frm + i));
13553737Shx147065 	}
13563737Shx147065 #endif
13573737Shx147065 	len = frm.wl_dat_len;
13583737Shx147065 	/*
13593737Shx147065 	 * this driver deal with WEP by itself. so plugin always thinks no wep.
13603737Shx147065 	 */
13613737Shx147065 	frm.wl_frame_ctl &= ~(IEEE80211_FC1_WEP << 8);
13623737Shx147065 	frm_ctl = frm.wl_frame_ctl;
13633737Shx147065 	switch (frm.wl_status) {
13643737Shx147065 	case WL_STAT_1042:
13653737Shx147065 	case WL_STAT_TUNNEL:
13663737Shx147065 	case WL_STAT_WMP_MSG:
13673737Shx147065 		PCWL_SWAP16((uint16_t *)&frm.wl_frame_ctl,
13683737Shx147065 		    sizeof (struct ieee80211_frame));
13693737Shx147065 		/*
13703737Shx147065 		 * discard those frames which are not from the AP we connect or
13713737Shx147065 		 * without 'ap->sta' direction
13723737Shx147065 		 */
13733737Shx147065 		if ((pcwl_p->pcwl_rf.rf_porttype == WL_BSS_BSS) &&
13743737Shx147065 		    ((((frm_ctl >> 8) & IEEE80211_FC1_DIR_MASK) !=
13753737Shx147065 		    IEEE80211_FC1_DIR_FROMDS) ||
13763737Shx147065 		    bcmp(pcwl_p->pcwl_bssid, frm.wl_addr2, 6) != 0)) {
13773737Shx147065 			ret = PCWL_FAIL;
13783737Shx147065 			goto done;
13793737Shx147065 		}
13803737Shx147065 
13813737Shx147065 		bcopy(&frm.wl_frame_ctl, mp->b_wptr,
13823737Shx147065 		    sizeof (struct ieee80211_frame));
13833737Shx147065 		mp->b_wptr += sizeof (struct ieee80211_frame);
13843737Shx147065 
13853737Shx147065 		PCWL_SWAP16((uint16_t *)&frm.wl_dat[0],
13863737Shx147065 		    sizeof (struct ieee80211_llc));
13873737Shx147065 		bcopy(&frm.wl_dat[0], mp->b_wptr,
13883737Shx147065 		    sizeof (struct ieee80211_llc));
13893737Shx147065 		mp->b_wptr += sizeof (struct ieee80211_llc);
13903737Shx147065 
13913737Shx147065 		len -= (2 + WL_SNAPHDR_LEN);
13923737Shx147065 		off = WL_802_11_HDRLEN;
13933737Shx147065 		break;
13943737Shx147065 	default:
13953737Shx147065 		PCWLDBG((CE_NOTE, "pcwl rcv: incorrect pkt\n"));
13963737Shx147065 		break;
13973737Shx147065 	}
13983737Shx147065 	if (len > MBLKSIZE(mp)) {
13993737Shx147065 		PCWLDBG((CE_NOTE, "pcwl rcv: oversz pkt %x\n", len));
14003737Shx147065 		ret = PCWL_FAIL;
14013737Shx147065 		goto done;
14023737Shx147065 	}
14033737Shx147065 	if (len & 1)
14043737Shx147065 		len++;
14053737Shx147065 	ret = RDPKT(pcwl_p, id, off, (uint16_t *)mp->b_wptr, len);
14063737Shx147065 done:
14073737Shx147065 	if (ret) {
14083737Shx147065 		PCWLDBG((CE_NOTE, "pcwl rcv: rd data %x\n", ret));
14093737Shx147065 		freemsg(mp);
14103737Shx147065 		return;
14113737Shx147065 	}
14123737Shx147065 	mp->b_wptr = mp->b_wptr + len;
14133737Shx147065 #ifdef DEBUG
14143737Shx147065 	if (pcwl_debug & PCWL_DBG_RCV) {
14153737Shx147065 		int i;
14163737Shx147065 		cmn_err(CE_NOTE, "pcwl rcv: len=0x%x\n", len);
14173737Shx147065 		for (i = 0; i < len+14; i++)
14183737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
14193737Shx147065 			    *((uint8_t *)mp->b_rptr + i));
14203737Shx147065 	}
14213737Shx147065 #endif
14223737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
14233737Shx147065 	mac_rx(GLD3(pcwl_p), NULL, mp);
14243737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
14253737Shx147065 }
14263737Shx147065 
14273737Shx147065 static uint32_t
14283737Shx147065 pcwl_txdone(pcwl_maci_t *pcwl_p)
14293737Shx147065 {
14303737Shx147065 	uint16_t fid, i;
14313737Shx147065 	PCWL_READ(pcwl_p, WL_ALLOC_FID, fid);
14323737Shx147065 	PCWL_WRITE(pcwl_p, WL_ALLOC_FID, 0);
14333737Shx147065 
14343737Shx147065 	mutex_enter(&pcwl_p->pcwl_txring.wl_tx_lock);
14353737Shx147065 	for (i = 0; i < WL_XMT_BUF_NUM; i++) {
14363737Shx147065 		if (fid == pcwl_p->pcwl_txring.wl_tx_ring[i]) {
14373737Shx147065 			pcwl_p->pcwl_txring.wl_tx_ring[i] = 0;
14383737Shx147065 			break;
14393737Shx147065 		}
14403737Shx147065 	}
14413737Shx147065 	pcwl_p->pcwl_txring.wl_tx_cons =
14423737Shx147065 	    (pcwl_p->pcwl_txring.wl_tx_cons + 1) & (WL_XMT_BUF_NUM - 1);
14433737Shx147065 	mutex_exit(&pcwl_p->pcwl_txring.wl_tx_lock);
14443737Shx147065 	if (i == WL_XMT_BUF_NUM)
14453737Shx147065 		return (PCWL_FAIL);
14463737Shx147065 	return (PCWL_SUCCESS);
14473737Shx147065 
14483737Shx147065 }
14493737Shx147065 
14503737Shx147065 static void
14513737Shx147065 pcwl_infodone(pcwl_maci_t *pcwl_p)
14523737Shx147065 {
14533737Shx147065 	uint16_t id, ret, i;
14543737Shx147065 	uint16_t linkStatus[2];
14553737Shx147065 	uint16_t linkStat;
14563737Shx147065 	wifi_data_t wd = { 0 };
14573737Shx147065 
14583737Shx147065 	PCWL_READ(pcwl_p, WL_INFO_FID, id);
14593737Shx147065 	if (id == WL_INVALID_FID) {
14603737Shx147065 		cmn_err(CE_WARN, "pcwl infodone: read info_fid failed\n");
14613737Shx147065 		return;
14623737Shx147065 	}
14633737Shx147065 	if (ret = RDCH0(pcwl_p, id, 0, linkStatus, sizeof (linkStatus))) {
14643737Shx147065 		PCWLDBG((CE_WARN, "pcwl infodone read infoFrm failed %x\n",
14653737Shx147065 		    ret));
14663737Shx147065 		return;
14673737Shx147065 	}
14683737Shx147065 	PCWLDBG((CE_NOTE, "pcwl infodone: Frame length= %x, Frame Type = %x\n",
14693737Shx147065 	    linkStatus[0], linkStatus[1]));
14703737Shx147065 
14713737Shx147065 	switch (linkStatus[1]) {
14723737Shx147065 	case WL_INFO_LINK_STAT:
14733737Shx147065 		(void) RDCH0(pcwl_p, id, sizeof (linkStatus), &linkStat,
14743737Shx147065 		    sizeof (linkStat));
14753737Shx147065 		PCWLDBG((CE_NOTE, "pcwl infodone: link status=%x\n", linkStat));
14763737Shx147065 		if (!(pcwl_p->pcwl_flag & PCWL_CARD_LINKUP) &&
14773737Shx147065 		    linkStat == WL_LINK_CONNECT) {
14783737Shx147065 #ifdef DEBUG
14793737Shx147065 		if (pcwl_debug & PCWL_DBG_LINKINFO)
14803737Shx147065 			cmn_err(CE_NOTE, "pcwl: Link up \n");
14813737Shx147065 #endif
14823737Shx147065 			pcwl_p->pcwl_flag |= PCWL_CARD_LINKUP;
14833737Shx147065 			mutex_exit(&pcwl_p->pcwl_glock);
14843737Shx147065 			if (pcwl_p->pcwl_connect_timeout_id != 0) {
14853737Shx147065 				(void) untimeout(pcwl_p->
14863737Shx147065 				    pcwl_connect_timeout_id);
14873737Shx147065 				pcwl_p->pcwl_connect_timeout_id = 0;
14883737Shx147065 			}
14893737Shx147065 			mutex_enter(&pcwl_p->pcwl_glock);
14903737Shx147065 			mac_link_update(GLD3(pcwl_p), LINK_STATE_UP);
14913737Shx147065 			(void) pcwl_get_ltv(pcwl_p, 6,
14923737Shx147065 			    WL_RID_BSSID, (uint16_t *)pcwl_p->pcwl_bssid);
14933737Shx147065 			PCWL_SWAP16((uint16_t *)pcwl_p->pcwl_bssid, 6);
14943737Shx147065 			pcwl_get_rssi(pcwl_p);
14953737Shx147065 			bcopy(pcwl_p->pcwl_bssid, wd.wd_bssid, 6);
14963737Shx147065 			wd.wd_secalloc = WIFI_SEC_NONE;
14973737Shx147065 			wd.wd_opmode = IEEE80211_M_STA;
14983737Shx147065 			(void) mac_pdata_update(pcwl_p->pcwl_mh, &wd,
14993737Shx147065 			    sizeof (wd));
15003737Shx147065 		}
15013737Shx147065 		if ((pcwl_p->pcwl_flag & PCWL_CARD_LINKUP) &&
15023737Shx147065 		    ((linkStat == WL_LINK_DISCONNECT) ||
15033737Shx147065 		    (linkStat == WL_LINK_AP_OOR))) {
15043737Shx147065 #ifdef DEBUG
15053737Shx147065 		if (pcwl_debug & PCWL_DBG_LINKINFO)
15063737Shx147065 			cmn_err(CE_NOTE, "pcwl: Link down \n");
15073737Shx147065 #endif
15083737Shx147065 			PCWLDBG((CE_NOTE, "pcwl infodone: link status = %d\n",
15093737Shx147065 			    linkStat));
15103737Shx147065 			pcwl_p->pcwl_flag &= ~PCWL_CARD_LINKUP;
15113737Shx147065 			if (linkStat == WL_LINK_AP_OOR)
15123737Shx147065 				pcwl_p->pcwl_connect_timeout_id =
15133737Shx147065 				    timeout(pcwl_connect_timeout,
15143737Shx147065 				    pcwl_p, drv_usectohz(1000));
15153737Shx147065 			mutex_exit(&pcwl_p->pcwl_glock);
15163737Shx147065 			mac_link_update(GLD3(pcwl_p), LINK_STATE_DOWN);
15173737Shx147065 			mutex_enter(&pcwl_p->pcwl_glock);
15183737Shx147065 		}
15193737Shx147065 		break;
15203737Shx147065 	case WL_INFO_SCAN_RESULTS:
15213737Shx147065 	case WL_INFO_HSCAN_RESULTS:
15223737Shx147065 		pcwl_ssid_scan(pcwl_p, id, linkStatus[0], linkStatus[1]);
15233737Shx147065 			break;
15243737Shx147065 	case WL_INFO_COUNTERS:
15253737Shx147065 		linkStatus[0]--;
15263737Shx147065 		if (linkStatus[0] > WLC_STAT_CNT) {
15273737Shx147065 			linkStatus[0] = MIN(linkStatus[0], WLC_STAT_CNT);
15283737Shx147065 		}
15293737Shx147065 		(void) RDCH0(pcwl_p, id, sizeof (linkStatus),
15303737Shx147065 		    pcwl_p->pcwl_cntrs_t, linkStatus[0]<<1);
15313737Shx147065 		/*
15323737Shx147065 		 * accumulate all the statistics items for kstat use.
15333737Shx147065 		 */
15343737Shx147065 		for (i = 0; i < WLC_STAT_CNT; i++)
15353737Shx147065 			pcwl_p->pcwl_cntrs_s[i] += pcwl_p->pcwl_cntrs_t[i];
15363737Shx147065 		break;
15373737Shx147065 	default:
15383737Shx147065 		break;
15393737Shx147065 	}
15403737Shx147065 }
15413737Shx147065 
15423737Shx147065 static uint16_t
15433737Shx147065 pcwl_set_cmd(pcwl_maci_t *pcwl_p, uint16_t cmd, uint16_t param)
15443737Shx147065 {
15453737Shx147065 	int i;
15463737Shx147065 	uint16_t stat;
15473737Shx147065 
15483737Shx147065 	if (((cmd == WL_CMD_ENABLE) &&
15493737Shx147065 	    ((pcwl_p->pcwl_flag & PCWL_ENABLED) != 0)) ||
15503737Shx147065 	    ((cmd == WL_CMD_DISABLE) &&
15513737Shx147065 	    ((pcwl_p->pcwl_flag & PCWL_ENABLED) == 0)))
15523737Shx147065 		return (PCWL_SUCCESS);
15533737Shx147065 
15543737Shx147065 	for (i = 0; i < WL_TIMEOUT; i++) {
15553737Shx147065 		PCWL_READ(pcwl_p, WL_COMMAND, stat);
15563737Shx147065 		if (stat & WL_CMD_BUSY) {
15573737Shx147065 			drv_usecwait(1);
15583737Shx147065 		} else {
15593737Shx147065 			break;
15603737Shx147065 		}
15613737Shx147065 	}
15623737Shx147065 	if (i == WL_TIMEOUT) {
15633737Shx147065 		cmn_err(CE_WARN, "pcwl: setcmd %x, %x timeout %x due to "
15643737Shx147065 		    "busy bit\n", cmd, param, stat);
15653737Shx147065 		return (PCWL_TIMEDOUT_CMD);
15663737Shx147065 	}
15673737Shx147065 
15683737Shx147065 	PCWL_WRITE(pcwl_p, WL_PARAM0, param);
15693737Shx147065 	PCWL_WRITE(pcwl_p, WL_PARAM1, 0);
15703737Shx147065 	PCWL_WRITE(pcwl_p, WL_PARAM2, 0);
15713737Shx147065 	PCWL_WRITE(pcwl_p, WL_COMMAND, cmd);
15723737Shx147065 	if (cmd == WL_CMD_INI)
15733737Shx147065 		drv_usecwait(100000); /* wait .1 sec */
15743737Shx147065 
15753737Shx147065 	for (i = 0; i < WL_TIMEOUT; i++) {
15763737Shx147065 		PCWL_READ(pcwl_p, WL_EVENT_STAT, stat);
15773737Shx147065 		if (!(stat & WL_EV_CMD)) {
15783737Shx147065 			drv_usecwait(1);
15793737Shx147065 		} else {
15803737Shx147065 			break;
15813737Shx147065 		}
15823737Shx147065 	}
15833737Shx147065 	if (i == WL_TIMEOUT) {
15843737Shx147065 		cmn_err(CE_WARN, "pcwl: setcmd %x,%x timeout %x\n",
15853737Shx147065 		    cmd, param, stat);
15863737Shx147065 		if (stat & (WL_EV_ALLOC | WL_EV_RX))
15873737Shx147065 			PCWL_WRITE(pcwl_p, WL_EVENT_ACK, stat);
15883737Shx147065 		return (PCWL_TIMEDOUT_CMD);
15893737Shx147065 	}
15903737Shx147065 	PCWL_READ(pcwl_p, WL_STATUS, stat);
15913737Shx147065 	PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_CMD);
15923737Shx147065 	if (stat & WL_STAT_CMD_RESULT) { /* err in feedback status */
15933737Shx147065 		cmn_err(CE_WARN, "pcwl: set_cmd %x,%x failed %x\n",
15943737Shx147065 		    cmd, param, stat);
15953737Shx147065 		return (PCWL_FAILURE_CMD);
15963737Shx147065 	}
15973737Shx147065 	if (cmd == WL_CMD_ENABLE)
15983737Shx147065 		pcwl_p->pcwl_flag |= PCWL_ENABLED;
15993737Shx147065 	if (cmd == WL_CMD_DISABLE)
16003737Shx147065 		pcwl_p->pcwl_flag &= (~PCWL_ENABLED);
16013737Shx147065 	return (PCWL_SUCCESS);
16023737Shx147065 }
16033737Shx147065 
16043737Shx147065 static uint16_t
16053737Shx147065 pcwl_set_ch(pcwl_maci_t *pcwl_p, uint16_t type, uint16_t off, uint16_t channel)
16063737Shx147065 {
16073737Shx147065 	int i;
16083737Shx147065 	uint16_t stat, select, offset;
16093737Shx147065 
16103737Shx147065 	if (channel) {
16113737Shx147065 		select = WL_SEL1;
16123737Shx147065 		offset = WL_OFF1;
16133737Shx147065 	} else {
16143737Shx147065 		select = WL_SEL0;
16153737Shx147065 		offset = WL_OFF0;
16163737Shx147065 	}
16173737Shx147065 	PCWL_WRITE(pcwl_p, select, type);
16183737Shx147065 	PCWL_WRITE(pcwl_p, offset, off);
16193737Shx147065 	for (stat = 0, i = 0; i < WL_TIMEOUT; i++) {
16203737Shx147065 		PCWL_READ(pcwl_p, offset, stat);
16213737Shx147065 		if (!(stat & (WL_OFF_BUSY|WL_OFF_ERR)))
16223737Shx147065 			break;
16233737Shx147065 		else {
16243737Shx147065 			drv_usecwait(1);
16253737Shx147065 		}
16263737Shx147065 	}
16273737Shx147065 	if (i == WL_TIMEOUT) {
16283737Shx147065 		cmn_err(CE_WARN, "set_ch%d %x,%x failed %x\n",
16293737Shx147065 		    channel, type, off, stat);
16303737Shx147065 		return (PCWL_TIMEDOUT_TARGET);
16313737Shx147065 	}
16323737Shx147065 	return (PCWL_SUCCESS);
16333737Shx147065 }
16343737Shx147065 
16353737Shx147065 static uint16_t
16363737Shx147065 pcwl_get_ltv(pcwl_maci_t *pcwl_p, uint16_t len, uint16_t type, uint16_t *val_p)
16373737Shx147065 {
16383737Shx147065 	uint16_t stat;
16393737Shx147065 
16403737Shx147065 	ASSERT(!(len & 1));
16413737Shx147065 	len >>= 1;	/* convert bytes to 16-bit words */
16423737Shx147065 
16433737Shx147065 	/*
16443737Shx147065 	 * 1. select read mode
16453737Shx147065 	 */
16463737Shx147065 	if (stat = pcwl_set_cmd(pcwl_p, WL_CMD_ACCESS | WL_ACCESS_READ, type))
16473737Shx147065 		return (stat);
16483737Shx147065 
16493737Shx147065 	/*
16503737Shx147065 	 * 2. select Buffer Access Path (channel) 1 for PIO
16513737Shx147065 	 */
16523737Shx147065 	if (stat = pcwl_set_ch(pcwl_p, type, 0, 1))
16533737Shx147065 		return (stat);
16543737Shx147065 
16553737Shx147065 	/*
16563737Shx147065 	 * 3. read length
16573737Shx147065 	 */
16583737Shx147065 	PCWL_READ(pcwl_p, WL_DATA1, stat);
16593737Shx147065 	if (stat != (len + 1)) {
16603737Shx147065 		PCWLDBG((CE_NOTE, "get_ltv 0x%x expected 0x%x+1, got 0x%x\n",
16613737Shx147065 		    type, (len + 1) << 1, stat));
16623737Shx147065 		stat = (stat >> 1) - 1;
16633737Shx147065 		len = MIN(stat, len);
16643737Shx147065 	}
16653737Shx147065 
16663737Shx147065 	/*
16673737Shx147065 	 * 4. read type
16683737Shx147065 	 */
16693737Shx147065 	PCWL_READ(pcwl_p, WL_DATA1, stat);
16703737Shx147065 	if (stat != type)
16713737Shx147065 		return (PCWL_BADTYPE);
16723737Shx147065 
16733737Shx147065 	/*
16743737Shx147065 	 * 5. read value
16753737Shx147065 	 */
16763737Shx147065 	for (stat = 0; stat < len; stat++, val_p++) {
16773737Shx147065 		PCWL_READ_P(pcwl_p, WL_DATA1, val_p, 1);
16783737Shx147065 	}
16793737Shx147065 	return (PCWL_SUCCESS);
16803737Shx147065 }
16813737Shx147065 
16823737Shx147065 static uint16_t
16833737Shx147065 pcwl_fil_ltv(pcwl_maci_t *pcwl_p, uint16_t len, uint16_t type, uint16_t val)
16843737Shx147065 {
16853737Shx147065 	uint16_t stat;
16863737Shx147065 
16873737Shx147065 	ASSERT(!(len & 1));
16883737Shx147065 
16893737Shx147065 	/*
16903737Shx147065 	 * 1. select Buffer Access Path (channel) 1 for PIO
16913737Shx147065 	 */
16923737Shx147065 	if (stat = pcwl_set_ch(pcwl_p, type, 0, 1))
16933737Shx147065 		return (stat);
16943737Shx147065 
16953737Shx147065 	/*
16963737Shx147065 	 * 2. write length
16973737Shx147065 	 */
16983737Shx147065 	len >>= 1;		/* convert bytes to 16-bit words */
16993737Shx147065 	stat = len + 1;		/* 1 extra word */
17003737Shx147065 	PCWL_WRITE(pcwl_p, WL_DATA1, stat);
17013737Shx147065 
17023737Shx147065 	/*
17033737Shx147065 	 * 3. write type
17043737Shx147065 	 */
17053737Shx147065 	PCWL_WRITE(pcwl_p, WL_DATA1, type);
17063737Shx147065 
17073737Shx147065 	/*
17083737Shx147065 	 * 4. fill value
17093737Shx147065 	 */
17103737Shx147065 	for (stat = 0; stat < len; stat++) {
17113737Shx147065 		PCWL_WRITE(pcwl_p, WL_DATA1, val);
17123737Shx147065 	}
17133737Shx147065 
17143737Shx147065 	/*
17153737Shx147065 	 * 5. select write mode
17163737Shx147065 	 */
17173737Shx147065 	return (pcwl_set_cmd(pcwl_p, WL_CMD_ACCESS|WL_ACCESS_WRITE, type));
17183737Shx147065 }
17193737Shx147065 
17203737Shx147065 static uint16_t
17213737Shx147065 pcwl_put_ltv(pcwl_maci_t *pcwl_p, uint16_t len, uint16_t type, uint16_t *val_p)
17223737Shx147065 {
17233737Shx147065 	uint16_t stat;
17243737Shx147065 
17253737Shx147065 	ASSERT(!(len & 1));
17263737Shx147065 
17273737Shx147065 	/*
17283737Shx147065 	 * 1. select Buffer Access Path (channel) 1 for PIO
17293737Shx147065 	 */
17303737Shx147065 	if (stat = pcwl_set_ch(pcwl_p, type, 0, 1))
17313737Shx147065 		return (stat);
17323737Shx147065 
17333737Shx147065 	/*
17343737Shx147065 	 * 2. write length
17353737Shx147065 	 */
17363737Shx147065 	len >>= 1;		/* convert bytes to 16-bit words */
17373737Shx147065 	stat = len + 1;		/* 1 extra word */
17383737Shx147065 	PCWL_WRITE(pcwl_p, WL_DATA1, stat);
17393737Shx147065 
17403737Shx147065 	/*
17413737Shx147065 	 * 3. write type
17423737Shx147065 	 */
17433737Shx147065 	PCWL_WRITE(pcwl_p, WL_DATA1, type);
17443737Shx147065 
17453737Shx147065 	/*
17463737Shx147065 	 * 4. write value
17473737Shx147065 	 */
17483737Shx147065 	for (stat = 0; stat < len; stat++, val_p++) {
17493737Shx147065 		PCWL_WRITE_P(pcwl_p, WL_DATA1, val_p, 1);
17503737Shx147065 	}
17513737Shx147065 
17523737Shx147065 	/*
17533737Shx147065 	 * 5. select write mode
17543737Shx147065 	 */
17553737Shx147065 	return (pcwl_set_cmd(pcwl_p, WL_CMD_ACCESS|WL_ACCESS_WRITE, type));
17563737Shx147065 }
17573737Shx147065 
17583737Shx147065 #define	PCWL_COMPSTR_LEN	34
17593737Shx147065 static uint16_t
17603737Shx147065 pcwl_put_str(pcwl_maci_t *pcwl_p, uint16_t type, char *str_p)
17613737Shx147065 {
17623737Shx147065 	uint16_t buf[PCWL_COMPSTR_LEN / 2];
17633737Shx147065 	uint8_t str_len = strlen(str_p);
17643737Shx147065 
17653737Shx147065 	bzero(buf, PCWL_COMPSTR_LEN);
17663737Shx147065 	buf[0] = str_len;
17673737Shx147065 	bcopy(str_p, (caddr_t)(buf + 1), str_len);
17683737Shx147065 	PCWLDBG((CE_NOTE, "put_str: buf[0]=%x buf=%s\n",
17693737Shx147065 	    buf[0], (caddr_t)(buf + 1)));
17703737Shx147065 	PCWL_SWAP16(buf + 1, PCWL_COMPSTR_LEN - 2);
17713737Shx147065 	return (pcwl_put_ltv(pcwl_p, PCWL_COMPSTR_LEN, type, buf));
17723737Shx147065 }
17733737Shx147065 
17743737Shx147065 /*ARGSUSED*/
17753737Shx147065 static uint16_t
17763737Shx147065 pcwl_rdch0(pcwl_maci_t *pcwl_p, uint16_t type, uint16_t off, uint16_t *buf_p,
17773737Shx147065 	int len, int order)
17783737Shx147065 {
17793737Shx147065 	uint16_t o;
17803737Shx147065 	ASSERT(!(len & 1));
17813737Shx147065 	/*
17823737Shx147065 	 * It seems that for PrismII chip, frequently overlap use of path0
17833737Shx147065 	 * and path1 may hang the hardware. So for PrismII chip, just use
17843737Shx147065 	 * path1. Test proves this workaround is OK.
17853737Shx147065 	 */
17863737Shx147065 	if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) {
17873737Shx147065 		if (type = pcwl_set_ch(pcwl_p, type, off, 1))
17883737Shx147065 			return (type);
17893737Shx147065 		o = WL_DATA1;
17903737Shx147065 	} else {
17913737Shx147065 		if (type = pcwl_set_ch(pcwl_p, type, off, 0))
17923737Shx147065 			return (type);
17933737Shx147065 		o = WL_DATA0;
17943737Shx147065 	}
17953737Shx147065 	len >>= 1;
17963737Shx147065 	for (off = 0; off < len; off++, buf_p++) {
17973737Shx147065 		PCWL_READ_P(pcwl_p, o, buf_p, order);
17983737Shx147065 	}
17993737Shx147065 	return (PCWL_SUCCESS);
18003737Shx147065 }
18013737Shx147065 
18023737Shx147065 /*ARGSUSED*/
18033737Shx147065 static uint16_t
18043737Shx147065 pcwl_wrch1(pcwl_maci_t *pcwl_p, uint16_t type, uint16_t off, uint16_t *buf_p,
18053737Shx147065 	int len, int order)
18063737Shx147065 {
18073737Shx147065 	ASSERT(!(len & 1));
18083737Shx147065 	if (type = pcwl_set_ch(pcwl_p, type, off, 1))
18093737Shx147065 		return (type);
18103737Shx147065 	len >>= 1;
18113737Shx147065 	for (off = 0; off < len; off++, buf_p++) {
18123737Shx147065 		PCWL_WRITE_P(pcwl_p, WL_DATA1, buf_p, order);
18133737Shx147065 	}
18143737Shx147065 	return (PCWL_SUCCESS);
18153737Shx147065 }
18163737Shx147065 
18173737Shx147065 static uint16_t
18183737Shx147065 pcwl_alloc_nicmem(pcwl_maci_t *pcwl_p, uint16_t len, uint16_t *id_p)
18193737Shx147065 {
18203737Shx147065 	int i;
18213737Shx147065 	uint16_t stat;
18223737Shx147065 
18233737Shx147065 	len = ((len + 1) >> 1) << 1;	/* round up to 16-bit boundary */
18243737Shx147065 
18253737Shx147065 	if (stat = pcwl_set_cmd(pcwl_p, WL_CMD_ALLOC_MEM, len))
18263737Shx147065 		return (stat);
18273737Shx147065 	for (stat = 0, i = 0; i < WL_TIMEOUT; i++) {
18283737Shx147065 		PCWL_READ(pcwl_p, WL_EVENT_STAT, stat);
18293737Shx147065 		if (stat & WL_EV_ALLOC)
18303737Shx147065 			break;
18313737Shx147065 		else
18323737Shx147065 			drv_usecwait(1);
18333737Shx147065 	}
18343737Shx147065 	if (i == WL_TIMEOUT)
18353737Shx147065 		return (PCWL_TIMEDOUT_ALLOC);
18363737Shx147065 	PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_ALLOC);
18373737Shx147065 	PCWL_READ(pcwl_p, WL_ALLOC_FID, stat);
18383737Shx147065 	*id_p = stat;
18393737Shx147065 
18403737Shx147065 	/*
18413737Shx147065 	 * zero fill the allocated NIC mem - sort of pcwl_fill_ch
18423737Shx147065 	 */
18433737Shx147065 	(void) pcwl_set_ch(pcwl_p, stat, 0, 1);
18443737Shx147065 
18453737Shx147065 	for (len >>= 1, stat = 0; stat < len; stat++) {
18463737Shx147065 		PCWL_WRITE(pcwl_p, WL_DATA1, 0);
18473737Shx147065 	}
18483737Shx147065 	return (PCWL_SUCCESS);
18493737Shx147065 }
18503737Shx147065 
18513737Shx147065 static int
18523737Shx147065 pcwl_add_scan_item(pcwl_maci_t *pcwl_p, wl_scan_result_t s)
18533737Shx147065 {
18543737Shx147065 	wl_scan_list_t *scan_item;
18553737Shx147065 
18563737Shx147065 	scan_item = kmem_zalloc(sizeof (wl_scan_list_t), KM_SLEEP);
18573737Shx147065 	if (scan_item == NULL) {
18583737Shx147065 		cmn_err(CE_WARN, "pcwl add_scan_item: zalloc failed\n");
18593737Shx147065 		return (PCWL_FAIL);
18603737Shx147065 	}
18613737Shx147065 	scan_item->wl_val = s;
18623737Shx147065 	scan_item->wl_timeout = WL_SCAN_TIMEOUT_MAX;
18633737Shx147065 	list_insert_tail(&pcwl_p->pcwl_scan_list, scan_item);
18643737Shx147065 	pcwl_p->pcwl_scan_num++;
18653737Shx147065 	return (PCWL_SUCCESS);
18663737Shx147065 }
18673737Shx147065 
18683737Shx147065 static void
18693737Shx147065 pcwl_delete_scan_item(pcwl_maci_t *pcwl_p, wl_scan_list_t *s)
18703737Shx147065 {
18713737Shx147065 	list_remove(&pcwl_p->pcwl_scan_list, s);
18723737Shx147065 	kmem_free(s, sizeof (*s));
18733737Shx147065 	pcwl_p->pcwl_scan_num--;
18743737Shx147065 }
18753737Shx147065 
18763737Shx147065 static void
18773737Shx147065 pcwl_scanlist_timeout(void *arg)
18783737Shx147065 {
18793737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
18803737Shx147065 	wl_scan_list_t *scan_item0, *scan_item1;
18813737Shx147065 
18823737Shx147065 	mutex_enter(&pcwl_p->pcwl_scanlist_lock);
18833737Shx147065 	scan_item0 = list_head(&pcwl_p->pcwl_scan_list);
18843737Shx147065 	for (; scan_item0; ) {
18853737Shx147065 		PCWLDBG((CE_NOTE, "ssid = %s\n",
18863737Shx147065 		    scan_item0->wl_val.wl_srt_ssid));
18873737Shx147065 		PCWLDBG((CE_NOTE, "timeout left: %ds",
18883737Shx147065 		    scan_item0->wl_timeout));
18893737Shx147065 		scan_item1 = list_next(&pcwl_p->pcwl_scan_list, scan_item0);
18903737Shx147065 		if (scan_item0->wl_timeout == 0) {
18913737Shx147065 			pcwl_delete_scan_item(pcwl_p, scan_item0);
18923737Shx147065 		} else {
18933737Shx147065 			scan_item0->wl_timeout--;
18943737Shx147065 		}
18953737Shx147065 		scan_item0 = scan_item1;
18963737Shx147065 	}
18973737Shx147065 	mutex_exit(&pcwl_p->pcwl_scanlist_lock);
18983737Shx147065 	pcwl_p->pcwl_scanlist_timeout_id = timeout(pcwl_scanlist_timeout,
18993737Shx147065 	    pcwl_p, drv_usectohz(1000000));
19003737Shx147065 }
19013737Shx147065 
19023737Shx147065 static void
19033737Shx147065 pcwl_get_rssi(pcwl_maci_t *pcwl_p)
19043737Shx147065 {
19053737Shx147065 	wl_scan_list_t *scan_item0;
19063737Shx147065 	uint16_t cq[3];
19073737Shx147065 
19083737Shx147065 	bzero(cq, sizeof (cq));
19093737Shx147065 	mutex_enter(&pcwl_p->pcwl_scanlist_lock);
19103737Shx147065 	scan_item0 = list_head(&pcwl_p->pcwl_scan_list);
19113737Shx147065 	for (; scan_item0; ) {
19123737Shx147065 		if (bcmp(scan_item0->wl_val.wl_srt_bssid,
19133737Shx147065 		    pcwl_p->pcwl_bssid, 6) == 0) {
19143737Shx147065 			pcwl_p->pcwl_rssi = scan_item0->wl_val.wl_srt_sl;
19153737Shx147065 		}
19163737Shx147065 		scan_item0 = list_next(&pcwl_p->pcwl_scan_list, scan_item0);
19173737Shx147065 	}
19183737Shx147065 	mutex_exit(&pcwl_p->pcwl_scanlist_lock);
19193737Shx147065 	if (!pcwl_p->pcwl_rssi) {
19203737Shx147065 		(void) pcwl_get_ltv(pcwl_p, 6, WL_RID_COMMQUAL, cq);
19213737Shx147065 		pcwl_p->pcwl_rssi = cq[1];
19223737Shx147065 	}
19233737Shx147065 }
19243737Shx147065 
19253737Shx147065 /*
19263737Shx147065  * Note:
19273737Shx147065  * PrismII chipset has 2 extra space for the reason why scan is initiated
19283737Shx147065  */
19293737Shx147065 static void
19303737Shx147065 pcwl_ssid_scan(pcwl_maci_t *pcwl_p, uint16_t fid, uint16_t flen, uint16_t stype)
19313737Shx147065 {
19323737Shx147065 	uint16_t stat;
19333737Shx147065 	uint16_t ssidNum, i;
19343737Shx147065 	uint16_t off, szbuf;
19353737Shx147065 	uint16_t tmp[2];
19363737Shx147065 	wl_scan_list_t *scan_item0;
19373737Shx147065 	uint32_t check_num;
19383737Shx147065 	uint8_t	bssid_t[6];
19393737Shx147065 
19403737Shx147065 	wl_scan_result_t sctbl;
19413737Shx147065 
19423737Shx147065 	off = sizeof (uint16_t) * 2;
19433737Shx147065 	switch (pcwl_p->pcwl_chip_type) {
19443737Shx147065 	case PCWL_CHIP_PRISMII:
19453737Shx147065 		(void) RDCH0(pcwl_p, fid, off, tmp, 4);
19463737Shx147065 		off += 4;
19473737Shx147065 		szbuf = (stype == WL_INFO_SCAN_RESULTS ? 31 : 32);
19483737Shx147065 		PCWLDBG((CE_NOTE, "pcwl ssid_scan: PRISM chip\n"));
19493737Shx147065 		break;
19503737Shx147065 	case PCWL_CHIP_LUCENT:
19513737Shx147065 		PCWLDBG((CE_NOTE, "pcwl ssid_scan LUCENT chip\n"));
19523737Shx147065 	default:
19533737Shx147065 		szbuf = 25;
19543737Shx147065 	}
19553737Shx147065 
19563737Shx147065 	flen = flen + 1 - (off >> 1);
19573737Shx147065 	ssidNum = flen/szbuf;
19583737Shx147065 	ssidNum = min(WL_SRT_MAX_NUM, ssidNum);
19593737Shx147065 
19603737Shx147065 	PCWLDBG((CE_NOTE, "pcwl: ssid_scan frame length = %d\n", flen));
19613737Shx147065 
19623737Shx147065 	PCWLDBG((CE_NOTE, "pcwl ssid_scan: %d ssid(s) available", ssidNum));
19633737Shx147065 
19643737Shx147065 	bzero(bssid_t, sizeof (bssid_t));
19653737Shx147065 	for (i = 0; i < ssidNum; i++) {
19663737Shx147065 		(void) RDCH0(pcwl_p, fid, off, (uint16_t *)&sctbl, 2*szbuf);
19673737Shx147065 
19683737Shx147065 #ifdef DEBUG
19693737Shx147065 		if (pcwl_debug & PCWL_DBG_INFO) {
19703737Shx147065 			int j;
19713737Shx147065 			for (j = 0; j < sizeof (sctbl); j++)
19723737Shx147065 				cmn_err(CE_NOTE, "%d: %x\n", j,
19733737Shx147065 				    *((uint8_t *)&sctbl + j));
19743737Shx147065 		}
19753737Shx147065 #endif
19763737Shx147065 
19773737Shx147065 		off += (szbuf << 1);
19783737Shx147065 		stat = min(sctbl.wl_srt_ssidlen, 31);
19793737Shx147065 		PCWL_SWAP16((uint16_t *)(sctbl.wl_srt_bssid), 6);
19803737Shx147065 		PCWL_SWAP16((uint16_t *)(sctbl.wl_srt_ssid), stat);
19813737Shx147065 		sctbl.wl_srt_ssid[stat] = '\0';
19823737Shx147065 		sctbl.wl_srt_sl &= 0x7f;
19833737Shx147065 
19843737Shx147065 		/*
19853737Shx147065 		 * sometimes, those empty items are recorded by hardware,
19863737Shx147065 		 * this is wrong, just ignore those items here.
19873737Shx147065 		 */
19883737Shx147065 		if (bcmp(sctbl.wl_srt_bssid,
19893737Shx147065 		    bssid_t, 6) == 0) {
19903737Shx147065 			continue;
19913737Shx147065 		}
19923737Shx147065 		if (bcmp(sctbl.wl_srt_bssid,
19933737Shx147065 		    pcwl_p->pcwl_bssid, 6) == 0) {
19943737Shx147065 			pcwl_p->pcwl_rssi = sctbl.wl_srt_sl;
19953737Shx147065 		}
19963737Shx147065 		/*
19973737Shx147065 		 * save/update the scan item in scanlist
19983737Shx147065 		 */
19993737Shx147065 		mutex_enter(&pcwl_p->pcwl_scanlist_lock);
20003737Shx147065 		check_num = 0;
20013737Shx147065 		scan_item0 = list_head(&pcwl_p->pcwl_scan_list);
20023737Shx147065 		if (scan_item0 == NULL) {
20033737Shx147065 			if (pcwl_add_scan_item(pcwl_p, sctbl)
20043737Shx147065 			    != 0) {
20053737Shx147065 				mutex_exit(&pcwl_p->pcwl_scanlist_lock);
20063737Shx147065 				return;
20073737Shx147065 			}
20083737Shx147065 		}
20093737Shx147065 		for (; scan_item0; ) {
20103737Shx147065 			if (bcmp(sctbl.wl_srt_bssid,
20113737Shx147065 			    scan_item0->wl_val.wl_srt_bssid, 6) == 0) {
20123737Shx147065 				scan_item0->wl_val = sctbl;
20133737Shx147065 				scan_item0->wl_timeout = WL_SCAN_TIMEOUT_MAX;
20143737Shx147065 				break;
20153737Shx147065 			} else {
20163737Shx147065 				check_num++;
20173737Shx147065 			}
20183737Shx147065 			scan_item0 = list_next(&pcwl_p->pcwl_scan_list,
20193737Shx147065 			    scan_item0);
20203737Shx147065 		}
20213737Shx147065 		if (check_num == pcwl_p->pcwl_scan_num) {
20223737Shx147065 			if (pcwl_add_scan_item(pcwl_p, sctbl)
20233737Shx147065 			    != 0) {
20243737Shx147065 				mutex_exit(&pcwl_p->pcwl_scanlist_lock);
20253737Shx147065 				return;
20263737Shx147065 			}
20273737Shx147065 		}
20283737Shx147065 		mutex_exit(&pcwl_p->pcwl_scanlist_lock);
20293737Shx147065 		PCWLDBG((CE_NOTE, "pcwl ssid_scan: ssid%d = %s\n", i+1,
20303737Shx147065 		    sctbl.wl_srt_ssid));
20313737Shx147065 		PCWLDBG((CE_NOTE, "pcwl ssid_scan: channel = %d\n",
20323737Shx147065 		    sctbl.wl_srt_chid));
20333737Shx147065 		PCWLDBG((CE_NOTE, "pcwl ssid_scan: signal level= %d\n",
20343737Shx147065 		    sctbl.wl_srt_sl));
20353737Shx147065 		PCWLDBG((CE_NOTE, "pcwl ssid_scan: noise level = %d\n",
20363737Shx147065 		    sctbl.wl_srt_anl));
20373737Shx147065 		PCWLDBG((CE_NOTE, "pcwl ssid_scan: bssid%d ="
20383737Shx147065 		    " %x %x %x %x %x %x\n\n", i+1,
20393737Shx147065 		    sctbl.wl_srt_bssid[0],
20403737Shx147065 		    sctbl.wl_srt_bssid[1],
20413737Shx147065 		    sctbl.wl_srt_bssid[2],
20423737Shx147065 		    sctbl.wl_srt_bssid[3],
20433737Shx147065 		    sctbl.wl_srt_bssid[4],
20443737Shx147065 		    sctbl.wl_srt_bssid[5]));
20453737Shx147065 	}
20463737Shx147065 
20473737Shx147065 }
20483737Shx147065 
20493737Shx147065 /*
20503737Shx147065  * delay in which the mutex is not hold.
20513737Shx147065  * assuming the mutex has already been hold.
20523737Shx147065  */
20533737Shx147065 static void
20543737Shx147065 pcwl_delay(pcwl_maci_t *pcwl_p, clock_t microsecs)
20553737Shx147065 {
20563737Shx147065 	ASSERT(mutex_owned(&pcwl_p->pcwl_glock));
20573737Shx147065 
20583737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
20593737Shx147065 	delay(drv_usectohz(microsecs));
20603737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
20613737Shx147065 }
20623737Shx147065 
20633737Shx147065 static int
20643737Shx147065 pcwl_reset_backend(pcwl_maci_t *pcwl_p)
20653737Shx147065 {
20663737Shx147065 	uint16_t ret = 0;
20673737Shx147065 
20683737Shx147065 	if (ret =  pcwl_set_cmd(pcwl_p, WL_CMD_INI, 0)) {
20693737Shx147065 		return ((int)ret);
20703737Shx147065 	}
20713737Shx147065 
20723737Shx147065 	pcwl_delay(pcwl_p, 100000); /* wait .1 sec */
20733737Shx147065 
20743737Shx147065 	if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_INI, 0)) {
20753737Shx147065 		return ((int)ret);
20763737Shx147065 	}
20773737Shx147065 	pcwl_delay(pcwl_p, 100000); /* wait .1 sec */
20783737Shx147065 
20793737Shx147065 	PCWL_DISABLE_INTR(pcwl_p);
20803737Shx147065 	return (PCWL_SUCCESS);
20813737Shx147065 }
20823737Shx147065 
20833737Shx147065 
20843737Shx147065 /*
20853737Shx147065  * get card capability (WEP, default channel), setup broadcast, mac addresses
20863737Shx147065  */
20873737Shx147065 static int
20883737Shx147065 pcwl_get_cap(pcwl_maci_t *pcwl_p)
20893737Shx147065 {
20903737Shx147065 	uint16_t stat, ch_no;
20913737Shx147065 	uint16_t buf[ETHERADDRL >> 1];
20923737Shx147065 
20933737Shx147065 	bzero(buf, ETHERADDRL);
20943737Shx147065 	if (stat = pcwl_get_ltv(pcwl_p, 2, WL_RID_OWN_CHNL, &ch_no)) {
20953737Shx147065 		cmn_err(CE_CONT, "pcwl get_cap: get def channel failed"
20963737Shx147065 		    " %x\n", stat);
20973737Shx147065 		return ((int)stat);
20983737Shx147065 	}
20993737Shx147065 	if (stat = pcwl_get_ltv(pcwl_p, 2, WL_RID_WEP_AVAIL,
21003737Shx147065 	    &pcwl_p->pcwl_has_wep)) {
21013737Shx147065 		cmn_err(CE_CONT, "pcwl get_cap: get WEP capability failed"
21023737Shx147065 		    " %x\n", stat);
21033737Shx147065 		return ((int)stat);
21043737Shx147065 	}
21053737Shx147065 	if (stat = pcwl_get_ltv(pcwl_p, ETHERADDRL, WL_RID_MAC_NODE, buf)) {
21063737Shx147065 		cmn_err(CE_CONT, "pcwl get_cap: get macaddr failed"
21073737Shx147065 		    " %x\n", stat);
21083737Shx147065 		return ((int)stat);
21093737Shx147065 	}
21103737Shx147065 
21113737Shx147065 	/*
21123737Shx147065 	 * don't assume m_xxx members are 16-bit aligned
21133737Shx147065 	 */
21143737Shx147065 	PCWL_SWAP16(buf, ETHERADDRL);
21153737Shx147065 	ether_copy(buf, pcwl_p->pcwl_mac_addr);
21163737Shx147065 	return (PCWL_SUCCESS);
21173737Shx147065 }
21183737Shx147065 
21193737Shx147065 static int
21203737Shx147065 pcwl_init_nicmem(pcwl_maci_t *pcwl_p)
21213737Shx147065 {
21223737Shx147065 	uint16_t ret, i;
21233737Shx147065 	uint16_t rc;
21243737Shx147065 
21253737Shx147065 	for (i = 0; i < WL_XMT_BUF_NUM; i++) {
21263737Shx147065 		ret = pcwl_alloc_nicmem(pcwl_p, PCWL_NICMEM_SZ, &rc);
21273737Shx147065 		if (ret) {
21283737Shx147065 			cmn_err(CE_WARN,
21293737Shx147065 			    "pcwl: alloc NIC Tx buf failed %x\n", ret);
21303737Shx147065 			return (PCWL_FAIL);
21313737Shx147065 		}
21323737Shx147065 		pcwl_p->pcwl_txring.wl_tx_fids[i] = rc;
21333737Shx147065 		pcwl_p->pcwl_txring.wl_tx_ring[i] = 0;
21343737Shx147065 		PCWLDBG((CE_NOTE, "pcwl: alloc_nicmem_id[%d]=%x\n", i, rc));
21353737Shx147065 	}
21363737Shx147065 	pcwl_p->pcwl_txring.wl_tx_prod = pcwl_p->pcwl_txring.wl_tx_cons = 0;
21373737Shx147065 
21383737Shx147065 	ret = pcwl_alloc_nicmem(pcwl_p, PCWL_NICMEM_SZ, &pcwl_p->pcwl_mgmt_id);
21393737Shx147065 	if (ret) {
21403737Shx147065 		cmn_err(CE_WARN, "pcwl: alloc NIC Mgmt buf failed %x\n", ret);
21413737Shx147065 		return (PCWL_FAIL);
21423737Shx147065 	}
21433737Shx147065 	PCWLDBG((CE_NOTE, "pcwl: alloc_nicmem mgmt_id=%x\n",
21443737Shx147065 	    pcwl_p->pcwl_mgmt_id));
21453737Shx147065 	return (PCWL_SUCCESS);
21463737Shx147065 }
21473737Shx147065 
21483737Shx147065 static int
21493737Shx147065 pcwl_loaddef_rf(pcwl_maci_t *pcwl_p)
21503737Shx147065 {
21513737Shx147065 	pcwl_p->pcwl_rf.rf_max_datalen = WL_DEFAULT_DATALEN;
21523737Shx147065 	pcwl_p->pcwl_rf.rf_create_ibss = WL_DEFAULT_CREATE_IBSS;
21533737Shx147065 	pcwl_p->pcwl_rf.rf_porttype = WL_BSS_BSS;
21543737Shx147065 	pcwl_p->pcwl_rf.rf_rts_thresh = WL_DEFAULT_RTS_THRESH;
21553737Shx147065 	pcwl_p->pcwl_rf.rf_tx_rate = WL_TX_RATE_FIX_11M(pcwl_p);
21563737Shx147065 	pcwl_p->pcwl_rf.rf_pm_enabled = WL_DEFAULT_PM_ENABLED;
21573737Shx147065 	pcwl_p->pcwl_rf.rf_own_chnl = WL_DEFAULT_CHAN;
21583737Shx147065 	(void) strcpy(pcwl_p->pcwl_rf.rf_own_ssid, "");
21593737Shx147065 	(void) strcpy(pcwl_p->pcwl_rf.rf_desired_ssid, "");
21603737Shx147065 	(void) strcpy(pcwl_p->pcwl_rf.rf_nodename, "");
21613737Shx147065 	pcwl_p->pcwl_rf.rf_encryption = WL_NOENCRYPTION;
21623737Shx147065 	pcwl_p->pcwl_rf.rf_authtype = WL_OPENSYSTEM;
21633737Shx147065 	pcwl_p->pcwl_rf.rf_tx_crypt_key = WL_DEFAULT_TX_CRYPT_KEY;
21643737Shx147065 	bzero((pcwl_p->pcwl_rf.rf_ckeys), sizeof (rf_ckey_t) * 4);
21653737Shx147065 
21663737Shx147065 	pcwl_p->pcwl_rf.rf_promiscuous = 0;
21673737Shx147065 
21683737Shx147065 	return (pcwl_config_rf(pcwl_p));
21693737Shx147065 }
21703737Shx147065 
21713737Shx147065 static int
21723737Shx147065 pcwl_config_rf(pcwl_maci_t *pcwl_p)
21733737Shx147065 {
21743737Shx147065 	pcwl_rf_t *rf_p = &pcwl_p->pcwl_rf;
21753737Shx147065 	uint16_t create_ibss, porttype;
21763737Shx147065 
21773737Shx147065 	/*
21783737Shx147065 	 * Lucent card:
21793737Shx147065 	 * 0 Join ESS or IBSS; 1 Join ESS or join/create IBSS
21803737Shx147065 	 * PrismII card:
21813737Shx147065 	 * 3 Join ESS or IBSS(do not create IBSS);
21823737Shx147065 	 * 1 Join ESS or join/create IBSS
21833737Shx147065 	 */
21843737Shx147065 	create_ibss = rf_p->rf_create_ibss;
21853737Shx147065 	if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) {
21863737Shx147065 		if (rf_p->rf_create_ibss == 0)
21873737Shx147065 			create_ibss = 3;
21883737Shx147065 	}
21893737Shx147065 	/*
21903737Shx147065 	 * Lucent card:
21913737Shx147065 	 * 1 BSS; 3 pseudo IBSS(only for test,not the 802.11 IBSS)
21923737Shx147065 	 * so porttype register should always be set to 1
21933737Shx147065 	 * PrismII card:
21943737Shx147065 	 * 0 IBSS; 1 BSS; 2 WDS; 3 pseudo IBSS; 6 hostAP
21953737Shx147065 	 */
21963737Shx147065 	switch (pcwl_p->pcwl_chip_type) {
21973737Shx147065 	case PCWL_CHIP_PRISMII:
21983737Shx147065 		if (rf_p->rf_porttype == WL_BSS_BSS)
21993737Shx147065 			porttype = 1;
22003737Shx147065 		else if (rf_p->rf_porttype == WL_BSS_IBSS)
22013737Shx147065 			porttype = 0;
22023737Shx147065 		else
22033737Shx147065 			porttype = 0;
22043737Shx147065 		break;
22053737Shx147065 	case PCWL_CHIP_LUCENT:
22063737Shx147065 	default:
22073737Shx147065 		porttype = 1;
22083737Shx147065 	}
22093737Shx147065 
22103737Shx147065 
22113737Shx147065 	FIL_LTV(pcwl_p, PCWL_MCBUF_LEN, WL_RID_MCAST, 0);
22123737Shx147065 	FIL_LTV(pcwl_p, 2,	WL_RID_PROMISC,		0);
22133737Shx147065 	FIL_LTV(pcwl_p, 2,	WL_RID_TICK_TIME,	0);
22143737Shx147065 
22153737Shx147065 	FIL_LTV(pcwl_p, 2, WL_RID_MAX_DATALEN, rf_p->rf_max_datalen);
22163737Shx147065 	FIL_LTV(pcwl_p, 2, WL_RID_CREATE_IBSS, create_ibss);
22173737Shx147065 	FIL_LTV(pcwl_p, 2, WL_RID_PORTTYPE, porttype);
22183737Shx147065 	FIL_LTV(pcwl_p, 2, WL_RID_RTS_THRESH, rf_p->rf_rts_thresh);
22193737Shx147065 	FIL_LTV(pcwl_p, 2, WL_RID_TX_RATE, rf_p->rf_tx_rate);
22203737Shx147065 	FIL_LTV(pcwl_p, 2, WL_RID_SYSTEM_SCALE, rf_p->rf_system_scale);
22213737Shx147065 	FIL_LTV(pcwl_p, 2, WL_RID_PM_ENABLED, rf_p->rf_pm_enabled);
22223737Shx147065 	FIL_LTV(pcwl_p, 2, WL_RID_MAX_SLEEP, rf_p->rf_max_sleep);
22233737Shx147065 	FIL_LTV(pcwl_p, 2, WL_RID_OWN_CHNL, rf_p->rf_own_chnl);
22243737Shx147065 
22253737Shx147065 	PUT_STR(pcwl_p, WL_RID_OWN_SSID, rf_p->rf_own_ssid);
22263737Shx147065 	PUT_STR(pcwl_p, WL_RID_DESIRED_SSID, rf_p->rf_desired_ssid);
22273737Shx147065 	PUT_STR(pcwl_p, WL_RID_NODENAME, rf_p->rf_nodename);
22283737Shx147065 
22293737Shx147065 	if (!pcwl_p->pcwl_has_wep)
22303737Shx147065 		goto done;
22313737Shx147065 
22323737Shx147065 	switch (pcwl_p->pcwl_chip_type) {
22333737Shx147065 	case PCWL_CHIP_PRISMII: {
22343737Shx147065 		int i;
22353737Shx147065 
22363737Shx147065 		for (i = 0; i < 4; i++) {
22373737Shx147065 			int k_len = strlen((char *)rf_p->rf_ckeys[i].ckey_dat);
22383737Shx147065 			if (k_len == 0)
22393737Shx147065 				continue;
22403737Shx147065 			k_len = k_len > 5 ? 14 : 6;
22413737Shx147065 			PUT_LTV(pcwl_p, k_len, WL_RID_CRYPT_KEY0_P2 + i,
22423737Shx147065 			    (uint16_t *)&rf_p->rf_ckeys[i].ckey_dat);
22433737Shx147065 		}
22443737Shx147065 		FIL_LTV(pcwl_p, 2, WL_RID_TX_CRYPT_KEY_P2,
22453737Shx147065 		    rf_p->rf_tx_crypt_key);
22463737Shx147065 		FIL_LTV(pcwl_p, 2, WL_RID_AUTHTYPE_P2,
22473737Shx147065 		    rf_p->rf_authtype);
22483737Shx147065 		FIL_LTV(pcwl_p, 2, WL_RID_ENCRYPTION_P2,
22493737Shx147065 		    rf_p->rf_encryption);
22503737Shx147065 		if (pcwl_p->pcwl_rf.rf_promiscuous)
22513737Shx147065 			FIL_LTV(pcwl_p, 2, WL_RID_PROMISC, 1);
22523737Shx147065 		}
22533737Shx147065 		break;
22543737Shx147065 	case PCWL_CHIP_LUCENT:
22553737Shx147065 	default:
22563737Shx147065 		FIL_LTV(pcwl_p, 2, WL_RID_ENCRYPTION,
22573737Shx147065 		    rf_p->rf_encryption);
22583737Shx147065 		FIL_LTV(pcwl_p, 2, WL_RID_AUTHTYPE_L,
22593737Shx147065 		    rf_p->rf_authtype);
22603737Shx147065 		FIL_LTV(pcwl_p, 2, WL_RID_TX_CRYPT_KEY,
22613737Shx147065 		    rf_p->rf_tx_crypt_key);
22623737Shx147065 		PUT_LTV(pcwl_p, sizeof (rf_p->rf_ckeys),
22633737Shx147065 		    WL_RID_DEFLT_CRYPT_KEYS,
22643737Shx147065 		    (uint16_t *)rf_p->rf_ckeys);
22653737Shx147065 		break;
22663737Shx147065 	}
22673737Shx147065 done:
22683737Shx147065 	return (PCWL_SUCCESS);
22693737Shx147065 }
22703737Shx147065 
22713737Shx147065 static void
22723737Shx147065 pcwl_start_locked(pcwl_maci_t *pcwl_p)
22733737Shx147065 {
22743737Shx147065 	pcwl_p->pcwl_flag |= PCWL_CARD_INTREN;
22753737Shx147065 	PCWL_ENABLE_INTR(pcwl_p);
22763737Shx147065 }
22773737Shx147065 
22783737Shx147065 static void
22793737Shx147065 pcwl_stop_locked(pcwl_maci_t *pcwl_p)
22803737Shx147065 {
22813737Shx147065 	PCWL_DISABLE_INTR(pcwl_p);
22823737Shx147065 	pcwl_p->pcwl_flag &= (~PCWL_CARD_INTREN);
22833737Shx147065 	PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX|WL_EV_RX|WL_EV_TX_EXC|
22843737Shx147065 	    WL_EV_ALLOC|WL_EV_INFO|WL_EV_INFO_DROP);
22853737Shx147065 	PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX|WL_EV_RX|WL_EV_TX_EXC|
22863737Shx147065 	    WL_EV_ALLOC| WL_EV_INFO|WL_EV_INFO_DROP);
22873737Shx147065 }
22883737Shx147065 
22893737Shx147065 /*ARGSUSED*/
22903737Shx147065 static int
22913737Shx147065 pcwl_saddr_locked(pcwl_maci_t *pcwl_p)
22923737Shx147065 {
22933737Shx147065 	int ret;
22943737Shx147065 	uint16_t buf[ETHERADDRL >> 1];
22953737Shx147065 
22963737Shx147065 	ether_copy(pcwl_p->pcwl_mac_addr, buf);
22973737Shx147065 	PCWL_SWAP16(buf, ETHERADDRL);
22983737Shx147065 	ret = pcwl_put_ltv(pcwl_p, ETHERADDRL, WL_RID_MAC_NODE, buf);
22993737Shx147065 	if (ret) {
23003737Shx147065 		cmn_err(CE_WARN, "pcwl set_mac_addr: failed %x\n", ret);
23013737Shx147065 		return (PCWL_FAIL);
23023737Shx147065 	}
23033737Shx147065 	return (PCWL_SUCCESS);
23043737Shx147065 }
23053737Shx147065 
23063737Shx147065 static void
23073737Shx147065 pcwl_chip_type(pcwl_maci_t *pcwl_p)
23083737Shx147065 {
23093737Shx147065 	pcwl_ltv_ver_t ver;
23103737Shx147065 	pcwl_ltv_fwver_t f;
23113737Shx147065 
23123737Shx147065 	bzero(&ver, sizeof (ver));
23133737Shx147065 	(void) pcwl_get_ltv(pcwl_p, sizeof (ver),
23143737Shx147065 	    WL_RID_CARD_ID, (uint16_t *)&ver);
23153737Shx147065 	PCWLDBG((CE_NOTE, "card id:%04x-%04x-%04x-%04x\n",
23163737Shx147065 	    ver.wl_compid, ver.wl_variant, ver.wl_major, ver.wl_minor));
23173737Shx147065 	if ((ver.wl_compid & 0xf000) != 0x8000)
23183737Shx147065 		return;	/* lucent */
23193737Shx147065 
23203737Shx147065 	pcwl_p->pcwl_chip_type = PCWL_CHIP_PRISMII;
23213737Shx147065 	(void) pcwl_get_ltv(pcwl_p, sizeof (ver), WL_RID_COMP_IDENT,
23223737Shx147065 	    (uint16_t *)&ver);
23233737Shx147065 	PCWLDBG((CE_NOTE, "PRISM-II ver:%04x-%04x-%04x-%04x\n",
23243737Shx147065 	    ver.wl_compid, ver.wl_variant, ver.wl_major, ver.wl_minor));
23253737Shx147065 
23263737Shx147065 	bzero(&f, sizeof (f));
23273737Shx147065 	(void) pcwl_get_ltv(pcwl_p, sizeof (f), WL_RID_FWVER, (uint16_t *)&f);
23283737Shx147065 	PCWL_SWAP16((uint16_t *)&f, sizeof (f));
23293737Shx147065 	PCWLDBG((CE_NOTE, "Firmware Pri:%s 2,3:%s\n",
23303737Shx147065 	    (char *)f.pri, (char *)f.st));
23313737Shx147065 }
23323737Shx147065 
23333737Shx147065 /*
23343737Shx147065  * for wificonfig and dladm ioctl
23353737Shx147065  */
23363737Shx147065 
23373737Shx147065 static int
23383737Shx147065 pcwl_cfg_essid(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
23393737Shx147065 {
23403737Shx147065 	char ssid[36];
23413737Shx147065 	uint16_t ret, i;
23423737Shx147065 	uint16_t val;
23433737Shx147065 	pcwl_rf_t *rf_p;
23443737Shx147065 	char *value;
23453737Shx147065 	wldp_t	*infp;
23463737Shx147065 	wldp_t *outfp;
23473737Shx147065 	char *buf;
23483737Shx147065 	int iret;
23493737Shx147065 
23503737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
23513737Shx147065 	if (buf == NULL) {
23523737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
23533737Shx147065 		    MAX_BUF_LEN));
23543737Shx147065 		return (ENOMEM);
23553737Shx147065 	}
23563737Shx147065 	outfp = (wldp_t *)buf;
23573737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
23583737Shx147065 	infp = (wldp_t *)mp->b_rptr;
23593737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
23603737Shx147065 
23613737Shx147065 	bzero(ssid, sizeof (ssid));
23623737Shx147065 	if (cmd == WLAN_GET_PARAM) {
23633737Shx147065 		ret =  pcwl_get_ltv(pcwl_p, 2,
23643737Shx147065 		    WL_RID_PORTSTATUS, &val);
23653737Shx147065 		if (ret) {
23663737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
23673737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
23683737Shx147065 			PCWLDBG((CE_WARN, "cfg_essid_get_error\n"));
23693737Shx147065 			goto done;
23703737Shx147065 		}
23713737Shx147065 		PCWLDBG((CE_NOTE, "PortStatus = %d\n", val));
23723737Shx147065 
23733737Shx147065 		if (val == WL_PORT_DISABLED || val == WL_PORT_INITIAL) {
23743737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET +
23753737Shx147065 			    offsetof(wl_essid_t, wl_essid_essid) +
23763737Shx147065 			    mi_strlen(rf_p->rf_desired_ssid);
23773737Shx147065 			((wl_essid_t *)(outfp->wldp_buf))->wl_essid_length =
23783737Shx147065 			    mi_strlen(rf_p->rf_desired_ssid);
23793737Shx147065 			bcopy(rf_p->rf_desired_ssid, buf + WIFI_BUF_OFFSET +
23803737Shx147065 			    offsetof(wl_essid_t, wl_essid_essid),
23813737Shx147065 			    mi_strlen(rf_p->rf_desired_ssid));
23823737Shx147065 		} else if (val == WL_PORT_TO_IBSS ||
23833737Shx147065 		    val == WL_PORT_TO_BSS ||
23843737Shx147065 		    val == WL_PORT_OOR) {
23853737Shx147065 			(void) pcwl_get_ltv((pcwl_p), 34,
23863737Shx147065 			    WL_RID_SSID, (uint16_t *)ssid);
23873737Shx147065 			PCWL_SWAP16((uint16_t *)(ssid+2), *(uint16_t *)ssid);
23883737Shx147065 			ssid[*(uint16_t *)ssid + 2] = '\0';
23893737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET +
23903737Shx147065 			    offsetof(wl_essid_t, wl_essid_essid) +
23913737Shx147065 			    mi_strlen(ssid+2);
23923737Shx147065 			((wl_essid_t *)(outfp->wldp_buf))->wl_essid_length =
23933737Shx147065 			    mi_strlen(ssid+2);
23943737Shx147065 			bcopy(ssid + 2, buf + WIFI_BUF_OFFSET +
23953737Shx147065 			    offsetof(wl_essid_t, wl_essid_essid),
23963737Shx147065 			    mi_strlen(ssid+2));
23973737Shx147065 		} else {
23983737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
23993737Shx147065 		}
24003737Shx147065 		outfp->wldp_result = WL_SUCCESS;
24013737Shx147065 		PCWLDBG((CE_CONT, "outfp->length=%d\n", outfp->wldp_length));
24023737Shx147065 		PCWLDBG((CE_CONT, "pcwl: get desired essid=%s\n",
24033737Shx147065 		    rf_p->rf_desired_ssid));
24043737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
24053737Shx147065 		value = ((wl_essid_t *)(infp->wldp_buf))->wl_essid_essid;
24063737Shx147065 		(void) strncpy(rf_p->rf_desired_ssid, value,
24073737Shx147065 		    MIN(32, strlen(value)));
24083737Shx147065 		rf_p->rf_desired_ssid[strlen(value)] = '\0';
24093737Shx147065 		(void) strncpy(rf_p->rf_own_ssid, value,
24103737Shx147065 		    MIN(32, strlen(value)));
24113737Shx147065 		rf_p->rf_own_ssid[strlen(value)] = '\0';
24123737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
24133737Shx147065 		outfp->wldp_result = WL_SUCCESS;
24143737Shx147065 		PCWLDBG((CE_CONT, "pcwl: set: desired essid=%s\n",
24153737Shx147065 		    rf_p->rf_desired_ssid));
24163737Shx147065 	} else {
24173737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
24183737Shx147065 		return (EINVAL);
24193737Shx147065 	}
24203737Shx147065 done:
24213737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
24223737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
24233737Shx147065 	iret = (int)(outfp->wldp_result);
24243737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
24253737Shx147065 	return (iret);
24263737Shx147065 }
24273737Shx147065 
24283737Shx147065 static int
24293737Shx147065 pcwl_cfg_bssid(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
24303737Shx147065 {
24313737Shx147065 	uint16_t ret, i;
24323737Shx147065 	int iret;
24333737Shx147065 	wldp_t *outfp;
24343737Shx147065 	char *buf;
24353737Shx147065 	uint8_t bssid[6];
24363737Shx147065 
24373737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
24383737Shx147065 	if (buf == NULL) {
24393737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
24403737Shx147065 		    MAX_BUF_LEN));
24413737Shx147065 		return (ENOMEM);
24423737Shx147065 	}
24433737Shx147065 	outfp = (wldp_t *)buf;
24443737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
24453737Shx147065 
24463737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_bssid_t);
24473737Shx147065 	if (cmd == WLAN_GET_PARAM) {
24483737Shx147065 		if (ret = pcwl_get_ltv(pcwl_p, 2,
24493737Shx147065 		    WL_RID_PORTSTATUS, &ret)) {
24503737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
24513737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
24523737Shx147065 			goto done;
24533737Shx147065 		}
24543737Shx147065 		PCWLDBG((CE_NOTE, "PortStatus = %d\n", ret));
24553737Shx147065 		if (ret == WL_PORT_DISABLED || ret == WL_PORT_INITIAL) {
24563737Shx147065 			bzero(buf + WIFI_BUF_OFFSET,
24573737Shx147065 			    sizeof (wl_bssid_t));
24583737Shx147065 		} else if (ret == WL_PORT_TO_IBSS ||
2459*6062Shx147065 		    ret == WL_PORT_TO_BSS || ret == WL_PORT_OOR) {
24603737Shx147065 			(void) pcwl_get_ltv(pcwl_p, 6,
24613737Shx147065 			    WL_RID_BSSID, (uint16_t *)bssid);
24623737Shx147065 			PCWL_SWAP16((uint16_t *)bssid, 6);
24633737Shx147065 			bcopy(bssid, buf + WIFI_BUF_OFFSET,
24643737Shx147065 			    sizeof (wl_bssid_t));
24653737Shx147065 		}
24663737Shx147065 		outfp->wldp_result = WL_SUCCESS;
24673737Shx147065 
24683737Shx147065 		PCWLDBG((CE_CONT, "pcwl_getset: bssid=%x %x %x %x %x %x\n",
24693737Shx147065 		    bssid[0], bssid[1], bssid[2],
24703737Shx147065 		    bssid[3], bssid[4], bssid[5]));
24713737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
24723737Shx147065 		outfp->wldp_result = WL_READONLY;
24733737Shx147065 	} else {
24743737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
24753737Shx147065 		return (EINVAL);
24763737Shx147065 	}
24773737Shx147065 done:
24783737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
24793737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
24803737Shx147065 	iret = (int)(outfp->wldp_result);
24813737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
24823737Shx147065 	return (iret);
24833737Shx147065 }
24843737Shx147065 
24853737Shx147065 /*ARGSUSED*/
24863737Shx147065 static int
24873737Shx147065 pcwl_cmd_scan(pcwl_maci_t *pcwl_p)
24883737Shx147065 {
24893737Shx147065 	uint16_t vall[18], ret = WL_SUCCESS;
24903737Shx147065 	pcwl_rf_t *rf_p;
24913737Shx147065 	uint32_t enable, i;
24923737Shx147065 	size_t	len;
24933737Shx147065 
24943737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
24953737Shx147065 
24963737Shx147065 	/*
24973737Shx147065 	 * The logic of this funtion is really tricky.
24983737Shx147065 	 * Firstly, the chip can only scan in BSS mode, so necessary
24993737Shx147065 	 * backup and restore is required before and after the scan
25003737Shx147065 	 * command.
25013737Shx147065 	 * Secondly, for Lucent chip, Alrealy associated with an AP
25023737Shx147065 	 * can only scan the APes on the fixed channel, so we must
25033737Shx147065 	 * set the desired_ssid as "" before scan and restore after.
25043737Shx147065 	 * Thirdly, scan cmd is effective only when the card is enabled
25053737Shx147065 	 * and any 'set' operation(such as set bsstype, ssid)must disable
25063737Shx147065 	 * the card first and then enable the card after the 'set'
25073737Shx147065 	 */
25083737Shx147065 	enable = pcwl_p->pcwl_flag & PCWL_ENABLED;
25093737Shx147065 	len = strlen(rf_p->rf_desired_ssid);
25103737Shx147065 
25113737Shx147065 	if (pcwl_p->pcwl_rf.rf_porttype != WL_BSS_BSS) {
25123737Shx147065 		if ((enable) &&
25133737Shx147065 		    (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0))) {
25143737Shx147065 			ret = (int)WL_HW_ERROR;
25153737Shx147065 			goto done;
25163737Shx147065 		}
25173737Shx147065 		FIL_LTV(pcwl_p, 2, WL_RID_PORTTYPE, WL_BSS_BSS);
25183737Shx147065 	}
25193737Shx147065 
25203737Shx147065 	if ((pcwl_p->pcwl_chip_type == PCWL_CHIP_LUCENT) && (len != 0)) {
25213737Shx147065 		if ((enable) &&
25223737Shx147065 		    (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0))) {
25233737Shx147065 			ret = (int)WL_HW_ERROR;
25243737Shx147065 			goto done;
25253737Shx147065 		}
25263737Shx147065 		PUT_STR(pcwl_p, WL_RID_DESIRED_SSID, "");
25273737Shx147065 	}
25283737Shx147065 
25293737Shx147065 	if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
25303737Shx147065 		ret = (int)WL_HW_ERROR;
25313737Shx147065 		goto done;
25323737Shx147065 	}
25333737Shx147065 	pcwl_delay(pcwl_p, 1000000);
25343737Shx147065 
25353737Shx147065 	switch (pcwl_p->pcwl_chip_type) {
25363737Shx147065 	case PCWL_CHIP_PRISMII:
25373737Shx147065 		bzero(vall, sizeof (vall));
25383737Shx147065 		vall[0] = 0x3fff; /* channel mask */
25393737Shx147065 		vall[1] = 0x1; /* tx rate */
25403737Shx147065 		for (i = 0; i < WL_MAX_SCAN_TIMES; i++) {
25413737Shx147065 			PUT_LTV(pcwl_p, sizeof (vall),
25423737Shx147065 			    WL_RID_HSCAN_REQUEST, vall);
25433737Shx147065 			pcwl_delay(pcwl_p, 1000000);
25443737Shx147065 			if (pcwl_p->pcwl_scan_num >= WL_SCAN_AGAIN_THRESHOLD)
25453737Shx147065 				break;
25463737Shx147065 		}
25473737Shx147065 		PCWLDBG((CE_NOTE, "PRISM chip\n"));
25483737Shx147065 		break;
25493737Shx147065 
25503737Shx147065 	case PCWL_CHIP_LUCENT:
25513737Shx147065 		PCWLDBG((CE_NOTE, "LUCENT chip\n"));
25523737Shx147065 	default:
25533737Shx147065 		for (i = 0; i < WL_MAX_SCAN_TIMES; i++) {
25543737Shx147065 			if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_INQUIRE,
25553737Shx147065 			    WL_INFO_SCAN_RESULTS)) {
25563737Shx147065 				ret = (int)WL_HW_ERROR;
25573737Shx147065 				goto done;
25583737Shx147065 			}
25593737Shx147065 			pcwl_delay(pcwl_p, 500000);
25603737Shx147065 			if (pcwl_p->pcwl_scan_num >= WL_SCAN_AGAIN_THRESHOLD)
25613737Shx147065 				break;
25623737Shx147065 		}
25633737Shx147065 		break;
25643737Shx147065 	}
25653737Shx147065 	if ((pcwl_p->pcwl_rf.rf_porttype != WL_BSS_BSS) ||
25663737Shx147065 	    ((pcwl_p->pcwl_chip_type == PCWL_CHIP_LUCENT) && (len != 0))) {
25673737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
25683737Shx147065 			ret = (int)WL_HW_ERROR;
25693737Shx147065 			goto done;
25703737Shx147065 		}
25713737Shx147065 		if (ret = pcwl_config_rf(pcwl_p)) {
25723737Shx147065 			ret = (int)WL_HW_ERROR;
25733737Shx147065 			goto done;
25743737Shx147065 		}
25753737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
25763737Shx147065 			ret = (int)WL_HW_ERROR;
25773737Shx147065 			goto done;
25783737Shx147065 		}
25793737Shx147065 
25803737Shx147065 		pcwl_delay(pcwl_p, 1000000);
25813737Shx147065 	}
25823737Shx147065 
25833737Shx147065 	if ((!enable) && (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0))) {
25843737Shx147065 		ret = (int)WL_HW_ERROR;
25853737Shx147065 	}
25863737Shx147065 done:
25873737Shx147065 	if (ret)
25884196Syx209050 		cmn_err(CE_WARN, "pcwl: scan failed due to hardware error");
25893737Shx147065 	return (ret);
25903737Shx147065 
25913737Shx147065 }
25923737Shx147065 
25933737Shx147065 /*ARGSUSED*/
25943737Shx147065 static int
25953737Shx147065 pcwl_cfg_scan(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
25963737Shx147065 {
25973737Shx147065 	wl_ess_conf_t *p_ess_conf;
25983737Shx147065 	wldp_t *outfp;
25993737Shx147065 	char *buf;
26003737Shx147065 	uint16_t i;
26013737Shx147065 	wl_scan_list_t *scan_item;
26023737Shx147065 
26033737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
26043737Shx147065 	if (buf == NULL) {
26053737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
26063737Shx147065 		    MAX_BUF_LEN));
26073737Shx147065 		return (ENOMEM);
26083737Shx147065 	}
26093737Shx147065 	outfp = (wldp_t *)buf;
26103737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
26113737Shx147065 
26123737Shx147065 	mutex_enter(&pcwl_p->pcwl_scanlist_lock);
26133737Shx147065 	((wl_ess_list_t *)(outfp->wldp_buf))->wl_ess_list_num =
26143737Shx147065 	    pcwl_p->pcwl_scan_num;
26153737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET +
26163737Shx147065 	    offsetof(wl_ess_list_t, wl_ess_list_ess) +
26173737Shx147065 	    pcwl_p->pcwl_scan_num * sizeof (wl_ess_conf_t);
26183737Shx147065 
26193737Shx147065 	scan_item = list_head(&pcwl_p->pcwl_scan_list);
26203737Shx147065 	for (i = 0; i < pcwl_p->pcwl_scan_num; i++) {
26213737Shx147065 		if (!scan_item)
26223737Shx147065 			goto done;
26233737Shx147065 		p_ess_conf = (wl_ess_conf_t *)(buf + WIFI_BUF_OFFSET +
26243737Shx147065 		    offsetof(wl_ess_list_t, wl_ess_list_ess) +
26253737Shx147065 		    i * sizeof (wl_ess_conf_t));
26263737Shx147065 		bcopy(scan_item->wl_val.wl_srt_ssid,
26273737Shx147065 		    p_ess_conf->wl_ess_conf_essid.wl_essid_essid,
26283737Shx147065 		    mi_strlen(scan_item->wl_val.wl_srt_ssid));
26293737Shx147065 		bcopy(scan_item->wl_val.wl_srt_bssid,
26303737Shx147065 		    p_ess_conf->wl_ess_conf_bssid, 6);
26313737Shx147065 		(p_ess_conf->wl_phy_conf).wl_phy_dsss_conf.wl_dsss_subtype
26323737Shx147065 		    = WL_DSSS;
26333737Shx147065 		p_ess_conf->wl_ess_conf_wepenabled =
26343737Shx147065 		    (scan_item->wl_val.wl_srt_cap & 0x10 ?
26353737Shx147065 		    WL_ENC_WEP : WL_NOENCRYPTION);
26363737Shx147065 		p_ess_conf->wl_ess_conf_bsstype =
26373737Shx147065 		    (scan_item->wl_val.wl_srt_cap & 0x1 ?
26383737Shx147065 		    WL_BSS_BSS : WL_BSS_IBSS);
26393737Shx147065 		p_ess_conf->wl_phy_conf.wl_phy_dsss_conf.wl_dsss_channel =
26403737Shx147065 		    scan_item->wl_val.wl_srt_chid;
26413737Shx147065 		if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) {
26423737Shx147065 			p_ess_conf->wl_ess_conf_sl =
26433737Shx147065 			    min(scan_item->wl_val.wl_srt_sl * 15 / 85 + 1,
26443737Shx147065 			    15);
26453737Shx147065 		} else {
26463737Shx147065 			if (scan_item->wl_val.wl_srt_sl <= 27)
26473737Shx147065 				p_ess_conf->wl_ess_conf_sl = 1;
26483737Shx147065 			else if (scan_item->wl_val.wl_srt_sl > 154)
26493737Shx147065 				p_ess_conf->wl_ess_conf_sl = 15;
26503737Shx147065 			else
26513737Shx147065 				p_ess_conf->wl_ess_conf_sl = min(15,
26523737Shx147065 				    ((scan_item->wl_val.wl_srt_sl - 27)
26533737Shx147065 				    * 15 / 127));
26543737Shx147065 		}
26553737Shx147065 		p_ess_conf->wl_supported_rates[0] = WL_RATE_1M;
26563737Shx147065 		p_ess_conf->wl_supported_rates[1] = WL_RATE_2M;
26573737Shx147065 		p_ess_conf->wl_supported_rates[2] = WL_RATE_5_5M;
26583737Shx147065 		p_ess_conf->wl_supported_rates[3] = WL_RATE_11M;
26593737Shx147065 		scan_item = list_next(&pcwl_p->pcwl_scan_list, scan_item);
26603737Shx147065 	}
26613737Shx147065 done:
26623737Shx147065 	mutex_exit(&pcwl_p->pcwl_scanlist_lock);
26633737Shx147065 	outfp->wldp_result = WL_SUCCESS;
26643737Shx147065 
26653737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
26663737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
26673737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
26683737Shx147065 	return (WL_SUCCESS);
26693737Shx147065 }
26703737Shx147065 
26713737Shx147065 /*ARGSUSED*/
26723737Shx147065 static int
26733737Shx147065 pcwl_cfg_linkstatus(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
26743737Shx147065 {
26753737Shx147065 	wldp_t *outfp;
26763737Shx147065 	char *buf;
26773737Shx147065 	uint16_t i, ret, val;
26783737Shx147065 	int iret;
26793737Shx147065 
26803737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
26813737Shx147065 	if (buf == NULL) {
26823737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
26833737Shx147065 		    MAX_BUF_LEN));
26843737Shx147065 		return (ENOMEM);
26853737Shx147065 	}
26863737Shx147065 	outfp = (wldp_t *)buf;
26873737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
26883737Shx147065 
26893737Shx147065 	ret =  pcwl_get_ltv(pcwl_p, 2,
26903737Shx147065 	    WL_RID_PORTSTATUS, &val);
26913737Shx147065 	if (ret) {
26923737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
26933737Shx147065 		outfp->wldp_result = WL_HW_ERROR;
26943737Shx147065 		PCWLDBG((CE_WARN, "cfg_linkstatus_get_error\n"));
26953737Shx147065 		goto done;
26963737Shx147065 	}
26973737Shx147065 	PCWLDBG((CE_NOTE, "PortStatus = %d\n", val));
26983737Shx147065 	if (val == WL_PORT_DISABLED || val == WL_PORT_INITIAL) {
26993737Shx147065 		*(wl_linkstatus_t *)(outfp->wldp_buf) = WL_NOTCONNECTED;
27003737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET +
27013737Shx147065 		    sizeof (wl_linkstatus_t);
27023737Shx147065 	} else if (val == WL_PORT_TO_IBSS ||
27033737Shx147065 	    val == WL_PORT_TO_BSS || val == WL_PORT_OOR) {
27043737Shx147065 		*(wl_linkstatus_t *)(outfp->wldp_buf) = WL_CONNECTED;
27053737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET +
27063737Shx147065 		    sizeof (wl_linkstatus_t);
27073737Shx147065 	} else {
27083737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
27093737Shx147065 	}
27103737Shx147065 	outfp->wldp_result = WL_SUCCESS;
27113737Shx147065 done:
27123737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
27133737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
27143737Shx147065 	iret = (int)(outfp->wldp_result);
27153737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
27163737Shx147065 	return (iret);
27173737Shx147065 }
27183737Shx147065 
27193737Shx147065 static int
27203737Shx147065 pcwl_cfg_bsstype(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
27213737Shx147065 {
27223737Shx147065 	uint16_t ret, i;
27233737Shx147065 	pcwl_rf_t *rf_p;
27243737Shx147065 	wldp_t	*infp;
27253737Shx147065 	wldp_t *outfp;
27263737Shx147065 	char *buf;
27273737Shx147065 	int iret;
27283737Shx147065 
27293737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
27303737Shx147065 	if (buf == NULL) {
27313737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
27323737Shx147065 		    MAX_BUF_LEN));
27333737Shx147065 		return (ENOMEM);
27343737Shx147065 	}
27353737Shx147065 	outfp = (wldp_t *)buf;
27363737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
27373737Shx147065 	infp = (wldp_t *)mp->b_rptr;
27383737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
27393737Shx147065 
27403737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_bss_type_t);
27413737Shx147065 	if (cmd == WLAN_GET_PARAM) {
27423737Shx147065 		*(wl_bss_type_t *)(outfp->wldp_buf) = rf_p->rf_porttype;
27433737Shx147065 		PCWLDBG((CE_CONT, "pcwl_getset: porttype=%d\n",
27443737Shx147065 		    rf_p->rf_porttype));
27453737Shx147065 		outfp->wldp_result = WL_SUCCESS;
27463737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
27473737Shx147065 		ret = (uint16_t)(*(wl_bss_type_t *)(infp->wldp_buf));
27483737Shx147065 		if ((ret != WL_BSS_BSS) &&
27493737Shx147065 		    (ret != WL_BSS_IBSS) &&
27503737Shx147065 		    (ret != WL_BSS_ANY)) {
27513737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
27523737Shx147065 			outfp->wldp_result = WL_NOTSUPPORTED;
27533737Shx147065 			goto done;
27543737Shx147065 		}
27553737Shx147065 		rf_p->rf_porttype = ret;
27563737Shx147065 		outfp->wldp_result = WL_SUCCESS;
27573737Shx147065 	} else {
27583737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
27593737Shx147065 		return (EINVAL);
27603737Shx147065 	}
27613737Shx147065 done:
27623737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
27633737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
27643737Shx147065 	iret = (int)(outfp->wldp_result);
27653737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
27663737Shx147065 	return (iret);
27673737Shx147065 }
27683737Shx147065 
27693737Shx147065 static int
27703737Shx147065 pcwl_cfg_phy(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
27713737Shx147065 {
2772*6062Shx147065 	uint16_t ret, retval, i;
27733737Shx147065 	pcwl_rf_t *rf_p;
27743737Shx147065 	wldp_t	*infp;
27753737Shx147065 	wldp_t *outfp;
27763737Shx147065 	char *buf;
27773737Shx147065 	int iret;
27783737Shx147065 
27793737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
27803737Shx147065 	if (buf == NULL) {
27813737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
27823737Shx147065 		    MAX_BUF_LEN));
27833737Shx147065 		return (ENOMEM);
27843737Shx147065 	}
27853737Shx147065 	outfp = (wldp_t *)buf;
27863737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
27873737Shx147065 	infp = (wldp_t *)mp->b_rptr;
27883737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
27893737Shx147065 
27903737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_dsss_t);
27913737Shx147065 	if (cmd == WLAN_GET_PARAM) {
27923737Shx147065 		if (ret = pcwl_get_ltv(pcwl_p, 2,
2793*6062Shx147065 		    WL_RID_CURRENT_CHNL, &retval)) {
27943737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
27953737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
27963737Shx147065 			goto done;
27973737Shx147065 		}
2798*6062Shx147065 		((wl_dsss_t *)(outfp->wldp_buf))->wl_dsss_channel = retval;
2799*6062Shx147065 		PCWLDBG((CE_CONT, "pcwl_getset: channel=%d\n", retval));
28003737Shx147065 		((wl_dsss_t *)(outfp->wldp_buf))->wl_dsss_subtype = WL_DSSS;
28013737Shx147065 		outfp->wldp_result = WL_SUCCESS;
28023737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
28033737Shx147065 		ret = (uint16_t)
28043737Shx147065 		    (((wl_phy_conf_t *)(infp->wldp_buf))
28053737Shx147065 		    ->wl_phy_dsss_conf.wl_dsss_channel);
28063737Shx147065 		if (ret < 1 || ret > 14) {
28073737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
28083737Shx147065 			outfp->wldp_result = WL_NOTSUPPORTED;
28093737Shx147065 			goto done;
28103737Shx147065 		}
28113737Shx147065 		rf_p->rf_own_chnl = ret;
28123737Shx147065 		PCWLDBG((CE_CONT, "pcwl: set channel=%d\n", rf_p->rf_own_chnl));
28133737Shx147065 		outfp->wldp_result = WL_SUCCESS;
28143737Shx147065 	} else {
28153737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
28163737Shx147065 		return (EINVAL);
28173737Shx147065 	}
28183737Shx147065 done:
28193737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
28203737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
28213737Shx147065 	iret = (int)(outfp->wldp_result);
28223737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
28233737Shx147065 	return (iret);
28243737Shx147065 
28253737Shx147065 }
28263737Shx147065 
28273737Shx147065 static int
28283737Shx147065 pcwl_cfg_desiredrates(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
28293737Shx147065 {
28303737Shx147065 	uint16_t rate;
28313737Shx147065 	uint16_t i;
28323737Shx147065 	pcwl_rf_t *rf_p;
28333737Shx147065 	wldp_t	*infp;
28343737Shx147065 	wldp_t *outfp;
28353737Shx147065 	char *buf;
28363737Shx147065 	int iret;
28373737Shx147065 	char rates[4];
28383737Shx147065 	char maxrate;
28393737Shx147065 
28403737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
28413737Shx147065 	if (buf == NULL) {
28423737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
28433737Shx147065 		    MAX_BUF_LEN));
28443737Shx147065 		return (ENOMEM);
28453737Shx147065 	}
28463737Shx147065 	outfp = (wldp_t *)buf;
28473737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
28483737Shx147065 	infp = (wldp_t *)mp->b_rptr;
28493737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
28503737Shx147065 
28513737Shx147065 	if (cmd == WLAN_GET_PARAM) {
28523737Shx147065 		if (i = pcwl_get_ltv(pcwl_p, 2, WL_RID_TX_RATE, &rate)) {
28533737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
28543737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
28553737Shx147065 			goto done;
28563737Shx147065 		}
28573737Shx147065 
28583737Shx147065 		if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) {
28593737Shx147065 			((wl_rates_t *)(outfp->wldp_buf))->wl_rates_num = 1;
28603737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET +
28613737Shx147065 			    offsetof(wl_rates_t, wl_rates_rates) +
28623737Shx147065 			    1 * sizeof (char);
28633737Shx147065 			switch (rate) {
28643737Shx147065 			case WL_SPEED_1Mbps_P2:
28653737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
28663737Shx147065 				    wl_rates_rates)[0] = WL_RATE_1M;
28673737Shx147065 				break;
28683737Shx147065 			case WL_SPEED_2Mbps_P2:
28693737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
28703737Shx147065 				    wl_rates_rates)[0] = WL_RATE_2M;
28713737Shx147065 				break;
28723737Shx147065 			case WL_SPEED_55Mbps_P2:
28733737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
28743737Shx147065 				    wl_rates_rates)[0] = WL_RATE_5_5M;
28753737Shx147065 				break;
28763737Shx147065 			case WL_SPEED_11Mbps_P2:
28773737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
28783737Shx147065 				    wl_rates_rates)[0] = WL_RATE_11M;
28793737Shx147065 				break;
28803737Shx147065 			default:
28813737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET;
28823737Shx147065 				outfp->wldp_result = WL_HW_ERROR;
28833737Shx147065 				goto done;
28843737Shx147065 			}
28853737Shx147065 		} else {
28863737Shx147065 			switch (rate) {
28873737Shx147065 			case WL_L_TX_RATE_FIX_1M:
28883737Shx147065 				((wl_rates_t *)(outfp->wldp_buf))->
28893737Shx147065 				    wl_rates_num = 1;
28903737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
28913737Shx147065 				    wl_rates_rates)[0] = WL_RATE_1M;
28923737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET +
28933737Shx147065 				    offsetof(wl_rates_t, wl_rates_rates) +
28943737Shx147065 				    1 * sizeof (char);
28953737Shx147065 				break;
28963737Shx147065 			case WL_L_TX_RATE_FIX_2M:
28973737Shx147065 				((wl_rates_t *)(outfp->wldp_buf))->
28983737Shx147065 				    wl_rates_num = 1;
28993737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
29003737Shx147065 				    wl_rates_rates)[0] = WL_RATE_2M;
29013737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET +
29023737Shx147065 				    offsetof(wl_rates_t, wl_rates_rates) +
29033737Shx147065 				    1 * sizeof (char);
29043737Shx147065 				break;
29053737Shx147065 			case WL_L_TX_RATE_AUTO_H:
29063737Shx147065 				((wl_rates_t *)(outfp->wldp_buf))->
29073737Shx147065 				    wl_rates_num = 4;
29083737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
29093737Shx147065 				    wl_rates_rates)[0] = WL_RATE_1M;
29103737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
29113737Shx147065 				    wl_rates_rates)[1] = WL_RATE_2M;
29123737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
29133737Shx147065 				    wl_rates_rates)[2] = WL_RATE_5_5M;
29143737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
29153737Shx147065 				    wl_rates_rates)[3] = WL_RATE_11M;
29163737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET +
29173737Shx147065 				    offsetof(wl_rates_t, wl_rates_rates) +
29183737Shx147065 				    4 * sizeof (char);
29193737Shx147065 				break;
29203737Shx147065 			case WL_L_TX_RATE_FIX_5M:
29213737Shx147065 				((wl_rates_t *)(outfp->wldp_buf))->
29223737Shx147065 				    wl_rates_num = 1;
29233737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
29243737Shx147065 				    wl_rates_rates)[0] = WL_RATE_5_5M;
29253737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET +
29263737Shx147065 				    offsetof(wl_rates_t, wl_rates_rates) +
29273737Shx147065 				    1 * sizeof (char);
29283737Shx147065 				break;
29293737Shx147065 			case WL_L_TX_RATE_FIX_11M:
29303737Shx147065 				((wl_rates_t *)(outfp->wldp_buf))->
29313737Shx147065 				    wl_rates_num = 1;
29323737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
29333737Shx147065 				    wl_rates_rates)[0] = WL_RATE_11M;
29343737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET +
29353737Shx147065 				    offsetof(wl_rates_t, wl_rates_rates) +
29363737Shx147065 				    1 * sizeof (char);
29373737Shx147065 				break;
29383737Shx147065 			case WL_L_TX_RATE_AUTO_L:
29393737Shx147065 				((wl_rates_t *)(outfp->wldp_buf))->
29403737Shx147065 				    wl_rates_num = 2;
29413737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
29423737Shx147065 				    wl_rates_rates)[0] = WL_RATE_1M;
29433737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
29443737Shx147065 				    wl_rates_rates)[1] = WL_RATE_2M;
29453737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET +
29463737Shx147065 				    offsetof(wl_rates_t, wl_rates_rates) +
29473737Shx147065 				    2 * sizeof (char);
29483737Shx147065 				break;
29493737Shx147065 			case WL_L_TX_RATE_AUTO_M:
29503737Shx147065 				((wl_rates_t *)(outfp->wldp_buf))->
29513737Shx147065 				    wl_rates_num = 3;
29523737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
29533737Shx147065 				    wl_rates_rates)[0] = WL_RATE_1M;
29543737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
29553737Shx147065 				    wl_rates_rates)[1] = WL_RATE_2M;
29563737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
29573737Shx147065 				    wl_rates_rates)[2] = WL_RATE_5_5M;
29583737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET +
29593737Shx147065 				    offsetof(wl_rates_t, wl_rates_rates) +
29603737Shx147065 				    3 * sizeof (char);
29613737Shx147065 				break;
29623737Shx147065 			default:
29633737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET;
29643737Shx147065 				outfp->wldp_result = WL_HW_ERROR;
29653737Shx147065 				goto done;
29663737Shx147065 			}
29673737Shx147065 		}
29683737Shx147065 		PCWLDBG((CE_CONT, "pcwl: get rate=%d\n", rate));
2969*6062Shx147065 		outfp->wldp_result = WL_SUCCESS;
29703737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
29713737Shx147065 		bzero(rates, sizeof (rates));
29723737Shx147065 		for (i = 0; i < 4; i++) {
29733737Shx147065 			rates[i] = (((wl_rates_t *)
29743737Shx147065 			    (infp->wldp_buf))->wl_rates_rates)[i];
29753737Shx147065 			PCWLDBG((CE_CONT, "pcwl: set tx_rate[%d]=%d\n",
29763737Shx147065 			    i, rates[i]));
29773737Shx147065 		}
29783737Shx147065 		PCWLDBG((CE_CONT, "pcwl: set rate_num=%d\n",
29793737Shx147065 		    ((wl_rates_t *)(infp->wldp_buf))
29803737Shx147065 		    ->wl_rates_num));
29813737Shx147065 		switch (((wl_rates_t *)
29823737Shx147065 		    (infp->wldp_buf))->wl_rates_num) {
29833737Shx147065 		case 1:
29843737Shx147065 			switch (rates[0]) {
29853737Shx147065 			case WL_RATE_1M:
29863737Shx147065 				rf_p->rf_tx_rate = WL_TX_RATE_FIX_1M(pcwl_p);
29873737Shx147065 				break;
29883737Shx147065 			case WL_RATE_2M:
29893737Shx147065 				rf_p->rf_tx_rate = WL_TX_RATE_FIX_2M(pcwl_p);
29903737Shx147065 				break;
29913737Shx147065 			case WL_RATE_11M:
29923737Shx147065 				rf_p->rf_tx_rate = WL_TX_RATE_FIX_11M(pcwl_p);
29933737Shx147065 				break;
29943737Shx147065 			case WL_RATE_5_5M:
29953737Shx147065 				rf_p->rf_tx_rate = WL_TX_RATE_FIX_5M(pcwl_p);
29963737Shx147065 				break;
29973737Shx147065 			default:
29983737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET;
29993737Shx147065 				outfp->wldp_result = WL_NOTSUPPORTED;
30003737Shx147065 				goto done;
30013737Shx147065 			}
30023737Shx147065 			break;
30033737Shx147065 		case 2:
30043737Shx147065 			maxrate = (rates[0] > rates[1] ?
30053737Shx147065 			    rates[0] : rates[1]);
30063737Shx147065 			switch (maxrate) {
30073737Shx147065 			case WL_RATE_2M:
30083737Shx147065 				rf_p->rf_tx_rate = WL_TX_RATE_AUTO_L(pcwl_p);
30093737Shx147065 				break;
30103737Shx147065 			case WL_RATE_11M:
30113737Shx147065 				rf_p->rf_tx_rate = WL_TX_RATE_AUTO_H(pcwl_p);
30123737Shx147065 				break;
30133737Shx147065 			case WL_RATE_5_5M:
30143737Shx147065 				rf_p->rf_tx_rate = WL_TX_RATE_AUTO_M(pcwl_p);
30153737Shx147065 				break;
30163737Shx147065 			default:
30173737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET;
30183737Shx147065 				outfp->wldp_result = WL_NOTSUPPORTED;
30193737Shx147065 				goto done;
30203737Shx147065 			}
30213737Shx147065 			break;
30223737Shx147065 		case 3:
30233737Shx147065 			maxrate = (rates[0] > rates[1] ?
30243737Shx147065 			    rates[0] : rates[1]);
30253737Shx147065 			maxrate = (rates[2] > maxrate ?
30263737Shx147065 			    rates[2] : maxrate);
30273737Shx147065 			switch (maxrate) {
30283737Shx147065 			case WL_RATE_11M:
30293737Shx147065 				rf_p->rf_tx_rate = WL_TX_RATE_AUTO_H(pcwl_p);
30303737Shx147065 				break;
30313737Shx147065 			case WL_RATE_5_5M:
30323737Shx147065 				rf_p->rf_tx_rate = WL_TX_RATE_AUTO_M(pcwl_p);
30333737Shx147065 				break;
30343737Shx147065 			default:
30353737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET;
30363737Shx147065 				outfp->wldp_result = WL_NOTSUPPORTED;
30373737Shx147065 				goto done;
30383737Shx147065 			}
30393737Shx147065 			break;
30403737Shx147065 		case 4:
30413737Shx147065 			rf_p->rf_tx_rate = WL_TX_RATE_AUTO_H(pcwl_p);
30423737Shx147065 			break;
30433737Shx147065 		default:
30443737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
30453737Shx147065 			outfp->wldp_result = WL_LACK_FEATURE;
30463737Shx147065 			goto done;
30473737Shx147065 		}
30483737Shx147065 		PCWLDBG((CE_CONT, "pcwl: set tx_rate=%d\n", rf_p->rf_tx_rate));
30493737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
30503737Shx147065 		outfp->wldp_result = WL_SUCCESS;
30513737Shx147065 	} else {
30523737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
30533737Shx147065 		return (EINVAL);
30543737Shx147065 	}
30553737Shx147065 done:
30563737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
30573737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
30583737Shx147065 	iret = (int)(outfp->wldp_result);
30593737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
30603737Shx147065 	return (iret);
30613737Shx147065 }
30623737Shx147065 
30633737Shx147065 /*ARGSUSED*/
30643737Shx147065 static int
30653737Shx147065 pcwl_cfg_supportrates(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
30663737Shx147065 {
30673737Shx147065 	uint16_t i;
30683737Shx147065 	wldp_t *outfp;
30693737Shx147065 	char *buf;
30703737Shx147065 
30713737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
30723737Shx147065 	if (buf == NULL) {
30733737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
30743737Shx147065 		    MAX_BUF_LEN));
30753737Shx147065 		return (ENOMEM);
30763737Shx147065 	}
30773737Shx147065 	outfp = (wldp_t *)buf;
30783737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
30793737Shx147065 
30803737Shx147065 	if (cmd == WLAN_GET_PARAM) {
30813737Shx147065 		((wl_rates_t *)(outfp->wldp_buf))->wl_rates_num = 4;
30823737Shx147065 		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[0]
30833737Shx147065 		    = WL_RATE_1M;
30843737Shx147065 		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[1]
30853737Shx147065 		    = WL_RATE_2M;
30863737Shx147065 		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[2]
30873737Shx147065 		    = WL_RATE_5_5M;
30883737Shx147065 		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[3]
30893737Shx147065 		    = WL_RATE_11M;
30903737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET +
30913737Shx147065 		    offsetof(wl_rates_t, wl_rates_rates) +
30923737Shx147065 		    4 * sizeof (char);
30933737Shx147065 		outfp->wldp_result = WL_SUCCESS;
30943737Shx147065 		for (i = 0; i < (outfp->wldp_length); i++)
30953737Shx147065 			(void) mi_mpprintf_putc((char *)mp, buf[i]);
30963737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
30973737Shx147065 		return (WL_SUCCESS);
30983737Shx147065 	} else {
30993737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
31003737Shx147065 		return (EINVAL);
31013737Shx147065 	}
31023737Shx147065 }
31033737Shx147065 
31043737Shx147065 static int
31053737Shx147065 pcwl_cfg_powermode(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
31063737Shx147065 {
31073737Shx147065 	uint16_t i, ret;
31083737Shx147065 	pcwl_rf_t *rf_p;
31093737Shx147065 	wldp_t	*infp;
31103737Shx147065 	wldp_t *outfp;
31113737Shx147065 	char *buf;
31123737Shx147065 	int iret;
31133737Shx147065 
31143737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
31153737Shx147065 	if (buf == NULL) {
31163737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
31173737Shx147065 		    MAX_BUF_LEN));
31183737Shx147065 		return (ENOMEM);
31193737Shx147065 	}
31203737Shx147065 	outfp = (wldp_t *)buf;
31213737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
31223737Shx147065 	infp = (wldp_t *)mp->b_rptr;
31233737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
31243737Shx147065 
31253737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_ps_mode_t);
31263737Shx147065 	if (cmd == WLAN_GET_PARAM) {
31273737Shx147065 		((wl_ps_mode_t *)(outfp->wldp_buf))->wl_ps_mode =
31283737Shx147065 		    rf_p->rf_pm_enabled;
31293737Shx147065 		outfp->wldp_result = WL_SUCCESS;
31303737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
31313737Shx147065 		ret = (uint16_t)(((wl_ps_mode_t *)(infp->wldp_buf))
31323737Shx147065 		    ->wl_ps_mode);
31333737Shx147065 		if (ret != WL_PM_AM && ret != WL_PM_MPS && ret != WL_PM_FAST) {
31343737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
31353737Shx147065 			outfp->wldp_result = WL_NOTSUPPORTED;
31363737Shx147065 			goto done;
31373737Shx147065 		}
31383737Shx147065 		rf_p->rf_pm_enabled = ret;
31393737Shx147065 		outfp->wldp_result = WL_SUCCESS;
31403737Shx147065 	} else {
31413737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
31423737Shx147065 		return (EINVAL);
31433737Shx147065 	}
31443737Shx147065 done:
31453737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
31463737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
31473737Shx147065 	iret = (int)(outfp->wldp_result);
31483737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
31493737Shx147065 	return (iret);
31503737Shx147065 
31513737Shx147065 }
31523737Shx147065 
31533737Shx147065 static int
31543737Shx147065 pcwl_cfg_authmode(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
31553737Shx147065 {
31563737Shx147065 	uint16_t i, ret;
31573737Shx147065 	pcwl_rf_t *rf_p;
31583737Shx147065 	wldp_t	*infp;
31593737Shx147065 	wldp_t *outfp;
31603737Shx147065 	char *buf;
31613737Shx147065 	int iret;
31623737Shx147065 
31633737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
31643737Shx147065 	if (buf == NULL) {
31653737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
31663737Shx147065 		    MAX_BUF_LEN));
31673737Shx147065 		return (ENOMEM);
31683737Shx147065 	}
31693737Shx147065 	outfp = (wldp_t *)buf;
31703737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
31713737Shx147065 	infp = (wldp_t *)mp->b_rptr;
31723737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
31733737Shx147065 
31743737Shx147065 
31753737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_authmode_t);
31763737Shx147065 	if (cmd == WLAN_GET_PARAM) {
31773737Shx147065 		*(wl_authmode_t *)(outfp->wldp_buf) = rf_p->rf_authtype;
31783737Shx147065 		outfp->wldp_result = WL_SUCCESS;
31793737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
31803737Shx147065 		ret = (uint16_t)(*(wl_authmode_t *)(infp->wldp_buf));
31813737Shx147065 		if (ret != WL_OPENSYSTEM && ret != WL_SHAREDKEY) {
31823737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
31833737Shx147065 			outfp->wldp_result = WL_NOTSUPPORTED;
31843737Shx147065 			goto done;
31853737Shx147065 		}
31863737Shx147065 		rf_p->rf_authtype = ret;
31873737Shx147065 		outfp->wldp_result = WL_SUCCESS;
31883737Shx147065 	} else {
31893737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
31903737Shx147065 		return (EINVAL);
31913737Shx147065 	}
31923737Shx147065 done:
31933737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
31943737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
31953737Shx147065 	iret = (int)(outfp->wldp_result);
31963737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
31973737Shx147065 	return (iret);
31983737Shx147065 }
31993737Shx147065 
32003737Shx147065 static int
32013737Shx147065 pcwl_cfg_encryption(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
32023737Shx147065 {
32033737Shx147065 	uint16_t i, ret;
32043737Shx147065 	pcwl_rf_t *rf_p;
32053737Shx147065 	wldp_t	*infp;
32063737Shx147065 	wldp_t *outfp;
32073737Shx147065 	char *buf;
32083737Shx147065 	int iret;
32093737Shx147065 
32103737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
32113737Shx147065 	if (buf == NULL) {
32123737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
32133737Shx147065 		    MAX_BUF_LEN));
32143737Shx147065 		return (ENOMEM);
32153737Shx147065 	}
32163737Shx147065 	outfp = (wldp_t *)buf;
32173737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
32183737Shx147065 	infp = (wldp_t *)mp->b_rptr;
32193737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
32203737Shx147065 
32213737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_encryption_t);
32223737Shx147065 	if (cmd == WLAN_GET_PARAM) {
32233737Shx147065 		*(wl_encryption_t *)(outfp->wldp_buf) = rf_p->rf_encryption;
32243737Shx147065 		outfp->wldp_result = WL_SUCCESS;
32253737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
32263737Shx147065 		ret = (uint16_t)(*(wl_encryption_t *)(infp->wldp_buf));
32273737Shx147065 		PCWLDBG((CE_NOTE, "set encryption: %d\n", ret));
32283737Shx147065 		if (ret != WL_NOENCRYPTION && ret != WL_ENC_WEP) {
32293737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
32303737Shx147065 			outfp->wldp_result = WL_NOTSUPPORTED;
32313737Shx147065 			goto done;
32323737Shx147065 		}
32333737Shx147065 		rf_p->rf_encryption = ret;
32343737Shx147065 		outfp->wldp_result = WL_SUCCESS;
32353737Shx147065 	} else {
32363737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
32373737Shx147065 		return (EINVAL);
32383737Shx147065 	}
32393737Shx147065 done:
32403737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
32413737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
32423737Shx147065 	iret = (int)(outfp->wldp_result);
32433737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
32443737Shx147065 	return (iret);
32453737Shx147065 }
32463737Shx147065 
32473737Shx147065 static int
32483737Shx147065 pcwl_cfg_wepkeyid(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
32493737Shx147065 {
32503737Shx147065 	uint16_t i, ret;
32513737Shx147065 	pcwl_rf_t *rf_p;
32523737Shx147065 	wldp_t	*infp;
32533737Shx147065 	wldp_t *outfp;
32543737Shx147065 	char *buf;
32553737Shx147065 	int iret;
32563737Shx147065 
32573737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
32583737Shx147065 	if (buf == NULL) {
32593737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
32603737Shx147065 		    MAX_BUF_LEN));
32613737Shx147065 		return (ENOMEM);
32623737Shx147065 	}
32633737Shx147065 	outfp = (wldp_t *)buf;
32643737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
32653737Shx147065 	infp = (wldp_t *)mp->b_rptr;
32663737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
32673737Shx147065 
32683737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_wep_key_id_t);
32693737Shx147065 	if (cmd == WLAN_GET_PARAM) {
32703737Shx147065 		*(wl_wep_key_id_t *)(outfp->wldp_buf) = rf_p->rf_tx_crypt_key;
32713737Shx147065 		outfp->wldp_result = WL_SUCCESS;
32723737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
32733737Shx147065 		ret = (uint16_t)(*(wl_wep_key_id_t *)(infp->wldp_buf));
32743737Shx147065 		if (ret >= MAX_NWEPKEYS) {
32753737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
32763737Shx147065 			outfp->wldp_result = WL_NOTSUPPORTED;
32773737Shx147065 			goto done;
32783737Shx147065 		}
32793737Shx147065 		rf_p->rf_tx_crypt_key = ret;
32803737Shx147065 		outfp->wldp_result = WL_SUCCESS;
32813737Shx147065 	} else {
32823737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
32833737Shx147065 		return (EINVAL);
32843737Shx147065 	}
32853737Shx147065 done:
32863737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
32873737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
32883737Shx147065 	iret = (int)(outfp->wldp_result);
32893737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
32903737Shx147065 	return (iret);
32913737Shx147065 }
32923737Shx147065 
32933737Shx147065 static int
32943737Shx147065 pcwl_cfg_createibss(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
32953737Shx147065 {
32963737Shx147065 	uint16_t i, ret;
32973737Shx147065 	pcwl_rf_t *rf_p;
32983737Shx147065 	wldp_t	*infp;
32993737Shx147065 	wldp_t *outfp;
33003737Shx147065 	char *buf;
33013737Shx147065 	int iret;
33023737Shx147065 
33033737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
33043737Shx147065 	if (buf == NULL) {
33053737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
33063737Shx147065 		    MAX_BUF_LEN));
33073737Shx147065 		return (ENOMEM);
33083737Shx147065 	}
33093737Shx147065 	outfp = (wldp_t *)buf;
33103737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
33113737Shx147065 	infp = (wldp_t *)mp->b_rptr;
33123737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
33133737Shx147065 
33143737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_create_ibss_t);
33153737Shx147065 	if (cmd == WLAN_GET_PARAM) {
33163737Shx147065 		*(wl_create_ibss_t *)(outfp->wldp_buf) = rf_p->rf_create_ibss;
33173737Shx147065 		outfp->wldp_result = WL_SUCCESS;
33183737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
33193737Shx147065 		ret = (uint16_t)(*(wl_create_ibss_t *)(infp->wldp_buf));
33203737Shx147065 		if (ret != 0 && ret != 1) {
33213737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
33223737Shx147065 			outfp->wldp_result = WL_NOTSUPPORTED;
33233737Shx147065 			goto done;
33243737Shx147065 		}
33253737Shx147065 		rf_p->rf_create_ibss = ret;
33263737Shx147065 		outfp->wldp_result = WL_SUCCESS;
33273737Shx147065 	} else {
33283737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
33293737Shx147065 		return (EINVAL);
33303737Shx147065 	}
33313737Shx147065 done:
33323737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
33333737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
33343737Shx147065 	iret = (int)(outfp->wldp_result);
33353737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
33363737Shx147065 	return (iret);
33373737Shx147065 }
33383737Shx147065 
33393737Shx147065 static int
33403737Shx147065 pcwl_cfg_rssi(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
33413737Shx147065 {
33423737Shx147065 	uint16_t i;
33433737Shx147065 	int iret;
33443737Shx147065 	wldp_t *outfp;
33453737Shx147065 	char *buf;
33463737Shx147065 
33473737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
33483737Shx147065 	if (buf == NULL) {
33493737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
33503737Shx147065 		    MAX_BUF_LEN));
33513737Shx147065 		return (ENOMEM);
33523737Shx147065 	}
33533737Shx147065 	outfp = (wldp_t *)buf;
33543737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
33553737Shx147065 
33563737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_rssi_t);
33573737Shx147065 
33583737Shx147065 	if (cmd == WLAN_GET_PARAM) {
33593737Shx147065 		if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) {
33603737Shx147065 			*(wl_rssi_t *)(outfp->wldp_buf) =
33613737Shx147065 			    min((pcwl_p->pcwl_rssi * 15 / 85 + 1), 15);
33623737Shx147065 		} else {
33633737Shx147065 		/*
33643737Shx147065 		 * According to the description of the
33653737Shx147065 		 * datasheet(Lucent card), the signal level
33663737Shx147065 		 * value is between 27 -- 154.
33673737Shx147065 		 * we reflect these value to 1-15 as rssi.
33683737Shx147065 		 */
33693737Shx147065 			if (pcwl_p->pcwl_rssi <= 27)
33703737Shx147065 				*(wl_rssi_t *)(outfp->wldp_buf) = 1;
33713737Shx147065 			else if (pcwl_p->pcwl_rssi > 154)
33723737Shx147065 				*(wl_rssi_t *)(outfp->wldp_buf) = 15;
33733737Shx147065 			else
33743737Shx147065 				*(wl_rssi_t *)(outfp->wldp_buf) =
33753737Shx147065 				    min(15, ((pcwl_p->pcwl_rssi - 27)
33763737Shx147065 				    * 15 / 127));
33773737Shx147065 		}
33783737Shx147065 		outfp->wldp_result = WL_SUCCESS;
33793737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
33803737Shx147065 		outfp->wldp_result = WL_READONLY;
33813737Shx147065 	} else {
33823737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
33833737Shx147065 		return (EINVAL);
33843737Shx147065 	}
33853737Shx147065 done:
33863737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
33873737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
33883737Shx147065 	iret = (int)(outfp->wldp_result);
33893737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
33903737Shx147065 	return (iret);
33913737Shx147065 }
33923737Shx147065 
33933737Shx147065 /*ARGSUSED*/
33943737Shx147065 static int
33953737Shx147065 pcwl_cfg_radio(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
33963737Shx147065 {
33973737Shx147065 	uint16_t i;
33983737Shx147065 	int iret;
33993737Shx147065 	wldp_t *outfp;
34003737Shx147065 	char *buf;
34013737Shx147065 
34023737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
34033737Shx147065 	if (buf == NULL) {
34043737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
34053737Shx147065 		    MAX_BUF_LEN));
34063737Shx147065 		return (ENOMEM);
34073737Shx147065 	}
34083737Shx147065 	outfp = (wldp_t *)buf;
34093737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
34103737Shx147065 
34113737Shx147065 	if (cmd == WLAN_GET_PARAM) {
34123737Shx147065 		*(wl_radio_t *)(outfp->wldp_buf) = B_TRUE;
34133737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_radio_t);
34143737Shx147065 		outfp->wldp_result = WL_SUCCESS;
34153737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
34163737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
34173737Shx147065 		outfp->wldp_result = WL_LACK_FEATURE;
34183737Shx147065 	} else {
34193737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
34203737Shx147065 		return (EINVAL);
34213737Shx147065 	}
34223737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
34233737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
34243737Shx147065 	iret = (int)(outfp->wldp_result);
34253737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
34263737Shx147065 	return (iret);
34273737Shx147065 }
34283737Shx147065 
34293737Shx147065 static int
34303737Shx147065 pcwl_cfg_wepkey(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
34313737Shx147065 {
34323737Shx147065 	uint16_t i;
34333737Shx147065 	wl_wep_key_t *p_wepkey_tab;
34343737Shx147065 	pcwl_rf_t *rf_p;
34353737Shx147065 	wldp_t	*infp;
34363737Shx147065 	wldp_t *outfp;
34373737Shx147065 	char *buf;
34383737Shx147065 	int iret;
34393737Shx147065 
34403737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
34413737Shx147065 	if (buf == NULL) {
34423737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
34433737Shx147065 		    MAX_BUF_LEN));
34443737Shx147065 		return (ENOMEM);
34453737Shx147065 	}
34463737Shx147065 	outfp = (wldp_t *)buf;
34473737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
34483737Shx147065 	infp = (wldp_t *)mp->b_rptr;
34493737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
34503737Shx147065 	bzero((rf_p->rf_ckeys), sizeof (rf_ckey_t) * MAX_NWEPKEYS);
34513737Shx147065 
34523737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_wep_key_tab_t);
34533737Shx147065 	if (cmd == WLAN_GET_PARAM) {
34543737Shx147065 		outfp->wldp_result = WL_WRITEONLY;
34553737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
34563737Shx147065 		p_wepkey_tab = (wl_wep_key_t *)(infp->wldp_buf);
34573737Shx147065 		for (i = 0; i < MAX_NWEPKEYS; i++) {
34583737Shx147065 			if (p_wepkey_tab[i].wl_wep_operation == WL_ADD) {
34593737Shx147065 				rf_p->rf_ckeys[i].ckey_len =
34603737Shx147065 				    p_wepkey_tab[i].wl_wep_length;
34613737Shx147065 				bcopy(p_wepkey_tab[i].wl_wep_key,
34623737Shx147065 				    rf_p->rf_ckeys[i].ckey_dat,
34633737Shx147065 				    p_wepkey_tab[i].wl_wep_length);
34643737Shx147065 				PCWL_SWAP16((uint16_t *)
34653737Shx147065 				    &rf_p->rf_ckeys[i].ckey_dat,
34663737Shx147065 				    rf_p->rf_ckeys[i].ckey_len + 1);
34673737Shx147065 				PCWLDBG((CE_CONT, "%s, %d\n",
34683737Shx147065 				    rf_p->rf_ckeys[i].ckey_dat, i));
34693737Shx147065 			}
34703737Shx147065 			PCWLDBG((CE_CONT, "pcwl: rf_ckeys[%d]=%s\n", i,
3471*6062Shx147065 			    (char *)(rf_p->rf_ckeys[i].ckey_dat)));
34723737Shx147065 		}
34733737Shx147065 		outfp->wldp_result = WL_SUCCESS;
34743737Shx147065 	} else {
34753737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
34763737Shx147065 		return (EINVAL);
34773737Shx147065 	}
34783737Shx147065 done:
34793737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
34803737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
34813737Shx147065 	iret = (int)(outfp->wldp_result);
34823737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
34833737Shx147065 	return (iret);
34843737Shx147065 }
34853737Shx147065 
34863737Shx147065 static void
34873737Shx147065 pcwl_connect_timeout(void *arg)
34883737Shx147065 {
34893737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
34903737Shx147065 	uint16_t ret = 0;
34913737Shx147065 
34923737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
34933737Shx147065 	PCWL_DISABLE_INTR(pcwl_p);
34943737Shx147065 	if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
34953737Shx147065 		goto done;
34963737Shx147065 	}
34973737Shx147065 	if (ret = pcwl_config_rf(pcwl_p)) {
34983737Shx147065 		goto done;
34993737Shx147065 	}
35003737Shx147065 	if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
35013737Shx147065 		goto done;
35023737Shx147065 	}
35033737Shx147065 	PCWL_ENABLE_INTR(pcwl_p);
35043737Shx147065 done:
35053737Shx147065 	if (ret)
35064196Syx209050 		cmn_err(CE_WARN, "pcwl: connect failed due to hardware error");
35073737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
35083737Shx147065 	pcwl_p->pcwl_connect_timeout_id = 0;
35093737Shx147065 }
35103737Shx147065 
35113737Shx147065 static int
35123737Shx147065 pcwl_getset(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
35133737Shx147065 {
35143737Shx147065 	int ret = WL_SUCCESS;
35153737Shx147065 	int connect = 0;
35163737Shx147065 
35173737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
35183737Shx147065 	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
35193737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
35203737Shx147065 		return (PCWL_FAIL);
35213737Shx147065 	}
35223737Shx147065 	switch (((wldp_t *)mp->b_rptr)->wldp_id) {
35233737Shx147065 	case WL_ESSID:
35243737Shx147065 		ret = pcwl_cfg_essid(mp, pcwl_p, cmd);
35253737Shx147065 		connect = 1;
35263737Shx147065 		PCWLDBG((CE_NOTE, "cfg_essid\n"));
35273737Shx147065 		break;
35283737Shx147065 	case WL_BSSID:
35293737Shx147065 		ret = pcwl_cfg_bssid(mp, pcwl_p, cmd);
35303737Shx147065 		connect = 1;
35313737Shx147065 		PCWLDBG((CE_NOTE, "cfg_bssid\n"));
35323737Shx147065 		break;
35333737Shx147065 	case WL_ESS_LIST:
35343737Shx147065 		ret = pcwl_cfg_scan(mp, pcwl_p, cmd);
35353737Shx147065 		PCWLDBG((CE_NOTE, "cfg_scan\n"));
35363737Shx147065 		break;
35373737Shx147065 	case WL_LINKSTATUS:
35383737Shx147065 		ret = pcwl_cfg_linkstatus(mp, pcwl_p, cmd);
35393737Shx147065 		PCWLDBG((CE_NOTE, "cfg_linkstatus\n"));
35403737Shx147065 		break;
35413737Shx147065 	case WL_BSS_TYPE:
35423737Shx147065 		ret = pcwl_cfg_bsstype(mp, pcwl_p, cmd);
35433737Shx147065 		connect = 1;
35443737Shx147065 		PCWLDBG((CE_NOTE, "cfg_bsstype\n"));
35453737Shx147065 		break;
35463737Shx147065 	case WL_PHY_CONFIG:
35473737Shx147065 		ret = pcwl_cfg_phy(mp, pcwl_p, cmd);
35483737Shx147065 		connect = 1;
35493737Shx147065 		PCWLDBG((CE_NOTE, "cfg_phy\n"));
35503737Shx147065 		break;
35513737Shx147065 	case WL_DESIRED_RATES:
35523737Shx147065 		ret = pcwl_cfg_desiredrates(mp, pcwl_p, cmd);
35533737Shx147065 		connect = 1;
35543737Shx147065 		PCWLDBG((CE_NOTE, "cfg_disred-rates\n"));
35553737Shx147065 		break;
35563737Shx147065 	case WL_SUPPORTED_RATES:
35573737Shx147065 		ret = pcwl_cfg_supportrates(mp, pcwl_p, cmd);
35583737Shx147065 		PCWLDBG((CE_NOTE, "cfg_supported-rates\n"));
35593737Shx147065 		break;
35603737Shx147065 	case WL_POWER_MODE:
35613737Shx147065 		ret = pcwl_cfg_powermode(mp, pcwl_p, cmd);
35623737Shx147065 		PCWLDBG((CE_NOTE, "cfg_powermode\n"));
35633737Shx147065 		break;
35643737Shx147065 	case WL_AUTH_MODE:
35653737Shx147065 		ret = pcwl_cfg_authmode(mp, pcwl_p, cmd);
35663737Shx147065 		connect = 1;
35673737Shx147065 		PCWLDBG((CE_NOTE, "cfg_authmode\n"));
35683737Shx147065 		break;
35693737Shx147065 	case WL_ENCRYPTION:
35703737Shx147065 		ret = pcwl_cfg_encryption(mp, pcwl_p, cmd);
35713737Shx147065 		connect = 1;
35723737Shx147065 		PCWLDBG((CE_NOTE, "cfg_encryption\n"));
35733737Shx147065 		break;
35743737Shx147065 	case WL_WEP_KEY_ID:
35753737Shx147065 		ret = pcwl_cfg_wepkeyid(mp, pcwl_p, cmd);
35763737Shx147065 		connect = 1;
35773737Shx147065 		PCWLDBG((CE_NOTE, "cfg_wepkeyid\n"));
35783737Shx147065 		break;
35793737Shx147065 	case WL_CREATE_IBSS:
35803737Shx147065 		ret = pcwl_cfg_createibss(mp, pcwl_p, cmd);
35813737Shx147065 		connect = 1;
35823737Shx147065 		PCWLDBG((CE_NOTE, "cfg_create-ibss\n"));
35833737Shx147065 		break;
35843737Shx147065 	case WL_RSSI:
35853737Shx147065 		ret = pcwl_cfg_rssi(mp, pcwl_p, cmd);
35863737Shx147065 		PCWLDBG((CE_NOTE, "cfg_rssi\n"));
35873737Shx147065 		break;
35883737Shx147065 	case WL_RADIO:
35893737Shx147065 		ret = pcwl_cfg_radio(mp, pcwl_p, cmd);
35903737Shx147065 		PCWLDBG((CE_NOTE, "cfg_radio\n"));
35913737Shx147065 		break;
35923737Shx147065 	case WL_WEP_KEY_TAB:
35933737Shx147065 		ret = pcwl_cfg_wepkey(mp, pcwl_p, cmd);
35943737Shx147065 		connect = 1;
35953737Shx147065 		PCWLDBG((CE_NOTE, "cfg_wepkey\n"));
35963737Shx147065 		break;
35973737Shx147065 	case WL_SCAN:
35983737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
35993737Shx147065 		if (pcwl_p->pcwl_connect_timeout_id != 0) {
36003737Shx147065 			(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
36013737Shx147065 			pcwl_p->pcwl_connect_timeout_id = 0;
36023737Shx147065 		}
36033737Shx147065 		mutex_enter(&pcwl_p->pcwl_glock);
36043737Shx147065 		ret = pcwl_cmd_scan(pcwl_p);
36053737Shx147065 		break;
36063737Shx147065 	case WL_LOAD_DEFAULTS:
36073737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
36083737Shx147065 		if (pcwl_p->pcwl_connect_timeout_id != 0) {
36093737Shx147065 			(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
36103737Shx147065 			pcwl_p->pcwl_connect_timeout_id = 0;
36113737Shx147065 		}
36123737Shx147065 		mutex_enter(&pcwl_p->pcwl_glock);
36133737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
36143737Shx147065 			ret = (int)WL_HW_ERROR;
36153737Shx147065 			break;
36163737Shx147065 		}
36173737Shx147065 		if (ret = pcwl_loaddef_rf(pcwl_p)) {
36183737Shx147065 			ret = (int)WL_HW_ERROR;
36193737Shx147065 			PCWLDBG((CE_WARN, "cfg_loaddef_err\n"));
36203737Shx147065 			break;
36213737Shx147065 		}
36223737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
36233737Shx147065 			ret = (int)WL_HW_ERROR;
36243737Shx147065 			break;
36253737Shx147065 		}
36263737Shx147065 		pcwl_delay(pcwl_p, 1000000);
36273737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
36283737Shx147065 			ret = (int)WL_HW_ERROR;
36293737Shx147065 			break;
36303737Shx147065 		}
36313737Shx147065 		PCWLDBG((CE_NOTE, "loaddef\n"));
36323737Shx147065 		break;
36333737Shx147065 	case WL_DISASSOCIATE:
36343737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
36353737Shx147065 		if (pcwl_p->pcwl_connect_timeout_id != 0) {
36363737Shx147065 			(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
36373737Shx147065 			pcwl_p->pcwl_connect_timeout_id = 0;
36383737Shx147065 		}
36393737Shx147065 
36403737Shx147065 		mutex_enter(&pcwl_p->pcwl_glock);
36413737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
36423737Shx147065 			ret = (int)WL_HW_ERROR;
36433737Shx147065 			break;
36443737Shx147065 		}
36453737Shx147065 		/*
36463737Shx147065 		 * A workaround here: If the card is in ad-hoc mode, the
36473737Shx147065 		 * following scan will not work correctly, so any
36483737Shx147065 		 * 'dladm connect-wifi' which need a scan first will not
36493737Shx147065 		 * succeed. software reset the card here as a workround.
36503737Shx147065 		 */
36513737Shx147065 		if ((pcwl_p->pcwl_rf.rf_porttype == WL_BSS_IBSS) &&
36523737Shx147065 		    (pcwl_p->pcwl_chip_type == PCWL_CHIP_LUCENT)) {
36533737Shx147065 			if (ret = pcwl_reset_backend(pcwl_p)) {
36543737Shx147065 				ret = (int)WL_HW_ERROR;
36553737Shx147065 				break;
36563737Shx147065 			}
36573737Shx147065 			if (ret = pcwl_init_nicmem(pcwl_p)) {
36583737Shx147065 				ret = (int)WL_HW_ERROR;
36593737Shx147065 				break;
36603737Shx147065 			}
36613737Shx147065 			pcwl_start_locked(pcwl_p);
36623737Shx147065 		}
36633737Shx147065 		if (ret = pcwl_loaddef_rf(pcwl_p)) {
36643737Shx147065 			ret = (int)WL_HW_ERROR;
36653737Shx147065 			PCWLDBG((CE_WARN, "cfg_loaddef_err\n"));
36663737Shx147065 			break;
36673737Shx147065 		}
36683737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
36693737Shx147065 			ret = (int)WL_HW_ERROR;
36703737Shx147065 			break;
36713737Shx147065 		}
36723737Shx147065 		pcwl_delay(pcwl_p, 1000000);
36733737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
36743737Shx147065 			ret = (int)WL_HW_ERROR;
36753737Shx147065 			break;
36763737Shx147065 		}
36773737Shx147065 		PCWLDBG((CE_NOTE, "disassociate\n"));
36783737Shx147065 		break;
36793737Shx147065 	case WL_REASSOCIATE:
36803737Shx147065 	case WL_ASSOCIAT:
36813737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
36823737Shx147065 		if (pcwl_p->pcwl_connect_timeout_id != 0) {
36833737Shx147065 			(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
36843737Shx147065 			pcwl_p->pcwl_connect_timeout_id = 0;
36853737Shx147065 		}
36863737Shx147065 		mutex_enter(&pcwl_p->pcwl_glock);
36873737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
36883737Shx147065 			ret = (int)WL_HW_ERROR;
36893737Shx147065 			break;
36903737Shx147065 		}
36913737Shx147065 		if (ret = pcwl_config_rf(pcwl_p)) {
36923737Shx147065 			ret = (int)WL_HW_ERROR;
36933737Shx147065 			break;
36943737Shx147065 		}
36953737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
36963737Shx147065 			ret = (int)WL_HW_ERROR;
36973737Shx147065 			break;
36983737Shx147065 		}
36993737Shx147065 		PCWLDBG((CE_NOTE, "associate"));
37003737Shx147065 		break;
37013737Shx147065 	default:
37023737Shx147065 		break;
37033737Shx147065 	}
37043737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
37053737Shx147065 	if ((cmd == WLAN_SET_PARAM) && (connect)) {
37063737Shx147065 		(void) pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0);
37073737Shx147065 		if (pcwl_p->pcwl_connect_timeout_id != 0) {
37083737Shx147065 			(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
37093737Shx147065 			pcwl_p->pcwl_connect_timeout_id = 0;
37103737Shx147065 		}
37113737Shx147065 		pcwl_p->pcwl_connect_timeout_id = timeout(pcwl_connect_timeout,
37123737Shx147065 		    pcwl_p, 2 * drv_usectohz(1000000));
37133737Shx147065 	}
37143737Shx147065 	return (ret);
37153737Shx147065 }
37163737Shx147065 
37173737Shx147065 static void
37183737Shx147065 pcwl_wlan_ioctl(pcwl_maci_t *pcwl_p, queue_t *wq, mblk_t *mp, uint32_t cmd)
37193737Shx147065 {
37203737Shx147065 
37213737Shx147065 	struct	iocblk	*iocp = (struct iocblk *)mp->b_rptr;
37223737Shx147065 	wldp_t 	*infp;
37233737Shx147065 	uint32_t len, ret;
37243737Shx147065 	mblk_t		*mp1;
37253737Shx147065 
37263737Shx147065 	/*
37273737Shx147065 	 * sanity check
37283737Shx147065 	 */
37293737Shx147065 	if (iocp->ioc_count == 0 || !(mp1 = mp->b_cont)) {
37303737Shx147065 		miocnak(wq, mp, 0, EINVAL);
37313737Shx147065 		return;
37323737Shx147065 	}
37333737Shx147065 
37343737Shx147065 	/*
37353737Shx147065 	 * assuming single data block
37363737Shx147065 	 */
37373737Shx147065 	if (mp1->b_cont) {
37383737Shx147065 		freemsg(mp1->b_cont);
37393737Shx147065 		mp1->b_cont = NULL;
37403737Shx147065 	}
37413737Shx147065 
37423737Shx147065 	/*
37433737Shx147065 	 * we will overwrite everything
37443737Shx147065 	 */
37453737Shx147065 	mp1->b_wptr = mp1->b_rptr;
37463737Shx147065 
37473737Shx147065 	infp = (wldp_t *)mp1->b_rptr;
37483737Shx147065 	PCWLDBG((CE_NOTE, "pcwl: wldp->length=0x%x\n", infp->wldp_length));
37493737Shx147065 	PCWLDBG((CE_NOTE, "pcwl: wldp->type =:%s\n",
37503737Shx147065 	    infp->wldp_type == NET_802_11 ? "NET_802_11" : "Unknown"));
37513737Shx147065 	PCWLDBG((CE_NOTE, "pcwl: wldp->id=0x%x\n", infp->wldp_id));
37523737Shx147065 	PCWLDBG((CE_NOTE, "pcwl: wldp->result=0x%x\n", infp->wldp_result));
37533737Shx147065 
37543737Shx147065 	ret = pcwl_getset(mp1, pcwl_p, cmd);
37553737Shx147065 	len = msgdsize(mp1);
37563737Shx147065 	PCWLDBG((CE_CONT, "pcwl: ioctl message length = %d\n", len));
37573737Shx147065 	miocack(wq, mp, len, ret);
37583737Shx147065 
37593737Shx147065 }
37603737Shx147065 
37613737Shx147065 
37623737Shx147065 static void
37633737Shx147065 pcwl_ioctl(void *arg, queue_t *wq, mblk_t *mp)
37643737Shx147065 {
37653737Shx147065 	struct iocblk *iocp;
37663737Shx147065 	uint32_t cmd, ret;
37673737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
37683737Shx147065 	boolean_t need_privilege = B_TRUE;
37693737Shx147065 
37703737Shx147065 	/*
37713737Shx147065 	 * Validate the command before bothering with the mutexen ...
37723737Shx147065 	 */
37733737Shx147065 	iocp = (struct iocblk *)mp->b_rptr;
37743737Shx147065 	iocp->ioc_error = 0;
37753737Shx147065 	cmd = iocp->ioc_cmd;
37763737Shx147065 	switch (cmd) {
37773737Shx147065 	default:
37783737Shx147065 		PCWLDBG((CE_CONT, "pcwl_ioctl: unknown cmd 0x%x", cmd));
37793737Shx147065 		miocnak(wq, mp, 0, EINVAL);
37803737Shx147065 		return;
37813737Shx147065 	case WLAN_GET_PARAM:
37823737Shx147065 		need_privilege = B_TRUE;
37833737Shx147065 		break;
37843737Shx147065 	case WLAN_SET_PARAM:
37853737Shx147065 	case WLAN_COMMAND:
37863737Shx147065 		break;
37873737Shx147065 	}
37883737Shx147065 	/*
37893737Shx147065 	 * Check net_config privilege
37903737Shx147065 	 */
37913737Shx147065 	if (need_privilege) {
37923737Shx147065 		if (ret = secpolicy_net_config(iocp->ioc_cr, B_FALSE)) {
37933737Shx147065 			miocnak(wq, mp, 0, ret);
37943737Shx147065 			return;
37953737Shx147065 		}
37963737Shx147065 	}
37973737Shx147065 	pcwl_wlan_ioctl(pcwl_p, wq, mp, cmd);
37983737Shx147065 }
3799