xref: /onnv-gate/usr/src/uts/common/io/pcan/pcan.c (revision 3737:ddc3d2cb268a)
1*3737Shx147065 /*
2*3737Shx147065  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3*3737Shx147065  * Use is subject to license terms.
4*3737Shx147065  */
5*3737Shx147065 
6*3737Shx147065 /*
7*3737Shx147065  * Copyright (c) 1997, 1998, 1999
8*3737Shx147065  *      Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
9*3737Shx147065  *
10*3737Shx147065  * Redistribution and use in source and binary forms, with or without
11*3737Shx147065  * modification, are permitted provided that the following conditions
12*3737Shx147065  * are met:
13*3737Shx147065  * 1. Redistributions of source code must retain the above copyright
14*3737Shx147065  *    notice, this list of conditions and the following disclaimer.
15*3737Shx147065  * 2. Redistributions in binary form must reproduce the above copyright
16*3737Shx147065  *    notice, this list of conditions and the following disclaimer in the
17*3737Shx147065  *    documentation and/or other materials provided with the distribution.
18*3737Shx147065  * 3. All advertising materials mentioning features or use of this software
19*3737Shx147065  *    must display the following acknowledgement:
20*3737Shx147065  *      This product includes software developed by Bill Paul.
21*3737Shx147065  * 4. Neither the name of the author nor the names of any co-contributors
22*3737Shx147065  *    may be used to endorse or promote products derived from this software
23*3737Shx147065  *    without specific prior written permission.
24*3737Shx147065  *
25*3737Shx147065  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
26*3737Shx147065  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27*3737Shx147065  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28*3737Shx147065  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
29*3737Shx147065  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30*3737Shx147065  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31*3737Shx147065  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32*3737Shx147065  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33*3737Shx147065  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34*3737Shx147065  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
35*3737Shx147065  * THE POSSIBILITY OF SUCH DAMAGE.
36*3737Shx147065  */
37*3737Shx147065 
38*3737Shx147065 #pragma ident	"%Z%%M%	%I%	%E% SMI"
39*3737Shx147065 
40*3737Shx147065 #include <sys/conf.h>
41*3737Shx147065 #include <sys/ddi.h>
42*3737Shx147065 #include <sys/sunddi.h>
43*3737Shx147065 #include <sys/dlpi.h>
44*3737Shx147065 #include <sys/ethernet.h>
45*3737Shx147065 #include <sys/strsun.h>
46*3737Shx147065 #include <sys/stat.h>
47*3737Shx147065 #include <sys/byteorder.h>
48*3737Shx147065 #include <sys/pccard.h>
49*3737Shx147065 #include <sys/pci.h>
50*3737Shx147065 #include <sys/policy.h>
51*3737Shx147065 #include <sys/mac.h>
52*3737Shx147065 #include <sys/stream.h>
53*3737Shx147065 #include <inet/common.h>
54*3737Shx147065 #include <inet/nd.h>
55*3737Shx147065 #include <inet/mi.h>
56*3737Shx147065 
57*3737Shx147065 #include "pcan.h"
58*3737Shx147065 #include <sys/mac_wifi.h>
59*3737Shx147065 #include <inet/wifi_ioctl.h>
60*3737Shx147065 
61*3737Shx147065 #ifdef	DEBUG
62*3737Shx147065 #define	PCAN_DBG_BASIC		0x1
63*3737Shx147065 #define	PCAN_DBG_INFO		0x2
64*3737Shx147065 #define	PCAN_DBG_SEND		0x4
65*3737Shx147065 #define	PCAN_DBG_RCV		0x8
66*3737Shx147065 #define	PCAN_DBG_LINKINFO	0x10
67*3737Shx147065 #define	PCAN_DBG_FW_VERSION	0x20
68*3737Shx147065 #define	PCAN_DBG_CMD		0x40
69*3737Shx147065 uint32_t pcan_debug = 0;
70*3737Shx147065 #define	PCANDBG(x) \
71*3737Shx147065 	if (pcan_debug & PCAN_DBG_BASIC) cmn_err x
72*3737Shx147065 #else
73*3737Shx147065 #define	PCANDBG(x)
74*3737Shx147065 #endif
75*3737Shx147065 
76*3737Shx147065 static ddi_device_acc_attr_t accattr = {
77*3737Shx147065 		DDI_DEVICE_ATTR_V0,
78*3737Shx147065 		DDI_STRUCTURE_LE_ACC,
79*3737Shx147065 		DDI_STRICTORDER_ACC,
80*3737Shx147065 };
81*3737Shx147065 
82*3737Shx147065 static ddi_dma_attr_t control_cmd_dma_attr = {
83*3737Shx147065 	DMA_ATTR_V0,		/* version of this structure */
84*3737Shx147065 	0,			/* lowest usable address */
85*3737Shx147065 	0xffffffffffffffffull,	/* highest usable address */
86*3737Shx147065 	0xffffffffull,		/* maximum DMAable byte count */
87*3737Shx147065 	4,			/* alignment in bytes */
88*3737Shx147065 	0xfff,			/* burst sizes (any) */
89*3737Shx147065 	1,			/* minimum transfer */
90*3737Shx147065 	0xffffull,		/* maximum transfer */
91*3737Shx147065 	0xffffffffffffffffull,	/* maximum segment length */
92*3737Shx147065 	1,			/* maximum number of segments */
93*3737Shx147065 	1,			/* granularity */
94*3737Shx147065 	0,			/* flags (reserved) */
95*3737Shx147065 };
96*3737Shx147065 
97*3737Shx147065 void *pcan_soft_state_p = NULL;
98*3737Shx147065 static int pcan_device_type;
99*3737Shx147065 
100*3737Shx147065 mac_callbacks_t pcan_m_callbacks = {
101*3737Shx147065 	MC_IOCTL,
102*3737Shx147065 	pcan_gstat,
103*3737Shx147065 	pcan_start,
104*3737Shx147065 	pcan_stop,
105*3737Shx147065 	pcan_prom,
106*3737Shx147065 	pcan_sdmulti,
107*3737Shx147065 	pcan_saddr,
108*3737Shx147065 	pcan_tx,
109*3737Shx147065 	NULL,
110*3737Shx147065 	pcan_ioctl
111*3737Shx147065 };
112*3737Shx147065 
113*3737Shx147065 static char *pcan_name_str = "pcan";
114*3737Shx147065 
115*3737Shx147065 DDI_DEFINE_STREAM_OPS(pcan_dev_ops, nulldev, pcan_probe, pcan_attach,
116*3737Shx147065 
117*3737Shx147065     pcan_detach, nodev, NULL, D_MP, NULL);
118*3737Shx147065 
119*3737Shx147065 extern struct mod_ops mod_driverops;
120*3737Shx147065 static struct modldrv modldrv = {
121*3737Shx147065 	&mod_driverops,
122*3737Shx147065 	"Cisco-Aironet 802.11b driver",
123*3737Shx147065 	&pcan_dev_ops
124*3737Shx147065 };
125*3737Shx147065 
126*3737Shx147065 static struct modlinkage modlinkage = {
127*3737Shx147065 	MODREV_1, (void *)&modldrv, NULL
128*3737Shx147065 	};
129*3737Shx147065 
130*3737Shx147065 int
131*3737Shx147065 _init(void)
132*3737Shx147065 {
133*3737Shx147065 	int stat;
134*3737Shx147065 
135*3737Shx147065 	/* Allocate soft state */
136*3737Shx147065 	if ((stat = ddi_soft_state_init(&pcan_soft_state_p,
137*3737Shx147065 	    sizeof (pcan_maci_t), 2)) != DDI_SUCCESS)
138*3737Shx147065 		return (stat);
139*3737Shx147065 
140*3737Shx147065 	mac_init_ops(&pcan_dev_ops, "pcan");
141*3737Shx147065 	stat = mod_install(&modlinkage);
142*3737Shx147065 	if (stat != 0) {
143*3737Shx147065 		mac_fini_ops(&pcan_dev_ops);
144*3737Shx147065 		ddi_soft_state_fini(&pcan_soft_state_p);
145*3737Shx147065 	}
146*3737Shx147065 
147*3737Shx147065 	return (stat);
148*3737Shx147065 }
149*3737Shx147065 
150*3737Shx147065 int
151*3737Shx147065 _fini(void)
152*3737Shx147065 {
153*3737Shx147065 	int stat;
154*3737Shx147065 
155*3737Shx147065 	stat = mod_remove(&modlinkage);
156*3737Shx147065 	if (stat != DDI_SUCCESS)
157*3737Shx147065 		return (stat);
158*3737Shx147065 	mac_fini_ops(&pcan_dev_ops);
159*3737Shx147065 	ddi_soft_state_fini(&pcan_soft_state_p);
160*3737Shx147065 	return (stat);
161*3737Shx147065 }
162*3737Shx147065 
163*3737Shx147065 int
164*3737Shx147065 _info(struct modinfo *modinfop)
165*3737Shx147065 {
166*3737Shx147065 	return (mod_info(&modlinkage, modinfop));
167*3737Shx147065 }
168*3737Shx147065 
169*3737Shx147065 static int
170*3737Shx147065 pcan_probe(dev_info_t *dip)
171*3737Shx147065 {
172*3737Shx147065 	int len, ret;
173*3737Shx147065 	char *buf;
174*3737Shx147065 	dev_info_t *pdip = ddi_get_parent(dip);
175*3737Shx147065 
176*3737Shx147065 	PCANDBG((CE_NOTE, "pcan probe: parent dip=0x%p-%s(%d)\n", (void *)pdip,
177*3737Shx147065 	    ddi_driver_name(pdip), ddi_get_instance(pdip)));
178*3737Shx147065 
179*3737Shx147065 	ret = ddi_getlongprop(DDI_DEV_T_ANY, pdip, 0, "device_type",
180*3737Shx147065 	    (caddr_t)&buf, &len);
181*3737Shx147065 	if (ret != DDI_SUCCESS)
182*3737Shx147065 		return (DDI_PROBE_FAILURE);
183*3737Shx147065 
184*3737Shx147065 	PCANDBG((CE_NOTE, "pcan probe: device_type %s\n", buf));
185*3737Shx147065 	if ((strcmp(buf, "pccard") == 0) || (strcmp(buf, "pcmcia") == 0)) {
186*3737Shx147065 		pcan_device_type = PCAN_DEVICE_PCCARD;
187*3737Shx147065 #ifdef DEBUG
188*3737Shx147065 		if (pcan_debug & PCAN_DBG_FW_VERSION) {
189*3737Shx147065 			cmn_err(CE_NOTE, "Cisco 802.11 pccard\n");
190*3737Shx147065 		}
191*3737Shx147065 #endif
192*3737Shx147065 		ret = DDI_PROBE_SUCCESS;
193*3737Shx147065 	} else if (strcmp(buf, "pci") == 0) {
194*3737Shx147065 		pcan_device_type = PCAN_DEVICE_PCI;
195*3737Shx147065 #ifdef DEBUG
196*3737Shx147065 		if (pcan_debug & PCAN_DBG_FW_VERSION) {
197*3737Shx147065 			cmn_err(CE_NOTE, "Cisco 802.11 minipci card\n");
198*3737Shx147065 		}
199*3737Shx147065 #endif
200*3737Shx147065 		ret = DDI_PROBE_SUCCESS;
201*3737Shx147065 	} else {
202*3737Shx147065 		cmn_err(CE_NOTE, "pcan probe: unsupported card\n");
203*3737Shx147065 		ret = DDI_PROBE_FAILURE;
204*3737Shx147065 	}
205*3737Shx147065 
206*3737Shx147065 	kmem_free(buf, len);
207*3737Shx147065 	return (ret);
208*3737Shx147065 }
209*3737Shx147065 
210*3737Shx147065 static int
211*3737Shx147065 pcan_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
212*3737Shx147065 {
213*3737Shx147065 	int ret;
214*3737Shx147065 	int instance;
215*3737Shx147065 	uint16_t stat;
216*3737Shx147065 	uint32_t err;
217*3737Shx147065 	pcan_maci_t *pcan_p;
218*3737Shx147065 	wifi_data_t	wd = { 0 };
219*3737Shx147065 	mac_register_t	*macp;
220*3737Shx147065 	modify_config_t cfgmod;
221*3737Shx147065 	char strbuf[256];
222*3737Shx147065 
223*3737Shx147065 	PCANDBG((CE_NOTE, "dip=0x%p cmd=%x\n", (void *)dip, cmd));
224*3737Shx147065 	if (cmd != DDI_ATTACH)
225*3737Shx147065 		goto attach_fail1;
226*3737Shx147065 
227*3737Shx147065 	/*
228*3737Shx147065 	 * Since this driver is porting from freebsd, so just like
229*3737Shx147065 	 * the original driver, the minipci card doesn't work on amd64
230*3737Shx147065 	 * machine.
231*3737Shx147065 	 * For sparc, since no pci card is available for the test, so this
232*3737Shx147065 	 * version doesn't support sparc. If there is card available and
233*3737Shx147065 	 * requirement, future version will try to support sparc.
234*3737Shx147065 	 * This driver works well for minipci card on 32bit x86
235*3737Shx147065 	 * machine, so keep the code to just support minipci card on 32bit
236*3737Shx147065 	 * mode.
237*3737Shx147065 	 */
238*3737Shx147065 #if defined(sparc) || defined(__sparc)
239*3737Shx147065 	if (pcan_device_type == PCAN_DEVICE_PCI) {
240*3737Shx147065 		cmn_err(CE_NOTE, "pcan attach: this driver does not support "
241*3737Shx147065 		    "PCI/MiniPCI card on Sparc\n");
242*3737Shx147065 		goto attach_fail1;
243*3737Shx147065 	}
244*3737Shx147065 #endif /* sparc */
245*3737Shx147065 #if defined(__amd64)
246*3737Shx147065 	if (pcan_device_type == PCAN_DEVICE_PCI) {
247*3737Shx147065 		cmn_err(CE_NOTE, "pcan attach: this driver does not support "
248*3737Shx147065 		    "PCI/MiniPCI card on amd64\n");
249*3737Shx147065 		goto attach_fail1;
250*3737Shx147065 	}
251*3737Shx147065 #endif /* amd64 */
252*3737Shx147065 
253*3737Shx147065 	/* Allocate soft state associated with this instance. */
254*3737Shx147065 	if (ddi_soft_state_zalloc(pcan_soft_state_p,
255*3737Shx147065 	    ddi_get_instance(dip)) != DDI_SUCCESS) {
256*3737Shx147065 		cmn_err(CE_CONT, "pcan attach: alloc softstate failed\n");
257*3737Shx147065 		goto attach_fail1;
258*3737Shx147065 	}
259*3737Shx147065 	pcan_p = (pcan_maci_t *)ddi_get_soft_state(pcan_soft_state_p,
260*3737Shx147065 	    ddi_get_instance(dip));
261*3737Shx147065 
262*3737Shx147065 	pcan_p->pcan_device_type = pcan_device_type;
263*3737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
264*3737Shx147065 		if (ddi_regs_map_setup(dip, 0,
265*3737Shx147065 		    (caddr_t *)&pcan_p->pcan_cfg_base, 0, 0,
266*3737Shx147065 		    &accattr, &pcan_p->pcan_cfg_handle) != DDI_SUCCESS)
267*3737Shx147065 			goto attach_fail2;
268*3737Shx147065 
269*3737Shx147065 		stat = ddi_get16(pcan_p->pcan_cfg_handle,
270*3737Shx147065 		    (uint16_t *)(pcan_p->pcan_cfg_base + PCI_CONF_COMM));
271*3737Shx147065 		stat |= (PCI_COMM_IO | PCI_COMM_MAE);
272*3737Shx147065 		ddi_put16(pcan_p->pcan_cfg_handle,
273*3737Shx147065 		    (uint16_t *)(pcan_p->pcan_cfg_base + PCI_CONF_COMM), stat);
274*3737Shx147065 
275*3737Shx147065 		ddi_regs_map_free(&pcan_p->pcan_cfg_handle);
276*3737Shx147065 		if (ddi_regs_map_setup(dip, 1, (caddr_t *)&pcan_p->pcan_bar0,
277*3737Shx147065 		    0, 0, &accattr, &pcan_p->pcan_handle0) != DDI_SUCCESS)
278*3737Shx147065 			goto attach_fail3;
279*3737Shx147065 		if (ddi_regs_map_setup(dip, 2, (caddr_t *)&pcan_p->pcan_bar1,
280*3737Shx147065 		    0, 0, &accattr, &pcan_p->pcan_handle1) != DDI_SUCCESS)
281*3737Shx147065 			goto attach_fail3;
282*3737Shx147065 		if (ddi_regs_map_setup(dip, 3, (caddr_t *)&pcan_p->pcan_bar2,
283*3737Shx147065 		    0, 0, &accattr, &pcan_p->pcan_handle2) != DDI_SUCCESS)
284*3737Shx147065 			goto attach_fail3;
285*3737Shx147065 	}
286*3737Shx147065 
287*3737Shx147065 	pcan_p->pcan_dip		= dip;
288*3737Shx147065 	pcan_p->pcan_flag		= 0;
289*3737Shx147065 	pcan_p->glds_nocarrier		= 0;
290*3737Shx147065 	pcan_p->glds_noxmtbuf		= 0;
291*3737Shx147065 	pcan_p->glds_norcvbuf		= 0;
292*3737Shx147065 	pcan_p->pcan_socket		= ddi_getprop(DDI_DEV_T_NONE, dip,
293*3737Shx147065 					DDI_PROP_DONTPASS, "socket", -1);
294*3737Shx147065 
295*3737Shx147065 	pcan_p->pcan_reschedule_need = B_FALSE;
296*3737Shx147065 	pcan_p->pcan_info_softint_pending = 0;
297*3737Shx147065 	pcan_p->pcan_reset_delay = ddi_getprop(DDI_DEV_T_ANY, dip,
298*3737Shx147065 	    DDI_PROP_DONTPASS, "reset-delay", 5000);
299*3737Shx147065 
300*3737Shx147065 	if (ddi_get_iblock_cookie(dip,
301*3737Shx147065 	    0, &pcan_p->pcan_ib_cookie) != DDI_SUCCESS) {
302*3737Shx147065 		cmn_err(CE_WARN, "pcan attach: get_iblk_cookie failed\n");
303*3737Shx147065 		goto attach_fail3;
304*3737Shx147065 	}
305*3737Shx147065 
306*3737Shx147065 	mutex_init(&pcan_p->pcan_glock, NULL,
307*3737Shx147065 	    MUTEX_DRIVER, pcan_p->pcan_ib_cookie);
308*3737Shx147065 	mutex_init(&pcan_p->pcan_scanlist_lock, NULL,
309*3737Shx147065 	    MUTEX_DRIVER, pcan_p->pcan_ib_cookie);
310*3737Shx147065 	mutex_init(&pcan_p->pcan_txring.an_tx_lock, NULL,
311*3737Shx147065 	    MUTEX_DRIVER, pcan_p->pcan_ib_cookie);
312*3737Shx147065 
313*3737Shx147065 	if (ret = ddi_add_softintr(dip, DDI_SOFTINT_LOW,
314*3737Shx147065 	    &pcan_p->pcan_info_softint_id, &pcan_p->pcan_ib_cookie, NULL,
315*3737Shx147065 	    pcan_info_softint, (caddr_t)pcan_p)) {
316*3737Shx147065 		cmn_err(CE_WARN, "pcan attach: add info_softintr failed\n");
317*3737Shx147065 		goto attach_fail3a;
318*3737Shx147065 	}
319*3737Shx147065 
320*3737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
321*3737Shx147065 		if (ret = ddi_add_intr(dip, 0, NULL, NULL,
322*3737Shx147065 		    pcan_intr, (caddr_t)pcan_p)) {
323*3737Shx147065 			cmn_err(CE_WARN, "pcan attach: add intr failed\n");
324*3737Shx147065 			goto attach_fail4;
325*3737Shx147065 		}
326*3737Shx147065 	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
327*3737Shx147065 		if (ret = pcan_register_cs(dip, pcan_p)) {
328*3737Shx147065 			PCANDBG((CE_NOTE, "pcan attach: register_cs failed"
329*3737Shx147065 			    " %x\n", ret));
330*3737Shx147065 			goto attach_fail4;
331*3737Shx147065 		}
332*3737Shx147065 	} else {
333*3737Shx147065 		cmn_err(CE_WARN, "pcan attach: unsupported device type\n");
334*3737Shx147065 		goto attach_fail4;
335*3737Shx147065 	}
336*3737Shx147065 
337*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
338*3737Shx147065 	pcan_reset_backend(pcan_p, pcan_p->pcan_reset_delay);
339*3737Shx147065 	/* leaves IF down, intr disabled */
340*3737Shx147065 
341*3737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
342*3737Shx147065 		if (ret = pcan_init_dma(dip, pcan_p)) {
343*3737Shx147065 			cmn_err(CE_WARN, "pcan init_dma: failed\n");
344*3737Shx147065 			mutex_exit(&pcan_p->pcan_glock);
345*3737Shx147065 			goto attach_fail5;
346*3737Shx147065 		}
347*3737Shx147065 	}
348*3737Shx147065 	if (ret = pcan_get_cap(pcan_p)) { /* sets macaddr for gld_register */
349*3737Shx147065 		cmn_err(CE_WARN, "pcan attach: get_cap failed %x\n", ret);
350*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
351*3737Shx147065 		goto attach_fail6;
352*3737Shx147065 	}
353*3737Shx147065 
354*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
355*3737Shx147065 	/*
356*3737Shx147065 	 * Provide initial settings for the WiFi plugin; whenever this
357*3737Shx147065 	 * information changes, we need to call mac_pdata_update()
358*3737Shx147065 	 */
359*3737Shx147065 	wd.wd_secalloc = WIFI_SEC_NONE;
360*3737Shx147065 	wd.wd_opmode = IEEE80211_M_STA;
361*3737Shx147065 
362*3737Shx147065 	macp = mac_alloc(MAC_VERSION);
363*3737Shx147065 	if (macp == NULL) {
364*3737Shx147065 		PCANDBG((CE_NOTE, "pcan attach: "
365*3737Shx147065 		    "MAC version mismatch\n"));
366*3737Shx147065 		goto attach_fail6;
367*3737Shx147065 	}
368*3737Shx147065 
369*3737Shx147065 	macp->m_type_ident	= MAC_PLUGIN_IDENT_WIFI;
370*3737Shx147065 	macp->m_driver		= pcan_p;
371*3737Shx147065 	macp->m_dip		= dip;
372*3737Shx147065 	macp->m_src_addr	= pcan_p->pcan_mac_addr;
373*3737Shx147065 	macp->m_callbacks	= &pcan_m_callbacks;
374*3737Shx147065 	macp->m_min_sdu		= 0;
375*3737Shx147065 	macp->m_max_sdu		= IEEE80211_MTU;
376*3737Shx147065 	macp->m_pdata		= &wd;
377*3737Shx147065 	macp->m_pdata_size	= sizeof (wd);
378*3737Shx147065 
379*3737Shx147065 	err = mac_register(macp, &pcan_p->pcan_mh);
380*3737Shx147065 	mac_free(macp);
381*3737Shx147065 	if (err != 0) {
382*3737Shx147065 		PCANDBG((CE_NOTE, "pcan attach: "
383*3737Shx147065 		    "mac_register err\n"));
384*3737Shx147065 		goto attach_fail6;
385*3737Shx147065 	}
386*3737Shx147065 
387*3737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
388*3737Shx147065 		/* turn on CS interrupt */
389*3737Shx147065 		cfgmod.Attributes = CONF_ENABLE_IRQ_STEERING |
390*3737Shx147065 		    CONF_IRQ_CHANGE_VALID;
391*3737Shx147065 		cfgmod.Vpp1 = 50;
392*3737Shx147065 		cfgmod.Vpp2 = 50;
393*3737Shx147065 		(void) csx_ModifyConfiguration(pcan_p->pcan_chdl, &cfgmod);
394*3737Shx147065 
395*3737Shx147065 		mutex_enter(&pcan_p->pcan_glock);
396*3737Shx147065 		if (ret = pcan_init_nicmem(pcan_p)) {
397*3737Shx147065 			cmn_err(CE_WARN, "pcan attach: init_nicmem failed %x\n",
398*3737Shx147065 			    ret);
399*3737Shx147065 			mutex_exit(&pcan_p->pcan_glock);
400*3737Shx147065 			goto attach_fail7;
401*3737Shx147065 		}
402*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
403*3737Shx147065 	}
404*3737Shx147065 	(void) ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
405*3737Shx147065 	    "bad-rids", (caddr_t)&pcan_p->pcan_badrids,
406*3737Shx147065 	    &pcan_p->pcan_badrids_len);
407*3737Shx147065 
408*3737Shx147065 	pcan_p->an_config.an_rxmode = AN_NORMAL_RXMODE;
409*3737Shx147065 	ether_copy(pcan_p->pcan_mac_addr, pcan_p->an_config.an_macaddr);
410*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
411*3737Shx147065 	list_create(&pcan_p->an_scan_list, sizeof (an_scan_list_t),
412*3737Shx147065 	    offsetof(an_scan_list_t, an_scan_node));
413*3737Shx147065 	pcan_p->an_scan_num = 0;
414*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
415*3737Shx147065 	pcan_p->an_scanlist_timeout_id = timeout(pcan_scanlist_timeout,
416*3737Shx147065 	    pcan_p, drv_usectohz(1000000));
417*3737Shx147065 
418*3737Shx147065 	instance = ddi_get_instance(dip);
419*3737Shx147065 	(void) snprintf(strbuf, sizeof (strbuf), "pcan%d", instance);
420*3737Shx147065 	if (ddi_create_minor_node(dip, strbuf, S_IFCHR,
421*3737Shx147065 	    instance + 1, DDI_NT_NET_WIFI, 0) != DDI_SUCCESS) {
422*3737Shx147065 		goto attach_fail8;
423*3737Shx147065 	}
424*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
425*3737Shx147065 	PCAN_DISABLE_INTR_CLEAR(pcan_p);
426*3737Shx147065 	(void) pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0);
427*3737Shx147065 	pcan_p->pcan_flag |= PCAN_ATTACHED;
428*3737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
429*3737Shx147065 		pcan_p->pcan_flag |= PCAN_CARD_READY;
430*3737Shx147065 	}
431*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
432*3737Shx147065 	return (DDI_SUCCESS);
433*3737Shx147065 attach_fail8:
434*3737Shx147065 	if (pcan_p->an_scanlist_timeout_id != 0) {
435*3737Shx147065 		(void) untimeout(pcan_p->an_scanlist_timeout_id);
436*3737Shx147065 		pcan_p->an_scanlist_timeout_id = 0;
437*3737Shx147065 	}
438*3737Shx147065 	list_destroy(&pcan_p->an_scan_list);
439*3737Shx147065 attach_fail7:
440*3737Shx147065 	(void) mac_unregister(pcan_p->pcan_mh);
441*3737Shx147065 attach_fail6:
442*3737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI)
443*3737Shx147065 		pcan_free_dma(pcan_p);
444*3737Shx147065 attach_fail5:
445*3737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
446*3737Shx147065 		ddi_remove_intr(dip, 0, pcan_p->pcan_ib_cookie);
447*3737Shx147065 	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
448*3737Shx147065 		pcan_unregister_cs(pcan_p);
449*3737Shx147065 	}
450*3737Shx147065 attach_fail4:
451*3737Shx147065 	if (pcan_p->pcan_info_softint_id)
452*3737Shx147065 		ddi_remove_softintr(pcan_p->pcan_info_softint_id);
453*3737Shx147065 attach_fail3a:
454*3737Shx147065 	pcan_destroy_locks(pcan_p);
455*3737Shx147065 attach_fail3:
456*3737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
457*3737Shx147065 		if (pcan_p->pcan_handle0)
458*3737Shx147065 			ddi_regs_map_free(&pcan_p->pcan_handle0);
459*3737Shx147065 		if (pcan_p->pcan_handle1)
460*3737Shx147065 			ddi_regs_map_free(&pcan_p->pcan_handle1);
461*3737Shx147065 		if (pcan_p->pcan_handle2)
462*3737Shx147065 			ddi_regs_map_free(&pcan_p->pcan_handle2);
463*3737Shx147065 	}
464*3737Shx147065 attach_fail2:
465*3737Shx147065 	ddi_soft_state_free(pcan_soft_state_p, ddi_get_instance(dip));
466*3737Shx147065 attach_fail1:
467*3737Shx147065 	return (DDI_FAILURE);
468*3737Shx147065 }
469*3737Shx147065 
470*3737Shx147065 static int
471*3737Shx147065 pcan_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
472*3737Shx147065 {
473*3737Shx147065 	pcan_maci_t *pcan_p;
474*3737Shx147065 	an_scan_list_t *scan_item0;
475*3737Shx147065 	int ret;
476*3737Shx147065 	pcan_p = ddi_get_soft_state(pcan_soft_state_p, ddi_get_instance(dip));
477*3737Shx147065 
478*3737Shx147065 	if (cmd != DDI_DETACH)
479*3737Shx147065 		return (DDI_FAILURE);
480*3737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_ATTACHED))
481*3737Shx147065 		return (DDI_FAILURE);
482*3737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
483*3737Shx147065 		mutex_enter(&pcan_p->pcan_glock);
484*3737Shx147065 		pcan_stop_locked(pcan_p);
485*3737Shx147065 		PCAN_DISABLE_INTR(pcan_p);
486*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
487*3737Shx147065 	}
488*3737Shx147065 	if (pcan_p->an_scanlist_timeout_id != 0) {
489*3737Shx147065 		(void) untimeout(pcan_p->an_scanlist_timeout_id);
490*3737Shx147065 		pcan_p->an_scanlist_timeout_id = 0;
491*3737Shx147065 	}
492*3737Shx147065 	if (pcan_p->pcan_connect_timeout_id != 0) {
493*3737Shx147065 		(void) untimeout(pcan_p->pcan_connect_timeout_id);
494*3737Shx147065 		pcan_p->pcan_connect_timeout_id = 0;
495*3737Shx147065 	}
496*3737Shx147065 	mutex_enter(&pcan_p->pcan_scanlist_lock);
497*3737Shx147065 	scan_item0 = list_head(&pcan_p->an_scan_list);
498*3737Shx147065 	while (scan_item0) {
499*3737Shx147065 		pcan_delete_scan_item(pcan_p, scan_item0);
500*3737Shx147065 		scan_item0 = list_head(&pcan_p->an_scan_list);
501*3737Shx147065 	}
502*3737Shx147065 	list_destroy(&pcan_p->an_scan_list);
503*3737Shx147065 	mutex_exit(&pcan_p->pcan_scanlist_lock);
504*3737Shx147065 
505*3737Shx147065 	ret = mac_unregister(pcan_p->pcan_mh);
506*3737Shx147065 	if (ret != 0)
507*3737Shx147065 		return (DDI_FAILURE);
508*3737Shx147065 
509*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
510*3737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
511*3737Shx147065 		ddi_remove_intr(dip, 0, pcan_p->pcan_ib_cookie);
512*3737Shx147065 		pcan_free_dma(pcan_p);
513*3737Shx147065 		if (pcan_p->pcan_handle0)
514*3737Shx147065 			ddi_regs_map_free(&pcan_p->pcan_handle0);
515*3737Shx147065 		if (pcan_p->pcan_handle1)
516*3737Shx147065 			ddi_regs_map_free(&pcan_p->pcan_handle1);
517*3737Shx147065 		if (pcan_p->pcan_handle2)
518*3737Shx147065 			ddi_regs_map_free(&pcan_p->pcan_handle2);
519*3737Shx147065 	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
520*3737Shx147065 		pcan_unregister_cs(pcan_p);
521*3737Shx147065 	} else {
522*3737Shx147065 		cmn_err(CE_WARN, "pcan detach: unsupported device type\n");
523*3737Shx147065 	}
524*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
525*3737Shx147065 	pcan_destroy_locks(pcan_p);
526*3737Shx147065 	if (pcan_p->pcan_info_softint_id)
527*3737Shx147065 		ddi_remove_softintr(pcan_p->pcan_info_softint_id);
528*3737Shx147065 
529*3737Shx147065 	if (pcan_p->pcan_badrids_len)
530*3737Shx147065 		kmem_free(pcan_p->pcan_badrids, pcan_p->pcan_badrids_len);
531*3737Shx147065 
532*3737Shx147065 	ddi_soft_state_free(pcan_soft_state_p, ddi_get_instance(dip));
533*3737Shx147065 	ddi_remove_minor_node(dip, NULL);
534*3737Shx147065 
535*3737Shx147065 	return (DDI_SUCCESS);
536*3737Shx147065 }
537*3737Shx147065 
538*3737Shx147065 /*
539*3737Shx147065  * card services and event handlers
540*3737Shx147065  */
541*3737Shx147065 
542*3737Shx147065 static int
543*3737Shx147065 pcan_register_cs(dev_info_t *dip, pcan_maci_t *pcan_p)
544*3737Shx147065 {
545*3737Shx147065 	int ret;
546*3737Shx147065 	client_reg_t cr;
547*3737Shx147065 	client_handle_t chdl; /* uint encoding of socket, function, client */
548*3737Shx147065 	get_status_t card_status;
549*3737Shx147065 	request_socket_mask_t sock_req;
550*3737Shx147065 
551*3737Shx147065 	bzero(&cr, sizeof (cr));
552*3737Shx147065 	cr.Attributes	= INFO_IO_CLIENT|INFO_CARD_EXCL|INFO_CARD_SHARE;
553*3737Shx147065 	cr.EventMask	= CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
554*3737Shx147065 	    CS_EVENT_REGISTRATION_COMPLETE | CS_EVENT_CARD_REMOVAL_LOWP |
555*3737Shx147065 	    CS_EVENT_CARD_READY | CS_EVENT_PM_RESUME | CS_EVENT_PM_SUSPEND |
556*3737Shx147065 	    CS_EVENT_CLIENT_INFO;
557*3737Shx147065 	cr.event_callback_args.client_data = pcan_p;
558*3737Shx147065 	cr.Version = CS_VERSION;
559*3737Shx147065 	cr.event_handler = (csfunction_t *)pcan_ev_hdlr;
560*3737Shx147065 	cr.dip = dip;
561*3737Shx147065 	(void) strcpy(cr.driver_name, pcan_name_str);
562*3737Shx147065 	if (ret = csx_RegisterClient(&chdl, &cr)) {
563*3737Shx147065 		cmn_err(CE_WARN, "pcan: RegisterClient failed %x", ret);
564*3737Shx147065 		goto regcs_ret;
565*3737Shx147065 	}
566*3737Shx147065 
567*3737Shx147065 	pcan_p->pcan_chdl = chdl;
568*3737Shx147065 
569*3737Shx147065 	bzero(&card_status, sizeof (card_status));
570*3737Shx147065 	(void) csx_GetStatus(chdl, &card_status);
571*3737Shx147065 	PCANDBG((CE_NOTE, "pcan: getstat Sock=%x CState=%x SState=%x rState=%x",
572*3737Shx147065 	    card_status.Socket, card_status.CardState,
573*3737Shx147065 	    card_status.SocketState, card_status.raw_CardState));
574*3737Shx147065 	if (!(card_status.CardState & CS_STATUS_CARD_INSERTED)) {
575*3737Shx147065 		/* card is not present, why are we attaching ? */
576*3737Shx147065 		ret = CS_NO_CARD;
577*3737Shx147065 		goto unreg;
578*3737Shx147065 	}
579*3737Shx147065 	cv_init(&pcan_p->pcan_cscv, NULL, CV_DRIVER, NULL);
580*3737Shx147065 	mutex_init(&pcan_p->pcan_cslock, NULL, MUTEX_DRIVER, *cr.iblk_cookie);
581*3737Shx147065 	mutex_enter(&pcan_p->pcan_cslock);
582*3737Shx147065 	if (ret = csx_MapLogSocket(chdl, &pcan_p->pcan_log_sock)) {
583*3737Shx147065 		cmn_err(CE_WARN, "pcan: MapLogSocket failed %x", ret);
584*3737Shx147065 		goto fail;
585*3737Shx147065 	}
586*3737Shx147065 	PCANDBG((CE_NOTE, "pcan: logsock: LogSock=%x PhyAdapter=%x PhySock=%x",
587*3737Shx147065 	    pcan_p->pcan_log_sock.LogSocket,
588*3737Shx147065 	    pcan_p->pcan_log_sock.PhyAdapter,
589*3737Shx147065 	    pcan_p->pcan_log_sock.PhySocket));
590*3737Shx147065 
591*3737Shx147065 	/* turn on initialization events */
592*3737Shx147065 	sock_req.Socket = 0;
593*3737Shx147065 	sock_req.EventMask = CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
594*3737Shx147065 	    CS_EVENT_REGISTRATION_COMPLETE;
595*3737Shx147065 	if (ret = csx_RequestSocketMask(chdl, &sock_req)) {
596*3737Shx147065 		cmn_err(CE_WARN, "pcan: RequestSocketMask failed %x\n", ret);
597*3737Shx147065 		goto fail;
598*3737Shx147065 	}
599*3737Shx147065 
600*3737Shx147065 	/* wait for and process card insertion events */
601*3737Shx147065 	while (!(pcan_p->pcan_flag & PCAN_CARD_READY))
602*3737Shx147065 		cv_wait(&pcan_p->pcan_cscv, &pcan_p->pcan_cslock);
603*3737Shx147065 	mutex_exit(&pcan_p->pcan_cslock);
604*3737Shx147065 
605*3737Shx147065 	pcan_p->pcan_flag |= PCAN_CS_REGISTERED;
606*3737Shx147065 	return (CS_SUCCESS);
607*3737Shx147065 fail:
608*3737Shx147065 	mutex_destroy(&pcan_p->pcan_cslock);
609*3737Shx147065 	cv_destroy(&pcan_p->pcan_cscv);
610*3737Shx147065 unreg:
611*3737Shx147065 	(void) csx_DeregisterClient(chdl);
612*3737Shx147065 regcs_ret:
613*3737Shx147065 	pcan_p->pcan_flag &= ~PCAN_CS_REGISTERED;
614*3737Shx147065 	return (ret);
615*3737Shx147065 }
616*3737Shx147065 
617*3737Shx147065 static void
618*3737Shx147065 pcan_unregister_cs(pcan_maci_t *pcan_p)
619*3737Shx147065 {
620*3737Shx147065 	int ret;
621*3737Shx147065 	release_socket_mask_t mask;
622*3737Shx147065 	mask.Socket = pcan_p->pcan_socket;
623*3737Shx147065 
624*3737Shx147065 	/*
625*3737Shx147065 	 * The card service not registered means register_cs function
626*3737Shx147065 	 * doesnot return TRUE. Then all the lelated resource has been
627*3737Shx147065 	 * released in register_cs.
628*3737Shx147065 	 */
629*3737Shx147065 	if (!(pcan_p->pcan_flag | PCAN_CS_REGISTERED))
630*3737Shx147065 		return;
631*3737Shx147065 	(void) csx_ReleaseSocketMask(pcan_p->pcan_chdl, &mask);
632*3737Shx147065 
633*3737Shx147065 	if (pcan_p->pcan_flag & PCAN_CARD_READY) {
634*3737Shx147065 		pcan_card_remove(pcan_p);
635*3737Shx147065 		pcan_p->pcan_flag &= ~PCAN_CARD_READY;
636*3737Shx147065 	}
637*3737Shx147065 	mutex_destroy(&pcan_p->pcan_cslock);
638*3737Shx147065 	cv_destroy(&pcan_p->pcan_cscv);
639*3737Shx147065 	if (ret = csx_DeregisterClient(pcan_p->pcan_chdl))
640*3737Shx147065 		cmn_err(CE_WARN, "pcan: deregister failed %x\n", ret);
641*3737Shx147065 }
642*3737Shx147065 static void
643*3737Shx147065 pcan_destroy_locks(pcan_maci_t *pcan_p)
644*3737Shx147065 {
645*3737Shx147065 	mutex_destroy(&pcan_p->pcan_txring.an_tx_lock);
646*3737Shx147065 	mutex_destroy(&pcan_p->pcan_scanlist_lock);
647*3737Shx147065 	mutex_destroy(&pcan_p->pcan_glock);
648*3737Shx147065 }
649*3737Shx147065 
650*3737Shx147065 static int
651*3737Shx147065 pcan_ev_hdlr(event_t event, int priority, event_callback_args_t *arg)
652*3737Shx147065 {
653*3737Shx147065 	int ret = CS_SUCCESS;
654*3737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg->client_data;
655*3737Shx147065 	client_info_t *ci_p = (client_info_t *)&arg->client_info;
656*3737Shx147065 
657*3737Shx147065 	mutex_enter(&pcan_p->pcan_cslock);
658*3737Shx147065 	switch (event) {
659*3737Shx147065 	case CS_EVENT_CARD_INSERTION:
660*3737Shx147065 		ret = pcan_card_insert(pcan_p);
661*3737Shx147065 		cv_broadcast(&pcan_p->pcan_cscv);
662*3737Shx147065 		break;
663*3737Shx147065 	case CS_EVENT_REGISTRATION_COMPLETE:
664*3737Shx147065 		cv_broadcast(&pcan_p->pcan_cscv);
665*3737Shx147065 		break;
666*3737Shx147065 	case CS_EVENT_CARD_REMOVAL:
667*3737Shx147065 		if (priority & CS_EVENT_PRI_HIGH)
668*3737Shx147065 			break;
669*3737Shx147065 		pcan_card_remove(pcan_p);
670*3737Shx147065 		cv_broadcast(&pcan_p->pcan_cscv);
671*3737Shx147065 		break;
672*3737Shx147065 	case CS_EVENT_CLIENT_INFO:
673*3737Shx147065 		if (GET_CLIENT_INFO_SUBSVC(ci_p->Attributes) !=
674*3737Shx147065 		    CS_CLIENT_INFO_SUBSVC_CS)
675*3737Shx147065 			break;
676*3737Shx147065 
677*3737Shx147065 		ci_p->Revision = 0x0101;
678*3737Shx147065 		ci_p->CSLevel = CS_VERSION;
679*3737Shx147065 		ci_p->RevDate = CS_CLIENT_INFO_MAKE_DATE(9, 12, 14);
680*3737Shx147065 		(void) strcpy(ci_p->ClientName, PCAN_IDENT_STRING);
681*3737Shx147065 		(void) strcpy(ci_p->VendorName, CS_SUN_VENDOR_DESCRIPTION);
682*3737Shx147065 		ci_p->Attributes |= CS_CLIENT_INFO_VALID;
683*3737Shx147065 		break;
684*3737Shx147065 	default:
685*3737Shx147065 		ret = CS_UNSUPPORTED_EVENT;
686*3737Shx147065 		break;
687*3737Shx147065 	}
688*3737Shx147065 	mutex_exit(&pcan_p->pcan_cslock);
689*3737Shx147065 	return (ret);
690*3737Shx147065 }
691*3737Shx147065 
692*3737Shx147065 static int
693*3737Shx147065 pcan_card_insert(pcan_maci_t *pcan_p)
694*3737Shx147065 {
695*3737Shx147065 	int ret, hi, lo;
696*3737Shx147065 	tuple_t tuple;
697*3737Shx147065 	cisparse_t cisparse;
698*3737Shx147065 	io_req_t	io;
699*3737Shx147065 	irq_req_t	irq;
700*3737Shx147065 	config_req_t	cfg;
701*3737Shx147065 	cistpl_config_t config;
702*3737Shx147065 	cistpl_cftable_entry_t *tbl_p;
703*3737Shx147065 	register client_handle_t chdl = pcan_p->pcan_chdl;
704*3737Shx147065 
705*3737Shx147065 	bzero(&tuple, sizeof (tuple));
706*3737Shx147065 	tuple.DesiredTuple = CISTPL_MANFID;
707*3737Shx147065 	if (ret = csx_GetFirstTuple(chdl, &tuple)) {
708*3737Shx147065 		cmn_err(CE_WARN, "pcan: get manufacture id failed %x\n", ret);
709*3737Shx147065 		goto insert_ret;
710*3737Shx147065 	}
711*3737Shx147065 	bzero(&cisparse, sizeof (cisparse));
712*3737Shx147065 	if (ret = csx_Parse_CISTPL_MANFID(chdl, &tuple, &cisparse.manfid)) {
713*3737Shx147065 		cmn_err(CE_WARN, "pcan: parse manufacture id failed %x\n", ret);
714*3737Shx147065 		goto insert_ret;
715*3737Shx147065 	}
716*3737Shx147065 	/* verify manufacture ID */
717*3737Shx147065 	PCANDBG((CE_NOTE, "pcan: manufacturer_id=%x card=%x\n",
718*3737Shx147065 	    cisparse.manfid.manf, cisparse.manfid.card));
719*3737Shx147065 
720*3737Shx147065 	bzero(&tuple, sizeof (tuple));
721*3737Shx147065 	tuple.DesiredTuple = CISTPL_FUNCID;
722*3737Shx147065 	if (ret = csx_GetFirstTuple(chdl, &tuple)) {
723*3737Shx147065 		cmn_err(CE_WARN, "pcan: get function id failed %x\n", ret);
724*3737Shx147065 		goto insert_ret;
725*3737Shx147065 	}
726*3737Shx147065 	bzero(&cisparse, sizeof (cisparse));
727*3737Shx147065 	if (ret = csx_Parse_CISTPL_FUNCID(chdl, &tuple, &cisparse.funcid)) {
728*3737Shx147065 		cmn_err(CE_WARN, "pcan: parse function id failed %x\n", ret);
729*3737Shx147065 		goto insert_ret;
730*3737Shx147065 	}
731*3737Shx147065 	/* verify function ID */
732*3737Shx147065 	PCANDBG((CE_NOTE, "funcid=%x\n", cisparse.funcid.function));
733*3737Shx147065 
734*3737Shx147065 	bzero(&tuple, sizeof (tuple));
735*3737Shx147065 	tuple.DesiredTuple = CISTPL_CONFIG;
736*3737Shx147065 	if (ret = csx_GetFirstTuple(chdl, &tuple)) {
737*3737Shx147065 		cmn_err(CE_WARN, "pcan: get config failed %x\n", ret);
738*3737Shx147065 		goto insert_ret;
739*3737Shx147065 	}
740*3737Shx147065 	bzero(&config, sizeof (config));
741*3737Shx147065 	if (ret = csx_Parse_CISTPL_CONFIG(chdl, &tuple, &config)) {
742*3737Shx147065 		cmn_err(CE_WARN, "pcan: parse config failed %x\n", ret);
743*3737Shx147065 		goto insert_ret;
744*3737Shx147065 	}
745*3737Shx147065 	PCANDBG((CE_NOTE,
746*3737Shx147065 	    "pcan: config present=%x nr=%x hr=%x regs[0]=%x base=%x last=%x\n",
747*3737Shx147065 	    config.present, config.nr, config.hr, config.regs[0],
748*3737Shx147065 	    config.base, config.last));
749*3737Shx147065 
750*3737Shx147065 	hi = 0;
751*3737Shx147065 	lo = (int)-1;		/* really big number */
752*3737Shx147065 	tbl_p = &cisparse.cftable;
753*3737Shx147065 	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
754*3737Shx147065 	for (tbl_p->index = 0; tbl_p->index <= config.hr; ) {
755*3737Shx147065 		PCANDBG((CE_NOTE, "pcan: tuple idx=%x:\n", tbl_p->index));
756*3737Shx147065 		if (ret = csx_GetNextTuple(chdl, &tuple)) {
757*3737Shx147065 			cmn_err(CE_WARN, "pcan: get cftable failed %x\n", ret);
758*3737Shx147065 			break;
759*3737Shx147065 		}
760*3737Shx147065 		bzero((caddr_t)&cisparse, sizeof (cisparse));
761*3737Shx147065 		if (ret = csx_Parse_CISTPL_CFTABLE_ENTRY(chdl, &tuple, tbl_p)) {
762*3737Shx147065 			cmn_err(CE_WARN, "pcan: parse cftable failed%x\n", ret);
763*3737Shx147065 			break;
764*3737Shx147065 		}
765*3737Shx147065 		if (tbl_p->flags & CISTPL_CFTABLE_TPCE_FS_PWR &&
766*3737Shx147065 			tbl_p->pd.flags & CISTPL_CFTABLE_TPCE_FS_PWR_VCC) {
767*3737Shx147065 			if (tbl_p->pd.pd_vcc.avgI > hi) {
768*3737Shx147065 				hi = tbl_p->pd.pd_vcc.avgI;
769*3737Shx147065 				pcan_p->pcan_config_hi = tbl_p->index;
770*3737Shx147065 			}
771*3737Shx147065 			if (tbl_p->pd.pd_vcc.avgI < lo) {
772*3737Shx147065 				lo = tbl_p->pd.pd_vcc.avgI;
773*3737Shx147065 				pcan_p->pcan_config = tbl_p->index;
774*3737Shx147065 			}
775*3737Shx147065 		}
776*3737Shx147065 		if (tbl_p->flags & CISTPL_CFTABLE_TPCE_DEFAULT) {
777*3737Shx147065 			if (tbl_p->pd.flags & CISTPL_CFTABLE_TPCE_FS_PWR_VCC)
778*3737Shx147065 				pcan_p->pcan_vcc = tbl_p->pd.pd_vcc.nomV;
779*3737Shx147065 			if (tbl_p->flags & CISTPL_CFTABLE_TPCE_FS_IO)
780*3737Shx147065 				pcan_p->pcan_iodecode = tbl_p->io.addr_lines;
781*3737Shx147065 		}
782*3737Shx147065 	}
783*3737Shx147065 	PCANDBG((CE_NOTE, "pcan: cfg_hi=%x cfg=%x vcc=%x iodecode=%x\n",
784*3737Shx147065 	    pcan_p->pcan_config_hi, pcan_p->pcan_config,
785*3737Shx147065 	    pcan_p->pcan_vcc, pcan_p->pcan_iodecode));
786*3737Shx147065 
787*3737Shx147065 	bzero(&io, sizeof (io));
788*3737Shx147065 	io.BasePort1.base = 0;
789*3737Shx147065 	io.NumPorts1 = 1 << pcan_p->pcan_iodecode;
790*3737Shx147065 	io.Attributes1 = IO_DATA_PATH_WIDTH_16;
791*3737Shx147065 	io.IOAddrLines = pcan_p->pcan_iodecode;
792*3737Shx147065 	if (ret = csx_RequestIO(chdl, &io)) {
793*3737Shx147065 		cmn_err(CE_WARN, "pcan: RequestIO failed %x\n", ret);
794*3737Shx147065 		goto insert_ret;
795*3737Shx147065 	}
796*3737Shx147065 	pcan_p->pcan_port = io.BasePort1.handle;
797*3737Shx147065 
798*3737Shx147065 	if (ret = ddi_add_softintr(DIP(pcan_p), DDI_SOFTINT_HIGH,
799*3737Shx147065 	    &pcan_p->pcan_softint_id, &pcan_p->pcan_ib_cookie, NULL,
800*3737Shx147065 	    pcan_intr, (caddr_t)pcan_p)) {
801*3737Shx147065 		cmn_err(CE_NOTE, "pcan: Add softintr failed\n");
802*3737Shx147065 		goto insert_ret;
803*3737Shx147065 	}
804*3737Shx147065 	irq.Attributes = IRQ_TYPE_EXCLUSIVE;
805*3737Shx147065 	irq.irq_handler = ddi_intr_hilevel(DIP(pcan_p), 0) ?
806*3737Shx147065 	    (csfunction_t *)pcan_intr_hi : (csfunction_t *)pcan_intr;
807*3737Shx147065 	irq.irq_handler_arg = pcan_p;
808*3737Shx147065 	if (ret = csx_RequestIRQ(chdl, &irq)) {
809*3737Shx147065 		cmn_err(CE_WARN, "pcan: RequestIRQ failed %x\n", ret);
810*3737Shx147065 		goto un_io;
811*3737Shx147065 	}
812*3737Shx147065 
813*3737Shx147065 	bzero(&cfg, sizeof (cfg));
814*3737Shx147065 	cfg.Attributes = 0; /* not ready for CONF_ENABLE_IRQ_STEERING yet */
815*3737Shx147065 	cfg.Vcc = 50; /* pcan_vcc == 0 */
816*3737Shx147065 	cfg.Vpp1 = 50;
817*3737Shx147065 	cfg.Vpp2 = 50;
818*3737Shx147065 	cfg.IntType = SOCKET_INTERFACE_MEMORY_AND_IO;
819*3737Shx147065 	cfg.ConfigBase = config.base;
820*3737Shx147065 	cfg.ConfigIndex = pcan_p->pcan_config;
821*3737Shx147065 	cfg.Status = CCSR_IO_IS_8; /* no use */
822*3737Shx147065 	cfg.Present = config.present;
823*3737Shx147065 	pcan_p->pcan_flag |= PCAN_CARD_READY;
824*3737Shx147065 	if (ret = csx_RequestConfiguration(chdl, &cfg)) {
825*3737Shx147065 		cmn_err(CE_WARN, "pcan: RequestConfiguration failed %x\n", ret);
826*3737Shx147065 		goto un_irq;
827*3737Shx147065 	}
828*3737Shx147065 	return (CS_SUCCESS);
829*3737Shx147065 un_irq:
830*3737Shx147065 	(void) csx_ReleaseIRQ(chdl, &irq);
831*3737Shx147065 un_io:
832*3737Shx147065 	ddi_remove_softintr(pcan_p->pcan_softint_id);
833*3737Shx147065 
834*3737Shx147065 	(void) csx_ReleaseIO(chdl, &io);
835*3737Shx147065 	pcan_p->pcan_port = 0;
836*3737Shx147065 insert_ret:
837*3737Shx147065 	pcan_p->pcan_flag &= ~PCAN_CARD_READY;
838*3737Shx147065 	return (ret);
839*3737Shx147065 }
840*3737Shx147065 
841*3737Shx147065 /*
842*3737Shx147065  * assume card is already removed, don't touch the hardware
843*3737Shx147065  */
844*3737Shx147065 static void
845*3737Shx147065 pcan_card_remove(pcan_maci_t *pcan_p)
846*3737Shx147065 {
847*3737Shx147065 	int ret;
848*3737Shx147065 	io_req_t io;
849*3737Shx147065 	irq_req_t irq;
850*3737Shx147065 
851*3737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_READY))
852*3737Shx147065 		return;
853*3737Shx147065 	if (ret = csx_ReleaseConfiguration(pcan_p->pcan_chdl, NULL))
854*3737Shx147065 		cmn_err(CE_WARN, "pcan: ReleaseConfiguration failed %x\n", ret);
855*3737Shx147065 
856*3737Shx147065 	bzero(&irq, sizeof (irq));
857*3737Shx147065 	if (ret = csx_ReleaseIRQ(pcan_p->pcan_chdl, &irq))
858*3737Shx147065 		cmn_err(CE_WARN, "pcan: ReleaseIRQ failed %x\n", ret);
859*3737Shx147065 
860*3737Shx147065 	ddi_remove_softintr(pcan_p->pcan_softint_id);
861*3737Shx147065 
862*3737Shx147065 	bzero(&io, sizeof (io));
863*3737Shx147065 	io.BasePort1.handle = pcan_p->pcan_port;
864*3737Shx147065 	io.NumPorts1 = 16;
865*3737Shx147065 	if (ret = csx_ReleaseIO(pcan_p->pcan_chdl, &io))
866*3737Shx147065 		cmn_err(CE_WARN, "pcan: Release IO failed %x\n", ret);
867*3737Shx147065 
868*3737Shx147065 	pcan_p->pcan_port = 0;
869*3737Shx147065 	pcan_p->pcan_flag &= ~PCAN_CARD_READY;
870*3737Shx147065 }
871*3737Shx147065 
872*3737Shx147065 /*
873*3737Shx147065  * gld operation interface routines
874*3737Shx147065  */
875*3737Shx147065 static int
876*3737Shx147065 pcan_start(void *arg)
877*3737Shx147065 {
878*3737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
879*3737Shx147065 
880*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
881*3737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
882*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
883*3737Shx147065 		return (PCAN_FAIL);
884*3737Shx147065 	}
885*3737Shx147065 	(void) pcan_loaddef(pcan_p);
886*3737Shx147065 	pcan_start_locked(pcan_p);
887*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
888*3737Shx147065 	return (PCAN_SUCCESS);
889*3737Shx147065 }
890*3737Shx147065 
891*3737Shx147065 static void
892*3737Shx147065 pcan_stop(void *arg)
893*3737Shx147065 {
894*3737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
895*3737Shx147065 
896*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
897*3737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
898*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
899*3737Shx147065 		return;
900*3737Shx147065 	}
901*3737Shx147065 	pcan_stop_locked(pcan_p);
902*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
903*3737Shx147065 	if (pcan_p->pcan_connect_timeout_id != 0) {
904*3737Shx147065 		(void) untimeout(pcan_p->pcan_connect_timeout_id);
905*3737Shx147065 		pcan_p->pcan_connect_timeout_id = 0;
906*3737Shx147065 	}
907*3737Shx147065 }
908*3737Shx147065 
909*3737Shx147065 /*
910*3737Shx147065  * mac address can only be set in 'disable' state and
911*3737Shx147065  * be effective after 'enable' state.
912*3737Shx147065  */
913*3737Shx147065 static int
914*3737Shx147065 pcan_saddr(void *arg, const uint8_t *macaddr)
915*3737Shx147065 {
916*3737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
917*3737Shx147065 	int ret = PCAN_SUCCESS;
918*3737Shx147065 	ether_copy(macaddr, pcan_p->pcan_mac_addr);
919*3737Shx147065 
920*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
921*3737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
922*3737Shx147065 		ret = PCAN_FAIL;
923*3737Shx147065 		goto done;
924*3737Shx147065 	}
925*3737Shx147065 	ether_copy(pcan_p->pcan_mac_addr, pcan_p->an_config.an_macaddr);
926*3737Shx147065 	if (pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0)) {
927*3737Shx147065 		cmn_err(CE_WARN, "pcan set mac addr: failed\n");
928*3737Shx147065 		ret = PCAN_FAIL;
929*3737Shx147065 		goto done;
930*3737Shx147065 	}
931*3737Shx147065 	if (pcan_config_mac(pcan_p)) {
932*3737Shx147065 		cmn_err(CE_WARN, "pcan set mac addr: config_mac failed\n");
933*3737Shx147065 		ret = PCAN_FAIL;
934*3737Shx147065 		goto done;
935*3737Shx147065 	}
936*3737Shx147065 	if (pcan_set_cmd(pcan_p, AN_CMD_ENABLE, 0)) {
937*3737Shx147065 		cmn_err(CE_WARN, "pcan set mac addr: failed\n");
938*3737Shx147065 		ret = PCAN_FAIL;
939*3737Shx147065 	}
940*3737Shx147065 done:
941*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
942*3737Shx147065 	return (ret);
943*3737Shx147065 }
944*3737Shx147065 
945*3737Shx147065 /*
946*3737Shx147065  * send a packet out for pccard
947*3737Shx147065  */
948*3737Shx147065 static int
949*3737Shx147065 pcan_send(pcan_maci_t *pcan_p, mblk_t *mblk_p)
950*3737Shx147065 {
951*3737Shx147065 	char *buf, *buf_p;
952*3737Shx147065 	an_txfrm_t *frm_p;
953*3737Shx147065 #ifdef PCAN_SEND_DEBUG
954*3737Shx147065 	struct an_ltv_status radio_status;
955*3737Shx147065 #endif /* PCAN_SEND_DEBUG */
956*3737Shx147065 	uint16_t pkt_len, xmt_id, ring_idx;
957*3737Shx147065 	struct ieee80211_frame *wh;
958*3737Shx147065 	int i = 0;
959*3737Shx147065 
960*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
961*3737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
962*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
963*3737Shx147065 		freemsg(mblk_p);
964*3737Shx147065 		return (PCAN_SUCCESS);		/* drop packet */
965*3737Shx147065 	}
966*3737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_LINKUP)) {	/* link down */
967*3737Shx147065 		PCANDBG((CE_NOTE, "pcan: link down, dropped\n"));
968*3737Shx147065 		pcan_p->glds_nocarrier++;
969*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
970*3737Shx147065 		freemsg(mblk_p);
971*3737Shx147065 		return (PCAN_SUCCESS);		/* drop packet */
972*3737Shx147065 	}
973*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
974*3737Shx147065 	if (pullupmsg(mblk_p, -1) == 0) {
975*3737Shx147065 		cmn_err(CE_NOTE, "pcan send: pullupmsg failed\n");
976*3737Shx147065 		freemsg(mblk_p);
977*3737Shx147065 		return (PCAN_SUCCESS);		/* drop packet */
978*3737Shx147065 	}
979*3737Shx147065 	wh = (struct ieee80211_frame *)mblk_p->b_rptr;
980*3737Shx147065 
981*3737Shx147065 	mutex_enter(&pcan_p->pcan_txring.an_tx_lock);
982*3737Shx147065 	ring_idx = pcan_p->pcan_txring.an_tx_prod;
983*3737Shx147065 	pcan_p->pcan_txring.an_tx_prod = (ring_idx + 1) & AN_TX_RING_MASK;
984*3737Shx147065 
985*3737Shx147065 	/* check whether there is a xmt buffer available */
986*3737Shx147065 	while ((i < AN_TX_RING_CNT) &&
987*3737Shx147065 	    (pcan_p->pcan_txring.an_tx_ring[ring_idx])) {
988*3737Shx147065 		ring_idx = pcan_p->pcan_txring.an_tx_prod;
989*3737Shx147065 		pcan_p->pcan_txring.an_tx_prod =
990*3737Shx147065 		    (ring_idx + 1) & AN_TX_RING_MASK;
991*3737Shx147065 		i++;
992*3737Shx147065 	}
993*3737Shx147065 
994*3737Shx147065 	if (i == AN_TX_RING_CNT) {
995*3737Shx147065 		mutex_exit(&pcan_p->pcan_txring.an_tx_lock);
996*3737Shx147065 		PCANDBG((CE_NOTE, "pcan: ring full, retrying\n"));
997*3737Shx147065 		mutex_enter(&pcan_p->pcan_glock);
998*3737Shx147065 		pcan_p->pcan_reschedule_need = B_TRUE;
999*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
1000*3737Shx147065 		pcan_p->glds_noxmtbuf++;
1001*3737Shx147065 		return (PCAN_FAIL);
1002*3737Shx147065 	}
1003*3737Shx147065 	xmt_id = pcan_p->pcan_txring.an_tx_fids[ring_idx];
1004*3737Shx147065 	pcan_p->pcan_txring.an_tx_ring[ring_idx] = xmt_id;
1005*3737Shx147065 	mutex_exit(&pcan_p->pcan_txring.an_tx_lock);
1006*3737Shx147065 
1007*3737Shx147065 	buf = kmem_zalloc(PCAN_NICMEM_SZ, KM_SLEEP); /* too big for stack */
1008*3737Shx147065 	buf_p = (ulong_t)buf & 1 ? buf + 1 : buf;	/* 16-bit round up */
1009*3737Shx147065 	frm_p = (an_txfrm_t *)buf_p;
1010*3737Shx147065 
1011*3737Shx147065 #ifdef DEBUG
1012*3737Shx147065 	if (pcan_debug & PCAN_DBG_SEND) {
1013*3737Shx147065 		cmn_err(CE_NOTE, "pcan send: packet from plugin");
1014*3737Shx147065 		for (i = 0; i < mblk_p->b_wptr - mblk_p->b_rptr; i++)
1015*3737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
1016*3737Shx147065 			    *((unsigned char *)mblk_p->b_rptr + i));
1017*3737Shx147065 	}
1018*3737Shx147065 #endif
1019*3737Shx147065 	pkt_len = msgdsize(mblk_p);
1020*3737Shx147065 	if (pkt_len > PCAN_NICMEM_SZ - sizeof (an_txfrm_t)) {
1021*3737Shx147065 		cmn_err(CE_WARN, "pcan send: mblk is too long");
1022*3737Shx147065 		kmem_free(buf, PCAN_NICMEM_SZ);
1023*3737Shx147065 		freemsg(mblk_p);
1024*3737Shx147065 		return (PCAN_SUCCESS);		/* drop packet */
1025*3737Shx147065 	}
1026*3737Shx147065 	if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) !=
1027*3737Shx147065 	    IEEE80211_FC1_DIR_TODS) {
1028*3737Shx147065 		kmem_free(buf, PCAN_NICMEM_SZ);
1029*3737Shx147065 		freemsg(mblk_p);
1030*3737Shx147065 		return (PCAN_SUCCESS);		/* drop packet */
1031*3737Shx147065 	}
1032*3737Shx147065 
1033*3737Shx147065 	/* initialize xmt frame header, payload_len must be stored in LE */
1034*3737Shx147065 	bzero(frm_p, sizeof (an_txfrm_t) + 2);
1035*3737Shx147065 	frm_p->an_tx_ctl = AN_TXCTL_8023;
1036*3737Shx147065 
1037*3737Shx147065 	/*
1038*3737Shx147065 	 * mblk sent down from plugin includes station mode 802.11 frame and
1039*3737Shx147065 	 * llc, so we here need to remove them and add an ethernet header.
1040*3737Shx147065 	 */
1041*3737Shx147065 	pkt_len = pkt_len - (sizeof (*wh) + sizeof (struct ieee80211_llc))
1042*3737Shx147065 	    + 2;
1043*3737Shx147065 	bcopy(wh->i_addr3, buf_p + 0x38, ETHERADDRL); /* dst macaddr */
1044*3737Shx147065 	bcopy(wh->i_addr2, buf_p + 0x3e, ETHERADDRL); /* src macaddr */
1045*3737Shx147065 	*((uint16_t *)(buf_p + 0x36)) = pkt_len;
1046*3737Shx147065 	bcopy(mblk_p->b_rptr + sizeof (*wh) + sizeof (struct ieee80211_llc)
1047*3737Shx147065 	    - 2, buf_p + 0x44, pkt_len);
1048*3737Shx147065 
1049*3737Shx147065 	if (pkt_len & 1) {	/* round up to 16-bit boundary and pad 0 */
1050*3737Shx147065 		buf_p[pkt_len + 0x44] = 0;
1051*3737Shx147065 		pkt_len++;
1052*3737Shx147065 	}
1053*3737Shx147065 	ASSERT(pkt_len <= PCAN_NICMEM_SZ);
1054*3737Shx147065 #ifdef DEBUG
1055*3737Shx147065 	if (pcan_debug & PCAN_DBG_SEND) {
1056*3737Shx147065 		cmn_err(CE_NOTE, "pcan send: packet to hardware--pkt_len=%x",
1057*3737Shx147065 		    pkt_len);
1058*3737Shx147065 		for (i = 0; i < pkt_len + 4; i++)
1059*3737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
1060*3737Shx147065 			    *((unsigned char *)buf_p + 0x36 + i));
1061*3737Shx147065 	}
1062*3737Shx147065 #endif
1063*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
1064*3737Shx147065 	(void) WRCH1(pcan_p, xmt_id, 0, (uint16_t *)buf_p, 0x38); /* frm */
1065*3737Shx147065 	(void) WRPKT(pcan_p, xmt_id, 0x38, (uint16_t *)(buf_p + 0x38),
1066*3737Shx147065 		pkt_len + 12);
1067*3737Shx147065 	ring_idx = pcan_set_cmd(pcan_p, AN_CMD_TX, xmt_id);
1068*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
1069*3737Shx147065 
1070*3737Shx147065 	PCANDBG((CE_NOTE, "pcan: pkt_len=0x44+%x=%x xmt=%x ret=%x\n",
1071*3737Shx147065 	    pkt_len, 0x44 + pkt_len, xmt_id, ring_idx));
1072*3737Shx147065 	kmem_free(buf, PCAN_NICMEM_SZ);
1073*3737Shx147065 #ifdef PCAN_SEND_DEBUG
1074*3737Shx147065 	if (pkt_len = pcan_status_ltv(PCAN_READ_LTV, pcan_p, &radio_status)) {
1075*3737Shx147065 		PCANDBG((CE_NOTE, "pcan: bad radio status %x\n", pkt_len));
1076*3737Shx147065 	} else {
1077*3737Shx147065 		PCANDBG((CE_NOTE, "pcan: radio status:\n"));
1078*3737Shx147065 	}
1079*3737Shx147065 #endif /* PCAN_SEND_DEBUG */
1080*3737Shx147065 	if (ring_idx)
1081*3737Shx147065 		return (PCAN_FAIL);
1082*3737Shx147065 	else {
1083*3737Shx147065 		freemsg(mblk_p);
1084*3737Shx147065 		return (PCAN_SUCCESS);
1085*3737Shx147065 	}
1086*3737Shx147065 }
1087*3737Shx147065 
1088*3737Shx147065 /*
1089*3737Shx147065  * send a packet out for PCI/MiniPCI card
1090*3737Shx147065  */
1091*3737Shx147065 static int
1092*3737Shx147065 pcian_send(pcan_maci_t *pcan_p, mblk_t *mblk_p)
1093*3737Shx147065 {
1094*3737Shx147065 	char *buf;
1095*3737Shx147065 	uint16_t pkt_len = msgdsize(mblk_p), ring_idx;
1096*3737Shx147065 	uint32_t i;
1097*3737Shx147065 	struct ieee80211_frame *wh;
1098*3737Shx147065 	struct an_card_tx_desc an_tx_desc;
1099*3737Shx147065 
1100*3737Shx147065 	ring_idx = pcan_p->pcan_txring.an_tx_prod;
1101*3737Shx147065 
1102*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
1103*3737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_LINKUP)) {	/* link down */
1104*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
1105*3737Shx147065 		pcan_p->glds_nocarrier++;
1106*3737Shx147065 		freemsg(mblk_p);
1107*3737Shx147065 		return (PCAN_SUCCESS);		/* drop packet */
1108*3737Shx147065 	}
1109*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
1110*3737Shx147065 	if (pullupmsg(mblk_p, -1) == 0) {
1111*3737Shx147065 		cmn_err(CE_NOTE, "pcan(pci) send: pullupmsg failed\n");
1112*3737Shx147065 		freemsg(mblk_p);
1113*3737Shx147065 		return (PCAN_SUCCESS);		/* drop packet */
1114*3737Shx147065 	}
1115*3737Shx147065 	wh = (struct ieee80211_frame *)mblk_p->b_rptr;
1116*3737Shx147065 
1117*3737Shx147065 	mutex_enter(&pcan_p->pcan_txring.an_tx_lock);
1118*3737Shx147065 	if ((pcan_p->pcan_flag & PCAN_CARD_SEND) &&
1119*3737Shx147065 	    (ring_idx == pcan_p->pcan_txring.an_tx_cons)) {
1120*3737Shx147065 		pcan_p->glds_noxmtbuf++;
1121*3737Shx147065 		pcan_p->pcan_reschedule_need = B_TRUE;
1122*3737Shx147065 		mutex_exit(&pcan_p->pcan_txring.an_tx_lock);
1123*3737Shx147065 		return (PCAN_FAIL);
1124*3737Shx147065 	}
1125*3737Shx147065 	mutex_exit(&pcan_p->pcan_txring.an_tx_lock);
1126*3737Shx147065 
1127*3737Shx147065 #ifdef DEBUG
1128*3737Shx147065 	if (pcan_debug & PCAN_DBG_SEND) {
1129*3737Shx147065 		cmn_err(CE_NOTE, "pcan(pci) send: packet from plugin");
1130*3737Shx147065 		for (i = 0; i < mblk_p->b_wptr - mblk_p->b_rptr; i++)
1131*3737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
1132*3737Shx147065 			    *((unsigned char *)mblk_p->b_rptr + i));
1133*3737Shx147065 	}
1134*3737Shx147065 #endif
1135*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
1136*3737Shx147065 
1137*3737Shx147065 	buf = pcan_p->pcan_tx[ring_idx].dma_virtaddr;
1138*3737Shx147065 	bzero(buf, AN_TX_BUFFER_SIZE);
1139*3737Shx147065 
1140*3737Shx147065 	/*
1141*3737Shx147065 	 * mblk sent down from plugin includes station mode 802.11 frame and
1142*3737Shx147065 	 * llc, so we here need to remove them and add an ethernet header.
1143*3737Shx147065 	 */
1144*3737Shx147065 	*((uint16_t *)(buf + 8)) = htons(AN_TXCTL_8023);
1145*3737Shx147065 	pkt_len = pkt_len - (sizeof (*wh) + sizeof (struct ieee80211_llc))
1146*3737Shx147065 	    + 2;
1147*3737Shx147065 	bcopy(wh->i_addr3, buf + 0x38, ETHERADDRL); /* dst macaddr */
1148*3737Shx147065 	bcopy(wh->i_addr2, buf + 0x3e, ETHERADDRL); /* src macaddr */
1149*3737Shx147065 	*((uint16_t *)(buf + 0x36)) = pkt_len;
1150*3737Shx147065 	bcopy(mblk_p->b_rptr + sizeof (*wh) + sizeof (struct ieee80211_llc)
1151*3737Shx147065 	    - 2, buf + 0x44, pkt_len);
1152*3737Shx147065 
1153*3737Shx147065 #ifdef DEBUG
1154*3737Shx147065 	if (pcan_debug & PCAN_DBG_SEND) {
1155*3737Shx147065 		cmn_err(CE_NOTE, "pcan(pci) send: packet to hardware "
1156*3737Shx147065 		    "pkt_len=%x", pkt_len);
1157*3737Shx147065 		for (i = 0; i < pkt_len + 14; i++)
1158*3737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
1159*3737Shx147065 			    *((unsigned char *)buf + 0x36 + i));
1160*3737Shx147065 	}
1161*3737Shx147065 #endif
1162*3737Shx147065 	bzero(&an_tx_desc, sizeof (an_tx_desc));
1163*3737Shx147065 	an_tx_desc.an_offset = 0;
1164*3737Shx147065 	an_tx_desc.an_eoc = (ring_idx == (AN_MAX_TX_DESC-1) ? 1 : 0);
1165*3737Shx147065 	an_tx_desc.an_valid = 1;
1166*3737Shx147065 	an_tx_desc.an_len =  0x44 + pkt_len;
1167*3737Shx147065 	an_tx_desc.an_phys  = pcan_p->pcan_tx[ring_idx].dma_physaddr;
1168*3737Shx147065 	for (i = 0; i < sizeof (an_tx_desc) / 4; i++) {
1169*3737Shx147065 		PCAN_AUX_PUT32(pcan_p, AN_TX_DESC_OFFSET +
1170*3737Shx147065 		    (ring_idx * sizeof (an_tx_desc)) + (i * 4),
1171*3737Shx147065 		    ((uint32_t *)&an_tx_desc)[i]);
1172*3737Shx147065 	}
1173*3737Shx147065 
1174*3737Shx147065 	mutex_enter(&pcan_p->pcan_txring.an_tx_lock);
1175*3737Shx147065 	pcan_p->pcan_txring.an_tx_prod = (ring_idx + 1) % AN_MAX_TX_DESC;
1176*3737Shx147065 	pcan_p->pcan_flag |= PCAN_CARD_SEND;
1177*3737Shx147065 	PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_ALLOC);
1178*3737Shx147065 	mutex_exit(&pcan_p->pcan_txring.an_tx_lock);
1179*3737Shx147065 
1180*3737Shx147065 	freemsg(mblk_p);
1181*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
1182*3737Shx147065 	return (PCAN_SUCCESS);
1183*3737Shx147065 }
1184*3737Shx147065 
1185*3737Shx147065 static mblk_t *
1186*3737Shx147065 pcan_tx(void *arg, mblk_t *mp)
1187*3737Shx147065 {
1188*3737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
1189*3737Shx147065 	mblk_t *next;
1190*3737Shx147065 	int ret = 0;
1191*3737Shx147065 
1192*3737Shx147065 	ASSERT(mp != NULL);
1193*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
1194*3737Shx147065 	if ((pcan_p->pcan_flag & (PCAN_CARD_LINKUP | PCAN_CARD_READY)) !=
1195*3737Shx147065 	    (PCAN_CARD_LINKUP | PCAN_CARD_READY)) {
1196*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
1197*3737Shx147065 		return (mp);
1198*3737Shx147065 	}
1199*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
1200*3737Shx147065 	while (mp != NULL) {
1201*3737Shx147065 		next =  mp->b_next;
1202*3737Shx147065 		mp->b_next = NULL;
1203*3737Shx147065 
1204*3737Shx147065 		if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
1205*3737Shx147065 			ret = pcian_send(pcan_p, mp);
1206*3737Shx147065 		} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
1207*3737Shx147065 			ret = pcan_send(pcan_p, mp);
1208*3737Shx147065 		}
1209*3737Shx147065 		if (ret) {
1210*3737Shx147065 			mp->b_next = next;
1211*3737Shx147065 			break;
1212*3737Shx147065 		}
1213*3737Shx147065 		mp = next;
1214*3737Shx147065 	}
1215*3737Shx147065 	return (mp);
1216*3737Shx147065 }
1217*3737Shx147065 
1218*3737Shx147065 /*
1219*3737Shx147065  * this driver is porting from freebsd, the code in freebsd
1220*3737Shx147065  * doesn't show how to set promiscous mode.
1221*3737Shx147065  */
1222*3737Shx147065 /*ARGSUSED*/
1223*3737Shx147065 static int
1224*3737Shx147065 pcan_prom(void *arg, boolean_t on)
1225*3737Shx147065 {
1226*3737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
1227*3737Shx147065 	int ret = PCAN_SUCCESS;
1228*3737Shx147065 
1229*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
1230*3737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
1231*3737Shx147065 		ret = PCAN_FAIL;
1232*3737Shx147065 	}
1233*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
1234*3737Shx147065 	return (ret);
1235*3737Shx147065 }
1236*3737Shx147065 
1237*3737Shx147065 /*ARGSUSED*/
1238*3737Shx147065 static int
1239*3737Shx147065 pcan_gstat(void *arg, uint_t statitem, uint64_t *val)
1240*3737Shx147065 {
1241*3737Shx147065 	uint16_t i;
1242*3737Shx147065 	int ret = PCAN_SUCCESS;
1243*3737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
1244*3737Shx147065 	uint64_t *cntr_p = pcan_p->pcan_cntrs_s;
1245*3737Shx147065 
1246*3737Shx147065 	PCANDBG((CE_NOTE, "pcan: gstat called\n"));
1247*3737Shx147065 
1248*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
1249*3737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
1250*3737Shx147065 		ret = PCAN_FAIL;
1251*3737Shx147065 		goto done;
1252*3737Shx147065 	}
1253*3737Shx147065 	if (pcan_get_ltv(pcan_p, sizeof (pcan_p->an_stats),
1254*3737Shx147065 		AN_RID_16BITS_DELTACLR, (uint16_t *)&pcan_p->an_stats)) {
1255*3737Shx147065 		cmn_err(CE_WARN, "pcan kstat: get ltv(32 delta statistics)"
1256*3737Shx147065 		    " failed \n");
1257*3737Shx147065 		ret = PCAN_FAIL;
1258*3737Shx147065 		goto done;
1259*3737Shx147065 	}
1260*3737Shx147065 	for (i = 0; i < ANC_STAT_CNT; i++) {
1261*3737Shx147065 		cntr_p[i] += *((uint16_t *)&pcan_p->an_stats + 1 + i);
1262*3737Shx147065 	}
1263*3737Shx147065 	if (pcan_status_ltv(PCAN_READ_LTV, pcan_p, &pcan_p->an_status)) {
1264*3737Shx147065 		cmn_err(CE_WARN, "pcan kstat: read status failed \n");
1265*3737Shx147065 		ret = PCAN_FAIL;
1266*3737Shx147065 		goto done;
1267*3737Shx147065 	}
1268*3737Shx147065 
1269*3737Shx147065 	switch (statitem) {
1270*3737Shx147065 	case MAC_STAT_IFSPEED:
1271*3737Shx147065 		*val = 500000 * pcan_p->an_status.an_cur_tx_rate;
1272*3737Shx147065 		break;
1273*3737Shx147065 	case MAC_STAT_NOXMTBUF:
1274*3737Shx147065 		*val = pcan_p->glds_noxmtbuf;
1275*3737Shx147065 		break;
1276*3737Shx147065 	case MAC_STAT_NORCVBUF:
1277*3737Shx147065 		*val = pcan_p->glds_norcvbuf;
1278*3737Shx147065 		break;
1279*3737Shx147065 	case MAC_STAT_IERRORS:
1280*3737Shx147065 		*val = cntr_p[ANC_RX_OVERRUNS] +
1281*3737Shx147065 		    cntr_p[ANC_RX_PLCP_CSUM_ERRS] +
1282*3737Shx147065 		    cntr_p[ANC_RX_PLCP_FORMAT_ERRS] +
1283*3737Shx147065 		    cntr_p[ANC_RX_PLCP_LEN_ERRS] +
1284*3737Shx147065 		    cntr_p[ANC_RX_MAC_CRC_ERRS] +
1285*3737Shx147065 		    cntr_p[ANC_RX_WEP_ERRS];
1286*3737Shx147065 		break;
1287*3737Shx147065 	case MAC_STAT_OERRORS:
1288*3737Shx147065 		*val = cntr_p[ANC_TX_HOST_FAILED];
1289*3737Shx147065 		break;
1290*3737Shx147065 	case MAC_STAT_RBYTES:
1291*3737Shx147065 		*val = cntr_p[ANC_HOST_RX_BYTES];
1292*3737Shx147065 		break;
1293*3737Shx147065 	case MAC_STAT_IPACKETS:
1294*3737Shx147065 		*val = cntr_p[ANC_RX_HOST_UCASTS];
1295*3737Shx147065 		break;
1296*3737Shx147065 	case MAC_STAT_OBYTES:
1297*3737Shx147065 		*val = cntr_p[ANC_HOST_TX_BYTES];
1298*3737Shx147065 		break;
1299*3737Shx147065 	case MAC_STAT_OPACKETS:
1300*3737Shx147065 		*val = cntr_p[ANC_TX_HOST_UCASTS];
1301*3737Shx147065 		break;
1302*3737Shx147065 	case WIFI_STAT_TX_FAILED:
1303*3737Shx147065 		*val = cntr_p[ANC_TX_HOST_FAILED];
1304*3737Shx147065 		break;
1305*3737Shx147065 	case WIFI_STAT_TX_RETRANS:
1306*3737Shx147065 		*val = cntr_p[ANC_HOST_RETRIES];
1307*3737Shx147065 		break;
1308*3737Shx147065 	case WIFI_STAT_FCS_ERRORS:
1309*3737Shx147065 		*val = cntr_p[ANC_RX_MAC_CRC_ERRS];
1310*3737Shx147065 		break;
1311*3737Shx147065 	case WIFI_STAT_WEP_ERRORS:
1312*3737Shx147065 		*val = cntr_p[ANC_RX_WEP_ERRS];
1313*3737Shx147065 		break;
1314*3737Shx147065 	case WIFI_STAT_MCAST_TX:
1315*3737Shx147065 		*val = cntr_p[ANC_TX_HOST_MCASTS];
1316*3737Shx147065 		break;
1317*3737Shx147065 	case WIFI_STAT_MCAST_RX:
1318*3737Shx147065 		*val = cntr_p[ANC_RX_HOST_MCASTS];
1319*3737Shx147065 		break;
1320*3737Shx147065 	case WIFI_STAT_TX_FRAGS:
1321*3737Shx147065 	case WIFI_STAT_RX_FRAGS:
1322*3737Shx147065 		*val = 0;
1323*3737Shx147065 		break;
1324*3737Shx147065 	case WIFI_STAT_RTS_SUCCESS:
1325*3737Shx147065 		*val = cntr_p[ANC_TX_RTS_OK];
1326*3737Shx147065 		break;
1327*3737Shx147065 	case WIFI_STAT_RTS_FAILURE:
1328*3737Shx147065 		*val = cntr_p[ANC_NO_CTS];
1329*3737Shx147065 		break;
1330*3737Shx147065 	case WIFI_STAT_ACK_FAILURE:
1331*3737Shx147065 		*val = cntr_p[ANC_NO_ACK];
1332*3737Shx147065 		break;
1333*3737Shx147065 	case WIFI_STAT_RX_DUPS:
1334*3737Shx147065 		*val = cntr_p[ANC_RX_DUPS];
1335*3737Shx147065 		break;
1336*3737Shx147065 	default:
1337*3737Shx147065 		ret = ENOTSUP;
1338*3737Shx147065 	}
1339*3737Shx147065 
1340*3737Shx147065 
1341*3737Shx147065 done:
1342*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
1343*3737Shx147065 	return (ret);
1344*3737Shx147065 }
1345*3737Shx147065 
1346*3737Shx147065 /*
1347*3737Shx147065  * this driver is porting from freebsd, the code in freebsd
1348*3737Shx147065  * doesn't show how to set multi address.
1349*3737Shx147065  */
1350*3737Shx147065 /*ARGSUSED*/
1351*3737Shx147065 static int
1352*3737Shx147065 pcan_sdmulti(void *arg, boolean_t add, const uint8_t *eth_p)
1353*3737Shx147065 {
1354*3737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
1355*3737Shx147065 
1356*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
1357*3737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
1358*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
1359*3737Shx147065 		return (PCAN_FAIL);
1360*3737Shx147065 	}
1361*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
1362*3737Shx147065 	return (PCAN_SUCCESS);
1363*3737Shx147065 }
1364*3737Shx147065 
1365*3737Shx147065 static uint_t
1366*3737Shx147065 pcan_info_softint(caddr_t arg)
1367*3737Shx147065 {
1368*3737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
1369*3737Shx147065 	wifi_data_t wd = { 0 };
1370*3737Shx147065 	uint16_t link;
1371*3737Shx147065 	uint32_t link_up;
1372*3737Shx147065 
1373*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
1374*3737Shx147065 	if (pcan_p->pcan_info_softint_pending != 1) {
1375*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
1376*3737Shx147065 		return (DDI_INTR_UNCLAIMED);
1377*3737Shx147065 	}
1378*3737Shx147065 
1379*3737Shx147065 	PCAN_READ(pcan_p, AN_LINKSTAT(pcan_p), link);
1380*3737Shx147065 	link_up = pcan_p->pcan_flag & PCAN_CARD_LINKUP;
1381*3737Shx147065 	if ((link == AN_LINKSTAT_ASSOCIATED) && !link_up) {
1382*3737Shx147065 		pcan_p->pcan_flag |= PCAN_CARD_LINKUP;
1383*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
1384*3737Shx147065 		if (pcan_p->pcan_connect_timeout_id != 0) {
1385*3737Shx147065 			(void) untimeout(pcan_p->pcan_connect_timeout_id);
1386*3737Shx147065 			pcan_p->pcan_connect_timeout_id = 0;
1387*3737Shx147065 		}
1388*3737Shx147065 		mac_link_update(GLD3(pcan_p), LINK_STATE_UP);
1389*3737Shx147065 		mutex_enter(&pcan_p->pcan_glock);
1390*3737Shx147065 		(void) pcan_status_ltv(PCAN_READ_LTV, pcan_p,
1391*3737Shx147065 		    &pcan_p->an_status);
1392*3737Shx147065 		bcopy(pcan_p->an_status.an_cur_bssid, wd.wd_bssid, 6);
1393*3737Shx147065 		wd.wd_secalloc = WIFI_SEC_NONE;
1394*3737Shx147065 		wd.wd_opmode = IEEE80211_M_STA;
1395*3737Shx147065 		(void) mac_pdata_update(pcan_p->pcan_mh, &wd,
1396*3737Shx147065 		    sizeof (wd));
1397*3737Shx147065 #ifdef DEBUG
1398*3737Shx147065 		if (pcan_debug & PCAN_DBG_LINKINFO) {
1399*3737Shx147065 			cmn_err(CE_NOTE, "pcan: link Up, chan=%d, "
1400*3737Shx147065 			    "ssid=\"%s\""
1401*3737Shx147065 			    " (%02x:%02x:%02x:%02x:%02x:%02x)\n",
1402*3737Shx147065 			    pcan_p->an_status.an_channel_set,
1403*3737Shx147065 			    pcan_p->an_status.an_ssid,
1404*3737Shx147065 			    pcan_p->an_status.an_cur_bssid[0],
1405*3737Shx147065 			    pcan_p->an_status.an_cur_bssid[1],
1406*3737Shx147065 			    pcan_p->an_status.an_cur_bssid[2],
1407*3737Shx147065 			    pcan_p->an_status.an_cur_bssid[3],
1408*3737Shx147065 			    pcan_p->an_status.an_cur_bssid[4],
1409*3737Shx147065 			    pcan_p->an_status.an_cur_bssid[5]);
1410*3737Shx147065 		}
1411*3737Shx147065 #endif
1412*3737Shx147065 	}
1413*3737Shx147065 	if ((link != AN_LINKSTAT_ASSOCIATED) && link_up) {
1414*3737Shx147065 		pcan_p->pcan_flag &= ~PCAN_CARD_LINKUP;
1415*3737Shx147065 #ifdef DEBUG
1416*3737Shx147065 		if (pcan_debug & PCAN_DBG_LINKINFO) {
1417*3737Shx147065 			cmn_err(CE_NOTE, "pcan: link Down 0x%x\n", link);
1418*3737Shx147065 		}
1419*3737Shx147065 #endif
1420*3737Shx147065 		if (link != AN_LINKSTAT_SYNCLOST_HOSTREQ) {
1421*3737Shx147065 			pcan_p->pcan_connect_timeout_id =
1422*3737Shx147065 			    timeout(pcan_connect_timeout,
1423*3737Shx147065 			    pcan_p, drv_usectohz(1000));
1424*3737Shx147065 		}
1425*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
1426*3737Shx147065 		mac_link_update(GLD3(pcan_p), LINK_STATE_DOWN);
1427*3737Shx147065 		mutex_enter(&pcan_p->pcan_glock);
1428*3737Shx147065 	}
1429*3737Shx147065 
1430*3737Shx147065 	pcan_p->pcan_info_softint_pending = 0;
1431*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
1432*3737Shx147065 	return (DDI_INTR_CLAIMED);
1433*3737Shx147065 }
1434*3737Shx147065 
1435*3737Shx147065 static uint_t
1436*3737Shx147065 pcan_intr(caddr_t arg)
1437*3737Shx147065 {
1438*3737Shx147065 	uint16_t stat;
1439*3737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
1440*3737Shx147065 
1441*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
1442*3737Shx147065 	if ((pcan_p->pcan_flag & (PCAN_CARD_READY | PCAN_CARD_INTREN)) !=
1443*3737Shx147065 	    (PCAN_CARD_READY | PCAN_CARD_INTREN)) {
1444*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
1445*3737Shx147065 		return (DDI_INTR_UNCLAIMED);
1446*3737Shx147065 	}
1447*3737Shx147065 	PCAN_READ(pcan_p, AN_EVENT_STAT(pcan_p), stat);
1448*3737Shx147065 
1449*3737Shx147065 	if (!(stat & AN_INTRS(pcan_p)) || stat == AN_EV_ALL) {
1450*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
1451*3737Shx147065 		return (DDI_INTR_UNCLAIMED);
1452*3737Shx147065 	}
1453*3737Shx147065 
1454*3737Shx147065 	PCAN_DISABLE_INTR(pcan_p);
1455*3737Shx147065 	PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), ~AN_INTRS(pcan_p));
1456*3737Shx147065 
1457*3737Shx147065 	PCANDBG((CE_NOTE, "pcan intr: stat=%x pcan_flags=%x\n", stat,
1458*3737Shx147065 		pcan_p->pcan_flag));
1459*3737Shx147065 
1460*3737Shx147065 	if (stat & AN_EV_AWAKE) {
1461*3737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_AWAKE);
1462*3737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_AWAKE);
1463*3737Shx147065 	}
1464*3737Shx147065 	if (stat & AN_EV_LINKSTAT) {
1465*3737Shx147065 		pcan_p->pcan_info_softint_pending = 1;
1466*3737Shx147065 		ddi_trigger_softintr(pcan_p->pcan_info_softint_id);
1467*3737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_LINKSTAT);
1468*3737Shx147065 	}
1469*3737Shx147065 	if (stat & AN_EV_RX) {
1470*3737Shx147065 		if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
1471*3737Shx147065 			pcian_rcv(pcan_p);
1472*3737Shx147065 		} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
1473*3737Shx147065 			pcan_rcv(pcan_p);
1474*3737Shx147065 		}
1475*3737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_RX);
1476*3737Shx147065 	}
1477*3737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
1478*3737Shx147065 		if (stat & AN_EV_TX_CPY) {
1479*3737Shx147065 			(void) pcan_txdone(pcan_p, stat & AN_EV_TX_CPY);
1480*3737Shx147065 			if (pcan_p->pcan_reschedule_need == B_TRUE) {
1481*3737Shx147065 				mac_tx_update(GLD3(pcan_p));
1482*3737Shx147065 				pcan_p->pcan_reschedule_need = B_FALSE;
1483*3737Shx147065 			}
1484*3737Shx147065 			PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_TX_CPY);
1485*3737Shx147065 	}
1486*3737Shx147065 	}
1487*3737Shx147065 	if (stat & AN_EV_TX) {
1488*3737Shx147065 		if (pcan_txdone(pcan_p, stat & AN_EV_TX) == 0) {
1489*3737Shx147065 			if (pcan_p->pcan_reschedule_need == B_TRUE) {
1490*3737Shx147065 				mac_tx_update(GLD3(pcan_p));
1491*3737Shx147065 				pcan_p->pcan_reschedule_need = B_FALSE;
1492*3737Shx147065 			}
1493*3737Shx147065 		}
1494*3737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_TX);
1495*3737Shx147065 	}
1496*3737Shx147065 	if (stat & AN_EV_TX_EXC) {
1497*3737Shx147065 		if (pcan_txdone(pcan_p, stat & AN_EV_TX_EXC) == 0) {
1498*3737Shx147065 			if (pcan_p->pcan_reschedule_need == B_TRUE) {
1499*3737Shx147065 				mutex_exit(&pcan_p->pcan_glock);
1500*3737Shx147065 				mac_tx_update(GLD3(pcan_p));
1501*3737Shx147065 				mutex_enter(&pcan_p->pcan_glock);
1502*3737Shx147065 				pcan_p->pcan_reschedule_need = B_FALSE;
1503*3737Shx147065 			}
1504*3737Shx147065 		}
1505*3737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_TX_EXC);
1506*3737Shx147065 	}
1507*3737Shx147065 	if (stat & AN_EV_ALLOC) {
1508*3737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_ALLOC);
1509*3737Shx147065 		PCANDBG((CE_NOTE, "pcan intr: nicmem alloc done\n"));
1510*3737Shx147065 	}
1511*3737Shx147065 	if (stat & AN_EV_MIC) {
1512*3737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_MIC);
1513*3737Shx147065 	}
1514*3737Shx147065 	PCAN_ENABLE_INTR(pcan_p);
1515*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
1516*3737Shx147065 	return (DDI_INTR_CLAIMED);
1517*3737Shx147065 }
1518*3737Shx147065 
1519*3737Shx147065 static uint_t
1520*3737Shx147065 pcan_intr_hi(caddr_t arg)
1521*3737Shx147065 {
1522*3737Shx147065 	uint16_t stat;
1523*3737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
1524*3737Shx147065 
1525*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
1526*3737Shx147065 	if ((pcan_p->pcan_flag & (PCAN_CARD_READY | PCAN_CARD_INTREN)) !=
1527*3737Shx147065 	    (PCAN_CARD_READY | PCAN_CARD_INTREN)) {
1528*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
1529*3737Shx147065 		return (DDI_INTR_UNCLAIMED);
1530*3737Shx147065 	}
1531*3737Shx147065 	PCAN_READ(pcan_p, AN_EVENT_STAT(pcan_p), stat);
1532*3737Shx147065 	PCANDBG((CE_NOTE, "pcan intr(hi): stat=%x pcan_flags=%x\n", stat,
1533*3737Shx147065 	    pcan_p->pcan_flag));
1534*3737Shx147065 
1535*3737Shx147065 	if (!(stat & AN_INTRS(pcan_p)) || stat == AN_EV_ALL) {
1536*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
1537*3737Shx147065 		return (DDI_INTR_UNCLAIMED);
1538*3737Shx147065 	}
1539*3737Shx147065 	/* disable interrupt without ack */
1540*3737Shx147065 	PCAN_WRITE(pcan_p, AN_INT_EN(pcan_p), 0);
1541*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
1542*3737Shx147065 	ddi_trigger_softintr(pcan_p->pcan_softint_id);
1543*3737Shx147065 	return (DDI_INTR_CLAIMED);
1544*3737Shx147065 }
1545*3737Shx147065 
1546*3737Shx147065 /*
1547*3737Shx147065  * retrieve data from pccard
1548*3737Shx147065  */
1549*3737Shx147065 static void
1550*3737Shx147065 pcan_rcv(pcan_maci_t *pcan_p)
1551*3737Shx147065 {
1552*3737Shx147065 	uint16_t id, off, ret, data_len, pkt_stat, frm_ctl;
1553*3737Shx147065 	an_rxfrm_t frm;
1554*3737Shx147065 	struct ieee80211_llc *llc;
1555*3737Shx147065 
1556*3737Shx147065 	mblk_t *mp = allocb(PCAN_NICMEM_SZ, BPRI_MED);
1557*3737Shx147065 	if (!mp) {
1558*3737Shx147065 		cmn_err(CE_WARN, "pcan: failed to alloc rcv buf");
1559*3737Shx147065 		pcan_p->glds_norcvbuf++;
1560*3737Shx147065 		return;
1561*3737Shx147065 	}
1562*3737Shx147065 	ASSERT(mp->b_rptr == mp->b_wptr);
1563*3737Shx147065 
1564*3737Shx147065 	PCAN_READ(pcan_p, AN_RX_FID, id);
1565*3737Shx147065 	if (id == AN_INVALID_FID) {
1566*3737Shx147065 		PCANDBG((CE_NOTE, "pcan rcv: can't get rx_fid\n"));
1567*3737Shx147065 		pcan_p->glds_norcvbuf++;
1568*3737Shx147065 		ret = PCAN_FAIL;
1569*3737Shx147065 		goto done;
1570*3737Shx147065 	}
1571*3737Shx147065 	if (ret = RDCH0(pcan_p, id, 0, (uint16_t *)&frm, sizeof (frm))) {
1572*3737Shx147065 		PCANDBG((CE_NOTE, "pcan rcv: read frm err %x\n", ret));
1573*3737Shx147065 		goto done;
1574*3737Shx147065 	}
1575*3737Shx147065 	off = sizeof (frm);
1576*3737Shx147065 	if (frm.an_rx_status) {
1577*3737Shx147065 		PCANDBG((CE_NOTE, "pcan rcv: err stat %x\n", frm.an_rx_status));
1578*3737Shx147065 		ret = frm.an_rx_status;
1579*3737Shx147065 		goto done;
1580*3737Shx147065 	}
1581*3737Shx147065 	PCANDBG((CE_NOTE, "pcan rcv: payload_len=%x gap_len=%x\n",
1582*3737Shx147065 	    frm.an_rx_payload_len, frm.an_gaplen));
1583*3737Shx147065 	if (frm.an_rx_payload_len > PCAN_NICMEM_SZ ||
1584*3737Shx147065 	    frm.an_gaplen > AN_RXGAP_MAX) {
1585*3737Shx147065 		PCANDBG((CE_NOTE, "pcan rcv: bad len\n"));
1586*3737Shx147065 		ret = PCAN_FAIL;
1587*3737Shx147065 		goto done;
1588*3737Shx147065 	}
1589*3737Shx147065 	if (ret = RDCH0(pcan_p, id, off, &pkt_stat, sizeof (pkt_stat))) {
1590*3737Shx147065 		PCANDBG((CE_NOTE, "pcan rcv: pkt status err %x\n", ret));
1591*3737Shx147065 		ret = PCAN_FAIL;
1592*3737Shx147065 		goto done;
1593*3737Shx147065 	}
1594*3737Shx147065 	off += sizeof (pkt_stat);
1595*3737Shx147065 	if (ret = RDCH0(pcan_p, id, off, &data_len, sizeof (data_len))) {
1596*3737Shx147065 		PCANDBG((CE_NOTE, "pcan rcv: payload len err %x\n", ret));
1597*3737Shx147065 		ret = PCAN_FAIL;
1598*3737Shx147065 		goto done;
1599*3737Shx147065 	}
1600*3737Shx147065 	off += sizeof (data_len);
1601*3737Shx147065 	off += ETHERADDRL << 1;
1602*3737Shx147065 	PCANDBG((CE_NOTE, "pcan rcv: pkt_stat=%x payload_len=%x+c off=%x\n",
1603*3737Shx147065 		pkt_stat, data_len, off));
1604*3737Shx147065 
1605*3737Shx147065 #ifdef DEBUG
1606*3737Shx147065 	if (pcan_debug & PCAN_DBG_RCV) {
1607*3737Shx147065 		int i;
1608*3737Shx147065 		cmn_err(CE_NOTE, "pcan rcv: frm header\n");
1609*3737Shx147065 		for (i = 0; i < sizeof (frm); i++)
1610*3737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
1611*3737Shx147065 			    *((uint8_t *)&frm + i));
1612*3737Shx147065 	}
1613*3737Shx147065 #endif
1614*3737Shx147065 	/*
1615*3737Shx147065 	 * this driver deal with WEP by itself. so plugin always thinks no wep.
1616*3737Shx147065 	 */
1617*3737Shx147065 	frm.an_frame_ctl &= ~(IEEE80211_FC1_WEP << 8);
1618*3737Shx147065 	frm_ctl = frm.an_frame_ctl;
1619*3737Shx147065 	PCAN_SWAP16((uint16_t *)&frm.an_frame_ctl,
1620*3737Shx147065 	    sizeof (struct ieee80211_frame));
1621*3737Shx147065 	/*
1622*3737Shx147065 	 * discard those frames which are not from the AP we connect or
1623*3737Shx147065 	 * without 'ap->sta' direction
1624*3737Shx147065 	 */
1625*3737Shx147065 	if (((pcan_p->an_config.an_opmode == AN_OPMODE_INFR_STATION)) &&
1626*3737Shx147065 	    ((((frm_ctl >> 8) & IEEE80211_FC1_DIR_MASK) !=
1627*3737Shx147065 	    IEEE80211_FC1_DIR_FROMDS) ||
1628*3737Shx147065 	    bcmp(pcan_p->an_status.an_cur_bssid, frm.an_addr2, 6) != 0)) {
1629*3737Shx147065 		ret = PCAN_FAIL;
1630*3737Shx147065 		goto done;
1631*3737Shx147065 	}
1632*3737Shx147065 	bcopy(&frm.an_frame_ctl, mp->b_wptr,
1633*3737Shx147065 	    sizeof (struct ieee80211_frame));
1634*3737Shx147065 	mp->b_wptr += sizeof (struct ieee80211_frame);
1635*3737Shx147065 
1636*3737Shx147065 	/* the plugin need a llc here */
1637*3737Shx147065 	llc = (struct ieee80211_llc *)mp->b_wptr;
1638*3737Shx147065 	llc->illc_dsap = llc->illc_ssap = AN_SNAP_K1;
1639*3737Shx147065 	llc->illc_control = AN_SNAP_CONTROL;
1640*3737Shx147065 	bzero(llc->illc_oc, sizeof (llc->illc_oc));
1641*3737Shx147065 	mp->b_wptr += AN_SNAPHDR_LEN;
1642*3737Shx147065 
1643*3737Shx147065 	/* read in the rest of data */
1644*3737Shx147065 	data_len += data_len & 1;	/* adjust to word boundary */
1645*3737Shx147065 	if (data_len > MBLKSIZE(mp)) {
1646*3737Shx147065 		cmn_err(CE_NOTE, "pcan rcv: data over length%x\n", data_len);
1647*3737Shx147065 		ret = PCAN_FAIL;
1648*3737Shx147065 		goto done;
1649*3737Shx147065 	}
1650*3737Shx147065 
1651*3737Shx147065 	if (ret = RDPKT(pcan_p, id, off, (uint16_t *)mp->b_wptr, data_len)) {
1652*3737Shx147065 		PCANDBG((CE_NOTE, "pcan rcv: err read data %x\n", ret));
1653*3737Shx147065 	}
1654*3737Shx147065 done:
1655*3737Shx147065 	if (ret) {
1656*3737Shx147065 		PCANDBG((CE_NOTE, "pcan rcv: rd data %x\n", ret));
1657*3737Shx147065 		freemsg(mp);
1658*3737Shx147065 		return;
1659*3737Shx147065 	}
1660*3737Shx147065 	mp->b_wptr += data_len;
1661*3737Shx147065 #ifdef DEBUG
1662*3737Shx147065 	if (pcan_debug & PCAN_DBG_RCV) {
1663*3737Shx147065 		int i;
1664*3737Shx147065 		cmn_err(CE_NOTE, "pcan rcv: len=0x%x\n", data_len);
1665*3737Shx147065 		for (i = 0; i < data_len + sizeof (frm); i++)
1666*3737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
1667*3737Shx147065 			    *((uint8_t *)mp->b_rptr + i));
1668*3737Shx147065 	}
1669*3737Shx147065 #endif
1670*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
1671*3737Shx147065 	mac_rx(GLD3(pcan_p), NULL, mp);
1672*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
1673*3737Shx147065 }
1674*3737Shx147065 
1675*3737Shx147065 /*
1676*3737Shx147065  * retrieve data from mini-pci card
1677*3737Shx147065  */
1678*3737Shx147065 static void
1679*3737Shx147065 pcian_rcv(pcan_maci_t *pcan_p)
1680*3737Shx147065 {
1681*3737Shx147065 	struct an_card_rx_desc an_rx_desc;
1682*3737Shx147065 	char *buf;
1683*3737Shx147065 	uint16_t ret = 0, data_len;
1684*3737Shx147065 	int i, j;
1685*3737Shx147065 	struct ieee80211_frame *frm;
1686*3737Shx147065 	struct ieee80211_llc *llc;
1687*3737Shx147065 
1688*3737Shx147065 	mblk_t *mp = allocb(AN_RX_BUFFER_SIZE, BPRI_MED);
1689*3737Shx147065 	if (!mp) {
1690*3737Shx147065 		cmn_err(CE_WARN, "pcan(pci): failed to alloc rcv buf");
1691*3737Shx147065 		pcan_p->glds_norcvbuf++;
1692*3737Shx147065 		return;
1693*3737Shx147065 	}
1694*3737Shx147065 	ASSERT(mp->b_rptr == mp->b_wptr);
1695*3737Shx147065 
1696*3737Shx147065 	for (i = 0; i < sizeof (an_rx_desc) / 4; i++)
1697*3737Shx147065 		PCAN_AUX_GET32(pcan_p, AN_RX_DESC_OFFSET + (i * 4),
1698*3737Shx147065 		    ((uint32_t *)&an_rx_desc)[i]);
1699*3737Shx147065 	if (an_rx_desc.an_done && !an_rx_desc.an_valid) {
1700*3737Shx147065 		buf = pcan_p->pcan_rx[0].dma_virtaddr;
1701*3737Shx147065 		data_len = an_rx_desc.an_len;
1702*3737Shx147065 #ifdef DEBUG
1703*3737Shx147065 		if (pcan_debug & PCAN_DBG_RCV) {
1704*3737Shx147065 			cmn_err(CE_NOTE, "pcan(pci) rcv: data_len=%x",
1705*3737Shx147065 			    data_len);
1706*3737Shx147065 			for (j = 0; j < data_len + 14; j++)
1707*3737Shx147065 				cmn_err(CE_NOTE, "pcan_rcv %d: %x", j,
1708*3737Shx147065 				    *((uint8_t *)buf + j));
1709*3737Shx147065 		}
1710*3737Shx147065 #endif
1711*3737Shx147065 		if (data_len > MBLKSIZE(mp)) {
1712*3737Shx147065 			cmn_err(CE_NOTE, "pcan(pci) rcv: data over length%x\n",
1713*3737Shx147065 			    data_len);
1714*3737Shx147065 			ret = PCAN_FAIL;
1715*3737Shx147065 			goto done;
1716*3737Shx147065 		}
1717*3737Shx147065 		/*
1718*3737Shx147065 		 * minipci card receive an ethernet frame, so assembly a 802.11
1719*3737Shx147065 		 * frame here manually.
1720*3737Shx147065 		 */
1721*3737Shx147065 		frm = (struct ieee80211_frame *)mp->b_wptr;
1722*3737Shx147065 		bzero(frm, sizeof (*frm));
1723*3737Shx147065 		frm->i_fc[0] |= IEEE80211_FC0_TYPE_DATA;
1724*3737Shx147065 		frm->i_fc[1] |= IEEE80211_FC1_DIR_FROMDS;
1725*3737Shx147065 		bcopy(pcan_p->an_status.an_cur_bssid, frm->i_addr2, 6);
1726*3737Shx147065 		bcopy(buf, frm->i_addr1, 6);
1727*3737Shx147065 		bcopy(buf + 6, frm->i_addr3, 6);
1728*3737Shx147065 		mp->b_wptr += sizeof (struct ieee80211_frame);
1729*3737Shx147065 
1730*3737Shx147065 		llc = (struct ieee80211_llc *)mp->b_wptr;
1731*3737Shx147065 		llc->illc_dsap = llc->illc_ssap = AN_SNAP_K1;
1732*3737Shx147065 		llc->illc_control = AN_SNAP_CONTROL;
1733*3737Shx147065 		bzero(llc->illc_oc, sizeof (llc->illc_oc));
1734*3737Shx147065 		mp->b_wptr += AN_SNAPHDR_LEN;
1735*3737Shx147065 
1736*3737Shx147065 		bcopy(buf + 12, mp->b_wptr, data_len);
1737*3737Shx147065 		mp->b_wptr += data_len;
1738*3737Shx147065 #ifdef DEBUG
1739*3737Shx147065 		if (pcan_debug & PCAN_DBG_RCV) {
1740*3737Shx147065 			int i;
1741*3737Shx147065 			cmn_err(CE_NOTE, "pcan(pci) rcv: len=0x%x\n", data_len);
1742*3737Shx147065 			for (i = 0; i < data_len + sizeof (*frm)
1743*3737Shx147065 			    + sizeof (*llc); i++)
1744*3737Shx147065 				cmn_err(CE_NOTE, "%x: %x\n", i,
1745*3737Shx147065 				    *((uint8_t *)mp->b_rptr + i));
1746*3737Shx147065 		}
1747*3737Shx147065 #endif
1748*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
1749*3737Shx147065 		mac_rx(GLD3(pcan_p), NULL, mp);
1750*3737Shx147065 		mutex_enter(&pcan_p->pcan_glock);
1751*3737Shx147065 	}
1752*3737Shx147065 done:
1753*3737Shx147065 	bzero(&an_rx_desc, sizeof (an_rx_desc));
1754*3737Shx147065 	an_rx_desc.an_valid = 1;
1755*3737Shx147065 	an_rx_desc.an_len = AN_RX_BUFFER_SIZE;
1756*3737Shx147065 	an_rx_desc.an_done = 0;
1757*3737Shx147065 	an_rx_desc.an_phys = pcan_p->pcan_rx[0].dma_physaddr;
1758*3737Shx147065 
1759*3737Shx147065 	for (i = 0; i < sizeof (an_rx_desc) / 4; i++)
1760*3737Shx147065 		PCAN_AUX_PUT32(pcan_p, AN_RX_DESC_OFFSET + (i * 4),
1761*3737Shx147065 		    ((uint32_t *)&an_rx_desc)[i]);
1762*3737Shx147065 	if (ret) {
1763*3737Shx147065 		freemsg(mp);
1764*3737Shx147065 	}
1765*3737Shx147065 }
1766*3737Shx147065 
1767*3737Shx147065 /*ARGSUSED*/
1768*3737Shx147065 static uint32_t
1769*3737Shx147065 pcan_txdone(pcan_maci_t *pcan_p, uint16_t err)
1770*3737Shx147065 {
1771*3737Shx147065 	uint16_t fid, i, ring_idx;
1772*3737Shx147065 	uint32_t ret = 0;
1773*3737Shx147065 
1774*3737Shx147065 	PCAN_READ(pcan_p, AN_TX_CMP_FID(pcan_p), fid);
1775*3737Shx147065 	mutex_enter(&pcan_p->pcan_txring.an_tx_lock);
1776*3737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
1777*3737Shx147065 		if (pcan_p->pcan_flag & PCAN_CARD_SEND) {
1778*3737Shx147065 			ring_idx = pcan_p->pcan_txring.an_tx_cons;
1779*3737Shx147065 			pcan_p->pcan_txring.an_tx_cons =
1780*3737Shx147065 			    (ring_idx + 1) % AN_MAX_TX_DESC;
1781*3737Shx147065 			if (pcan_p->pcan_txring.an_tx_prod ==
1782*3737Shx147065 			    pcan_p->pcan_txring.an_tx_cons) {
1783*3737Shx147065 				pcan_p->pcan_flag &= ~PCAN_CARD_SEND;
1784*3737Shx147065 			}
1785*3737Shx147065 		}
1786*3737Shx147065 		ret = 0;
1787*3737Shx147065 	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
1788*3737Shx147065 		for (i = 0; i < AN_TX_RING_CNT; i++) {
1789*3737Shx147065 			if (fid == pcan_p->pcan_txring.an_tx_ring[i]) {
1790*3737Shx147065 				pcan_p->pcan_txring.an_tx_ring[i] = 0;
1791*3737Shx147065 				break;
1792*3737Shx147065 			}
1793*3737Shx147065 		}
1794*3737Shx147065 		pcan_p->pcan_txring.an_tx_cons =
1795*3737Shx147065 		    (pcan_p->pcan_txring.an_tx_cons + 1) & AN_TX_RING_MASK;
1796*3737Shx147065 		ret = (i == AN_TX_RING_CNT ? 1 : 0);
1797*3737Shx147065 	}
1798*3737Shx147065 	mutex_exit(&pcan_p->pcan_txring.an_tx_lock);
1799*3737Shx147065 	return (ret);
1800*3737Shx147065 }
1801*3737Shx147065 
1802*3737Shx147065 /*
1803*3737Shx147065  * delay in which the mutex is not hold.
1804*3737Shx147065  * assuming the mutex has already been hold.
1805*3737Shx147065  */
1806*3737Shx147065 static void
1807*3737Shx147065 pcan_delay(pcan_maci_t *pcan_p, clock_t microsecs)
1808*3737Shx147065 {
1809*3737Shx147065 	ASSERT(mutex_owned(&pcan_p->pcan_glock));
1810*3737Shx147065 
1811*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
1812*3737Shx147065 	delay(drv_usectohz(microsecs));
1813*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
1814*3737Shx147065 }
1815*3737Shx147065 
1816*3737Shx147065 static void
1817*3737Shx147065 pcan_reset_backend(pcan_maci_t *pcan_p, int timeout)
1818*3737Shx147065 {
1819*3737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
1820*3737Shx147065 		(void) pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0);
1821*3737Shx147065 		PCAN_DISABLE_INTR_CLEAR(pcan_p);
1822*3737Shx147065 		(void) pcan_set_cmd(pcan_p, AN_CMD_FW_RESTART, 0);
1823*3737Shx147065 		(void) pcan_set_cmd(pcan_p, AN_CMD_NOOP2, 0);
1824*3737Shx147065 		(void) pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0);
1825*3737Shx147065 	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
1826*3737Shx147065 		(void) pcan_set_cmd0(pcan_p, AN_CMD_DISABLE, 0, 0, 0);
1827*3737Shx147065 		(void) pcan_set_cmd0(pcan_p, AN_CMD_NOOP2, 0, 0, 0);
1828*3737Shx147065 		PCAN_WRITE(pcan_p, AN_COMMAND(pcan_p), AN_CMD_FW_RESTART);
1829*3737Shx147065 		pcan_delay(pcan_p, timeout); /* wait for firmware restart */
1830*3737Shx147065 
1831*3737Shx147065 		(void) pcan_set_cmd(pcan_p, AN_CMD_NOOP, 0);
1832*3737Shx147065 		(void) pcan_set_cmd0(pcan_p, AN_CMD_DISABLE, 0, 0, 0);
1833*3737Shx147065 
1834*3737Shx147065 		PCAN_DISABLE_INTR_CLEAR(pcan_p);
1835*3737Shx147065 	}
1836*3737Shx147065 }
1837*3737Shx147065 
1838*3737Shx147065 /*
1839*3737Shx147065  * set command without the need of ACK.
1840*3737Shx147065  */
1841*3737Shx147065 static uint16_t
1842*3737Shx147065 pcan_set_cmd0(pcan_maci_t *pcan_p, uint16_t cmd, uint16_t p0,
1843*3737Shx147065     uint16_t p1, uint16_t p2)
1844*3737Shx147065 {
1845*3737Shx147065 	int i;
1846*3737Shx147065 	uint16_t stat, r0, r1, r2;
1847*3737Shx147065 
1848*3737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
1849*3737Shx147065 		for (i = 0; i < AN_TIMEOUT; i++) {
1850*3737Shx147065 			PCAN_READ(pcan_p, AN_COMMAND(pcan_p), stat);
1851*3737Shx147065 			if (!(stat & AN_CMD_BUSY))
1852*3737Shx147065 				break;
1853*3737Shx147065 		}
1854*3737Shx147065 		if (i == AN_TIMEOUT) {
1855*3737Shx147065 			PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p),
1856*3737Shx147065 			    AN_EV_CLR_STUCK_BUSY);
1857*3737Shx147065 			PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
1858*3737Shx147065 			drv_usecwait(10);
1859*3737Shx147065 		}
1860*3737Shx147065 		PCAN_WRITE(pcan_p, AN_PARAM0(pcan_p), p0);
1861*3737Shx147065 		PCAN_WRITE(pcan_p, AN_PARAM1(pcan_p), p1);
1862*3737Shx147065 		PCAN_WRITE(pcan_p, AN_PARAM2(pcan_p), p2);
1863*3737Shx147065 	}
1864*3737Shx147065 	PCAN_WRITE(pcan_p, AN_COMMAND(pcan_p), cmd);
1865*3737Shx147065 	for (i = 0; i < AN_TIMEOUT; i++) {
1866*3737Shx147065 		PCAN_READ(pcan_p, AN_EVENT_STAT(pcan_p), stat);
1867*3737Shx147065 		if (stat & AN_EV_CMD)
1868*3737Shx147065 			break;
1869*3737Shx147065 	}
1870*3737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
1871*3737Shx147065 		PCAN_READ(pcan_p, AN_RESP0(pcan_p), r0);
1872*3737Shx147065 		PCAN_READ(pcan_p, AN_RESP1(pcan_p), r1);
1873*3737Shx147065 		PCAN_READ(pcan_p, AN_RESP2(pcan_p), r2);
1874*3737Shx147065 		PCAN_READ(pcan_p, AN_COMMAND(pcan_p), stat);
1875*3737Shx147065 		if (stat & AN_CMD_BUSY)
1876*3737Shx147065 			PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p),
1877*3737Shx147065 			    AN_EV_CLR_STUCK_BUSY);
1878*3737Shx147065 		PCANDBG((CE_NOTE, "pcan set_cmd0: "
1879*3737Shx147065 		    "stat=%x, r0=%x, r1=%x, r2=%x\n",
1880*3737Shx147065 		    stat, r0, r1, r2));
1881*3737Shx147065 	}
1882*3737Shx147065 	PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
1883*3737Shx147065 	return (i == AN_TIMEOUT ? PCAN_TIMEDOUT_ACCESS : PCAN_SUCCESS);
1884*3737Shx147065 }
1885*3737Shx147065 
1886*3737Shx147065 static uint16_t
1887*3737Shx147065 pcan_set_cmd(pcan_maci_t *pcan_p, uint16_t cmd, uint16_t param)
1888*3737Shx147065 {
1889*3737Shx147065 	int i;
1890*3737Shx147065 	uint16_t stat, r0, r1, r2;
1891*3737Shx147065 	uint16_t ret;
1892*3737Shx147065 
1893*3737Shx147065 	if (((cmd == AN_CMD_ENABLE) &&
1894*3737Shx147065 	    ((pcan_p->pcan_flag & PCAN_ENABLED) != 0)) ||
1895*3737Shx147065 	    ((cmd == AN_CMD_DISABLE) &&
1896*3737Shx147065 	    ((pcan_p->pcan_flag & PCAN_ENABLED) == 0)))
1897*3737Shx147065 		return (PCAN_SUCCESS);
1898*3737Shx147065 	for (i = 0; i < AN_TIMEOUT; i++) {
1899*3737Shx147065 		PCAN_READ(pcan_p, AN_COMMAND(pcan_p), stat);
1900*3737Shx147065 		if (!(stat & AN_CMD_BUSY)) {
1901*3737Shx147065 			break;
1902*3737Shx147065 		}
1903*3737Shx147065 	}
1904*3737Shx147065 	if (i == AN_TIMEOUT) {
1905*3737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CLR_STUCK_BUSY);
1906*3737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
1907*3737Shx147065 		drv_usecwait(10);
1908*3737Shx147065 	}
1909*3737Shx147065 
1910*3737Shx147065 	PCAN_WRITE(pcan_p, AN_PARAM0(pcan_p), param);
1911*3737Shx147065 	PCAN_WRITE(pcan_p, AN_PARAM1(pcan_p), 0);
1912*3737Shx147065 	PCAN_WRITE(pcan_p, AN_PARAM2(pcan_p), 0);
1913*3737Shx147065 	PCAN_WRITE(pcan_p, AN_COMMAND(pcan_p), cmd);
1914*3737Shx147065 
1915*3737Shx147065 	for (i = 0; i < AN_TIMEOUT; i++) {
1916*3737Shx147065 		PCAN_READ(pcan_p, AN_EVENT_STAT(pcan_p), stat);
1917*3737Shx147065 		if (stat & AN_EV_CMD) {
1918*3737Shx147065 			break;
1919*3737Shx147065 		}
1920*3737Shx147065 		PCAN_READ(pcan_p, AN_COMMAND(pcan_p), stat);
1921*3737Shx147065 		if (stat == cmd)
1922*3737Shx147065 			PCAN_WRITE(pcan_p, AN_COMMAND(pcan_p), cmd);
1923*3737Shx147065 	}
1924*3737Shx147065 	if (i == AN_TIMEOUT) {
1925*3737Shx147065 		if (cmd == AN_CMD_FW_RESTART) {
1926*3737Shx147065 			PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
1927*3737Shx147065 			return (PCAN_SUCCESS);
1928*3737Shx147065 		}
1929*3737Shx147065 #ifdef DEBUG
1930*3737Shx147065 		if (pcan_debug & PCAN_DBG_CMD) {
1931*3737Shx147065 			cmn_err(CE_WARN, "pcan set_cmd: %x timeout stat=%x\n",
1932*3737Shx147065 			    cmd, stat);
1933*3737Shx147065 		}
1934*3737Shx147065 #endif
1935*3737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
1936*3737Shx147065 		return (PCAN_TIMEDOUT_CMD);
1937*3737Shx147065 	}
1938*3737Shx147065 
1939*3737Shx147065 	for (i = 0; i < AN_TIMEOUT; i++) {
1940*3737Shx147065 		PCAN_READ(pcan_p, AN_STATUS(pcan_p), stat);
1941*3737Shx147065 		PCAN_READ(pcan_p, AN_RESP0(pcan_p), r0);
1942*3737Shx147065 		PCAN_READ(pcan_p, AN_RESP1(pcan_p), r1);
1943*3737Shx147065 		PCAN_READ(pcan_p, AN_RESP2(pcan_p), r2);
1944*3737Shx147065 		if ((stat & AN_STAT_CMD_CODE) == (cmd & AN_STAT_CMD_CODE))
1945*3737Shx147065 			break;
1946*3737Shx147065 	}
1947*3737Shx147065 	if (cmd == AN_CMD_FW_RESTART) {
1948*3737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
1949*3737Shx147065 		return (PCAN_SUCCESS);
1950*3737Shx147065 	}
1951*3737Shx147065 	if (i == AN_TIMEOUT) {
1952*3737Shx147065 #ifdef DEBUG
1953*3737Shx147065 		if (pcan_debug & PCAN_DBG_CMD) {
1954*3737Shx147065 			cmn_err(CE_WARN, "pcan set_cmd<%x,%x>: timeout "
1955*3737Shx147065 			    "%x,%x,%x,%x\n", cmd, param, stat, r0, r1, r2);
1956*3737Shx147065 		}
1957*3737Shx147065 #endif
1958*3737Shx147065 		ret = PCAN_TIMEDOUT_ACCESS;
1959*3737Shx147065 	} else {
1960*3737Shx147065 		if (stat & AN_STAT_CMD_RESULT) {
1961*3737Shx147065 #ifdef DEBUG
1962*3737Shx147065 			if (pcan_debug & PCAN_DBG_CMD) {
1963*3737Shx147065 				cmn_err(CE_WARN, "pcan set_cmd<%x,%x>: failed "
1964*3737Shx147065 				    "%x,%x,%x,%x\n",
1965*3737Shx147065 				    cmd, param, stat, r0, r1, r2);
1966*3737Shx147065 			}
1967*3737Shx147065 #endif
1968*3737Shx147065 			ret = PCAN_TIMEDOUT_ACCESS;
1969*3737Shx147065 		} else {
1970*3737Shx147065 			ret = PCAN_SUCCESS;
1971*3737Shx147065 		}
1972*3737Shx147065 	}
1973*3737Shx147065 	PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
1974*3737Shx147065 	PCAN_READ(pcan_p, AN_COMMAND(pcan_p), stat);
1975*3737Shx147065 	if (stat & AN_CMD_BUSY)
1976*3737Shx147065 		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CLR_STUCK_BUSY);
1977*3737Shx147065 	if (ret == PCAN_SUCCESS) {
1978*3737Shx147065 		if (cmd == AN_CMD_ENABLE)
1979*3737Shx147065 			pcan_p->pcan_flag |= PCAN_ENABLED;
1980*3737Shx147065 		if (cmd == AN_CMD_DISABLE)
1981*3737Shx147065 			pcan_p->pcan_flag &= (~PCAN_ENABLED);
1982*3737Shx147065 	}
1983*3737Shx147065 	return (ret);
1984*3737Shx147065 }
1985*3737Shx147065 
1986*3737Shx147065 static uint16_t
1987*3737Shx147065 pcan_set_ch(pcan_maci_t *pcan_p, uint16_t type, uint16_t off, uint16_t channel)
1988*3737Shx147065 {
1989*3737Shx147065 	int i;
1990*3737Shx147065 	uint16_t stat, select, offset;
1991*3737Shx147065 
1992*3737Shx147065 	if (channel) {
1993*3737Shx147065 		select = AN_SEL1;
1994*3737Shx147065 		offset = AN_OFF1;
1995*3737Shx147065 	} else {
1996*3737Shx147065 		select = AN_SEL0;
1997*3737Shx147065 		offset = AN_OFF0;
1998*3737Shx147065 	}
1999*3737Shx147065 	PCAN_WRITE(pcan_p, select, type);
2000*3737Shx147065 	PCAN_WRITE(pcan_p, offset, off);
2001*3737Shx147065 	for (i = 0; i < AN_TIMEOUT; i++) {
2002*3737Shx147065 		PCAN_READ(pcan_p, offset, stat);
2003*3737Shx147065 		if (!(stat & (AN_OFF_BUSY|AN_OFF_ERR)))
2004*3737Shx147065 			break;
2005*3737Shx147065 	}
2006*3737Shx147065 	if (stat & (AN_OFF_BUSY|AN_OFF_ERR)) { /* time out */
2007*3737Shx147065 		PCANDBG((CE_WARN, "pcan: set_ch%d %x %x TO %x\n",
2008*3737Shx147065 		    channel, type, off, stat));
2009*3737Shx147065 		return (PCAN_TIMEDOUT_TARGET);
2010*3737Shx147065 	}
2011*3737Shx147065 	return (PCAN_SUCCESS);
2012*3737Shx147065 }
2013*3737Shx147065 
2014*3737Shx147065 static uint16_t
2015*3737Shx147065 pcan_get_ltv(pcan_maci_t *pcan_p, uint16_t len, uint16_t type, uint16_t *val_p)
2016*3737Shx147065 {
2017*3737Shx147065 	uint16_t stat;
2018*3737Shx147065 
2019*3737Shx147065 	PCANDBG((CE_NOTE, "pcan: get_ltv(%p,%x,%x,%p)\n",
2020*3737Shx147065 		(void *)pcan_p, len, type, (void *)val_p));
2021*3737Shx147065 	ASSERT(!(len & 1));
2022*3737Shx147065 
2023*3737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
2024*3737Shx147065 		uint32_t i;
2025*3737Shx147065 		struct an_card_rid_desc an_rid_desc;
2026*3737Shx147065 		struct an_ltv_gen *an_ltv;
2027*3737Shx147065 		if (!pcan_p->pcan_cmd.dma_virtaddr)
2028*3737Shx147065 			return (EIO);
2029*3737Shx147065 		an_rid_desc.an_valid = 1;
2030*3737Shx147065 		an_rid_desc.an_len = AN_RID_BUFFER_SIZE;
2031*3737Shx147065 		an_rid_desc.an_rid = 0;
2032*3737Shx147065 		an_rid_desc.an_phys = pcan_p->pcan_cmd.dma_physaddr;
2033*3737Shx147065 		bzero(pcan_p->pcan_cmd.dma_virtaddr, AN_RID_BUFFER_SIZE);
2034*3737Shx147065 
2035*3737Shx147065 		for (i = 0; i < sizeof (an_rid_desc) / 4; i++)
2036*3737Shx147065 			PCAN_AUX_PUT32(pcan_p, AN_HOST_DESC_OFFSET + i * 4,
2037*3737Shx147065 			    ((uint32_t *)&an_rid_desc)[i]);
2038*3737Shx147065 
2039*3737Shx147065 		if (pcan_set_cmd0(pcan_p, AN_CMD_ACCESS |
2040*3737Shx147065 		    AN_ACCESS_READ, type, 0, 0)) {
2041*3737Shx147065 			cmn_err(CE_WARN, "pcan get_ltv: set cmd error");
2042*3737Shx147065 			return (EIO);
2043*3737Shx147065 		}
2044*3737Shx147065 
2045*3737Shx147065 		an_ltv = (struct an_ltv_gen *)pcan_p->pcan_cmd.dma_virtaddr;
2046*3737Shx147065 #ifdef DEBUG
2047*3737Shx147065 		if (pcan_debug & PCAN_DBG_INFO) {
2048*3737Shx147065 			cmn_err(CE_NOTE, "pcan get_ltv: type=%x,"
2049*3737Shx147065 			    "expected len=%d," "actual len=%d",
2050*3737Shx147065 			    type, len, an_ltv->an_len);
2051*3737Shx147065 			for (i = 0; i < an_ltv->an_len; i++)
2052*3737Shx147065 				cmn_err(CE_NOTE, "%d: %x", i,
2053*3737Shx147065 				    *(((uint8_t *)an_ltv) + i));
2054*3737Shx147065 		}
2055*3737Shx147065 #endif
2056*3737Shx147065 		if (an_ltv->an_len != len) {
2057*3737Shx147065 			PCANDBG((CE_WARN, "pcan get_ltv: rid=%x expected len=%d"
2058*3737Shx147065 			    "actual: len=%d", type,
2059*3737Shx147065 			    len, an_ltv->an_len));
2060*3737Shx147065 			/* return (EIO); */
2061*3737Shx147065 		}
2062*3737Shx147065 		bcopy(an_ltv, val_p, len);
2063*3737Shx147065 	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
2064*3737Shx147065 		len >>= 1;	/* convert bytes to 16-bit words */
2065*3737Shx147065 
2066*3737Shx147065 		/* 1. select read mode */
2067*3737Shx147065 		if (stat = pcan_set_cmd(pcan_p, AN_CMD_ACCESS |
2068*3737Shx147065 		    AN_ACCESS_READ, type))
2069*3737Shx147065 			return (stat);
2070*3737Shx147065 
2071*3737Shx147065 		/* 2. select Buffer Access Path (channel) 1 for PIO */
2072*3737Shx147065 		if (stat = pcan_set_ch(pcan_p, type, 0, 1))
2073*3737Shx147065 			return (stat);
2074*3737Shx147065 
2075*3737Shx147065 		/* 3. read length */
2076*3737Shx147065 		PCAN_READ(pcan_p, AN_DATA1, stat);
2077*3737Shx147065 		*val_p++ = stat;
2078*3737Shx147065 		if (stat != (len << 1)) {
2079*3737Shx147065 			PCANDBG((CE_NOTE, "pcan get_ltv[%x]:expect %x,"
2080*3737Shx147065 			    "got %x\n", type, (len + 1) << 1, stat));
2081*3737Shx147065 			stat = (stat >> 1) - 1;
2082*3737Shx147065 			len = MIN(stat, len);
2083*3737Shx147065 		}
2084*3737Shx147065 		/* 4. read value */
2085*3737Shx147065 		for (stat = 0; stat < len - 1; stat++, val_p++) {
2086*3737Shx147065 			PCAN_READ_P(pcan_p, AN_DATA1, val_p, 1);
2087*3737Shx147065 		}
2088*3737Shx147065 	}
2089*3737Shx147065 	return (PCAN_SUCCESS);
2090*3737Shx147065 }
2091*3737Shx147065 
2092*3737Shx147065 static uint16_t
2093*3737Shx147065 pcan_put_ltv(pcan_maci_t *pcan_p, uint16_t len, uint16_t type, uint16_t *val_p)
2094*3737Shx147065 {
2095*3737Shx147065 	uint16_t stat;
2096*3737Shx147065 	int i;
2097*3737Shx147065 
2098*3737Shx147065 	ASSERT(!(len & 1));
2099*3737Shx147065 
2100*3737Shx147065 	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
2101*3737Shx147065 		struct an_card_rid_desc an_rid_desc;
2102*3737Shx147065 
2103*3737Shx147065 		for (i = 0; i < AN_TIMEOUT; i++) {
2104*3737Shx147065 			PCAN_READ(pcan_p, AN_COMMAND(pcan_p), stat);
2105*3737Shx147065 			if (!(stat & AN_CMD_BUSY)) {
2106*3737Shx147065 				break;
2107*3737Shx147065 			}
2108*3737Shx147065 		}
2109*3737Shx147065 		if (i == AN_TIMEOUT) {
2110*3737Shx147065 			cmn_err(CE_WARN, "pcan put_ltv: busy");
2111*3737Shx147065 		}
2112*3737Shx147065 
2113*3737Shx147065 		an_rid_desc.an_valid = 1;
2114*3737Shx147065 		an_rid_desc.an_len = len;
2115*3737Shx147065 		an_rid_desc.an_rid = type;
2116*3737Shx147065 		an_rid_desc.an_phys = pcan_p->pcan_cmd.dma_physaddr;
2117*3737Shx147065 
2118*3737Shx147065 		bcopy(val_p, pcan_p->pcan_cmd.dma_virtaddr,
2119*3737Shx147065 		    an_rid_desc.an_len);
2120*3737Shx147065 
2121*3737Shx147065 		for (i = 0; i < sizeof (an_rid_desc) / 4; i++)
2122*3737Shx147065 			PCAN_AUX_PUT32(pcan_p, AN_HOST_DESC_OFFSET + i * 4,
2123*3737Shx147065 			    ((uint32_t *)&an_rid_desc)[i]);
2124*3737Shx147065 		pcan_delay(pcan_p, 100000);
2125*3737Shx147065 		stat = pcan_set_cmd0(pcan_p, AN_CMD_ACCESS |
2126*3737Shx147065 		    AN_ACCESS_WRITE, type, 0, 0);
2127*3737Shx147065 		pcan_delay(pcan_p, 100000);
2128*3737Shx147065 		return (stat);
2129*3737Shx147065 	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
2130*3737Shx147065 		/* 0. select read mode first */
2131*3737Shx147065 		if (stat = pcan_set_cmd(pcan_p, AN_CMD_ACCESS |
2132*3737Shx147065 		    AN_ACCESS_READ, type))
2133*3737Shx147065 			return (stat);
2134*3737Shx147065 
2135*3737Shx147065 		/* 1. select Buffer Access Path (channel) 1 for PIO */
2136*3737Shx147065 		if (stat = pcan_set_ch(pcan_p, type, 0, 1))
2137*3737Shx147065 			return (stat);
2138*3737Shx147065 
2139*3737Shx147065 		/* 2. write length */
2140*3737Shx147065 		len >>= 1;		/* convert bytes to 16-bit words */
2141*3737Shx147065 		stat = len;
2142*3737Shx147065 		PCAN_WRITE(pcan_p, AN_DATA1, stat);
2143*3737Shx147065 
2144*3737Shx147065 		/* 3. write value */
2145*3737Shx147065 		val_p++;
2146*3737Shx147065 		for (stat = 0; stat < len-1; stat++, val_p++) {
2147*3737Shx147065 			PCAN_WRITE_P(pcan_p, AN_DATA1, val_p, 1);
2148*3737Shx147065 		}
2149*3737Shx147065 
2150*3737Shx147065 		/* 4. select write mode */
2151*3737Shx147065 		return (pcan_set_cmd(pcan_p, AN_CMD_ACCESS |
2152*3737Shx147065 		    AN_ACCESS_WRITE, type));
2153*3737Shx147065 	}
2154*3737Shx147065 	return (PCAN_FAIL);
2155*3737Shx147065 }
2156*3737Shx147065 
2157*3737Shx147065 /*ARGSUSED*/
2158*3737Shx147065 static uint16_t
2159*3737Shx147065 pcan_rdch0(pcan_maci_t *pcan_p, uint16_t type, uint16_t off, uint16_t *buf_p,
2160*3737Shx147065 	int len, int order)
2161*3737Shx147065 {
2162*3737Shx147065 	ASSERT(!(len & 1));
2163*3737Shx147065 
2164*3737Shx147065 	if (pcan_set_ch(pcan_p, type, off, 0) != PCAN_SUCCESS)
2165*3737Shx147065 		return (PCAN_FAIL);
2166*3737Shx147065 	len >>= 1;
2167*3737Shx147065 	for (off = 0; off < len; off++, buf_p++) {
2168*3737Shx147065 		PCAN_READ_P(pcan_p, AN_DATA0, buf_p, order);
2169*3737Shx147065 	}
2170*3737Shx147065 	return (PCAN_SUCCESS);
2171*3737Shx147065 }
2172*3737Shx147065 
2173*3737Shx147065 /*ARGSUSED*/
2174*3737Shx147065 static uint16_t
2175*3737Shx147065 pcan_wrch1(pcan_maci_t *pcan_p, uint16_t type, uint16_t off, uint16_t *buf_p,
2176*3737Shx147065 	int len, int order)
2177*3737Shx147065 {
2178*3737Shx147065 	ASSERT(!(len & 1));
2179*3737Shx147065 
2180*3737Shx147065 	if (pcan_set_ch(pcan_p, type, off, 1) != PCAN_SUCCESS)
2181*3737Shx147065 		return (PCAN_FAIL);
2182*3737Shx147065 	len >>= 1;
2183*3737Shx147065 	for (off = 0; off < len; off++, buf_p++) {
2184*3737Shx147065 		PCAN_WRITE_P(pcan_p, AN_DATA1, buf_p, order);
2185*3737Shx147065 	}
2186*3737Shx147065 	return (PCAN_SUCCESS);
2187*3737Shx147065 }
2188*3737Shx147065 
2189*3737Shx147065 static uint16_t
2190*3737Shx147065 pcan_status_ltv(int rw, pcan_maci_t *pcan_p, struct an_ltv_status *status_p)
2191*3737Shx147065 {
2192*3737Shx147065 	uint16_t ret, len;
2193*3737Shx147065 
2194*3737Shx147065 	if (rw != PCAN_READ_LTV) {
2195*3737Shx147065 		cmn_err(CE_WARN, "pcan status_ltv: unsupported op %x", rw);
2196*3737Shx147065 		return (PCAN_FAIL);
2197*3737Shx147065 	}
2198*3737Shx147065 	if (ret = pcan_get_ltv(pcan_p, sizeof (*status_p), AN_RID_STATUS,
2199*3737Shx147065 	    (uint16_t *)status_p))
2200*3737Shx147065 		return (ret);
2201*3737Shx147065 
2202*3737Shx147065 	PCAN_SWAP16_BUF(status_p->an_macaddr);
2203*3737Shx147065 	PCAN_SWAP16_BUF(status_p->an_ssid);
2204*3737Shx147065 	len = min(status_p->an_ssidlen, 31);
2205*3737Shx147065 	status_p->an_ssid[len] = '\0';
2206*3737Shx147065 	PCAN_SWAP16_BUF(status_p->an_ap_name);
2207*3737Shx147065 	PCAN_SWAP16_BUF(status_p->an_cur_bssid);
2208*3737Shx147065 	PCAN_SWAP16_BUF(status_p->an_prev_bssid1);
2209*3737Shx147065 	PCAN_SWAP16_BUF(status_p->an_prev_bssid2);
2210*3737Shx147065 	PCAN_SWAP16_BUF(status_p->an_prev_bssid3);
2211*3737Shx147065 	PCAN_SWAP16_BUF(status_p->an_ap_ip_address);
2212*3737Shx147065 	PCAN_SWAP16_BUF(status_p->an_carrier);
2213*3737Shx147065 	return (PCAN_SUCCESS);
2214*3737Shx147065 }
2215*3737Shx147065 
2216*3737Shx147065 static uint16_t
2217*3737Shx147065 pcan_cfg_ltv(int rw, pcan_maci_t *pcan_p, struct an_ltv_genconfig *cfg_p)
2218*3737Shx147065 {
2219*3737Shx147065 	uint16_t ret;
2220*3737Shx147065 	uint16_t rid = cfg_p == &pcan_p->an_config ?
2221*3737Shx147065 	    AN_RID_GENCONFIG : AN_RID_ACTUALCFG;
2222*3737Shx147065 
2223*3737Shx147065 	if (rw == PCAN_READ_LTV) {
2224*3737Shx147065 		if (ret = pcan_get_ltv(pcan_p, sizeof (*cfg_p), rid,
2225*3737Shx147065 		    (uint16_t *)cfg_p))
2226*3737Shx147065 			return (ret);
2227*3737Shx147065 		goto done;
2228*3737Shx147065 	}
2229*3737Shx147065 	PCAN_SWAP16_BUF(cfg_p->an_macaddr);
2230*3737Shx147065 	PCAN_SWAP16_BUF(cfg_p->an_rates);
2231*3737Shx147065 	if (ret = pcan_put_ltv(pcan_p, sizeof (*cfg_p),
2232*3737Shx147065 	    rid, (uint16_t *)cfg_p))
2233*3737Shx147065 		return (ret);
2234*3737Shx147065 done:
2235*3737Shx147065 	PCAN_SWAP16_BUF(cfg_p->an_macaddr);
2236*3737Shx147065 	PCAN_SWAP16_BUF(cfg_p->an_rates);
2237*3737Shx147065 	return (ret);
2238*3737Shx147065 }
2239*3737Shx147065 
2240*3737Shx147065 static uint16_t
2241*3737Shx147065 pcan_cap_ltv(int rw, pcan_maci_t *pcan_p)
2242*3737Shx147065 {
2243*3737Shx147065 	uint16_t ret;
2244*3737Shx147065 
2245*3737Shx147065 	if (rw != PCAN_READ_LTV) {
2246*3737Shx147065 		cmn_err(CE_WARN, "pcan cap_ltv: unsupported op %x", rw);
2247*3737Shx147065 		return (PCAN_FAIL);
2248*3737Shx147065 	}
2249*3737Shx147065 	if (ret = pcan_get_ltv(pcan_p, sizeof (struct an_ltv_caps),
2250*3737Shx147065 		AN_RID_CAPABILITIES, (uint16_t *)&pcan_p->an_caps))
2251*3737Shx147065 		return (ret);
2252*3737Shx147065 
2253*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_caps.an_oui);
2254*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_caps.an_manufname);
2255*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_caps.an_prodname);
2256*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_caps.an_prodvers);
2257*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_caps.an_oemaddr);
2258*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_caps.an_aironetaddr);
2259*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_caps.an_callid);
2260*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_caps.an_supported_rates);
2261*3737Shx147065 	return (PCAN_SUCCESS);
2262*3737Shx147065 }
2263*3737Shx147065 
2264*3737Shx147065 static uint16_t
2265*3737Shx147065 pcan_ssid_ltv(int rw, pcan_maci_t *pcan_p)
2266*3737Shx147065 {
2267*3737Shx147065 	uint16_t ret;
2268*3737Shx147065 
2269*3737Shx147065 	if (rw == PCAN_READ_LTV) {
2270*3737Shx147065 		if (ret = pcan_get_ltv(pcan_p, sizeof (struct an_ltv_ssidlist),
2271*3737Shx147065 		    AN_RID_SSIDLIST, (uint16_t *)&pcan_p->an_ssidlist))
2272*3737Shx147065 			return (ret);
2273*3737Shx147065 		goto done;
2274*3737Shx147065 	}
2275*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_ssidlist.an_ssid1);
2276*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_ssidlist.an_ssid2);
2277*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_ssidlist.an_ssid3);
2278*3737Shx147065 	if (ret = pcan_put_ltv(pcan_p, sizeof (struct an_ltv_ssidlist),
2279*3737Shx147065 	    AN_RID_SSIDLIST, (uint16_t *)&pcan_p->an_ssidlist))
2280*3737Shx147065 		return (ret);
2281*3737Shx147065 done:
2282*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_ssidlist.an_ssid1);
2283*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_ssidlist.an_ssid2);
2284*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_ssidlist.an_ssid3);
2285*3737Shx147065 	return (ret);
2286*3737Shx147065 }
2287*3737Shx147065 
2288*3737Shx147065 static uint16_t
2289*3737Shx147065 pcan_aplist_ltv(int rw, pcan_maci_t *pcan_p)
2290*3737Shx147065 {
2291*3737Shx147065 	uint16_t ret;
2292*3737Shx147065 
2293*3737Shx147065 	if (rw == PCAN_READ_LTV) {
2294*3737Shx147065 		if (ret = pcan_get_ltv(pcan_p, sizeof (struct an_ltv_aplist),
2295*3737Shx147065 		    AN_RID_APLIST, (uint16_t *)&pcan_p->an_aplist))
2296*3737Shx147065 			return (ret);
2297*3737Shx147065 		goto done;
2298*3737Shx147065 	}
2299*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap1);
2300*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap2);
2301*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap3);
2302*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap4);
2303*3737Shx147065 	if (ret = pcan_put_ltv(pcan_p, sizeof (struct an_ltv_aplist),
2304*3737Shx147065 	    AN_RID_APLIST, (uint16_t *)&pcan_p->an_aplist))
2305*3737Shx147065 		return (ret);
2306*3737Shx147065 done:
2307*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap1);
2308*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap2);
2309*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap3);
2310*3737Shx147065 	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap4);
2311*3737Shx147065 	return (ret);
2312*3737Shx147065 }
2313*3737Shx147065 
2314*3737Shx147065 static uint16_t
2315*3737Shx147065 pcan_scanresult_ltv(int rw, pcan_maci_t *pcan_p, uint16_t type,
2316*3737Shx147065     struct an_ltv_scanresult *scanresult_p)
2317*3737Shx147065 {
2318*3737Shx147065 	uint16_t ret, len;
2319*3737Shx147065 
2320*3737Shx147065 	if (rw != PCAN_READ_LTV) {
2321*3737Shx147065 		cmn_err(CE_WARN, "pcan scan_ltv: readonly rid %x\n", type);
2322*3737Shx147065 		return (PCAN_FAIL);
2323*3737Shx147065 	}
2324*3737Shx147065 	if (ret = pcan_get_ltv(pcan_p, sizeof (struct an_ltv_scanresult),
2325*3737Shx147065 	    type, (uint16_t *)scanresult_p))
2326*3737Shx147065 		return (ret);
2327*3737Shx147065 	PCAN_SWAP16_BUF(scanresult_p->an_bssid);
2328*3737Shx147065 	PCAN_SWAP16_BUF(scanresult_p->an_ssid);
2329*3737Shx147065 	len = min(scanresult_p->an_ssidlen, 31);
2330*3737Shx147065 	scanresult_p->an_ssid[len] = '\0';
2331*3737Shx147065 	PCAN_SWAP16_BUF(scanresult_p->an_rates);
2332*3737Shx147065 	return (PCAN_SUCCESS);
2333*3737Shx147065 }
2334*3737Shx147065 
2335*3737Shx147065 static uint16_t
2336*3737Shx147065 pcan_one_wepkey(int rw, pcan_maci_t *pcan_p, struct an_ltv_wepkey *wkp,
2337*3737Shx147065     uint16_t rid)
2338*3737Shx147065 {
2339*3737Shx147065 	uint16_t ret;
2340*3737Shx147065 
2341*3737Shx147065 	if (rw == PCAN_READ_LTV) {
2342*3737Shx147065 		if (ret = pcan_get_ltv(pcan_p, sizeof (struct an_ltv_wepkey),
2343*3737Shx147065 		    rid, (uint16_t *)wkp)) {
2344*3737Shx147065 			return (ret);
2345*3737Shx147065 		}
2346*3737Shx147065 		goto done;
2347*3737Shx147065 	}
2348*3737Shx147065 	PCAN_SWAP16_BUF(wkp->an_macaddr);
2349*3737Shx147065 	PCAN_SWAP16_BUF(wkp->an_key);
2350*3737Shx147065 	if (ret = pcan_put_ltv(pcan_p, sizeof (struct an_ltv_wepkey),
2351*3737Shx147065 	    rid, (uint16_t *)wkp))
2352*3737Shx147065 		return (ret);
2353*3737Shx147065 done:
2354*3737Shx147065 	PCAN_SWAP16_BUF(wkp->an_macaddr);
2355*3737Shx147065 	PCAN_SWAP16_BUF(wkp->an_key);
2356*3737Shx147065 	return (ret);
2357*3737Shx147065 }
2358*3737Shx147065 
2359*3737Shx147065 static uint16_t
2360*3737Shx147065 pcan_wepkey_ltv(int rw, pcan_maci_t *pcan_p)
2361*3737Shx147065 {
2362*3737Shx147065 	uint16_t ret, i;
2363*3737Shx147065 	struct an_ltv_wepkey wk;
2364*3737Shx147065 
2365*3737Shx147065 	if (rw == PCAN_READ_LTV) {
2366*3737Shx147065 		uint16_t rid = AN_RID_WEPKEY2;
2367*3737Shx147065 
2368*3737Shx147065 		if (ret = pcan_one_wepkey(rw, pcan_p, &wk, rid))
2369*3737Shx147065 			return (ret);
2370*3737Shx147065 		for (i = 0; i < 5; i++) {
2371*3737Shx147065 			if (wk.an_index < 4)
2372*3737Shx147065 				pcan_p->an_wepkey[wk.an_index] = wk;
2373*3737Shx147065 			else if (wk.an_index == 0xffff)
2374*3737Shx147065 				pcan_p->an_cur_wepkey = wk.an_macaddr[0];
2375*3737Shx147065 			rid = AN_RID_WEPKEY;
2376*3737Shx147065 		}
2377*3737Shx147065 		return (PCAN_SUCCESS);
2378*3737Shx147065 	}
2379*3737Shx147065 	for (i = 0; i < MAX_NWEPKEYS; i++) {
2380*3737Shx147065 		if (pcan_p->an_wepkey[i].an_index == i) {
2381*3737Shx147065 			if (ret = pcan_one_wepkey(rw, pcan_p,
2382*3737Shx147065 			    &pcan_p->an_wepkey[i], AN_RID_WEPKEY2))
2383*3737Shx147065 				return (ret);
2384*3737Shx147065 		}
2385*3737Shx147065 	}
2386*3737Shx147065 	/* Now set the default key */
2387*3737Shx147065 	(void) memset(&wk, 0, sizeof (wk));
2388*3737Shx147065 	wk.an_index = 0xffff;
2389*3737Shx147065 	wk.an_macaddr[0] = pcan_p->an_cur_wepkey;
2390*3737Shx147065 	ret = pcan_one_wepkey(rw, pcan_p, &wk, AN_RID_WEPKEY2);
2391*3737Shx147065 	return (ret);
2392*3737Shx147065 }
2393*3737Shx147065 
2394*3737Shx147065 static uint16_t
2395*3737Shx147065 pcan_alloc_nicmem(pcan_maci_t *pcan_p, uint16_t len, uint16_t *id_p)
2396*3737Shx147065 {
2397*3737Shx147065 	int i;
2398*3737Shx147065 	uint16_t stat;
2399*3737Shx147065 
2400*3737Shx147065 	len = ((len + 1) >> 1) << 1;	/* round up to 16-bit boundary */
2401*3737Shx147065 
2402*3737Shx147065 	if (stat = pcan_set_cmd(pcan_p, AN_CMD_ALLOC_MEM, len))
2403*3737Shx147065 		return (stat);
2404*3737Shx147065 	for (i = 0; !(stat & AN_EV_ALLOC) && (i < AN_TIMEOUT); i++) {
2405*3737Shx147065 		PCAN_READ(pcan_p, AN_EVENT_STAT(pcan_p), stat);
2406*3737Shx147065 	}
2407*3737Shx147065 	if (!(stat & AN_EV_ALLOC))
2408*3737Shx147065 		return (PCAN_TIMEDOUT_ALLOC);
2409*3737Shx147065 	PCAN_READ(pcan_p, AN_ALLOC_FID, stat);
2410*3737Shx147065 	PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_ALLOC);
2411*3737Shx147065 	*id_p = stat;
2412*3737Shx147065 
2413*3737Shx147065 	/* zero fill the allocated NIC mem - sort of pcan_fill_ch0 */
2414*3737Shx147065 	(void) pcan_set_ch(pcan_p, stat, 0, 0);
2415*3737Shx147065 	for (len >>= 1, stat = 0; stat < len; stat++) {
2416*3737Shx147065 		PCAN_WRITE(pcan_p, AN_DATA0, 0);
2417*3737Shx147065 	}
2418*3737Shx147065 	return (PCAN_SUCCESS);
2419*3737Shx147065 }
2420*3737Shx147065 
2421*3737Shx147065 static void
2422*3737Shx147065 pcan_stop_rx_dma(pcan_maci_t *pcan_p)
2423*3737Shx147065 {
2424*3737Shx147065 	int i, j;
2425*3737Shx147065 	struct an_card_rx_desc  an_rx_desc;
2426*3737Shx147065 
2427*3737Shx147065 	for (i = 0; i < AN_MAX_RX_DESC; i++) {
2428*3737Shx147065 		bzero(&an_rx_desc, sizeof (an_rx_desc));
2429*3737Shx147065 		an_rx_desc.an_valid = 0;
2430*3737Shx147065 		an_rx_desc.an_len = AN_RX_BUFFER_SIZE;
2431*3737Shx147065 		an_rx_desc.an_done = 1;
2432*3737Shx147065 		an_rx_desc.an_phys = pcan_p->pcan_rx[i].dma_physaddr;
2433*3737Shx147065 		for (j = 0; j < sizeof (an_rx_desc) / 4; j++)
2434*3737Shx147065 			PCAN_AUX_PUT32(pcan_p, AN_RX_DESC_OFFSET
2435*3737Shx147065 			    + (i * sizeof (an_rx_desc))
2436*3737Shx147065 			    + (j * 4), ((uint32_t *)&an_rx_desc)[j]);
2437*3737Shx147065 	}
2438*3737Shx147065 }
2439*3737Shx147065 
2440*3737Shx147065 static int
2441*3737Shx147065 pcan_init_dma_desc(pcan_maci_t *pcan_p)
2442*3737Shx147065 {
2443*3737Shx147065 	int i, j;
2444*3737Shx147065 	struct an_card_rid_desc an_rid_desc;
2445*3737Shx147065 	struct an_card_rx_desc  an_rx_desc;
2446*3737Shx147065 	struct an_card_tx_desc  an_tx_desc;
2447*3737Shx147065 
2448*3737Shx147065 	/* Allocate DMA for rx */
2449*3737Shx147065 	if (pcan_set_cmd0(pcan_p, AN_CMD_ALLOC_DESC,
2450*3737Shx147065 	    AN_DESCRIPTOR_RX, AN_RX_DESC_OFFSET,
2451*3737Shx147065 	    AN_MAX_RX_DESC) != PCAN_SUCCESS) {
2452*3737Shx147065 		cmn_err(CE_WARN, "pcan init_dma: fail to alloc rx descriptor");
2453*3737Shx147065 		goto error;
2454*3737Shx147065 	}
2455*3737Shx147065 	for (i = 0; i < AN_MAX_RX_DESC; i++) {
2456*3737Shx147065 		bzero(&an_rx_desc, sizeof (an_rx_desc));
2457*3737Shx147065 		an_rx_desc.an_valid = 1;
2458*3737Shx147065 		an_rx_desc.an_len = AN_RX_BUFFER_SIZE;
2459*3737Shx147065 		an_rx_desc.an_done = 0;
2460*3737Shx147065 		an_rx_desc.an_phys = pcan_p->pcan_rx[i].dma_physaddr;
2461*3737Shx147065 		for (j = 0; j < sizeof (an_rx_desc) / 4; j++)
2462*3737Shx147065 			PCAN_AUX_PUT32(pcan_p, AN_RX_DESC_OFFSET
2463*3737Shx147065 			    + (i * sizeof (an_rx_desc))
2464*3737Shx147065 			    + (j * 4), ((uint32_t *)&an_rx_desc)[j]);
2465*3737Shx147065 	}
2466*3737Shx147065 
2467*3737Shx147065 
2468*3737Shx147065 	/* Allocate DMA for tx */
2469*3737Shx147065 	if (pcan_set_cmd0(pcan_p, AN_CMD_ALLOC_DESC,
2470*3737Shx147065 	    AN_DESCRIPTOR_TX, AN_TX_DESC_OFFSET,
2471*3737Shx147065 	    AN_MAX_TX_DESC) != PCAN_SUCCESS) {
2472*3737Shx147065 		cmn_err(CE_WARN, "pcan init_dma: fail to alloc tx descriptor");
2473*3737Shx147065 		goto error;
2474*3737Shx147065 	}
2475*3737Shx147065 
2476*3737Shx147065 	for (i = 0; i < AN_MAX_TX_DESC; i++) {
2477*3737Shx147065 		an_tx_desc.an_offset = 0;
2478*3737Shx147065 		an_tx_desc.an_eoc = 0;
2479*3737Shx147065 		an_tx_desc.an_valid = 0;
2480*3737Shx147065 		an_tx_desc.an_len = 0;
2481*3737Shx147065 		an_tx_desc.an_phys = pcan_p->pcan_tx[i].dma_physaddr;
2482*3737Shx147065 
2483*3737Shx147065 		for (j = 0; j < sizeof (an_tx_desc) / 4; j++)
2484*3737Shx147065 			PCAN_AUX_PUT32(pcan_p, AN_TX_DESC_OFFSET
2485*3737Shx147065 			    + (i * sizeof (an_tx_desc))
2486*3737Shx147065 			    + (j * 4), ((uint32_t *)&an_tx_desc)[j]);
2487*3737Shx147065 	}
2488*3737Shx147065 
2489*3737Shx147065 	/* Allocate DMA for rid */
2490*3737Shx147065 	if (pcan_set_cmd0(pcan_p, AN_CMD_ALLOC_DESC,
2491*3737Shx147065 	    AN_DESCRIPTOR_HOSTRW, AN_HOST_DESC_OFFSET, 1) != PCAN_SUCCESS) {
2492*3737Shx147065 		cmn_err(CE_WARN, "pcan init_dma: fail to alloc rid descriptor");
2493*3737Shx147065 		goto error;
2494*3737Shx147065 	}
2495*3737Shx147065 	bzero(&an_rid_desc, sizeof (an_rid_desc));
2496*3737Shx147065 	an_rid_desc.an_valid = 1;
2497*3737Shx147065 	an_rid_desc.an_len = AN_RID_BUFFER_SIZE;
2498*3737Shx147065 	an_rid_desc.an_rid = 0;
2499*3737Shx147065 	an_rid_desc.an_phys = pcan_p->pcan_cmd.dma_physaddr;
2500*3737Shx147065 
2501*3737Shx147065 	for (i = 0; i < sizeof (an_rid_desc) / 4; i++)
2502*3737Shx147065 		PCAN_AUX_PUT32(pcan_p, AN_HOST_DESC_OFFSET + i * 4,
2503*3737Shx147065 		    ((uint32_t *)&an_rid_desc)[i]);
2504*3737Shx147065 
2505*3737Shx147065 	pcan_p->pcan_txring.an_tx_prod = 0;
2506*3737Shx147065 	pcan_p->pcan_txring.an_tx_cons = 0;
2507*3737Shx147065 	pcan_p->pcan_flag &= ~PCAN_CARD_SEND;
2508*3737Shx147065 	return (PCAN_SUCCESS);
2509*3737Shx147065 error:
2510*3737Shx147065 	return (PCAN_FAIL);
2511*3737Shx147065 }
2512*3737Shx147065 
2513*3737Shx147065 static int
2514*3737Shx147065 pcan_init_dma(dev_info_t *dip, pcan_maci_t *pcan_p)
2515*3737Shx147065 {
2516*3737Shx147065 	int i, ret = PCAN_FAIL;
2517*3737Shx147065 	ddi_dma_cookie_t dma_cookie;
2518*3737Shx147065 	size_t len;
2519*3737Shx147065 
2520*3737Shx147065 	/* Allocate DMA for rx */
2521*3737Shx147065 	for (i = 0; i < AN_MAX_RX_DESC; i++) {
2522*3737Shx147065 		if (ddi_dma_alloc_handle(dip, &control_cmd_dma_attr,
2523*3737Shx147065 		    DDI_DMA_SLEEP, 0,
2524*3737Shx147065 		    &pcan_p->pcan_rx[i].dma_handle) != DDI_SUCCESS)
2525*3737Shx147065 			goto error;
2526*3737Shx147065 
2527*3737Shx147065 		if (ddi_dma_mem_alloc(pcan_p->pcan_rx[i].dma_handle,
2528*3737Shx147065 		    AN_RX_BUFFER_SIZE, &accattr,
2529*3737Shx147065 		    DDI_DMA_STREAMING, DDI_DMA_SLEEP, 0,
2530*3737Shx147065 		    (caddr_t *)&pcan_p->pcan_rx[i].dma_virtaddr, &len,
2531*3737Shx147065 		    &pcan_p->pcan_rx[i].dma_acc_handle) != DDI_SUCCESS) {
2532*3737Shx147065 			goto error;
2533*3737Shx147065 		}
2534*3737Shx147065 		if (ddi_dma_addr_bind_handle(
2535*3737Shx147065 		    pcan_p->pcan_rx[i].dma_handle,
2536*3737Shx147065 		    NULL, (caddr_t)pcan_p->pcan_rx[i].dma_virtaddr,
2537*3737Shx147065 		    len, DDI_DMA_READ |
2538*3737Shx147065 		    DDI_DMA_STREAMING, DDI_DMA_SLEEP, 0, &dma_cookie,
2539*3737Shx147065 		    &pcan_p->pcan_rx[i].ncookies) != DDI_DMA_MAPPED) {
2540*3737Shx147065 			goto error;
2541*3737Shx147065 		}
2542*3737Shx147065 		ASSERT(pcan_p->pcan_rx[i].ncookies == 1);
2543*3737Shx147065 		pcan_p->pcan_rx[i].dma_physaddr = dma_cookie.dmac_address;
2544*3737Shx147065 	}
2545*3737Shx147065 
2546*3737Shx147065 	/* Allocate DMA for tx */
2547*3737Shx147065 	for (i = 0; i < AN_MAX_TX_DESC; i++) {
2548*3737Shx147065 		if (ddi_dma_alloc_handle(dip, &control_cmd_dma_attr,
2549*3737Shx147065 		    DDI_DMA_SLEEP, 0,
2550*3737Shx147065 		    &pcan_p->pcan_tx[i].dma_handle) != DDI_SUCCESS)
2551*3737Shx147065 			goto error;
2552*3737Shx147065 
2553*3737Shx147065 		if (ddi_dma_mem_alloc(pcan_p->pcan_tx[i].dma_handle,
2554*3737Shx147065 		    AN_TX_BUFFER_SIZE, &accattr,
2555*3737Shx147065 		    DDI_DMA_STREAMING, DDI_DMA_SLEEP, 0,
2556*3737Shx147065 		    (caddr_t *)&pcan_p->pcan_tx[i].dma_virtaddr, &len,
2557*3737Shx147065 		    &pcan_p->pcan_tx[i].dma_acc_handle) != DDI_SUCCESS) {
2558*3737Shx147065 			goto error;
2559*3737Shx147065 		}
2560*3737Shx147065 		if (ddi_dma_addr_bind_handle(
2561*3737Shx147065 		    pcan_p->pcan_tx[i].dma_handle,
2562*3737Shx147065 		    NULL, (caddr_t)pcan_p->pcan_tx[i].dma_virtaddr,
2563*3737Shx147065 		    len, DDI_DMA_WRITE |
2564*3737Shx147065 		    DDI_DMA_STREAMING, DDI_DMA_SLEEP, 0, &dma_cookie,
2565*3737Shx147065 		    &pcan_p->pcan_tx[i].ncookies) != DDI_DMA_MAPPED) {
2566*3737Shx147065 			goto error;
2567*3737Shx147065 		}
2568*3737Shx147065 		ASSERT(pcan_p->pcan_tx[i].ncookies == 1);
2569*3737Shx147065 		pcan_p->pcan_tx[i].dma_physaddr = dma_cookie.dmac_address;
2570*3737Shx147065 	}
2571*3737Shx147065 
2572*3737Shx147065 	/* Allocate DMA for rid */
2573*3737Shx147065 	if (ddi_dma_alloc_handle(dip, &control_cmd_dma_attr,
2574*3737Shx147065 	    DDI_DMA_SLEEP, 0,
2575*3737Shx147065 	    &pcan_p->pcan_cmd.dma_handle) != DDI_SUCCESS)
2576*3737Shx147065 		goto error;
2577*3737Shx147065 
2578*3737Shx147065 	if (ddi_dma_mem_alloc(pcan_p->pcan_cmd.dma_handle,
2579*3737Shx147065 	    AN_RID_BUFFER_SIZE, &accattr,
2580*3737Shx147065 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0,
2581*3737Shx147065 	    (caddr_t *)&pcan_p->pcan_cmd.dma_virtaddr, &len,
2582*3737Shx147065 	    &pcan_p->pcan_cmd.dma_acc_handle) != DDI_SUCCESS) {
2583*3737Shx147065 		goto error;
2584*3737Shx147065 	}
2585*3737Shx147065 	if (ddi_dma_addr_bind_handle(
2586*3737Shx147065 	    pcan_p->pcan_cmd.dma_handle,
2587*3737Shx147065 	    NULL, (caddr_t)pcan_p->pcan_cmd.dma_virtaddr,
2588*3737Shx147065 	    len, DDI_DMA_RDWR |
2589*3737Shx147065 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0, &dma_cookie,
2590*3737Shx147065 	    &pcan_p->pcan_cmd.ncookies) != DDI_DMA_MAPPED) {
2591*3737Shx147065 		goto error;
2592*3737Shx147065 	}
2593*3737Shx147065 	ASSERT(pcan_p->pcan_cmd.ncookies == 1);
2594*3737Shx147065 	pcan_p->pcan_cmd.dma_physaddr = dma_cookie.dmac_address;
2595*3737Shx147065 
2596*3737Shx147065 	if (ret = pcan_init_dma_desc(pcan_p)) {
2597*3737Shx147065 		cmn_err(CE_WARN, "pcan init_dma_desc: failed\n");
2598*3737Shx147065 		goto error;
2599*3737Shx147065 	}
2600*3737Shx147065 
2601*3737Shx147065 	return (PCAN_SUCCESS);
2602*3737Shx147065 error:
2603*3737Shx147065 	pcan_free_dma(pcan_p);
2604*3737Shx147065 	return (ret);
2605*3737Shx147065 }
2606*3737Shx147065 
2607*3737Shx147065 static void
2608*3737Shx147065 pcan_free_dma(pcan_maci_t *pcan_p)
2609*3737Shx147065 {
2610*3737Shx147065 	int i;
2611*3737Shx147065 
2612*3737Shx147065 	/* free RX dma */
2613*3737Shx147065 	pcan_stop_rx_dma(pcan_p);
2614*3737Shx147065 	for (i = 0; i < AN_MAX_RX_DESC; i++) {
2615*3737Shx147065 		if (pcan_p->pcan_rx[i].dma_handle != NULL) {
2616*3737Shx147065 			if (pcan_p->pcan_rx[i].ncookies) {
2617*3737Shx147065 				(void) ddi_dma_unbind_handle(
2618*3737Shx147065 				    pcan_p->pcan_rx[i].dma_handle);
2619*3737Shx147065 				pcan_p->pcan_rx[i].ncookies = 0;
2620*3737Shx147065 			}
2621*3737Shx147065 			ddi_dma_free_handle(
2622*3737Shx147065 			    &pcan_p->pcan_rx[i].dma_handle);
2623*3737Shx147065 			pcan_p->pcan_rx[i].dma_handle = NULL;
2624*3737Shx147065 		}
2625*3737Shx147065 		if (pcan_p->pcan_rx[i].dma_acc_handle != NULL) {
2626*3737Shx147065 			ddi_dma_mem_free(
2627*3737Shx147065 			    &pcan_p->pcan_rx[i].dma_acc_handle);
2628*3737Shx147065 			pcan_p->pcan_rx[i].dma_acc_handle = NULL;
2629*3737Shx147065 		}
2630*3737Shx147065 	}
2631*3737Shx147065 
2632*3737Shx147065 	/* free TX dma */
2633*3737Shx147065 	for (i = 0; i < AN_MAX_TX_DESC; i++) {
2634*3737Shx147065 		if (pcan_p->pcan_tx[i].dma_handle != NULL) {
2635*3737Shx147065 			if (pcan_p->pcan_tx[i].ncookies) {
2636*3737Shx147065 				(void) ddi_dma_unbind_handle(
2637*3737Shx147065 				    pcan_p->pcan_tx[i].dma_handle);
2638*3737Shx147065 				pcan_p->pcan_tx[i].ncookies = 0;
2639*3737Shx147065 			}
2640*3737Shx147065 			ddi_dma_free_handle(
2641*3737Shx147065 			    &pcan_p->pcan_tx[i].dma_handle);
2642*3737Shx147065 			pcan_p->pcan_tx[i].dma_handle = NULL;
2643*3737Shx147065 		}
2644*3737Shx147065 		if (pcan_p->pcan_tx[i].dma_acc_handle != NULL) {
2645*3737Shx147065 			ddi_dma_mem_free(
2646*3737Shx147065 			    &pcan_p->pcan_tx[i].dma_acc_handle);
2647*3737Shx147065 			pcan_p->pcan_tx[i].dma_acc_handle = NULL;
2648*3737Shx147065 		}
2649*3737Shx147065 	}
2650*3737Shx147065 
2651*3737Shx147065 	/* free cmd dma */
2652*3737Shx147065 	if (pcan_p->pcan_cmd.dma_handle != NULL) {
2653*3737Shx147065 		if (pcan_p->pcan_cmd.ncookies) {
2654*3737Shx147065 			(void) ddi_dma_unbind_handle(
2655*3737Shx147065 			    pcan_p->pcan_cmd.dma_handle);
2656*3737Shx147065 			pcan_p->pcan_cmd.ncookies = 0;
2657*3737Shx147065 		}
2658*3737Shx147065 		ddi_dma_free_handle(
2659*3737Shx147065 		    &pcan_p->pcan_cmd.dma_handle);
2660*3737Shx147065 		pcan_p->pcan_cmd.dma_handle = NULL;
2661*3737Shx147065 	}
2662*3737Shx147065 	if (pcan_p->pcan_cmd.dma_acc_handle != NULL) {
2663*3737Shx147065 		ddi_dma_mem_free(
2664*3737Shx147065 		    &pcan_p->pcan_cmd.dma_acc_handle);
2665*3737Shx147065 		pcan_p->pcan_cmd.dma_acc_handle = NULL;
2666*3737Shx147065 	}
2667*3737Shx147065 }
2668*3737Shx147065 
2669*3737Shx147065 /*
2670*3737Shx147065  * get card capability (WEP, default channel), setup broadcast, mac addresses
2671*3737Shx147065  */
2672*3737Shx147065 static uint32_t
2673*3737Shx147065 pcan_get_cap(pcan_maci_t *pcan_p)
2674*3737Shx147065 {
2675*3737Shx147065 	uint16_t stat;
2676*3737Shx147065 
2677*3737Shx147065 	if (stat = pcan_cfg_ltv(PCAN_READ_LTV, pcan_p, &pcan_p->an_config)) {
2678*3737Shx147065 		PCANDBG((CE_NOTE, "pcan get_cap: read cfg fail %x", stat));
2679*3737Shx147065 		return ((uint32_t)AN_RID_GENCONFIG << 16 | stat);
2680*3737Shx147065 	}
2681*3737Shx147065 
2682*3737Shx147065 	if (stat = pcan_cap_ltv(PCAN_READ_LTV, pcan_p)) {
2683*3737Shx147065 		PCANDBG((CE_NOTE, "pcan get_cap: read cap fail %x", stat));
2684*3737Shx147065 		return ((uint32_t)AN_RID_CAPABILITIES << 16 | stat);
2685*3737Shx147065 	}
2686*3737Shx147065 #ifdef DEBUG
2687*3737Shx147065 	if (pcan_debug & PCAN_DBG_FW_VERSION) {
2688*3737Shx147065 		cmn_err(CE_NOTE, "the version of the firmware in the wifi card "
2689*3737Shx147065 		    "'%s %s %s' is %s\n",
2690*3737Shx147065 		    pcan_p->an_caps.an_manufname,
2691*3737Shx147065 		    pcan_p->an_caps.an_prodname,
2692*3737Shx147065 		    pcan_p->pcan_device_type == PCAN_DEVICE_PCI ?
2693*3737Shx147065 		    "minipci" : "pccard",
2694*3737Shx147065 		    pcan_p->an_caps.an_prodvers);
2695*3737Shx147065 	}
2696*3737Shx147065 #endif
2697*3737Shx147065 
2698*3737Shx147065 	if (stat = pcan_ssid_ltv(PCAN_READ_LTV, pcan_p)) {
2699*3737Shx147065 		PCANDBG((CE_NOTE, "pcan get_cap: read ssid fail %x", stat));
2700*3737Shx147065 		return ((uint32_t)AN_RID_SSIDLIST << 16 | stat);
2701*3737Shx147065 	}
2702*3737Shx147065 
2703*3737Shx147065 	if (stat = pcan_aplist_ltv(PCAN_READ_LTV, pcan_p)) {
2704*3737Shx147065 		PCANDBG((CE_NOTE, "pcan get_cap: read aplist fail %x", stat));
2705*3737Shx147065 		return ((uint32_t)AN_RID_APLIST << 16 | stat);
2706*3737Shx147065 	}
2707*3737Shx147065 	if (stat = pcan_wepkey_ltv(PCAN_READ_LTV, pcan_p)) {
2708*3737Shx147065 		PCANDBG((CE_NOTE, "pcan get_cap: read wepkey fail %x", stat));
2709*3737Shx147065 		return ((uint32_t)AN_RID_WEPKEY2 << 16 | stat);
2710*3737Shx147065 	}
2711*3737Shx147065 	ether_copy(pcan_p->an_caps.an_oemaddr, pcan_p->pcan_mac_addr);
2712*3737Shx147065 	return (PCAN_SUCCESS);
2713*3737Shx147065 }
2714*3737Shx147065 
2715*3737Shx147065 static int
2716*3737Shx147065 pcan_config_mac(pcan_maci_t *pcan_p)
2717*3737Shx147065 {
2718*3737Shx147065 	uint16_t stat;
2719*3737Shx147065 
2720*3737Shx147065 	if (stat = pcan_ssid_ltv(PCAN_WRITE_LTV, pcan_p)) {
2721*3737Shx147065 		PCANDBG((CE_NOTE, "pcan config_mac: write SSID failed%x\n",
2722*3737Shx147065 		    stat));
2723*3737Shx147065 		return ((int)stat);
2724*3737Shx147065 	}
2725*3737Shx147065 
2726*3737Shx147065 	if (stat = pcan_aplist_ltv(PCAN_WRITE_LTV, pcan_p)) {
2727*3737Shx147065 		PCANDBG((CE_NOTE, "pcan config_mac: write APlist failed%x\n",
2728*3737Shx147065 		    stat));
2729*3737Shx147065 		return ((int)stat);
2730*3737Shx147065 	}
2731*3737Shx147065 	if (stat = pcan_wepkey_ltv(PCAN_WRITE_LTV, pcan_p)) {
2732*3737Shx147065 		PCANDBG((CE_NOTE, "pcan config_mac: write wepkey failed%x\n",
2733*3737Shx147065 		    stat));
2734*3737Shx147065 		return ((int)stat);
2735*3737Shx147065 	}
2736*3737Shx147065 	if (pcan_p->pcan_usewep)
2737*3737Shx147065 		pcan_p->an_config.an_authtype |=
2738*3737Shx147065 		    AN_AUTHTYPE_ENABLEWEP | AN_AUTHTYPE_ALLOW_UNENCRYPTED;
2739*3737Shx147065 	PCANDBG((CE_NOTE, "pcan config_mac: usewep=%x authtype=%x opmode=%x\n",
2740*3737Shx147065 	    pcan_p->pcan_usewep, pcan_p->an_config.an_authtype,
2741*3737Shx147065 	    pcan_p->an_config.an_opmode));
2742*3737Shx147065 
2743*3737Shx147065 	pcan_p->an_config.an_assoc_timeout = 5000; /* stop assoc seq in 5 sec */
2744*3737Shx147065 	if (stat = pcan_cfg_ltv(PCAN_WRITE_LTV, pcan_p, &pcan_p->an_config)) {
2745*3737Shx147065 		PCANDBG((CE_NOTE, "pcan config_mac: write cfg failed %x\n",
2746*3737Shx147065 		    stat));
2747*3737Shx147065 		return ((int)stat);
2748*3737Shx147065 	}
2749*3737Shx147065 
2750*3737Shx147065 	if (stat = pcan_cfg_ltv(PCAN_READ_LTV, pcan_p,
2751*3737Shx147065 	    &pcan_p->an_actual_config)) {
2752*3737Shx147065 		PCANDBG((CE_NOTE, "pcan config_mac: read cfg failed%x\n",
2753*3737Shx147065 		    stat));
2754*3737Shx147065 		return ((int)stat);
2755*3737Shx147065 	}
2756*3737Shx147065 	PCANDBG((CE_NOTE, "pcan config_mac: optionmask=%x authtype=%x\n", 0,
2757*3737Shx147065 	    pcan_p->an_actual_config.an_authtype));
2758*3737Shx147065 
2759*3737Shx147065 	if (stat = pcan_status_ltv(PCAN_READ_LTV, pcan_p, &pcan_p->an_status)) {
2760*3737Shx147065 		PCANDBG((CE_NOTE, "pcan config_mac: read status failed %x\n",
2761*3737Shx147065 		    stat));
2762*3737Shx147065 		return ((int)stat);
2763*3737Shx147065 	}
2764*3737Shx147065 	return (PCAN_SUCCESS);
2765*3737Shx147065 }
2766*3737Shx147065 
2767*3737Shx147065 static int
2768*3737Shx147065 pcan_loaddef(pcan_maci_t *pcan_p)
2769*3737Shx147065 {
2770*3737Shx147065 	int i;
2771*3737Shx147065 
2772*3737Shx147065 	pcan_p->an_ssidlist.an_ssid1_len = 0;
2773*3737Shx147065 	bzero(pcan_p->an_ssidlist.an_ssid1,
2774*3737Shx147065 		sizeof (pcan_p->an_ssidlist.an_ssid1));
2775*3737Shx147065 	for (i = 0; i < MAX_NWEPKEYS; i++) {
2776*3737Shx147065 		pcan_p->an_wepkey[i].an_index = 0xffff;
2777*3737Shx147065 		bzero(pcan_p->an_wepkey[i].an_key,
2778*3737Shx147065 		    sizeof (pcan_p->an_wepkey[i].an_key));
2779*3737Shx147065 		pcan_p->an_wepkey[i].an_keylen = 0;
2780*3737Shx147065 		bzero(pcan_p->an_wepkey[i].an_macaddr,
2781*3737Shx147065 		    sizeof (pcan_p->an_wepkey[i].an_macaddr));
2782*3737Shx147065 		pcan_p->an_wepkey[i].an_macaddr[0] = 1;
2783*3737Shx147065 	}
2784*3737Shx147065 	pcan_p->an_cur_wepkey = 0;
2785*3737Shx147065 
2786*3737Shx147065 	pcan_p->pcan_usewep = 0;
2787*3737Shx147065 	pcan_p->an_config.an_opmode = AN_OPMODE_INFR_STATION;
2788*3737Shx147065 	pcan_p->an_config.an_authtype = AN_AUTHTYPE_OPEN;
2789*3737Shx147065 	pcan_p->an_config.an_stationary = 1;
2790*3737Shx147065 	pcan_p->an_config.an_max_beacon_lost_time = 0xffff;
2791*3737Shx147065 	i = pcan_config_mac(pcan_p);
2792*3737Shx147065 
2793*3737Shx147065 	return (i);
2794*3737Shx147065 }
2795*3737Shx147065 
2796*3737Shx147065 static int
2797*3737Shx147065 pcan_init_nicmem(pcan_maci_t *pcan_p)
2798*3737Shx147065 {
2799*3737Shx147065 	int i;
2800*3737Shx147065 	uint16_t ret;
2801*3737Shx147065 	pcan_txring_t *ring_p = &pcan_p->pcan_txring;
2802*3737Shx147065 
2803*3737Shx147065 	for (i = 0; i < AN_TX_RING_CNT; i++) {
2804*3737Shx147065 		uint16_t rc;
2805*3737Shx147065 		ret = pcan_alloc_nicmem(pcan_p, PCAN_NICMEM_SZ, &rc);
2806*3737Shx147065 		if (ret) {
2807*3737Shx147065 			cmn_err(CE_WARN, "pcan alloc NIC Tx buf[%x]: failed "
2808*3737Shx147065 			    "%x\n", i, ret);
2809*3737Shx147065 			return (DDI_FAILURE);
2810*3737Shx147065 		}
2811*3737Shx147065 		ring_p->an_tx_fids[i] = rc;
2812*3737Shx147065 		ring_p->an_tx_ring[i] = 0;
2813*3737Shx147065 		PCANDBG((CE_NOTE, "pcan: NIC tx_id[%x]=%x\n", i, rc));
2814*3737Shx147065 	}
2815*3737Shx147065 	ring_p->an_tx_prod = ring_p->an_tx_cons = 0;
2816*3737Shx147065 	return (PCAN_SUCCESS);
2817*3737Shx147065 }
2818*3737Shx147065 
2819*3737Shx147065 
2820*3737Shx147065 
2821*3737Shx147065 static void
2822*3737Shx147065 pcan_start_locked(pcan_maci_t *pcan_p)
2823*3737Shx147065 {
2824*3737Shx147065 	pcan_p->pcan_flag |= PCAN_CARD_INTREN;
2825*3737Shx147065 	PCAN_ENABLE_INTR(pcan_p);
2826*3737Shx147065 }
2827*3737Shx147065 
2828*3737Shx147065 static void
2829*3737Shx147065 pcan_stop_locked(pcan_maci_t *pcan_p)
2830*3737Shx147065 {
2831*3737Shx147065 	PCAN_DISABLE_INTR_CLEAR(pcan_p);
2832*3737Shx147065 	pcan_p->pcan_flag &= ~PCAN_CARD_INTREN;
2833*3737Shx147065 }
2834*3737Shx147065 
2835*3737Shx147065 /*
2836*3737Shx147065  * for scan result
2837*3737Shx147065  */
2838*3737Shx147065 static int
2839*3737Shx147065 pcan_add_scan_item(pcan_maci_t *pcan_p, struct an_ltv_scanresult s)
2840*3737Shx147065 {
2841*3737Shx147065 	an_scan_list_t *scan_item;
2842*3737Shx147065 
2843*3737Shx147065 	scan_item = kmem_zalloc(sizeof (an_scan_list_t), KM_SLEEP);
2844*3737Shx147065 	if (scan_item == NULL) {
2845*3737Shx147065 		cmn_err(CE_WARN, "pcan add_scan_item: zalloc failed\n");
2846*3737Shx147065 		return (PCAN_FAIL);
2847*3737Shx147065 	}
2848*3737Shx147065 	scan_item->an_val = s;
2849*3737Shx147065 	scan_item->an_timeout = AN_SCAN_TIMEOUT_MAX;
2850*3737Shx147065 	list_insert_tail(&pcan_p->an_scan_list, scan_item);
2851*3737Shx147065 	pcan_p->an_scan_num++;
2852*3737Shx147065 	return (PCAN_SUCCESS);
2853*3737Shx147065 }
2854*3737Shx147065 
2855*3737Shx147065 static void
2856*3737Shx147065 pcan_delete_scan_item(pcan_maci_t *pcan_p, an_scan_list_t *s)
2857*3737Shx147065 {
2858*3737Shx147065 	list_remove(&pcan_p->an_scan_list, s);
2859*3737Shx147065 	kmem_free(s, sizeof (*s));
2860*3737Shx147065 	pcan_p->an_scan_num--;
2861*3737Shx147065 }
2862*3737Shx147065 
2863*3737Shx147065 static void
2864*3737Shx147065 pcan_scanlist_timeout(void *arg)
2865*3737Shx147065 {
2866*3737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
2867*3737Shx147065 	an_scan_list_t *scan_item0, *scan_item1;
2868*3737Shx147065 
2869*3737Shx147065 	mutex_enter(&pcan_p->pcan_scanlist_lock);
2870*3737Shx147065 	scan_item0 = list_head(&pcan_p->an_scan_list);
2871*3737Shx147065 	for (; scan_item0; ) {
2872*3737Shx147065 		PCANDBG((CE_NOTE, "pcan scanlist: ssid = %s\n",
2873*3737Shx147065 		    scan_item0->an_val.an_ssid));
2874*3737Shx147065 		PCANDBG((CE_NOTE, "pcan scanlist: timeout left: %ds",
2875*3737Shx147065 		    scan_item0->an_timeout));
2876*3737Shx147065 		scan_item1 = list_next(&pcan_p->an_scan_list, scan_item0);
2877*3737Shx147065 		if (scan_item0->an_timeout == 0) {
2878*3737Shx147065 			pcan_delete_scan_item(pcan_p, scan_item0);
2879*3737Shx147065 		} else {
2880*3737Shx147065 			scan_item0->an_timeout--;
2881*3737Shx147065 		}
2882*3737Shx147065 		scan_item0 = scan_item1;
2883*3737Shx147065 	}
2884*3737Shx147065 	mutex_exit(&pcan_p->pcan_scanlist_lock);
2885*3737Shx147065 	pcan_p->an_scanlist_timeout_id = timeout(pcan_scanlist_timeout,
2886*3737Shx147065 	    pcan_p, drv_usectohz(1000000));
2887*3737Shx147065 }
2888*3737Shx147065 
2889*3737Shx147065 
2890*3737Shx147065 /*
2891*3737Shx147065  * for wificonfig and dlamd ioctl
2892*3737Shx147065  */
2893*3737Shx147065 static int
2894*3737Shx147065 pcan_cfg_essid(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
2895*3737Shx147065 {
2896*3737Shx147065 	uint16_t i;
2897*3737Shx147065 	char *value;
2898*3737Shx147065 	wldp_t	*infp;
2899*3737Shx147065 	wldp_t *outfp;
2900*3737Shx147065 	char *buf;
2901*3737Shx147065 	int iret;
2902*3737Shx147065 	struct an_ltv_status *status_p;
2903*3737Shx147065 	struct an_ltv_ssidlist *ssidlist_p;
2904*3737Shx147065 
2905*3737Shx147065 	status_p = &pcan_p->an_status;
2906*3737Shx147065 	ssidlist_p = &pcan_p->an_ssidlist;
2907*3737Shx147065 
2908*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
2909*3737Shx147065 	if (buf == NULL) {
2910*3737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_essid: failed to alloc "
2911*3737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
2912*3737Shx147065 		return (ENOMEM);
2913*3737Shx147065 	}
2914*3737Shx147065 	outfp = (wldp_t *)buf;
2915*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
2916*3737Shx147065 	infp = (wldp_t *)mp->b_rptr;
2917*3737Shx147065 
2918*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
2919*3737Shx147065 		if (pcan_status_ltv(PCAN_READ_LTV, pcan_p, status_p)) {
2920*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
2921*3737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
2922*3737Shx147065 			goto done;
2923*3737Shx147065 		}
2924*3737Shx147065 
2925*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET +
2926*3737Shx147065 		    offsetof(wl_essid_t, wl_essid_essid) +
2927*3737Shx147065 		    status_p->an_ssidlen;
2928*3737Shx147065 		((wl_essid_t *)(outfp->wldp_buf))->wl_essid_length =
2929*3737Shx147065 		    status_p->an_ssidlen;
2930*3737Shx147065 		bcopy(status_p->an_ssid, buf + WIFI_BUF_OFFSET +
2931*3737Shx147065 		    offsetof(wl_essid_t, wl_essid_essid),
2932*3737Shx147065 		    status_p->an_ssidlen);
2933*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
2934*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
2935*3737Shx147065 		bzero(ssidlist_p, sizeof (*ssidlist_p));
2936*3737Shx147065 		value = ((wl_essid_t *)(infp->wldp_buf))->wl_essid_essid;
2937*3737Shx147065 		(void) strncpy(ssidlist_p->an_ssid1, value,
2938*3737Shx147065 		    MIN(32, strlen(value)));
2939*3737Shx147065 		ssidlist_p->an_ssid1_len = strlen(value);
2940*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
2941*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
2942*3737Shx147065 	} else {
2943*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
2944*3737Shx147065 		return (EINVAL);
2945*3737Shx147065 	}
2946*3737Shx147065 done:
2947*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++) {
2948*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
2949*3737Shx147065 	}
2950*3737Shx147065 	iret = (int)(outfp->wldp_result);
2951*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
2952*3737Shx147065 	return (iret);
2953*3737Shx147065 }
2954*3737Shx147065 
2955*3737Shx147065 static int
2956*3737Shx147065 pcan_cfg_bssid(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
2957*3737Shx147065 {
2958*3737Shx147065 	uint16_t i;
2959*3737Shx147065 	wldp_t *infp;
2960*3737Shx147065 	wldp_t *outfp;
2961*3737Shx147065 	char *buf;
2962*3737Shx147065 	wl_bssid_t *value;
2963*3737Shx147065 	int iret;
2964*3737Shx147065 	struct an_ltv_status *status_p;
2965*3737Shx147065 	struct an_ltv_aplist *aplist_p;
2966*3737Shx147065 
2967*3737Shx147065 	status_p = &pcan_p->an_status;
2968*3737Shx147065 	aplist_p = &pcan_p->an_aplist;
2969*3737Shx147065 
2970*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
2971*3737Shx147065 	if (buf == NULL) {
2972*3737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_bssid: failed to alloc "
2973*3737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
2974*3737Shx147065 		return (ENOMEM);
2975*3737Shx147065 	}
2976*3737Shx147065 	outfp = (wldp_t *)buf;
2977*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
2978*3737Shx147065 	infp = (wldp_t *)mp->b_rptr;
2979*3737Shx147065 
2980*3737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_bssid_t);
2981*3737Shx147065 
2982*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
2983*3737Shx147065 		if (pcan_status_ltv(PCAN_READ_LTV, pcan_p, status_p)) {
2984*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
2985*3737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
2986*3737Shx147065 			goto done;
2987*3737Shx147065 		}
2988*3737Shx147065 
2989*3737Shx147065 		bcopy(status_p->an_cur_bssid, buf + WIFI_BUF_OFFSET,
2990*3737Shx147065 		    sizeof (wl_bssid_t));
2991*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
2992*3737Shx147065 		PCANDBG((CE_CONT,
2993*3737Shx147065 		    "pcan: cfg_bssid: bssid=%x %x %x %x %x %x\n",
2994*3737Shx147065 		    status_p->an_cur_bssid[0],
2995*3737Shx147065 		    status_p->an_cur_bssid[1],
2996*3737Shx147065 		    status_p->an_cur_bssid[2],
2997*3737Shx147065 		    status_p->an_cur_bssid[3],
2998*3737Shx147065 		    status_p->an_cur_bssid[4],
2999*3737Shx147065 		    status_p->an_cur_bssid[5]));
3000*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
3001*3737Shx147065 		value = (wl_bssid_t *)(infp->wldp_buf);
3002*3737Shx147065 		(void) strncpy((char *)aplist_p->an_ap1, (char *)value, 6);
3003*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
3004*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3005*3737Shx147065 	} else {
3006*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3007*3737Shx147065 		return (EINVAL);
3008*3737Shx147065 	}
3009*3737Shx147065 done:
3010*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++) {
3011*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3012*3737Shx147065 	}
3013*3737Shx147065 	iret = (int)(outfp->wldp_result);
3014*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3015*3737Shx147065 	return (iret);
3016*3737Shx147065 }
3017*3737Shx147065 
3018*3737Shx147065 /*ARGSUSED*/
3019*3737Shx147065 static int
3020*3737Shx147065 pcan_cmd_scan(pcan_maci_t *pcan_p)
3021*3737Shx147065 {
3022*3737Shx147065 	uint16_t i = 0, j, ret = WL_SUCCESS;
3023*3737Shx147065 	uint8_t	bssid_t[6];
3024*3737Shx147065 	uint32_t check_num, enable;
3025*3737Shx147065 	an_scan_list_t *scan_item0;
3026*3737Shx147065 
3027*3737Shx147065 	enable = pcan_p->pcan_flag & PCAN_ENABLED;
3028*3737Shx147065 	if ((!enable) &&
3029*3737Shx147065 	    (ret = pcan_set_cmd(pcan_p, AN_CMD_ENABLE, 0))) {
3030*3737Shx147065 		ret = (int)WL_HW_ERROR;
3031*3737Shx147065 		goto exit;
3032*3737Shx147065 	}
3033*3737Shx147065 	if (ret = pcan_set_cmd(pcan_p, AN_CMD_SCAN, 0)) {
3034*3737Shx147065 		ret = (int)WL_HW_ERROR;
3035*3737Shx147065 		goto exit;
3036*3737Shx147065 	}
3037*3737Shx147065 
3038*3737Shx147065 	pcan_delay(pcan_p, 500000);
3039*3737Shx147065 	ret =  pcan_scanresult_ltv(PCAN_READ_LTV,
3040*3737Shx147065 	    pcan_p, AN_RID_ESSIDLIST_FIRST, &pcan_p->an_scanresult[i]);
3041*3737Shx147065 	if ((ret) || pcan_p->an_scanresult[i].an_index == 0xffff) {
3042*3737Shx147065 		goto done;
3043*3737Shx147065 	}
3044*3737Shx147065 	do
3045*3737Shx147065 	{
3046*3737Shx147065 		i++;
3047*3737Shx147065 		ret =  pcan_scanresult_ltv(PCAN_READ_LTV,
3048*3737Shx147065 		    pcan_p, AN_RID_ESSIDLIST_NEXT, &pcan_p->an_scanresult[i]);
3049*3737Shx147065 	} while ((!ret) && (i < 32) &&
3050*3737Shx147065 	    (pcan_p->an_scanresult[i].an_index != 0xffff));
3051*3737Shx147065 done:
3052*3737Shx147065 	if ((!enable) &&
3053*3737Shx147065 	    (ret = pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0))) {
3054*3737Shx147065 		ret = (int)WL_HW_ERROR;
3055*3737Shx147065 		goto exit;
3056*3737Shx147065 	}
3057*3737Shx147065 	/* record the scan result for future use */
3058*3737Shx147065 	bzero(bssid_t, sizeof (bssid_t));
3059*3737Shx147065 	for (j = 0; j < i; j++) {
3060*3737Shx147065 		/*
3061*3737Shx147065 		 * sometimes, those empty items are recorded by hardware,
3062*3737Shx147065 		 * this is wrong, just ignore those items here.
3063*3737Shx147065 		 */
3064*3737Shx147065 		if (bcmp(pcan_p->an_scanresult[j].an_bssid,
3065*3737Shx147065 		    bssid_t, 6) == 0) {
3066*3737Shx147065 			continue;
3067*3737Shx147065 		}
3068*3737Shx147065 		/*
3069*3737Shx147065 		 * save/update the scan item in scanlist
3070*3737Shx147065 		 */
3071*3737Shx147065 		mutex_enter(&pcan_p->pcan_scanlist_lock);
3072*3737Shx147065 		check_num = 0;
3073*3737Shx147065 		scan_item0 = list_head(&pcan_p->an_scan_list);
3074*3737Shx147065 		if (scan_item0 == NULL) {
3075*3737Shx147065 			if (pcan_add_scan_item(pcan_p,
3076*3737Shx147065 			    pcan_p->an_scanresult[j]) != 0) {
3077*3737Shx147065 				mutex_exit(&pcan_p->pcan_scanlist_lock);
3078*3737Shx147065 				return (WL_SUCCESS);
3079*3737Shx147065 			}
3080*3737Shx147065 		}
3081*3737Shx147065 		for (; scan_item0; ) {
3082*3737Shx147065 			if (bcmp(pcan_p->an_scanresult[j].an_bssid,
3083*3737Shx147065 			    scan_item0->an_val.an_bssid, 6) == 0) {
3084*3737Shx147065 				scan_item0->an_val = pcan_p->an_scanresult[j];
3085*3737Shx147065 				scan_item0->an_timeout = AN_SCAN_TIMEOUT_MAX;
3086*3737Shx147065 				break;
3087*3737Shx147065 			} else {
3088*3737Shx147065 				check_num++;
3089*3737Shx147065 			}
3090*3737Shx147065 			scan_item0 = list_next(&pcan_p->an_scan_list,
3091*3737Shx147065 			    scan_item0);
3092*3737Shx147065 		}
3093*3737Shx147065 		if (check_num == pcan_p->an_scan_num) {
3094*3737Shx147065 			if (pcan_add_scan_item(pcan_p,
3095*3737Shx147065 			    pcan_p->an_scanresult[j]) != 0) {
3096*3737Shx147065 				mutex_exit(&pcan_p->pcan_scanlist_lock);
3097*3737Shx147065 				return (WL_SUCCESS);
3098*3737Shx147065 			}
3099*3737Shx147065 		}
3100*3737Shx147065 		mutex_exit(&pcan_p->pcan_scanlist_lock);
3101*3737Shx147065 	}
3102*3737Shx147065 exit:
3103*3737Shx147065 	if (ret)
3104*3737Shx147065 		cmn_err(CE_WARN, "pcan: scan failed due to hareware error");
3105*3737Shx147065 	return (ret);
3106*3737Shx147065 }
3107*3737Shx147065 
3108*3737Shx147065 /*ARGSUSED*/
3109*3737Shx147065 static int
3110*3737Shx147065 pcan_cfg_scan(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
3111*3737Shx147065 {
3112*3737Shx147065 	wl_ess_conf_t *p_ess_conf;
3113*3737Shx147065 	wldp_t *outfp;
3114*3737Shx147065 	char *buf;
3115*3737Shx147065 	uint16_t i;
3116*3737Shx147065 	an_scan_list_t *scan_item;
3117*3737Shx147065 
3118*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3119*3737Shx147065 	if (buf == NULL) {
3120*3737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_scanlist: failed to alloc "
3121*3737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
3122*3737Shx147065 		return (ENOMEM);
3123*3737Shx147065 	}
3124*3737Shx147065 	outfp = (wldp_t *)buf;
3125*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3126*3737Shx147065 
3127*3737Shx147065 	mutex_enter(&pcan_p->pcan_scanlist_lock);
3128*3737Shx147065 	((wl_ess_list_t *)(outfp->wldp_buf))->wl_ess_list_num =
3129*3737Shx147065 	    pcan_p->an_scan_num;
3130*3737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET +
3131*3737Shx147065 	    offsetof(wl_ess_list_t, wl_ess_list_ess) +
3132*3737Shx147065 	    pcan_p->an_scan_num * sizeof (wl_ess_conf_t);
3133*3737Shx147065 
3134*3737Shx147065 	scan_item = list_head(&pcan_p->an_scan_list);
3135*3737Shx147065 	for (i = 0; i < pcan_p->an_scan_num; i++) {
3136*3737Shx147065 		if (!scan_item)
3137*3737Shx147065 			goto done;
3138*3737Shx147065 
3139*3737Shx147065 		p_ess_conf = (wl_ess_conf_t *)(buf + WIFI_BUF_OFFSET +
3140*3737Shx147065 		    offsetof(wl_ess_list_t, wl_ess_list_ess) +
3141*3737Shx147065 		    i * sizeof (wl_ess_conf_t));
3142*3737Shx147065 		bcopy(scan_item->an_val.an_ssid,
3143*3737Shx147065 		    p_ess_conf->wl_ess_conf_essid.wl_essid_essid,
3144*3737Shx147065 		    mi_strlen(scan_item->an_val.an_ssid));
3145*3737Shx147065 		bcopy(scan_item->an_val.an_bssid,
3146*3737Shx147065 		    p_ess_conf->wl_ess_conf_bssid, 6);
3147*3737Shx147065 		(p_ess_conf->wl_phy_conf).wl_phy_dsss_conf.wl_dsss_subtype
3148*3737Shx147065 		    = WL_DSSS;
3149*3737Shx147065 		p_ess_conf->wl_ess_conf_wepenabled =
3150*3737Shx147065 		    (scan_item->an_val.an_cap & 0x10 ?
3151*3737Shx147065 		    WL_ENC_WEP : WL_NOENCRYPTION);
3152*3737Shx147065 		p_ess_conf->wl_ess_conf_bsstype =
3153*3737Shx147065 		    (scan_item->an_val.an_cap & 0x1 ?
3154*3737Shx147065 		    WL_BSS_BSS : WL_BSS_IBSS);
3155*3737Shx147065 		p_ess_conf->wl_phy_conf.wl_phy_dsss_conf.wl_dsss_channel =
3156*3737Shx147065 		    scan_item->an_val.an_dschannel;
3157*3737Shx147065 		p_ess_conf->wl_ess_conf_sl = 15 -
3158*3737Shx147065 		    ((scan_item->an_val.an_rssi & 0xff) * 15 / 128);
3159*3737Shx147065 		p_ess_conf->wl_supported_rates[0] = WL_RATE_1M;
3160*3737Shx147065 		p_ess_conf->wl_supported_rates[1] = WL_RATE_2M;
3161*3737Shx147065 		p_ess_conf->wl_supported_rates[2] = WL_RATE_5_5M;
3162*3737Shx147065 		p_ess_conf->wl_supported_rates[3] = WL_RATE_11M;
3163*3737Shx147065 		scan_item = list_next(&pcan_p->an_scan_list, scan_item);
3164*3737Shx147065 	}
3165*3737Shx147065 done:
3166*3737Shx147065 	mutex_exit(&pcan_p->pcan_scanlist_lock);
3167*3737Shx147065 	outfp->wldp_result = WL_SUCCESS;
3168*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3169*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3170*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3171*3737Shx147065 	return (WL_SUCCESS);
3172*3737Shx147065 }
3173*3737Shx147065 
3174*3737Shx147065 /*ARGSUSED*/
3175*3737Shx147065 static int
3176*3737Shx147065 pcan_cfg_linkstatus(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
3177*3737Shx147065 {
3178*3737Shx147065 	wldp_t *outfp;
3179*3737Shx147065 	char *buf;
3180*3737Shx147065 	uint16_t i;
3181*3737Shx147065 
3182*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3183*3737Shx147065 	if (buf == NULL) {
3184*3737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_linkstatus: failed to alloc "
3185*3737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
3186*3737Shx147065 		return (ENOMEM);
3187*3737Shx147065 	}
3188*3737Shx147065 	outfp = (wldp_t *)buf;
3189*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3190*3737Shx147065 
3191*3737Shx147065 	if (pcan_p->pcan_flag & PCAN_CARD_LINKUP)
3192*3737Shx147065 		*(wl_linkstatus_t *)(outfp->wldp_buf) = WL_CONNECTED;
3193*3737Shx147065 	else
3194*3737Shx147065 		*(wl_linkstatus_t *)(outfp->wldp_buf) = WL_NOTCONNECTED;
3195*3737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_encryption_t);
3196*3737Shx147065 	outfp->wldp_result = WL_SUCCESS;
3197*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3198*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3199*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3200*3737Shx147065 	return (WL_SUCCESS);
3201*3737Shx147065 }
3202*3737Shx147065 
3203*3737Shx147065 static int
3204*3737Shx147065 pcan_cfg_bsstype(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
3205*3737Shx147065 {
3206*3737Shx147065 	uint16_t i;
3207*3737Shx147065 	wldp_t	*infp;
3208*3737Shx147065 	wldp_t *outfp;
3209*3737Shx147065 	char *buf;
3210*3737Shx147065 	int iret;
3211*3737Shx147065 	struct an_ltv_genconfig *cfg_p;
3212*3737Shx147065 
3213*3737Shx147065 	cfg_p = &pcan_p->an_config;
3214*3737Shx147065 
3215*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3216*3737Shx147065 	if (buf == NULL) {
3217*3737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_bsstype: failed to alloc "
3218*3737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
3219*3737Shx147065 		return (ENOMEM);
3220*3737Shx147065 	}
3221*3737Shx147065 	outfp = (wldp_t *)buf;
3222*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3223*3737Shx147065 	infp = (wldp_t *)mp->b_rptr;
3224*3737Shx147065 
3225*3737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_bss_type_t);
3226*3737Shx147065 
3227*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3228*3737Shx147065 		if (cfg_p->an_opmode == AN_OPMODE_INFR_STATION) {
3229*3737Shx147065 			*(wl_bss_type_t *)(outfp->wldp_buf) = WL_BSS_BSS;
3230*3737Shx147065 		} else if (cfg_p->an_opmode == AN_OPMODE_IBSS_ADHOC) {
3231*3737Shx147065 			*(wl_bss_type_t *)(outfp->wldp_buf) = WL_BSS_IBSS;
3232*3737Shx147065 		}
3233*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3234*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
3235*3737Shx147065 		if (*(wl_bss_type_t *)(infp->wldp_buf) == WL_BSS_BSS)
3236*3737Shx147065 			cfg_p->an_opmode = AN_OPMODE_INFR_STATION;
3237*3737Shx147065 		if (*(wl_bss_type_t *)(infp->wldp_buf) == WL_BSS_IBSS)
3238*3737Shx147065 			cfg_p->an_opmode = AN_OPMODE_IBSS_ADHOC;
3239*3737Shx147065 		if (*(wl_bss_type_t *)(infp->wldp_buf) == WL_BSS_ANY)
3240*3737Shx147065 			cfg_p->an_opmode = AN_OPMODE_INFR_STATION;
3241*3737Shx147065 		cfg_p->an_assoc_timeout = 5000;
3242*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3243*3737Shx147065 	} else {
3244*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3245*3737Shx147065 		return (EINVAL);
3246*3737Shx147065 	}
3247*3737Shx147065 
3248*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3249*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3250*3737Shx147065 	iret = (int)(outfp->wldp_result);
3251*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3252*3737Shx147065 	return (iret);
3253*3737Shx147065 }
3254*3737Shx147065 
3255*3737Shx147065 static int
3256*3737Shx147065 pcan_cfg_phy(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
3257*3737Shx147065 {
3258*3737Shx147065 	uint16_t ret, i;
3259*3737Shx147065 	wldp_t	*infp;
3260*3737Shx147065 	wldp_t *outfp;
3261*3737Shx147065 	char *buf;
3262*3737Shx147065 	int iret;
3263*3737Shx147065 	struct an_ltv_genconfig *cfg_p;
3264*3737Shx147065 	struct an_ltv_status *status_p;
3265*3737Shx147065 
3266*3737Shx147065 	cfg_p = &pcan_p->an_config;
3267*3737Shx147065 	status_p = &pcan_p->an_status;
3268*3737Shx147065 
3269*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3270*3737Shx147065 	if (buf == NULL) {
3271*3737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_phy: failed to alloc "
3272*3737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
3273*3737Shx147065 		return (ENOMEM);
3274*3737Shx147065 	}
3275*3737Shx147065 	outfp = (wldp_t *)buf;
3276*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3277*3737Shx147065 	infp = (wldp_t *)mp->b_rptr;
3278*3737Shx147065 
3279*3737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_dsss_t);
3280*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3281*3737Shx147065 		if (ret = pcan_status_ltv(PCAN_READ_LTV, pcan_p, status_p)) {
3282*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
3283*3737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
3284*3737Shx147065 			goto done;
3285*3737Shx147065 		}
3286*3737Shx147065 		((wl_dsss_t *)(outfp->wldp_buf))->wl_dsss_channel =
3287*3737Shx147065 		    status_p->an_channel_set;
3288*3737Shx147065 		((wl_dsss_t *)(outfp->wldp_buf))->wl_dsss_subtype = WL_DSSS;
3289*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3290*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
3291*3737Shx147065 		ret = (uint16_t)
3292*3737Shx147065 		    (((wl_phy_conf_t *)(infp->wldp_buf))
3293*3737Shx147065 		    ->wl_phy_dsss_conf.wl_dsss_channel);
3294*3737Shx147065 		if (ret < 1 || ret > 14) {
3295*3737Shx147065 			outfp->wldp_result = WL_NOTSUPPORTED;
3296*3737Shx147065 			goto done;
3297*3737Shx147065 		}
3298*3737Shx147065 		cfg_p->an_ds_channel = ret;
3299*3737Shx147065 		cfg_p->an_assoc_timeout = 5000;
3300*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3301*3737Shx147065 	} else {
3302*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3303*3737Shx147065 		return (EINVAL);
3304*3737Shx147065 	}
3305*3737Shx147065 done:
3306*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3307*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3308*3737Shx147065 	iret = (int)(outfp->wldp_result);
3309*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3310*3737Shx147065 	return (iret);
3311*3737Shx147065 
3312*3737Shx147065 }
3313*3737Shx147065 
3314*3737Shx147065 /*ARGSUSED*/
3315*3737Shx147065 static int
3316*3737Shx147065 pcan_cfg_desiredrates(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
3317*3737Shx147065 {
3318*3737Shx147065 	uint16_t i;
3319*3737Shx147065 	uint8_t rates = 0;
3320*3737Shx147065 	wldp_t	*infp;
3321*3737Shx147065 	wldp_t *outfp;
3322*3737Shx147065 	char *buf;
3323*3737Shx147065 	int iret;
3324*3737Shx147065 	struct an_ltv_genconfig *cfg_p;
3325*3737Shx147065 	struct an_ltv_genconfig *actcfg_p;
3326*3737Shx147065 
3327*3737Shx147065 	cfg_p = &pcan_p->an_config;
3328*3737Shx147065 	actcfg_p = &pcan_p->an_actual_config;
3329*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3330*3737Shx147065 	if (buf == NULL) {
3331*3737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_rates: failed to alloc "
3332*3737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
3333*3737Shx147065 		return (ENOMEM);
3334*3737Shx147065 	}
3335*3737Shx147065 	outfp = (wldp_t *)buf;
3336*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3337*3737Shx147065 	infp = (wldp_t *)mp->b_rptr;
3338*3737Shx147065 
3339*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3340*3737Shx147065 		if (pcan_cfg_ltv(PCAN_READ_LTV, pcan_p, actcfg_p)) {
3341*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
3342*3737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
3343*3737Shx147065 			goto done;
3344*3737Shx147065 		}
3345*3737Shx147065 		for (i = 0; i < sizeof (actcfg_p->an_rates); i++) {
3346*3737Shx147065 			if (actcfg_p->an_rates[i] == 0)
3347*3737Shx147065 				break;
3348*3737Shx147065 			rates = MAX(rates, actcfg_p->an_rates[i]);
3349*3737Shx147065 		}
3350*3737Shx147065 		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[0]
3351*3737Shx147065 		    = rates;
3352*3737Shx147065 		((wl_rates_t *)(outfp->wldp_buf))->wl_rates_num = 1;
3353*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET +
3354*3737Shx147065 		    offsetof(wl_rates_t, wl_rates_rates) + sizeof (char);
3355*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3356*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
3357*3737Shx147065 		bzero(cfg_p->an_rates, sizeof (cfg_p->an_rates));
3358*3737Shx147065 		for (i = 0; i < ((wl_rates_t *)(infp->wldp_buf))->wl_rates_num;
3359*3737Shx147065 		    i++) {
3360*3737Shx147065 			cfg_p->an_rates[i] = (((wl_rates_t *)
3361*3737Shx147065 			    (infp->wldp_buf))->wl_rates_rates)[i];
3362*3737Shx147065 		}
3363*3737Shx147065 		cfg_p->an_assoc_timeout = 5000;
3364*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
3365*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3366*3737Shx147065 	} else {
3367*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3368*3737Shx147065 		return (EINVAL);
3369*3737Shx147065 	}
3370*3737Shx147065 done:
3371*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3372*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3373*3737Shx147065 	iret = (int)(outfp->wldp_result);
3374*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3375*3737Shx147065 	return (iret);
3376*3737Shx147065 }
3377*3737Shx147065 
3378*3737Shx147065 /*ARGSUSED*/
3379*3737Shx147065 static int
3380*3737Shx147065 pcan_cfg_supportrates(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
3381*3737Shx147065 {
3382*3737Shx147065 	uint16_t i;
3383*3737Shx147065 	int iret;
3384*3737Shx147065 	wldp_t *outfp;
3385*3737Shx147065 	char *buf;
3386*3737Shx147065 
3387*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3388*3737Shx147065 	if (buf == NULL) {
3389*3737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_supportedrates: failed to alloc "
3390*3737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
3391*3737Shx147065 		return (ENOMEM);
3392*3737Shx147065 	}
3393*3737Shx147065 	outfp = (wldp_t *)buf;
3394*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3395*3737Shx147065 
3396*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3397*3737Shx147065 		((wl_rates_t *)(outfp->wldp_buf))->wl_rates_num = 4;
3398*3737Shx147065 		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[0]
3399*3737Shx147065 		    = WL_RATE_1M;
3400*3737Shx147065 		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[1]
3401*3737Shx147065 		    = WL_RATE_2M;
3402*3737Shx147065 		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[2]
3403*3737Shx147065 		    = WL_RATE_5_5M;
3404*3737Shx147065 		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[3]
3405*3737Shx147065 		    = WL_RATE_11M;
3406*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET +
3407*3737Shx147065 		    offsetof(wl_rates_t, wl_rates_rates) +
3408*3737Shx147065 		    4 * sizeof (char);
3409*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3410*3737Shx147065 	} else {
3411*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3412*3737Shx147065 		return (EINVAL);
3413*3737Shx147065 	}
3414*3737Shx147065 done:
3415*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3416*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3417*3737Shx147065 	iret = (int)(outfp->wldp_result);
3418*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3419*3737Shx147065 	return (iret);
3420*3737Shx147065 }
3421*3737Shx147065 
3422*3737Shx147065 /*ARGSUSED*/
3423*3737Shx147065 static int
3424*3737Shx147065 pcan_cfg_powermode(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
3425*3737Shx147065 {
3426*3737Shx147065 	uint16_t i;
3427*3737Shx147065 	wldp_t *outfp;
3428*3737Shx147065 	char *buf;
3429*3737Shx147065 	int iret;
3430*3737Shx147065 	struct an_ltv_genconfig *actcfg_p;
3431*3737Shx147065 
3432*3737Shx147065 	actcfg_p = &pcan_p->an_actual_config;
3433*3737Shx147065 
3434*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3435*3737Shx147065 	if (buf == NULL) {
3436*3737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_powermode: failed to alloc "
3437*3737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
3438*3737Shx147065 		return (ENOMEM);
3439*3737Shx147065 	}
3440*3737Shx147065 	outfp = (wldp_t *)buf;
3441*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3442*3737Shx147065 
3443*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3444*3737Shx147065 		if (pcan_cfg_ltv(PCAN_READ_LTV, pcan_p, actcfg_p)) {
3445*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
3446*3737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
3447*3737Shx147065 			goto done;
3448*3737Shx147065 		}
3449*3737Shx147065 		((wl_ps_mode_t *)(outfp->wldp_buf))->wl_ps_mode =
3450*3737Shx147065 		    actcfg_p->an_psave_mode;
3451*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET +
3452*3737Shx147065 		    sizeof (wl_ps_mode_t);
3453*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3454*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
3455*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
3456*3737Shx147065 		outfp->wldp_result = WL_LACK_FEATURE;
3457*3737Shx147065 	} else {
3458*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3459*3737Shx147065 		return (EINVAL);
3460*3737Shx147065 	}
3461*3737Shx147065 done:
3462*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3463*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3464*3737Shx147065 	iret = (int)(outfp->wldp_result);
3465*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3466*3737Shx147065 	return (iret);
3467*3737Shx147065 
3468*3737Shx147065 }
3469*3737Shx147065 
3470*3737Shx147065 static int
3471*3737Shx147065 pcan_cfg_authmode(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
3472*3737Shx147065 {
3473*3737Shx147065 	uint16_t i;
3474*3737Shx147065 	wldp_t *outfp;
3475*3737Shx147065 	char *buf;
3476*3737Shx147065 	int iret;
3477*3737Shx147065 	struct an_ltv_genconfig *cfg_p;
3478*3737Shx147065 	struct an_ltv_genconfig *actcfg_p;
3479*3737Shx147065 
3480*3737Shx147065 	cfg_p = &pcan_p->an_config;
3481*3737Shx147065 	actcfg_p = &pcan_p->an_actual_config;
3482*3737Shx147065 
3483*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3484*3737Shx147065 	if (buf == NULL) {
3485*3737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_autymode: failed to alloc "
3486*3737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
3487*3737Shx147065 		return (ENOMEM);
3488*3737Shx147065 	}
3489*3737Shx147065 	outfp = (wldp_t *)buf;
3490*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3491*3737Shx147065 
3492*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3493*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_authmode_t);
3494*3737Shx147065 		if (cfg_p->an_authtype & AN_AUTHTYPE_SHAREDKEY) {
3495*3737Shx147065 			*(wl_bss_type_t *)(outfp->wldp_buf) = WL_SHAREDKEY;
3496*3737Shx147065 		} else {
3497*3737Shx147065 			*(wl_bss_type_t *)(outfp->wldp_buf) = WL_OPENSYSTEM;
3498*3737Shx147065 		}
3499*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3500*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
3501*3737Shx147065 		if (*(wl_authmode_t *)(outfp->wldp_buf) == WL_OPENSYSTEM) {
3502*3737Shx147065 			cfg_p->an_authtype |= AN_AUTHTYPE_OPEN;
3503*3737Shx147065 			cfg_p->an_assoc_timeout = 5000;
3504*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
3505*3737Shx147065 			outfp->wldp_result = WL_SUCCESS;
3506*3737Shx147065 		} else {
3507*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
3508*3737Shx147065 			outfp->wldp_result = WL_LACK_FEATURE;
3509*3737Shx147065 		}
3510*3737Shx147065 	} else {
3511*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3512*3737Shx147065 		return (EINVAL);
3513*3737Shx147065 	}
3514*3737Shx147065 	PCANDBG((CE_NOTE, "pcan cfg_authmode: actual.authmode=%x",
3515*3737Shx147065 	    actcfg_p->an_authtype));
3516*3737Shx147065 	PCANDBG((CE_NOTE, "pcan cfg_authmode: actual.home_product=%x",
3517*3737Shx147065 	    actcfg_p->an_rsvd6[2]));
3518*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3519*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3520*3737Shx147065 	iret = (int)(outfp->wldp_result);
3521*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3522*3737Shx147065 	return (iret);
3523*3737Shx147065 }
3524*3737Shx147065 
3525*3737Shx147065 static int
3526*3737Shx147065 pcan_cfg_encryption(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
3527*3737Shx147065 {
3528*3737Shx147065 	uint16_t i;
3529*3737Shx147065 	wldp_t *outfp;
3530*3737Shx147065 	char *buf;
3531*3737Shx147065 	int iret;
3532*3737Shx147065 	struct an_ltv_genconfig *cfg_p;
3533*3737Shx147065 
3534*3737Shx147065 	cfg_p = &pcan_p->an_config;
3535*3737Shx147065 
3536*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3537*3737Shx147065 	if (buf == NULL) {
3538*3737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_encryption: failed to alloc "
3539*3737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
3540*3737Shx147065 		return (ENOMEM);
3541*3737Shx147065 	}
3542*3737Shx147065 	outfp = (wldp_t *)buf;
3543*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3544*3737Shx147065 
3545*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3546*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_encryption_t);
3547*3737Shx147065 		if (cfg_p->an_authtype & AN_AUTHTYPE_ENABLEWEP) {
3548*3737Shx147065 			*(wl_bss_type_t *)(outfp->wldp_buf) = WL_ENC_WEP;
3549*3737Shx147065 		} else {
3550*3737Shx147065 			*(wl_bss_type_t *)(outfp->wldp_buf) = WL_NOENCRYPTION;
3551*3737Shx147065 		}
3552*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3553*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
3554*3737Shx147065 		if (*(wl_encryption_t *)(outfp->wldp_buf) == WL_ENC_WEP) {
3555*3737Shx147065 			cfg_p->an_authtype |= (AN_AUTHTYPE_ENABLEWEP |
3556*3737Shx147065 			    AN_AUTHTYPE_ALLOW_UNENCRYPTED);
3557*3737Shx147065 			pcan_p->pcan_usewep = 1;
3558*3737Shx147065 		}
3559*3737Shx147065 		if (*(wl_authmode_t *)(outfp->wldp_buf) == WL_NOENCRYPTION) {
3560*3737Shx147065 			cfg_p->an_authtype &= (~(AN_AUTHTYPE_ENABLEWEP |
3561*3737Shx147065 			    AN_AUTHTYPE_ALLOW_UNENCRYPTED));
3562*3737Shx147065 			pcan_p->pcan_usewep = 0;
3563*3737Shx147065 		}
3564*3737Shx147065 		cfg_p->an_assoc_timeout = 5000;
3565*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
3566*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3567*3737Shx147065 	} else {
3568*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3569*3737Shx147065 		return (EINVAL);
3570*3737Shx147065 	}
3571*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3572*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3573*3737Shx147065 	iret = (int)(outfp->wldp_result);
3574*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3575*3737Shx147065 	return (iret);
3576*3737Shx147065 }
3577*3737Shx147065 
3578*3737Shx147065 static int
3579*3737Shx147065 pcan_cfg_wepkeyid(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
3580*3737Shx147065 {
3581*3737Shx147065 	uint16_t i, ret;
3582*3737Shx147065 	wldp_t	*infp;
3583*3737Shx147065 	wldp_t *outfp;
3584*3737Shx147065 	char *buf;
3585*3737Shx147065 	int iret;
3586*3737Shx147065 	struct an_ltv_wepkey wepkey;
3587*3737Shx147065 
3588*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3589*3737Shx147065 	if (buf == NULL) {
3590*3737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_wepkeyid: failed to alloc "
3591*3737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
3592*3737Shx147065 		return (ENOMEM);
3593*3737Shx147065 	}
3594*3737Shx147065 	outfp = (wldp_t *)buf;
3595*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3596*3737Shx147065 	infp = (wldp_t *)mp->b_rptr;
3597*3737Shx147065 
3598*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3599*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_wep_key_id_t);
3600*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3601*3737Shx147065 		*(wl_wep_key_id_t *)(outfp->wldp_buf) = pcan_p->an_cur_wepkey;
3602*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
3603*3737Shx147065 		ret = (uint16_t)(*(wl_wep_key_id_t *)(infp->wldp_buf));
3604*3737Shx147065 		if (ret > 3) {
3605*3737Shx147065 			kmem_free(buf, MAX_BUF_LEN);
3606*3737Shx147065 			return (EINVAL);
3607*3737Shx147065 		}
3608*3737Shx147065 		wepkey.an_index = 0xffff;
3609*3737Shx147065 		wepkey.an_macaddr[0] = ret & 0xff;
3610*3737Shx147065 		pcan_p->an_cur_wepkey = ret;
3611*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
3612*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3613*3737Shx147065 	} else {
3614*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3615*3737Shx147065 		return (EINVAL);
3616*3737Shx147065 	}
3617*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3618*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3619*3737Shx147065 	iret = (int)(outfp->wldp_result);
3620*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3621*3737Shx147065 	return (iret);
3622*3737Shx147065 }
3623*3737Shx147065 
3624*3737Shx147065 /*ARGSUSED*/
3625*3737Shx147065 static int
3626*3737Shx147065 pcan_cfg_createibss(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
3627*3737Shx147065 {
3628*3737Shx147065 	uint16_t i;
3629*3737Shx147065 	wldp_t *outfp;
3630*3737Shx147065 	char *buf;
3631*3737Shx147065 	int iret;
3632*3737Shx147065 
3633*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3634*3737Shx147065 	if (buf == NULL) {
3635*3737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_createibss: failed to alloc "
3636*3737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
3637*3737Shx147065 		return (ENOMEM);
3638*3737Shx147065 	}
3639*3737Shx147065 	outfp = (wldp_t *)buf;
3640*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3641*3737Shx147065 
3642*3737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_create_ibss_t);
3643*3737Shx147065 	outfp->wldp_result = WL_LACK_FEATURE;
3644*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3645*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3646*3737Shx147065 	iret = (int)(outfp->wldp_result);
3647*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3648*3737Shx147065 	return (iret);
3649*3737Shx147065 }
3650*3737Shx147065 
3651*3737Shx147065 static int
3652*3737Shx147065 pcan_cfg_rssi(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
3653*3737Shx147065 {
3654*3737Shx147065 	uint16_t i, val;
3655*3737Shx147065 	int iret;
3656*3737Shx147065 	wldp_t *outfp;
3657*3737Shx147065 	char *buf;
3658*3737Shx147065 	struct an_ltv_status *status_p;
3659*3737Shx147065 	status_p = &pcan_p->an_status;
3660*3737Shx147065 
3661*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3662*3737Shx147065 	if (buf == NULL) {
3663*3737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_rssi: failed to alloc "
3664*3737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
3665*3737Shx147065 		return (ENOMEM);
3666*3737Shx147065 	}
3667*3737Shx147065 	outfp = (wldp_t *)buf;
3668*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3669*3737Shx147065 
3670*3737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_rssi_t);
3671*3737Shx147065 
3672*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3673*3737Shx147065 		if (val = pcan_status_ltv(PCAN_READ_LTV, pcan_p, status_p)) {
3674*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
3675*3737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
3676*3737Shx147065 			goto done;
3677*3737Shx147065 		}
3678*3737Shx147065 		val = status_p->an_cur_signal_quality;
3679*3737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_rssi: sl=%x", val));
3680*3737Shx147065 		/*
3681*3737Shx147065 		 * we reflect the value to 1-15 as rssi
3682*3737Shx147065 		 */
3683*3737Shx147065 		*(wl_rssi_t *)(outfp->wldp_buf) = 15 -
3684*3737Shx147065 		    ((val & 0xff) * 15 / 128 + 1);
3685*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3686*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
3687*3737Shx147065 		outfp->wldp_result = WL_READONLY;
3688*3737Shx147065 	} else {
3689*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3690*3737Shx147065 		return (EINVAL);
3691*3737Shx147065 	}
3692*3737Shx147065 done:
3693*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3694*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3695*3737Shx147065 	iret = (int)(outfp->wldp_result);
3696*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3697*3737Shx147065 	return (iret);
3698*3737Shx147065 }
3699*3737Shx147065 
3700*3737Shx147065 /*ARGSUSED*/
3701*3737Shx147065 static int
3702*3737Shx147065 pcan_cfg_radio(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
3703*3737Shx147065 {
3704*3737Shx147065 	uint16_t i;
3705*3737Shx147065 	int iret;
3706*3737Shx147065 	wldp_t *outfp;
3707*3737Shx147065 	char *buf;
3708*3737Shx147065 
3709*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3710*3737Shx147065 	if (buf == NULL) {
3711*3737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_radio: failed to alloc "
3712*3737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
3713*3737Shx147065 		return (ENOMEM);
3714*3737Shx147065 	}
3715*3737Shx147065 	outfp = (wldp_t *)buf;
3716*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3717*3737Shx147065 
3718*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3719*3737Shx147065 		*(wl_radio_t *)(outfp->wldp_buf) = B_TRUE;
3720*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_radio_t);
3721*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3722*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
3723*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
3724*3737Shx147065 		outfp->wldp_result = WL_LACK_FEATURE;
3725*3737Shx147065 	} else {
3726*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3727*3737Shx147065 		return (EINVAL);
3728*3737Shx147065 	}
3729*3737Shx147065 
3730*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3731*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3732*3737Shx147065 	iret = (int)(outfp->wldp_result);
3733*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3734*3737Shx147065 	return (iret);
3735*3737Shx147065 }
3736*3737Shx147065 
3737*3737Shx147065 static int
3738*3737Shx147065 pcan_cfg_wepkey(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
3739*3737Shx147065 {
3740*3737Shx147065 	uint16_t i;
3741*3737Shx147065 	wl_wep_key_t *p_wepkey_tab;
3742*3737Shx147065 	wldp_t *outfp;
3743*3737Shx147065 	char *buf;
3744*3737Shx147065 	int iret;
3745*3737Shx147065 	wldp_t	*infp;
3746*3737Shx147065 	struct an_ltv_wepkey *wepkey_p;
3747*3737Shx147065 
3748*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3749*3737Shx147065 	if (buf == NULL) {
3750*3737Shx147065 		PCANDBG((CE_NOTE, "pcan cfg_wep: failed to alloc "
3751*3737Shx147065 		    "memory(%d)\n", MAX_BUF_LEN));
3752*3737Shx147065 		return (ENOMEM);
3753*3737Shx147065 	}
3754*3737Shx147065 	outfp = (wldp_t *)buf;
3755*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3756*3737Shx147065 	infp = (wldp_t *)mp->b_rptr;
3757*3737Shx147065 
3758*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3759*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET +
3760*3737Shx147065 		    sizeof (wl_wep_key_tab_t);
3761*3737Shx147065 		outfp->wldp_result = WL_WRITEONLY;
3762*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
3763*3737Shx147065 		p_wepkey_tab = (wl_wep_key_t *)(infp->wldp_buf);
3764*3737Shx147065 		for (i = 0; i < MAX_NWEPKEYS; i++) {
3765*3737Shx147065 			if (p_wepkey_tab[i].wl_wep_operation == WL_ADD) {
3766*3737Shx147065 				wepkey_p = &pcan_p->an_wepkey[i];
3767*3737Shx147065 				bzero(wepkey_p, sizeof (*wepkey_p));
3768*3737Shx147065 				wepkey_p->an_keylen =
3769*3737Shx147065 					p_wepkey_tab[i].wl_wep_length;
3770*3737Shx147065 				bcopy(p_wepkey_tab[i].wl_wep_key,
3771*3737Shx147065 					wepkey_p->an_key,
3772*3737Shx147065 					p_wepkey_tab[i].wl_wep_length);
3773*3737Shx147065 				wepkey_p->an_index = i;
3774*3737Shx147065 				wepkey_p->an_macaddr[0] = 1;
3775*3737Shx147065 			}
3776*3737Shx147065 		}
3777*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
3778*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3779*3737Shx147065 	} else {
3780*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3781*3737Shx147065 		return (EINVAL);
3782*3737Shx147065 	}
3783*3737Shx147065 
3784*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3785*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3786*3737Shx147065 	iret = (int)(outfp->wldp_result);
3787*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3788*3737Shx147065 	return (iret);
3789*3737Shx147065 }
3790*3737Shx147065 
3791*3737Shx147065 static void
3792*3737Shx147065 pcan_connect_timeout(void *arg)
3793*3737Shx147065 {
3794*3737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
3795*3737Shx147065 	uint16_t ret;
3796*3737Shx147065 
3797*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
3798*3737Shx147065 	if (ret = pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0))
3799*3737Shx147065 		goto done;
3800*3737Shx147065 	pcan_p->pcan_flag &= ~PCAN_CARD_LINKUP;
3801*3737Shx147065 	if (ret = pcan_config_mac(pcan_p))
3802*3737Shx147065 		goto done;
3803*3737Shx147065 	ret = pcan_set_cmd(pcan_p, AN_CMD_ENABLE, 0);
3804*3737Shx147065 done:
3805*3737Shx147065 	if (ret)
3806*3737Shx147065 		cmn_err(CE_WARN, "pcan: connect failed due to hareware error");
3807*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
3808*3737Shx147065 	pcan_p->pcan_connect_timeout_id = 0;
3809*3737Shx147065 }
3810*3737Shx147065 
3811*3737Shx147065 static int
3812*3737Shx147065 pcan_getset(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
3813*3737Shx147065 {
3814*3737Shx147065 	int ret = WL_SUCCESS;
3815*3737Shx147065 	int connect = 0;
3816*3737Shx147065 
3817*3737Shx147065 	mutex_enter(&pcan_p->pcan_glock);
3818*3737Shx147065 	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
3819*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
3820*3737Shx147065 		return (PCAN_FAIL);
3821*3737Shx147065 	}
3822*3737Shx147065 
3823*3737Shx147065 	switch (((wldp_t *)mp->b_rptr)->wldp_id) {
3824*3737Shx147065 	case WL_ESSID:
3825*3737Shx147065 		ret = pcan_cfg_essid(mp, pcan_p, cmd);
3826*3737Shx147065 		connect = 1;
3827*3737Shx147065 		PCANDBG((CE_NOTE, "cfg_essid\n"));
3828*3737Shx147065 		break;
3829*3737Shx147065 	case WL_BSSID:
3830*3737Shx147065 		ret = pcan_cfg_bssid(mp, pcan_p, cmd);
3831*3737Shx147065 		connect = 1;
3832*3737Shx147065 		PCANDBG((CE_NOTE, "cfg_bssid\n"));
3833*3737Shx147065 		break;
3834*3737Shx147065 	case WL_ESS_LIST:
3835*3737Shx147065 		ret = pcan_cfg_scan(mp, pcan_p, cmd);
3836*3737Shx147065 		PCANDBG((CE_NOTE, "cfg_scan\n"));
3837*3737Shx147065 		break;
3838*3737Shx147065 	case WL_LINKSTATUS:
3839*3737Shx147065 		ret = pcan_cfg_linkstatus(mp, pcan_p, cmd);
3840*3737Shx147065 		PCANDBG((CE_NOTE, "cfg_linkstatus\n"));
3841*3737Shx147065 		break;
3842*3737Shx147065 	case WL_BSS_TYPE:
3843*3737Shx147065 		ret = pcan_cfg_bsstype(mp, pcan_p, cmd);
3844*3737Shx147065 		connect = 1;
3845*3737Shx147065 		PCANDBG((CE_NOTE, "cfg_bsstype\n"));
3846*3737Shx147065 		break;
3847*3737Shx147065 	case WL_PHY_CONFIG:
3848*3737Shx147065 		ret = pcan_cfg_phy(mp, pcan_p, cmd);
3849*3737Shx147065 		connect = 1;
3850*3737Shx147065 		PCANDBG((CE_NOTE, "cfg_phy\n"));
3851*3737Shx147065 		break;
3852*3737Shx147065 	case WL_DESIRED_RATES:
3853*3737Shx147065 		ret = pcan_cfg_desiredrates(mp, pcan_p, cmd);
3854*3737Shx147065 		connect = 1;
3855*3737Shx147065 		PCANDBG((CE_NOTE, "cfg_disred-rates\n"));
3856*3737Shx147065 		break;
3857*3737Shx147065 	case WL_SUPPORTED_RATES:
3858*3737Shx147065 		ret = pcan_cfg_supportrates(mp, pcan_p, cmd);
3859*3737Shx147065 		PCANDBG((CE_NOTE, "cfg_supported-rates\n"));
3860*3737Shx147065 		break;
3861*3737Shx147065 	case WL_POWER_MODE:
3862*3737Shx147065 		ret = pcan_cfg_powermode(mp, pcan_p, cmd);
3863*3737Shx147065 		PCANDBG((CE_NOTE, "cfg_powermode\n"));
3864*3737Shx147065 		break;
3865*3737Shx147065 	case WL_AUTH_MODE:
3866*3737Shx147065 		ret = pcan_cfg_authmode(mp, pcan_p, cmd);
3867*3737Shx147065 		connect = 1;
3868*3737Shx147065 		PCANDBG((CE_NOTE, "cfg_authmode\n"));
3869*3737Shx147065 		break;
3870*3737Shx147065 	case WL_ENCRYPTION:
3871*3737Shx147065 		ret = pcan_cfg_encryption(mp, pcan_p, cmd);
3872*3737Shx147065 		connect = 1;
3873*3737Shx147065 		PCANDBG((CE_NOTE, "cfg_encryption\n"));
3874*3737Shx147065 		break;
3875*3737Shx147065 	case WL_WEP_KEY_ID:
3876*3737Shx147065 		ret = pcan_cfg_wepkeyid(mp, pcan_p, cmd);
3877*3737Shx147065 		connect = 1;
3878*3737Shx147065 		PCANDBG((CE_NOTE, "cfg_wepkeyid\n"));
3879*3737Shx147065 		break;
3880*3737Shx147065 	case WL_CREATE_IBSS:
3881*3737Shx147065 		ret = pcan_cfg_createibss(mp, pcan_p, cmd);
3882*3737Shx147065 		connect = 1;
3883*3737Shx147065 		PCANDBG((CE_NOTE, "cfg_create-ibss\n"));
3884*3737Shx147065 		break;
3885*3737Shx147065 	case WL_RSSI:
3886*3737Shx147065 		ret = pcan_cfg_rssi(mp, pcan_p, cmd);
3887*3737Shx147065 		PCANDBG((CE_NOTE, "cfg_rssi\n"));
3888*3737Shx147065 		break;
3889*3737Shx147065 	case WL_RADIO:
3890*3737Shx147065 		ret = pcan_cfg_radio(mp, pcan_p, cmd);
3891*3737Shx147065 		PCANDBG((CE_NOTE, "cfg_radio\n"));
3892*3737Shx147065 		break;
3893*3737Shx147065 	case WL_WEP_KEY_TAB:
3894*3737Shx147065 		ret = pcan_cfg_wepkey(mp, pcan_p, cmd);
3895*3737Shx147065 		connect = 1;
3896*3737Shx147065 		PCANDBG((CE_NOTE, "cfg_wepkey\n"));
3897*3737Shx147065 		break;
3898*3737Shx147065 	case WL_SCAN:
3899*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
3900*3737Shx147065 		if (pcan_p->pcan_connect_timeout_id != 0) {
3901*3737Shx147065 			(void) untimeout(pcan_p->pcan_connect_timeout_id);
3902*3737Shx147065 			pcan_p->pcan_connect_timeout_id = 0;
3903*3737Shx147065 		}
3904*3737Shx147065 		mutex_enter(&pcan_p->pcan_glock);
3905*3737Shx147065 		ret = pcan_cmd_scan(pcan_p);
3906*3737Shx147065 		/*
3907*3737Shx147065 		 * a trick here.
3908*3737Shx147065 		 * since the scan doesn't return too many items due to hardware
3909*3737Shx147065 		 * reason, so the current scan result is an accumulation of
3910*3737Shx147065 		 * several scans. For the first time or after many of the items
3911*3737Shx147065 		 * aged, we scan again if too few items now in the scan table.
3912*3737Shx147065 		 */
3913*3737Shx147065 		if (pcan_p->an_scan_num < AN_SCAN_AGAIN_THRESHOLD)
3914*3737Shx147065 			ret = pcan_cmd_scan(pcan_p);
3915*3737Shx147065 		break;
3916*3737Shx147065 	case WL_LOAD_DEFAULTS:
3917*3737Shx147065 		if (ret = pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0)) {
3918*3737Shx147065 			ret = (int)WL_HW_ERROR;
3919*3737Shx147065 			break;
3920*3737Shx147065 		}
3921*3737Shx147065 		if (ret = pcan_loaddef(pcan_p)) {
3922*3737Shx147065 			ret = (int)WL_HW_ERROR;
3923*3737Shx147065 			break;
3924*3737Shx147065 		}
3925*3737Shx147065 		if (ret = pcan_set_cmd(pcan_p, AN_CMD_ENABLE, 0)) {
3926*3737Shx147065 			ret = (int)WL_HW_ERROR;
3927*3737Shx147065 			break;
3928*3737Shx147065 		}
3929*3737Shx147065 		PCANDBG((CE_NOTE, "loaddef\n"));
3930*3737Shx147065 		break;
3931*3737Shx147065 	case WL_DISASSOCIATE:
3932*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
3933*3737Shx147065 		if (pcan_p->pcan_connect_timeout_id != 0) {
3934*3737Shx147065 			(void) untimeout(pcan_p->pcan_connect_timeout_id);
3935*3737Shx147065 			pcan_p->pcan_connect_timeout_id = 0;
3936*3737Shx147065 		}
3937*3737Shx147065 		mutex_enter(&pcan_p->pcan_glock);
3938*3737Shx147065 		pcan_p->pcan_flag &= ~PCAN_CARD_LINKUP;
3939*3737Shx147065 		if (ret = pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0)) {
3940*3737Shx147065 			ret = (int)WL_HW_ERROR;
3941*3737Shx147065 			break;
3942*3737Shx147065 		}
3943*3737Shx147065 		if (ret = pcan_loaddef(pcan_p)) {
3944*3737Shx147065 			ret = (int)WL_HW_ERROR;
3945*3737Shx147065 			break;
3946*3737Shx147065 		}
3947*3737Shx147065 		PCANDBG((CE_NOTE, "disassociate\n"));
3948*3737Shx147065 		break;
3949*3737Shx147065 	case WL_REASSOCIATE:
3950*3737Shx147065 	case WL_ASSOCIAT:
3951*3737Shx147065 		mutex_exit(&pcan_p->pcan_glock);
3952*3737Shx147065 		if (pcan_p->pcan_connect_timeout_id != 0) {
3953*3737Shx147065 			(void) untimeout(pcan_p->pcan_connect_timeout_id);
3954*3737Shx147065 			pcan_p->pcan_connect_timeout_id = 0;
3955*3737Shx147065 		}
3956*3737Shx147065 		mutex_enter(&pcan_p->pcan_glock);
3957*3737Shx147065 		if (ret = pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0)) {
3958*3737Shx147065 			ret = (int)WL_HW_ERROR;
3959*3737Shx147065 			break;
3960*3737Shx147065 		}
3961*3737Shx147065 		pcan_p->pcan_flag &= ~PCAN_CARD_LINKUP;
3962*3737Shx147065 		if (ret = pcan_config_mac(pcan_p)) {
3963*3737Shx147065 			ret = (int)WL_HW_ERROR;
3964*3737Shx147065 			break;
3965*3737Shx147065 		}
3966*3737Shx147065 		if (ret = pcan_set_cmd(pcan_p, AN_CMD_ENABLE, 0)) {
3967*3737Shx147065 			ret = (int)WL_HW_ERROR;
3968*3737Shx147065 			break;
3969*3737Shx147065 		}
3970*3737Shx147065 		PCANDBG((CE_NOTE, "associate"));
3971*3737Shx147065 		break;
3972*3737Shx147065 
3973*3737Shx147065 	default:
3974*3737Shx147065 		break;
3975*3737Shx147065 	}
3976*3737Shx147065 	mutex_exit(&pcan_p->pcan_glock);
3977*3737Shx147065 	if ((cmd == WLAN_SET_PARAM) && (ret == WL_SUCCESS) && (connect)) {
3978*3737Shx147065 		pcan_p->pcan_flag &= ~PCAN_CARD_LINKUP;
3979*3737Shx147065 		(void) pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0);
3980*3737Shx147065 		if (pcan_p->pcan_connect_timeout_id != 0) {
3981*3737Shx147065 			(void) untimeout(pcan_p->pcan_connect_timeout_id);
3982*3737Shx147065 			pcan_p->pcan_connect_timeout_id = 0;
3983*3737Shx147065 		}
3984*3737Shx147065 		pcan_p->pcan_connect_timeout_id = timeout(pcan_connect_timeout,
3985*3737Shx147065 		    pcan_p, drv_usectohz(1000000));
3986*3737Shx147065 	}
3987*3737Shx147065 	return (ret);
3988*3737Shx147065 }
3989*3737Shx147065 
3990*3737Shx147065 static void
3991*3737Shx147065 pcan_wlan_ioctl(pcan_maci_t *pcan_p, queue_t *wq, mblk_t *mp, uint32_t cmd)
3992*3737Shx147065 {
3993*3737Shx147065 
3994*3737Shx147065 	struct	iocblk	*iocp = (struct iocblk *)mp->b_rptr;
3995*3737Shx147065 	uint32_t len, ret;
3996*3737Shx147065 	mblk_t	*mp1;
3997*3737Shx147065 
3998*3737Shx147065 	/* sanity check */
3999*3737Shx147065 	if (iocp->ioc_count == 0 || !(mp1 = mp->b_cont)) {
4000*3737Shx147065 		miocnak(wq, mp, 0, EINVAL);
4001*3737Shx147065 		return;
4002*3737Shx147065 	}
4003*3737Shx147065 
4004*3737Shx147065 	/* assuming single data block */
4005*3737Shx147065 	if (mp1->b_cont) {
4006*3737Shx147065 		freemsg(mp1->b_cont);
4007*3737Shx147065 		mp1->b_cont = NULL;
4008*3737Shx147065 	}
4009*3737Shx147065 
4010*3737Shx147065 	/* we will overwrite everything */
4011*3737Shx147065 	mp1->b_wptr = mp1->b_rptr;
4012*3737Shx147065 
4013*3737Shx147065 	ret = pcan_getset(mp1, pcan_p, cmd);
4014*3737Shx147065 	len = msgdsize(mp1);
4015*3737Shx147065 	miocack(wq, mp, len, ret);
4016*3737Shx147065 }
4017*3737Shx147065 
4018*3737Shx147065 static void
4019*3737Shx147065 pcan_ioctl(void *arg, queue_t *wq, mblk_t *mp)
4020*3737Shx147065 {
4021*3737Shx147065 	struct iocblk *iocp;
4022*3737Shx147065 	uint32_t cmd, ret;
4023*3737Shx147065 	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
4024*3737Shx147065 	boolean_t need_privilege = B_TRUE;
4025*3737Shx147065 
4026*3737Shx147065 	iocp = (struct iocblk *)mp->b_rptr;
4027*3737Shx147065 	iocp->ioc_error = 0;
4028*3737Shx147065 	cmd = iocp->ioc_cmd;
4029*3737Shx147065 	switch (cmd) {
4030*3737Shx147065 	default:
4031*3737Shx147065 		miocnak(wq, mp, 0, EINVAL);
4032*3737Shx147065 		return;
4033*3737Shx147065 	case WLAN_GET_PARAM:
4034*3737Shx147065 		need_privilege = B_FALSE;
4035*3737Shx147065 		break;
4036*3737Shx147065 	case WLAN_SET_PARAM:
4037*3737Shx147065 	case WLAN_COMMAND:
4038*3737Shx147065 		break;
4039*3737Shx147065 	}
4040*3737Shx147065 
4041*3737Shx147065 	/* Check net_config privilege */
4042*3737Shx147065 	if (need_privilege) {
4043*3737Shx147065 		if (ret = secpolicy_net_config(iocp->ioc_cr, B_FALSE)) {
4044*3737Shx147065 			miocnak(wq, mp, 0, ret);
4045*3737Shx147065 			return;
4046*3737Shx147065 		}
4047*3737Shx147065 	}
4048*3737Shx147065 
4049*3737Shx147065 	pcan_wlan_ioctl(pcan_p, wq, mp, cmd);
4050*3737Shx147065 }
4051