xref: /onnv-gate/usr/src/uts/common/io/pcan/pcan.c (revision 7507:dc95e568b3fd)
13737Shx147065 /*
27249Sff224033  * 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 #include <sys/conf.h>
393737Shx147065 #include <sys/ddi.h>
403737Shx147065 #include <sys/sunddi.h>
413737Shx147065 #include <sys/dlpi.h>
423737Shx147065 #include <sys/ethernet.h>
433737Shx147065 #include <sys/strsun.h>
443737Shx147065 #include <sys/stat.h>
453737Shx147065 #include <sys/byteorder.h>
463737Shx147065 #include <sys/pccard.h>
473737Shx147065 #include <sys/pci.h>
483737Shx147065 #include <sys/policy.h>
493737Shx147065 #include <sys/mac.h>
503737Shx147065 #include <sys/stream.h>
513737Shx147065 #include <inet/common.h>
523737Shx147065 #include <inet/nd.h>
533737Shx147065 #include <inet/mi.h>
543737Shx147065 
553737Shx147065 #include "pcan.h"
563737Shx147065 #include <sys/mac_wifi.h>
573737Shx147065 #include <inet/wifi_ioctl.h>
583737Shx147065 
593737Shx147065 #ifdef	DEBUG
603737Shx147065 #define	PCAN_DBG_BASIC		0x1
613737Shx147065 #define	PCAN_DBG_INFO		0x2
623737Shx147065 #define	PCAN_DBG_SEND		0x4
633737Shx147065 #define	PCAN_DBG_RCV		0x8
643737Shx147065 #define	PCAN_DBG_LINKINFO	0x10
653737Shx147065 #define	PCAN_DBG_FW_VERSION	0x20
663737Shx147065 #define	PCAN_DBG_CMD		0x40
673737Shx147065 uint32_t pcan_debug = 0;
683737Shx147065 #define	PCANDBG(x) \
693737Shx147065 	if (pcan_debug & PCAN_DBG_BASIC) cmn_err x
703737Shx147065 #else
713737Shx147065 #define	PCANDBG(x)
723737Shx147065 #endif
733737Shx147065 
743737Shx147065 static ddi_device_acc_attr_t accattr = {
753737Shx147065 		DDI_DEVICE_ATTR_V0,
763737Shx147065 		DDI_STRUCTURE_LE_ACC,
773737Shx147065 		DDI_STRICTORDER_ACC,
783737Shx147065 };
793737Shx147065 
803737Shx147065 static ddi_dma_attr_t control_cmd_dma_attr = {
813737Shx147065 	DMA_ATTR_V0,		/* version of this structure */
823737Shx147065 	0,			/* lowest usable address */
833737Shx147065 	0xffffffffffffffffull,	/* highest usable address */
843737Shx147065 	0xffffffffull,		/* maximum DMAable byte count */
853737Shx147065 	4,			/* alignment in bytes */
863737Shx147065 	0xfff,			/* burst sizes (any) */
873737Shx147065 	1,			/* minimum transfer */
883737Shx147065 	0xffffull,		/* maximum transfer */
893737Shx147065 	0xffffffffffffffffull,	/* maximum segment length */
903737Shx147065 	1,			/* maximum number of segments */
913737Shx147065 	1,			/* granularity */
923737Shx147065 	0,			/* flags (reserved) */
933737Shx147065 };
943737Shx147065 
953737Shx147065 void *pcan_soft_state_p = NULL;
963737Shx147065 static int pcan_device_type;
973737Shx147065 
983737Shx147065 mac_callbacks_t pcan_m_callbacks = {
993737Shx147065 	MC_IOCTL,
1003737Shx147065 	pcan_gstat,
1013737Shx147065 	pcan_start,
1023737Shx147065 	pcan_stop,
1033737Shx147065 	pcan_prom,
1043737Shx147065 	pcan_sdmulti,
1053737Shx147065 	pcan_saddr,
1063737Shx147065 	pcan_tx,
1073737Shx147065 	NULL,
1083737Shx147065 	pcan_ioctl
1093737Shx147065 };
1103737Shx147065 
1113737Shx147065 static char *pcan_name_str = "pcan";
1123737Shx147065 
1133737Shx147065 DDI_DEFINE_STREAM_OPS(pcan_dev_ops, nulldev, pcan_probe, pcan_attach,
1143737Shx147065 
1153737Shx147065     pcan_detach, nodev, NULL, D_MP, NULL);
1163737Shx147065 
1173737Shx147065 extern struct mod_ops mod_driverops;
1183737Shx147065 static struct modldrv modldrv = {
1193737Shx147065 	&mod_driverops,
1203737Shx147065 	"Cisco-Aironet 802.11b driver",
1213737Shx147065 	&pcan_dev_ops
1223737Shx147065 };
1233737Shx147065 
1243737Shx147065 static struct modlinkage modlinkage = {
1253737Shx147065 	MODREV_1, (void *)&modldrv, NULL
1263737Shx147065 	};
1273737Shx147065 
1283737Shx147065 int
1293737Shx147065 _init(void)
1303737Shx147065 {
1313737Shx147065 	int stat;
1323737Shx147065 
1333737Shx147065 	/* Allocate soft state */
1343737Shx147065 	if ((stat = ddi_soft_state_init(&pcan_soft_state_p,
1353737Shx147065 	    sizeof (pcan_maci_t), 2)) != DDI_SUCCESS)
1363737Shx147065 		return (stat);
1373737Shx147065 
1383737Shx147065 	mac_init_ops(&pcan_dev_ops, "pcan");
1393737Shx147065 	stat = mod_install(&modlinkage);
1403737Shx147065 	if (stat != 0) {
1413737Shx147065 		mac_fini_ops(&pcan_dev_ops);
1423737Shx147065 		ddi_soft_state_fini(&pcan_soft_state_p);
1433737Shx147065 	}
1443737Shx147065 
1453737Shx147065 	return (stat);
1463737Shx147065 }
1473737Shx147065 
1483737Shx147065 int
1493737Shx147065 _fini(void)
1503737Shx147065 {
1513737Shx147065 	int stat;
1523737Shx147065 
1533737Shx147065 	stat = mod_remove(&modlinkage);
1543737Shx147065 	if (stat != DDI_SUCCESS)
1553737Shx147065 		return (stat);
1563737Shx147065 	mac_fini_ops(&pcan_dev_ops);
1573737Shx147065 	ddi_soft_state_fini(&pcan_soft_state_p);
1583737Shx147065 	return (stat);
1593737Shx147065 }
1603737Shx147065 
1613737Shx147065 int
1623737Shx147065 _info(struct modinfo *modinfop)
1633737Shx147065 {
1643737Shx147065 	return (mod_info(&modlinkage, modinfop));
1653737Shx147065 }
1663737Shx147065 
1673737Shx147065 static int
1683737Shx147065 pcan_probe(dev_info_t *dip)
1693737Shx147065 {
1703737Shx147065 	int len, ret;
1713737Shx147065 	char *buf;
1723737Shx147065 	dev_info_t *pdip = ddi_get_parent(dip);
1733737Shx147065 
1743737Shx147065 	PCANDBG((CE_NOTE, "pcan probe: parent dip=0x%p-%s(%d)\n", (void *)pdip,
1753737Shx147065 	    ddi_driver_name(pdip), ddi_get_instance(pdip)));
1763737Shx147065 
1773737Shx147065 	ret = ddi_getlongprop(DDI_DEV_T_ANY, pdip, 0, "device_type",
1783737Shx147065 	    (caddr_t)&buf, &len);
1793737Shx147065 	if (ret != DDI_SUCCESS)
1803737Shx147065 		return (DDI_PROBE_FAILURE);
1813737Shx147065 
1823737Shx147065 	PCANDBG((CE_NOTE, "pcan probe: device_type %s\n", buf));
1833737Shx147065 	if ((strcmp(buf, "pccard") == 0) || (strcmp(buf, "pcmcia") == 0)) {
1843737Shx147065 		pcan_device_type = PCAN_DEVICE_PCCARD;
1853737Shx147065 #ifdef DEBUG
1863737Shx147065 		if (pcan_debug & PCAN_DBG_FW_VERSION) {
1873737Shx147065 			cmn_err(CE_NOTE, "Cisco 802.11 pccard\n");
1883737Shx147065 		}
1893737Shx147065 #endif
1903737Shx147065 		ret = DDI_PROBE_SUCCESS;
1913737Shx147065 	} else if (strcmp(buf, "pci") == 0) {
1923737Shx147065 		pcan_device_type = PCAN_DEVICE_PCI;
1933737Shx147065 #ifdef DEBUG
1943737Shx147065 		if (pcan_debug & PCAN_DBG_FW_VERSION) {
1953737Shx147065 			cmn_err(CE_NOTE, "Cisco 802.11 minipci card\n");
1963737Shx147065 		}
1973737Shx147065 #endif
1983737Shx147065 		ret = DDI_PROBE_SUCCESS;
1993737Shx147065 	} else {
2003737Shx147065 		cmn_err(CE_NOTE, "pcan probe: unsupported card\n");
2013737Shx147065 		ret = DDI_PROBE_FAILURE;
2023737Shx147065 	}
2033737Shx147065 
2043737Shx147065 	kmem_free(buf, len);
2053737Shx147065 	return (ret);
2063737Shx147065 }
2073737Shx147065 
2083737Shx147065 static int
2093737Shx147065 pcan_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2103737Shx147065 {
2113737Shx147065 	int ret;
2123737Shx147065 	int instance;
2133737Shx147065 	uint16_t stat;
2143737Shx147065 	uint32_t err;
2153737Shx147065 	pcan_maci_t *pcan_p;
2163737Shx147065 	wifi_data_t	wd = { 0 };
2173737Shx147065 	mac_register_t	*macp;
2183737Shx147065 	modify_config_t cfgmod;
2193737Shx147065 	char strbuf[256];
2203737Shx147065 
2213737Shx147065 	PCANDBG((CE_NOTE, "dip=0x%p cmd=%x\n", (void *)dip, cmd));
2223737Shx147065 	if (cmd != DDI_ATTACH)
2233737Shx147065 		goto attach_fail1;
2243737Shx147065 
2253737Shx147065 	/*
2263737Shx147065 	 * Since this driver is porting from freebsd, so just like
2273737Shx147065 	 * the original driver, the minipci card doesn't work on amd64
2283737Shx147065 	 * machine.
2293737Shx147065 	 * For sparc, since no pci card is available for the test, so this
2303737Shx147065 	 * version doesn't support sparc. If there is card available and
2313737Shx147065 	 * requirement, future version will try to support sparc.
2323737Shx147065 	 * This driver works well for minipci card on 32bit x86
2333737Shx147065 	 * machine, so keep the code to just support minipci card on 32bit
2343737Shx147065 	 * mode.
2353737Shx147065 	 */
2363737Shx147065 #if defined(sparc) || defined(__sparc)
2373737Shx147065 	if (pcan_device_type == PCAN_DEVICE_PCI) {
2383737Shx147065 		cmn_err(CE_NOTE, "pcan attach: this driver does not support "
2393737Shx147065 		    "PCI/MiniPCI card on Sparc\n");
2403737Shx147065 		goto attach_fail1;
2413737Shx147065 	}
2423737Shx147065 #endif /* sparc */
2433737Shx147065 #if defined(__amd64)
2443737Shx147065 	if (pcan_device_type == PCAN_DEVICE_PCI) {
2453737Shx147065 		cmn_err(CE_NOTE, "pcan attach: this driver does not support "
2463737Shx147065 		    "PCI/MiniPCI card on amd64\n");
2473737Shx147065 		goto attach_fail1;
2483737Shx147065 	}
2493737Shx147065 #endif /* amd64 */
2503737Shx147065 
2513737Shx147065 	/* Allocate soft state associated with this instance. */
2523737Shx147065 	if (ddi_soft_state_zalloc(pcan_soft_state_p,
2533737Shx147065 	    ddi_get_instance(dip)) != DDI_SUCCESS) {
2543737Shx147065 		cmn_err(CE_CONT, "pcan attach: alloc softstate failed\n");
2553737Shx147065 		goto attach_fail1;
2563737Shx147065 	}
2573737Shx147065 	pcan_p = (pcan_maci_t *)ddi_get_soft_state(pcan_soft_state_p,
2583737Shx147065 	    ddi_get_instance(dip));
2593737Shx147065 
2603737Shx147065 	pcan_p->pcan_device_type = pcan_device_type;
2613737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
2623737Shx147065 		if (ddi_regs_map_setup(dip, 0,
2633737Shx147065 		    (caddr_t *)&pcan_p->pcan_cfg_base, 0, 0,
2643737Shx147065 		    &accattr, &pcan_p->pcan_cfg_handle) != DDI_SUCCESS)
2653737Shx147065 			goto attach_fail2;
2663737Shx147065 
2673737Shx147065 		stat = ddi_get16(pcan_p->pcan_cfg_handle,
2683737Shx147065 		    (uint16_t *)(pcan_p->pcan_cfg_base + PCI_CONF_COMM));
2693737Shx147065 		stat |= (PCI_COMM_IO | PCI_COMM_MAE);
2703737Shx147065 		ddi_put16(pcan_p->pcan_cfg_handle,
2713737Shx147065 		    (uint16_t *)(pcan_p->pcan_cfg_base + PCI_CONF_COMM), stat);
2723737Shx147065 
2733737Shx147065 		ddi_regs_map_free(&pcan_p->pcan_cfg_handle);
2743737Shx147065 		if (ddi_regs_map_setup(dip, 1, (caddr_t *)&pcan_p->pcan_bar0,
2753737Shx147065 		    0, 0, &accattr, &pcan_p->pcan_handle0) != DDI_SUCCESS)
2763737Shx147065 			goto attach_fail3;
2773737Shx147065 		if (ddi_regs_map_setup(dip, 2, (caddr_t *)&pcan_p->pcan_bar1,
2783737Shx147065 		    0, 0, &accattr, &pcan_p->pcan_handle1) != DDI_SUCCESS)
2793737Shx147065 			goto attach_fail3;
2803737Shx147065 		if (ddi_regs_map_setup(dip, 3, (caddr_t *)&pcan_p->pcan_bar2,
2813737Shx147065 		    0, 0, &accattr, &pcan_p->pcan_handle2) != DDI_SUCCESS)
2823737Shx147065 			goto attach_fail3;
2833737Shx147065 	}
2843737Shx147065 
2853737Shx147065 	pcan_p->pcan_dip		= dip;
2863737Shx147065 	pcan_p->pcan_flag		= 0;
2873737Shx147065 	pcan_p->glds_nocarrier		= 0;
2883737Shx147065 	pcan_p->glds_noxmtbuf		= 0;
2893737Shx147065 	pcan_p->glds_norcvbuf		= 0;
2903737Shx147065 	pcan_p->pcan_socket		= ddi_getprop(DDI_DEV_T_NONE, dip,
2914343Sgd78059 	    DDI_PROP_DONTPASS, "socket", -1);
2923737Shx147065 
2933737Shx147065 	pcan_p->pcan_reschedule_need = B_FALSE;
2943737Shx147065 	pcan_p->pcan_info_softint_pending = 0;
2953737Shx147065 	pcan_p->pcan_reset_delay = ddi_getprop(DDI_DEV_T_ANY, dip,
2963737Shx147065 	    DDI_PROP_DONTPASS, "reset-delay", 5000);
2973737Shx147065 
2983737Shx147065 	if (ddi_get_iblock_cookie(dip,
2993737Shx147065 	    0, &pcan_p->pcan_ib_cookie) != DDI_SUCCESS) {
3003737Shx147065 		cmn_err(CE_WARN, "pcan attach: get_iblk_cookie failed\n");
3013737Shx147065 		goto attach_fail3;
3023737Shx147065 	}
3033737Shx147065 
3043737Shx147065 	mutex_init(&pcan_p->pcan_glock, NULL,
3053737Shx147065 	    MUTEX_DRIVER, pcan_p->pcan_ib_cookie);
3063737Shx147065 	mutex_init(&pcan_p->pcan_scanlist_lock, NULL,
3073737Shx147065 	    MUTEX_DRIVER, pcan_p->pcan_ib_cookie);
3083737Shx147065 	mutex_init(&pcan_p->pcan_txring.an_tx_lock, NULL,
3093737Shx147065 	    MUTEX_DRIVER, pcan_p->pcan_ib_cookie);
3103737Shx147065 
3113737Shx147065 	if (ret = ddi_add_softintr(dip, DDI_SOFTINT_LOW,
3123737Shx147065 	    &pcan_p->pcan_info_softint_id, &pcan_p->pcan_ib_cookie, NULL,
3133737Shx147065 	    pcan_info_softint, (caddr_t)pcan_p)) {
3143737Shx147065 		cmn_err(CE_WARN, "pcan attach: add info_softintr failed\n");
3153737Shx147065 		goto attach_fail3a;
3163737Shx147065 	}
3173737Shx147065 
3183737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
3193737Shx147065 		if (ret = ddi_add_intr(dip, 0, NULL, NULL,
3203737Shx147065 		    pcan_intr, (caddr_t)pcan_p)) {
3213737Shx147065 			cmn_err(CE_WARN, "pcan attach: add intr failed\n");
3223737Shx147065 			goto attach_fail4;
3233737Shx147065 		}
3243737Shx147065 	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
3253737Shx147065 		if (ret = pcan_register_cs(dip, pcan_p)) {
3263737Shx147065 			PCANDBG((CE_NOTE, "pcan attach: register_cs failed"
3273737Shx147065 			    " %x\n", ret));
3283737Shx147065 			goto attach_fail4;
3293737Shx147065 		}
3303737Shx147065 	} else {
3313737Shx147065 		cmn_err(CE_WARN, "pcan attach: unsupported device type\n");
3323737Shx147065 		goto attach_fail4;
3333737Shx147065 	}
3343737Shx147065 
3353737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
3363737Shx147065 	pcan_reset_backend(pcan_p, pcan_p->pcan_reset_delay);
3373737Shx147065 	/* leaves IF down, intr disabled */
3383737Shx147065 
3393737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
3403737Shx147065 		if (ret = pcan_init_dma(dip, pcan_p)) {
3413737Shx147065 			cmn_err(CE_WARN, "pcan init_dma: failed\n");
3423737Shx147065 			mutex_exit(&pcan_p->pcan_glock);
3433737Shx147065 			goto attach_fail5;
3443737Shx147065 		}
3453737Shx147065 	}
3463737Shx147065 	if (ret = pcan_get_cap(pcan_p)) { /* sets macaddr for gld_register */
3473737Shx147065 		cmn_err(CE_WARN, "pcan attach: get_cap failed %x\n", ret);
3483737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
3493737Shx147065 		goto attach_fail6;
3503737Shx147065 	}
3513737Shx147065 
3523737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
3533737Shx147065 	/*
3543737Shx147065 	 * Provide initial settings for the WiFi plugin; whenever this
3553737Shx147065 	 * information changes, we need to call mac_pdata_update()
3563737Shx147065 	 */
3573737Shx147065 	wd.wd_secalloc = WIFI_SEC_NONE;
3583737Shx147065 	wd.wd_opmode = IEEE80211_M_STA;
3593737Shx147065 
3603737Shx147065 	macp = mac_alloc(MAC_VERSION);
3613737Shx147065 	if (macp == NULL) {
3623737Shx147065 		PCANDBG((CE_NOTE, "pcan attach: "
3633737Shx147065 		    "MAC version mismatch\n"));
3643737Shx147065 		goto attach_fail6;
3653737Shx147065 	}
3663737Shx147065 
3673737Shx147065 	macp->m_type_ident	= MAC_PLUGIN_IDENT_WIFI;
3683737Shx147065 	macp->m_driver		= pcan_p;
3693737Shx147065 	macp->m_dip		= dip;
3703737Shx147065 	macp->m_src_addr	= pcan_p->pcan_mac_addr;
3713737Shx147065 	macp->m_callbacks	= &pcan_m_callbacks;
3723737Shx147065 	macp->m_min_sdu		= 0;
3733737Shx147065 	macp->m_max_sdu		= IEEE80211_MTU;
3743737Shx147065 	macp->m_pdata		= &wd;
3753737Shx147065 	macp->m_pdata_size	= sizeof (wd);
3763737Shx147065 
3773737Shx147065 	err = mac_register(macp, &pcan_p->pcan_mh);
3783737Shx147065 	mac_free(macp);
3793737Shx147065 	if (err != 0) {
3803737Shx147065 		PCANDBG((CE_NOTE, "pcan attach: "
3813737Shx147065 		    "mac_register err\n"));
3823737Shx147065 		goto attach_fail6;
3833737Shx147065 	}
3843737Shx147065 
3853737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
3863737Shx147065 		/* turn on CS interrupt */
3873737Shx147065 		cfgmod.Attributes = CONF_ENABLE_IRQ_STEERING |
3883737Shx147065 		    CONF_IRQ_CHANGE_VALID;
3893737Shx147065 		cfgmod.Vpp1 = 50;
3903737Shx147065 		cfgmod.Vpp2 = 50;
3913737Shx147065 		(void) csx_ModifyConfiguration(pcan_p->pcan_chdl, &cfgmod);
3923737Shx147065 
3933737Shx147065 		mutex_enter(&pcan_p->pcan_glock);
3943737Shx147065 		if (ret = pcan_init_nicmem(pcan_p)) {
3953737Shx147065 			cmn_err(CE_WARN, "pcan attach: init_nicmem failed %x\n",
3963737Shx147065 			    ret);
3973737Shx147065 			mutex_exit(&pcan_p->pcan_glock);
3983737Shx147065 			goto attach_fail7;
3993737Shx147065 		}
4003737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
4013737Shx147065 	}
4023737Shx147065 	(void) ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
4033737Shx147065 	    "bad-rids", (caddr_t)&pcan_p->pcan_badrids,
4043737Shx147065 	    &pcan_p->pcan_badrids_len);
4053737Shx147065 
4063737Shx147065 	pcan_p->an_config.an_rxmode = AN_NORMAL_RXMODE;
4073737Shx147065 	ether_copy(pcan_p->pcan_mac_addr, pcan_p->an_config.an_macaddr);
4083737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
4093737Shx147065 	list_create(&pcan_p->an_scan_list, sizeof (an_scan_list_t),
4103737Shx147065 	    offsetof(an_scan_list_t, an_scan_node));
4113737Shx147065 	pcan_p->an_scan_num = 0;
4123737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
4133737Shx147065 	pcan_p->an_scanlist_timeout_id = timeout(pcan_scanlist_timeout,
4143737Shx147065 	    pcan_p, drv_usectohz(1000000));
4153737Shx147065 
4163737Shx147065 	instance = ddi_get_instance(dip);
4173737Shx147065 	(void) snprintf(strbuf, sizeof (strbuf), "pcan%d", instance);
4183737Shx147065 	if (ddi_create_minor_node(dip, strbuf, S_IFCHR,
4193737Shx147065 	    instance + 1, DDI_NT_NET_WIFI, 0) != DDI_SUCCESS) {
4203737Shx147065 		goto attach_fail8;
4213737Shx147065 	}
4223737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
4233737Shx147065 	PCAN_DISABLE_INTR_CLEAR(pcan_p);
4243737Shx147065 	(void) pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0);
4253737Shx147065 	pcan_p->pcan_flag |= PCAN_ATTACHED;
4263737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
4273737Shx147065 		pcan_p->pcan_flag |= PCAN_CARD_READY;
4283737Shx147065 	}
4293737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
4303737Shx147065 	return (DDI_SUCCESS);
4313737Shx147065 attach_fail8:
4323737Shx147065 	if (pcan_p->an_scanlist_timeout_id != 0) {
4333737Shx147065 		(void) untimeout(pcan_p->an_scanlist_timeout_id);
4343737Shx147065 		pcan_p->an_scanlist_timeout_id = 0;
4353737Shx147065 	}
4363737Shx147065 	list_destroy(&pcan_p->an_scan_list);
4373737Shx147065 attach_fail7:
4383737Shx147065 	(void) mac_unregister(pcan_p->pcan_mh);
4393737Shx147065 attach_fail6:
4403737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI)
4413737Shx147065 		pcan_free_dma(pcan_p);
4423737Shx147065 attach_fail5:
4433737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
4443737Shx147065 		ddi_remove_intr(dip, 0, pcan_p->pcan_ib_cookie);
4453737Shx147065 	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
4463737Shx147065 		pcan_unregister_cs(pcan_p);
4473737Shx147065 	}
4483737Shx147065 attach_fail4:
4493737Shx147065 	if (pcan_p->pcan_info_softint_id)
4503737Shx147065 		ddi_remove_softintr(pcan_p->pcan_info_softint_id);
4513737Shx147065 attach_fail3a:
4523737Shx147065 	pcan_destroy_locks(pcan_p);
4533737Shx147065 attach_fail3:
4543737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
4553737Shx147065 		if (pcan_p->pcan_handle0)
4563737Shx147065 			ddi_regs_map_free(&pcan_p->pcan_handle0);
4573737Shx147065 		if (pcan_p->pcan_handle1)
4583737Shx147065 			ddi_regs_map_free(&pcan_p->pcan_handle1);
4593737Shx147065 		if (pcan_p->pcan_handle2)
4603737Shx147065 			ddi_regs_map_free(&pcan_p->pcan_handle2);
4613737Shx147065 	}
4623737Shx147065 attach_fail2:
4633737Shx147065 	ddi_soft_state_free(pcan_soft_state_p, ddi_get_instance(dip));
4643737Shx147065 attach_fail1:
4653737Shx147065 	return (DDI_FAILURE);
4663737Shx147065 }
4673737Shx147065 
4683737Shx147065 static int
4693737Shx147065 pcan_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4703737Shx147065 {
4713737Shx147065 	pcan_maci_t *pcan_p;
4723737Shx147065 	an_scan_list_t *scan_item0;
4733737Shx147065 	int ret;
4743737Shx147065 	pcan_p = ddi_get_soft_state(pcan_soft_state_p, ddi_get_instance(dip));
4753737Shx147065 
4763737Shx147065 	if (cmd != DDI_DETACH)
4773737Shx147065 		return (DDI_FAILURE);
4783737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_ATTACHED))
4793737Shx147065 		return (DDI_FAILURE);
480*7507SXinghua.Wen@Sun.COM 
481*7507SXinghua.Wen@Sun.COM 	ret = mac_disable(pcan_p->pcan_mh);
482*7507SXinghua.Wen@Sun.COM 	if (ret != 0)
483*7507SXinghua.Wen@Sun.COM 		return (DDI_FAILURE);
484*7507SXinghua.Wen@Sun.COM 
4853737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
4863737Shx147065 		mutex_enter(&pcan_p->pcan_glock);
4873737Shx147065 		pcan_stop_locked(pcan_p);
4883737Shx147065 		PCAN_DISABLE_INTR(pcan_p);
4893737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
4903737Shx147065 	}
4913737Shx147065 	if (pcan_p->an_scanlist_timeout_id != 0) {
4923737Shx147065 		(void) untimeout(pcan_p->an_scanlist_timeout_id);
4933737Shx147065 		pcan_p->an_scanlist_timeout_id = 0;
4943737Shx147065 	}
4953737Shx147065 	if (pcan_p->pcan_connect_timeout_id != 0) {
4963737Shx147065 		(void) untimeout(pcan_p->pcan_connect_timeout_id);
4973737Shx147065 		pcan_p->pcan_connect_timeout_id = 0;
4983737Shx147065 	}
4993737Shx147065 	mutex_enter(&pcan_p->pcan_scanlist_lock);
5003737Shx147065 	scan_item0 = list_head(&pcan_p->an_scan_list);
5013737Shx147065 	while (scan_item0) {
5023737Shx147065 		pcan_delete_scan_item(pcan_p, scan_item0);
5033737Shx147065 		scan_item0 = list_head(&pcan_p->an_scan_list);
5043737Shx147065 	}
5053737Shx147065 	list_destroy(&pcan_p->an_scan_list);
5063737Shx147065 	mutex_exit(&pcan_p->pcan_scanlist_lock);
5073737Shx147065 
508*7507SXinghua.Wen@Sun.COM 	(void) mac_unregister(pcan_p->pcan_mh);
5093737Shx147065 
5103737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
5113737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
5123737Shx147065 		ddi_remove_intr(dip, 0, pcan_p->pcan_ib_cookie);
5133737Shx147065 		pcan_free_dma(pcan_p);
5143737Shx147065 		if (pcan_p->pcan_handle0)
5153737Shx147065 			ddi_regs_map_free(&pcan_p->pcan_handle0);
5163737Shx147065 		if (pcan_p->pcan_handle1)
5173737Shx147065 			ddi_regs_map_free(&pcan_p->pcan_handle1);
5183737Shx147065 		if (pcan_p->pcan_handle2)
5193737Shx147065 			ddi_regs_map_free(&pcan_p->pcan_handle2);
5203737Shx147065 	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
5213737Shx147065 		pcan_unregister_cs(pcan_p);
5223737Shx147065 	} else {
5233737Shx147065 		cmn_err(CE_WARN, "pcan detach: unsupported device type\n");
5243737Shx147065 	}
5253737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
5263737Shx147065 	pcan_destroy_locks(pcan_p);
5273737Shx147065 	if (pcan_p->pcan_info_softint_id)
5283737Shx147065 		ddi_remove_softintr(pcan_p->pcan_info_softint_id);
5293737Shx147065 
5303737Shx147065 	if (pcan_p->pcan_badrids_len)
5313737Shx147065 		kmem_free(pcan_p->pcan_badrids, pcan_p->pcan_badrids_len);
5323737Shx147065 
5333737Shx147065 	ddi_soft_state_free(pcan_soft_state_p, ddi_get_instance(dip));
5343737Shx147065 	ddi_remove_minor_node(dip, NULL);
5353737Shx147065 
5363737Shx147065 	return (DDI_SUCCESS);
5373737Shx147065 }
5383737Shx147065 
5393737Shx147065 /*
5403737Shx147065  * card services and event handlers
5413737Shx147065  */
5423737Shx147065 
5433737Shx147065 static int
5443737Shx147065 pcan_register_cs(dev_info_t *dip, pcan_maci_t *pcan_p)
5453737Shx147065 {
5463737Shx147065 	int ret;
5473737Shx147065 	client_reg_t cr;
5483737Shx147065 	client_handle_t chdl; /* uint encoding of socket, function, client */
5493737Shx147065 	get_status_t card_status;
5503737Shx147065 	request_socket_mask_t sock_req;
5513737Shx147065 
5523737Shx147065 	bzero(&cr, sizeof (cr));
5533737Shx147065 	cr.Attributes	= INFO_IO_CLIENT|INFO_CARD_EXCL|INFO_CARD_SHARE;
5543737Shx147065 	cr.EventMask	= CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
5553737Shx147065 	    CS_EVENT_REGISTRATION_COMPLETE | CS_EVENT_CARD_REMOVAL_LOWP |
5563737Shx147065 	    CS_EVENT_CARD_READY | CS_EVENT_PM_RESUME | CS_EVENT_PM_SUSPEND |
5573737Shx147065 	    CS_EVENT_CLIENT_INFO;
5583737Shx147065 	cr.event_callback_args.client_data = pcan_p;
5593737Shx147065 	cr.Version = CS_VERSION;
5603737Shx147065 	cr.event_handler = (csfunction_t *)pcan_ev_hdlr;
5613737Shx147065 	cr.dip = dip;
5623737Shx147065 	(void) strcpy(cr.driver_name, pcan_name_str);
5633737Shx147065 	if (ret = csx_RegisterClient(&chdl, &cr)) {
5643737Shx147065 		cmn_err(CE_WARN, "pcan: RegisterClient failed %x", ret);
5653737Shx147065 		goto regcs_ret;
5663737Shx147065 	}
5673737Shx147065 
5683737Shx147065 	pcan_p->pcan_chdl = chdl;
5693737Shx147065 
5703737Shx147065 	bzero(&card_status, sizeof (card_status));
5713737Shx147065 	(void) csx_GetStatus(chdl, &card_status);
5723737Shx147065 	PCANDBG((CE_NOTE, "pcan: getstat Sock=%x CState=%x SState=%x rState=%x",
5733737Shx147065 	    card_status.Socket, card_status.CardState,
5743737Shx147065 	    card_status.SocketState, card_status.raw_CardState));
5753737Shx147065 	if (!(card_status.CardState & CS_STATUS_CARD_INSERTED)) {
5763737Shx147065 		/* card is not present, why are we attaching ? */
5773737Shx147065 		ret = CS_NO_CARD;
5783737Shx147065 		goto unreg;
5793737Shx147065 	}
5803737Shx147065 	cv_init(&pcan_p->pcan_cscv, NULL, CV_DRIVER, NULL);
5813737Shx147065 	mutex_init(&pcan_p->pcan_cslock, NULL, MUTEX_DRIVER, *cr.iblk_cookie);
5823737Shx147065 	mutex_enter(&pcan_p->pcan_cslock);
5833737Shx147065 	if (ret = csx_MapLogSocket(chdl, &pcan_p->pcan_log_sock)) {
5843737Shx147065 		cmn_err(CE_WARN, "pcan: MapLogSocket failed %x", ret);
5853737Shx147065 		goto fail;
5863737Shx147065 	}
5873737Shx147065 	PCANDBG((CE_NOTE, "pcan: logsock: LogSock=%x PhyAdapter=%x PhySock=%x",
5883737Shx147065 	    pcan_p->pcan_log_sock.LogSocket,
5893737Shx147065 	    pcan_p->pcan_log_sock.PhyAdapter,
5903737Shx147065 	    pcan_p->pcan_log_sock.PhySocket));
5913737Shx147065 
5923737Shx147065 	/* turn on initialization events */
5933737Shx147065 	sock_req.Socket = 0;
5943737Shx147065 	sock_req.EventMask = CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
5953737Shx147065 	    CS_EVENT_REGISTRATION_COMPLETE;
5963737Shx147065 	if (ret = csx_RequestSocketMask(chdl, &sock_req)) {
5973737Shx147065 		cmn_err(CE_WARN, "pcan: RequestSocketMask failed %x\n", ret);
5983737Shx147065 		goto fail;
5993737Shx147065 	}
6003737Shx147065 
6013737Shx147065 	/* wait for and process card insertion events */
6023737Shx147065 	while (!(pcan_p->pcan_flag & PCAN_CARD_READY))
6033737Shx147065 		cv_wait(&pcan_p->pcan_cscv, &pcan_p->pcan_cslock);
6043737Shx147065 	mutex_exit(&pcan_p->pcan_cslock);
6053737Shx147065 
6063737Shx147065 	pcan_p->pcan_flag |= PCAN_CS_REGISTERED;
6073737Shx147065 	return (CS_SUCCESS);
6083737Shx147065 fail:
6093737Shx147065 	mutex_destroy(&pcan_p->pcan_cslock);
6103737Shx147065 	cv_destroy(&pcan_p->pcan_cscv);
6113737Shx147065 unreg:
6123737Shx147065 	(void) csx_DeregisterClient(chdl);
6133737Shx147065 regcs_ret:
6143737Shx147065 	pcan_p->pcan_flag &= ~PCAN_CS_REGISTERED;
6153737Shx147065 	return (ret);
6163737Shx147065 }
6173737Shx147065 
6183737Shx147065 static void
6193737Shx147065 pcan_unregister_cs(pcan_maci_t *pcan_p)
6203737Shx147065 {
6213737Shx147065 	int ret;
6223737Shx147065 	release_socket_mask_t mask;
6233737Shx147065 	mask.Socket = pcan_p->pcan_socket;
6243737Shx147065 
6253737Shx147065 	/*
6263737Shx147065 	 * The card service not registered means register_cs function
6273737Shx147065 	 * doesnot return TRUE. Then all the lelated resource has been
6283737Shx147065 	 * released in register_cs.
6293737Shx147065 	 */
6303737Shx147065 	if (!(pcan_p->pcan_flag | PCAN_CS_REGISTERED))
6313737Shx147065 		return;
6323737Shx147065 	(void) csx_ReleaseSocketMask(pcan_p->pcan_chdl, &mask);
6333737Shx147065 
6343737Shx147065 	if (pcan_p->pcan_flag & PCAN_CARD_READY) {
6353737Shx147065 		pcan_card_remove(pcan_p);
6363737Shx147065 		pcan_p->pcan_flag &= ~PCAN_CARD_READY;
6373737Shx147065 	}
6383737Shx147065 	mutex_destroy(&pcan_p->pcan_cslock);
6393737Shx147065 	cv_destroy(&pcan_p->pcan_cscv);
6403737Shx147065 	if (ret = csx_DeregisterClient(pcan_p->pcan_chdl))
6413737Shx147065 		cmn_err(CE_WARN, "pcan: deregister failed %x\n", ret);
6423737Shx147065 }
6433737Shx147065 static void
6443737Shx147065 pcan_destroy_locks(pcan_maci_t *pcan_p)
6453737Shx147065 {
6463737Shx147065 	mutex_destroy(&pcan_p->pcan_txring.an_tx_lock);
6473737Shx147065 	mutex_destroy(&pcan_p->pcan_scanlist_lock);
6483737Shx147065 	mutex_destroy(&pcan_p->pcan_glock);
6493737Shx147065 }
6503737Shx147065 
6513737Shx147065 static int
6523737Shx147065 pcan_ev_hdlr(event_t event, int priority, event_callback_args_t *arg)
6533737Shx147065 {
6543737Shx147065 	int ret = CS_SUCCESS;
6553737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg->client_data;
6563737Shx147065 	client_info_t *ci_p = (client_info_t *)&arg->client_info;
6573737Shx147065 
6583737Shx147065 	mutex_enter(&pcan_p->pcan_cslock);
6593737Shx147065 	switch (event) {
6603737Shx147065 	case CS_EVENT_CARD_INSERTION:
6613737Shx147065 		ret = pcan_card_insert(pcan_p);
6623737Shx147065 		cv_broadcast(&pcan_p->pcan_cscv);
6633737Shx147065 		break;
6643737Shx147065 	case CS_EVENT_REGISTRATION_COMPLETE:
6653737Shx147065 		cv_broadcast(&pcan_p->pcan_cscv);
6663737Shx147065 		break;
6673737Shx147065 	case CS_EVENT_CARD_REMOVAL:
6683737Shx147065 		if (priority & CS_EVENT_PRI_HIGH)
6693737Shx147065 			break;
6703737Shx147065 		pcan_card_remove(pcan_p);
6713737Shx147065 		cv_broadcast(&pcan_p->pcan_cscv);
6723737Shx147065 		break;
6733737Shx147065 	case CS_EVENT_CLIENT_INFO:
6743737Shx147065 		if (GET_CLIENT_INFO_SUBSVC(ci_p->Attributes) !=
6753737Shx147065 		    CS_CLIENT_INFO_SUBSVC_CS)
6763737Shx147065 			break;
6773737Shx147065 
6783737Shx147065 		ci_p->Revision = 0x0101;
6793737Shx147065 		ci_p->CSLevel = CS_VERSION;
6803737Shx147065 		ci_p->RevDate = CS_CLIENT_INFO_MAKE_DATE(9, 12, 14);
6813737Shx147065 		(void) strcpy(ci_p->ClientName, PCAN_IDENT_STRING);
6823737Shx147065 		(void) strcpy(ci_p->VendorName, CS_SUN_VENDOR_DESCRIPTION);
6833737Shx147065 		ci_p->Attributes |= CS_CLIENT_INFO_VALID;
6843737Shx147065 		break;
6853737Shx147065 	default:
6863737Shx147065 		ret = CS_UNSUPPORTED_EVENT;
6873737Shx147065 		break;
6883737Shx147065 	}
6893737Shx147065 	mutex_exit(&pcan_p->pcan_cslock);
6903737Shx147065 	return (ret);
6913737Shx147065 }
6923737Shx147065 
6933737Shx147065 static int
6943737Shx147065 pcan_card_insert(pcan_maci_t *pcan_p)
6953737Shx147065 {
6963737Shx147065 	int ret, hi, lo;
6973737Shx147065 	tuple_t tuple;
6983737Shx147065 	cisparse_t cisparse;
6993737Shx147065 	io_req_t	io;
7003737Shx147065 	irq_req_t	irq;
7013737Shx147065 	config_req_t	cfg;
7023737Shx147065 	cistpl_config_t config;
7033737Shx147065 	cistpl_cftable_entry_t *tbl_p;
7043737Shx147065 	register client_handle_t chdl = pcan_p->pcan_chdl;
7053737Shx147065 
7063737Shx147065 	bzero(&tuple, sizeof (tuple));
7073737Shx147065 	tuple.DesiredTuple = CISTPL_MANFID;
7083737Shx147065 	if (ret = csx_GetFirstTuple(chdl, &tuple)) {
7093737Shx147065 		cmn_err(CE_WARN, "pcan: get manufacture id failed %x\n", ret);
7103737Shx147065 		goto insert_ret;
7113737Shx147065 	}
7123737Shx147065 	bzero(&cisparse, sizeof (cisparse));
7133737Shx147065 	if (ret = csx_Parse_CISTPL_MANFID(chdl, &tuple, &cisparse.manfid)) {
7143737Shx147065 		cmn_err(CE_WARN, "pcan: parse manufacture id failed %x\n", ret);
7153737Shx147065 		goto insert_ret;
7163737Shx147065 	}
7173737Shx147065 	/* verify manufacture ID */
7183737Shx147065 	PCANDBG((CE_NOTE, "pcan: manufacturer_id=%x card=%x\n",
7193737Shx147065 	    cisparse.manfid.manf, cisparse.manfid.card));
7203737Shx147065 
7213737Shx147065 	bzero(&tuple, sizeof (tuple));
7223737Shx147065 	tuple.DesiredTuple = CISTPL_FUNCID;
7233737Shx147065 	if (ret = csx_GetFirstTuple(chdl, &tuple)) {
7243737Shx147065 		cmn_err(CE_WARN, "pcan: get function id failed %x\n", ret);
7253737Shx147065 		goto insert_ret;
7263737Shx147065 	}
7273737Shx147065 	bzero(&cisparse, sizeof (cisparse));
7283737Shx147065 	if (ret = csx_Parse_CISTPL_FUNCID(chdl, &tuple, &cisparse.funcid)) {
7293737Shx147065 		cmn_err(CE_WARN, "pcan: parse function id failed %x\n", ret);
7303737Shx147065 		goto insert_ret;
7313737Shx147065 	}
7323737Shx147065 	/* verify function ID */
7333737Shx147065 	PCANDBG((CE_NOTE, "funcid=%x\n", cisparse.funcid.function));
7343737Shx147065 
7353737Shx147065 	bzero(&tuple, sizeof (tuple));
7363737Shx147065 	tuple.DesiredTuple = CISTPL_CONFIG;
7373737Shx147065 	if (ret = csx_GetFirstTuple(chdl, &tuple)) {
7383737Shx147065 		cmn_err(CE_WARN, "pcan: get config failed %x\n", ret);
7393737Shx147065 		goto insert_ret;
7403737Shx147065 	}
7413737Shx147065 	bzero(&config, sizeof (config));
7423737Shx147065 	if (ret = csx_Parse_CISTPL_CONFIG(chdl, &tuple, &config)) {
7433737Shx147065 		cmn_err(CE_WARN, "pcan: parse config failed %x\n", ret);
7443737Shx147065 		goto insert_ret;
7453737Shx147065 	}
7463737Shx147065 	PCANDBG((CE_NOTE,
7473737Shx147065 	    "pcan: config present=%x nr=%x hr=%x regs[0]=%x base=%x last=%x\n",
7483737Shx147065 	    config.present, config.nr, config.hr, config.regs[0],
7493737Shx147065 	    config.base, config.last));
7503737Shx147065 
7513737Shx147065 	hi = 0;
7523737Shx147065 	lo = (int)-1;		/* really big number */
7533737Shx147065 	tbl_p = &cisparse.cftable;
7543737Shx147065 	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
7553737Shx147065 	for (tbl_p->index = 0; tbl_p->index <= config.hr; ) {
7563737Shx147065 		PCANDBG((CE_NOTE, "pcan: tuple idx=%x:\n", tbl_p->index));
7573737Shx147065 		if (ret = csx_GetNextTuple(chdl, &tuple)) {
7583737Shx147065 			cmn_err(CE_WARN, "pcan: get cftable failed %x\n", ret);
7593737Shx147065 			break;
7603737Shx147065 		}
7613737Shx147065 		bzero((caddr_t)&cisparse, sizeof (cisparse));
7623737Shx147065 		if (ret = csx_Parse_CISTPL_CFTABLE_ENTRY(chdl, &tuple, tbl_p)) {
7633737Shx147065 			cmn_err(CE_WARN, "pcan: parse cftable failed%x\n", ret);
7643737Shx147065 			break;
7653737Shx147065 		}
7663737Shx147065 		if (tbl_p->flags & CISTPL_CFTABLE_TPCE_FS_PWR &&
7674343Sgd78059 		    tbl_p->pd.flags & CISTPL_CFTABLE_TPCE_FS_PWR_VCC) {
7683737Shx147065 			if (tbl_p->pd.pd_vcc.avgI > hi) {
7693737Shx147065 				hi = tbl_p->pd.pd_vcc.avgI;
7703737Shx147065 				pcan_p->pcan_config_hi = tbl_p->index;
7713737Shx147065 			}
7723737Shx147065 			if (tbl_p->pd.pd_vcc.avgI < lo) {
7733737Shx147065 				lo = tbl_p->pd.pd_vcc.avgI;
7743737Shx147065 				pcan_p->pcan_config = tbl_p->index;
7753737Shx147065 			}
7763737Shx147065 		}
7773737Shx147065 		if (tbl_p->flags & CISTPL_CFTABLE_TPCE_DEFAULT) {
7783737Shx147065 			if (tbl_p->pd.flags & CISTPL_CFTABLE_TPCE_FS_PWR_VCC)
7793737Shx147065 				pcan_p->pcan_vcc = tbl_p->pd.pd_vcc.nomV;
7803737Shx147065 			if (tbl_p->flags & CISTPL_CFTABLE_TPCE_FS_IO)
7813737Shx147065 				pcan_p->pcan_iodecode = tbl_p->io.addr_lines;
7823737Shx147065 		}
7833737Shx147065 	}
7843737Shx147065 	PCANDBG((CE_NOTE, "pcan: cfg_hi=%x cfg=%x vcc=%x iodecode=%x\n",
7853737Shx147065 	    pcan_p->pcan_config_hi, pcan_p->pcan_config,
7863737Shx147065 	    pcan_p->pcan_vcc, pcan_p->pcan_iodecode));
7873737Shx147065 
7883737Shx147065 	bzero(&io, sizeof (io));
7893737Shx147065 	io.BasePort1.base = 0;
7903737Shx147065 	io.NumPorts1 = 1 << pcan_p->pcan_iodecode;
7913737Shx147065 	io.Attributes1 = IO_DATA_PATH_WIDTH_16;
7923737Shx147065 	io.IOAddrLines = pcan_p->pcan_iodecode;
7933737Shx147065 	if (ret = csx_RequestIO(chdl, &io)) {
7943737Shx147065 		cmn_err(CE_WARN, "pcan: RequestIO failed %x\n", ret);
7953737Shx147065 		goto insert_ret;
7963737Shx147065 	}
7973737Shx147065 	pcan_p->pcan_port = io.BasePort1.handle;
7983737Shx147065 
7993737Shx147065 	if (ret = ddi_add_softintr(DIP(pcan_p), DDI_SOFTINT_HIGH,
8003737Shx147065 	    &pcan_p->pcan_softint_id, &pcan_p->pcan_ib_cookie, NULL,
8013737Shx147065 	    pcan_intr, (caddr_t)pcan_p)) {
8023737Shx147065 		cmn_err(CE_NOTE, "pcan: Add softintr failed\n");
8033737Shx147065 		goto insert_ret;
8043737Shx147065 	}
8053737Shx147065 	irq.Attributes = IRQ_TYPE_EXCLUSIVE;
8063737Shx147065 	irq.irq_handler = ddi_intr_hilevel(DIP(pcan_p), 0) ?
8073737Shx147065 	    (csfunction_t *)pcan_intr_hi : (csfunction_t *)pcan_intr;
8083737Shx147065 	irq.irq_handler_arg = pcan_p;
8093737Shx147065 	if (ret = csx_RequestIRQ(chdl, &irq)) {
8103737Shx147065 		cmn_err(CE_WARN, "pcan: RequestIRQ failed %x\n", ret);
8113737Shx147065 		goto un_io;
8123737Shx147065 	}
8133737Shx147065 
8143737Shx147065 	bzero(&cfg, sizeof (cfg));
8153737Shx147065 	cfg.Attributes = 0; /* not ready for CONF_ENABLE_IRQ_STEERING yet */
8163737Shx147065 	cfg.Vcc = 50; /* pcan_vcc == 0 */
8173737Shx147065 	cfg.Vpp1 = 50;
8183737Shx147065 	cfg.Vpp2 = 50;
8193737Shx147065 	cfg.IntType = SOCKET_INTERFACE_MEMORY_AND_IO;
8203737Shx147065 	cfg.ConfigBase = config.base;
8213737Shx147065 	cfg.ConfigIndex = pcan_p->pcan_config;
8223737Shx147065 	cfg.Status = CCSR_IO_IS_8; /* no use */
8233737Shx147065 	cfg.Present = config.present;
8243737Shx147065 	pcan_p->pcan_flag |= PCAN_CARD_READY;
8253737Shx147065 	if (ret = csx_RequestConfiguration(chdl, &cfg)) {
8263737Shx147065 		cmn_err(CE_WARN, "pcan: RequestConfiguration failed %x\n", ret);
8273737Shx147065 		goto un_irq;
8283737Shx147065 	}
8293737Shx147065 	return (CS_SUCCESS);
8303737Shx147065 un_irq:
8313737Shx147065 	(void) csx_ReleaseIRQ(chdl, &irq);
8323737Shx147065 un_io:
8333737Shx147065 	ddi_remove_softintr(pcan_p->pcan_softint_id);
8343737Shx147065 
8353737Shx147065 	(void) csx_ReleaseIO(chdl, &io);
8363737Shx147065 	pcan_p->pcan_port = 0;
8373737Shx147065 insert_ret:
8383737Shx147065 	pcan_p->pcan_flag &= ~PCAN_CARD_READY;
8393737Shx147065 	return (ret);
8403737Shx147065 }
8413737Shx147065 
8423737Shx147065 /*
8433737Shx147065  * assume card is already removed, don't touch the hardware
8443737Shx147065  */
8453737Shx147065 static void
8463737Shx147065 pcan_card_remove(pcan_maci_t *pcan_p)
8473737Shx147065 {
8483737Shx147065 	int ret;
8493737Shx147065 	io_req_t io;
8503737Shx147065 	irq_req_t irq;
8513737Shx147065 
8523737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_READY))
8533737Shx147065 		return;
8543737Shx147065 	if (ret = csx_ReleaseConfiguration(pcan_p->pcan_chdl, NULL))
8553737Shx147065 		cmn_err(CE_WARN, "pcan: ReleaseConfiguration failed %x\n", ret);
8563737Shx147065 
8573737Shx147065 	bzero(&irq, sizeof (irq));
8583737Shx147065 	if (ret = csx_ReleaseIRQ(pcan_p->pcan_chdl, &irq))
8593737Shx147065 		cmn_err(CE_WARN, "pcan: ReleaseIRQ failed %x\n", ret);
8603737Shx147065 
8613737Shx147065 	ddi_remove_softintr(pcan_p->pcan_softint_id);
8623737Shx147065 
8633737Shx147065 	bzero(&io, sizeof (io));
8643737Shx147065 	io.BasePort1.handle = pcan_p->pcan_port;
8653737Shx147065 	io.NumPorts1 = 16;
8663737Shx147065 	if (ret = csx_ReleaseIO(pcan_p->pcan_chdl, &io))
8673737Shx147065 		cmn_err(CE_WARN, "pcan: Release IO failed %x\n", ret);
8683737Shx147065 
8693737Shx147065 	pcan_p->pcan_port = 0;
8703737Shx147065 	pcan_p->pcan_flag &= ~PCAN_CARD_READY;
8713737Shx147065 }
8723737Shx147065 
8733737Shx147065 /*
8743737Shx147065  * gld operation interface routines
8753737Shx147065  */
8763737Shx147065 static int
8773737Shx147065 pcan_start(void *arg)
8783737Shx147065 {
8793737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
8803737Shx147065 
8813737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
8823737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
8833737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
8843737Shx147065 		return (PCAN_FAIL);
8853737Shx147065 	}
8863737Shx147065 	(void) pcan_loaddef(pcan_p);
8873737Shx147065 	pcan_start_locked(pcan_p);
8883737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
8893737Shx147065 	return (PCAN_SUCCESS);
8903737Shx147065 }
8913737Shx147065 
8923737Shx147065 static void
8933737Shx147065 pcan_stop(void *arg)
8943737Shx147065 {
8953737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
8963737Shx147065 
8973737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
8983737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
8993737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
9003737Shx147065 		return;
9013737Shx147065 	}
9023737Shx147065 	pcan_stop_locked(pcan_p);
9033737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
9043737Shx147065 	if (pcan_p->pcan_connect_timeout_id != 0) {
9053737Shx147065 		(void) untimeout(pcan_p->pcan_connect_timeout_id);
9063737Shx147065 		pcan_p->pcan_connect_timeout_id = 0;
9073737Shx147065 	}
9083737Shx147065 }
9093737Shx147065 
9103737Shx147065 /*
9113737Shx147065  * mac address can only be set in 'disable' state and
9123737Shx147065  * be effective after 'enable' state.
9133737Shx147065  */
9143737Shx147065 static int
9153737Shx147065 pcan_saddr(void *arg, const uint8_t *macaddr)
9163737Shx147065 {
9173737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
9183737Shx147065 	int ret = PCAN_SUCCESS;
9193737Shx147065 	ether_copy(macaddr, pcan_p->pcan_mac_addr);
9203737Shx147065 
9213737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
9223737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
9233737Shx147065 		ret = PCAN_FAIL;
9243737Shx147065 		goto done;
9253737Shx147065 	}
9263737Shx147065 	ether_copy(pcan_p->pcan_mac_addr, pcan_p->an_config.an_macaddr);
9273737Shx147065 	if (pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0)) {
9283737Shx147065 		cmn_err(CE_WARN, "pcan set mac addr: failed\n");
9293737Shx147065 		ret = PCAN_FAIL;
9303737Shx147065 		goto done;
9313737Shx147065 	}
9323737Shx147065 	if (pcan_config_mac(pcan_p)) {
9333737Shx147065 		cmn_err(CE_WARN, "pcan set mac addr: config_mac failed\n");
9343737Shx147065 		ret = PCAN_FAIL;
9353737Shx147065 		goto done;
9363737Shx147065 	}
9373737Shx147065 	if (pcan_set_cmd(pcan_p, AN_CMD_ENABLE, 0)) {
9383737Shx147065 		cmn_err(CE_WARN, "pcan set mac addr: failed\n");
9393737Shx147065 		ret = PCAN_FAIL;
9403737Shx147065 	}
9413737Shx147065 done:
9423737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
9433737Shx147065 	return (ret);
9443737Shx147065 }
9453737Shx147065 
9463737Shx147065 /*
9473737Shx147065  * send a packet out for pccard
9483737Shx147065  */
9493737Shx147065 static int
9503737Shx147065 pcan_send(pcan_maci_t *pcan_p, mblk_t *mblk_p)
9513737Shx147065 {
9523737Shx147065 	char *buf, *buf_p;
9533737Shx147065 	an_txfrm_t *frm_p;
9543737Shx147065 #ifdef PCAN_SEND_DEBUG
9553737Shx147065 	struct an_ltv_status radio_status;
9563737Shx147065 #endif /* PCAN_SEND_DEBUG */
9573737Shx147065 	uint16_t pkt_len, xmt_id, ring_idx;
9583737Shx147065 	struct ieee80211_frame *wh;
9593737Shx147065 	int i = 0;
9603737Shx147065 
9613737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
9623737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
9633737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
9643737Shx147065 		freemsg(mblk_p);
9653737Shx147065 		return (PCAN_SUCCESS);		/* drop packet */
9663737Shx147065 	}
9673737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_LINKUP)) {	/* link down */
9683737Shx147065 		PCANDBG((CE_NOTE, "pcan: link down, dropped\n"));
9693737Shx147065 		pcan_p->glds_nocarrier++;
9703737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
9713737Shx147065 		freemsg(mblk_p);
9723737Shx147065 		return (PCAN_SUCCESS);		/* drop packet */
9733737Shx147065 	}
9743737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
9753737Shx147065 	if (pullupmsg(mblk_p, -1) == 0) {
9763737Shx147065 		cmn_err(CE_NOTE, "pcan send: pullupmsg failed\n");
9773737Shx147065 		freemsg(mblk_p);
9783737Shx147065 		return (PCAN_SUCCESS);		/* drop packet */
9793737Shx147065 	}
9803737Shx147065 	wh = (struct ieee80211_frame *)mblk_p->b_rptr;
9813737Shx147065 
9823737Shx147065 	mutex_enter(&pcan_p->pcan_txring.an_tx_lock);
9833737Shx147065 	ring_idx = pcan_p->pcan_txring.an_tx_prod;
9843737Shx147065 	pcan_p->pcan_txring.an_tx_prod = (ring_idx + 1) & AN_TX_RING_MASK;
9853737Shx147065 
9863737Shx147065 	/* check whether there is a xmt buffer available */
9873737Shx147065 	while ((i < AN_TX_RING_CNT) &&
9883737Shx147065 	    (pcan_p->pcan_txring.an_tx_ring[ring_idx])) {
9893737Shx147065 		ring_idx = pcan_p->pcan_txring.an_tx_prod;
9903737Shx147065 		pcan_p->pcan_txring.an_tx_prod =
9913737Shx147065 		    (ring_idx + 1) & AN_TX_RING_MASK;
9923737Shx147065 		i++;
9933737Shx147065 	}
9943737Shx147065 
9953737Shx147065 	if (i == AN_TX_RING_CNT) {
9963737Shx147065 		mutex_exit(&pcan_p->pcan_txring.an_tx_lock);
9973737Shx147065 		PCANDBG((CE_NOTE, "pcan: ring full, retrying\n"));
9983737Shx147065 		mutex_enter(&pcan_p->pcan_glock);
9993737Shx147065 		pcan_p->pcan_reschedule_need = B_TRUE;
10003737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
10013737Shx147065 		pcan_p->glds_noxmtbuf++;
10023737Shx147065 		return (PCAN_FAIL);
10033737Shx147065 	}
10043737Shx147065 	xmt_id = pcan_p->pcan_txring.an_tx_fids[ring_idx];
10053737Shx147065 	pcan_p->pcan_txring.an_tx_ring[ring_idx] = xmt_id;
10063737Shx147065 	mutex_exit(&pcan_p->pcan_txring.an_tx_lock);
10073737Shx147065 
10083737Shx147065 	buf = kmem_zalloc(PCAN_NICMEM_SZ, KM_SLEEP); /* too big for stack */
10093737Shx147065 	buf_p = (ulong_t)buf & 1 ? buf + 1 : buf;	/* 16-bit round up */
10103737Shx147065 	frm_p = (an_txfrm_t *)buf_p;
10113737Shx147065 
10123737Shx147065 #ifdef DEBUG
10133737Shx147065 	if (pcan_debug & PCAN_DBG_SEND) {
10143737Shx147065 		cmn_err(CE_NOTE, "pcan send: packet from plugin");
10157249Sff224033 		for (i = 0; i < MBLKL(mblk_p); i++)
10163737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
10173737Shx147065 			    *((unsigned char *)mblk_p->b_rptr + i));
10183737Shx147065 	}
10193737Shx147065 #endif
10203737Shx147065 	pkt_len = msgdsize(mblk_p);
10213737Shx147065 	if (pkt_len > PCAN_NICMEM_SZ - sizeof (an_txfrm_t)) {
10223737Shx147065 		cmn_err(CE_WARN, "pcan send: mblk is too long");
10233737Shx147065 		kmem_free(buf, PCAN_NICMEM_SZ);
10243737Shx147065 		freemsg(mblk_p);
10253737Shx147065 		return (PCAN_SUCCESS);		/* drop packet */
10263737Shx147065 	}
10273737Shx147065 	if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) !=
10283737Shx147065 	    IEEE80211_FC1_DIR_TODS) {
10293737Shx147065 		kmem_free(buf, PCAN_NICMEM_SZ);
10303737Shx147065 		freemsg(mblk_p);
10313737Shx147065 		return (PCAN_SUCCESS);		/* drop packet */
10323737Shx147065 	}
10333737Shx147065 
10343737Shx147065 	/* initialize xmt frame header, payload_len must be stored in LE */
10353737Shx147065 	bzero(frm_p, sizeof (an_txfrm_t) + 2);
10363737Shx147065 	frm_p->an_tx_ctl = AN_TXCTL_8023;
10373737Shx147065 
10383737Shx147065 	/*
10393737Shx147065 	 * mblk sent down from plugin includes station mode 802.11 frame and
10403737Shx147065 	 * llc, so we here need to remove them and add an ethernet header.
10413737Shx147065 	 */
10423737Shx147065 	pkt_len = pkt_len - (sizeof (*wh) + sizeof (struct ieee80211_llc))
10433737Shx147065 	    + 2;
10443737Shx147065 	bcopy(wh->i_addr3, buf_p + 0x38, ETHERADDRL); /* dst macaddr */
10453737Shx147065 	bcopy(wh->i_addr2, buf_p + 0x3e, ETHERADDRL); /* src macaddr */
10463737Shx147065 	*((uint16_t *)(buf_p + 0x36)) = pkt_len;
10473737Shx147065 	bcopy(mblk_p->b_rptr + sizeof (*wh) + sizeof (struct ieee80211_llc)
10483737Shx147065 	    - 2, buf_p + 0x44, pkt_len);
10493737Shx147065 
10503737Shx147065 	if (pkt_len & 1) {	/* round up to 16-bit boundary and pad 0 */
10513737Shx147065 		buf_p[pkt_len + 0x44] = 0;
10523737Shx147065 		pkt_len++;
10533737Shx147065 	}
10543737Shx147065 	ASSERT(pkt_len <= PCAN_NICMEM_SZ);
10553737Shx147065 #ifdef DEBUG
10563737Shx147065 	if (pcan_debug & PCAN_DBG_SEND) {
10573737Shx147065 		cmn_err(CE_NOTE, "pcan send: packet to hardware--pkt_len=%x",
10583737Shx147065 		    pkt_len);
10593737Shx147065 		for (i = 0; i < pkt_len + 4; i++)
10603737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
10613737Shx147065 			    *((unsigned char *)buf_p + 0x36 + i));
10623737Shx147065 	}
10633737Shx147065 #endif
10643737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
10653737Shx147065 	(void) WRCH1(pcan_p, xmt_id, 0, (uint16_t *)buf_p, 0x38); /* frm */
10663737Shx147065 	(void) WRPKT(pcan_p, xmt_id, 0x38, (uint16_t *)(buf_p + 0x38),
10674343Sgd78059 	    pkt_len + 12);
10683737Shx147065 	ring_idx = pcan_set_cmd(pcan_p, AN_CMD_TX, xmt_id);
10693737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
10703737Shx147065 
10713737Shx147065 	PCANDBG((CE_NOTE, "pcan: pkt_len=0x44+%x=%x xmt=%x ret=%x\n",
10723737Shx147065 	    pkt_len, 0x44 + pkt_len, xmt_id, ring_idx));
10733737Shx147065 	kmem_free(buf, PCAN_NICMEM_SZ);
10743737Shx147065 #ifdef PCAN_SEND_DEBUG
10753737Shx147065 	if (pkt_len = pcan_status_ltv(PCAN_READ_LTV, pcan_p, &radio_status)) {
10763737Shx147065 		PCANDBG((CE_NOTE, "pcan: bad radio status %x\n", pkt_len));
10773737Shx147065 	} else {
10783737Shx147065 		PCANDBG((CE_NOTE, "pcan: radio status:\n"));
10793737Shx147065 	}
10803737Shx147065 #endif /* PCAN_SEND_DEBUG */
10813737Shx147065 	if (ring_idx)
10823737Shx147065 		return (PCAN_FAIL);
10833737Shx147065 	else {
10843737Shx147065 		freemsg(mblk_p);
10853737Shx147065 		return (PCAN_SUCCESS);
10863737Shx147065 	}
10873737Shx147065 }
10883737Shx147065 
10893737Shx147065 /*
10903737Shx147065  * send a packet out for PCI/MiniPCI card
10913737Shx147065  */
10923737Shx147065 static int
10933737Shx147065 pcian_send(pcan_maci_t *pcan_p, mblk_t *mblk_p)
10943737Shx147065 {
10953737Shx147065 	char *buf;
10963737Shx147065 	uint16_t pkt_len = msgdsize(mblk_p), ring_idx;
10973737Shx147065 	uint32_t i;
10983737Shx147065 	struct ieee80211_frame *wh;
10993737Shx147065 	struct an_card_tx_desc an_tx_desc;
11003737Shx147065 
11013737Shx147065 	ring_idx = pcan_p->pcan_txring.an_tx_prod;
11023737Shx147065 
11033737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
11043737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_LINKUP)) {	/* link down */
11053737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
11063737Shx147065 		pcan_p->glds_nocarrier++;
11073737Shx147065 		freemsg(mblk_p);
11083737Shx147065 		return (PCAN_SUCCESS);		/* drop packet */
11093737Shx147065 	}
11103737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
11113737Shx147065 	if (pullupmsg(mblk_p, -1) == 0) {
11123737Shx147065 		cmn_err(CE_NOTE, "pcan(pci) send: pullupmsg failed\n");
11133737Shx147065 		freemsg(mblk_p);
11143737Shx147065 		return (PCAN_SUCCESS);		/* drop packet */
11153737Shx147065 	}
11163737Shx147065 	wh = (struct ieee80211_frame *)mblk_p->b_rptr;
11173737Shx147065 
11183737Shx147065 	mutex_enter(&pcan_p->pcan_txring.an_tx_lock);
11193737Shx147065 	if ((pcan_p->pcan_flag & PCAN_CARD_SEND) &&
11203737Shx147065 	    (ring_idx == pcan_p->pcan_txring.an_tx_cons)) {
11213737Shx147065 		pcan_p->glds_noxmtbuf++;
11223737Shx147065 		pcan_p->pcan_reschedule_need = B_TRUE;
11233737Shx147065 		mutex_exit(&pcan_p->pcan_txring.an_tx_lock);
11243737Shx147065 		return (PCAN_FAIL);
11253737Shx147065 	}
11263737Shx147065 	mutex_exit(&pcan_p->pcan_txring.an_tx_lock);
11273737Shx147065 
11283737Shx147065 #ifdef DEBUG
11293737Shx147065 	if (pcan_debug & PCAN_DBG_SEND) {
11303737Shx147065 		cmn_err(CE_NOTE, "pcan(pci) send: packet from plugin");
11317249Sff224033 		for (i = 0; i < MBLKL(mblk_p); i++)
11323737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
11333737Shx147065 			    *((unsigned char *)mblk_p->b_rptr + i));
11343737Shx147065 	}
11353737Shx147065 #endif
11363737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
11373737Shx147065 
11383737Shx147065 	buf = pcan_p->pcan_tx[ring_idx].dma_virtaddr;
11393737Shx147065 	bzero(buf, AN_TX_BUFFER_SIZE);
11403737Shx147065 
11413737Shx147065 	/*
11423737Shx147065 	 * mblk sent down from plugin includes station mode 802.11 frame and
11433737Shx147065 	 * llc, so we here need to remove them and add an ethernet header.
11443737Shx147065 	 */
11453737Shx147065 	*((uint16_t *)(buf + 8)) = htons(AN_TXCTL_8023);
11463737Shx147065 	pkt_len = pkt_len - (sizeof (*wh) + sizeof (struct ieee80211_llc))
11473737Shx147065 	    + 2;
11483737Shx147065 	bcopy(wh->i_addr3, buf + 0x38, ETHERADDRL); /* dst macaddr */
11493737Shx147065 	bcopy(wh->i_addr2, buf + 0x3e, ETHERADDRL); /* src macaddr */
11503737Shx147065 	*((uint16_t *)(buf + 0x36)) = pkt_len;
11513737Shx147065 	bcopy(mblk_p->b_rptr + sizeof (*wh) + sizeof (struct ieee80211_llc)
11523737Shx147065 	    - 2, buf + 0x44, pkt_len);
11533737Shx147065 
11543737Shx147065 #ifdef DEBUG
11553737Shx147065 	if (pcan_debug & PCAN_DBG_SEND) {
11563737Shx147065 		cmn_err(CE_NOTE, "pcan(pci) send: packet to hardware "
11573737Shx147065 		    "pkt_len=%x", pkt_len);
11583737Shx147065 		for (i = 0; i < pkt_len + 14; i++)
11593737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
11603737Shx147065 			    *((unsigned char *)buf + 0x36 + i));
11613737Shx147065 	}
11623737Shx147065 #endif
11633737Shx147065 	bzero(&an_tx_desc, sizeof (an_tx_desc));
11643737Shx147065 	an_tx_desc.an_offset = 0;
11653737Shx147065 	an_tx_desc.an_eoc = (ring_idx == (AN_MAX_TX_DESC-1) ? 1 : 0);
11663737Shx147065 	an_tx_desc.an_valid = 1;
11673737Shx147065 	an_tx_desc.an_len =  0x44 + pkt_len;
11683737Shx147065 	an_tx_desc.an_phys  = pcan_p->pcan_tx[ring_idx].dma_physaddr;
11693737Shx147065 	for (i = 0; i < sizeof (an_tx_desc) / 4; i++) {
11703737Shx147065 		PCAN_AUX_PUT32(pcan_p, AN_TX_DESC_OFFSET +
11713737Shx147065 		    (ring_idx * sizeof (an_tx_desc)) + (i * 4),
11723737Shx147065 		    ((uint32_t *)&an_tx_desc)[i]);
11733737Shx147065 	}
11743737Shx147065 
11753737Shx147065 	mutex_enter(&pcan_p->pcan_txring.an_tx_lock);
11763737Shx147065 	pcan_p->pcan_txring.an_tx_prod = (ring_idx + 1) % AN_MAX_TX_DESC;
11773737Shx147065 	pcan_p->pcan_flag |= PCAN_CARD_SEND;
11783737Shx147065 	PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_ALLOC);
11793737Shx147065 	mutex_exit(&pcan_p->pcan_txring.an_tx_lock);
11803737Shx147065 
11813737Shx147065 	freemsg(mblk_p);
11823737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
11833737Shx147065 	return (PCAN_SUCCESS);
11843737Shx147065 }
11853737Shx147065 
11863737Shx147065 static mblk_t *
11873737Shx147065 pcan_tx(void *arg, mblk_t *mp)
11883737Shx147065 {
11893737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
11903737Shx147065 	mblk_t *next;
11913737Shx147065 	int ret = 0;
11923737Shx147065 
11933737Shx147065 	ASSERT(mp != NULL);
11943737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
11953737Shx147065 	if ((pcan_p->pcan_flag & (PCAN_CARD_LINKUP | PCAN_CARD_READY)) !=
11963737Shx147065 	    (PCAN_CARD_LINKUP | PCAN_CARD_READY)) {
11973737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
11983737Shx147065 		return (mp);
11993737Shx147065 	}
12003737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
12013737Shx147065 	while (mp != NULL) {
12023737Shx147065 		next =  mp->b_next;
12033737Shx147065 		mp->b_next = NULL;
12043737Shx147065 
12053737Shx147065 		if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
12063737Shx147065 			ret = pcian_send(pcan_p, mp);
12073737Shx147065 		} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
12083737Shx147065 			ret = pcan_send(pcan_p, mp);
12093737Shx147065 		}
12103737Shx147065 		if (ret) {
12113737Shx147065 			mp->b_next = next;
12123737Shx147065 			break;
12133737Shx147065 		}
12143737Shx147065 		mp = next;
12153737Shx147065 	}
12163737Shx147065 	return (mp);
12173737Shx147065 }
12183737Shx147065 
12193737Shx147065 /*
12203737Shx147065  * this driver is porting from freebsd, the code in freebsd
12213737Shx147065  * doesn't show how to set promiscous mode.
12223737Shx147065  */
12233737Shx147065 /*ARGSUSED*/
12243737Shx147065 static int
12253737Shx147065 pcan_prom(void *arg, boolean_t on)
12263737Shx147065 {
12273737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
12283737Shx147065 	int ret = PCAN_SUCCESS;
12293737Shx147065 
12303737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
12313737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
12323737Shx147065 		ret = PCAN_FAIL;
12333737Shx147065 	}
12343737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
12353737Shx147065 	return (ret);
12363737Shx147065 }
12373737Shx147065 
12383737Shx147065 /*ARGSUSED*/
12393737Shx147065 static int
12403737Shx147065 pcan_gstat(void *arg, uint_t statitem, uint64_t *val)
12413737Shx147065 {
12423737Shx147065 	uint16_t i;
12433737Shx147065 	int ret = PCAN_SUCCESS;
12443737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
12453737Shx147065 	uint64_t *cntr_p = pcan_p->pcan_cntrs_s;
12463737Shx147065 
12473737Shx147065 	PCANDBG((CE_NOTE, "pcan: gstat called\n"));
12483737Shx147065 
12493737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
12503737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
12513737Shx147065 		ret = PCAN_FAIL;
12523737Shx147065 		goto done;
12533737Shx147065 	}
12543737Shx147065 	if (pcan_get_ltv(pcan_p, sizeof (pcan_p->an_stats),
12554343Sgd78059 	    AN_RID_16BITS_DELTACLR, (uint16_t *)&pcan_p->an_stats)) {
12563737Shx147065 		cmn_err(CE_WARN, "pcan kstat: get ltv(32 delta statistics)"
12573737Shx147065 		    " failed \n");
12583737Shx147065 		ret = PCAN_FAIL;
12593737Shx147065 		goto done;
12603737Shx147065 	}
12613737Shx147065 	for (i = 0; i < ANC_STAT_CNT; i++) {
12623737Shx147065 		cntr_p[i] += *((uint16_t *)&pcan_p->an_stats + 1 + i);
12633737Shx147065 	}
12643737Shx147065 	if (pcan_status_ltv(PCAN_READ_LTV, pcan_p, &pcan_p->an_status)) {
12653737Shx147065 		cmn_err(CE_WARN, "pcan kstat: read status failed \n");
12663737Shx147065 		ret = PCAN_FAIL;
12673737Shx147065 		goto done;
12683737Shx147065 	}
12693737Shx147065 
12703737Shx147065 	switch (statitem) {
12713737Shx147065 	case MAC_STAT_IFSPEED:
12723737Shx147065 		*val = 500000 * pcan_p->an_status.an_cur_tx_rate;
12733737Shx147065 		break;
12743737Shx147065 	case MAC_STAT_NOXMTBUF:
12753737Shx147065 		*val = pcan_p->glds_noxmtbuf;
12763737Shx147065 		break;
12773737Shx147065 	case MAC_STAT_NORCVBUF:
12783737Shx147065 		*val = pcan_p->glds_norcvbuf;
12793737Shx147065 		break;
12803737Shx147065 	case MAC_STAT_IERRORS:
12813737Shx147065 		*val = cntr_p[ANC_RX_OVERRUNS] +
12823737Shx147065 		    cntr_p[ANC_RX_PLCP_CSUM_ERRS] +
12833737Shx147065 		    cntr_p[ANC_RX_PLCP_FORMAT_ERRS] +
12843737Shx147065 		    cntr_p[ANC_RX_PLCP_LEN_ERRS] +
12853737Shx147065 		    cntr_p[ANC_RX_MAC_CRC_ERRS] +
12863737Shx147065 		    cntr_p[ANC_RX_WEP_ERRS];
12873737Shx147065 		break;
12883737Shx147065 	case MAC_STAT_OERRORS:
12893737Shx147065 		*val = cntr_p[ANC_TX_HOST_FAILED];
12903737Shx147065 		break;
12913737Shx147065 	case MAC_STAT_RBYTES:
12923737Shx147065 		*val = cntr_p[ANC_HOST_RX_BYTES];
12933737Shx147065 		break;
12943737Shx147065 	case MAC_STAT_IPACKETS:
12953737Shx147065 		*val = cntr_p[ANC_RX_HOST_UCASTS];
12963737Shx147065 		break;
12973737Shx147065 	case MAC_STAT_OBYTES:
12983737Shx147065 		*val = cntr_p[ANC_HOST_TX_BYTES];
12993737Shx147065 		break;
13003737Shx147065 	case MAC_STAT_OPACKETS:
13013737Shx147065 		*val = cntr_p[ANC_TX_HOST_UCASTS];
13023737Shx147065 		break;
13033737Shx147065 	case WIFI_STAT_TX_FAILED:
13043737Shx147065 		*val = cntr_p[ANC_TX_HOST_FAILED];
13053737Shx147065 		break;
13063737Shx147065 	case WIFI_STAT_TX_RETRANS:
13073737Shx147065 		*val = cntr_p[ANC_HOST_RETRIES];
13083737Shx147065 		break;
13093737Shx147065 	case WIFI_STAT_FCS_ERRORS:
13103737Shx147065 		*val = cntr_p[ANC_RX_MAC_CRC_ERRS];
13113737Shx147065 		break;
13123737Shx147065 	case WIFI_STAT_WEP_ERRORS:
13133737Shx147065 		*val = cntr_p[ANC_RX_WEP_ERRS];
13143737Shx147065 		break;
13153737Shx147065 	case WIFI_STAT_MCAST_TX:
13163737Shx147065 		*val = cntr_p[ANC_TX_HOST_MCASTS];
13173737Shx147065 		break;
13183737Shx147065 	case WIFI_STAT_MCAST_RX:
13193737Shx147065 		*val = cntr_p[ANC_RX_HOST_MCASTS];
13203737Shx147065 		break;
13213737Shx147065 	case WIFI_STAT_TX_FRAGS:
13223737Shx147065 	case WIFI_STAT_RX_FRAGS:
13233737Shx147065 		*val = 0;
13243737Shx147065 		break;
13253737Shx147065 	case WIFI_STAT_RTS_SUCCESS:
13263737Shx147065 		*val = cntr_p[ANC_TX_RTS_OK];
13273737Shx147065 		break;
13283737Shx147065 	case WIFI_STAT_RTS_FAILURE:
13293737Shx147065 		*val = cntr_p[ANC_NO_CTS];
13303737Shx147065 		break;
13313737Shx147065 	case WIFI_STAT_ACK_FAILURE:
13323737Shx147065 		*val = cntr_p[ANC_NO_ACK];
13333737Shx147065 		break;
13343737Shx147065 	case WIFI_STAT_RX_DUPS:
13353737Shx147065 		*val = cntr_p[ANC_RX_DUPS];
13363737Shx147065 		break;
13373737Shx147065 	default:
13383737Shx147065 		ret = ENOTSUP;
13393737Shx147065 	}
13403737Shx147065 
13413737Shx147065 
13423737Shx147065 done:
13433737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
13443737Shx147065 	return (ret);
13453737Shx147065 }
13463737Shx147065 
13473737Shx147065 /*
13483737Shx147065  * this driver is porting from freebsd, the code in freebsd
13493737Shx147065  * doesn't show how to set multi address.
13503737Shx147065  */
13513737Shx147065 /*ARGSUSED*/
13523737Shx147065 static int
13533737Shx147065 pcan_sdmulti(void *arg, boolean_t add, const uint8_t *eth_p)
13543737Shx147065 {
13553737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
13563737Shx147065 
13573737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
13583737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
13593737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
13603737Shx147065 		return (PCAN_FAIL);
13613737Shx147065 	}
13623737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
13633737Shx147065 	return (PCAN_SUCCESS);
13643737Shx147065 }
13653737Shx147065 
13663737Shx147065 static uint_t
13673737Shx147065 pcan_info_softint(caddr_t arg)
13683737Shx147065 {
13693737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
13703737Shx147065 	wifi_data_t wd = { 0 };
13713737Shx147065 	uint16_t link;
13723737Shx147065 	uint32_t link_up;
13733737Shx147065 
13743737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
13753737Shx147065 	if (pcan_p->pcan_info_softint_pending != 1) {
13763737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
13773737Shx147065 		return (DDI_INTR_UNCLAIMED);
13783737Shx147065 	}
13793737Shx147065 
13803737Shx147065 	PCAN_READ(pcan_p, AN_LINKSTAT(pcan_p), link);
13813737Shx147065 	link_up = pcan_p->pcan_flag & PCAN_CARD_LINKUP;
13823737Shx147065 	if ((link == AN_LINKSTAT_ASSOCIATED) && !link_up) {
13833737Shx147065 		pcan_p->pcan_flag |= PCAN_CARD_LINKUP;
13843737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
13853737Shx147065 		if (pcan_p->pcan_connect_timeout_id != 0) {
13863737Shx147065 			(void) untimeout(pcan_p->pcan_connect_timeout_id);
13873737Shx147065 			pcan_p->pcan_connect_timeout_id = 0;
13883737Shx147065 		}
13893737Shx147065 		mac_link_update(GLD3(pcan_p), LINK_STATE_UP);
13903737Shx147065 		mutex_enter(&pcan_p->pcan_glock);
13913737Shx147065 		(void) pcan_status_ltv(PCAN_READ_LTV, pcan_p,
13923737Shx147065 		    &pcan_p->an_status);
13933737Shx147065 		bcopy(pcan_p->an_status.an_cur_bssid, wd.wd_bssid, 6);
13943737Shx147065 		wd.wd_secalloc = WIFI_SEC_NONE;
13953737Shx147065 		wd.wd_opmode = IEEE80211_M_STA;
13963737Shx147065 		(void) mac_pdata_update(pcan_p->pcan_mh, &wd,
13973737Shx147065 		    sizeof (wd));
13983737Shx147065 #ifdef DEBUG
13993737Shx147065 		if (pcan_debug & PCAN_DBG_LINKINFO) {
14003737Shx147065 			cmn_err(CE_NOTE, "pcan: link Up, chan=%d, "
14013737Shx147065 			    "ssid=\"%s\""
14023737Shx147065 			    " (%02x:%02x:%02x:%02x:%02x:%02x)\n",
14033737Shx147065 			    pcan_p->an_status.an_channel_set,
14043737Shx147065 			    pcan_p->an_status.an_ssid,
14053737Shx147065 			    pcan_p->an_status.an_cur_bssid[0],
14063737Shx147065 			    pcan_p->an_status.an_cur_bssid[1],
14073737Shx147065 			    pcan_p->an_status.an_cur_bssid[2],
14083737Shx147065 			    pcan_p->an_status.an_cur_bssid[3],
14093737Shx147065 			    pcan_p->an_status.an_cur_bssid[4],
14103737Shx147065 			    pcan_p->an_status.an_cur_bssid[5]);
14113737Shx147065 		}
14123737Shx147065 #endif
14133737Shx147065 	}
14143737Shx147065 	if ((link != AN_LINKSTAT_ASSOCIATED) && link_up) {
14153737Shx147065 		pcan_p->pcan_flag &= ~PCAN_CARD_LINKUP;
14163737Shx147065 #ifdef DEBUG
14173737Shx147065 		if (pcan_debug & PCAN_DBG_LINKINFO) {
14183737Shx147065 			cmn_err(CE_NOTE, "pcan: link Down 0x%x\n", link);
14193737Shx147065 		}
14203737Shx147065 #endif
14213737Shx147065 		if (link != AN_LINKSTAT_SYNCLOST_HOSTREQ) {
14223737Shx147065 			pcan_p->pcan_connect_timeout_id =
14233737Shx147065 			    timeout(pcan_connect_timeout,
14243737Shx147065 			    pcan_p, drv_usectohz(1000));
14253737Shx147065 		}
14263737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
14273737Shx147065 		mac_link_update(GLD3(pcan_p), LINK_STATE_DOWN);
14283737Shx147065 		mutex_enter(&pcan_p->pcan_glock);
14293737Shx147065 	}
14303737Shx147065 
14313737Shx147065 	pcan_p->pcan_info_softint_pending = 0;
14323737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
14333737Shx147065 	return (DDI_INTR_CLAIMED);
14343737Shx147065 }
14353737Shx147065 
14363737Shx147065 static uint_t
14373737Shx147065 pcan_intr(caddr_t arg)
14383737Shx147065 {
14393737Shx147065 	uint16_t stat;
14403737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
14413737Shx147065 
14423737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
14433737Shx147065 	if ((pcan_p->pcan_flag & (PCAN_CARD_READY | PCAN_CARD_INTREN)) !=
14443737Shx147065 	    (PCAN_CARD_READY | PCAN_CARD_INTREN)) {
14453737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
14463737Shx147065 		return (DDI_INTR_UNCLAIMED);
14473737Shx147065 	}
14483737Shx147065 	PCAN_READ(pcan_p, AN_EVENT_STAT(pcan_p), stat);
14493737Shx147065 
14503737Shx147065 	if (!(stat & AN_INTRS(pcan_p)) || stat == AN_EV_ALL) {
14513737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
14523737Shx147065 		return (DDI_INTR_UNCLAIMED);
14533737Shx147065 	}
14543737Shx147065 
14553737Shx147065 	PCAN_DISABLE_INTR(pcan_p);
14563737Shx147065 	PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), ~AN_INTRS(pcan_p));
14573737Shx147065 
14583737Shx147065 	PCANDBG((CE_NOTE, "pcan intr: stat=%x pcan_flags=%x\n", stat,
14594343Sgd78059 	    pcan_p->pcan_flag));
14603737Shx147065 
14613737Shx147065 	if (stat & AN_EV_AWAKE) {
14623737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_AWAKE);
14633737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_AWAKE);
14643737Shx147065 	}
14653737Shx147065 	if (stat & AN_EV_LINKSTAT) {
14663737Shx147065 		pcan_p->pcan_info_softint_pending = 1;
14673737Shx147065 		ddi_trigger_softintr(pcan_p->pcan_info_softint_id);
14683737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_LINKSTAT);
14693737Shx147065 	}
14703737Shx147065 	if (stat & AN_EV_RX) {
14713737Shx147065 		if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
14723737Shx147065 			pcian_rcv(pcan_p);
14733737Shx147065 		} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
14743737Shx147065 			pcan_rcv(pcan_p);
14753737Shx147065 		}
14763737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_RX);
14773737Shx147065 	}
14783737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
14793737Shx147065 		if (stat & AN_EV_TX_CPY) {
14803737Shx147065 			(void) pcan_txdone(pcan_p, stat & AN_EV_TX_CPY);
14813737Shx147065 			if (pcan_p->pcan_reschedule_need == B_TRUE) {
14823737Shx147065 				mac_tx_update(GLD3(pcan_p));
14833737Shx147065 				pcan_p->pcan_reschedule_need = B_FALSE;
14843737Shx147065 			}
14853737Shx147065 			PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_TX_CPY);
14863737Shx147065 	}
14873737Shx147065 	}
14883737Shx147065 	if (stat & AN_EV_TX) {
14893737Shx147065 		if (pcan_txdone(pcan_p, stat & AN_EV_TX) == 0) {
14903737Shx147065 			if (pcan_p->pcan_reschedule_need == B_TRUE) {
14913737Shx147065 				mac_tx_update(GLD3(pcan_p));
14923737Shx147065 				pcan_p->pcan_reschedule_need = B_FALSE;
14933737Shx147065 			}
14943737Shx147065 		}
14953737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_TX);
14963737Shx147065 	}
14973737Shx147065 	if (stat & AN_EV_TX_EXC) {
14983737Shx147065 		if (pcan_txdone(pcan_p, stat & AN_EV_TX_EXC) == 0) {
14993737Shx147065 			if (pcan_p->pcan_reschedule_need == B_TRUE) {
15003737Shx147065 				mutex_exit(&pcan_p->pcan_glock);
15013737Shx147065 				mac_tx_update(GLD3(pcan_p));
15023737Shx147065 				mutex_enter(&pcan_p->pcan_glock);
15033737Shx147065 				pcan_p->pcan_reschedule_need = B_FALSE;
15043737Shx147065 			}
15053737Shx147065 		}
15063737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_TX_EXC);
15073737Shx147065 	}
15083737Shx147065 	if (stat & AN_EV_ALLOC) {
15093737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_ALLOC);
15103737Shx147065 		PCANDBG((CE_NOTE, "pcan intr: nicmem alloc done\n"));
15113737Shx147065 	}
15123737Shx147065 	if (stat & AN_EV_MIC) {
15133737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_MIC);
15143737Shx147065 	}
15153737Shx147065 	PCAN_ENABLE_INTR(pcan_p);
15163737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
15173737Shx147065 	return (DDI_INTR_CLAIMED);
15183737Shx147065 }
15193737Shx147065 
15203737Shx147065 static uint_t
15213737Shx147065 pcan_intr_hi(caddr_t arg)
15223737Shx147065 {
15233737Shx147065 	uint16_t stat;
15243737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
15253737Shx147065 
15263737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
15273737Shx147065 	if ((pcan_p->pcan_flag & (PCAN_CARD_READY | PCAN_CARD_INTREN)) !=
15283737Shx147065 	    (PCAN_CARD_READY | PCAN_CARD_INTREN)) {
15293737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
15303737Shx147065 		return (DDI_INTR_UNCLAIMED);
15313737Shx147065 	}
15323737Shx147065 	PCAN_READ(pcan_p, AN_EVENT_STAT(pcan_p), stat);
15333737Shx147065 	PCANDBG((CE_NOTE, "pcan intr(hi): stat=%x pcan_flags=%x\n", stat,
15343737Shx147065 	    pcan_p->pcan_flag));
15353737Shx147065 
15363737Shx147065 	if (!(stat & AN_INTRS(pcan_p)) || stat == AN_EV_ALL) {
15373737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
15383737Shx147065 		return (DDI_INTR_UNCLAIMED);
15393737Shx147065 	}
15403737Shx147065 	/* disable interrupt without ack */
15413737Shx147065 	PCAN_WRITE(pcan_p, AN_INT_EN(pcan_p), 0);
15423737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
15433737Shx147065 	ddi_trigger_softintr(pcan_p->pcan_softint_id);
15443737Shx147065 	return (DDI_INTR_CLAIMED);
15453737Shx147065 }
15463737Shx147065 
15473737Shx147065 /*
15483737Shx147065  * retrieve data from pccard
15493737Shx147065  */
15503737Shx147065 static void
15513737Shx147065 pcan_rcv(pcan_maci_t *pcan_p)
15523737Shx147065 {
15533737Shx147065 	uint16_t id, off, ret, data_len, pkt_stat, frm_ctl;
15543737Shx147065 	an_rxfrm_t frm;
15553737Shx147065 	struct ieee80211_llc *llc;
15563737Shx147065 
15573737Shx147065 	mblk_t *mp = allocb(PCAN_NICMEM_SZ, BPRI_MED);
15583737Shx147065 	if (!mp) {
15593737Shx147065 		cmn_err(CE_WARN, "pcan: failed to alloc rcv buf");
15603737Shx147065 		pcan_p->glds_norcvbuf++;
15613737Shx147065 		return;
15623737Shx147065 	}
15633737Shx147065 	ASSERT(mp->b_rptr == mp->b_wptr);
15643737Shx147065 
15653737Shx147065 	PCAN_READ(pcan_p, AN_RX_FID, id);
15663737Shx147065 	if (id == AN_INVALID_FID) {
15673737Shx147065 		PCANDBG((CE_NOTE, "pcan rcv: can't get rx_fid\n"));
15683737Shx147065 		pcan_p->glds_norcvbuf++;
15693737Shx147065 		ret = PCAN_FAIL;
15703737Shx147065 		goto done;
15713737Shx147065 	}
15723737Shx147065 	if (ret = RDCH0(pcan_p, id, 0, (uint16_t *)&frm, sizeof (frm))) {
15733737Shx147065 		PCANDBG((CE_NOTE, "pcan rcv: read frm err %x\n", ret));
15743737Shx147065 		goto done;
15753737Shx147065 	}
15763737Shx147065 	off = sizeof (frm);
15773737Shx147065 	if (frm.an_rx_status) {
15783737Shx147065 		PCANDBG((CE_NOTE, "pcan rcv: err stat %x\n", frm.an_rx_status));
15793737Shx147065 		ret = frm.an_rx_status;
15803737Shx147065 		goto done;
15813737Shx147065 	}
15823737Shx147065 	PCANDBG((CE_NOTE, "pcan rcv: payload_len=%x gap_len=%x\n",
15833737Shx147065 	    frm.an_rx_payload_len, frm.an_gaplen));
15843737Shx147065 	if (frm.an_rx_payload_len > PCAN_NICMEM_SZ ||
15853737Shx147065 	    frm.an_gaplen > AN_RXGAP_MAX) {
15863737Shx147065 		PCANDBG((CE_NOTE, "pcan rcv: bad len\n"));
15873737Shx147065 		ret = PCAN_FAIL;
15883737Shx147065 		goto done;
15893737Shx147065 	}
15903737Shx147065 	if (ret = RDCH0(pcan_p, id, off, &pkt_stat, sizeof (pkt_stat))) {
15913737Shx147065 		PCANDBG((CE_NOTE, "pcan rcv: pkt status err %x\n", ret));
15923737Shx147065 		ret = PCAN_FAIL;
15933737Shx147065 		goto done;
15943737Shx147065 	}
15953737Shx147065 	off += sizeof (pkt_stat);
15963737Shx147065 	if (ret = RDCH0(pcan_p, id, off, &data_len, sizeof (data_len))) {
15973737Shx147065 		PCANDBG((CE_NOTE, "pcan rcv: payload len err %x\n", ret));
15983737Shx147065 		ret = PCAN_FAIL;
15993737Shx147065 		goto done;
16003737Shx147065 	}
16013737Shx147065 	off += sizeof (data_len);
16023737Shx147065 	off += ETHERADDRL << 1;
16033737Shx147065 	PCANDBG((CE_NOTE, "pcan rcv: pkt_stat=%x payload_len=%x+c off=%x\n",
16044343Sgd78059 	    pkt_stat, data_len, off));
16053737Shx147065 
16063737Shx147065 #ifdef DEBUG
16073737Shx147065 	if (pcan_debug & PCAN_DBG_RCV) {
16083737Shx147065 		int i;
16093737Shx147065 		cmn_err(CE_NOTE, "pcan rcv: frm header\n");
16103737Shx147065 		for (i = 0; i < sizeof (frm); i++)
16113737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
16123737Shx147065 			    *((uint8_t *)&frm + i));
16133737Shx147065 	}
16143737Shx147065 #endif
16153737Shx147065 	/*
16163737Shx147065 	 * this driver deal with WEP by itself. so plugin always thinks no wep.
16173737Shx147065 	 */
16183737Shx147065 	frm.an_frame_ctl &= ~(IEEE80211_FC1_WEP << 8);
16193737Shx147065 	frm_ctl = frm.an_frame_ctl;
16203737Shx147065 	PCAN_SWAP16((uint16_t *)&frm.an_frame_ctl,
16213737Shx147065 	    sizeof (struct ieee80211_frame));
16223737Shx147065 	/*
16233737Shx147065 	 * discard those frames which are not from the AP we connect or
16243737Shx147065 	 * without 'ap->sta' direction
16253737Shx147065 	 */
16263737Shx147065 	if (((pcan_p->an_config.an_opmode == AN_OPMODE_INFR_STATION)) &&
16273737Shx147065 	    ((((frm_ctl >> 8) & IEEE80211_FC1_DIR_MASK) !=
16283737Shx147065 	    IEEE80211_FC1_DIR_FROMDS) ||
16293737Shx147065 	    bcmp(pcan_p->an_status.an_cur_bssid, frm.an_addr2, 6) != 0)) {
16303737Shx147065 		ret = PCAN_FAIL;
16313737Shx147065 		goto done;
16323737Shx147065 	}
16333737Shx147065 	bcopy(&frm.an_frame_ctl, mp->b_wptr,
16343737Shx147065 	    sizeof (struct ieee80211_frame));
16353737Shx147065 	mp->b_wptr += sizeof (struct ieee80211_frame);
16363737Shx147065 
16373737Shx147065 	/* the plugin need a llc here */
16383737Shx147065 	llc = (struct ieee80211_llc *)mp->b_wptr;
16393737Shx147065 	llc->illc_dsap = llc->illc_ssap = AN_SNAP_K1;
16403737Shx147065 	llc->illc_control = AN_SNAP_CONTROL;
16413737Shx147065 	bzero(llc->illc_oc, sizeof (llc->illc_oc));
16423737Shx147065 	mp->b_wptr += AN_SNAPHDR_LEN;
16433737Shx147065 
16443737Shx147065 	/* read in the rest of data */
16453737Shx147065 	data_len += data_len & 1;	/* adjust to word boundary */
16463737Shx147065 	if (data_len > MBLKSIZE(mp)) {
16473737Shx147065 		cmn_err(CE_NOTE, "pcan rcv: data over length%x\n", data_len);
16483737Shx147065 		ret = PCAN_FAIL;
16493737Shx147065 		goto done;
16503737Shx147065 	}
16513737Shx147065 
16523737Shx147065 	if (ret = RDPKT(pcan_p, id, off, (uint16_t *)mp->b_wptr, data_len)) {
16533737Shx147065 		PCANDBG((CE_NOTE, "pcan rcv: err read data %x\n", ret));
16543737Shx147065 	}
16553737Shx147065 done:
16563737Shx147065 	if (ret) {
16573737Shx147065 		PCANDBG((CE_NOTE, "pcan rcv: rd data %x\n", ret));
16583737Shx147065 		freemsg(mp);
16593737Shx147065 		return;
16603737Shx147065 	}
16613737Shx147065 	mp->b_wptr += data_len;
16623737Shx147065 #ifdef DEBUG
16633737Shx147065 	if (pcan_debug & PCAN_DBG_RCV) {
16643737Shx147065 		int i;
16653737Shx147065 		cmn_err(CE_NOTE, "pcan rcv: len=0x%x\n", data_len);
16663737Shx147065 		for (i = 0; i < data_len + sizeof (frm); i++)
16673737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
16683737Shx147065 			    *((uint8_t *)mp->b_rptr + i));
16693737Shx147065 	}
16703737Shx147065 #endif
16713737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
16723737Shx147065 	mac_rx(GLD3(pcan_p), NULL, mp);
16733737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
16743737Shx147065 }
16753737Shx147065 
16763737Shx147065 /*
16773737Shx147065  * retrieve data from mini-pci card
16783737Shx147065  */
16793737Shx147065 static void
16803737Shx147065 pcian_rcv(pcan_maci_t *pcan_p)
16813737Shx147065 {
16823737Shx147065 	struct an_card_rx_desc an_rx_desc;
16833737Shx147065 	char *buf;
16843737Shx147065 	uint16_t ret = 0, data_len;
16853737Shx147065 	int i, j;
16863737Shx147065 	struct ieee80211_frame *frm;
16873737Shx147065 	struct ieee80211_llc *llc;
16883737Shx147065 
16893737Shx147065 	mblk_t *mp = allocb(AN_RX_BUFFER_SIZE, BPRI_MED);
16903737Shx147065 	if (!mp) {
16913737Shx147065 		cmn_err(CE_WARN, "pcan(pci): failed to alloc rcv buf");
16923737Shx147065 		pcan_p->glds_norcvbuf++;
16933737Shx147065 		return;
16943737Shx147065 	}
16953737Shx147065 	ASSERT(mp->b_rptr == mp->b_wptr);
16963737Shx147065 
16973737Shx147065 	for (i = 0; i < sizeof (an_rx_desc) / 4; i++)
16983737Shx147065 		PCAN_AUX_GET32(pcan_p, AN_RX_DESC_OFFSET + (i * 4),
16993737Shx147065 		    ((uint32_t *)&an_rx_desc)[i]);
17003737Shx147065 	if (an_rx_desc.an_done && !an_rx_desc.an_valid) {
17013737Shx147065 		buf = pcan_p->pcan_rx[0].dma_virtaddr;
17023737Shx147065 		data_len = an_rx_desc.an_len;
17033737Shx147065 #ifdef DEBUG
17043737Shx147065 		if (pcan_debug & PCAN_DBG_RCV) {
17053737Shx147065 			cmn_err(CE_NOTE, "pcan(pci) rcv: data_len=%x",
17063737Shx147065 			    data_len);
17073737Shx147065 			for (j = 0; j < data_len + 14; j++)
17083737Shx147065 				cmn_err(CE_NOTE, "pcan_rcv %d: %x", j,
17093737Shx147065 				    *((uint8_t *)buf + j));
17103737Shx147065 		}
17113737Shx147065 #endif
17123737Shx147065 		if (data_len > MBLKSIZE(mp)) {
17133737Shx147065 			cmn_err(CE_NOTE, "pcan(pci) rcv: data over length%x\n",
17143737Shx147065 			    data_len);
17153737Shx147065 			ret = PCAN_FAIL;
17163737Shx147065 			goto done;
17173737Shx147065 		}
17183737Shx147065 		/*
17193737Shx147065 		 * minipci card receive an ethernet frame, so assembly a 802.11
17203737Shx147065 		 * frame here manually.
17213737Shx147065 		 */
17223737Shx147065 		frm = (struct ieee80211_frame *)mp->b_wptr;
17233737Shx147065 		bzero(frm, sizeof (*frm));
17243737Shx147065 		frm->i_fc[0] |= IEEE80211_FC0_TYPE_DATA;
17253737Shx147065 		frm->i_fc[1] |= IEEE80211_FC1_DIR_FROMDS;
17263737Shx147065 		bcopy(pcan_p->an_status.an_cur_bssid, frm->i_addr2, 6);
17273737Shx147065 		bcopy(buf, frm->i_addr1, 6);
17283737Shx147065 		bcopy(buf + 6, frm->i_addr3, 6);
17293737Shx147065 		mp->b_wptr += sizeof (struct ieee80211_frame);
17303737Shx147065 
17313737Shx147065 		llc = (struct ieee80211_llc *)mp->b_wptr;
17323737Shx147065 		llc->illc_dsap = llc->illc_ssap = AN_SNAP_K1;
17333737Shx147065 		llc->illc_control = AN_SNAP_CONTROL;
17343737Shx147065 		bzero(llc->illc_oc, sizeof (llc->illc_oc));
17353737Shx147065 		mp->b_wptr += AN_SNAPHDR_LEN;
17363737Shx147065 
17373737Shx147065 		bcopy(buf + 12, mp->b_wptr, data_len);
17383737Shx147065 		mp->b_wptr += data_len;
17393737Shx147065 #ifdef DEBUG
17403737Shx147065 		if (pcan_debug & PCAN_DBG_RCV) {
17413737Shx147065 			int i;
17423737Shx147065 			cmn_err(CE_NOTE, "pcan(pci) rcv: len=0x%x\n", data_len);
17433737Shx147065 			for (i = 0; i < data_len + sizeof (*frm)
17443737Shx147065 			    + sizeof (*llc); i++)
17453737Shx147065 				cmn_err(CE_NOTE, "%x: %x\n", i,
17463737Shx147065 				    *((uint8_t *)mp->b_rptr + i));
17473737Shx147065 		}
17483737Shx147065 #endif
17493737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
17503737Shx147065 		mac_rx(GLD3(pcan_p), NULL, mp);
17513737Shx147065 		mutex_enter(&pcan_p->pcan_glock);
17523737Shx147065 	}
17533737Shx147065 done:
17543737Shx147065 	bzero(&an_rx_desc, sizeof (an_rx_desc));
17553737Shx147065 	an_rx_desc.an_valid = 1;
17563737Shx147065 	an_rx_desc.an_len = AN_RX_BUFFER_SIZE;
17573737Shx147065 	an_rx_desc.an_done = 0;
17583737Shx147065 	an_rx_desc.an_phys = pcan_p->pcan_rx[0].dma_physaddr;
17593737Shx147065 
17603737Shx147065 	for (i = 0; i < sizeof (an_rx_desc) / 4; i++)
17613737Shx147065 		PCAN_AUX_PUT32(pcan_p, AN_RX_DESC_OFFSET + (i * 4),
17623737Shx147065 		    ((uint32_t *)&an_rx_desc)[i]);
17633737Shx147065 	if (ret) {
17643737Shx147065 		freemsg(mp);
17653737Shx147065 	}
17663737Shx147065 }
17673737Shx147065 
17683737Shx147065 /*ARGSUSED*/
17693737Shx147065 static uint32_t
17703737Shx147065 pcan_txdone(pcan_maci_t *pcan_p, uint16_t err)
17713737Shx147065 {
17723737Shx147065 	uint16_t fid, i, ring_idx;
17733737Shx147065 	uint32_t ret = 0;
17743737Shx147065 
17753737Shx147065 	PCAN_READ(pcan_p, AN_TX_CMP_FID(pcan_p), fid);
17763737Shx147065 	mutex_enter(&pcan_p->pcan_txring.an_tx_lock);
17773737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
17783737Shx147065 		if (pcan_p->pcan_flag & PCAN_CARD_SEND) {
17793737Shx147065 			ring_idx = pcan_p->pcan_txring.an_tx_cons;
17803737Shx147065 			pcan_p->pcan_txring.an_tx_cons =
17813737Shx147065 			    (ring_idx + 1) % AN_MAX_TX_DESC;
17823737Shx147065 			if (pcan_p->pcan_txring.an_tx_prod ==
17833737Shx147065 			    pcan_p->pcan_txring.an_tx_cons) {
17843737Shx147065 				pcan_p->pcan_flag &= ~PCAN_CARD_SEND;
17853737Shx147065 			}
17863737Shx147065 		}
17873737Shx147065 		ret = 0;
17883737Shx147065 	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
17893737Shx147065 		for (i = 0; i < AN_TX_RING_CNT; i++) {
17903737Shx147065 			if (fid == pcan_p->pcan_txring.an_tx_ring[i]) {
17913737Shx147065 				pcan_p->pcan_txring.an_tx_ring[i] = 0;
17923737Shx147065 				break;
17933737Shx147065 			}
17943737Shx147065 		}
17953737Shx147065 		pcan_p->pcan_txring.an_tx_cons =
17963737Shx147065 		    (pcan_p->pcan_txring.an_tx_cons + 1) & AN_TX_RING_MASK;
17973737Shx147065 		ret = (i == AN_TX_RING_CNT ? 1 : 0);
17983737Shx147065 	}
17993737Shx147065 	mutex_exit(&pcan_p->pcan_txring.an_tx_lock);
18003737Shx147065 	return (ret);
18013737Shx147065 }
18023737Shx147065 
18033737Shx147065 /*
18043737Shx147065  * delay in which the mutex is not hold.
18053737Shx147065  * assuming the mutex has already been hold.
18063737Shx147065  */
18073737Shx147065 static void
18083737Shx147065 pcan_delay(pcan_maci_t *pcan_p, clock_t microsecs)
18093737Shx147065 {
18103737Shx147065 	ASSERT(mutex_owned(&pcan_p->pcan_glock));
18113737Shx147065 
18123737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
18133737Shx147065 	delay(drv_usectohz(microsecs));
18143737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
18153737Shx147065 }
18163737Shx147065 
18173737Shx147065 static void
18183737Shx147065 pcan_reset_backend(pcan_maci_t *pcan_p, int timeout)
18193737Shx147065 {
18203737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
18213737Shx147065 		(void) pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0);
18223737Shx147065 		PCAN_DISABLE_INTR_CLEAR(pcan_p);
18233737Shx147065 		(void) pcan_set_cmd(pcan_p, AN_CMD_FW_RESTART, 0);
18243737Shx147065 		(void) pcan_set_cmd(pcan_p, AN_CMD_NOOP2, 0);
18253737Shx147065 		(void) pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0);
18263737Shx147065 	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
18273737Shx147065 		(void) pcan_set_cmd0(pcan_p, AN_CMD_DISABLE, 0, 0, 0);
18283737Shx147065 		(void) pcan_set_cmd0(pcan_p, AN_CMD_NOOP2, 0, 0, 0);
18293737Shx147065 		PCAN_WRITE(pcan_p, AN_COMMAND(pcan_p), AN_CMD_FW_RESTART);
18303737Shx147065 		pcan_delay(pcan_p, timeout); /* wait for firmware restart */
18313737Shx147065 
18323737Shx147065 		(void) pcan_set_cmd(pcan_p, AN_CMD_NOOP, 0);
18333737Shx147065 		(void) pcan_set_cmd0(pcan_p, AN_CMD_DISABLE, 0, 0, 0);
18343737Shx147065 
18353737Shx147065 		PCAN_DISABLE_INTR_CLEAR(pcan_p);
18363737Shx147065 	}
18373737Shx147065 }
18383737Shx147065 
18393737Shx147065 /*
18403737Shx147065  * set command without the need of ACK.
18413737Shx147065  */
18423737Shx147065 static uint16_t
18433737Shx147065 pcan_set_cmd0(pcan_maci_t *pcan_p, uint16_t cmd, uint16_t p0,
18443737Shx147065     uint16_t p1, uint16_t p2)
18453737Shx147065 {
18463737Shx147065 	int i;
18473737Shx147065 	uint16_t stat, r0, r1, r2;
18483737Shx147065 
18493737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
18503737Shx147065 		for (i = 0; i < AN_TIMEOUT; i++) {
18513737Shx147065 			PCAN_READ(pcan_p, AN_COMMAND(pcan_p), stat);
18523737Shx147065 			if (!(stat & AN_CMD_BUSY))
18533737Shx147065 				break;
18543737Shx147065 		}
18553737Shx147065 		if (i == AN_TIMEOUT) {
18563737Shx147065 			PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p),
18573737Shx147065 			    AN_EV_CLR_STUCK_BUSY);
18583737Shx147065 			PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
18593737Shx147065 			drv_usecwait(10);
18603737Shx147065 		}
18613737Shx147065 		PCAN_WRITE(pcan_p, AN_PARAM0(pcan_p), p0);
18623737Shx147065 		PCAN_WRITE(pcan_p, AN_PARAM1(pcan_p), p1);
18633737Shx147065 		PCAN_WRITE(pcan_p, AN_PARAM2(pcan_p), p2);
18643737Shx147065 	}
18653737Shx147065 	PCAN_WRITE(pcan_p, AN_COMMAND(pcan_p), cmd);
18663737Shx147065 	for (i = 0; i < AN_TIMEOUT; i++) {
18673737Shx147065 		PCAN_READ(pcan_p, AN_EVENT_STAT(pcan_p), stat);
18683737Shx147065 		if (stat & AN_EV_CMD)
18693737Shx147065 			break;
18703737Shx147065 	}
18713737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
18723737Shx147065 		PCAN_READ(pcan_p, AN_RESP0(pcan_p), r0);
18733737Shx147065 		PCAN_READ(pcan_p, AN_RESP1(pcan_p), r1);
18743737Shx147065 		PCAN_READ(pcan_p, AN_RESP2(pcan_p), r2);
18753737Shx147065 		PCAN_READ(pcan_p, AN_COMMAND(pcan_p), stat);
18763737Shx147065 		if (stat & AN_CMD_BUSY)
18773737Shx147065 			PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p),
18783737Shx147065 			    AN_EV_CLR_STUCK_BUSY);
18793737Shx147065 		PCANDBG((CE_NOTE, "pcan set_cmd0: "
18803737Shx147065 		    "stat=%x, r0=%x, r1=%x, r2=%x\n",
18813737Shx147065 		    stat, r0, r1, r2));
18823737Shx147065 	}
18833737Shx147065 	PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
18843737Shx147065 	return (i == AN_TIMEOUT ? PCAN_TIMEDOUT_ACCESS : PCAN_SUCCESS);
18853737Shx147065 }
18863737Shx147065 
18873737Shx147065 static uint16_t
18883737Shx147065 pcan_set_cmd(pcan_maci_t *pcan_p, uint16_t cmd, uint16_t param)
18893737Shx147065 {
18903737Shx147065 	int i;
18913737Shx147065 	uint16_t stat, r0, r1, r2;
18923737Shx147065 	uint16_t ret;
18933737Shx147065 
18943737Shx147065 	if (((cmd == AN_CMD_ENABLE) &&
18953737Shx147065 	    ((pcan_p->pcan_flag & PCAN_ENABLED) != 0)) ||
18963737Shx147065 	    ((cmd == AN_CMD_DISABLE) &&
18973737Shx147065 	    ((pcan_p->pcan_flag & PCAN_ENABLED) == 0)))
18983737Shx147065 		return (PCAN_SUCCESS);
18993737Shx147065 	for (i = 0; i < AN_TIMEOUT; i++) {
19003737Shx147065 		PCAN_READ(pcan_p, AN_COMMAND(pcan_p), stat);
19013737Shx147065 		if (!(stat & AN_CMD_BUSY)) {
19023737Shx147065 			break;
19033737Shx147065 		}
19043737Shx147065 	}
19053737Shx147065 	if (i == AN_TIMEOUT) {
19063737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CLR_STUCK_BUSY);
19073737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
19083737Shx147065 		drv_usecwait(10);
19093737Shx147065 	}
19103737Shx147065 
19113737Shx147065 	PCAN_WRITE(pcan_p, AN_PARAM0(pcan_p), param);
19123737Shx147065 	PCAN_WRITE(pcan_p, AN_PARAM1(pcan_p), 0);
19133737Shx147065 	PCAN_WRITE(pcan_p, AN_PARAM2(pcan_p), 0);
19143737Shx147065 	PCAN_WRITE(pcan_p, AN_COMMAND(pcan_p), cmd);
19153737Shx147065 
19163737Shx147065 	for (i = 0; i < AN_TIMEOUT; i++) {
19173737Shx147065 		PCAN_READ(pcan_p, AN_EVENT_STAT(pcan_p), stat);
19183737Shx147065 		if (stat & AN_EV_CMD) {
19193737Shx147065 			break;
19203737Shx147065 		}
19213737Shx147065 		PCAN_READ(pcan_p, AN_COMMAND(pcan_p), stat);
19223737Shx147065 		if (stat == cmd)
19233737Shx147065 			PCAN_WRITE(pcan_p, AN_COMMAND(pcan_p), cmd);
19243737Shx147065 	}
19253737Shx147065 	if (i == AN_TIMEOUT) {
19263737Shx147065 		if (cmd == AN_CMD_FW_RESTART) {
19273737Shx147065 			PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
19283737Shx147065 			return (PCAN_SUCCESS);
19293737Shx147065 		}
19303737Shx147065 #ifdef DEBUG
19313737Shx147065 		if (pcan_debug & PCAN_DBG_CMD) {
19323737Shx147065 			cmn_err(CE_WARN, "pcan set_cmd: %x timeout stat=%x\n",
19333737Shx147065 			    cmd, stat);
19343737Shx147065 		}
19353737Shx147065 #endif
19363737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
19373737Shx147065 		return (PCAN_TIMEDOUT_CMD);
19383737Shx147065 	}
19393737Shx147065 
19403737Shx147065 	for (i = 0; i < AN_TIMEOUT; i++) {
19413737Shx147065 		PCAN_READ(pcan_p, AN_STATUS(pcan_p), stat);
19423737Shx147065 		PCAN_READ(pcan_p, AN_RESP0(pcan_p), r0);
19433737Shx147065 		PCAN_READ(pcan_p, AN_RESP1(pcan_p), r1);
19443737Shx147065 		PCAN_READ(pcan_p, AN_RESP2(pcan_p), r2);
19453737Shx147065 		if ((stat & AN_STAT_CMD_CODE) == (cmd & AN_STAT_CMD_CODE))
19463737Shx147065 			break;
19473737Shx147065 	}
19483737Shx147065 	if (cmd == AN_CMD_FW_RESTART) {
19493737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
19503737Shx147065 		return (PCAN_SUCCESS);
19513737Shx147065 	}
19523737Shx147065 	if (i == AN_TIMEOUT) {
19533737Shx147065 #ifdef DEBUG
19543737Shx147065 		if (pcan_debug & PCAN_DBG_CMD) {
19553737Shx147065 			cmn_err(CE_WARN, "pcan set_cmd<%x,%x>: timeout "
19563737Shx147065 			    "%x,%x,%x,%x\n", cmd, param, stat, r0, r1, r2);
19573737Shx147065 		}
19583737Shx147065 #endif
19593737Shx147065 		ret = PCAN_TIMEDOUT_ACCESS;
19603737Shx147065 	} else {
19613737Shx147065 		if (stat & AN_STAT_CMD_RESULT) {
19623737Shx147065 #ifdef DEBUG
19633737Shx147065 			if (pcan_debug & PCAN_DBG_CMD) {
19643737Shx147065 				cmn_err(CE_WARN, "pcan set_cmd<%x,%x>: failed "
19653737Shx147065 				    "%x,%x,%x,%x\n",
19663737Shx147065 				    cmd, param, stat, r0, r1, r2);
19673737Shx147065 			}
19683737Shx147065 #endif
19693737Shx147065 			ret = PCAN_TIMEDOUT_ACCESS;
19703737Shx147065 		} else {
19713737Shx147065 			ret = PCAN_SUCCESS;
19723737Shx147065 		}
19733737Shx147065 	}
19743737Shx147065 	PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
19753737Shx147065 	PCAN_READ(pcan_p, AN_COMMAND(pcan_p), stat);
19763737Shx147065 	if (stat & AN_CMD_BUSY)
19773737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CLR_STUCK_BUSY);
19783737Shx147065 	if (ret == PCAN_SUCCESS) {
19793737Shx147065 		if (cmd == AN_CMD_ENABLE)
19803737Shx147065 			pcan_p->pcan_flag |= PCAN_ENABLED;
19813737Shx147065 		if (cmd == AN_CMD_DISABLE)
19823737Shx147065 			pcan_p->pcan_flag &= (~PCAN_ENABLED);
19833737Shx147065 	}
19843737Shx147065 	return (ret);
19853737Shx147065 }
19863737Shx147065 
19873737Shx147065 static uint16_t
19883737Shx147065 pcan_set_ch(pcan_maci_t *pcan_p, uint16_t type, uint16_t off, uint16_t channel)
19893737Shx147065 {
19903737Shx147065 	int i;
19913737Shx147065 	uint16_t stat, select, offset;
19923737Shx147065 
19933737Shx147065 	if (channel) {
19943737Shx147065 		select = AN_SEL1;
19953737Shx147065 		offset = AN_OFF1;
19963737Shx147065 	} else {
19973737Shx147065 		select = AN_SEL0;
19983737Shx147065 		offset = AN_OFF0;
19993737Shx147065 	}
20003737Shx147065 	PCAN_WRITE(pcan_p, select, type);
20013737Shx147065 	PCAN_WRITE(pcan_p, offset, off);
20023737Shx147065 	for (i = 0; i < AN_TIMEOUT; i++) {
20033737Shx147065 		PCAN_READ(pcan_p, offset, stat);
20043737Shx147065 		if (!(stat & (AN_OFF_BUSY|AN_OFF_ERR)))
20053737Shx147065 			break;
20063737Shx147065 	}
20073737Shx147065 	if (stat & (AN_OFF_BUSY|AN_OFF_ERR)) { /* time out */
20083737Shx147065 		PCANDBG((CE_WARN, "pcan: set_ch%d %x %x TO %x\n",
20093737Shx147065 		    channel, type, off, stat));
20103737Shx147065 		return (PCAN_TIMEDOUT_TARGET);
20113737Shx147065 	}
20123737Shx147065 	return (PCAN_SUCCESS);
20133737Shx147065 }
20143737Shx147065 
20153737Shx147065 static uint16_t
20163737Shx147065 pcan_get_ltv(pcan_maci_t *pcan_p, uint16_t len, uint16_t type, uint16_t *val_p)
20173737Shx147065 {
20183737Shx147065 	uint16_t stat;
20193737Shx147065 
20203737Shx147065 	PCANDBG((CE_NOTE, "pcan: get_ltv(%p,%x,%x,%p)\n",
20214343Sgd78059 	    (void *)pcan_p, len, type, (void *)val_p));
20223737Shx147065 	ASSERT(!(len & 1));
20233737Shx147065 
20243737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
20253737Shx147065 		uint32_t i;
20263737Shx147065 		struct an_card_rid_desc an_rid_desc;
20273737Shx147065 		struct an_ltv_gen *an_ltv;
20283737Shx147065 		if (!pcan_p->pcan_cmd.dma_virtaddr)
20293737Shx147065 			return (EIO);
20303737Shx147065 		an_rid_desc.an_valid = 1;
20313737Shx147065 		an_rid_desc.an_len = AN_RID_BUFFER_SIZE;
20323737Shx147065 		an_rid_desc.an_rid = 0;
20333737Shx147065 		an_rid_desc.an_phys = pcan_p->pcan_cmd.dma_physaddr;
20343737Shx147065 		bzero(pcan_p->pcan_cmd.dma_virtaddr, AN_RID_BUFFER_SIZE);
20353737Shx147065 
20363737Shx147065 		for (i = 0; i < sizeof (an_rid_desc) / 4; i++)
20373737Shx147065 			PCAN_AUX_PUT32(pcan_p, AN_HOST_DESC_OFFSET + i * 4,
20383737Shx147065 			    ((uint32_t *)&an_rid_desc)[i]);
20393737Shx147065 
20403737Shx147065 		if (pcan_set_cmd0(pcan_p, AN_CMD_ACCESS |
20413737Shx147065 		    AN_ACCESS_READ, type, 0, 0)) {
20423737Shx147065 			cmn_err(CE_WARN, "pcan get_ltv: set cmd error");
20433737Shx147065 			return (EIO);
20443737Shx147065 		}
20453737Shx147065 
20463737Shx147065 		an_ltv = (struct an_ltv_gen *)pcan_p->pcan_cmd.dma_virtaddr;
20473737Shx147065 #ifdef DEBUG
20483737Shx147065 		if (pcan_debug & PCAN_DBG_INFO) {
20493737Shx147065 			cmn_err(CE_NOTE, "pcan get_ltv: type=%x,"
20503737Shx147065 			    "expected len=%d," "actual len=%d",
20513737Shx147065 			    type, len, an_ltv->an_len);
20523737Shx147065 			for (i = 0; i < an_ltv->an_len; i++)
20533737Shx147065 				cmn_err(CE_NOTE, "%d: %x", i,
20543737Shx147065 				    *(((uint8_t *)an_ltv) + i));
20553737Shx147065 		}
20563737Shx147065 #endif
20573737Shx147065 		if (an_ltv->an_len != len) {
20583737Shx147065 			PCANDBG((CE_WARN, "pcan get_ltv: rid=%x expected len=%d"
20593737Shx147065 			    "actual: len=%d", type,
20603737Shx147065 			    len, an_ltv->an_len));
20613737Shx147065 			/* return (EIO); */
20623737Shx147065 		}
20633737Shx147065 		bcopy(an_ltv, val_p, len);
20643737Shx147065 	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
20653737Shx147065 		len >>= 1;	/* convert bytes to 16-bit words */
20663737Shx147065 
20673737Shx147065 		/* 1. select read mode */
20683737Shx147065 		if (stat = pcan_set_cmd(pcan_p, AN_CMD_ACCESS |
20693737Shx147065 		    AN_ACCESS_READ, type))
20703737Shx147065 			return (stat);
20713737Shx147065 
20723737Shx147065 		/* 2. select Buffer Access Path (channel) 1 for PIO */
20733737Shx147065 		if (stat = pcan_set_ch(pcan_p, type, 0, 1))
20743737Shx147065 			return (stat);
20753737Shx147065 
20763737Shx147065 		/* 3. read length */
20773737Shx147065 		PCAN_READ(pcan_p, AN_DATA1, stat);
20783737Shx147065 		*val_p++ = stat;
20793737Shx147065 		if (stat != (len << 1)) {
20803737Shx147065 			PCANDBG((CE_NOTE, "pcan get_ltv[%x]:expect %x,"
20813737Shx147065 			    "got %x\n", type, (len + 1) << 1, stat));
20823737Shx147065 			stat = (stat >> 1) - 1;
20833737Shx147065 			len = MIN(stat, len);
20843737Shx147065 		}
20853737Shx147065 		/* 4. read value */
20863737Shx147065 		for (stat = 0; stat < len - 1; stat++, val_p++) {
20873737Shx147065 			PCAN_READ_P(pcan_p, AN_DATA1, val_p, 1);
20883737Shx147065 		}
20893737Shx147065 	}
20903737Shx147065 	return (PCAN_SUCCESS);
20913737Shx147065 }
20923737Shx147065 
20933737Shx147065 static uint16_t
20943737Shx147065 pcan_put_ltv(pcan_maci_t *pcan_p, uint16_t len, uint16_t type, uint16_t *val_p)
20953737Shx147065 {
20963737Shx147065 	uint16_t stat;
20973737Shx147065 	int i;
20983737Shx147065 
20993737Shx147065 	ASSERT(!(len & 1));
21003737Shx147065 
21013737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
21023737Shx147065 		struct an_card_rid_desc an_rid_desc;
21033737Shx147065 
21043737Shx147065 		for (i = 0; i < AN_TIMEOUT; i++) {
21053737Shx147065 			PCAN_READ(pcan_p, AN_COMMAND(pcan_p), stat);
21063737Shx147065 			if (!(stat & AN_CMD_BUSY)) {
21073737Shx147065 				break;
21083737Shx147065 			}
21093737Shx147065 		}
21103737Shx147065 		if (i == AN_TIMEOUT) {
21113737Shx147065 			cmn_err(CE_WARN, "pcan put_ltv: busy");
21123737Shx147065 		}
21133737Shx147065 
21143737Shx147065 		an_rid_desc.an_valid = 1;
21153737Shx147065 		an_rid_desc.an_len = len;
21163737Shx147065 		an_rid_desc.an_rid = type;
21173737Shx147065 		an_rid_desc.an_phys = pcan_p->pcan_cmd.dma_physaddr;
21183737Shx147065 
21193737Shx147065 		bcopy(val_p, pcan_p->pcan_cmd.dma_virtaddr,
21203737Shx147065 		    an_rid_desc.an_len);
21213737Shx147065 
21223737Shx147065 		for (i = 0; i < sizeof (an_rid_desc) / 4; i++)
21233737Shx147065 			PCAN_AUX_PUT32(pcan_p, AN_HOST_DESC_OFFSET + i * 4,
21243737Shx147065 			    ((uint32_t *)&an_rid_desc)[i]);
21253737Shx147065 		pcan_delay(pcan_p, 100000);
21263737Shx147065 		stat = pcan_set_cmd0(pcan_p, AN_CMD_ACCESS |
21273737Shx147065 		    AN_ACCESS_WRITE, type, 0, 0);
21283737Shx147065 		pcan_delay(pcan_p, 100000);
21293737Shx147065 		return (stat);
21303737Shx147065 	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
21313737Shx147065 		/* 0. select read mode first */
21323737Shx147065 		if (stat = pcan_set_cmd(pcan_p, AN_CMD_ACCESS |
21333737Shx147065 		    AN_ACCESS_READ, type))
21343737Shx147065 			return (stat);
21353737Shx147065 
21363737Shx147065 		/* 1. select Buffer Access Path (channel) 1 for PIO */
21373737Shx147065 		if (stat = pcan_set_ch(pcan_p, type, 0, 1))
21383737Shx147065 			return (stat);
21393737Shx147065 
21403737Shx147065 		/* 2. write length */
21413737Shx147065 		len >>= 1;		/* convert bytes to 16-bit words */
21423737Shx147065 		stat = len;
21433737Shx147065 		PCAN_WRITE(pcan_p, AN_DATA1, stat);
21443737Shx147065 
21453737Shx147065 		/* 3. write value */
21463737Shx147065 		val_p++;
21473737Shx147065 		for (stat = 0; stat < len-1; stat++, val_p++) {
21483737Shx147065 			PCAN_WRITE_P(pcan_p, AN_DATA1, val_p, 1);
21493737Shx147065 		}
21503737Shx147065 
21513737Shx147065 		/* 4. select write mode */
21523737Shx147065 		return (pcan_set_cmd(pcan_p, AN_CMD_ACCESS |
21533737Shx147065 		    AN_ACCESS_WRITE, type));
21543737Shx147065 	}
21553737Shx147065 	return (PCAN_FAIL);
21563737Shx147065 }
21573737Shx147065 
21583737Shx147065 /*ARGSUSED*/
21593737Shx147065 static uint16_t
21603737Shx147065 pcan_rdch0(pcan_maci_t *pcan_p, uint16_t type, uint16_t off, uint16_t *buf_p,
21613737Shx147065 	int len, int order)
21623737Shx147065 {
21633737Shx147065 	ASSERT(!(len & 1));
21643737Shx147065 
21653737Shx147065 	if (pcan_set_ch(pcan_p, type, off, 0) != PCAN_SUCCESS)
21663737Shx147065 		return (PCAN_FAIL);
21673737Shx147065 	len >>= 1;
21683737Shx147065 	for (off = 0; off < len; off++, buf_p++) {
21693737Shx147065 		PCAN_READ_P(pcan_p, AN_DATA0, buf_p, order);
21703737Shx147065 	}
21713737Shx147065 	return (PCAN_SUCCESS);
21723737Shx147065 }
21733737Shx147065 
21743737Shx147065 /*ARGSUSED*/
21753737Shx147065 static uint16_t
21763737Shx147065 pcan_wrch1(pcan_maci_t *pcan_p, uint16_t type, uint16_t off, uint16_t *buf_p,
21773737Shx147065 	int len, int order)
21783737Shx147065 {
21793737Shx147065 	ASSERT(!(len & 1));
21803737Shx147065 
21813737Shx147065 	if (pcan_set_ch(pcan_p, type, off, 1) != PCAN_SUCCESS)
21823737Shx147065 		return (PCAN_FAIL);
21833737Shx147065 	len >>= 1;
21843737Shx147065 	for (off = 0; off < len; off++, buf_p++) {
21853737Shx147065 		PCAN_WRITE_P(pcan_p, AN_DATA1, buf_p, order);
21863737Shx147065 	}
21873737Shx147065 	return (PCAN_SUCCESS);
21883737Shx147065 }
21893737Shx147065 
21903737Shx147065 static uint16_t
21913737Shx147065 pcan_status_ltv(int rw, pcan_maci_t *pcan_p, struct an_ltv_status *status_p)
21923737Shx147065 {
21933737Shx147065 	uint16_t ret, len;
21943737Shx147065 
21953737Shx147065 	if (rw != PCAN_READ_LTV) {
21963737Shx147065 		cmn_err(CE_WARN, "pcan status_ltv: unsupported op %x", rw);
21973737Shx147065 		return (PCAN_FAIL);
21983737Shx147065 	}
21993737Shx147065 	if (ret = pcan_get_ltv(pcan_p, sizeof (*status_p), AN_RID_STATUS,
22003737Shx147065 	    (uint16_t *)status_p))
22013737Shx147065 		return (ret);
22023737Shx147065 
22033737Shx147065 	PCAN_SWAP16_BUF(status_p->an_macaddr);
22043737Shx147065 	PCAN_SWAP16_BUF(status_p->an_ssid);
22053737Shx147065 	len = min(status_p->an_ssidlen, 31);
22063737Shx147065 	status_p->an_ssid[len] = '\0';
22073737Shx147065 	PCAN_SWAP16_BUF(status_p->an_ap_name);
22083737Shx147065 	PCAN_SWAP16_BUF(status_p->an_cur_bssid);
22093737Shx147065 	PCAN_SWAP16_BUF(status_p->an_prev_bssid1);
22103737Shx147065 	PCAN_SWAP16_BUF(status_p->an_prev_bssid2);
22113737Shx147065 	PCAN_SWAP16_BUF(status_p->an_prev_bssid3);
22123737Shx147065 	PCAN_SWAP16_BUF(status_p->an_ap_ip_address);
22133737Shx147065 	PCAN_SWAP16_BUF(status_p->an_carrier);
22143737Shx147065 	return (PCAN_SUCCESS);
22153737Shx147065 }
22163737Shx147065 
22173737Shx147065 static uint16_t
22183737Shx147065 pcan_cfg_ltv(int rw, pcan_maci_t *pcan_p, struct an_ltv_genconfig *cfg_p)
22193737Shx147065 {
22203737Shx147065 	uint16_t ret;
22213737Shx147065 	uint16_t rid = cfg_p == &pcan_p->an_config ?
22223737Shx147065 	    AN_RID_GENCONFIG : AN_RID_ACTUALCFG;
22233737Shx147065 
22243737Shx147065 	if (rw == PCAN_READ_LTV) {
22253737Shx147065 		if (ret = pcan_get_ltv(pcan_p, sizeof (*cfg_p), rid,
22263737Shx147065 		    (uint16_t *)cfg_p))
22273737Shx147065 			return (ret);
22283737Shx147065 		goto done;
22293737Shx147065 	}
22303737Shx147065 	PCAN_SWAP16_BUF(cfg_p->an_macaddr);
22313737Shx147065 	PCAN_SWAP16_BUF(cfg_p->an_rates);
22323737Shx147065 	if (ret = pcan_put_ltv(pcan_p, sizeof (*cfg_p),
22333737Shx147065 	    rid, (uint16_t *)cfg_p))
22343737Shx147065 		return (ret);
22353737Shx147065 done:
22363737Shx147065 	PCAN_SWAP16_BUF(cfg_p->an_macaddr);
22373737Shx147065 	PCAN_SWAP16_BUF(cfg_p->an_rates);
22383737Shx147065 	return (ret);
22393737Shx147065 }
22403737Shx147065 
22413737Shx147065 static uint16_t
22423737Shx147065 pcan_cap_ltv(int rw, pcan_maci_t *pcan_p)
22433737Shx147065 {
22443737Shx147065 	uint16_t ret;
22453737Shx147065 
22463737Shx147065 	if (rw != PCAN_READ_LTV) {
22473737Shx147065 		cmn_err(CE_WARN, "pcan cap_ltv: unsupported op %x", rw);
22483737Shx147065 		return (PCAN_FAIL);
22493737Shx147065 	}
22503737Shx147065 	if (ret = pcan_get_ltv(pcan_p, sizeof (struct an_ltv_caps),
22514343Sgd78059 	    AN_RID_CAPABILITIES, (uint16_t *)&pcan_p->an_caps))
22523737Shx147065 		return (ret);
22533737Shx147065 
22543737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_caps.an_oui);
22553737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_caps.an_manufname);
22563737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_caps.an_prodname);
22573737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_caps.an_prodvers);
22583737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_caps.an_oemaddr);
22593737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_caps.an_aironetaddr);
22603737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_caps.an_callid);
22613737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_caps.an_supported_rates);
22623737Shx147065 	return (PCAN_SUCCESS);
22633737Shx147065 }
22643737Shx147065 
22653737Shx147065 static uint16_t
22663737Shx147065 pcan_ssid_ltv(int rw, pcan_maci_t *pcan_p)
22673737Shx147065 {
22683737Shx147065 	uint16_t ret;
22693737Shx147065 
22703737Shx147065 	if (rw == PCAN_READ_LTV) {
22713737Shx147065 		if (ret = pcan_get_ltv(pcan_p, sizeof (struct an_ltv_ssidlist),
22723737Shx147065 		    AN_RID_SSIDLIST, (uint16_t *)&pcan_p->an_ssidlist))
22733737Shx147065 			return (ret);
22743737Shx147065 		goto done;
22753737Shx147065 	}
22763737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_ssidlist.an_ssid1);
22773737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_ssidlist.an_ssid2);
22783737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_ssidlist.an_ssid3);
22793737Shx147065 	if (ret = pcan_put_ltv(pcan_p, sizeof (struct an_ltv_ssidlist),
22803737Shx147065 	    AN_RID_SSIDLIST, (uint16_t *)&pcan_p->an_ssidlist))
22813737Shx147065 		return (ret);
22823737Shx147065 done:
22833737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_ssidlist.an_ssid1);
22843737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_ssidlist.an_ssid2);
22853737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_ssidlist.an_ssid3);
22863737Shx147065 	return (ret);
22873737Shx147065 }
22883737Shx147065 
22893737Shx147065 static uint16_t
22903737Shx147065 pcan_aplist_ltv(int rw, pcan_maci_t *pcan_p)
22913737Shx147065 {
22923737Shx147065 	uint16_t ret;
22933737Shx147065 
22943737Shx147065 	if (rw == PCAN_READ_LTV) {
22953737Shx147065 		if (ret = pcan_get_ltv(pcan_p, sizeof (struct an_ltv_aplist),
22963737Shx147065 		    AN_RID_APLIST, (uint16_t *)&pcan_p->an_aplist))
22973737Shx147065 			return (ret);
22983737Shx147065 		goto done;
22993737Shx147065 	}
23003737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap1);
23013737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap2);
23023737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap3);
23033737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap4);
23043737Shx147065 	if (ret = pcan_put_ltv(pcan_p, sizeof (struct an_ltv_aplist),
23053737Shx147065 	    AN_RID_APLIST, (uint16_t *)&pcan_p->an_aplist))
23063737Shx147065 		return (ret);
23073737Shx147065 done:
23083737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap1);
23093737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap2);
23103737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap3);
23113737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap4);
23123737Shx147065 	return (ret);
23133737Shx147065 }
23143737Shx147065 
23153737Shx147065 static uint16_t
23163737Shx147065 pcan_scanresult_ltv(int rw, pcan_maci_t *pcan_p, uint16_t type,
23173737Shx147065     struct an_ltv_scanresult *scanresult_p)
23183737Shx147065 {
23193737Shx147065 	uint16_t ret, len;
23203737Shx147065 
23213737Shx147065 	if (rw != PCAN_READ_LTV) {
23223737Shx147065 		cmn_err(CE_WARN, "pcan scan_ltv: readonly rid %x\n", type);
23233737Shx147065 		return (PCAN_FAIL);
23243737Shx147065 	}
23253737Shx147065 	if (ret = pcan_get_ltv(pcan_p, sizeof (struct an_ltv_scanresult),
23263737Shx147065 	    type, (uint16_t *)scanresult_p))
23273737Shx147065 		return (ret);
23283737Shx147065 	PCAN_SWAP16_BUF(scanresult_p->an_bssid);
23293737Shx147065 	PCAN_SWAP16_BUF(scanresult_p->an_ssid);
23303737Shx147065 	len = min(scanresult_p->an_ssidlen, 31);
23313737Shx147065 	scanresult_p->an_ssid[len] = '\0';
23323737Shx147065 	PCAN_SWAP16_BUF(scanresult_p->an_rates);
23333737Shx147065 	return (PCAN_SUCCESS);
23343737Shx147065 }
23353737Shx147065 
23363737Shx147065 static uint16_t
23373737Shx147065 pcan_one_wepkey(int rw, pcan_maci_t *pcan_p, struct an_ltv_wepkey *wkp,
23383737Shx147065     uint16_t rid)
23393737Shx147065 {
23403737Shx147065 	uint16_t ret;
23413737Shx147065 
23423737Shx147065 	if (rw == PCAN_READ_LTV) {
23433737Shx147065 		if (ret = pcan_get_ltv(pcan_p, sizeof (struct an_ltv_wepkey),
23443737Shx147065 		    rid, (uint16_t *)wkp)) {
23453737Shx147065 			return (ret);
23463737Shx147065 		}
23473737Shx147065 		goto done;
23483737Shx147065 	}
23493737Shx147065 	PCAN_SWAP16_BUF(wkp->an_macaddr);
23503737Shx147065 	PCAN_SWAP16_BUF(wkp->an_key);
23513737Shx147065 	if (ret = pcan_put_ltv(pcan_p, sizeof (struct an_ltv_wepkey),
23523737Shx147065 	    rid, (uint16_t *)wkp))
23533737Shx147065 		return (ret);
23543737Shx147065 done:
23553737Shx147065 	PCAN_SWAP16_BUF(wkp->an_macaddr);
23563737Shx147065 	PCAN_SWAP16_BUF(wkp->an_key);
23573737Shx147065 	return (ret);
23583737Shx147065 }
23593737Shx147065 
23603737Shx147065 static uint16_t
23613737Shx147065 pcan_wepkey_ltv(int rw, pcan_maci_t *pcan_p)
23623737Shx147065 {
23633737Shx147065 	uint16_t ret, i;
23643737Shx147065 	struct an_ltv_wepkey wk;
23653737Shx147065 
23663737Shx147065 	if (rw == PCAN_READ_LTV) {
23673737Shx147065 		uint16_t rid = AN_RID_WEPKEY2;
23683737Shx147065 
23693737Shx147065 		if (ret = pcan_one_wepkey(rw, pcan_p, &wk, rid))
23703737Shx147065 			return (ret);
23713737Shx147065 		for (i = 0; i < 5; i++) {
23723737Shx147065 			if (wk.an_index < 4)
23733737Shx147065 				pcan_p->an_wepkey[wk.an_index] = wk;
23743737Shx147065 			else if (wk.an_index == 0xffff)
23753737Shx147065 				pcan_p->an_cur_wepkey = wk.an_macaddr[0];
23763737Shx147065 			rid = AN_RID_WEPKEY;
23773737Shx147065 		}
23783737Shx147065 		return (PCAN_SUCCESS);
23793737Shx147065 	}
23803737Shx147065 	for (i = 0; i < MAX_NWEPKEYS; i++) {
23813737Shx147065 		if (pcan_p->an_wepkey[i].an_index == i) {
23823737Shx147065 			if (ret = pcan_one_wepkey(rw, pcan_p,
23833737Shx147065 			    &pcan_p->an_wepkey[i], AN_RID_WEPKEY2))
23843737Shx147065 				return (ret);
23853737Shx147065 		}
23863737Shx147065 	}
23873737Shx147065 	/* Now set the default key */
23883737Shx147065 	(void) memset(&wk, 0, sizeof (wk));
23893737Shx147065 	wk.an_index = 0xffff;
23903737Shx147065 	wk.an_macaddr[0] = pcan_p->an_cur_wepkey;
23913737Shx147065 	ret = pcan_one_wepkey(rw, pcan_p, &wk, AN_RID_WEPKEY2);
23923737Shx147065 	return (ret);
23933737Shx147065 }
23943737Shx147065 
23953737Shx147065 static uint16_t
23963737Shx147065 pcan_alloc_nicmem(pcan_maci_t *pcan_p, uint16_t len, uint16_t *id_p)
23973737Shx147065 {
23983737Shx147065 	int i;
23993737Shx147065 	uint16_t stat;
24003737Shx147065 
24013737Shx147065 	len = ((len + 1) >> 1) << 1;	/* round up to 16-bit boundary */
24023737Shx147065 
24033737Shx147065 	if (stat = pcan_set_cmd(pcan_p, AN_CMD_ALLOC_MEM, len))
24043737Shx147065 		return (stat);
24053737Shx147065 	for (i = 0; !(stat & AN_EV_ALLOC) && (i < AN_TIMEOUT); i++) {
24063737Shx147065 		PCAN_READ(pcan_p, AN_EVENT_STAT(pcan_p), stat);
24073737Shx147065 	}
24083737Shx147065 	if (!(stat & AN_EV_ALLOC))
24093737Shx147065 		return (PCAN_TIMEDOUT_ALLOC);
24103737Shx147065 	PCAN_READ(pcan_p, AN_ALLOC_FID, stat);
24113737Shx147065 	PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_ALLOC);
24123737Shx147065 	*id_p = stat;
24133737Shx147065 
24143737Shx147065 	/* zero fill the allocated NIC mem - sort of pcan_fill_ch0 */
24153737Shx147065 	(void) pcan_set_ch(pcan_p, stat, 0, 0);
24163737Shx147065 	for (len >>= 1, stat = 0; stat < len; stat++) {
24173737Shx147065 		PCAN_WRITE(pcan_p, AN_DATA0, 0);
24183737Shx147065 	}
24193737Shx147065 	return (PCAN_SUCCESS);
24203737Shx147065 }
24213737Shx147065 
24223737Shx147065 static void
24233737Shx147065 pcan_stop_rx_dma(pcan_maci_t *pcan_p)
24243737Shx147065 {
24253737Shx147065 	int i, j;
24263737Shx147065 	struct an_card_rx_desc  an_rx_desc;
24273737Shx147065 
24283737Shx147065 	for (i = 0; i < AN_MAX_RX_DESC; i++) {
24293737Shx147065 		bzero(&an_rx_desc, sizeof (an_rx_desc));
24303737Shx147065 		an_rx_desc.an_valid = 0;
24313737Shx147065 		an_rx_desc.an_len = AN_RX_BUFFER_SIZE;
24323737Shx147065 		an_rx_desc.an_done = 1;
24333737Shx147065 		an_rx_desc.an_phys = pcan_p->pcan_rx[i].dma_physaddr;
24343737Shx147065 		for (j = 0; j < sizeof (an_rx_desc) / 4; j++)
24353737Shx147065 			PCAN_AUX_PUT32(pcan_p, AN_RX_DESC_OFFSET
24363737Shx147065 			    + (i * sizeof (an_rx_desc))
24373737Shx147065 			    + (j * 4), ((uint32_t *)&an_rx_desc)[j]);
24383737Shx147065 	}
24393737Shx147065 }
24403737Shx147065 
24413737Shx147065 static int
24423737Shx147065 pcan_init_dma_desc(pcan_maci_t *pcan_p)
24433737Shx147065 {
24443737Shx147065 	int i, j;
24453737Shx147065 	struct an_card_rid_desc an_rid_desc;
24463737Shx147065 	struct an_card_rx_desc  an_rx_desc;
24473737Shx147065 	struct an_card_tx_desc  an_tx_desc;
24483737Shx147065 
24493737Shx147065 	/* Allocate DMA for rx */
24503737Shx147065 	if (pcan_set_cmd0(pcan_p, AN_CMD_ALLOC_DESC,
24513737Shx147065 	    AN_DESCRIPTOR_RX, AN_RX_DESC_OFFSET,
24523737Shx147065 	    AN_MAX_RX_DESC) != PCAN_SUCCESS) {
24533737Shx147065 		cmn_err(CE_WARN, "pcan init_dma: fail to alloc rx descriptor");
24543737Shx147065 		goto error;
24553737Shx147065 	}
24563737Shx147065 	for (i = 0; i < AN_MAX_RX_DESC; i++) {
24573737Shx147065 		bzero(&an_rx_desc, sizeof (an_rx_desc));
24583737Shx147065 		an_rx_desc.an_valid = 1;
24593737Shx147065 		an_rx_desc.an_len = AN_RX_BUFFER_SIZE;
24603737Shx147065 		an_rx_desc.an_done = 0;
24613737Shx147065 		an_rx_desc.an_phys = pcan_p->pcan_rx[i].dma_physaddr;
24623737Shx147065 		for (j = 0; j < sizeof (an_rx_desc) / 4; j++)
24633737Shx147065 			PCAN_AUX_PUT32(pcan_p, AN_RX_DESC_OFFSET
24643737Shx147065 			    + (i * sizeof (an_rx_desc))
24653737Shx147065 			    + (j * 4), ((uint32_t *)&an_rx_desc)[j]);
24663737Shx147065 	}
24673737Shx147065 
24683737Shx147065 
24693737Shx147065 	/* Allocate DMA for tx */
24703737Shx147065 	if (pcan_set_cmd0(pcan_p, AN_CMD_ALLOC_DESC,
24713737Shx147065 	    AN_DESCRIPTOR_TX, AN_TX_DESC_OFFSET,
24723737Shx147065 	    AN_MAX_TX_DESC) != PCAN_SUCCESS) {
24733737Shx147065 		cmn_err(CE_WARN, "pcan init_dma: fail to alloc tx descriptor");
24743737Shx147065 		goto error;
24753737Shx147065 	}
24763737Shx147065 
24773737Shx147065 	for (i = 0; i < AN_MAX_TX_DESC; i++) {
24783737Shx147065 		an_tx_desc.an_offset = 0;
24793737Shx147065 		an_tx_desc.an_eoc = 0;
24803737Shx147065 		an_tx_desc.an_valid = 0;
24813737Shx147065 		an_tx_desc.an_len = 0;
24823737Shx147065 		an_tx_desc.an_phys = pcan_p->pcan_tx[i].dma_physaddr;
24833737Shx147065 
24843737Shx147065 		for (j = 0; j < sizeof (an_tx_desc) / 4; j++)
24853737Shx147065 			PCAN_AUX_PUT32(pcan_p, AN_TX_DESC_OFFSET
24863737Shx147065 			    + (i * sizeof (an_tx_desc))
24873737Shx147065 			    + (j * 4), ((uint32_t *)&an_tx_desc)[j]);
24883737Shx147065 	}
24893737Shx147065 
24903737Shx147065 	/* Allocate DMA for rid */
24913737Shx147065 	if (pcan_set_cmd0(pcan_p, AN_CMD_ALLOC_DESC,
24923737Shx147065 	    AN_DESCRIPTOR_HOSTRW, AN_HOST_DESC_OFFSET, 1) != PCAN_SUCCESS) {
24933737Shx147065 		cmn_err(CE_WARN, "pcan init_dma: fail to alloc rid descriptor");
24943737Shx147065 		goto error;
24953737Shx147065 	}
24963737Shx147065 	bzero(&an_rid_desc, sizeof (an_rid_desc));
24973737Shx147065 	an_rid_desc.an_valid = 1;
24983737Shx147065 	an_rid_desc.an_len = AN_RID_BUFFER_SIZE;
24993737Shx147065 	an_rid_desc.an_rid = 0;
25003737Shx147065 	an_rid_desc.an_phys = pcan_p->pcan_cmd.dma_physaddr;
25013737Shx147065 
25023737Shx147065 	for (i = 0; i < sizeof (an_rid_desc) / 4; i++)
25033737Shx147065 		PCAN_AUX_PUT32(pcan_p, AN_HOST_DESC_OFFSET + i * 4,
25043737Shx147065 		    ((uint32_t *)&an_rid_desc)[i]);
25053737Shx147065 
25063737Shx147065 	pcan_p->pcan_txring.an_tx_prod = 0;
25073737Shx147065 	pcan_p->pcan_txring.an_tx_cons = 0;
25083737Shx147065 	pcan_p->pcan_flag &= ~PCAN_CARD_SEND;
25093737Shx147065 	return (PCAN_SUCCESS);
25103737Shx147065 error:
25113737Shx147065 	return (PCAN_FAIL);
25123737Shx147065 }
25133737Shx147065 
25143737Shx147065 static int
25153737Shx147065 pcan_init_dma(dev_info_t *dip, pcan_maci_t *pcan_p)
25163737Shx147065 {
25173737Shx147065 	int i, ret = PCAN_FAIL;
25183737Shx147065 	ddi_dma_cookie_t dma_cookie;
25193737Shx147065 	size_t len;
25203737Shx147065 
25213737Shx147065 	/* Allocate DMA for rx */
25223737Shx147065 	for (i = 0; i < AN_MAX_RX_DESC; i++) {
25233737Shx147065 		if (ddi_dma_alloc_handle(dip, &control_cmd_dma_attr,
25243737Shx147065 		    DDI_DMA_SLEEP, 0,
25253737Shx147065 		    &pcan_p->pcan_rx[i].dma_handle) != DDI_SUCCESS)
25263737Shx147065 			goto error;
25273737Shx147065 
25283737Shx147065 		if (ddi_dma_mem_alloc(pcan_p->pcan_rx[i].dma_handle,
25293737Shx147065 		    AN_RX_BUFFER_SIZE, &accattr,
25303737Shx147065 		    DDI_DMA_STREAMING, DDI_DMA_SLEEP, 0,
25313737Shx147065 		    (caddr_t *)&pcan_p->pcan_rx[i].dma_virtaddr, &len,
25323737Shx147065 		    &pcan_p->pcan_rx[i].dma_acc_handle) != DDI_SUCCESS) {
25333737Shx147065 			goto error;
25343737Shx147065 		}
25353737Shx147065 		if (ddi_dma_addr_bind_handle(
25363737Shx147065 		    pcan_p->pcan_rx[i].dma_handle,
25373737Shx147065 		    NULL, (caddr_t)pcan_p->pcan_rx[i].dma_virtaddr,
25383737Shx147065 		    len, DDI_DMA_READ |
25393737Shx147065 		    DDI_DMA_STREAMING, DDI_DMA_SLEEP, 0, &dma_cookie,
25403737Shx147065 		    &pcan_p->pcan_rx[i].ncookies) != DDI_DMA_MAPPED) {
25413737Shx147065 			goto error;
25423737Shx147065 		}
25433737Shx147065 		ASSERT(pcan_p->pcan_rx[i].ncookies == 1);
25443737Shx147065 		pcan_p->pcan_rx[i].dma_physaddr = dma_cookie.dmac_address;
25453737Shx147065 	}
25463737Shx147065 
25473737Shx147065 	/* Allocate DMA for tx */
25483737Shx147065 	for (i = 0; i < AN_MAX_TX_DESC; i++) {
25493737Shx147065 		if (ddi_dma_alloc_handle(dip, &control_cmd_dma_attr,
25503737Shx147065 		    DDI_DMA_SLEEP, 0,
25513737Shx147065 		    &pcan_p->pcan_tx[i].dma_handle) != DDI_SUCCESS)
25523737Shx147065 			goto error;
25533737Shx147065 
25543737Shx147065 		if (ddi_dma_mem_alloc(pcan_p->pcan_tx[i].dma_handle,
25553737Shx147065 		    AN_TX_BUFFER_SIZE, &accattr,
25563737Shx147065 		    DDI_DMA_STREAMING, DDI_DMA_SLEEP, 0,
25573737Shx147065 		    (caddr_t *)&pcan_p->pcan_tx[i].dma_virtaddr, &len,
25583737Shx147065 		    &pcan_p->pcan_tx[i].dma_acc_handle) != DDI_SUCCESS) {
25593737Shx147065 			goto error;
25603737Shx147065 		}
25613737Shx147065 		if (ddi_dma_addr_bind_handle(
25623737Shx147065 		    pcan_p->pcan_tx[i].dma_handle,
25633737Shx147065 		    NULL, (caddr_t)pcan_p->pcan_tx[i].dma_virtaddr,
25643737Shx147065 		    len, DDI_DMA_WRITE |
25653737Shx147065 		    DDI_DMA_STREAMING, DDI_DMA_SLEEP, 0, &dma_cookie,
25663737Shx147065 		    &pcan_p->pcan_tx[i].ncookies) != DDI_DMA_MAPPED) {
25673737Shx147065 			goto error;
25683737Shx147065 		}
25693737Shx147065 		ASSERT(pcan_p->pcan_tx[i].ncookies == 1);
25703737Shx147065 		pcan_p->pcan_tx[i].dma_physaddr = dma_cookie.dmac_address;
25713737Shx147065 	}
25723737Shx147065 
25733737Shx147065 	/* Allocate DMA for rid */
25743737Shx147065 	if (ddi_dma_alloc_handle(dip, &control_cmd_dma_attr,
25753737Shx147065 	    DDI_DMA_SLEEP, 0,
25763737Shx147065 	    &pcan_p->pcan_cmd.dma_handle) != DDI_SUCCESS)
25773737Shx147065 		goto error;
25783737Shx147065 
25793737Shx147065 	if (ddi_dma_mem_alloc(pcan_p->pcan_cmd.dma_handle,
25803737Shx147065 	    AN_RID_BUFFER_SIZE, &accattr,
25813737Shx147065 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0,
25823737Shx147065 	    (caddr_t *)&pcan_p->pcan_cmd.dma_virtaddr, &len,
25833737Shx147065 	    &pcan_p->pcan_cmd.dma_acc_handle) != DDI_SUCCESS) {
25843737Shx147065 		goto error;
25853737Shx147065 	}
25863737Shx147065 	if (ddi_dma_addr_bind_handle(
25873737Shx147065 	    pcan_p->pcan_cmd.dma_handle,
25883737Shx147065 	    NULL, (caddr_t)pcan_p->pcan_cmd.dma_virtaddr,
25893737Shx147065 	    len, DDI_DMA_RDWR |
25903737Shx147065 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0, &dma_cookie,
25913737Shx147065 	    &pcan_p->pcan_cmd.ncookies) != DDI_DMA_MAPPED) {
25923737Shx147065 		goto error;
25933737Shx147065 	}
25943737Shx147065 	ASSERT(pcan_p->pcan_cmd.ncookies == 1);
25953737Shx147065 	pcan_p->pcan_cmd.dma_physaddr = dma_cookie.dmac_address;
25963737Shx147065 
25973737Shx147065 	if (ret = pcan_init_dma_desc(pcan_p)) {
25983737Shx147065 		cmn_err(CE_WARN, "pcan init_dma_desc: failed\n");
25993737Shx147065 		goto error;
26003737Shx147065 	}
26013737Shx147065 
26023737Shx147065 	return (PCAN_SUCCESS);
26033737Shx147065 error:
26043737Shx147065 	pcan_free_dma(pcan_p);
26053737Shx147065 	return (ret);
26063737Shx147065 }
26073737Shx147065 
26083737Shx147065 static void
26093737Shx147065 pcan_free_dma(pcan_maci_t *pcan_p)
26103737Shx147065 {
26113737Shx147065 	int i;
26123737Shx147065 
26133737Shx147065 	/* free RX dma */
26143737Shx147065 	pcan_stop_rx_dma(pcan_p);
26153737Shx147065 	for (i = 0; i < AN_MAX_RX_DESC; i++) {
26163737Shx147065 		if (pcan_p->pcan_rx[i].dma_handle != NULL) {
26173737Shx147065 			if (pcan_p->pcan_rx[i].ncookies) {
26183737Shx147065 				(void) ddi_dma_unbind_handle(
26193737Shx147065 				    pcan_p->pcan_rx[i].dma_handle);
26203737Shx147065 				pcan_p->pcan_rx[i].ncookies = 0;
26213737Shx147065 			}
26223737Shx147065 			ddi_dma_free_handle(
26233737Shx147065 			    &pcan_p->pcan_rx[i].dma_handle);
26243737Shx147065 			pcan_p->pcan_rx[i].dma_handle = NULL;
26253737Shx147065 		}
26263737Shx147065 		if (pcan_p->pcan_rx[i].dma_acc_handle != NULL) {
26273737Shx147065 			ddi_dma_mem_free(
26283737Shx147065 			    &pcan_p->pcan_rx[i].dma_acc_handle);
26293737Shx147065 			pcan_p->pcan_rx[i].dma_acc_handle = NULL;
26303737Shx147065 		}
26313737Shx147065 	}
26323737Shx147065 
26333737Shx147065 	/* free TX dma */
26343737Shx147065 	for (i = 0; i < AN_MAX_TX_DESC; i++) {
26353737Shx147065 		if (pcan_p->pcan_tx[i].dma_handle != NULL) {
26363737Shx147065 			if (pcan_p->pcan_tx[i].ncookies) {
26373737Shx147065 				(void) ddi_dma_unbind_handle(
26383737Shx147065 				    pcan_p->pcan_tx[i].dma_handle);
26393737Shx147065 				pcan_p->pcan_tx[i].ncookies = 0;
26403737Shx147065 			}
26413737Shx147065 			ddi_dma_free_handle(
26423737Shx147065 			    &pcan_p->pcan_tx[i].dma_handle);
26433737Shx147065 			pcan_p->pcan_tx[i].dma_handle = NULL;
26443737Shx147065 		}
26453737Shx147065 		if (pcan_p->pcan_tx[i].dma_acc_handle != NULL) {
26463737Shx147065 			ddi_dma_mem_free(
26473737Shx147065 			    &pcan_p->pcan_tx[i].dma_acc_handle);
26483737Shx147065 			pcan_p->pcan_tx[i].dma_acc_handle = NULL;
26493737Shx147065 		}
26503737Shx147065 	}
26513737Shx147065 
26523737Shx147065 	/* free cmd dma */
26533737Shx147065 	if (pcan_p->pcan_cmd.dma_handle != NULL) {
26543737Shx147065 		if (pcan_p->pcan_cmd.ncookies) {
26553737Shx147065 			(void) ddi_dma_unbind_handle(
26563737Shx147065 			    pcan_p->pcan_cmd.dma_handle);
26573737Shx147065 			pcan_p->pcan_cmd.ncookies = 0;
26583737Shx147065 		}
26593737Shx147065 		ddi_dma_free_handle(
26603737Shx147065 		    &pcan_p->pcan_cmd.dma_handle);
26613737Shx147065 		pcan_p->pcan_cmd.dma_handle = NULL;
26623737Shx147065 	}
26633737Shx147065 	if (pcan_p->pcan_cmd.dma_acc_handle != NULL) {
26643737Shx147065 		ddi_dma_mem_free(
26653737Shx147065 		    &pcan_p->pcan_cmd.dma_acc_handle);
26663737Shx147065 		pcan_p->pcan_cmd.dma_acc_handle = NULL;
26673737Shx147065 	}
26683737Shx147065 }
26693737Shx147065 
26703737Shx147065 /*
26713737Shx147065  * get card capability (WEP, default channel), setup broadcast, mac addresses
26723737Shx147065  */
26733737Shx147065 static uint32_t
26743737Shx147065 pcan_get_cap(pcan_maci_t *pcan_p)
26753737Shx147065 {
26763737Shx147065 	uint16_t stat;
26773737Shx147065 
26783737Shx147065 	if (stat = pcan_cfg_ltv(PCAN_READ_LTV, pcan_p, &pcan_p->an_config)) {
26793737Shx147065 		PCANDBG((CE_NOTE, "pcan get_cap: read cfg fail %x", stat));
26803737Shx147065 		return ((uint32_t)AN_RID_GENCONFIG << 16 | stat);
26813737Shx147065 	}
26823737Shx147065 
26833737Shx147065 	if (stat = pcan_cap_ltv(PCAN_READ_LTV, pcan_p)) {
26843737Shx147065 		PCANDBG((CE_NOTE, "pcan get_cap: read cap fail %x", stat));
26853737Shx147065 		return ((uint32_t)AN_RID_CAPABILITIES << 16 | stat);
26863737Shx147065 	}
26873737Shx147065 #ifdef DEBUG
26883737Shx147065 	if (pcan_debug & PCAN_DBG_FW_VERSION) {
26893737Shx147065 		cmn_err(CE_NOTE, "the version of the firmware in the wifi card "
26903737Shx147065 		    "'%s %s %s' is %s\n",
26913737Shx147065 		    pcan_p->an_caps.an_manufname,
26923737Shx147065 		    pcan_p->an_caps.an_prodname,
26933737Shx147065 		    pcan_p->pcan_device_type == PCAN_DEVICE_PCI ?
26943737Shx147065 		    "minipci" : "pccard",
26953737Shx147065 		    pcan_p->an_caps.an_prodvers);
26963737Shx147065 	}
26973737Shx147065 #endif
26983737Shx147065 
26993737Shx147065 	if (stat = pcan_ssid_ltv(PCAN_READ_LTV, pcan_p)) {
27003737Shx147065 		PCANDBG((CE_NOTE, "pcan get_cap: read ssid fail %x", stat));
27013737Shx147065 		return ((uint32_t)AN_RID_SSIDLIST << 16 | stat);
27023737Shx147065 	}
27033737Shx147065 
27043737Shx147065 	if (stat = pcan_aplist_ltv(PCAN_READ_LTV, pcan_p)) {
27053737Shx147065 		PCANDBG((CE_NOTE, "pcan get_cap: read aplist fail %x", stat));
27063737Shx147065 		return ((uint32_t)AN_RID_APLIST << 16 | stat);
27073737Shx147065 	}
27083737Shx147065 	if (stat = pcan_wepkey_ltv(PCAN_READ_LTV, pcan_p)) {
27093737Shx147065 		PCANDBG((CE_NOTE, "pcan get_cap: read wepkey fail %x", stat));
27103737Shx147065 		return ((uint32_t)AN_RID_WEPKEY2 << 16 | stat);
27113737Shx147065 	}
27123737Shx147065 	ether_copy(pcan_p->an_caps.an_oemaddr, pcan_p->pcan_mac_addr);
27133737Shx147065 	return (PCAN_SUCCESS);
27143737Shx147065 }
27153737Shx147065 
27163737Shx147065 static int
27173737Shx147065 pcan_config_mac(pcan_maci_t *pcan_p)
27183737Shx147065 {
27193737Shx147065 	uint16_t stat;
27203737Shx147065 
27213737Shx147065 	if (stat = pcan_ssid_ltv(PCAN_WRITE_LTV, pcan_p)) {
27223737Shx147065 		PCANDBG((CE_NOTE, "pcan config_mac: write SSID failed%x\n",
27233737Shx147065 		    stat));
27243737Shx147065 		return ((int)stat);
27253737Shx147065 	}
27263737Shx147065 
27273737Shx147065 	if (stat = pcan_aplist_ltv(PCAN_WRITE_LTV, pcan_p)) {
27283737Shx147065 		PCANDBG((CE_NOTE, "pcan config_mac: write APlist failed%x\n",
27293737Shx147065 		    stat));
27303737Shx147065 		return ((int)stat);
27313737Shx147065 	}
27323737Shx147065 	if (stat = pcan_wepkey_ltv(PCAN_WRITE_LTV, pcan_p)) {
27333737Shx147065 		PCANDBG((CE_NOTE, "pcan config_mac: write wepkey failed%x\n",
27343737Shx147065 		    stat));
27353737Shx147065 		return ((int)stat);
27363737Shx147065 	}
27373737Shx147065 	if (pcan_p->pcan_usewep)
27383737Shx147065 		pcan_p->an_config.an_authtype |=
27393737Shx147065 		    AN_AUTHTYPE_ENABLEWEP | AN_AUTHTYPE_ALLOW_UNENCRYPTED;
27403737Shx147065 	PCANDBG((CE_NOTE, "pcan config_mac: usewep=%x authtype=%x opmode=%x\n",
27413737Shx147065 	    pcan_p->pcan_usewep, pcan_p->an_config.an_authtype,
27423737Shx147065 	    pcan_p->an_config.an_opmode));
27433737Shx147065 
27443737Shx147065 	pcan_p->an_config.an_assoc_timeout = 5000; /* stop assoc seq in 5 sec */
27453737Shx147065 	if (stat = pcan_cfg_ltv(PCAN_WRITE_LTV, pcan_p, &pcan_p->an_config)) {
27463737Shx147065 		PCANDBG((CE_NOTE, "pcan config_mac: write cfg failed %x\n",
27473737Shx147065 		    stat));
27483737Shx147065 		return ((int)stat);
27493737Shx147065 	}
27503737Shx147065 
27513737Shx147065 	if (stat = pcan_cfg_ltv(PCAN_READ_LTV, pcan_p,
27523737Shx147065 	    &pcan_p->an_actual_config)) {
27533737Shx147065 		PCANDBG((CE_NOTE, "pcan config_mac: read cfg failed%x\n",
27543737Shx147065 		    stat));
27553737Shx147065 		return ((int)stat);
27563737Shx147065 	}
27573737Shx147065 	PCANDBG((CE_NOTE, "pcan config_mac: optionmask=%x authtype=%x\n", 0,
27583737Shx147065 	    pcan_p->an_actual_config.an_authtype));
27593737Shx147065 
27603737Shx147065 	if (stat = pcan_status_ltv(PCAN_READ_LTV, pcan_p, &pcan_p->an_status)) {
27613737Shx147065 		PCANDBG((CE_NOTE, "pcan config_mac: read status failed %x\n",
27623737Shx147065 		    stat));
27633737Shx147065 		return ((int)stat);
27643737Shx147065 	}
27653737Shx147065 	return (PCAN_SUCCESS);
27663737Shx147065 }
27673737Shx147065 
27683737Shx147065 static int
27693737Shx147065 pcan_loaddef(pcan_maci_t *pcan_p)
27703737Shx147065 {
27713737Shx147065 	int i;
27723737Shx147065 
27733737Shx147065 	pcan_p->an_ssidlist.an_ssid1_len = 0;
27743737Shx147065 	bzero(pcan_p->an_ssidlist.an_ssid1,
27754343Sgd78059 	    sizeof (pcan_p->an_ssidlist.an_ssid1));
27763737Shx147065 	for (i = 0; i < MAX_NWEPKEYS; i++) {
27773737Shx147065 		pcan_p->an_wepkey[i].an_index = 0xffff;
27783737Shx147065 		bzero(pcan_p->an_wepkey[i].an_key,
27793737Shx147065 		    sizeof (pcan_p->an_wepkey[i].an_key));
27803737Shx147065 		pcan_p->an_wepkey[i].an_keylen = 0;
27813737Shx147065 		bzero(pcan_p->an_wepkey[i].an_macaddr,
27823737Shx147065 		    sizeof (pcan_p->an_wepkey[i].an_macaddr));
27833737Shx147065 		pcan_p->an_wepkey[i].an_macaddr[0] = 1;
27843737Shx147065 	}
27853737Shx147065 	pcan_p->an_cur_wepkey = 0;
27863737Shx147065 
27873737Shx147065 	pcan_p->pcan_usewep = 0;
27883737Shx147065 	pcan_p->an_config.an_opmode = AN_OPMODE_INFR_STATION;
27893737Shx147065 	pcan_p->an_config.an_authtype = AN_AUTHTYPE_OPEN;
27903737Shx147065 	pcan_p->an_config.an_stationary = 1;
27913737Shx147065 	pcan_p->an_config.an_max_beacon_lost_time = 0xffff;
27923737Shx147065 	i = pcan_config_mac(pcan_p);
27933737Shx147065 
27943737Shx147065 	return (i);
27953737Shx147065 }
27963737Shx147065 
27973737Shx147065 static int
27983737Shx147065 pcan_init_nicmem(pcan_maci_t *pcan_p)
27993737Shx147065 {
28003737Shx147065 	int i;
28013737Shx147065 	uint16_t ret;
28023737Shx147065 	pcan_txring_t *ring_p = &pcan_p->pcan_txring;
28033737Shx147065 
28043737Shx147065 	for (i = 0; i < AN_TX_RING_CNT; i++) {
28053737Shx147065 		uint16_t rc;
28063737Shx147065 		ret = pcan_alloc_nicmem(pcan_p, PCAN_NICMEM_SZ, &rc);
28073737Shx147065 		if (ret) {
28083737Shx147065 			cmn_err(CE_WARN, "pcan alloc NIC Tx buf[%x]: failed "
28093737Shx147065 			    "%x\n", i, ret);
28103737Shx147065 			return (DDI_FAILURE);
28113737Shx147065 		}
28123737Shx147065 		ring_p->an_tx_fids[i] = rc;
28133737Shx147065 		ring_p->an_tx_ring[i] = 0;
28143737Shx147065 		PCANDBG((CE_NOTE, "pcan: NIC tx_id[%x]=%x\n", i, rc));
28153737Shx147065 	}
28163737Shx147065 	ring_p->an_tx_prod = ring_p->an_tx_cons = 0;
28173737Shx147065 	return (PCAN_SUCCESS);
28183737Shx147065 }
28193737Shx147065 
28203737Shx147065 
28213737Shx147065 
28223737Shx147065 static void
28233737Shx147065 pcan_start_locked(pcan_maci_t *pcan_p)
28243737Shx147065 {
28253737Shx147065 	pcan_p->pcan_flag |= PCAN_CARD_INTREN;
28263737Shx147065 	PCAN_ENABLE_INTR(pcan_p);
28273737Shx147065 }
28283737Shx147065 
28293737Shx147065 static void
28303737Shx147065 pcan_stop_locked(pcan_maci_t *pcan_p)
28313737Shx147065 {
28323737Shx147065 	PCAN_DISABLE_INTR_CLEAR(pcan_p);
28333737Shx147065 	pcan_p->pcan_flag &= ~PCAN_CARD_INTREN;
28343737Shx147065 }
28353737Shx147065 
28363737Shx147065 /*
28373737Shx147065  * for scan result
28383737Shx147065  */
28393737Shx147065 static int
28403737Shx147065 pcan_add_scan_item(pcan_maci_t *pcan_p, struct an_ltv_scanresult s)
28413737Shx147065 {
28423737Shx147065 	an_scan_list_t *scan_item;
28433737Shx147065 
28443737Shx147065 	scan_item = kmem_zalloc(sizeof (an_scan_list_t), KM_SLEEP);
28453737Shx147065 	if (scan_item == NULL) {
28463737Shx147065 		cmn_err(CE_WARN, "pcan add_scan_item: zalloc failed\n");
28473737Shx147065 		return (PCAN_FAIL);
28483737Shx147065 	}
28493737Shx147065 	scan_item->an_val = s;
28503737Shx147065 	scan_item->an_timeout = AN_SCAN_TIMEOUT_MAX;
28513737Shx147065 	list_insert_tail(&pcan_p->an_scan_list, scan_item);
28523737Shx147065 	pcan_p->an_scan_num++;
28533737Shx147065 	return (PCAN_SUCCESS);
28543737Shx147065 }
28553737Shx147065 
28563737Shx147065 static void
28573737Shx147065 pcan_delete_scan_item(pcan_maci_t *pcan_p, an_scan_list_t *s)
28583737Shx147065 {
28593737Shx147065 	list_remove(&pcan_p->an_scan_list, s);
28603737Shx147065 	kmem_free(s, sizeof (*s));
28613737Shx147065 	pcan_p->an_scan_num--;
28623737Shx147065 }
28633737Shx147065 
28643737Shx147065 static void
28653737Shx147065 pcan_scanlist_timeout(void *arg)
28663737Shx147065 {
28673737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
28683737Shx147065 	an_scan_list_t *scan_item0, *scan_item1;
28693737Shx147065 
28703737Shx147065 	mutex_enter(&pcan_p->pcan_scanlist_lock);
28713737Shx147065 	scan_item0 = list_head(&pcan_p->an_scan_list);
28723737Shx147065 	for (; scan_item0; ) {
28733737Shx147065 		PCANDBG((CE_NOTE, "pcan scanlist: ssid = %s\n",
28743737Shx147065 		    scan_item0->an_val.an_ssid));
28753737Shx147065 		PCANDBG((CE_NOTE, "pcan scanlist: timeout left: %ds",
28763737Shx147065 		    scan_item0->an_timeout));
28773737Shx147065 		scan_item1 = list_next(&pcan_p->an_scan_list, scan_item0);
28783737Shx147065 		if (scan_item0->an_timeout == 0) {
28793737Shx147065 			pcan_delete_scan_item(pcan_p, scan_item0);
28803737Shx147065 		} else {
28813737Shx147065 			scan_item0->an_timeout--;
28823737Shx147065 		}
28833737Shx147065 		scan_item0 = scan_item1;
28843737Shx147065 	}
28853737Shx147065 	mutex_exit(&pcan_p->pcan_scanlist_lock);
28863737Shx147065 	pcan_p->an_scanlist_timeout_id = timeout(pcan_scanlist_timeout,
28873737Shx147065 	    pcan_p, drv_usectohz(1000000));
28883737Shx147065 }
28893737Shx147065 
28903737Shx147065 
28913737Shx147065 /*
28923737Shx147065  * for wificonfig and dlamd ioctl
28933737Shx147065  */
28943737Shx147065 static int
28953737Shx147065 pcan_cfg_essid(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
28963737Shx147065 {
28973737Shx147065 	uint16_t i;
28983737Shx147065 	char *value;
28993737Shx147065 	wldp_t	*infp;
29003737Shx147065 	wldp_t *outfp;
29013737Shx147065 	char *buf;
29023737Shx147065 	int iret;
29033737Shx147065 	struct an_ltv_status *status_p;
29043737Shx147065 	struct an_ltv_ssidlist *ssidlist_p;
29053737Shx147065 
29063737Shx147065 	status_p = &pcan_p->an_status;
29073737Shx147065 	ssidlist_p = &pcan_p->an_ssidlist;
29083737Shx147065 
29093737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
29103737Shx147065 	if (buf == NULL) {
29113737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_essid: failed to alloc "
29123737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
29133737Shx147065 		return (ENOMEM);
29143737Shx147065 	}
29153737Shx147065 	outfp = (wldp_t *)buf;
29163737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
29173737Shx147065 	infp = (wldp_t *)mp->b_rptr;
29183737Shx147065 
29193737Shx147065 	if (cmd == WLAN_GET_PARAM) {
29203737Shx147065 		if (pcan_status_ltv(PCAN_READ_LTV, pcan_p, status_p)) {
29213737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
29223737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
29233737Shx147065 			goto done;
29243737Shx147065 		}
29253737Shx147065 
29263737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET +
29273737Shx147065 		    offsetof(wl_essid_t, wl_essid_essid) +
29283737Shx147065 		    status_p->an_ssidlen;
29293737Shx147065 		((wl_essid_t *)(outfp->wldp_buf))->wl_essid_length =
29303737Shx147065 		    status_p->an_ssidlen;
29313737Shx147065 		bcopy(status_p->an_ssid, buf + WIFI_BUF_OFFSET +
29323737Shx147065 		    offsetof(wl_essid_t, wl_essid_essid),
29333737Shx147065 		    status_p->an_ssidlen);
29343737Shx147065 		outfp->wldp_result = WL_SUCCESS;
29353737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
29363737Shx147065 		bzero(ssidlist_p, sizeof (*ssidlist_p));
29373737Shx147065 		value = ((wl_essid_t *)(infp->wldp_buf))->wl_essid_essid;
29383737Shx147065 		(void) strncpy(ssidlist_p->an_ssid1, value,
29393737Shx147065 		    MIN(32, strlen(value)));
29403737Shx147065 		ssidlist_p->an_ssid1_len = strlen(value);
29413737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
29423737Shx147065 		outfp->wldp_result = WL_SUCCESS;
29433737Shx147065 	} else {
29443737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
29453737Shx147065 		return (EINVAL);
29463737Shx147065 	}
29473737Shx147065 done:
29483737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++) {
29493737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
29503737Shx147065 	}
29513737Shx147065 	iret = (int)(outfp->wldp_result);
29523737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
29533737Shx147065 	return (iret);
29543737Shx147065 }
29553737Shx147065 
29563737Shx147065 static int
29573737Shx147065 pcan_cfg_bssid(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
29583737Shx147065 {
29593737Shx147065 	uint16_t i;
29603737Shx147065 	wldp_t *infp;
29613737Shx147065 	wldp_t *outfp;
29623737Shx147065 	char *buf;
29633737Shx147065 	wl_bssid_t *value;
29643737Shx147065 	int iret;
29653737Shx147065 	struct an_ltv_status *status_p;
29663737Shx147065 	struct an_ltv_aplist *aplist_p;
29673737Shx147065 
29683737Shx147065 	status_p = &pcan_p->an_status;
29693737Shx147065 	aplist_p = &pcan_p->an_aplist;
29703737Shx147065 
29713737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
29723737Shx147065 	if (buf == NULL) {
29733737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_bssid: failed to alloc "
29743737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
29753737Shx147065 		return (ENOMEM);
29763737Shx147065 	}
29773737Shx147065 	outfp = (wldp_t *)buf;
29783737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
29793737Shx147065 	infp = (wldp_t *)mp->b_rptr;
29803737Shx147065 
29813737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_bssid_t);
29823737Shx147065 
29833737Shx147065 	if (cmd == WLAN_GET_PARAM) {
29843737Shx147065 		if (pcan_status_ltv(PCAN_READ_LTV, pcan_p, status_p)) {
29853737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
29863737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
29873737Shx147065 			goto done;
29883737Shx147065 		}
29893737Shx147065 
29903737Shx147065 		bcopy(status_p->an_cur_bssid, buf + WIFI_BUF_OFFSET,
29913737Shx147065 		    sizeof (wl_bssid_t));
29923737Shx147065 		outfp->wldp_result = WL_SUCCESS;
29933737Shx147065 		PCANDBG((CE_CONT,
29943737Shx147065 		    "pcan: cfg_bssid: bssid=%x %x %x %x %x %x\n",
29953737Shx147065 		    status_p->an_cur_bssid[0],
29963737Shx147065 		    status_p->an_cur_bssid[1],
29973737Shx147065 		    status_p->an_cur_bssid[2],
29983737Shx147065 		    status_p->an_cur_bssid[3],
29993737Shx147065 		    status_p->an_cur_bssid[4],
30003737Shx147065 		    status_p->an_cur_bssid[5]));
30013737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
30023737Shx147065 		value = (wl_bssid_t *)(infp->wldp_buf);
30033737Shx147065 		(void) strncpy((char *)aplist_p->an_ap1, (char *)value, 6);
30043737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
30053737Shx147065 		outfp->wldp_result = WL_SUCCESS;
30063737Shx147065 	} else {
30073737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
30083737Shx147065 		return (EINVAL);
30093737Shx147065 	}
30103737Shx147065 done:
30113737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++) {
30123737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
30133737Shx147065 	}
30143737Shx147065 	iret = (int)(outfp->wldp_result);
30153737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
30163737Shx147065 	return (iret);
30173737Shx147065 }
30183737Shx147065 
30193737Shx147065 /*ARGSUSED*/
30203737Shx147065 static int
30213737Shx147065 pcan_cmd_scan(pcan_maci_t *pcan_p)
30223737Shx147065 {
30233737Shx147065 	uint16_t i = 0, j, ret = WL_SUCCESS;
30243737Shx147065 	uint8_t	bssid_t[6];
30253737Shx147065 	uint32_t check_num, enable;
30263737Shx147065 	an_scan_list_t *scan_item0;
30273737Shx147065 
30283737Shx147065 	enable = pcan_p->pcan_flag & PCAN_ENABLED;
30293737Shx147065 	if ((!enable) &&
30303737Shx147065 	    (ret = pcan_set_cmd(pcan_p, AN_CMD_ENABLE, 0))) {
30313737Shx147065 		ret = (int)WL_HW_ERROR;
30323737Shx147065 		goto exit;
30333737Shx147065 	}
30343737Shx147065 	if (ret = pcan_set_cmd(pcan_p, AN_CMD_SCAN, 0)) {
30353737Shx147065 		ret = (int)WL_HW_ERROR;
30363737Shx147065 		goto exit;
30373737Shx147065 	}
30383737Shx147065 
30393737Shx147065 	pcan_delay(pcan_p, 500000);
30403737Shx147065 	ret =  pcan_scanresult_ltv(PCAN_READ_LTV,
30413737Shx147065 	    pcan_p, AN_RID_ESSIDLIST_FIRST, &pcan_p->an_scanresult[i]);
30423737Shx147065 	if ((ret) || pcan_p->an_scanresult[i].an_index == 0xffff) {
30433737Shx147065 		goto done;
30443737Shx147065 	}
30453737Shx147065 	do
30463737Shx147065 	{
30473737Shx147065 		i++;
30483737Shx147065 		ret =  pcan_scanresult_ltv(PCAN_READ_LTV,
30493737Shx147065 		    pcan_p, AN_RID_ESSIDLIST_NEXT, &pcan_p->an_scanresult[i]);
30503737Shx147065 	} while ((!ret) && (i < 32) &&
30513737Shx147065 	    (pcan_p->an_scanresult[i].an_index != 0xffff));
30523737Shx147065 done:
30533737Shx147065 	if ((!enable) &&
30543737Shx147065 	    (ret = pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0))) {
30553737Shx147065 		ret = (int)WL_HW_ERROR;
30563737Shx147065 		goto exit;
30573737Shx147065 	}
30583737Shx147065 	/* record the scan result for future use */
30593737Shx147065 	bzero(bssid_t, sizeof (bssid_t));
30603737Shx147065 	for (j = 0; j < i; j++) {
30613737Shx147065 		/*
30623737Shx147065 		 * sometimes, those empty items are recorded by hardware,
30633737Shx147065 		 * this is wrong, just ignore those items here.
30643737Shx147065 		 */
30653737Shx147065 		if (bcmp(pcan_p->an_scanresult[j].an_bssid,
30663737Shx147065 		    bssid_t, 6) == 0) {
30673737Shx147065 			continue;
30683737Shx147065 		}
30693737Shx147065 		/*
30703737Shx147065 		 * save/update the scan item in scanlist
30713737Shx147065 		 */
30723737Shx147065 		mutex_enter(&pcan_p->pcan_scanlist_lock);
30733737Shx147065 		check_num = 0;
30743737Shx147065 		scan_item0 = list_head(&pcan_p->an_scan_list);
30753737Shx147065 		if (scan_item0 == NULL) {
30763737Shx147065 			if (pcan_add_scan_item(pcan_p,
30773737Shx147065 			    pcan_p->an_scanresult[j]) != 0) {
30783737Shx147065 				mutex_exit(&pcan_p->pcan_scanlist_lock);
30793737Shx147065 				return (WL_SUCCESS);
30803737Shx147065 			}
30813737Shx147065 		}
30823737Shx147065 		for (; scan_item0; ) {
30833737Shx147065 			if (bcmp(pcan_p->an_scanresult[j].an_bssid,
30843737Shx147065 			    scan_item0->an_val.an_bssid, 6) == 0) {
30853737Shx147065 				scan_item0->an_val = pcan_p->an_scanresult[j];
30863737Shx147065 				scan_item0->an_timeout = AN_SCAN_TIMEOUT_MAX;
30873737Shx147065 				break;
30883737Shx147065 			} else {
30893737Shx147065 				check_num++;
30903737Shx147065 			}
30913737Shx147065 			scan_item0 = list_next(&pcan_p->an_scan_list,
30923737Shx147065 			    scan_item0);
30933737Shx147065 		}
30943737Shx147065 		if (check_num == pcan_p->an_scan_num) {
30953737Shx147065 			if (pcan_add_scan_item(pcan_p,
30963737Shx147065 			    pcan_p->an_scanresult[j]) != 0) {
30973737Shx147065 				mutex_exit(&pcan_p->pcan_scanlist_lock);
30983737Shx147065 				return (WL_SUCCESS);
30993737Shx147065 			}
31003737Shx147065 		}
31013737Shx147065 		mutex_exit(&pcan_p->pcan_scanlist_lock);
31023737Shx147065 	}
31033737Shx147065 exit:
31043737Shx147065 	if (ret)
31054343Sgd78059 		cmn_err(CE_WARN, "pcan: scan failed due to hardware error");
31063737Shx147065 	return (ret);
31073737Shx147065 }
31083737Shx147065 
31093737Shx147065 /*ARGSUSED*/
31103737Shx147065 static int
31113737Shx147065 pcan_cfg_scan(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
31123737Shx147065 {
31133737Shx147065 	wl_ess_conf_t *p_ess_conf;
31143737Shx147065 	wldp_t *outfp;
31153737Shx147065 	char *buf;
31163737Shx147065 	uint16_t i;
31173737Shx147065 	an_scan_list_t *scan_item;
31183737Shx147065 
31193737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
31203737Shx147065 	if (buf == NULL) {
31213737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_scanlist: failed to alloc "
31223737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
31233737Shx147065 		return (ENOMEM);
31243737Shx147065 	}
31253737Shx147065 	outfp = (wldp_t *)buf;
31263737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
31273737Shx147065 
31283737Shx147065 	mutex_enter(&pcan_p->pcan_scanlist_lock);
31293737Shx147065 	((wl_ess_list_t *)(outfp->wldp_buf))->wl_ess_list_num =
31303737Shx147065 	    pcan_p->an_scan_num;
31313737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET +
31323737Shx147065 	    offsetof(wl_ess_list_t, wl_ess_list_ess) +
31333737Shx147065 	    pcan_p->an_scan_num * sizeof (wl_ess_conf_t);
31343737Shx147065 
31353737Shx147065 	scan_item = list_head(&pcan_p->an_scan_list);
31363737Shx147065 	for (i = 0; i < pcan_p->an_scan_num; i++) {
31373737Shx147065 		if (!scan_item)
31383737Shx147065 			goto done;
31393737Shx147065 
31403737Shx147065 		p_ess_conf = (wl_ess_conf_t *)(buf + WIFI_BUF_OFFSET +
31413737Shx147065 		    offsetof(wl_ess_list_t, wl_ess_list_ess) +
31423737Shx147065 		    i * sizeof (wl_ess_conf_t));
31433737Shx147065 		bcopy(scan_item->an_val.an_ssid,
31443737Shx147065 		    p_ess_conf->wl_ess_conf_essid.wl_essid_essid,
31453737Shx147065 		    mi_strlen(scan_item->an_val.an_ssid));
31463737Shx147065 		bcopy(scan_item->an_val.an_bssid,
31473737Shx147065 		    p_ess_conf->wl_ess_conf_bssid, 6);
31483737Shx147065 		(p_ess_conf->wl_phy_conf).wl_phy_dsss_conf.wl_dsss_subtype
31493737Shx147065 		    = WL_DSSS;
31503737Shx147065 		p_ess_conf->wl_ess_conf_wepenabled =
31513737Shx147065 		    (scan_item->an_val.an_cap & 0x10 ?
31523737Shx147065 		    WL_ENC_WEP : WL_NOENCRYPTION);
31533737Shx147065 		p_ess_conf->wl_ess_conf_bsstype =
31543737Shx147065 		    (scan_item->an_val.an_cap & 0x1 ?
31553737Shx147065 		    WL_BSS_BSS : WL_BSS_IBSS);
31563737Shx147065 		p_ess_conf->wl_phy_conf.wl_phy_dsss_conf.wl_dsss_channel =
31573737Shx147065 		    scan_item->an_val.an_dschannel;
31583737Shx147065 		p_ess_conf->wl_ess_conf_sl = 15 -
31593737Shx147065 		    ((scan_item->an_val.an_rssi & 0xff) * 15 / 128);
31603737Shx147065 		p_ess_conf->wl_supported_rates[0] = WL_RATE_1M;
31613737Shx147065 		p_ess_conf->wl_supported_rates[1] = WL_RATE_2M;
31623737Shx147065 		p_ess_conf->wl_supported_rates[2] = WL_RATE_5_5M;
31633737Shx147065 		p_ess_conf->wl_supported_rates[3] = WL_RATE_11M;
31643737Shx147065 		scan_item = list_next(&pcan_p->an_scan_list, scan_item);
31653737Shx147065 	}
31663737Shx147065 done:
31673737Shx147065 	mutex_exit(&pcan_p->pcan_scanlist_lock);
31683737Shx147065 	outfp->wldp_result = WL_SUCCESS;
31693737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
31703737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
31713737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
31723737Shx147065 	return (WL_SUCCESS);
31733737Shx147065 }
31743737Shx147065 
31753737Shx147065 /*ARGSUSED*/
31763737Shx147065 static int
31773737Shx147065 pcan_cfg_linkstatus(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
31783737Shx147065 {
31793737Shx147065 	wldp_t *outfp;
31803737Shx147065 	char *buf;
31813737Shx147065 	uint16_t i;
31823737Shx147065 
31833737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
31843737Shx147065 	if (buf == NULL) {
31853737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_linkstatus: failed to alloc "
31863737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
31873737Shx147065 		return (ENOMEM);
31883737Shx147065 	}
31893737Shx147065 	outfp = (wldp_t *)buf;
31903737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
31913737Shx147065 
31923737Shx147065 	if (pcan_p->pcan_flag & PCAN_CARD_LINKUP)
31933737Shx147065 		*(wl_linkstatus_t *)(outfp->wldp_buf) = WL_CONNECTED;
31943737Shx147065 	else
31953737Shx147065 		*(wl_linkstatus_t *)(outfp->wldp_buf) = WL_NOTCONNECTED;
31963737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_encryption_t);
31973737Shx147065 	outfp->wldp_result = WL_SUCCESS;
31983737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
31993737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
32003737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
32013737Shx147065 	return (WL_SUCCESS);
32023737Shx147065 }
32033737Shx147065 
32043737Shx147065 static int
32053737Shx147065 pcan_cfg_bsstype(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
32063737Shx147065 {
32073737Shx147065 	uint16_t i;
32083737Shx147065 	wldp_t	*infp;
32093737Shx147065 	wldp_t *outfp;
32103737Shx147065 	char *buf;
32113737Shx147065 	int iret;
32123737Shx147065 	struct an_ltv_genconfig *cfg_p;
32133737Shx147065 
32143737Shx147065 	cfg_p = &pcan_p->an_config;
32153737Shx147065 
32163737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
32173737Shx147065 	if (buf == NULL) {
32183737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_bsstype: failed to alloc "
32193737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
32203737Shx147065 		return (ENOMEM);
32213737Shx147065 	}
32223737Shx147065 	outfp = (wldp_t *)buf;
32233737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
32243737Shx147065 	infp = (wldp_t *)mp->b_rptr;
32253737Shx147065 
32263737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_bss_type_t);
32273737Shx147065 
32283737Shx147065 	if (cmd == WLAN_GET_PARAM) {
32293737Shx147065 		if (cfg_p->an_opmode == AN_OPMODE_INFR_STATION) {
32303737Shx147065 			*(wl_bss_type_t *)(outfp->wldp_buf) = WL_BSS_BSS;
32313737Shx147065 		} else if (cfg_p->an_opmode == AN_OPMODE_IBSS_ADHOC) {
32323737Shx147065 			*(wl_bss_type_t *)(outfp->wldp_buf) = WL_BSS_IBSS;
32333737Shx147065 		}
32343737Shx147065 		outfp->wldp_result = WL_SUCCESS;
32353737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
32363737Shx147065 		if (*(wl_bss_type_t *)(infp->wldp_buf) == WL_BSS_BSS)
32373737Shx147065 			cfg_p->an_opmode = AN_OPMODE_INFR_STATION;
32383737Shx147065 		if (*(wl_bss_type_t *)(infp->wldp_buf) == WL_BSS_IBSS)
32393737Shx147065 			cfg_p->an_opmode = AN_OPMODE_IBSS_ADHOC;
32403737Shx147065 		if (*(wl_bss_type_t *)(infp->wldp_buf) == WL_BSS_ANY)
32413737Shx147065 			cfg_p->an_opmode = AN_OPMODE_INFR_STATION;
32423737Shx147065 		cfg_p->an_assoc_timeout = 5000;
32433737Shx147065 		outfp->wldp_result = WL_SUCCESS;
32443737Shx147065 	} else {
32453737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
32463737Shx147065 		return (EINVAL);
32473737Shx147065 	}
32483737Shx147065 
32493737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
32503737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
32513737Shx147065 	iret = (int)(outfp->wldp_result);
32523737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
32533737Shx147065 	return (iret);
32543737Shx147065 }
32553737Shx147065 
32563737Shx147065 static int
32573737Shx147065 pcan_cfg_phy(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
32583737Shx147065 {
32593737Shx147065 	uint16_t ret, i;
32603737Shx147065 	wldp_t	*infp;
32613737Shx147065 	wldp_t *outfp;
32623737Shx147065 	char *buf;
32633737Shx147065 	int iret;
32643737Shx147065 	struct an_ltv_genconfig *cfg_p;
32653737Shx147065 	struct an_ltv_status *status_p;
32663737Shx147065 
32673737Shx147065 	cfg_p = &pcan_p->an_config;
32683737Shx147065 	status_p = &pcan_p->an_status;
32693737Shx147065 
32703737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
32713737Shx147065 	if (buf == NULL) {
32723737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_phy: failed to alloc "
32733737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
32743737Shx147065 		return (ENOMEM);
32753737Shx147065 	}
32763737Shx147065 	outfp = (wldp_t *)buf;
32773737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
32783737Shx147065 	infp = (wldp_t *)mp->b_rptr;
32793737Shx147065 
32803737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_dsss_t);
32813737Shx147065 	if (cmd == WLAN_GET_PARAM) {
32823737Shx147065 		if (ret = pcan_status_ltv(PCAN_READ_LTV, pcan_p, status_p)) {
32833737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
32843737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
32853737Shx147065 			goto done;
32863737Shx147065 		}
32873737Shx147065 		((wl_dsss_t *)(outfp->wldp_buf))->wl_dsss_channel =
32883737Shx147065 		    status_p->an_channel_set;
32893737Shx147065 		((wl_dsss_t *)(outfp->wldp_buf))->wl_dsss_subtype = WL_DSSS;
32903737Shx147065 		outfp->wldp_result = WL_SUCCESS;
32913737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
32923737Shx147065 		ret = (uint16_t)
32933737Shx147065 		    (((wl_phy_conf_t *)(infp->wldp_buf))
32943737Shx147065 		    ->wl_phy_dsss_conf.wl_dsss_channel);
32953737Shx147065 		if (ret < 1 || ret > 14) {
32963737Shx147065 			outfp->wldp_result = WL_NOTSUPPORTED;
32973737Shx147065 			goto done;
32983737Shx147065 		}
32993737Shx147065 		cfg_p->an_ds_channel = ret;
33003737Shx147065 		cfg_p->an_assoc_timeout = 5000;
33013737Shx147065 		outfp->wldp_result = WL_SUCCESS;
33023737Shx147065 	} else {
33033737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
33043737Shx147065 		return (EINVAL);
33053737Shx147065 	}
33063737Shx147065 done:
33073737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
33083737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
33093737Shx147065 	iret = (int)(outfp->wldp_result);
33103737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
33113737Shx147065 	return (iret);
33123737Shx147065 
33133737Shx147065 }
33143737Shx147065 
33153737Shx147065 /*ARGSUSED*/
33163737Shx147065 static int
33173737Shx147065 pcan_cfg_desiredrates(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
33183737Shx147065 {
33193737Shx147065 	uint16_t i;
33203737Shx147065 	uint8_t rates = 0;
33213737Shx147065 	wldp_t	*infp;
33223737Shx147065 	wldp_t *outfp;
33233737Shx147065 	char *buf;
33243737Shx147065 	int iret;
33253737Shx147065 	struct an_ltv_genconfig *cfg_p;
33263737Shx147065 	struct an_ltv_genconfig *actcfg_p;
33273737Shx147065 
33283737Shx147065 	cfg_p = &pcan_p->an_config;
33293737Shx147065 	actcfg_p = &pcan_p->an_actual_config;
33303737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
33313737Shx147065 	if (buf == NULL) {
33323737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_rates: failed to alloc "
33333737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
33343737Shx147065 		return (ENOMEM);
33353737Shx147065 	}
33363737Shx147065 	outfp = (wldp_t *)buf;
33373737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
33383737Shx147065 	infp = (wldp_t *)mp->b_rptr;
33393737Shx147065 
33403737Shx147065 	if (cmd == WLAN_GET_PARAM) {
33413737Shx147065 		if (pcan_cfg_ltv(PCAN_READ_LTV, pcan_p, actcfg_p)) {
33423737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
33433737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
33443737Shx147065 			goto done;
33453737Shx147065 		}
33463737Shx147065 		for (i = 0; i < sizeof (actcfg_p->an_rates); i++) {
33473737Shx147065 			if (actcfg_p->an_rates[i] == 0)
33483737Shx147065 				break;
33493737Shx147065 			rates = MAX(rates, actcfg_p->an_rates[i]);
33503737Shx147065 		}
33513737Shx147065 		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[0]
33523737Shx147065 		    = rates;
33533737Shx147065 		((wl_rates_t *)(outfp->wldp_buf))->wl_rates_num = 1;
33543737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET +
33553737Shx147065 		    offsetof(wl_rates_t, wl_rates_rates) + sizeof (char);
33563737Shx147065 		outfp->wldp_result = WL_SUCCESS;
33573737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
33583737Shx147065 		bzero(cfg_p->an_rates, sizeof (cfg_p->an_rates));
33593737Shx147065 		for (i = 0; i < ((wl_rates_t *)(infp->wldp_buf))->wl_rates_num;
33603737Shx147065 		    i++) {
33613737Shx147065 			cfg_p->an_rates[i] = (((wl_rates_t *)
33623737Shx147065 			    (infp->wldp_buf))->wl_rates_rates)[i];
33633737Shx147065 		}
33643737Shx147065 		cfg_p->an_assoc_timeout = 5000;
33653737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
33663737Shx147065 		outfp->wldp_result = WL_SUCCESS;
33673737Shx147065 	} else {
33683737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
33693737Shx147065 		return (EINVAL);
33703737Shx147065 	}
33713737Shx147065 done:
33723737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
33733737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
33743737Shx147065 	iret = (int)(outfp->wldp_result);
33753737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
33763737Shx147065 	return (iret);
33773737Shx147065 }
33783737Shx147065 
33793737Shx147065 /*ARGSUSED*/
33803737Shx147065 static int
33813737Shx147065 pcan_cfg_supportrates(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
33823737Shx147065 {
33833737Shx147065 	uint16_t i;
33843737Shx147065 	int iret;
33853737Shx147065 	wldp_t *outfp;
33863737Shx147065 	char *buf;
33873737Shx147065 
33883737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
33893737Shx147065 	if (buf == NULL) {
33903737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_supportedrates: failed to alloc "
33913737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
33923737Shx147065 		return (ENOMEM);
33933737Shx147065 	}
33943737Shx147065 	outfp = (wldp_t *)buf;
33953737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
33963737Shx147065 
33973737Shx147065 	if (cmd == WLAN_GET_PARAM) {
33983737Shx147065 		((wl_rates_t *)(outfp->wldp_buf))->wl_rates_num = 4;
33993737Shx147065 		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[0]
34003737Shx147065 		    = WL_RATE_1M;
34013737Shx147065 		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[1]
34023737Shx147065 		    = WL_RATE_2M;
34033737Shx147065 		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[2]
34043737Shx147065 		    = WL_RATE_5_5M;
34053737Shx147065 		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[3]
34063737Shx147065 		    = WL_RATE_11M;
34073737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET +
34083737Shx147065 		    offsetof(wl_rates_t, wl_rates_rates) +
34093737Shx147065 		    4 * sizeof (char);
34103737Shx147065 		outfp->wldp_result = WL_SUCCESS;
34113737Shx147065 	} else {
34123737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
34133737Shx147065 		return (EINVAL);
34143737Shx147065 	}
34153737Shx147065 done:
34163737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
34173737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
34183737Shx147065 	iret = (int)(outfp->wldp_result);
34193737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
34203737Shx147065 	return (iret);
34213737Shx147065 }
34223737Shx147065 
34233737Shx147065 /*ARGSUSED*/
34243737Shx147065 static int
34253737Shx147065 pcan_cfg_powermode(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
34263737Shx147065 {
34273737Shx147065 	uint16_t i;
34283737Shx147065 	wldp_t *outfp;
34293737Shx147065 	char *buf;
34303737Shx147065 	int iret;
34313737Shx147065 	struct an_ltv_genconfig *actcfg_p;
34323737Shx147065 
34333737Shx147065 	actcfg_p = &pcan_p->an_actual_config;
34343737Shx147065 
34353737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
34363737Shx147065 	if (buf == NULL) {
34373737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_powermode: failed to alloc "
34383737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
34393737Shx147065 		return (ENOMEM);
34403737Shx147065 	}
34413737Shx147065 	outfp = (wldp_t *)buf;
34423737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
34433737Shx147065 
34443737Shx147065 	if (cmd == WLAN_GET_PARAM) {
34453737Shx147065 		if (pcan_cfg_ltv(PCAN_READ_LTV, pcan_p, actcfg_p)) {
34463737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
34473737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
34483737Shx147065 			goto done;
34493737Shx147065 		}
34503737Shx147065 		((wl_ps_mode_t *)(outfp->wldp_buf))->wl_ps_mode =
34513737Shx147065 		    actcfg_p->an_psave_mode;
34523737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET +
34533737Shx147065 		    sizeof (wl_ps_mode_t);
34543737Shx147065 		outfp->wldp_result = WL_SUCCESS;
34553737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
34563737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
34573737Shx147065 		outfp->wldp_result = WL_LACK_FEATURE;
34583737Shx147065 	} else {
34593737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
34603737Shx147065 		return (EINVAL);
34613737Shx147065 	}
34623737Shx147065 done:
34633737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
34643737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
34653737Shx147065 	iret = (int)(outfp->wldp_result);
34663737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
34673737Shx147065 	return (iret);
34683737Shx147065 
34693737Shx147065 }
34703737Shx147065 
34713737Shx147065 static int
34723737Shx147065 pcan_cfg_authmode(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
34733737Shx147065 {
34743737Shx147065 	uint16_t i;
34753737Shx147065 	wldp_t *outfp;
34763737Shx147065 	char *buf;
34773737Shx147065 	int iret;
34783737Shx147065 	struct an_ltv_genconfig *cfg_p;
34793737Shx147065 	struct an_ltv_genconfig *actcfg_p;
34803737Shx147065 
34813737Shx147065 	cfg_p = &pcan_p->an_config;
34823737Shx147065 	actcfg_p = &pcan_p->an_actual_config;
34833737Shx147065 
34843737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
34853737Shx147065 	if (buf == NULL) {
34863737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_autymode: failed to alloc "
34873737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
34883737Shx147065 		return (ENOMEM);
34893737Shx147065 	}
34903737Shx147065 	outfp = (wldp_t *)buf;
34913737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
34923737Shx147065 
34933737Shx147065 	if (cmd == WLAN_GET_PARAM) {
34943737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_authmode_t);
34953737Shx147065 		if (cfg_p->an_authtype & AN_AUTHTYPE_SHAREDKEY) {
34963737Shx147065 			*(wl_bss_type_t *)(outfp->wldp_buf) = WL_SHAREDKEY;
34973737Shx147065 		} else {
34983737Shx147065 			*(wl_bss_type_t *)(outfp->wldp_buf) = WL_OPENSYSTEM;
34993737Shx147065 		}
35003737Shx147065 		outfp->wldp_result = WL_SUCCESS;
35013737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
35023737Shx147065 		if (*(wl_authmode_t *)(outfp->wldp_buf) == WL_OPENSYSTEM) {
35033737Shx147065 			cfg_p->an_authtype |= AN_AUTHTYPE_OPEN;
35043737Shx147065 			cfg_p->an_assoc_timeout = 5000;
35053737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
35063737Shx147065 			outfp->wldp_result = WL_SUCCESS;
35073737Shx147065 		} else {
35083737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
35093737Shx147065 			outfp->wldp_result = WL_LACK_FEATURE;
35103737Shx147065 		}
35113737Shx147065 	} else {
35123737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
35133737Shx147065 		return (EINVAL);
35143737Shx147065 	}
35153737Shx147065 	PCANDBG((CE_NOTE, "pcan cfg_authmode: actual.authmode=%x",
35163737Shx147065 	    actcfg_p->an_authtype));
35173737Shx147065 	PCANDBG((CE_NOTE, "pcan cfg_authmode: actual.home_product=%x",
35183737Shx147065 	    actcfg_p->an_rsvd6[2]));
35193737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
35203737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
35213737Shx147065 	iret = (int)(outfp->wldp_result);
35223737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
35233737Shx147065 	return (iret);
35243737Shx147065 }
35253737Shx147065 
35263737Shx147065 static int
35273737Shx147065 pcan_cfg_encryption(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
35283737Shx147065 {
35293737Shx147065 	uint16_t i;
35303737Shx147065 	wldp_t *outfp;
35313737Shx147065 	char *buf;
35323737Shx147065 	int iret;
35333737Shx147065 	struct an_ltv_genconfig *cfg_p;
35343737Shx147065 
35353737Shx147065 	cfg_p = &pcan_p->an_config;
35363737Shx147065 
35373737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
35383737Shx147065 	if (buf == NULL) {
35393737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_encryption: failed to alloc "
35403737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
35413737Shx147065 		return (ENOMEM);
35423737Shx147065 	}
35433737Shx147065 	outfp = (wldp_t *)buf;
35443737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
35453737Shx147065 
35463737Shx147065 	if (cmd == WLAN_GET_PARAM) {
35473737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_encryption_t);
35483737Shx147065 		if (cfg_p->an_authtype & AN_AUTHTYPE_ENABLEWEP) {
35493737Shx147065 			*(wl_bss_type_t *)(outfp->wldp_buf) = WL_ENC_WEP;
35503737Shx147065 		} else {
35513737Shx147065 			*(wl_bss_type_t *)(outfp->wldp_buf) = WL_NOENCRYPTION;
35523737Shx147065 		}
35533737Shx147065 		outfp->wldp_result = WL_SUCCESS;
35543737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
35553737Shx147065 		if (*(wl_encryption_t *)(outfp->wldp_buf) == WL_ENC_WEP) {
35563737Shx147065 			cfg_p->an_authtype |= (AN_AUTHTYPE_ENABLEWEP |
35573737Shx147065 			    AN_AUTHTYPE_ALLOW_UNENCRYPTED);
35583737Shx147065 			pcan_p->pcan_usewep = 1;
35593737Shx147065 		}
35603737Shx147065 		if (*(wl_authmode_t *)(outfp->wldp_buf) == WL_NOENCRYPTION) {
35613737Shx147065 			cfg_p->an_authtype &= (~(AN_AUTHTYPE_ENABLEWEP |
35623737Shx147065 			    AN_AUTHTYPE_ALLOW_UNENCRYPTED));
35633737Shx147065 			pcan_p->pcan_usewep = 0;
35643737Shx147065 		}
35653737Shx147065 		cfg_p->an_assoc_timeout = 5000;
35663737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
35673737Shx147065 		outfp->wldp_result = WL_SUCCESS;
35683737Shx147065 	} else {
35693737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
35703737Shx147065 		return (EINVAL);
35713737Shx147065 	}
35723737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
35733737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
35743737Shx147065 	iret = (int)(outfp->wldp_result);
35753737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
35763737Shx147065 	return (iret);
35773737Shx147065 }
35783737Shx147065 
35793737Shx147065 static int
35803737Shx147065 pcan_cfg_wepkeyid(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
35813737Shx147065 {
35823737Shx147065 	uint16_t i, ret;
35833737Shx147065 	wldp_t	*infp;
35843737Shx147065 	wldp_t *outfp;
35853737Shx147065 	char *buf;
35863737Shx147065 	int iret;
35873737Shx147065 	struct an_ltv_wepkey wepkey;
35883737Shx147065 
35893737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
35903737Shx147065 	if (buf == NULL) {
35913737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_wepkeyid: failed to alloc "
35923737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
35933737Shx147065 		return (ENOMEM);
35943737Shx147065 	}
35953737Shx147065 	outfp = (wldp_t *)buf;
35963737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
35973737Shx147065 	infp = (wldp_t *)mp->b_rptr;
35983737Shx147065 
35993737Shx147065 	if (cmd == WLAN_GET_PARAM) {
36003737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_wep_key_id_t);
36013737Shx147065 		outfp->wldp_result = WL_SUCCESS;
36023737Shx147065 		*(wl_wep_key_id_t *)(outfp->wldp_buf) = pcan_p->an_cur_wepkey;
36033737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
36043737Shx147065 		ret = (uint16_t)(*(wl_wep_key_id_t *)(infp->wldp_buf));
36053737Shx147065 		if (ret > 3) {
36063737Shx147065 			kmem_free(buf, MAX_BUF_LEN);
36073737Shx147065 			return (EINVAL);
36083737Shx147065 		}
36093737Shx147065 		wepkey.an_index = 0xffff;
36103737Shx147065 		wepkey.an_macaddr[0] = ret & 0xff;
36113737Shx147065 		pcan_p->an_cur_wepkey = ret;
36123737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
36133737Shx147065 		outfp->wldp_result = WL_SUCCESS;
36143737Shx147065 	} else {
36153737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
36163737Shx147065 		return (EINVAL);
36173737Shx147065 	}
36183737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
36193737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
36203737Shx147065 	iret = (int)(outfp->wldp_result);
36213737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
36223737Shx147065 	return (iret);
36233737Shx147065 }
36243737Shx147065 
36253737Shx147065 /*ARGSUSED*/
36263737Shx147065 static int
36273737Shx147065 pcan_cfg_createibss(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
36283737Shx147065 {
36293737Shx147065 	uint16_t i;
36303737Shx147065 	wldp_t *outfp;
36313737Shx147065 	char *buf;
36323737Shx147065 	int iret;
36333737Shx147065 
36343737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
36353737Shx147065 	if (buf == NULL) {
36363737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_createibss: failed to alloc "
36373737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
36383737Shx147065 		return (ENOMEM);
36393737Shx147065 	}
36403737Shx147065 	outfp = (wldp_t *)buf;
36413737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
36423737Shx147065 
36433737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_create_ibss_t);
36443737Shx147065 	outfp->wldp_result = WL_LACK_FEATURE;
36453737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
36463737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
36473737Shx147065 	iret = (int)(outfp->wldp_result);
36483737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
36493737Shx147065 	return (iret);
36503737Shx147065 }
36513737Shx147065 
36523737Shx147065 static int
36533737Shx147065 pcan_cfg_rssi(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
36543737Shx147065 {
36553737Shx147065 	uint16_t i, val;
36563737Shx147065 	int iret;
36573737Shx147065 	wldp_t *outfp;
36583737Shx147065 	char *buf;
36593737Shx147065 	struct an_ltv_status *status_p;
36603737Shx147065 	status_p = &pcan_p->an_status;
36613737Shx147065 
36623737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
36633737Shx147065 	if (buf == NULL) {
36643737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_rssi: failed to alloc "
36653737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
36663737Shx147065 		return (ENOMEM);
36673737Shx147065 	}
36683737Shx147065 	outfp = (wldp_t *)buf;
36693737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
36703737Shx147065 
36713737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_rssi_t);
36723737Shx147065 
36733737Shx147065 	if (cmd == WLAN_GET_PARAM) {
36743737Shx147065 		if (val = pcan_status_ltv(PCAN_READ_LTV, pcan_p, status_p)) {
36753737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
36763737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
36773737Shx147065 			goto done;
36783737Shx147065 		}
36793737Shx147065 		val = status_p->an_cur_signal_quality;
36803737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_rssi: sl=%x", val));
36813737Shx147065 		/*
36823737Shx147065 		 * we reflect the value to 1-15 as rssi
36833737Shx147065 		 */
36843737Shx147065 		*(wl_rssi_t *)(outfp->wldp_buf) = 15 -
36853737Shx147065 		    ((val & 0xff) * 15 / 128 + 1);
36863737Shx147065 		outfp->wldp_result = WL_SUCCESS;
36873737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
36883737Shx147065 		outfp->wldp_result = WL_READONLY;
36893737Shx147065 	} else {
36903737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
36913737Shx147065 		return (EINVAL);
36923737Shx147065 	}
36933737Shx147065 done:
36943737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
36953737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
36963737Shx147065 	iret = (int)(outfp->wldp_result);
36973737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
36983737Shx147065 	return (iret);
36993737Shx147065 }
37003737Shx147065 
37013737Shx147065 /*ARGSUSED*/
37023737Shx147065 static int
37033737Shx147065 pcan_cfg_radio(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
37043737Shx147065 {
37053737Shx147065 	uint16_t i;
37063737Shx147065 	int iret;
37073737Shx147065 	wldp_t *outfp;
37083737Shx147065 	char *buf;
37093737Shx147065 
37103737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
37113737Shx147065 	if (buf == NULL) {
37123737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_radio: failed to alloc "
37133737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
37143737Shx147065 		return (ENOMEM);
37153737Shx147065 	}
37163737Shx147065 	outfp = (wldp_t *)buf;
37173737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
37183737Shx147065 
37193737Shx147065 	if (cmd == WLAN_GET_PARAM) {
37203737Shx147065 		*(wl_radio_t *)(outfp->wldp_buf) = B_TRUE;
37213737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_radio_t);
37223737Shx147065 		outfp->wldp_result = WL_SUCCESS;
37233737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
37243737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
37253737Shx147065 		outfp->wldp_result = WL_LACK_FEATURE;
37263737Shx147065 	} else {
37273737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
37283737Shx147065 		return (EINVAL);
37293737Shx147065 	}
37303737Shx147065 
37313737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
37323737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
37333737Shx147065 	iret = (int)(outfp->wldp_result);
37343737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
37353737Shx147065 	return (iret);
37363737Shx147065 }
37373737Shx147065 
37383737Shx147065 static int
37393737Shx147065 pcan_cfg_wepkey(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
37403737Shx147065 {
37413737Shx147065 	uint16_t i;
37423737Shx147065 	wl_wep_key_t *p_wepkey_tab;
37433737Shx147065 	wldp_t *outfp;
37443737Shx147065 	char *buf;
37453737Shx147065 	int iret;
37463737Shx147065 	wldp_t	*infp;
37473737Shx147065 	struct an_ltv_wepkey *wepkey_p;
37483737Shx147065 
37493737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
37503737Shx147065 	if (buf == NULL) {
37513737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_wep: failed to alloc "
37523737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
37533737Shx147065 		return (ENOMEM);
37543737Shx147065 	}
37553737Shx147065 	outfp = (wldp_t *)buf;
37563737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
37573737Shx147065 	infp = (wldp_t *)mp->b_rptr;
37583737Shx147065 
37593737Shx147065 	if (cmd == WLAN_GET_PARAM) {
37603737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET +
37613737Shx147065 		    sizeof (wl_wep_key_tab_t);
37623737Shx147065 		outfp->wldp_result = WL_WRITEONLY;
37633737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
37643737Shx147065 		p_wepkey_tab = (wl_wep_key_t *)(infp->wldp_buf);
37653737Shx147065 		for (i = 0; i < MAX_NWEPKEYS; i++) {
37663737Shx147065 			if (p_wepkey_tab[i].wl_wep_operation == WL_ADD) {
37673737Shx147065 				wepkey_p = &pcan_p->an_wepkey[i];
37683737Shx147065 				bzero(wepkey_p, sizeof (*wepkey_p));
37693737Shx147065 				wepkey_p->an_keylen =
37704343Sgd78059 				    p_wepkey_tab[i].wl_wep_length;
37713737Shx147065 				bcopy(p_wepkey_tab[i].wl_wep_key,
37724343Sgd78059 				    wepkey_p->an_key,
37734343Sgd78059 				    p_wepkey_tab[i].wl_wep_length);
37743737Shx147065 				wepkey_p->an_index = i;
37753737Shx147065 				wepkey_p->an_macaddr[0] = 1;
37763737Shx147065 			}
37773737Shx147065 		}
37783737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
37793737Shx147065 		outfp->wldp_result = WL_SUCCESS;
37803737Shx147065 	} else {
37813737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
37823737Shx147065 		return (EINVAL);
37833737Shx147065 	}
37843737Shx147065 
37853737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
37863737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
37873737Shx147065 	iret = (int)(outfp->wldp_result);
37883737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
37893737Shx147065 	return (iret);
37903737Shx147065 }
37913737Shx147065 
37923737Shx147065 static void
37933737Shx147065 pcan_connect_timeout(void *arg)
37943737Shx147065 {
37953737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
37963737Shx147065 	uint16_t ret;
37973737Shx147065 
37983737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
37993737Shx147065 	if (ret = pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0))
38003737Shx147065 		goto done;
38013737Shx147065 	pcan_p->pcan_flag &= ~PCAN_CARD_LINKUP;
38023737Shx147065 	if (ret = pcan_config_mac(pcan_p))
38033737Shx147065 		goto done;
38043737Shx147065 	ret = pcan_set_cmd(pcan_p, AN_CMD_ENABLE, 0);
38053737Shx147065 done:
38063737Shx147065 	if (ret)
38074343Sgd78059 		cmn_err(CE_WARN, "pcan: connect failed due to hardware error");
38083737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
38093737Shx147065 	pcan_p->pcan_connect_timeout_id = 0;
38103737Shx147065 }
38113737Shx147065 
38123737Shx147065 static int
38133737Shx147065 pcan_getset(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
38143737Shx147065 {
38153737Shx147065 	int ret = WL_SUCCESS;
38163737Shx147065 	int connect = 0;
38173737Shx147065 
38183737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
38193737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
38203737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
38213737Shx147065 		return (PCAN_FAIL);
38223737Shx147065 	}
38233737Shx147065 
38243737Shx147065 	switch (((wldp_t *)mp->b_rptr)->wldp_id) {
38253737Shx147065 	case WL_ESSID:
38263737Shx147065 		ret = pcan_cfg_essid(mp, pcan_p, cmd);
38273737Shx147065 		connect = 1;
38283737Shx147065 		PCANDBG((CE_NOTE, "cfg_essid\n"));
38293737Shx147065 		break;
38303737Shx147065 	case WL_BSSID:
38313737Shx147065 		ret = pcan_cfg_bssid(mp, pcan_p, cmd);
38323737Shx147065 		connect = 1;
38333737Shx147065 		PCANDBG((CE_NOTE, "cfg_bssid\n"));
38343737Shx147065 		break;
38353737Shx147065 	case WL_ESS_LIST:
38363737Shx147065 		ret = pcan_cfg_scan(mp, pcan_p, cmd);
38373737Shx147065 		PCANDBG((CE_NOTE, "cfg_scan\n"));
38383737Shx147065 		break;
38393737Shx147065 	case WL_LINKSTATUS:
38403737Shx147065 		ret = pcan_cfg_linkstatus(mp, pcan_p, cmd);
38413737Shx147065 		PCANDBG((CE_NOTE, "cfg_linkstatus\n"));
38423737Shx147065 		break;
38433737Shx147065 	case WL_BSS_TYPE:
38443737Shx147065 		ret = pcan_cfg_bsstype(mp, pcan_p, cmd);
38453737Shx147065 		connect = 1;
38463737Shx147065 		PCANDBG((CE_NOTE, "cfg_bsstype\n"));
38473737Shx147065 		break;
38483737Shx147065 	case WL_PHY_CONFIG:
38493737Shx147065 		ret = pcan_cfg_phy(mp, pcan_p, cmd);
38503737Shx147065 		connect = 1;
38513737Shx147065 		PCANDBG((CE_NOTE, "cfg_phy\n"));
38523737Shx147065 		break;
38533737Shx147065 	case WL_DESIRED_RATES:
38543737Shx147065 		ret = pcan_cfg_desiredrates(mp, pcan_p, cmd);
38553737Shx147065 		connect = 1;
38563737Shx147065 		PCANDBG((CE_NOTE, "cfg_disred-rates\n"));
38573737Shx147065 		break;
38583737Shx147065 	case WL_SUPPORTED_RATES:
38593737Shx147065 		ret = pcan_cfg_supportrates(mp, pcan_p, cmd);
38603737Shx147065 		PCANDBG((CE_NOTE, "cfg_supported-rates\n"));
38613737Shx147065 		break;
38623737Shx147065 	case WL_POWER_MODE:
38633737Shx147065 		ret = pcan_cfg_powermode(mp, pcan_p, cmd);
38643737Shx147065 		PCANDBG((CE_NOTE, "cfg_powermode\n"));
38653737Shx147065 		break;
38663737Shx147065 	case WL_AUTH_MODE:
38673737Shx147065 		ret = pcan_cfg_authmode(mp, pcan_p, cmd);
38683737Shx147065 		connect = 1;
38693737Shx147065 		PCANDBG((CE_NOTE, "cfg_authmode\n"));
38703737Shx147065 		break;
38713737Shx147065 	case WL_ENCRYPTION:
38723737Shx147065 		ret = pcan_cfg_encryption(mp, pcan_p, cmd);
38733737Shx147065 		connect = 1;
38743737Shx147065 		PCANDBG((CE_NOTE, "cfg_encryption\n"));
38753737Shx147065 		break;
38763737Shx147065 	case WL_WEP_KEY_ID:
38773737Shx147065 		ret = pcan_cfg_wepkeyid(mp, pcan_p, cmd);
38783737Shx147065 		connect = 1;
38793737Shx147065 		PCANDBG((CE_NOTE, "cfg_wepkeyid\n"));
38803737Shx147065 		break;
38813737Shx147065 	case WL_CREATE_IBSS:
38823737Shx147065 		ret = pcan_cfg_createibss(mp, pcan_p, cmd);
38833737Shx147065 		connect = 1;
38843737Shx147065 		PCANDBG((CE_NOTE, "cfg_create-ibss\n"));
38853737Shx147065 		break;
38863737Shx147065 	case WL_RSSI:
38873737Shx147065 		ret = pcan_cfg_rssi(mp, pcan_p, cmd);
38883737Shx147065 		PCANDBG((CE_NOTE, "cfg_rssi\n"));
38893737Shx147065 		break;
38903737Shx147065 	case WL_RADIO:
38913737Shx147065 		ret = pcan_cfg_radio(mp, pcan_p, cmd);
38923737Shx147065 		PCANDBG((CE_NOTE, "cfg_radio\n"));
38933737Shx147065 		break;
38943737Shx147065 	case WL_WEP_KEY_TAB:
38953737Shx147065 		ret = pcan_cfg_wepkey(mp, pcan_p, cmd);
38963737Shx147065 		connect = 1;
38973737Shx147065 		PCANDBG((CE_NOTE, "cfg_wepkey\n"));
38983737Shx147065 		break;
38993737Shx147065 	case WL_SCAN:
39003737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
39013737Shx147065 		if (pcan_p->pcan_connect_timeout_id != 0) {
39023737Shx147065 			(void) untimeout(pcan_p->pcan_connect_timeout_id);
39033737Shx147065 			pcan_p->pcan_connect_timeout_id = 0;
39043737Shx147065 		}
39053737Shx147065 		mutex_enter(&pcan_p->pcan_glock);
39063737Shx147065 		ret = pcan_cmd_scan(pcan_p);
39073737Shx147065 		/*
39083737Shx147065 		 * a trick here.
39093737Shx147065 		 * since the scan doesn't return too many items due to hardware
39103737Shx147065 		 * reason, so the current scan result is an accumulation of
39113737Shx147065 		 * several scans. For the first time or after many of the items
39123737Shx147065 		 * aged, we scan again if too few items now in the scan table.
39133737Shx147065 		 */
39143737Shx147065 		if (pcan_p->an_scan_num < AN_SCAN_AGAIN_THRESHOLD)
39153737Shx147065 			ret = pcan_cmd_scan(pcan_p);
39163737Shx147065 		break;
39173737Shx147065 	case WL_LOAD_DEFAULTS:
39183737Shx147065 		if (ret = pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0)) {
39193737Shx147065 			ret = (int)WL_HW_ERROR;
39203737Shx147065 			break;
39213737Shx147065 		}
39223737Shx147065 		if (ret = pcan_loaddef(pcan_p)) {
39233737Shx147065 			ret = (int)WL_HW_ERROR;
39243737Shx147065 			break;
39253737Shx147065 		}
39263737Shx147065 		if (ret = pcan_set_cmd(pcan_p, AN_CMD_ENABLE, 0)) {
39273737Shx147065 			ret = (int)WL_HW_ERROR;
39283737Shx147065 			break;
39293737Shx147065 		}
39303737Shx147065 		PCANDBG((CE_NOTE, "loaddef\n"));
39313737Shx147065 		break;
39323737Shx147065 	case WL_DISASSOCIATE:
39333737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
39343737Shx147065 		if (pcan_p->pcan_connect_timeout_id != 0) {
39353737Shx147065 			(void) untimeout(pcan_p->pcan_connect_timeout_id);
39363737Shx147065 			pcan_p->pcan_connect_timeout_id = 0;
39373737Shx147065 		}
39383737Shx147065 		mutex_enter(&pcan_p->pcan_glock);
39393737Shx147065 		pcan_p->pcan_flag &= ~PCAN_CARD_LINKUP;
39403737Shx147065 		if (ret = pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0)) {
39413737Shx147065 			ret = (int)WL_HW_ERROR;
39423737Shx147065 			break;
39433737Shx147065 		}
39443737Shx147065 		if (ret = pcan_loaddef(pcan_p)) {
39453737Shx147065 			ret = (int)WL_HW_ERROR;
39463737Shx147065 			break;
39473737Shx147065 		}
39483737Shx147065 		PCANDBG((CE_NOTE, "disassociate\n"));
39493737Shx147065 		break;
39503737Shx147065 	case WL_REASSOCIATE:
39513737Shx147065 	case WL_ASSOCIAT:
39523737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
39533737Shx147065 		if (pcan_p->pcan_connect_timeout_id != 0) {
39543737Shx147065 			(void) untimeout(pcan_p->pcan_connect_timeout_id);
39553737Shx147065 			pcan_p->pcan_connect_timeout_id = 0;
39563737Shx147065 		}
39573737Shx147065 		mutex_enter(&pcan_p->pcan_glock);
39583737Shx147065 		if (ret = pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0)) {
39593737Shx147065 			ret = (int)WL_HW_ERROR;
39603737Shx147065 			break;
39613737Shx147065 		}
39623737Shx147065 		pcan_p->pcan_flag &= ~PCAN_CARD_LINKUP;
39633737Shx147065 		if (ret = pcan_config_mac(pcan_p)) {
39643737Shx147065 			ret = (int)WL_HW_ERROR;
39653737Shx147065 			break;
39663737Shx147065 		}
39673737Shx147065 		if (ret = pcan_set_cmd(pcan_p, AN_CMD_ENABLE, 0)) {
39683737Shx147065 			ret = (int)WL_HW_ERROR;
39693737Shx147065 			break;
39703737Shx147065 		}
39713737Shx147065 		PCANDBG((CE_NOTE, "associate"));
39723737Shx147065 		break;
39733737Shx147065 
39743737Shx147065 	default:
39753737Shx147065 		break;
39763737Shx147065 	}
39773737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
39783737Shx147065 	if ((cmd == WLAN_SET_PARAM) && (ret == WL_SUCCESS) && (connect)) {
39793737Shx147065 		pcan_p->pcan_flag &= ~PCAN_CARD_LINKUP;
39803737Shx147065 		(void) pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0);
39813737Shx147065 		if (pcan_p->pcan_connect_timeout_id != 0) {
39823737Shx147065 			(void) untimeout(pcan_p->pcan_connect_timeout_id);
39833737Shx147065 			pcan_p->pcan_connect_timeout_id = 0;
39843737Shx147065 		}
39853737Shx147065 		pcan_p->pcan_connect_timeout_id = timeout(pcan_connect_timeout,
39863737Shx147065 		    pcan_p, drv_usectohz(1000000));
39873737Shx147065 	}
39883737Shx147065 	return (ret);
39893737Shx147065 }
39903737Shx147065 
39913737Shx147065 static void
39923737Shx147065 pcan_wlan_ioctl(pcan_maci_t *pcan_p, queue_t *wq, mblk_t *mp, uint32_t cmd)
39933737Shx147065 {
39943737Shx147065 
39953737Shx147065 	struct	iocblk	*iocp = (struct iocblk *)mp->b_rptr;
39963737Shx147065 	uint32_t len, ret;
39973737Shx147065 	mblk_t	*mp1;
39983737Shx147065 
39993737Shx147065 	/* sanity check */
40003737Shx147065 	if (iocp->ioc_count == 0 || !(mp1 = mp->b_cont)) {
40013737Shx147065 		miocnak(wq, mp, 0, EINVAL);
40023737Shx147065 		return;
40033737Shx147065 	}
40043737Shx147065 
40053737Shx147065 	/* assuming single data block */
40063737Shx147065 	if (mp1->b_cont) {
40073737Shx147065 		freemsg(mp1->b_cont);
40083737Shx147065 		mp1->b_cont = NULL;
40093737Shx147065 	}
40103737Shx147065 
40113737Shx147065 	/* we will overwrite everything */
40123737Shx147065 	mp1->b_wptr = mp1->b_rptr;
40133737Shx147065 
40143737Shx147065 	ret = pcan_getset(mp1, pcan_p, cmd);
40153737Shx147065 	len = msgdsize(mp1);
40163737Shx147065 	miocack(wq, mp, len, ret);
40173737Shx147065 }
40183737Shx147065 
40193737Shx147065 static void
40203737Shx147065 pcan_ioctl(void *arg, queue_t *wq, mblk_t *mp)
40213737Shx147065 {
40223737Shx147065 	struct iocblk *iocp;
40233737Shx147065 	uint32_t cmd, ret;
40243737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
40253737Shx147065 	boolean_t need_privilege = B_TRUE;
40263737Shx147065 
40273737Shx147065 	iocp = (struct iocblk *)mp->b_rptr;
40283737Shx147065 	iocp->ioc_error = 0;
40293737Shx147065 	cmd = iocp->ioc_cmd;
40303737Shx147065 	switch (cmd) {
40313737Shx147065 	default:
40323737Shx147065 		miocnak(wq, mp, 0, EINVAL);
40333737Shx147065 		return;
40343737Shx147065 	case WLAN_GET_PARAM:
40353737Shx147065 		need_privilege = B_FALSE;
40363737Shx147065 		break;
40373737Shx147065 	case WLAN_SET_PARAM:
40383737Shx147065 	case WLAN_COMMAND:
40393737Shx147065 		break;
40403737Shx147065 	}
40413737Shx147065 
40427408SSebastien.Roy@Sun.COM 	if (need_privilege && (ret = secpolicy_dl_config(iocp->ioc_cr)) != 0)
40437408SSebastien.Roy@Sun.COM 		miocnak(wq, mp, 0, ret);
40447408SSebastien.Roy@Sun.COM 	else
40457408SSebastien.Roy@Sun.COM 		pcan_wlan_ioctl(pcan_p, wq, mp, cmd);
40463737Shx147065 }
4047