xref: /onnv-gate/usr/src/uts/common/io/pcwl/pcwl.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 "pcwl.h"
58*3737Shx147065 #include <sys/mac_wifi.h>
59*3737Shx147065 #include <inet/wifi_ioctl.h>
60*3737Shx147065 
61*3737Shx147065 #ifdef DEBUG
62*3737Shx147065 #define	PCWL_DBG_BASIC		0x1
63*3737Shx147065 #define	PCWL_DBG_INFO		0x2
64*3737Shx147065 #define	PCWL_DBG_SEND		0x4
65*3737Shx147065 #define	PCWL_DBG_RCV		0x8
66*3737Shx147065 #define	PCWL_DBG_LINKINFO	0x10
67*3737Shx147065 uint32_t pcwl_debug = 0;
68*3737Shx147065 #define	PCWLDBG(x) \
69*3737Shx147065 	if (pcwl_debug & PCWL_DBG_BASIC) cmn_err x
70*3737Shx147065 #else
71*3737Shx147065 #define	PCWLDBG(x)
72*3737Shx147065 #endif
73*3737Shx147065 
74*3737Shx147065 /* for pci card */
75*3737Shx147065 static ddi_device_acc_attr_t accattr = {
76*3737Shx147065 		DDI_DEVICE_ATTR_V0,
77*3737Shx147065 		DDI_NEVERSWAP_ACC,
78*3737Shx147065 		DDI_STRICTORDER_ACC,
79*3737Shx147065 		DDI_DEFAULT_ACC
80*3737Shx147065 };
81*3737Shx147065 
82*3737Shx147065 void *pcwl_soft_state_p = NULL;
83*3737Shx147065 static int pcwl_device_type;
84*3737Shx147065 
85*3737Shx147065 mac_callbacks_t pcwl_m_callbacks = {
86*3737Shx147065 	MC_IOCTL,
87*3737Shx147065 	pcwl_gstat,
88*3737Shx147065 	pcwl_start,
89*3737Shx147065 	pcwl_stop,
90*3737Shx147065 	pcwl_prom,
91*3737Shx147065 	pcwl_sdmulti,
92*3737Shx147065 	pcwl_saddr,
93*3737Shx147065 	pcwl_tx,
94*3737Shx147065 	NULL,
95*3737Shx147065 	pcwl_ioctl
96*3737Shx147065 };
97*3737Shx147065 
98*3737Shx147065 static char *pcwl_name_str = "pcwl";
99*3737Shx147065 
100*3737Shx147065 DDI_DEFINE_STREAM_OPS(pcwl_dev_ops, nulldev, pcwl_probe, pcwl_attach,
101*3737Shx147065     pcwl_detach, nodev, NULL, D_MP, NULL);
102*3737Shx147065 
103*3737Shx147065 extern struct mod_ops mod_driverops;
104*3737Shx147065 static struct modldrv modldrv = {
105*3737Shx147065 	&mod_driverops,
106*3737Shx147065 	"Lucent/PRISM-II 802.11b driver",
107*3737Shx147065 	&pcwl_dev_ops
108*3737Shx147065 };
109*3737Shx147065 
110*3737Shx147065 static struct modlinkage modlinkage = {
111*3737Shx147065 	MODREV_1, (void *)&modldrv, NULL
112*3737Shx147065 	};
113*3737Shx147065 
114*3737Shx147065 int
115*3737Shx147065 _init(void)
116*3737Shx147065 {
117*3737Shx147065 	int stat;
118*3737Shx147065 
119*3737Shx147065 	/* Allocate soft state */
120*3737Shx147065 	if ((stat = ddi_soft_state_init(&pcwl_soft_state_p,
121*3737Shx147065 	    sizeof (pcwl_maci_t), N_PCWL)) != DDI_SUCCESS)
122*3737Shx147065 		return (stat);
123*3737Shx147065 
124*3737Shx147065 	mac_init_ops(&pcwl_dev_ops, "pcwl");
125*3737Shx147065 	wl_frame_default.wl_dat[0] = htons(WL_SNAP_WORD0);
126*3737Shx147065 	wl_frame_default.wl_dat[1] = htons(WL_SNAP_WORD1);
127*3737Shx147065 	stat = mod_install(&modlinkage);
128*3737Shx147065 	if (stat != DDI_SUCCESS) {
129*3737Shx147065 		mac_fini_ops(&pcwl_dev_ops);
130*3737Shx147065 		ddi_soft_state_fini(&pcwl_soft_state_p);
131*3737Shx147065 	}
132*3737Shx147065 	return (stat);
133*3737Shx147065 }
134*3737Shx147065 
135*3737Shx147065 int
136*3737Shx147065 _fini(void)
137*3737Shx147065 {
138*3737Shx147065 	int stat;
139*3737Shx147065 
140*3737Shx147065 	if ((stat = mod_remove(&modlinkage)) != DDI_SUCCESS)
141*3737Shx147065 		return (stat);
142*3737Shx147065 	mac_fini_ops(&pcwl_dev_ops);
143*3737Shx147065 	ddi_soft_state_fini(&pcwl_soft_state_p);
144*3737Shx147065 
145*3737Shx147065 	return (stat);
146*3737Shx147065 }
147*3737Shx147065 
148*3737Shx147065 int
149*3737Shx147065 _info(struct modinfo *modinfop)
150*3737Shx147065 {
151*3737Shx147065 	return (mod_info(&modlinkage, modinfop));
152*3737Shx147065 }
153*3737Shx147065 
154*3737Shx147065 static int
155*3737Shx147065 pcwl_probe(dev_info_t *dip)
156*3737Shx147065 {
157*3737Shx147065 	int len, ret;
158*3737Shx147065 	char *buf;
159*3737Shx147065 	dev_info_t *pdip = ddi_get_parent(dip);
160*3737Shx147065 
161*3737Shx147065 	ret = ddi_getlongprop(DDI_DEV_T_ANY, pdip, 0, "device_type",
162*3737Shx147065 	    (caddr_t)&buf, &len);
163*3737Shx147065 	if (ret != DDI_SUCCESS)
164*3737Shx147065 		return (DDI_PROBE_FAILURE);
165*3737Shx147065 
166*3737Shx147065 	PCWLDBG((CE_NOTE, "pcwl probe: device_type %s\n", buf));
167*3737Shx147065 	if ((strcmp(buf, "pccard") == 0) || (strcmp(buf, "pcmcia") == 0)) {
168*3737Shx147065 		pcwl_device_type = PCWL_DEVICE_PCCARD;
169*3737Shx147065 		ret = DDI_PROBE_SUCCESS;
170*3737Shx147065 	} else if (strcmp(buf, "pci") == 0) {
171*3737Shx147065 		pcwl_device_type = PCWL_DEVICE_PCI;
172*3737Shx147065 		ret = DDI_PROBE_SUCCESS;
173*3737Shx147065 	} else {
174*3737Shx147065 		ret = DDI_PROBE_FAILURE;
175*3737Shx147065 	}
176*3737Shx147065 	kmem_free(buf, len);
177*3737Shx147065 	return (ret);
178*3737Shx147065 }
179*3737Shx147065 
180*3737Shx147065 static int
181*3737Shx147065 pcwl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
182*3737Shx147065 {
183*3737Shx147065 	int ret, i;
184*3737Shx147065 	int instance;
185*3737Shx147065 	uint16_t stat;
186*3737Shx147065 	uint32_t err;
187*3737Shx147065 	pcwl_maci_t *pcwl_p;
188*3737Shx147065 	wifi_data_t	wd = { 0 };
189*3737Shx147065 	mac_register_t	*macp;
190*3737Shx147065 	modify_config_t cfgmod;
191*3737Shx147065 	char strbuf[256];
192*3737Shx147065 
193*3737Shx147065 	PCWLDBG((CE_NOTE, "pcwl attach: dip=0x%p cmd=%x\n", (void *)dip, cmd));
194*3737Shx147065 	if (cmd != DDI_ATTACH)
195*3737Shx147065 		goto attach_fail1;
196*3737Shx147065 	/*
197*3737Shx147065 	 * Allocate soft state associated with this instance.
198*3737Shx147065 	 */
199*3737Shx147065 	if (ddi_soft_state_zalloc(pcwl_soft_state_p,
200*3737Shx147065 	    ddi_get_instance(dip)) != DDI_SUCCESS) {
201*3737Shx147065 		cmn_err(CE_CONT, "pcwl attach: alloc softstate failed\n");
202*3737Shx147065 		goto attach_fail1;
203*3737Shx147065 	}
204*3737Shx147065 	pcwl_p = (pcwl_maci_t *)ddi_get_soft_state(pcwl_soft_state_p,
205*3737Shx147065 	    ddi_get_instance(dip));
206*3737Shx147065 	pcwl_p->pcwl_device_type = pcwl_device_type;
207*3737Shx147065 	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) {
208*3737Shx147065 		if (ddi_regs_map_setup(dip, 0,
209*3737Shx147065 		    (caddr_t *)&pcwl_p->pcwl_cfg_base, 0, 0,
210*3737Shx147065 		    &accattr, &pcwl_p->pcwl_cfg_handle) != DDI_SUCCESS) {
211*3737Shx147065 			cmn_err(CE_WARN, "pcwl(pci) attach: pci_regs_map_setup"
212*3737Shx147065 			    " failed\n");
213*3737Shx147065 			goto attach_fail2;
214*3737Shx147065 		}
215*3737Shx147065 
216*3737Shx147065 		stat = ddi_get16(pcwl_p->pcwl_cfg_handle,
217*3737Shx147065 		    (uint16_t *)(pcwl_p->pcwl_cfg_base + PCI_CONF_COMM));
218*3737Shx147065 		stat |= (PCI_COMM_IO | PCI_COMM_MAE);
219*3737Shx147065 		ddi_put16(pcwl_p->pcwl_cfg_handle,
220*3737Shx147065 		    (uint16_t *)(pcwl_p->pcwl_cfg_base + PCI_CONF_COMM), stat);
221*3737Shx147065 		stat = ddi_get16(pcwl_p->pcwl_cfg_handle,
222*3737Shx147065 		    (uint16_t *)(pcwl_p->pcwl_cfg_base + PCI_CONF_COMM));
223*3737Shx147065 		if ((stat & (PCI_COMM_IO | PCI_COMM_MAE)) !=
224*3737Shx147065 		    (PCI_COMM_IO | PCI_COMM_MAE)) {
225*3737Shx147065 			cmn_err(CE_WARN, "pcwl(pci) attach: pci command"
226*3737Shx147065 			    " reg enable failed\n");
227*3737Shx147065 			goto attach_fail2a;
228*3737Shx147065 		}
229*3737Shx147065 
230*3737Shx147065 
231*3737Shx147065 		if (ddi_regs_map_setup(dip, 1, (caddr_t *)&pcwl_p->pcwl_bar,
232*3737Shx147065 		    0, 0, &accattr, (ddi_acc_handle_t *)&pcwl_p->pcwl_handle)
233*3737Shx147065 		    != DDI_SUCCESS) {
234*3737Shx147065 			cmn_err(CE_WARN, "pcwl(pci) attach: pci_regs_map_setup"
235*3737Shx147065 			    " failed\n");
236*3737Shx147065 			goto attach_fail2a;
237*3737Shx147065 		}
238*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl(pci): regs_map_setup,bar=%p\n",
239*3737Shx147065 		    (void *)pcwl_p->pcwl_bar));
240*3737Shx147065 
241*3737Shx147065 		/*
242*3737Shx147065 		 * tricky! copy from freebsd code.
243*3737Shx147065 		 */
244*3737Shx147065 		PCWL_WRITE(pcwl_p, 0x26, 0x80);
245*3737Shx147065 		drv_usecwait(500000);
246*3737Shx147065 		PCWL_WRITE(pcwl_p, 0x26, 0x0);
247*3737Shx147065 		drv_usecwait(500000);
248*3737Shx147065 
249*3737Shx147065 		for (i = 0; i < WL_TIMEOUT; i++) {
250*3737Shx147065 			PCWL_READ(pcwl_p, 0x0, stat);
251*3737Shx147065 			if (stat & WL_CMD_BUSY)
252*3737Shx147065 				drv_usecwait(10);
253*3737Shx147065 			else
254*3737Shx147065 				break;
255*3737Shx147065 		}
256*3737Shx147065 		if (i == WL_TIMEOUT) {
257*3737Shx147065 			cmn_err(CE_WARN, "pcwl(pci) attach: hardware init"
258*3737Shx147065 			    " failed\n");
259*3737Shx147065 			goto attach_fail3;
260*3737Shx147065 		}
261*3737Shx147065 
262*3737Shx147065 		/*
263*3737Shx147065 		 * magic number verification.
264*3737Shx147065 		 * tricky! copy from freebsd code.
265*3737Shx147065 		 */
266*3737Shx147065 		PCWL_WRITE(pcwl_p, 0x28, 0x4a2d);
267*3737Shx147065 		PCWL_READ(pcwl_p, 0x28, stat);
268*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl(pci):magic number = %x\n", stat));
269*3737Shx147065 		if (stat != 0x4a2d) {
270*3737Shx147065 			cmn_err(CE_WARN, "pcwl(pci) attach: magic verify"
271*3737Shx147065 			    " failed\n");
272*3737Shx147065 			goto attach_fail3;
273*3737Shx147065 		}
274*3737Shx147065 	}
275*3737Shx147065 	pcwl_p->pcwl_dip	= dip;
276*3737Shx147065 	pcwl_p->pcwl_flag	= 0;
277*3737Shx147065 	pcwl_p->pcwl_socket	= ddi_getprop(DDI_DEV_T_NONE, dip,
278*3737Shx147065 	    DDI_PROP_DONTPASS, "socket", -1);
279*3737Shx147065 	pcwl_p->pcwl_reschedule_need = B_FALSE;
280*3737Shx147065 
281*3737Shx147065 	if (ddi_get_iblock_cookie(dip,
282*3737Shx147065 	    0, &pcwl_p->pcwl_ib_cookie) != DDI_SUCCESS) {
283*3737Shx147065 		cmn_err(CE_WARN, "pcwl attach: get_iblk_cookie failed\n");
284*3737Shx147065 		goto attach_fail3;
285*3737Shx147065 	}
286*3737Shx147065 
287*3737Shx147065 	mutex_init(&pcwl_p->pcwl_glock, NULL, MUTEX_DRIVER,
288*3737Shx147065 	    pcwl_p->pcwl_ib_cookie);
289*3737Shx147065 	mutex_init(&pcwl_p->pcwl_scanlist_lock, NULL, MUTEX_DRIVER,
290*3737Shx147065 	    pcwl_p->pcwl_ib_cookie);
291*3737Shx147065 	mutex_init(&pcwl_p->pcwl_txring.wl_tx_lock, NULL, MUTEX_DRIVER,
292*3737Shx147065 	    pcwl_p->pcwl_ib_cookie);
293*3737Shx147065 
294*3737Shx147065 	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) {
295*3737Shx147065 		if (ret = ddi_add_intr(dip, 0, NULL, NULL,
296*3737Shx147065 		    pcwl_intr, (caddr_t)pcwl_p)) {
297*3737Shx147065 			cmn_err(CE_NOTE, "pcwl(pci) attach: add intr failed\n");
298*3737Shx147065 			goto attach_fail3a;
299*3737Shx147065 		}
300*3737Shx147065 	} else if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCCARD) {
301*3737Shx147065 		if (ret = pcwl_register_cs(dip, pcwl_p)) {
302*3737Shx147065 			cmn_err(CE_WARN, "pcwl attach(pccard): "
303*3737Shx147065 			    "register_cs err %x\n", ret);
304*3737Shx147065 			goto attach_fail3a;
305*3737Shx147065 		}
306*3737Shx147065 	} else {
307*3737Shx147065 		cmn_err(CE_WARN, "pcwl attach: unsupported device type\n");
308*3737Shx147065 		goto attach_fail3a;
309*3737Shx147065 	}
310*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
311*3737Shx147065 	if (ret = pcwl_reset_backend(pcwl_p)) {
312*3737Shx147065 		cmn_err(CE_WARN, "pcwl attach: reset_backend failed %x\n", ret);
313*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
314*3737Shx147065 		goto attach_fail4;
315*3737Shx147065 	}
316*3737Shx147065 	if (ret = pcwl_get_cap(pcwl_p)) { /* sets macaddr for mac_register */
317*3737Shx147065 		cmn_err(CE_WARN, "pcwl attach: get_cap failed %x\n", ret);
318*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
319*3737Shx147065 		goto attach_fail4;
320*3737Shx147065 	}
321*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
322*3737Shx147065 	/*
323*3737Shx147065 	 * Provide initial settings for the WiFi plugin; whenever this
324*3737Shx147065 	 * information changes, we need to call mac_pdata_update()
325*3737Shx147065 	 */
326*3737Shx147065 	wd.wd_secalloc = WIFI_SEC_NONE;
327*3737Shx147065 	wd.wd_opmode = IEEE80211_M_STA;
328*3737Shx147065 
329*3737Shx147065 	macp = mac_alloc(MAC_VERSION);
330*3737Shx147065 	if (macp == NULL) {
331*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl attach: "
332*3737Shx147065 		    "MAC version mismatch\n"));
333*3737Shx147065 		goto attach_fail4;
334*3737Shx147065 	}
335*3737Shx147065 
336*3737Shx147065 	macp->m_type_ident	= MAC_PLUGIN_IDENT_WIFI;
337*3737Shx147065 	macp->m_driver		= pcwl_p;
338*3737Shx147065 	macp->m_dip		= dip;
339*3737Shx147065 	macp->m_src_addr	= pcwl_p->pcwl_mac_addr;
340*3737Shx147065 	macp->m_callbacks	= &pcwl_m_callbacks;
341*3737Shx147065 	macp->m_min_sdu		= 0;
342*3737Shx147065 	macp->m_max_sdu		= IEEE80211_MTU;
343*3737Shx147065 	macp->m_pdata		= &wd;
344*3737Shx147065 	macp->m_pdata_size	= sizeof (wd);
345*3737Shx147065 
346*3737Shx147065 	err = mac_register(macp, &pcwl_p->pcwl_mh);
347*3737Shx147065 	mac_free(macp);
348*3737Shx147065 	if (err != 0) {
349*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl attach: "
350*3737Shx147065 		    "mac_register err\n"));
351*3737Shx147065 		goto attach_fail4;
352*3737Shx147065 	}
353*3737Shx147065 
354*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
355*3737Shx147065 	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCCARD) {
356*3737Shx147065 		/*
357*3737Shx147065 		 * turn on CS interrupt
358*3737Shx147065 		 */
359*3737Shx147065 		cfgmod.Attributes = CONF_ENABLE_IRQ_STEERING |
360*3737Shx147065 		    CONF_IRQ_CHANGE_VALID;
361*3737Shx147065 		cfgmod.Vpp1 = 0;
362*3737Shx147065 		cfgmod.Vpp2 = 0;
363*3737Shx147065 		(void) csx_ModifyConfiguration(pcwl_p->pcwl_chdl, &cfgmod);
364*3737Shx147065 
365*3737Shx147065 	}
366*3737Shx147065 	if (ret = pcwl_init_nicmem(pcwl_p)) {
367*3737Shx147065 		cmn_err(CE_WARN, "pcwl(pccard) attach: pcwl_init_nicmem"
368*3737Shx147065 		    " failed %x\n", ret);
369*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
370*3737Shx147065 		goto attach_fail5;
371*3737Shx147065 	}
372*3737Shx147065 	pcwl_chip_type(pcwl_p);
373*3737Shx147065 	if (ret = pcwl_loaddef_rf(pcwl_p)) {
374*3737Shx147065 		cmn_err(CE_WARN, "pcwl attach: config_rf failed%x\n", ret);
375*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
376*3737Shx147065 		goto attach_fail5;
377*3737Shx147065 	}
378*3737Shx147065 	(void) pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0);
379*3737Shx147065 	pcwl_stop_locked(pcwl_p);	/* leaves interface down */
380*3737Shx147065 	list_create(&pcwl_p->pcwl_scan_list, sizeof (wl_scan_list_t),
381*3737Shx147065 	    offsetof(wl_scan_list_t, wl_scan_node));
382*3737Shx147065 	pcwl_p->pcwl_scan_num = 0;
383*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
384*3737Shx147065 	pcwl_p->pcwl_scanlist_timeout_id = timeout(pcwl_scanlist_timeout,
385*3737Shx147065 	    pcwl_p, drv_usectohz(1000000));
386*3737Shx147065 	instance = ddi_get_instance(dip);
387*3737Shx147065 	(void) snprintf(strbuf, sizeof (strbuf), "pcwl%d", instance);
388*3737Shx147065 	if (ddi_create_minor_node(dip, strbuf, S_IFCHR,
389*3737Shx147065 	    instance + 1, DDI_NT_NET_WIFI, 0) != DDI_SUCCESS) {
390*3737Shx147065 		goto attach_fail6;
391*3737Shx147065 	}
392*3737Shx147065 	pcwl_p->pcwl_flag |= PCWL_ATTACHED;
393*3737Shx147065 	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) {
394*3737Shx147065 		pcwl_p->pcwl_flag |= PCWL_CARD_READY;
395*3737Shx147065 	}
396*3737Shx147065 	return (DDI_SUCCESS);
397*3737Shx147065 attach_fail6:
398*3737Shx147065 	if (pcwl_p->pcwl_scanlist_timeout_id != 0) {
399*3737Shx147065 		(void) untimeout(pcwl_p->pcwl_scanlist_timeout_id);
400*3737Shx147065 		pcwl_p->pcwl_scanlist_timeout_id = 0;
401*3737Shx147065 	}
402*3737Shx147065 	list_destroy(&pcwl_p->pcwl_scan_list);
403*3737Shx147065 attach_fail5:
404*3737Shx147065 	(void) mac_unregister(pcwl_p->pcwl_mh);
405*3737Shx147065 attach_fail4:
406*3737Shx147065 	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) {
407*3737Shx147065 		ddi_remove_intr(dip, 0, pcwl_p->pcwl_ib_cookie);
408*3737Shx147065 	} else if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCCARD) {
409*3737Shx147065 		pcwl_unregister_cs(pcwl_p);
410*3737Shx147065 	}
411*3737Shx147065 attach_fail3a:
412*3737Shx147065 	pcwl_destroy_locks(pcwl_p);
413*3737Shx147065 attach_fail3:
414*3737Shx147065 	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI)
415*3737Shx147065 		ddi_regs_map_free(&pcwl_p->pcwl_handle);
416*3737Shx147065 attach_fail2a:
417*3737Shx147065 	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI)
418*3737Shx147065 		ddi_regs_map_free(&pcwl_p->pcwl_cfg_handle);
419*3737Shx147065 attach_fail2:
420*3737Shx147065 	ddi_soft_state_free(pcwl_soft_state_p, ddi_get_instance(dip));
421*3737Shx147065 attach_fail1:
422*3737Shx147065 	return (DDI_FAILURE);
423*3737Shx147065 }
424*3737Shx147065 
425*3737Shx147065 static int
426*3737Shx147065 pcwl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
427*3737Shx147065 {
428*3737Shx147065 	pcwl_maci_t *pcwl_p;
429*3737Shx147065 	wl_scan_list_t *scan_item0;
430*3737Shx147065 	int ret;
431*3737Shx147065 	pcwl_p = ddi_get_soft_state(pcwl_soft_state_p, ddi_get_instance(dip));
432*3737Shx147065 
433*3737Shx147065 	PCWLDBG((CE_NOTE, "pcwl detach: dip=0x%p cmd=%x\n", (void *)dip, cmd));
434*3737Shx147065 	if (cmd != DDI_DETACH)
435*3737Shx147065 		return (DDI_FAILURE);
436*3737Shx147065 	if (!(pcwl_p->pcwl_flag & PCWL_ATTACHED))
437*3737Shx147065 		return (DDI_FAILURE);
438*3737Shx147065 
439*3737Shx147065 	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) {
440*3737Shx147065 		mutex_enter(&pcwl_p->pcwl_glock);
441*3737Shx147065 		(void) pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0);
442*3737Shx147065 		PCWL_DISABLE_INTR(pcwl_p);
443*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
444*3737Shx147065 	}
445*3737Shx147065 	if (pcwl_p->pcwl_scanlist_timeout_id != 0) {
446*3737Shx147065 		(void) untimeout(pcwl_p->pcwl_scanlist_timeout_id);
447*3737Shx147065 		pcwl_p->pcwl_scanlist_timeout_id = 0;
448*3737Shx147065 	}
449*3737Shx147065 	if (pcwl_p->pcwl_connect_timeout_id != 0) {
450*3737Shx147065 		(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
451*3737Shx147065 		pcwl_p->pcwl_connect_timeout_id = 0;
452*3737Shx147065 	}
453*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_scanlist_lock);
454*3737Shx147065 	scan_item0 = list_head(&pcwl_p->pcwl_scan_list);
455*3737Shx147065 	while (scan_item0) {
456*3737Shx147065 		pcwl_delete_scan_item(pcwl_p, scan_item0);
457*3737Shx147065 		scan_item0 = list_head(&pcwl_p->pcwl_scan_list);
458*3737Shx147065 	}
459*3737Shx147065 	list_destroy(&pcwl_p->pcwl_scan_list);
460*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_scanlist_lock);
461*3737Shx147065 	ret = mac_unregister(pcwl_p->pcwl_mh);
462*3737Shx147065 	if (ret != 0)
463*3737Shx147065 		return (DDI_FAILURE);
464*3737Shx147065 
465*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
466*3737Shx147065 	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) {
467*3737Shx147065 		ddi_remove_intr(dip, 0, pcwl_p->pcwl_ib_cookie);
468*3737Shx147065 		ddi_regs_map_free(&pcwl_p->pcwl_handle);
469*3737Shx147065 		ddi_regs_map_free(&pcwl_p->pcwl_cfg_handle);
470*3737Shx147065 	} else if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCCARD) {
471*3737Shx147065 		pcwl_unregister_cs(pcwl_p);
472*3737Shx147065 	}
473*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
474*3737Shx147065 	pcwl_destroy_locks(pcwl_p);
475*3737Shx147065 	ddi_remove_minor_node(dip, NULL);
476*3737Shx147065 	ddi_soft_state_free(pcwl_soft_state_p, ddi_get_instance(dip));
477*3737Shx147065 	return (DDI_SUCCESS);
478*3737Shx147065 }
479*3737Shx147065 
480*3737Shx147065 /*
481*3737Shx147065  * card services and event handlers
482*3737Shx147065  */
483*3737Shx147065 static int
484*3737Shx147065 pcwl_register_cs(dev_info_t *dip, pcwl_maci_t *pcwl_p)
485*3737Shx147065 {
486*3737Shx147065 	int ret;
487*3737Shx147065 	client_reg_t cr;
488*3737Shx147065 	client_handle_t chdl; /* uint encoding of socket, function, client */
489*3737Shx147065 	get_status_t card_status;
490*3737Shx147065 	request_socket_mask_t sock_req;
491*3737Shx147065 
492*3737Shx147065 	bzero(&cr, sizeof (cr));
493*3737Shx147065 	cr.Attributes	= INFO_IO_CLIENT|INFO_CARD_EXCL|INFO_CARD_SHARE;
494*3737Shx147065 	cr.EventMask	= CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
495*3737Shx147065 	    CS_EVENT_REGISTRATION_COMPLETE | CS_EVENT_CARD_REMOVAL_LOWP |
496*3737Shx147065 	    CS_EVENT_CARD_READY | CS_EVENT_PM_RESUME |
497*3737Shx147065 	    CS_EVENT_PM_SUSPEND | CS_EVENT_CLIENT_INFO;
498*3737Shx147065 	cr.event_callback_args.client_data = pcwl_p;
499*3737Shx147065 	cr.Version = CS_VERSION;
500*3737Shx147065 	cr.event_handler = (csfunction_t *)pcwl_ev_hdlr;
501*3737Shx147065 	cr.dip = dip;
502*3737Shx147065 	(void) strcpy(cr.driver_name, pcwl_name_str);
503*3737Shx147065 	if (ret = csx_RegisterClient(&chdl, &cr)) {
504*3737Shx147065 		cmn_err(CE_WARN, "pcwl: RegisterClient failed %x\n", ret);
505*3737Shx147065 		goto regcs_ret;
506*3737Shx147065 	}
507*3737Shx147065 	pcwl_p->pcwl_chdl = chdl;
508*3737Shx147065 
509*3737Shx147065 	bzero(&card_status, sizeof (card_status));
510*3737Shx147065 	(void) csx_GetStatus(chdl, &card_status);
511*3737Shx147065 	PCWLDBG((CE_NOTE,
512*3737Shx147065 	    "pcwl: register_cs: Sock=%x CState=%x SState=%x rState=%x\n",
513*3737Shx147065 	    card_status.Socket, card_status.CardState,
514*3737Shx147065 	    card_status.SocketState, card_status.raw_CardState));
515*3737Shx147065 	if (!(card_status.CardState & CS_STATUS_CARD_INSERTED)) {
516*3737Shx147065 		/* card is not present, why are we attaching ? */
517*3737Shx147065 		ret = CS_NO_CARD;
518*3737Shx147065 		goto regcs_unreg;
519*3737Shx147065 	}
520*3737Shx147065 	cv_init(&pcwl_p->pcwl_cscv, NULL, CV_DRIVER, NULL);
521*3737Shx147065 	mutex_init(&pcwl_p->pcwl_cslock, NULL, MUTEX_DRIVER, *cr.iblk_cookie);
522*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_cslock);
523*3737Shx147065 	if (ret = csx_MapLogSocket(chdl, &pcwl_p->pcwl_log_sock)) {
524*3737Shx147065 		cmn_err(CE_WARN, "pcwl: MapLogSocket failed %x\n", ret);
525*3737Shx147065 		goto regcs_fail;
526*3737Shx147065 	}
527*3737Shx147065 	PCWLDBG((CE_NOTE,
528*3737Shx147065 	    "pcwl: register_cs: LogSock=%x PhyAdapter=%x PhySock=%x\n",
529*3737Shx147065 	    pcwl_p->pcwl_log_sock.LogSocket,
530*3737Shx147065 	    pcwl_p->pcwl_log_sock.PhyAdapter,
531*3737Shx147065 	    pcwl_p->pcwl_log_sock.PhySocket));
532*3737Shx147065 	/* turn on initialization events */
533*3737Shx147065 	sock_req.Socket = 0;
534*3737Shx147065 	sock_req.EventMask = CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
535*3737Shx147065 	    CS_EVENT_REGISTRATION_COMPLETE;
536*3737Shx147065 	if (ret = csx_RequestSocketMask(chdl, &sock_req)) {
537*3737Shx147065 		cmn_err(CE_WARN, "pcwl: RequestSocketMask failed %x\n", ret);
538*3737Shx147065 		goto regcs_fail;
539*3737Shx147065 	}
540*3737Shx147065 	/* wait for and process card insertion events */
541*3737Shx147065 	while (!(pcwl_p->pcwl_flag & PCWL_CARD_READY))
542*3737Shx147065 		cv_wait(&pcwl_p->pcwl_cscv, &pcwl_p->pcwl_cslock);
543*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_cslock);
544*3737Shx147065 
545*3737Shx147065 	pcwl_p->pcwl_flag |= PCWL_CS_REGISTERED;
546*3737Shx147065 	return (PCWL_SUCCESS);
547*3737Shx147065 regcs_fail:
548*3737Shx147065 	mutex_destroy(&pcwl_p->pcwl_cslock);
549*3737Shx147065 	cv_destroy(&pcwl_p->pcwl_cscv);
550*3737Shx147065 regcs_unreg:
551*3737Shx147065 	(void) csx_DeregisterClient(chdl);
552*3737Shx147065 regcs_ret:
553*3737Shx147065 	pcwl_p->pcwl_flag &= ~PCWL_CS_REGISTERED;
554*3737Shx147065 	return (ret);
555*3737Shx147065 }
556*3737Shx147065 
557*3737Shx147065 static void
558*3737Shx147065 pcwl_unregister_cs(pcwl_maci_t *pcwl_p)
559*3737Shx147065 {
560*3737Shx147065 	int ret;
561*3737Shx147065 	release_socket_mask_t mask;
562*3737Shx147065 	mask.Socket = pcwl_p->pcwl_socket;
563*3737Shx147065 
564*3737Shx147065 	/*
565*3737Shx147065 	 * The card service not registered means register_cs function
566*3737Shx147065 	 * doesnot return TRUE. Then all the lelated resource has been
567*3737Shx147065 	 * released in register_cs.
568*3737Shx147065 	 */
569*3737Shx147065 	if (!(pcwl_p->pcwl_flag | PCWL_CS_REGISTERED))
570*3737Shx147065 		return;
571*3737Shx147065 
572*3737Shx147065 	if (ret = csx_ReleaseSocketMask(pcwl_p->pcwl_chdl, &mask))
573*3737Shx147065 		cmn_err(CE_WARN, "pcwl: ReleaseSocket mask failed %x\n", ret);
574*3737Shx147065 
575*3737Shx147065 	if (pcwl_p->pcwl_flag & PCWL_CARD_READY) {
576*3737Shx147065 		pcwl_card_remove(pcwl_p);
577*3737Shx147065 		pcwl_p->pcwl_flag &= ~PCWL_CARD_READY;
578*3737Shx147065 	}
579*3737Shx147065 	mutex_destroy(&pcwl_p->pcwl_cslock);
580*3737Shx147065 	cv_destroy(&pcwl_p->pcwl_cscv);
581*3737Shx147065 	if (ret = csx_DeregisterClient(pcwl_p->pcwl_chdl))
582*3737Shx147065 		cmn_err(CE_WARN, "pcwl: Deregister failed %x\n", ret);
583*3737Shx147065 }
584*3737Shx147065 
585*3737Shx147065 static void
586*3737Shx147065 pcwl_destroy_locks(pcwl_maci_t *pcwl_p)
587*3737Shx147065 {
588*3737Shx147065 	mutex_destroy(&pcwl_p->pcwl_txring.wl_tx_lock);
589*3737Shx147065 	mutex_destroy(&pcwl_p->pcwl_scanlist_lock);
590*3737Shx147065 	mutex_destroy(&pcwl_p->pcwl_glock);
591*3737Shx147065 }
592*3737Shx147065 
593*3737Shx147065 static int
594*3737Shx147065 pcwl_ev_hdlr(event_t event, int priority, event_callback_args_t *arg)
595*3737Shx147065 {
596*3737Shx147065 	int ret = CS_SUCCESS;
597*3737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg->client_data;
598*3737Shx147065 	client_info_t *ci_p = (client_info_t *)&arg->client_info;
599*3737Shx147065 
600*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_cslock);
601*3737Shx147065 	switch (event) {
602*3737Shx147065 	case CS_EVENT_CARD_INSERTION:
603*3737Shx147065 		ret = pcwl_card_insert(pcwl_p);
604*3737Shx147065 		cv_broadcast(&pcwl_p->pcwl_cscv);
605*3737Shx147065 		break;
606*3737Shx147065 	case CS_EVENT_REGISTRATION_COMPLETE:
607*3737Shx147065 		cv_broadcast(&pcwl_p->pcwl_cscv);
608*3737Shx147065 		break;
609*3737Shx147065 	case CS_EVENT_CARD_REMOVAL:
610*3737Shx147065 		if (priority & CS_EVENT_PRI_HIGH)
611*3737Shx147065 			break;
612*3737Shx147065 		pcwl_card_remove(pcwl_p);
613*3737Shx147065 		cv_broadcast(&pcwl_p->pcwl_cscv);
614*3737Shx147065 		break;
615*3737Shx147065 	case CS_EVENT_CLIENT_INFO:
616*3737Shx147065 		if (GET_CLIENT_INFO_SUBSVC(ci_p->Attributes) !=
617*3737Shx147065 		    CS_CLIENT_INFO_SUBSVC_CS)
618*3737Shx147065 			break;
619*3737Shx147065 
620*3737Shx147065 		ci_p->Revision = 0x0101;
621*3737Shx147065 		ci_p->CSLevel = CS_VERSION;
622*3737Shx147065 		ci_p->RevDate = CS_CLIENT_INFO_MAKE_DATE(9, 12, 14);
623*3737Shx147065 		(void) strcpy(ci_p->ClientName, PCWL_IDENT_STRING);
624*3737Shx147065 		(void) strcpy(ci_p->VendorName, CS_SUN_VENDOR_DESCRIPTION);
625*3737Shx147065 		ci_p->Attributes |= CS_CLIENT_INFO_VALID;
626*3737Shx147065 		break;
627*3737Shx147065 	default:
628*3737Shx147065 		ret = CS_UNSUPPORTED_EVENT;
629*3737Shx147065 		break;
630*3737Shx147065 	}
631*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_cslock);
632*3737Shx147065 	return (ret);
633*3737Shx147065 }
634*3737Shx147065 
635*3737Shx147065 static int
636*3737Shx147065 pcwl_card_insert(pcwl_maci_t *pcwl_p)
637*3737Shx147065 {
638*3737Shx147065 	int ret, hi, lo;
639*3737Shx147065 	tuple_t tuple;
640*3737Shx147065 	cisparse_t cisparse;
641*3737Shx147065 	io_req_t	io;
642*3737Shx147065 	irq_req_t	irq;
643*3737Shx147065 	config_req_t	cfg;
644*3737Shx147065 	cistpl_config_t config;
645*3737Shx147065 	cistpl_cftable_entry_t *tbl_p;
646*3737Shx147065 	register client_handle_t chdl = pcwl_p->pcwl_chdl;
647*3737Shx147065 
648*3737Shx147065 	bzero(&tuple, sizeof (tuple));
649*3737Shx147065 	tuple.DesiredTuple = CISTPL_MANFID;
650*3737Shx147065 	if (ret = csx_GetFirstTuple(chdl, &tuple)) {
651*3737Shx147065 		cmn_err(CE_WARN, "pcwl: get manufacture id failed %x\n", ret);
652*3737Shx147065 		goto insert_ret;
653*3737Shx147065 	}
654*3737Shx147065 	bzero(&cisparse, sizeof (cisparse));
655*3737Shx147065 	if (ret = csx_Parse_CISTPL_MANFID(chdl, &tuple, &cisparse.manfid)) {
656*3737Shx147065 		cmn_err(CE_WARN, "pcwl: parse manufacture id failed %x\n", ret);
657*3737Shx147065 		goto insert_ret;
658*3737Shx147065 	}
659*3737Shx147065 
660*3737Shx147065 	/*
661*3737Shx147065 	 * verify manufacture ID
662*3737Shx147065 	 */
663*3737Shx147065 	PCWLDBG((CE_NOTE, "pcwl insert: manufacturer_id=%x card=%x\n",
664*3737Shx147065 	    cisparse.manfid.manf, cisparse.manfid.card));
665*3737Shx147065 	bzero(&tuple, sizeof (tuple));
666*3737Shx147065 	tuple.DesiredTuple = CISTPL_FUNCID;
667*3737Shx147065 	if (ret = csx_GetFirstTuple(chdl, &tuple)) {
668*3737Shx147065 		cmn_err(CE_WARN, "pcwl: get function id failed %x\n", ret);
669*3737Shx147065 		goto insert_ret;
670*3737Shx147065 	}
671*3737Shx147065 	bzero(&cisparse, sizeof (cisparse));
672*3737Shx147065 	if (ret = csx_Parse_CISTPL_FUNCID(chdl, &tuple, &cisparse.funcid)) {
673*3737Shx147065 		cmn_err(CE_WARN, "pcwl: parse function id failed %x\n", ret);
674*3737Shx147065 		goto insert_ret;
675*3737Shx147065 	}
676*3737Shx147065 
677*3737Shx147065 	/*
678*3737Shx147065 	 * verify function ID
679*3737Shx147065 	 */
680*3737Shx147065 	PCWLDBG((CE_NOTE, "insert:fun_id=%x\n", cisparse.funcid.function));
681*3737Shx147065 	bzero(&tuple, sizeof (tuple));
682*3737Shx147065 	tuple.DesiredTuple = CISTPL_CONFIG;
683*3737Shx147065 	if (ret = csx_GetFirstTuple(chdl, &tuple)) {
684*3737Shx147065 		cmn_err(CE_WARN, "pcwl: get config failed %x\n", ret);
685*3737Shx147065 		goto insert_ret;
686*3737Shx147065 	}
687*3737Shx147065 	bzero(&config, sizeof (config));
688*3737Shx147065 	if (ret = csx_Parse_CISTPL_CONFIG(chdl, &tuple, &config)) {
689*3737Shx147065 		cmn_err(CE_WARN, "pcwl: parse config failed %x\n", ret);
690*3737Shx147065 		goto insert_ret;
691*3737Shx147065 	}
692*3737Shx147065 	PCWLDBG((CE_NOTE,
693*3737Shx147065 	    "pcwl: config present=%x nr=%x hr=%x regs[0]=%x base=%x last=%x\n",
694*3737Shx147065 	    config.present, config.nr, config.hr, config.regs[0],
695*3737Shx147065 	    config.base, config.last));
696*3737Shx147065 	hi = 0;
697*3737Shx147065 	lo = (int)-1;		/* really big number */
698*3737Shx147065 	tbl_p = &cisparse.cftable;
699*3737Shx147065 	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
700*3737Shx147065 	for (tbl_p->index = 0; tbl_p->index <= config.hr; ) {
701*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl insert:tuple idx=%x:\n", tbl_p->index));
702*3737Shx147065 		if (ret = csx_GetNextTuple(chdl, &tuple)) {
703*3737Shx147065 			cmn_err(CE_WARN, "pcwl: get cftable failed %x\n",
704*3737Shx147065 			    ret);
705*3737Shx147065 			break;
706*3737Shx147065 		}
707*3737Shx147065 		bzero((caddr_t)&cisparse, sizeof (cisparse));
708*3737Shx147065 		if (ret = csx_Parse_CISTPL_CFTABLE_ENTRY(chdl, &tuple, tbl_p)) {
709*3737Shx147065 			cmn_err(CE_WARN, "pcwl: parse cftable failed %x\n",
710*3737Shx147065 			    ret);
711*3737Shx147065 			break;
712*3737Shx147065 		}
713*3737Shx147065 		if (tbl_p->flags & CISTPL_CFTABLE_TPCE_FS_PWR &&
714*3737Shx147065 			tbl_p->pd.flags & CISTPL_CFTABLE_TPCE_FS_PWR_VCC) {
715*3737Shx147065 			if (tbl_p->pd.pd_vcc.avgI > hi) {
716*3737Shx147065 				hi = tbl_p->pd.pd_vcc.avgI;
717*3737Shx147065 				pcwl_p->pcwl_config_hi = tbl_p->index;
718*3737Shx147065 			}
719*3737Shx147065 			if (tbl_p->pd.pd_vcc.avgI < lo) {
720*3737Shx147065 				lo = tbl_p->pd.pd_vcc.avgI;
721*3737Shx147065 				pcwl_p->pcwl_config = tbl_p->index;
722*3737Shx147065 			}
723*3737Shx147065 		}
724*3737Shx147065 		if (tbl_p->flags & CISTPL_CFTABLE_TPCE_DEFAULT) {
725*3737Shx147065 			if (tbl_p->pd.flags & CISTPL_CFTABLE_TPCE_FS_PWR_VCC)
726*3737Shx147065 				pcwl_p->pcwl_vcc = tbl_p->pd.pd_vcc.nomV;
727*3737Shx147065 			if (tbl_p->flags & CISTPL_CFTABLE_TPCE_FS_IO)
728*3737Shx147065 				pcwl_p->pcwl_iodecode = tbl_p->io.addr_lines;
729*3737Shx147065 		}
730*3737Shx147065 	}
731*3737Shx147065 	PCWLDBG((CE_NOTE, "pcwl: insert:cfg_hi=%x cfg=%x vcc=%x iodecode=%x\n",
732*3737Shx147065 	    pcwl_p->pcwl_config_hi, pcwl_p->pcwl_config,
733*3737Shx147065 	    pcwl_p->pcwl_vcc, pcwl_p->pcwl_iodecode));
734*3737Shx147065 	bzero(&io, sizeof (io));
735*3737Shx147065 	io.BasePort1.base = 0;
736*3737Shx147065 	io.NumPorts1 = 1 << pcwl_p->pcwl_iodecode;
737*3737Shx147065 	io.Attributes1 = IO_DATA_PATH_WIDTH_16;
738*3737Shx147065 	io.IOAddrLines = pcwl_p->pcwl_iodecode;
739*3737Shx147065 	if (ret = csx_RequestIO(chdl, &io)) {
740*3737Shx147065 		cmn_err(CE_WARN, "pcwl: RequestIO failed %x\n", ret);
741*3737Shx147065 		goto insert_ret;
742*3737Shx147065 	}
743*3737Shx147065 	pcwl_p->pcwl_port = io.BasePort1.handle;
744*3737Shx147065 	if (ret = ddi_add_softintr(DIP(pcwl_p), DDI_SOFTINT_HIGH,
745*3737Shx147065 	    &pcwl_p->pcwl_softint_id, &pcwl_p->pcwl_ib_cookie, NULL,
746*3737Shx147065 	    pcwl_intr, (caddr_t)pcwl_p)) {
747*3737Shx147065 		cmn_err(CE_NOTE, "pcwl(pccard): add softintr failed\n");
748*3737Shx147065 		goto insert_ret;
749*3737Shx147065 	}
750*3737Shx147065 	irq.Attributes = IRQ_TYPE_EXCLUSIVE;
751*3737Shx147065 	irq.irq_handler = ddi_intr_hilevel(DIP(pcwl_p), 0) ?
752*3737Shx147065 	    (csfunction_t *)pcwl_intr_hi : (csfunction_t *)pcwl_intr;
753*3737Shx147065 	irq.irq_handler_arg = pcwl_p;
754*3737Shx147065 	if (ret = csx_RequestIRQ(pcwl_p->pcwl_chdl, &irq)) {
755*3737Shx147065 		cmn_err(CE_WARN, "pcwl: RequestIRQ failed %x\n", ret);
756*3737Shx147065 		goto un_io;
757*3737Shx147065 	}
758*3737Shx147065 	bzero(&cfg, sizeof (cfg));
759*3737Shx147065 	cfg.Attributes = 0; /* not ready for CONF_ENABLE_IRQ_STEERING yet */
760*3737Shx147065 	cfg.Vcc = 50;
761*3737Shx147065 	cfg.IntType = SOCKET_INTERFACE_MEMORY_AND_IO;
762*3737Shx147065 	cfg.ConfigBase = config.base;
763*3737Shx147065 	cfg.ConfigIndex = pcwl_p->pcwl_config;
764*3737Shx147065 	cfg.Status = CCSR_IO_IS_8;
765*3737Shx147065 	cfg.Present = config.present;
766*3737Shx147065 	pcwl_p->pcwl_flag |= PCWL_CARD_READY;
767*3737Shx147065 	if (ret = csx_RequestConfiguration(chdl, &cfg)) {
768*3737Shx147065 		cmn_err(CE_WARN, "pcwl: RequestConfiguration failed %x\n", ret);
769*3737Shx147065 		goto un_irq;
770*3737Shx147065 	}
771*3737Shx147065 	return (CS_SUCCESS);
772*3737Shx147065 un_irq:
773*3737Shx147065 	(void) csx_ReleaseIRQ(chdl, &irq);
774*3737Shx147065 un_io:
775*3737Shx147065 	ddi_remove_softintr(pcwl_p->pcwl_softint_id);
776*3737Shx147065 	(void) csx_ReleaseIO(chdl, &io);
777*3737Shx147065 	pcwl_p->pcwl_port = 0;
778*3737Shx147065 insert_ret:
779*3737Shx147065 	pcwl_p->pcwl_flag &= ~PCWL_CARD_READY;
780*3737Shx147065 	return (ret);
781*3737Shx147065 
782*3737Shx147065 }
783*3737Shx147065 
784*3737Shx147065 /*
785*3737Shx147065  * assume card is already removed, don't touch the hardware
786*3737Shx147065  */
787*3737Shx147065 static void
788*3737Shx147065 pcwl_card_remove(pcwl_maci_t *pcwl_p)
789*3737Shx147065 {
790*3737Shx147065 	int ret;
791*3737Shx147065 	io_req_t io;
792*3737Shx147065 	irq_req_t irq;
793*3737Shx147065 
794*3737Shx147065 	/*
795*3737Shx147065 	 * The card not ready means Insert function doesnot return TRUE.
796*3737Shx147065 	 * then the IO and IRQ has been released in Insert
797*3737Shx147065 	 */
798*3737Shx147065 	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY))
799*3737Shx147065 		return;
800*3737Shx147065 	if (ret = csx_ReleaseConfiguration(pcwl_p->pcwl_chdl, NULL))
801*3737Shx147065 		cmn_err(CE_WARN, "pcwl: ReleaseConfiguration failed %x\n", ret);
802*3737Shx147065 
803*3737Shx147065 	bzero(&irq, sizeof (irq));
804*3737Shx147065 	if (ret = csx_ReleaseIRQ(pcwl_p->pcwl_chdl, &irq))
805*3737Shx147065 		cmn_err(CE_WARN, "pcwl: ReleaseIRQ failed %x\n", ret);
806*3737Shx147065 
807*3737Shx147065 	ddi_remove_softintr(pcwl_p->pcwl_softint_id);
808*3737Shx147065 
809*3737Shx147065 	bzero(&io, sizeof (io));
810*3737Shx147065 	io.BasePort1.handle = pcwl_p->pcwl_port;
811*3737Shx147065 	io.NumPorts1 = 16;
812*3737Shx147065 	if (ret = csx_ReleaseIO(pcwl_p->pcwl_chdl, &io))
813*3737Shx147065 		cmn_err(CE_WARN, "pcwl: ReleaseIO failed %x\n", ret);
814*3737Shx147065 
815*3737Shx147065 	pcwl_p->pcwl_port = 0;
816*3737Shx147065 	pcwl_p->pcwl_flag &= ~PCWL_CARD_READY;
817*3737Shx147065 }
818*3737Shx147065 
819*3737Shx147065 /*
820*3737Shx147065  * mac operation interface routines
821*3737Shx147065  */
822*3737Shx147065 static int
823*3737Shx147065 pcwl_start(void *arg)
824*3737Shx147065 {
825*3737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
826*3737Shx147065 
827*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
828*3737Shx147065 	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
829*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
830*3737Shx147065 		return (PCWL_FAIL);
831*3737Shx147065 	}
832*3737Shx147065 	pcwl_start_locked(pcwl_p);
833*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
834*3737Shx147065 	return (PCWL_SUCCESS);
835*3737Shx147065 }
836*3737Shx147065 
837*3737Shx147065 static void
838*3737Shx147065 pcwl_stop(void *arg)
839*3737Shx147065 {
840*3737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
841*3737Shx147065 
842*3737Shx147065 	PCWLDBG((CE_NOTE, "pcwl_stop called\n"));
843*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
844*3737Shx147065 	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
845*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
846*3737Shx147065 		return;
847*3737Shx147065 	}
848*3737Shx147065 
849*3737Shx147065 	pcwl_stop_locked(pcwl_p);
850*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
851*3737Shx147065 	if (pcwl_p->pcwl_connect_timeout_id != 0) {
852*3737Shx147065 		(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
853*3737Shx147065 		pcwl_p->pcwl_connect_timeout_id = 0;
854*3737Shx147065 	}
855*3737Shx147065 }
856*3737Shx147065 
857*3737Shx147065 static int
858*3737Shx147065 pcwl_saddr(void *arg, const uint8_t *macaddr)
859*3737Shx147065 {
860*3737Shx147065 	int ret = PCWL_SUCCESS;
861*3737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
862*3737Shx147065 
863*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
864*3737Shx147065 	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
865*3737Shx147065 		ret = PCWL_FAIL;
866*3737Shx147065 		goto done;
867*3737Shx147065 	}
868*3737Shx147065 	ether_copy(macaddr, pcwl_p->pcwl_mac_addr);
869*3737Shx147065 	if (pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
870*3737Shx147065 		ret = PCWL_FAIL;
871*3737Shx147065 		goto done;
872*3737Shx147065 	}
873*3737Shx147065 	if (pcwl_saddr_locked(pcwl_p)) {
874*3737Shx147065 		ret = PCWL_FAIL;
875*3737Shx147065 		goto done;
876*3737Shx147065 	}
877*3737Shx147065 	if (pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
878*3737Shx147065 		ret = PCWL_FAIL;
879*3737Shx147065 	}
880*3737Shx147065 done:
881*3737Shx147065 	if (ret)
882*3737Shx147065 		cmn_err(CE_WARN, "pcwl set_mac_addr: failed\n");
883*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
884*3737Shx147065 	return (ret);
885*3737Shx147065 }
886*3737Shx147065 
887*3737Shx147065 static int
888*3737Shx147065 pcwl_send(pcwl_maci_t *pcwl_p, mblk_t *mblk_p)
889*3737Shx147065 {
890*3737Shx147065 	int i = 0;
891*3737Shx147065 	char *buf, *buf_p;
892*3737Shx147065 	wl_frame_t *frm_p;
893*3737Shx147065 	uint16_t pkt_len, ret;
894*3737Shx147065 	uint16_t xmt_id, ring_idx;
895*3737Shx147065 	struct ieee80211_frame *wh;
896*3737Shx147065 	struct ieee80211_llc *llc;
897*3737Shx147065 
898*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
899*3737Shx147065 	if ((pcwl_p->pcwl_flag & (PCWL_CARD_READY | PCWL_CARD_LINKUP)) !=
900*3737Shx147065 	    (PCWL_CARD_READY | PCWL_CARD_LINKUP)) {
901*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
902*3737Shx147065 		freemsg(mblk_p);
903*3737Shx147065 		return (PCWL_SUCCESS);		/* drop packet */
904*3737Shx147065 	}
905*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
906*3737Shx147065 
907*3737Shx147065 	if (pullupmsg(mblk_p, -1) == 0) {
908*3737Shx147065 		freemsg(mblk_p);
909*3737Shx147065 		return (PCWL_SUCCESS);		/* drop packet */
910*3737Shx147065 	}
911*3737Shx147065 	wh = (struct ieee80211_frame *)mblk_p->b_rptr;
912*3737Shx147065 	llc = (struct ieee80211_llc *)&wh[1];
913*3737Shx147065 
914*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_txring.wl_tx_lock);
915*3737Shx147065 	ring_idx = pcwl_p->pcwl_txring.wl_tx_prod;
916*3737Shx147065 	pcwl_p->pcwl_txring.wl_tx_prod = (ring_idx + 1) & (WL_XMT_BUF_NUM - 1);
917*3737Shx147065 
918*3737Shx147065 	/*
919*3737Shx147065 	 * check whether there is a xmt buffer available
920*3737Shx147065 	 */
921*3737Shx147065 	while ((i < WL_XMT_BUF_NUM) &&
922*3737Shx147065 	    (pcwl_p->pcwl_txring.wl_tx_ring[ring_idx])) {
923*3737Shx147065 		ring_idx = pcwl_p->pcwl_txring.wl_tx_prod;
924*3737Shx147065 		pcwl_p->pcwl_txring.wl_tx_prod =
925*3737Shx147065 		    (ring_idx + 1) & (WL_XMT_BUF_NUM - 1);
926*3737Shx147065 		i++;
927*3737Shx147065 	}
928*3737Shx147065 	if (i == WL_XMT_BUF_NUM) {
929*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_txring.wl_tx_lock);
930*3737Shx147065 		mutex_enter(&pcwl_p->pcwl_glock);
931*3737Shx147065 		pcwl_p->pcwl_reschedule_need = B_TRUE;
932*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
933*3737Shx147065 		pcwl_p->pcwl_noxmtbuf++;
934*3737Shx147065 		return (PCWL_FAIL);
935*3737Shx147065 	}
936*3737Shx147065 	xmt_id = pcwl_p->pcwl_txring.wl_tx_fids[ring_idx];
937*3737Shx147065 	pcwl_p->pcwl_txring.wl_tx_ring[ring_idx] = xmt_id;
938*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_txring.wl_tx_lock);
939*3737Shx147065 
940*3737Shx147065 	buf = kmem_zalloc(PCWL_NICMEM_SZ, KM_SLEEP);
941*3737Shx147065 	buf_p = (ulong_t)buf & 1 ? buf + 1 : buf;
942*3737Shx147065 	frm_p = (wl_frame_t *)buf_p;
943*3737Shx147065 #ifdef DEBUG
944*3737Shx147065 	if (pcwl_debug & PCWL_DBG_SEND) {
945*3737Shx147065 		cmn_err(CE_NOTE, "pcwl send: packet");
946*3737Shx147065 		for (i = 0; i < mblk_p->b_wptr - mblk_p->b_rptr; i++)
947*3737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
948*3737Shx147065 			    *((unsigned char *)mblk_p->b_rptr + i));
949*3737Shx147065 	}
950*3737Shx147065 #endif
951*3737Shx147065 	pkt_len = msgdsize(mblk_p);
952*3737Shx147065 	if (pkt_len > (PCWL_NICMEM_SZ - sizeof (wl_frame_t))) {
953*3737Shx147065 		cmn_err(CE_WARN, "pcwl: send mblk is too long");
954*3737Shx147065 		kmem_free(buf, PCWL_NICMEM_SZ);
955*3737Shx147065 		freemsg(mblk_p);
956*3737Shx147065 		return (PCWL_SUCCESS);		/* drop packet */
957*3737Shx147065 	}
958*3737Shx147065 	if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) !=
959*3737Shx147065 	    IEEE80211_FC1_DIR_TODS) {
960*3737Shx147065 		kmem_free(buf, PCWL_NICMEM_SZ);
961*3737Shx147065 		freemsg(mblk_p);
962*3737Shx147065 		return (PCWL_SUCCESS);		/* drop packet */
963*3737Shx147065 	}
964*3737Shx147065 	bzero(frm_p, WL_802_11_HDRLEN);
965*3737Shx147065 
966*3737Shx147065 	frm_p->wl_tx_ctl = WL_TXCNTL_SET;
967*3737Shx147065 	bcopy(wh->i_addr3, frm_p->wl_dst_addr, ETHERADDRL); /* dst macaddr */
968*3737Shx147065 	bcopy(wh->i_addr2, frm_p->wl_src_addr, ETHERADDRL); /* src macaddr */
969*3737Shx147065 	frm_p->wl_len = htons(pkt_len  - sizeof (*wh));
970*3737Shx147065 	bcopy(llc, &frm_p->wl_dat[0], pkt_len - sizeof (*wh));
971*3737Shx147065 	pkt_len = pkt_len - (sizeof (*wh) + sizeof (*llc)) +
972*3737Shx147065 	    WL_802_11_HDRLEN;
973*3737Shx147065 	PCWLDBG((CE_NOTE, "send: DIX frmsz=%x pkt_len=%x\n",
974*3737Shx147065 	    WL_802_11_HDRLEN, pkt_len));
975*3737Shx147065 
976*3737Shx147065 	if (pkt_len & 1)	/* round up to 16-bit boundary and pad 0 */
977*3737Shx147065 		buf_p[pkt_len++] = 0;
978*3737Shx147065 
979*3737Shx147065 	ASSERT(pkt_len <= PCWL_NICMEM_SZ);
980*3737Shx147065 #ifdef DEBUG
981*3737Shx147065 	if (pcwl_debug & PCWL_DBG_SEND) {
982*3737Shx147065 		cmn_err(CE_NOTE, "pkt_len = %x\n", pkt_len);
983*3737Shx147065 		for (i = 0; i < pkt_len; i++)
984*3737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
985*3737Shx147065 			    *((unsigned char *)buf + i));
986*3737Shx147065 	}
987*3737Shx147065 #endif
988*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
989*3737Shx147065 	ret = (WRCH1(pcwl_p, xmt_id, 0, (uint16_t *)buf_p, 0x2e) ||
990*3737Shx147065 	    WRPKT(pcwl_p, xmt_id, 0x2e, (uint16_t *)(buf_p + 0x2e),
991*3737Shx147065 	    pkt_len - 0x2e));
992*3737Shx147065 	if (ret) {
993*3737Shx147065 		goto done;
994*3737Shx147065 	}
995*3737Shx147065 	PCWLDBG((CE_NOTE, "send: xmt_id=%x send=%x\n", xmt_id, pkt_len));
996*3737Shx147065 	(void) pcwl_set_cmd(pcwl_p, WL_CMD_TX | WL_RECLAIM, xmt_id);
997*3737Shx147065 
998*3737Shx147065 done:
999*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
1000*3737Shx147065 	kmem_free(buf, PCWL_NICMEM_SZ);
1001*3737Shx147065 	freemsg(mblk_p);
1002*3737Shx147065 	return (PCWL_SUCCESS);
1003*3737Shx147065 }
1004*3737Shx147065 
1005*3737Shx147065 static mblk_t *
1006*3737Shx147065 pcwl_tx(void *arg, mblk_t *mp)
1007*3737Shx147065 {
1008*3737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
1009*3737Shx147065 	mblk_t *next;
1010*3737Shx147065 
1011*3737Shx147065 	ASSERT(mp != NULL);
1012*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
1013*3737Shx147065 	if ((pcwl_p->pcwl_flag & (PCWL_CARD_LINKUP | PCWL_CARD_READY)) !=
1014*3737Shx147065 	    (PCWL_CARD_LINKUP | PCWL_CARD_READY)) {
1015*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
1016*3737Shx147065 		return (mp);
1017*3737Shx147065 	}
1018*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
1019*3737Shx147065 	while (mp != NULL) {
1020*3737Shx147065 		next =  mp->b_next;
1021*3737Shx147065 		mp->b_next = NULL;
1022*3737Shx147065 
1023*3737Shx147065 		if (pcwl_send(pcwl_p, mp)) {
1024*3737Shx147065 			mp->b_next = next;
1025*3737Shx147065 			break;
1026*3737Shx147065 		}
1027*3737Shx147065 		mp = next;
1028*3737Shx147065 	}
1029*3737Shx147065 	return (mp);
1030*3737Shx147065 }
1031*3737Shx147065 
1032*3737Shx147065 static int
1033*3737Shx147065 pcwl_prom(void *arg, boolean_t on)
1034*3737Shx147065 {
1035*3737Shx147065 	int ret = PCWL_SUCCESS;
1036*3737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
1037*3737Shx147065 
1038*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
1039*3737Shx147065 	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
1040*3737Shx147065 		ret = PCWL_FAIL;
1041*3737Shx147065 		goto done;
1042*3737Shx147065 	}
1043*3737Shx147065 
1044*3737Shx147065 	PCWLDBG((CE_NOTE, "pcwl_prom called %x\n", on));
1045*3737Shx147065 
1046*3737Shx147065 	if (on)
1047*3737Shx147065 		pcwl_p->pcwl_rf.rf_promiscuous = 1;
1048*3737Shx147065 	else
1049*3737Shx147065 		pcwl_p->pcwl_rf.rf_promiscuous = 0;
1050*3737Shx147065 	if (ret = pcwl_fil_ltv(pcwl_p, 2, WL_RID_PROMISC,
1051*3737Shx147065 	    pcwl_p->pcwl_rf.rf_promiscuous)) {
1052*3737Shx147065 		ret = PCWL_FAIL;
1053*3737Shx147065 	}
1054*3737Shx147065 done:
1055*3737Shx147065 	if (ret)
1056*3737Shx147065 		cmn_err(CE_WARN, "pcwl promisc: failed\n");
1057*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
1058*3737Shx147065 	return (ret);
1059*3737Shx147065 }
1060*3737Shx147065 
1061*3737Shx147065 static int
1062*3737Shx147065 pcwl_gstat(void *arg, uint_t statitem, uint64_t *val)
1063*3737Shx147065 {
1064*3737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
1065*3737Shx147065 	int ret = PCWL_SUCCESS;
1066*3737Shx147065 	uint64_t *cntr_p = pcwl_p->pcwl_cntrs_s;
1067*3737Shx147065 	uint16_t rate = 0;
1068*3737Shx147065 	uint64_t speed;
1069*3737Shx147065 
1070*3737Shx147065 	PCWLDBG((CE_NOTE, "pcwl_gstat called\n"));
1071*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
1072*3737Shx147065 	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
1073*3737Shx147065 		ret = PCWL_FAIL;
1074*3737Shx147065 		goto done;
1075*3737Shx147065 	}
1076*3737Shx147065 
1077*3737Shx147065 	if (pcwl_get_ltv(pcwl_p, 2, WL_RID_CUR_TX_RATE, &rate)) {
1078*3737Shx147065 		cmn_err(CE_WARN, "pcwl kstat: get speed failed\n");
1079*3737Shx147065 		ret = PCWL_FAIL;
1080*3737Shx147065 		goto done;
1081*3737Shx147065 	}
1082*3737Shx147065 	switch (pcwl_p->pcwl_chip_type) {
1083*3737Shx147065 	case PCWL_CHIP_PRISMII:
1084*3737Shx147065 		switch (rate) {
1085*3737Shx147065 		case WL_SPEED_1Mbps_P2:		rate = 2;	break;
1086*3737Shx147065 		case WL_SPEED_2Mbps_P2:		rate = 4;	break;
1087*3737Shx147065 		case WL_SPEED_55Mbps_P2:	rate = 11;	break;
1088*3737Shx147065 		case WL_SPEED_11Mbps_P2:	rate = 22;	break;
1089*3737Shx147065 		default:			rate = 0;	break;
1090*3737Shx147065 		}
1091*3737Shx147065 		speed = rate * 500000;
1092*3737Shx147065 		break;
1093*3737Shx147065 	case PCWL_CHIP_LUCENT:
1094*3737Shx147065 	default:
1095*3737Shx147065 		speed = rate * 1000000;
1096*3737Shx147065 		if (rate == 6)
1097*3737Shx147065 			speed = 5500000;
1098*3737Shx147065 		break;
1099*3737Shx147065 	}
1100*3737Shx147065 
1101*3737Shx147065 	switch (statitem) {
1102*3737Shx147065 	case MAC_STAT_IFSPEED:
1103*3737Shx147065 		*val = speed;
1104*3737Shx147065 		break;
1105*3737Shx147065 	case MAC_STAT_NOXMTBUF:
1106*3737Shx147065 		*val = pcwl_p->pcwl_noxmtbuf;
1107*3737Shx147065 		break;
1108*3737Shx147065 	case MAC_STAT_NORCVBUF:
1109*3737Shx147065 		*val = cntr_p[WLC_RX_DISCARDS_NOBUF];
1110*3737Shx147065 		break;
1111*3737Shx147065 	case MAC_STAT_IERRORS:
1112*3737Shx147065 		*val = 0;
1113*3737Shx147065 		break;
1114*3737Shx147065 	case MAC_STAT_OERRORS:
1115*3737Shx147065 		*val = cntr_p[WLC_TX_DISCARDS] +
1116*3737Shx147065 		    cntr_p[WLC_TX_DISCARDS_WRONG_SA];
1117*3737Shx147065 		break;
1118*3737Shx147065 	case MAC_STAT_RBYTES:
1119*3737Shx147065 		*val = cntr_p[WLC_RX_UNICAST_OCTETS];
1120*3737Shx147065 		break;
1121*3737Shx147065 	case MAC_STAT_IPACKETS:
1122*3737Shx147065 		*val = cntr_p[WLC_RX_UNICAST_FRAMES];
1123*3737Shx147065 		break;
1124*3737Shx147065 	case MAC_STAT_OBYTES:
1125*3737Shx147065 		*val = cntr_p[WLC_TX_UNICAST_OCTETS];
1126*3737Shx147065 		break;
1127*3737Shx147065 	case MAC_STAT_OPACKETS:
1128*3737Shx147065 		*val = cntr_p[WLC_TX_UNICAST_FRAMES];
1129*3737Shx147065 		break;
1130*3737Shx147065 	case WIFI_STAT_TX_FAILED:
1131*3737Shx147065 		*val = cntr_p[WLC_TX_RETRY_LIMIT] +
1132*3737Shx147065 		    cntr_p[WLC_TX_DEFERRED_XMITS];
1133*3737Shx147065 		break;
1134*3737Shx147065 	case WIFI_STAT_TX_RETRANS:
1135*3737Shx147065 		*val = cntr_p[WLC_TX_SINGLE_RETRIES] +
1136*3737Shx147065 		    cntr_p[WLC_TX_MULTI_RETRIES];
1137*3737Shx147065 		break;
1138*3737Shx147065 	case WIFI_STAT_FCS_ERRORS:
1139*3737Shx147065 		*val = cntr_p[WLC_RX_FCS_ERRORS];
1140*3737Shx147065 		break;
1141*3737Shx147065 	case WIFI_STAT_WEP_ERRORS:
1142*3737Shx147065 		*val = cntr_p[WLC_RX_WEP_CANT_DECRYPT];
1143*3737Shx147065 		break;
1144*3737Shx147065 	case WIFI_STAT_MCAST_TX:
1145*3737Shx147065 		*val = cntr_p[WLC_TX_MULTICAST_FRAMES];
1146*3737Shx147065 		break;
1147*3737Shx147065 	case WIFI_STAT_MCAST_RX:
1148*3737Shx147065 		*val = cntr_p[WLC_RX_MULTICAST_FRAMES];
1149*3737Shx147065 		break;
1150*3737Shx147065 	case WIFI_STAT_TX_FRAGS:
1151*3737Shx147065 		*val = cntr_p[WLC_TX_FRAGMENTS];
1152*3737Shx147065 		break;
1153*3737Shx147065 	case WIFI_STAT_RX_FRAGS:
1154*3737Shx147065 		*val =	cntr_p[WLC_RX_FRAGMENTS] +
1155*3737Shx147065 		    cntr_p[WLC_RX_MSG_IN_MSG_FRAGS] +
1156*3737Shx147065 		    cntr_p[WLC_RX_MSG_IN_BAD_MSG_FRAGS];
1157*3737Shx147065 		break;
1158*3737Shx147065 	case WIFI_STAT_RTS_SUCCESS:
1159*3737Shx147065 	case WIFI_STAT_RTS_FAILURE:
1160*3737Shx147065 	case WIFI_STAT_ACK_FAILURE:
1161*3737Shx147065 	case WIFI_STAT_RX_DUPS:
1162*3737Shx147065 		*val = 0;
1163*3737Shx147065 		break;
1164*3737Shx147065 	default:
1165*3737Shx147065 		ret = ENOTSUP;
1166*3737Shx147065 	}
1167*3737Shx147065 done:
1168*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
1169*3737Shx147065 	return (ret);
1170*3737Shx147065 }
1171*3737Shx147065 
1172*3737Shx147065 static int
1173*3737Shx147065 pcwl_sdmulti(void *arg, boolean_t add, const uint8_t *eth_p)
1174*3737Shx147065 {
1175*3737Shx147065 	int ret = PCWL_SUCCESS;
1176*3737Shx147065 	uint16_t i;
1177*3737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
1178*3737Shx147065 	uint16_t *mc_p = pcwl_p->pcwl_mcast;
1179*3737Shx147065 
1180*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
1181*3737Shx147065 	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
1182*3737Shx147065 		ret = PCWL_FAIL;
1183*3737Shx147065 		goto done;
1184*3737Shx147065 	}
1185*3737Shx147065 
1186*3737Shx147065 	if (add) { /* enable multicast on eth_p, search for available entries */
1187*3737Shx147065 		for (i = 0; i < 16; i++, mc_p += (ETHERADDRL >> 1)) {
1188*3737Shx147065 			if (!ether_cmp(eth_p, mc_p))
1189*3737Shx147065 				break;
1190*3737Shx147065 		}
1191*3737Shx147065 		if (i < 16)	/* already part of the filter */
1192*3737Shx147065 			goto done;
1193*3737Shx147065 		mc_p = pcwl_p->pcwl_mcast;	/* reset mc_p for 2nd scan */
1194*3737Shx147065 		for (i = 0; i < 16; i++, mc_p += (ETHERADDRL >> 1)) {
1195*3737Shx147065 			PCWLDBG((CE_NOTE, "smulti: mc[%x]=%s\n", i,
1196*3737Shx147065 			    ether_sprintf((struct ether_addr *)mc_p)));
1197*3737Shx147065 			if (mc_p[0] == 0 && mc_p[1] == 0 && mc_p[2] == 0)
1198*3737Shx147065 				break;
1199*3737Shx147065 		}
1200*3737Shx147065 		if (i >= 16)	/* can't find a vacant entry */
1201*3737Shx147065 			goto done;
1202*3737Shx147065 		ether_copy(eth_p, mc_p);
1203*3737Shx147065 	} else { /* disable multicast, locate the entry and clear it */
1204*3737Shx147065 		for (i = 0; i < 16; i++, mc_p += (ETHERADDRL >> 1)) {
1205*3737Shx147065 			if (!ether_cmp(eth_p, mc_p))
1206*3737Shx147065 				break;
1207*3737Shx147065 		}
1208*3737Shx147065 		if (i >= 16)
1209*3737Shx147065 			goto done;
1210*3737Shx147065 		mc_p[0] = 0;
1211*3737Shx147065 		mc_p[1] = 0;
1212*3737Shx147065 		mc_p[2] = 0;
1213*3737Shx147065 	}
1214*3737Shx147065 	/*
1215*3737Shx147065 	 * re-blow the entire 16 entries buffer
1216*3737Shx147065 	 */
1217*3737Shx147065 	if (i = pcwl_put_ltv(pcwl_p, ETHERADDRL << 4, WL_RID_MCAST,
1218*3737Shx147065 	    pcwl_p->pcwl_mcast)) {
1219*3737Shx147065 		ret = PCWL_FAIL;
1220*3737Shx147065 	}
1221*3737Shx147065 done:
1222*3737Shx147065 	if (ret)
1223*3737Shx147065 		cmn_err(CE_WARN, "pcwl set multi addr: failed\n");
1224*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
1225*3737Shx147065 	return (ret);
1226*3737Shx147065 }
1227*3737Shx147065 
1228*3737Shx147065 static uint_t
1229*3737Shx147065 pcwl_intr(caddr_t arg)
1230*3737Shx147065 {
1231*3737Shx147065 	uint16_t stat;
1232*3737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
1233*3737Shx147065 
1234*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
1235*3737Shx147065 	if ((pcwl_p->pcwl_flag & (PCWL_CARD_READY | PCWL_CARD_INTREN)) !=
1236*3737Shx147065 	    (PCWL_CARD_READY | PCWL_CARD_INTREN)) {
1237*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
1238*3737Shx147065 		return (DDI_INTR_UNCLAIMED);
1239*3737Shx147065 	}
1240*3737Shx147065 	PCWL_READ(pcwl_p, WL_EVENT_STAT, stat);
1241*3737Shx147065 	if (!(stat & WL_INTRS) || stat == WL_EV_ALL) {
1242*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
1243*3737Shx147065 		return (DDI_INTR_UNCLAIMED);
1244*3737Shx147065 	}
1245*3737Shx147065 
1246*3737Shx147065 	PCWL_WRITE(pcwl_p, WL_INT_EN, 0);
1247*3737Shx147065 	if (stat & WL_EV_RX) {
1248*3737Shx147065 		pcwl_rcv(pcwl_p);
1249*3737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_RX);
1250*3737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_RX);
1251*3737Shx147065 	}
1252*3737Shx147065 	if (stat & WL_EV_TX) {
1253*3737Shx147065 		if (pcwl_txdone(pcwl_p) == PCWL_SUCCESS) {
1254*3737Shx147065 			if (pcwl_p->pcwl_reschedule_need == B_TRUE) {
1255*3737Shx147065 				mutex_exit(&pcwl_p->pcwl_glock);
1256*3737Shx147065 				mac_tx_update(GLD3(pcwl_p));
1257*3737Shx147065 				mutex_enter(&pcwl_p->pcwl_glock);
1258*3737Shx147065 				pcwl_p->pcwl_reschedule_need = B_FALSE;
1259*3737Shx147065 			}
1260*3737Shx147065 		}
1261*3737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX);
1262*3737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX);
1263*3737Shx147065 	}
1264*3737Shx147065 	if (stat & WL_EV_ALLOC) {
1265*3737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_ALLOC | 0x1000);
1266*3737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, 0x1000);
1267*3737Shx147065 	}
1268*3737Shx147065 	if (stat & WL_EV_INFO) {
1269*3737Shx147065 		pcwl_infodone(pcwl_p);
1270*3737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_INFO);
1271*3737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_INFO);
1272*3737Shx147065 	}
1273*3737Shx147065 	if (stat & WL_EV_TX_EXC) {
1274*3737Shx147065 		if (pcwl_txdone(pcwl_p) == PCWL_SUCCESS) {
1275*3737Shx147065 			if (pcwl_p->pcwl_reschedule_need == B_TRUE) {
1276*3737Shx147065 				mutex_exit(&pcwl_p->pcwl_glock);
1277*3737Shx147065 				mac_tx_update(GLD3(pcwl_p));
1278*3737Shx147065 				mutex_enter(&pcwl_p->pcwl_glock);
1279*3737Shx147065 				pcwl_p->pcwl_reschedule_need = B_FALSE;
1280*3737Shx147065 			}
1281*3737Shx147065 		}
1282*3737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX_EXC);
1283*3737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX_EXC);
1284*3737Shx147065 	}
1285*3737Shx147065 	if (stat & WL_EV_INFO_DROP) {
1286*3737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_INFO_DROP);
1287*3737Shx147065 		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_INFO_DROP);
1288*3737Shx147065 	}
1289*3737Shx147065 	PCWL_ENABLE_INTR(pcwl_p);
1290*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
1291*3737Shx147065 
1292*3737Shx147065 	return (DDI_INTR_CLAIMED);
1293*3737Shx147065 }
1294*3737Shx147065 
1295*3737Shx147065 static uint_t
1296*3737Shx147065 pcwl_intr_hi(caddr_t arg)
1297*3737Shx147065 {
1298*3737Shx147065 	uint16_t stat;
1299*3737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
1300*3737Shx147065 
1301*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
1302*3737Shx147065 	if ((pcwl_p->pcwl_flag & (PCWL_CARD_READY | PCWL_CARD_INTREN)) !=
1303*3737Shx147065 	    (PCWL_CARD_READY | PCWL_CARD_INTREN)) {
1304*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
1305*3737Shx147065 		return (DDI_INTR_UNCLAIMED);
1306*3737Shx147065 	}
1307*3737Shx147065 	PCWL_READ(pcwl_p, WL_EVENT_STAT, stat);
1308*3737Shx147065 	if (!(stat & WL_INTRS) || stat == WL_EV_ALL) {
1309*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
1310*3737Shx147065 		return (DDI_INTR_UNCLAIMED);
1311*3737Shx147065 	}
1312*3737Shx147065 	PCWL_WRITE(pcwl_p, WL_INT_EN, 0); /* disable interrupt without ack */
1313*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
1314*3737Shx147065 	ddi_trigger_softintr(pcwl_p->pcwl_softint_id);
1315*3737Shx147065 	return (DDI_INTR_CLAIMED);
1316*3737Shx147065 }
1317*3737Shx147065 
1318*3737Shx147065 /*
1319*3737Shx147065  * called at interrupt context to retrieve data from card
1320*3737Shx147065  */
1321*3737Shx147065 static void
1322*3737Shx147065 pcwl_rcv(pcwl_maci_t *pcwl_p)
1323*3737Shx147065 {
1324*3737Shx147065 	uint16_t id, len, off, ret, frm_ctl;
1325*3737Shx147065 	wl_frame_t frm;
1326*3737Shx147065 	mblk_t *mp = allocb(PCWL_NICMEM_SZ, BPRI_MED);
1327*3737Shx147065 	if (!mp)
1328*3737Shx147065 		return;
1329*3737Shx147065 	ASSERT(mp->b_rptr == mp->b_wptr);
1330*3737Shx147065 
1331*3737Shx147065 	PCWL_READ(pcwl_p, WL_RX_FID, id);
1332*3737Shx147065 	PCWL_WRITE(pcwl_p, WL_RX_FID, 0);
1333*3737Shx147065 	if (id == WL_INVALID_FID) {
1334*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl rcv: get rx_fid failed\n"));
1335*3737Shx147065 		ret = PCWL_FAIL;
1336*3737Shx147065 		goto done;
1337*3737Shx147065 	}
1338*3737Shx147065 	if (ret = RDCH0(pcwl_p, id, 0, (uint16_t *)&frm, sizeof (frm))) {
1339*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl rcv: read frm failed %x\n", ret));
1340*3737Shx147065 		goto done;
1341*3737Shx147065 	}
1342*3737Shx147065 	if (frm.wl_status & WL_STAT_ERRSTAT) {
1343*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl rcv: errstat %x\n", frm.wl_status));
1344*3737Shx147065 		ret = frm.wl_status;
1345*3737Shx147065 		goto done;
1346*3737Shx147065 	}
1347*3737Shx147065 	PCWLDBG((CE_NOTE, "pcwl rcv: frame type %x\n", frm.wl_status));
1348*3737Shx147065 #ifdef DEBUG
1349*3737Shx147065 	if (pcwl_debug & PCWL_DBG_RCV) {
1350*3737Shx147065 		int i;
1351*3737Shx147065 		cmn_err(CE_NOTE, "pcwl rcv: frm header\n");
1352*3737Shx147065 		for (i = 0; i < WL_802_11_HDRLEN; i++)
1353*3737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
1354*3737Shx147065 			    *((uint8_t *)&frm + i));
1355*3737Shx147065 	}
1356*3737Shx147065 #endif
1357*3737Shx147065 	len = frm.wl_dat_len;
1358*3737Shx147065 	/*
1359*3737Shx147065 	 * this driver deal with WEP by itself. so plugin always thinks no wep.
1360*3737Shx147065 	 */
1361*3737Shx147065 	frm.wl_frame_ctl &= ~(IEEE80211_FC1_WEP << 8);
1362*3737Shx147065 	frm_ctl = frm.wl_frame_ctl;
1363*3737Shx147065 	switch (frm.wl_status) {
1364*3737Shx147065 	case WL_STAT_1042:
1365*3737Shx147065 	case WL_STAT_TUNNEL:
1366*3737Shx147065 	case WL_STAT_WMP_MSG:
1367*3737Shx147065 		PCWL_SWAP16((uint16_t *)&frm.wl_frame_ctl,
1368*3737Shx147065 		    sizeof (struct ieee80211_frame));
1369*3737Shx147065 		/*
1370*3737Shx147065 		 * discard those frames which are not from the AP we connect or
1371*3737Shx147065 		 * without 'ap->sta' direction
1372*3737Shx147065 		 */
1373*3737Shx147065 		if ((pcwl_p->pcwl_rf.rf_porttype == WL_BSS_BSS) &&
1374*3737Shx147065 		    ((((frm_ctl >> 8) & IEEE80211_FC1_DIR_MASK) !=
1375*3737Shx147065 		    IEEE80211_FC1_DIR_FROMDS) ||
1376*3737Shx147065 		    bcmp(pcwl_p->pcwl_bssid, frm.wl_addr2, 6) != 0)) {
1377*3737Shx147065 			ret = PCWL_FAIL;
1378*3737Shx147065 			goto done;
1379*3737Shx147065 		}
1380*3737Shx147065 
1381*3737Shx147065 		bcopy(&frm.wl_frame_ctl, mp->b_wptr,
1382*3737Shx147065 		    sizeof (struct ieee80211_frame));
1383*3737Shx147065 		mp->b_wptr += sizeof (struct ieee80211_frame);
1384*3737Shx147065 
1385*3737Shx147065 		PCWL_SWAP16((uint16_t *)&frm.wl_dat[0],
1386*3737Shx147065 		    sizeof (struct ieee80211_llc));
1387*3737Shx147065 		bcopy(&frm.wl_dat[0], mp->b_wptr,
1388*3737Shx147065 		    sizeof (struct ieee80211_llc));
1389*3737Shx147065 		mp->b_wptr += sizeof (struct ieee80211_llc);
1390*3737Shx147065 
1391*3737Shx147065 		len -= (2 + WL_SNAPHDR_LEN);
1392*3737Shx147065 		off = WL_802_11_HDRLEN;
1393*3737Shx147065 		break;
1394*3737Shx147065 	default:
1395*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl rcv: incorrect pkt\n"));
1396*3737Shx147065 		break;
1397*3737Shx147065 	}
1398*3737Shx147065 	if (len > MBLKSIZE(mp)) {
1399*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl rcv: oversz pkt %x\n", len));
1400*3737Shx147065 		ret = PCWL_FAIL;
1401*3737Shx147065 		goto done;
1402*3737Shx147065 	}
1403*3737Shx147065 	if (len & 1)
1404*3737Shx147065 		len++;
1405*3737Shx147065 	ret = RDPKT(pcwl_p, id, off, (uint16_t *)mp->b_wptr, len);
1406*3737Shx147065 done:
1407*3737Shx147065 	if (ret) {
1408*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl rcv: rd data %x\n", ret));
1409*3737Shx147065 		freemsg(mp);
1410*3737Shx147065 		return;
1411*3737Shx147065 	}
1412*3737Shx147065 	mp->b_wptr = mp->b_wptr + len;
1413*3737Shx147065 #ifdef DEBUG
1414*3737Shx147065 	if (pcwl_debug & PCWL_DBG_RCV) {
1415*3737Shx147065 		int i;
1416*3737Shx147065 		cmn_err(CE_NOTE, "pcwl rcv: len=0x%x\n", len);
1417*3737Shx147065 		for (i = 0; i < len+14; i++)
1418*3737Shx147065 			cmn_err(CE_NOTE, "%x: %x\n", i,
1419*3737Shx147065 			    *((uint8_t *)mp->b_rptr + i));
1420*3737Shx147065 	}
1421*3737Shx147065 #endif
1422*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
1423*3737Shx147065 	mac_rx(GLD3(pcwl_p), NULL, mp);
1424*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
1425*3737Shx147065 }
1426*3737Shx147065 
1427*3737Shx147065 static uint32_t
1428*3737Shx147065 pcwl_txdone(pcwl_maci_t *pcwl_p)
1429*3737Shx147065 {
1430*3737Shx147065 	uint16_t fid, i;
1431*3737Shx147065 	PCWL_READ(pcwl_p, WL_ALLOC_FID, fid);
1432*3737Shx147065 	PCWL_WRITE(pcwl_p, WL_ALLOC_FID, 0);
1433*3737Shx147065 
1434*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_txring.wl_tx_lock);
1435*3737Shx147065 	for (i = 0; i < WL_XMT_BUF_NUM; i++) {
1436*3737Shx147065 		if (fid == pcwl_p->pcwl_txring.wl_tx_ring[i]) {
1437*3737Shx147065 			pcwl_p->pcwl_txring.wl_tx_ring[i] = 0;
1438*3737Shx147065 			break;
1439*3737Shx147065 		}
1440*3737Shx147065 	}
1441*3737Shx147065 	pcwl_p->pcwl_txring.wl_tx_cons =
1442*3737Shx147065 	    (pcwl_p->pcwl_txring.wl_tx_cons + 1) & (WL_XMT_BUF_NUM - 1);
1443*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_txring.wl_tx_lock);
1444*3737Shx147065 	if (i == WL_XMT_BUF_NUM)
1445*3737Shx147065 		return (PCWL_FAIL);
1446*3737Shx147065 	return (PCWL_SUCCESS);
1447*3737Shx147065 
1448*3737Shx147065 }
1449*3737Shx147065 
1450*3737Shx147065 static void
1451*3737Shx147065 pcwl_infodone(pcwl_maci_t *pcwl_p)
1452*3737Shx147065 {
1453*3737Shx147065 	uint16_t id, ret, i;
1454*3737Shx147065 	uint16_t linkStatus[2];
1455*3737Shx147065 	uint16_t linkStat;
1456*3737Shx147065 	wifi_data_t wd = { 0 };
1457*3737Shx147065 
1458*3737Shx147065 	PCWL_READ(pcwl_p, WL_INFO_FID, id);
1459*3737Shx147065 	if (id == WL_INVALID_FID) {
1460*3737Shx147065 		cmn_err(CE_WARN, "pcwl infodone: read info_fid failed\n");
1461*3737Shx147065 		return;
1462*3737Shx147065 	}
1463*3737Shx147065 	if (ret = RDCH0(pcwl_p, id, 0, linkStatus, sizeof (linkStatus))) {
1464*3737Shx147065 		PCWLDBG((CE_WARN, "pcwl infodone read infoFrm failed %x\n",
1465*3737Shx147065 		    ret));
1466*3737Shx147065 		return;
1467*3737Shx147065 	}
1468*3737Shx147065 	PCWLDBG((CE_NOTE, "pcwl infodone: Frame length= %x, Frame Type = %x\n",
1469*3737Shx147065 	    linkStatus[0], linkStatus[1]));
1470*3737Shx147065 
1471*3737Shx147065 	switch (linkStatus[1]) {
1472*3737Shx147065 	case WL_INFO_LINK_STAT:
1473*3737Shx147065 		(void) RDCH0(pcwl_p, id, sizeof (linkStatus), &linkStat,
1474*3737Shx147065 		    sizeof (linkStat));
1475*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl infodone: link status=%x\n", linkStat));
1476*3737Shx147065 		if (!(pcwl_p->pcwl_flag & PCWL_CARD_LINKUP) &&
1477*3737Shx147065 		    linkStat == WL_LINK_CONNECT) {
1478*3737Shx147065 #ifdef DEBUG
1479*3737Shx147065 		if (pcwl_debug & PCWL_DBG_LINKINFO)
1480*3737Shx147065 			cmn_err(CE_NOTE, "pcwl: Link up \n");
1481*3737Shx147065 #endif
1482*3737Shx147065 			pcwl_p->pcwl_flag |= PCWL_CARD_LINKUP;
1483*3737Shx147065 			mutex_exit(&pcwl_p->pcwl_glock);
1484*3737Shx147065 			if (pcwl_p->pcwl_connect_timeout_id != 0) {
1485*3737Shx147065 				(void) untimeout(pcwl_p->
1486*3737Shx147065 				    pcwl_connect_timeout_id);
1487*3737Shx147065 				pcwl_p->pcwl_connect_timeout_id = 0;
1488*3737Shx147065 			}
1489*3737Shx147065 			mutex_enter(&pcwl_p->pcwl_glock);
1490*3737Shx147065 			mac_link_update(GLD3(pcwl_p), LINK_STATE_UP);
1491*3737Shx147065 			(void) pcwl_get_ltv(pcwl_p, 6,
1492*3737Shx147065 			    WL_RID_BSSID, (uint16_t *)pcwl_p->pcwl_bssid);
1493*3737Shx147065 			PCWL_SWAP16((uint16_t *)pcwl_p->pcwl_bssid, 6);
1494*3737Shx147065 			pcwl_get_rssi(pcwl_p);
1495*3737Shx147065 			bcopy(pcwl_p->pcwl_bssid, wd.wd_bssid, 6);
1496*3737Shx147065 			wd.wd_secalloc = WIFI_SEC_NONE;
1497*3737Shx147065 			wd.wd_opmode = IEEE80211_M_STA;
1498*3737Shx147065 			(void) mac_pdata_update(pcwl_p->pcwl_mh, &wd,
1499*3737Shx147065 			    sizeof (wd));
1500*3737Shx147065 		}
1501*3737Shx147065 		if ((pcwl_p->pcwl_flag & PCWL_CARD_LINKUP) &&
1502*3737Shx147065 		    ((linkStat == WL_LINK_DISCONNECT) ||
1503*3737Shx147065 		    (linkStat == WL_LINK_AP_OOR))) {
1504*3737Shx147065 #ifdef DEBUG
1505*3737Shx147065 		if (pcwl_debug & PCWL_DBG_LINKINFO)
1506*3737Shx147065 			cmn_err(CE_NOTE, "pcwl: Link down \n");
1507*3737Shx147065 #endif
1508*3737Shx147065 			PCWLDBG((CE_NOTE, "pcwl infodone: link status = %d\n",
1509*3737Shx147065 			    linkStat));
1510*3737Shx147065 			pcwl_p->pcwl_flag &= ~PCWL_CARD_LINKUP;
1511*3737Shx147065 			if (linkStat == WL_LINK_AP_OOR)
1512*3737Shx147065 				pcwl_p->pcwl_connect_timeout_id =
1513*3737Shx147065 				    timeout(pcwl_connect_timeout,
1514*3737Shx147065 				    pcwl_p, drv_usectohz(1000));
1515*3737Shx147065 			mutex_exit(&pcwl_p->pcwl_glock);
1516*3737Shx147065 			mac_link_update(GLD3(pcwl_p), LINK_STATE_DOWN);
1517*3737Shx147065 			mutex_enter(&pcwl_p->pcwl_glock);
1518*3737Shx147065 		}
1519*3737Shx147065 		break;
1520*3737Shx147065 	case WL_INFO_SCAN_RESULTS:
1521*3737Shx147065 	case WL_INFO_HSCAN_RESULTS:
1522*3737Shx147065 		pcwl_ssid_scan(pcwl_p, id, linkStatus[0], linkStatus[1]);
1523*3737Shx147065 			break;
1524*3737Shx147065 	case WL_INFO_COUNTERS:
1525*3737Shx147065 		linkStatus[0]--;
1526*3737Shx147065 		if (linkStatus[0] > WLC_STAT_CNT) {
1527*3737Shx147065 			linkStatus[0] = MIN(linkStatus[0], WLC_STAT_CNT);
1528*3737Shx147065 		}
1529*3737Shx147065 		(void) RDCH0(pcwl_p, id, sizeof (linkStatus),
1530*3737Shx147065 		    pcwl_p->pcwl_cntrs_t, linkStatus[0]<<1);
1531*3737Shx147065 		/*
1532*3737Shx147065 		 * accumulate all the statistics items for kstat use.
1533*3737Shx147065 		 */
1534*3737Shx147065 		for (i = 0; i < WLC_STAT_CNT; i++)
1535*3737Shx147065 			pcwl_p->pcwl_cntrs_s[i] += pcwl_p->pcwl_cntrs_t[i];
1536*3737Shx147065 		break;
1537*3737Shx147065 	default:
1538*3737Shx147065 		break;
1539*3737Shx147065 	}
1540*3737Shx147065 }
1541*3737Shx147065 
1542*3737Shx147065 static uint16_t
1543*3737Shx147065 pcwl_set_cmd(pcwl_maci_t *pcwl_p, uint16_t cmd, uint16_t param)
1544*3737Shx147065 {
1545*3737Shx147065 	int i;
1546*3737Shx147065 	uint16_t stat;
1547*3737Shx147065 
1548*3737Shx147065 	if (((cmd == WL_CMD_ENABLE) &&
1549*3737Shx147065 	    ((pcwl_p->pcwl_flag & PCWL_ENABLED) != 0)) ||
1550*3737Shx147065 	    ((cmd == WL_CMD_DISABLE) &&
1551*3737Shx147065 	    ((pcwl_p->pcwl_flag & PCWL_ENABLED) == 0)))
1552*3737Shx147065 		return (PCWL_SUCCESS);
1553*3737Shx147065 
1554*3737Shx147065 	for (i = 0; i < WL_TIMEOUT; i++) {
1555*3737Shx147065 		PCWL_READ(pcwl_p, WL_COMMAND, stat);
1556*3737Shx147065 		if (stat & WL_CMD_BUSY) {
1557*3737Shx147065 			drv_usecwait(1);
1558*3737Shx147065 		} else {
1559*3737Shx147065 			break;
1560*3737Shx147065 		}
1561*3737Shx147065 	}
1562*3737Shx147065 	if (i == WL_TIMEOUT) {
1563*3737Shx147065 		cmn_err(CE_WARN, "pcwl: setcmd %x, %x timeout %x due to "
1564*3737Shx147065 		    "busy bit\n", cmd, param, stat);
1565*3737Shx147065 		return (PCWL_TIMEDOUT_CMD);
1566*3737Shx147065 	}
1567*3737Shx147065 
1568*3737Shx147065 	PCWL_WRITE(pcwl_p, WL_PARAM0, param);
1569*3737Shx147065 	PCWL_WRITE(pcwl_p, WL_PARAM1, 0);
1570*3737Shx147065 	PCWL_WRITE(pcwl_p, WL_PARAM2, 0);
1571*3737Shx147065 	PCWL_WRITE(pcwl_p, WL_COMMAND, cmd);
1572*3737Shx147065 	if (cmd == WL_CMD_INI)
1573*3737Shx147065 		drv_usecwait(100000); /* wait .1 sec */
1574*3737Shx147065 
1575*3737Shx147065 	for (i = 0; i < WL_TIMEOUT; i++) {
1576*3737Shx147065 		PCWL_READ(pcwl_p, WL_EVENT_STAT, stat);
1577*3737Shx147065 		if (!(stat & WL_EV_CMD)) {
1578*3737Shx147065 			drv_usecwait(1);
1579*3737Shx147065 		} else {
1580*3737Shx147065 			break;
1581*3737Shx147065 		}
1582*3737Shx147065 	}
1583*3737Shx147065 	if (i == WL_TIMEOUT) {
1584*3737Shx147065 		cmn_err(CE_WARN, "pcwl: setcmd %x,%x timeout %x\n",
1585*3737Shx147065 		    cmd, param, stat);
1586*3737Shx147065 		if (stat & (WL_EV_ALLOC | WL_EV_RX))
1587*3737Shx147065 			PCWL_WRITE(pcwl_p, WL_EVENT_ACK, stat);
1588*3737Shx147065 		return (PCWL_TIMEDOUT_CMD);
1589*3737Shx147065 	}
1590*3737Shx147065 	PCWL_READ(pcwl_p, WL_STATUS, stat);
1591*3737Shx147065 	PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_CMD);
1592*3737Shx147065 	if (stat & WL_STAT_CMD_RESULT) { /* err in feedback status */
1593*3737Shx147065 		cmn_err(CE_WARN, "pcwl: set_cmd %x,%x failed %x\n",
1594*3737Shx147065 		    cmd, param, stat);
1595*3737Shx147065 		return (PCWL_FAILURE_CMD);
1596*3737Shx147065 	}
1597*3737Shx147065 	if (cmd == WL_CMD_ENABLE)
1598*3737Shx147065 		pcwl_p->pcwl_flag |= PCWL_ENABLED;
1599*3737Shx147065 	if (cmd == WL_CMD_DISABLE)
1600*3737Shx147065 		pcwl_p->pcwl_flag &= (~PCWL_ENABLED);
1601*3737Shx147065 	return (PCWL_SUCCESS);
1602*3737Shx147065 }
1603*3737Shx147065 
1604*3737Shx147065 static uint16_t
1605*3737Shx147065 pcwl_set_ch(pcwl_maci_t *pcwl_p, uint16_t type, uint16_t off, uint16_t channel)
1606*3737Shx147065 {
1607*3737Shx147065 	int i;
1608*3737Shx147065 	uint16_t stat, select, offset;
1609*3737Shx147065 
1610*3737Shx147065 	if (channel) {
1611*3737Shx147065 		select = WL_SEL1;
1612*3737Shx147065 		offset = WL_OFF1;
1613*3737Shx147065 	} else {
1614*3737Shx147065 		select = WL_SEL0;
1615*3737Shx147065 		offset = WL_OFF0;
1616*3737Shx147065 	}
1617*3737Shx147065 	PCWL_WRITE(pcwl_p, select, type);
1618*3737Shx147065 	PCWL_WRITE(pcwl_p, offset, off);
1619*3737Shx147065 	for (stat = 0, i = 0; i < WL_TIMEOUT; i++) {
1620*3737Shx147065 		PCWL_READ(pcwl_p, offset, stat);
1621*3737Shx147065 		if (!(stat & (WL_OFF_BUSY|WL_OFF_ERR)))
1622*3737Shx147065 			break;
1623*3737Shx147065 		else {
1624*3737Shx147065 			drv_usecwait(1);
1625*3737Shx147065 		}
1626*3737Shx147065 	}
1627*3737Shx147065 	if (i == WL_TIMEOUT) {
1628*3737Shx147065 		cmn_err(CE_WARN, "set_ch%d %x,%x failed %x\n",
1629*3737Shx147065 		    channel, type, off, stat);
1630*3737Shx147065 		return (PCWL_TIMEDOUT_TARGET);
1631*3737Shx147065 	}
1632*3737Shx147065 	return (PCWL_SUCCESS);
1633*3737Shx147065 }
1634*3737Shx147065 
1635*3737Shx147065 static uint16_t
1636*3737Shx147065 pcwl_get_ltv(pcwl_maci_t *pcwl_p, uint16_t len, uint16_t type, uint16_t *val_p)
1637*3737Shx147065 {
1638*3737Shx147065 	uint16_t stat;
1639*3737Shx147065 
1640*3737Shx147065 	ASSERT(!(len & 1));
1641*3737Shx147065 	len >>= 1;	/* convert bytes to 16-bit words */
1642*3737Shx147065 
1643*3737Shx147065 	/*
1644*3737Shx147065 	 * 1. select read mode
1645*3737Shx147065 	 */
1646*3737Shx147065 	if (stat = pcwl_set_cmd(pcwl_p, WL_CMD_ACCESS | WL_ACCESS_READ, type))
1647*3737Shx147065 		return (stat);
1648*3737Shx147065 
1649*3737Shx147065 	/*
1650*3737Shx147065 	 * 2. select Buffer Access Path (channel) 1 for PIO
1651*3737Shx147065 	 */
1652*3737Shx147065 	if (stat = pcwl_set_ch(pcwl_p, type, 0, 1))
1653*3737Shx147065 		return (stat);
1654*3737Shx147065 
1655*3737Shx147065 	/*
1656*3737Shx147065 	 * 3. read length
1657*3737Shx147065 	 */
1658*3737Shx147065 	PCWL_READ(pcwl_p, WL_DATA1, stat);
1659*3737Shx147065 	if (stat != (len + 1)) {
1660*3737Shx147065 		PCWLDBG((CE_NOTE, "get_ltv 0x%x expected 0x%x+1, got 0x%x\n",
1661*3737Shx147065 		    type, (len + 1) << 1, stat));
1662*3737Shx147065 		stat = (stat >> 1) - 1;
1663*3737Shx147065 		len = MIN(stat, len);
1664*3737Shx147065 	}
1665*3737Shx147065 
1666*3737Shx147065 	/*
1667*3737Shx147065 	 * 4. read type
1668*3737Shx147065 	 */
1669*3737Shx147065 	PCWL_READ(pcwl_p, WL_DATA1, stat);
1670*3737Shx147065 	if (stat != type)
1671*3737Shx147065 		return (PCWL_BADTYPE);
1672*3737Shx147065 
1673*3737Shx147065 	/*
1674*3737Shx147065 	 * 5. read value
1675*3737Shx147065 	 */
1676*3737Shx147065 	for (stat = 0; stat < len; stat++, val_p++) {
1677*3737Shx147065 		PCWL_READ_P(pcwl_p, WL_DATA1, val_p, 1);
1678*3737Shx147065 	}
1679*3737Shx147065 	return (PCWL_SUCCESS);
1680*3737Shx147065 }
1681*3737Shx147065 
1682*3737Shx147065 static uint16_t
1683*3737Shx147065 pcwl_fil_ltv(pcwl_maci_t *pcwl_p, uint16_t len, uint16_t type, uint16_t val)
1684*3737Shx147065 {
1685*3737Shx147065 	uint16_t stat;
1686*3737Shx147065 
1687*3737Shx147065 	ASSERT(!(len & 1));
1688*3737Shx147065 
1689*3737Shx147065 	/*
1690*3737Shx147065 	 * 1. select Buffer Access Path (channel) 1 for PIO
1691*3737Shx147065 	 */
1692*3737Shx147065 	if (stat = pcwl_set_ch(pcwl_p, type, 0, 1))
1693*3737Shx147065 		return (stat);
1694*3737Shx147065 
1695*3737Shx147065 	/*
1696*3737Shx147065 	 * 2. write length
1697*3737Shx147065 	 */
1698*3737Shx147065 	len >>= 1;		/* convert bytes to 16-bit words */
1699*3737Shx147065 	stat = len + 1;		/* 1 extra word */
1700*3737Shx147065 	PCWL_WRITE(pcwl_p, WL_DATA1, stat);
1701*3737Shx147065 
1702*3737Shx147065 	/*
1703*3737Shx147065 	 * 3. write type
1704*3737Shx147065 	 */
1705*3737Shx147065 	PCWL_WRITE(pcwl_p, WL_DATA1, type);
1706*3737Shx147065 
1707*3737Shx147065 	/*
1708*3737Shx147065 	 * 4. fill value
1709*3737Shx147065 	 */
1710*3737Shx147065 	for (stat = 0; stat < len; stat++) {
1711*3737Shx147065 		PCWL_WRITE(pcwl_p, WL_DATA1, val);
1712*3737Shx147065 	}
1713*3737Shx147065 
1714*3737Shx147065 	/*
1715*3737Shx147065 	 * 5. select write mode
1716*3737Shx147065 	 */
1717*3737Shx147065 	return (pcwl_set_cmd(pcwl_p, WL_CMD_ACCESS|WL_ACCESS_WRITE, type));
1718*3737Shx147065 }
1719*3737Shx147065 
1720*3737Shx147065 static uint16_t
1721*3737Shx147065 pcwl_put_ltv(pcwl_maci_t *pcwl_p, uint16_t len, uint16_t type, uint16_t *val_p)
1722*3737Shx147065 {
1723*3737Shx147065 	uint16_t stat;
1724*3737Shx147065 
1725*3737Shx147065 	ASSERT(!(len & 1));
1726*3737Shx147065 
1727*3737Shx147065 	/*
1728*3737Shx147065 	 * 1. select Buffer Access Path (channel) 1 for PIO
1729*3737Shx147065 	 */
1730*3737Shx147065 	if (stat = pcwl_set_ch(pcwl_p, type, 0, 1))
1731*3737Shx147065 		return (stat);
1732*3737Shx147065 
1733*3737Shx147065 	/*
1734*3737Shx147065 	 * 2. write length
1735*3737Shx147065 	 */
1736*3737Shx147065 	len >>= 1;		/* convert bytes to 16-bit words */
1737*3737Shx147065 	stat = len + 1;		/* 1 extra word */
1738*3737Shx147065 	PCWL_WRITE(pcwl_p, WL_DATA1, stat);
1739*3737Shx147065 
1740*3737Shx147065 	/*
1741*3737Shx147065 	 * 3. write type
1742*3737Shx147065 	 */
1743*3737Shx147065 	PCWL_WRITE(pcwl_p, WL_DATA1, type);
1744*3737Shx147065 
1745*3737Shx147065 	/*
1746*3737Shx147065 	 * 4. write value
1747*3737Shx147065 	 */
1748*3737Shx147065 	for (stat = 0; stat < len; stat++, val_p++) {
1749*3737Shx147065 		PCWL_WRITE_P(pcwl_p, WL_DATA1, val_p, 1);
1750*3737Shx147065 	}
1751*3737Shx147065 
1752*3737Shx147065 	/*
1753*3737Shx147065 	 * 5. select write mode
1754*3737Shx147065 	 */
1755*3737Shx147065 	return (pcwl_set_cmd(pcwl_p, WL_CMD_ACCESS|WL_ACCESS_WRITE, type));
1756*3737Shx147065 }
1757*3737Shx147065 
1758*3737Shx147065 #define	PCWL_COMPSTR_LEN	34
1759*3737Shx147065 static uint16_t
1760*3737Shx147065 pcwl_put_str(pcwl_maci_t *pcwl_p, uint16_t type, char *str_p)
1761*3737Shx147065 {
1762*3737Shx147065 	uint16_t buf[PCWL_COMPSTR_LEN / 2];
1763*3737Shx147065 	uint8_t str_len = strlen(str_p);
1764*3737Shx147065 
1765*3737Shx147065 	bzero(buf, PCWL_COMPSTR_LEN);
1766*3737Shx147065 	buf[0] = str_len;
1767*3737Shx147065 	bcopy(str_p, (caddr_t)(buf + 1), str_len);
1768*3737Shx147065 	PCWLDBG((CE_NOTE, "put_str: buf[0]=%x buf=%s\n",
1769*3737Shx147065 	    buf[0], (caddr_t)(buf + 1)));
1770*3737Shx147065 	PCWL_SWAP16(buf + 1, PCWL_COMPSTR_LEN - 2);
1771*3737Shx147065 	return (pcwl_put_ltv(pcwl_p, PCWL_COMPSTR_LEN, type, buf));
1772*3737Shx147065 }
1773*3737Shx147065 
1774*3737Shx147065 /*ARGSUSED*/
1775*3737Shx147065 static uint16_t
1776*3737Shx147065 pcwl_rdch0(pcwl_maci_t *pcwl_p, uint16_t type, uint16_t off, uint16_t *buf_p,
1777*3737Shx147065 	int len, int order)
1778*3737Shx147065 {
1779*3737Shx147065 	uint16_t o;
1780*3737Shx147065 	ASSERT(!(len & 1));
1781*3737Shx147065 	/*
1782*3737Shx147065 	 * It seems that for PrismII chip, frequently overlap use of path0
1783*3737Shx147065 	 * and path1 may hang the hardware. So for PrismII chip, just use
1784*3737Shx147065 	 * path1. Test proves this workaround is OK.
1785*3737Shx147065 	 */
1786*3737Shx147065 	if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) {
1787*3737Shx147065 		if (type = pcwl_set_ch(pcwl_p, type, off, 1))
1788*3737Shx147065 			return (type);
1789*3737Shx147065 		o = WL_DATA1;
1790*3737Shx147065 	} else {
1791*3737Shx147065 		if (type = pcwl_set_ch(pcwl_p, type, off, 0))
1792*3737Shx147065 			return (type);
1793*3737Shx147065 		o = WL_DATA0;
1794*3737Shx147065 	}
1795*3737Shx147065 	len >>= 1;
1796*3737Shx147065 	for (off = 0; off < len; off++, buf_p++) {
1797*3737Shx147065 		PCWL_READ_P(pcwl_p, o, buf_p, order);
1798*3737Shx147065 	}
1799*3737Shx147065 	return (PCWL_SUCCESS);
1800*3737Shx147065 }
1801*3737Shx147065 
1802*3737Shx147065 /*ARGSUSED*/
1803*3737Shx147065 static uint16_t
1804*3737Shx147065 pcwl_wrch1(pcwl_maci_t *pcwl_p, uint16_t type, uint16_t off, uint16_t *buf_p,
1805*3737Shx147065 	int len, int order)
1806*3737Shx147065 {
1807*3737Shx147065 	ASSERT(!(len & 1));
1808*3737Shx147065 	if (type = pcwl_set_ch(pcwl_p, type, off, 1))
1809*3737Shx147065 		return (type);
1810*3737Shx147065 	len >>= 1;
1811*3737Shx147065 	for (off = 0; off < len; off++, buf_p++) {
1812*3737Shx147065 		PCWL_WRITE_P(pcwl_p, WL_DATA1, buf_p, order);
1813*3737Shx147065 	}
1814*3737Shx147065 	return (PCWL_SUCCESS);
1815*3737Shx147065 }
1816*3737Shx147065 
1817*3737Shx147065 static uint16_t
1818*3737Shx147065 pcwl_alloc_nicmem(pcwl_maci_t *pcwl_p, uint16_t len, uint16_t *id_p)
1819*3737Shx147065 {
1820*3737Shx147065 	int i;
1821*3737Shx147065 	uint16_t stat;
1822*3737Shx147065 
1823*3737Shx147065 	len = ((len + 1) >> 1) << 1;	/* round up to 16-bit boundary */
1824*3737Shx147065 
1825*3737Shx147065 	if (stat = pcwl_set_cmd(pcwl_p, WL_CMD_ALLOC_MEM, len))
1826*3737Shx147065 		return (stat);
1827*3737Shx147065 	for (stat = 0, i = 0; i < WL_TIMEOUT; i++) {
1828*3737Shx147065 		PCWL_READ(pcwl_p, WL_EVENT_STAT, stat);
1829*3737Shx147065 		if (stat & WL_EV_ALLOC)
1830*3737Shx147065 			break;
1831*3737Shx147065 		else
1832*3737Shx147065 			drv_usecwait(1);
1833*3737Shx147065 	}
1834*3737Shx147065 	if (i == WL_TIMEOUT)
1835*3737Shx147065 		return (PCWL_TIMEDOUT_ALLOC);
1836*3737Shx147065 	PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_ALLOC);
1837*3737Shx147065 	PCWL_READ(pcwl_p, WL_ALLOC_FID, stat);
1838*3737Shx147065 	*id_p = stat;
1839*3737Shx147065 
1840*3737Shx147065 	/*
1841*3737Shx147065 	 * zero fill the allocated NIC mem - sort of pcwl_fill_ch
1842*3737Shx147065 	 */
1843*3737Shx147065 	(void) pcwl_set_ch(pcwl_p, stat, 0, 1);
1844*3737Shx147065 
1845*3737Shx147065 	for (len >>= 1, stat = 0; stat < len; stat++) {
1846*3737Shx147065 		PCWL_WRITE(pcwl_p, WL_DATA1, 0);
1847*3737Shx147065 	}
1848*3737Shx147065 	return (PCWL_SUCCESS);
1849*3737Shx147065 }
1850*3737Shx147065 
1851*3737Shx147065 static int
1852*3737Shx147065 pcwl_add_scan_item(pcwl_maci_t *pcwl_p, wl_scan_result_t s)
1853*3737Shx147065 {
1854*3737Shx147065 	wl_scan_list_t *scan_item;
1855*3737Shx147065 
1856*3737Shx147065 	scan_item = kmem_zalloc(sizeof (wl_scan_list_t), KM_SLEEP);
1857*3737Shx147065 	if (scan_item == NULL) {
1858*3737Shx147065 		cmn_err(CE_WARN, "pcwl add_scan_item: zalloc failed\n");
1859*3737Shx147065 		return (PCWL_FAIL);
1860*3737Shx147065 	}
1861*3737Shx147065 	scan_item->wl_val = s;
1862*3737Shx147065 	scan_item->wl_timeout = WL_SCAN_TIMEOUT_MAX;
1863*3737Shx147065 	list_insert_tail(&pcwl_p->pcwl_scan_list, scan_item);
1864*3737Shx147065 	pcwl_p->pcwl_scan_num++;
1865*3737Shx147065 	return (PCWL_SUCCESS);
1866*3737Shx147065 }
1867*3737Shx147065 
1868*3737Shx147065 static void
1869*3737Shx147065 pcwl_delete_scan_item(pcwl_maci_t *pcwl_p, wl_scan_list_t *s)
1870*3737Shx147065 {
1871*3737Shx147065 	list_remove(&pcwl_p->pcwl_scan_list, s);
1872*3737Shx147065 	kmem_free(s, sizeof (*s));
1873*3737Shx147065 	pcwl_p->pcwl_scan_num--;
1874*3737Shx147065 }
1875*3737Shx147065 
1876*3737Shx147065 static void
1877*3737Shx147065 pcwl_scanlist_timeout(void *arg)
1878*3737Shx147065 {
1879*3737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
1880*3737Shx147065 	wl_scan_list_t *scan_item0, *scan_item1;
1881*3737Shx147065 
1882*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_scanlist_lock);
1883*3737Shx147065 	scan_item0 = list_head(&pcwl_p->pcwl_scan_list);
1884*3737Shx147065 	for (; scan_item0; ) {
1885*3737Shx147065 		PCWLDBG((CE_NOTE, "ssid = %s\n",
1886*3737Shx147065 		    scan_item0->wl_val.wl_srt_ssid));
1887*3737Shx147065 		PCWLDBG((CE_NOTE, "timeout left: %ds",
1888*3737Shx147065 		    scan_item0->wl_timeout));
1889*3737Shx147065 		scan_item1 = list_next(&pcwl_p->pcwl_scan_list, scan_item0);
1890*3737Shx147065 		if (scan_item0->wl_timeout == 0) {
1891*3737Shx147065 			pcwl_delete_scan_item(pcwl_p, scan_item0);
1892*3737Shx147065 		} else {
1893*3737Shx147065 			scan_item0->wl_timeout--;
1894*3737Shx147065 		}
1895*3737Shx147065 		scan_item0 = scan_item1;
1896*3737Shx147065 	}
1897*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_scanlist_lock);
1898*3737Shx147065 	pcwl_p->pcwl_scanlist_timeout_id = timeout(pcwl_scanlist_timeout,
1899*3737Shx147065 	    pcwl_p, drv_usectohz(1000000));
1900*3737Shx147065 }
1901*3737Shx147065 
1902*3737Shx147065 static void
1903*3737Shx147065 pcwl_get_rssi(pcwl_maci_t *pcwl_p)
1904*3737Shx147065 {
1905*3737Shx147065 	wl_scan_list_t *scan_item0;
1906*3737Shx147065 	uint16_t cq[3];
1907*3737Shx147065 
1908*3737Shx147065 	bzero(cq, sizeof (cq));
1909*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_scanlist_lock);
1910*3737Shx147065 	scan_item0 = list_head(&pcwl_p->pcwl_scan_list);
1911*3737Shx147065 	for (; scan_item0; ) {
1912*3737Shx147065 		if (bcmp(scan_item0->wl_val.wl_srt_bssid,
1913*3737Shx147065 		    pcwl_p->pcwl_bssid, 6) == 0) {
1914*3737Shx147065 			pcwl_p->pcwl_rssi = scan_item0->wl_val.wl_srt_sl;
1915*3737Shx147065 		}
1916*3737Shx147065 		scan_item0 = list_next(&pcwl_p->pcwl_scan_list, scan_item0);
1917*3737Shx147065 	}
1918*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_scanlist_lock);
1919*3737Shx147065 	if (!pcwl_p->pcwl_rssi) {
1920*3737Shx147065 		(void) pcwl_get_ltv(pcwl_p, 6, WL_RID_COMMQUAL, cq);
1921*3737Shx147065 		pcwl_p->pcwl_rssi = cq[1];
1922*3737Shx147065 	}
1923*3737Shx147065 }
1924*3737Shx147065 
1925*3737Shx147065 /*
1926*3737Shx147065  * Note:
1927*3737Shx147065  * PrismII chipset has 2 extra space for the reason why scan is initiated
1928*3737Shx147065  */
1929*3737Shx147065 static void
1930*3737Shx147065 pcwl_ssid_scan(pcwl_maci_t *pcwl_p, uint16_t fid, uint16_t flen, uint16_t stype)
1931*3737Shx147065 {
1932*3737Shx147065 	uint16_t stat;
1933*3737Shx147065 	uint16_t ssidNum, i;
1934*3737Shx147065 	uint16_t off, szbuf;
1935*3737Shx147065 	uint16_t tmp[2];
1936*3737Shx147065 	wl_scan_list_t *scan_item0;
1937*3737Shx147065 	uint32_t check_num;
1938*3737Shx147065 	uint8_t	bssid_t[6];
1939*3737Shx147065 
1940*3737Shx147065 	wl_scan_result_t sctbl;
1941*3737Shx147065 
1942*3737Shx147065 	off = sizeof (uint16_t) * 2;
1943*3737Shx147065 	switch (pcwl_p->pcwl_chip_type) {
1944*3737Shx147065 	case PCWL_CHIP_PRISMII:
1945*3737Shx147065 		(void) RDCH0(pcwl_p, fid, off, tmp, 4);
1946*3737Shx147065 		off += 4;
1947*3737Shx147065 		szbuf = (stype == WL_INFO_SCAN_RESULTS ? 31 : 32);
1948*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl ssid_scan: PRISM chip\n"));
1949*3737Shx147065 		break;
1950*3737Shx147065 	case PCWL_CHIP_LUCENT:
1951*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl ssid_scan LUCENT chip\n"));
1952*3737Shx147065 	default:
1953*3737Shx147065 		szbuf = 25;
1954*3737Shx147065 	}
1955*3737Shx147065 
1956*3737Shx147065 	flen = flen + 1 - (off >> 1);
1957*3737Shx147065 	ssidNum = flen/szbuf;
1958*3737Shx147065 	ssidNum = min(WL_SRT_MAX_NUM, ssidNum);
1959*3737Shx147065 
1960*3737Shx147065 	PCWLDBG((CE_NOTE, "pcwl: ssid_scan frame length = %d\n", flen));
1961*3737Shx147065 
1962*3737Shx147065 	PCWLDBG((CE_NOTE, "pcwl ssid_scan: %d ssid(s) available", ssidNum));
1963*3737Shx147065 
1964*3737Shx147065 	bzero(bssid_t, sizeof (bssid_t));
1965*3737Shx147065 	for (i = 0; i < ssidNum; i++) {
1966*3737Shx147065 		(void) RDCH0(pcwl_p, fid, off, (uint16_t *)&sctbl, 2*szbuf);
1967*3737Shx147065 
1968*3737Shx147065 #ifdef DEBUG
1969*3737Shx147065 		if (pcwl_debug & PCWL_DBG_INFO) {
1970*3737Shx147065 			int j;
1971*3737Shx147065 			for (j = 0; j < sizeof (sctbl); j++)
1972*3737Shx147065 				cmn_err(CE_NOTE, "%d: %x\n", j,
1973*3737Shx147065 				    *((uint8_t *)&sctbl + j));
1974*3737Shx147065 		}
1975*3737Shx147065 #endif
1976*3737Shx147065 
1977*3737Shx147065 		off += (szbuf << 1);
1978*3737Shx147065 		stat = min(sctbl.wl_srt_ssidlen, 31);
1979*3737Shx147065 		PCWL_SWAP16((uint16_t *)(sctbl.wl_srt_bssid), 6);
1980*3737Shx147065 		PCWL_SWAP16((uint16_t *)(sctbl.wl_srt_ssid), stat);
1981*3737Shx147065 		sctbl.wl_srt_ssid[stat] = '\0';
1982*3737Shx147065 		sctbl.wl_srt_sl &= 0x7f;
1983*3737Shx147065 
1984*3737Shx147065 		/*
1985*3737Shx147065 		 * sometimes, those empty items are recorded by hardware,
1986*3737Shx147065 		 * this is wrong, just ignore those items here.
1987*3737Shx147065 		 */
1988*3737Shx147065 		if (bcmp(sctbl.wl_srt_bssid,
1989*3737Shx147065 		    bssid_t, 6) == 0) {
1990*3737Shx147065 			continue;
1991*3737Shx147065 		}
1992*3737Shx147065 		if (bcmp(sctbl.wl_srt_bssid,
1993*3737Shx147065 		    pcwl_p->pcwl_bssid, 6) == 0) {
1994*3737Shx147065 			pcwl_p->pcwl_rssi = sctbl.wl_srt_sl;
1995*3737Shx147065 		}
1996*3737Shx147065 		/*
1997*3737Shx147065 		 * save/update the scan item in scanlist
1998*3737Shx147065 		 */
1999*3737Shx147065 		mutex_enter(&pcwl_p->pcwl_scanlist_lock);
2000*3737Shx147065 		check_num = 0;
2001*3737Shx147065 		scan_item0 = list_head(&pcwl_p->pcwl_scan_list);
2002*3737Shx147065 		if (scan_item0 == NULL) {
2003*3737Shx147065 			if (pcwl_add_scan_item(pcwl_p, sctbl)
2004*3737Shx147065 			    != 0) {
2005*3737Shx147065 				mutex_exit(&pcwl_p->pcwl_scanlist_lock);
2006*3737Shx147065 				return;
2007*3737Shx147065 			}
2008*3737Shx147065 		}
2009*3737Shx147065 		for (; scan_item0; ) {
2010*3737Shx147065 			if (bcmp(sctbl.wl_srt_bssid,
2011*3737Shx147065 			    scan_item0->wl_val.wl_srt_bssid, 6) == 0) {
2012*3737Shx147065 				scan_item0->wl_val = sctbl;
2013*3737Shx147065 				scan_item0->wl_timeout = WL_SCAN_TIMEOUT_MAX;
2014*3737Shx147065 				break;
2015*3737Shx147065 			} else {
2016*3737Shx147065 				check_num++;
2017*3737Shx147065 			}
2018*3737Shx147065 			scan_item0 = list_next(&pcwl_p->pcwl_scan_list,
2019*3737Shx147065 			    scan_item0);
2020*3737Shx147065 		}
2021*3737Shx147065 		if (check_num == pcwl_p->pcwl_scan_num) {
2022*3737Shx147065 			if (pcwl_add_scan_item(pcwl_p, sctbl)
2023*3737Shx147065 			    != 0) {
2024*3737Shx147065 				mutex_exit(&pcwl_p->pcwl_scanlist_lock);
2025*3737Shx147065 				return;
2026*3737Shx147065 			}
2027*3737Shx147065 		}
2028*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_scanlist_lock);
2029*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl ssid_scan: ssid%d = %s\n", i+1,
2030*3737Shx147065 		    sctbl.wl_srt_ssid));
2031*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl ssid_scan: channel = %d\n",
2032*3737Shx147065 		    sctbl.wl_srt_chid));
2033*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl ssid_scan: signal level= %d\n",
2034*3737Shx147065 		    sctbl.wl_srt_sl));
2035*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl ssid_scan: noise level = %d\n",
2036*3737Shx147065 		    sctbl.wl_srt_anl));
2037*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl ssid_scan: bssid%d ="
2038*3737Shx147065 		    " %x %x %x %x %x %x\n\n", i+1,
2039*3737Shx147065 		    sctbl.wl_srt_bssid[0],
2040*3737Shx147065 		    sctbl.wl_srt_bssid[1],
2041*3737Shx147065 		    sctbl.wl_srt_bssid[2],
2042*3737Shx147065 		    sctbl.wl_srt_bssid[3],
2043*3737Shx147065 		    sctbl.wl_srt_bssid[4],
2044*3737Shx147065 		    sctbl.wl_srt_bssid[5]));
2045*3737Shx147065 	}
2046*3737Shx147065 
2047*3737Shx147065 }
2048*3737Shx147065 
2049*3737Shx147065 /*
2050*3737Shx147065  * delay in which the mutex is not hold.
2051*3737Shx147065  * assuming the mutex has already been hold.
2052*3737Shx147065  */
2053*3737Shx147065 static void
2054*3737Shx147065 pcwl_delay(pcwl_maci_t *pcwl_p, clock_t microsecs)
2055*3737Shx147065 {
2056*3737Shx147065 	ASSERT(mutex_owned(&pcwl_p->pcwl_glock));
2057*3737Shx147065 
2058*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
2059*3737Shx147065 	delay(drv_usectohz(microsecs));
2060*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
2061*3737Shx147065 }
2062*3737Shx147065 
2063*3737Shx147065 static int
2064*3737Shx147065 pcwl_reset_backend(pcwl_maci_t *pcwl_p)
2065*3737Shx147065 {
2066*3737Shx147065 	uint16_t ret = 0;
2067*3737Shx147065 
2068*3737Shx147065 	if (ret =  pcwl_set_cmd(pcwl_p, WL_CMD_INI, 0)) {
2069*3737Shx147065 		return ((int)ret);
2070*3737Shx147065 	}
2071*3737Shx147065 
2072*3737Shx147065 	pcwl_delay(pcwl_p, 100000); /* wait .1 sec */
2073*3737Shx147065 
2074*3737Shx147065 	if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_INI, 0)) {
2075*3737Shx147065 		return ((int)ret);
2076*3737Shx147065 	}
2077*3737Shx147065 	pcwl_delay(pcwl_p, 100000); /* wait .1 sec */
2078*3737Shx147065 
2079*3737Shx147065 	PCWL_DISABLE_INTR(pcwl_p);
2080*3737Shx147065 	return (PCWL_SUCCESS);
2081*3737Shx147065 }
2082*3737Shx147065 
2083*3737Shx147065 
2084*3737Shx147065 /*
2085*3737Shx147065  * get card capability (WEP, default channel), setup broadcast, mac addresses
2086*3737Shx147065  */
2087*3737Shx147065 static int
2088*3737Shx147065 pcwl_get_cap(pcwl_maci_t *pcwl_p)
2089*3737Shx147065 {
2090*3737Shx147065 	uint16_t stat, ch_no;
2091*3737Shx147065 	uint16_t buf[ETHERADDRL >> 1];
2092*3737Shx147065 
2093*3737Shx147065 	bzero(buf, ETHERADDRL);
2094*3737Shx147065 	if (stat = pcwl_get_ltv(pcwl_p, 2, WL_RID_OWN_CHNL, &ch_no)) {
2095*3737Shx147065 		cmn_err(CE_CONT, "pcwl get_cap: get def channel failed"
2096*3737Shx147065 		    " %x\n", stat);
2097*3737Shx147065 		return ((int)stat);
2098*3737Shx147065 	}
2099*3737Shx147065 	if (stat = pcwl_get_ltv(pcwl_p, 2, WL_RID_WEP_AVAIL,
2100*3737Shx147065 	    &pcwl_p->pcwl_has_wep)) {
2101*3737Shx147065 		cmn_err(CE_CONT, "pcwl get_cap: get WEP capability failed"
2102*3737Shx147065 		    " %x\n", stat);
2103*3737Shx147065 		return ((int)stat);
2104*3737Shx147065 	}
2105*3737Shx147065 	if (stat = pcwl_get_ltv(pcwl_p, ETHERADDRL, WL_RID_MAC_NODE, buf)) {
2106*3737Shx147065 		cmn_err(CE_CONT, "pcwl get_cap: get macaddr failed"
2107*3737Shx147065 		    " %x\n", stat);
2108*3737Shx147065 		return ((int)stat);
2109*3737Shx147065 	}
2110*3737Shx147065 
2111*3737Shx147065 	/*
2112*3737Shx147065 	 * don't assume m_xxx members are 16-bit aligned
2113*3737Shx147065 	 */
2114*3737Shx147065 	PCWL_SWAP16(buf, ETHERADDRL);
2115*3737Shx147065 	ether_copy(buf, pcwl_p->pcwl_mac_addr);
2116*3737Shx147065 	return (PCWL_SUCCESS);
2117*3737Shx147065 }
2118*3737Shx147065 
2119*3737Shx147065 static int
2120*3737Shx147065 pcwl_init_nicmem(pcwl_maci_t *pcwl_p)
2121*3737Shx147065 {
2122*3737Shx147065 	uint16_t ret, i;
2123*3737Shx147065 	uint16_t rc;
2124*3737Shx147065 
2125*3737Shx147065 	for (i = 0; i < WL_XMT_BUF_NUM; i++) {
2126*3737Shx147065 		ret = pcwl_alloc_nicmem(pcwl_p, PCWL_NICMEM_SZ, &rc);
2127*3737Shx147065 		if (ret) {
2128*3737Shx147065 			cmn_err(CE_WARN,
2129*3737Shx147065 			    "pcwl: alloc NIC Tx buf failed %x\n", ret);
2130*3737Shx147065 			return (PCWL_FAIL);
2131*3737Shx147065 		}
2132*3737Shx147065 		pcwl_p->pcwl_txring.wl_tx_fids[i] = rc;
2133*3737Shx147065 		pcwl_p->pcwl_txring.wl_tx_ring[i] = 0;
2134*3737Shx147065 		PCWLDBG((CE_NOTE, "pcwl: alloc_nicmem_id[%d]=%x\n", i, rc));
2135*3737Shx147065 	}
2136*3737Shx147065 	pcwl_p->pcwl_txring.wl_tx_prod = pcwl_p->pcwl_txring.wl_tx_cons = 0;
2137*3737Shx147065 
2138*3737Shx147065 	ret = pcwl_alloc_nicmem(pcwl_p, PCWL_NICMEM_SZ, &pcwl_p->pcwl_mgmt_id);
2139*3737Shx147065 	if (ret) {
2140*3737Shx147065 		cmn_err(CE_WARN, "pcwl: alloc NIC Mgmt buf failed %x\n", ret);
2141*3737Shx147065 		return (PCWL_FAIL);
2142*3737Shx147065 	}
2143*3737Shx147065 	PCWLDBG((CE_NOTE, "pcwl: alloc_nicmem mgmt_id=%x\n",
2144*3737Shx147065 	    pcwl_p->pcwl_mgmt_id));
2145*3737Shx147065 	return (PCWL_SUCCESS);
2146*3737Shx147065 }
2147*3737Shx147065 
2148*3737Shx147065 static int
2149*3737Shx147065 pcwl_loaddef_rf(pcwl_maci_t *pcwl_p)
2150*3737Shx147065 {
2151*3737Shx147065 	pcwl_p->pcwl_rf.rf_max_datalen = WL_DEFAULT_DATALEN;
2152*3737Shx147065 	pcwl_p->pcwl_rf.rf_create_ibss = WL_DEFAULT_CREATE_IBSS;
2153*3737Shx147065 	pcwl_p->pcwl_rf.rf_porttype = WL_BSS_BSS;
2154*3737Shx147065 	pcwl_p->pcwl_rf.rf_rts_thresh = WL_DEFAULT_RTS_THRESH;
2155*3737Shx147065 	pcwl_p->pcwl_rf.rf_tx_rate = WL_TX_RATE_FIX_11M(pcwl_p);
2156*3737Shx147065 	pcwl_p->pcwl_rf.rf_pm_enabled = WL_DEFAULT_PM_ENABLED;
2157*3737Shx147065 	pcwl_p->pcwl_rf.rf_own_chnl = WL_DEFAULT_CHAN;
2158*3737Shx147065 	(void) strcpy(pcwl_p->pcwl_rf.rf_own_ssid, "");
2159*3737Shx147065 	(void) strcpy(pcwl_p->pcwl_rf.rf_desired_ssid, "");
2160*3737Shx147065 	(void) strcpy(pcwl_p->pcwl_rf.rf_nodename, "");
2161*3737Shx147065 	pcwl_p->pcwl_rf.rf_encryption = WL_NOENCRYPTION;
2162*3737Shx147065 	pcwl_p->pcwl_rf.rf_authtype = WL_OPENSYSTEM;
2163*3737Shx147065 	pcwl_p->pcwl_rf.rf_tx_crypt_key = WL_DEFAULT_TX_CRYPT_KEY;
2164*3737Shx147065 	bzero((pcwl_p->pcwl_rf.rf_ckeys), sizeof (rf_ckey_t) * 4);
2165*3737Shx147065 
2166*3737Shx147065 	pcwl_p->pcwl_rf.rf_promiscuous = 0;
2167*3737Shx147065 
2168*3737Shx147065 	return (pcwl_config_rf(pcwl_p));
2169*3737Shx147065 }
2170*3737Shx147065 
2171*3737Shx147065 static int
2172*3737Shx147065 pcwl_config_rf(pcwl_maci_t *pcwl_p)
2173*3737Shx147065 {
2174*3737Shx147065 	pcwl_rf_t *rf_p = &pcwl_p->pcwl_rf;
2175*3737Shx147065 	uint16_t create_ibss, porttype;
2176*3737Shx147065 
2177*3737Shx147065 	/*
2178*3737Shx147065 	 * Lucent card:
2179*3737Shx147065 	 * 0 Join ESS or IBSS; 1 Join ESS or join/create IBSS
2180*3737Shx147065 	 * PrismII card:
2181*3737Shx147065 	 * 3 Join ESS or IBSS(do not create IBSS);
2182*3737Shx147065 	 * 1 Join ESS or join/create IBSS
2183*3737Shx147065 	 */
2184*3737Shx147065 	create_ibss = rf_p->rf_create_ibss;
2185*3737Shx147065 	if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) {
2186*3737Shx147065 		if (rf_p->rf_create_ibss == 0)
2187*3737Shx147065 			create_ibss = 3;
2188*3737Shx147065 	}
2189*3737Shx147065 	/*
2190*3737Shx147065 	 * Lucent card:
2191*3737Shx147065 	 * 1 BSS; 3 pseudo IBSS(only for test,not the 802.11 IBSS)
2192*3737Shx147065 	 * so porttype register should always be set to 1
2193*3737Shx147065 	 * PrismII card:
2194*3737Shx147065 	 * 0 IBSS; 1 BSS; 2 WDS; 3 pseudo IBSS; 6 hostAP
2195*3737Shx147065 	 */
2196*3737Shx147065 	switch (pcwl_p->pcwl_chip_type) {
2197*3737Shx147065 	case PCWL_CHIP_PRISMII:
2198*3737Shx147065 		if (rf_p->rf_porttype == WL_BSS_BSS)
2199*3737Shx147065 			porttype = 1;
2200*3737Shx147065 		else if (rf_p->rf_porttype == WL_BSS_IBSS)
2201*3737Shx147065 			porttype = 0;
2202*3737Shx147065 		else
2203*3737Shx147065 			porttype = 0;
2204*3737Shx147065 		break;
2205*3737Shx147065 	case PCWL_CHIP_LUCENT:
2206*3737Shx147065 	default:
2207*3737Shx147065 		porttype = 1;
2208*3737Shx147065 	}
2209*3737Shx147065 
2210*3737Shx147065 
2211*3737Shx147065 	FIL_LTV(pcwl_p, PCWL_MCBUF_LEN, WL_RID_MCAST, 0);
2212*3737Shx147065 	FIL_LTV(pcwl_p, 2,	WL_RID_PROMISC,		0);
2213*3737Shx147065 	FIL_LTV(pcwl_p, 2,	WL_RID_TICK_TIME,	0);
2214*3737Shx147065 
2215*3737Shx147065 	FIL_LTV(pcwl_p, 2, WL_RID_MAX_DATALEN, rf_p->rf_max_datalen);
2216*3737Shx147065 	FIL_LTV(pcwl_p, 2, WL_RID_CREATE_IBSS, create_ibss);
2217*3737Shx147065 	FIL_LTV(pcwl_p, 2, WL_RID_PORTTYPE, porttype);
2218*3737Shx147065 	FIL_LTV(pcwl_p, 2, WL_RID_RTS_THRESH, rf_p->rf_rts_thresh);
2219*3737Shx147065 	FIL_LTV(pcwl_p, 2, WL_RID_TX_RATE, rf_p->rf_tx_rate);
2220*3737Shx147065 	FIL_LTV(pcwl_p, 2, WL_RID_SYSTEM_SCALE, rf_p->rf_system_scale);
2221*3737Shx147065 	FIL_LTV(pcwl_p, 2, WL_RID_PM_ENABLED, rf_p->rf_pm_enabled);
2222*3737Shx147065 	FIL_LTV(pcwl_p, 2, WL_RID_MAX_SLEEP, rf_p->rf_max_sleep);
2223*3737Shx147065 	FIL_LTV(pcwl_p, 2, WL_RID_OWN_CHNL, rf_p->rf_own_chnl);
2224*3737Shx147065 
2225*3737Shx147065 	PUT_STR(pcwl_p, WL_RID_OWN_SSID, rf_p->rf_own_ssid);
2226*3737Shx147065 	PUT_STR(pcwl_p, WL_RID_DESIRED_SSID, rf_p->rf_desired_ssid);
2227*3737Shx147065 	PUT_STR(pcwl_p, WL_RID_NODENAME, rf_p->rf_nodename);
2228*3737Shx147065 
2229*3737Shx147065 	if (!pcwl_p->pcwl_has_wep)
2230*3737Shx147065 		goto done;
2231*3737Shx147065 
2232*3737Shx147065 	switch (pcwl_p->pcwl_chip_type) {
2233*3737Shx147065 	case PCWL_CHIP_PRISMII: {
2234*3737Shx147065 		int i;
2235*3737Shx147065 
2236*3737Shx147065 		for (i = 0; i < 4; i++) {
2237*3737Shx147065 			int k_len = strlen((char *)rf_p->rf_ckeys[i].ckey_dat);
2238*3737Shx147065 			if (k_len == 0)
2239*3737Shx147065 				continue;
2240*3737Shx147065 			k_len = k_len > 5 ? 14 : 6;
2241*3737Shx147065 			PUT_LTV(pcwl_p, k_len, WL_RID_CRYPT_KEY0_P2 + i,
2242*3737Shx147065 			    (uint16_t *)&rf_p->rf_ckeys[i].ckey_dat);
2243*3737Shx147065 		}
2244*3737Shx147065 		FIL_LTV(pcwl_p, 2, WL_RID_TX_CRYPT_KEY_P2,
2245*3737Shx147065 		    rf_p->rf_tx_crypt_key);
2246*3737Shx147065 		FIL_LTV(pcwl_p, 2, WL_RID_AUTHTYPE_P2,
2247*3737Shx147065 		    rf_p->rf_authtype);
2248*3737Shx147065 		FIL_LTV(pcwl_p, 2, WL_RID_ENCRYPTION_P2,
2249*3737Shx147065 		    rf_p->rf_encryption);
2250*3737Shx147065 		if (pcwl_p->pcwl_rf.rf_promiscuous)
2251*3737Shx147065 			FIL_LTV(pcwl_p, 2, WL_RID_PROMISC, 1);
2252*3737Shx147065 		}
2253*3737Shx147065 		break;
2254*3737Shx147065 	case PCWL_CHIP_LUCENT:
2255*3737Shx147065 	default:
2256*3737Shx147065 		FIL_LTV(pcwl_p, 2, WL_RID_ENCRYPTION,
2257*3737Shx147065 		    rf_p->rf_encryption);
2258*3737Shx147065 		FIL_LTV(pcwl_p, 2, WL_RID_AUTHTYPE_L,
2259*3737Shx147065 		    rf_p->rf_authtype);
2260*3737Shx147065 		FIL_LTV(pcwl_p, 2, WL_RID_TX_CRYPT_KEY,
2261*3737Shx147065 		    rf_p->rf_tx_crypt_key);
2262*3737Shx147065 		PUT_LTV(pcwl_p, sizeof (rf_p->rf_ckeys),
2263*3737Shx147065 		    WL_RID_DEFLT_CRYPT_KEYS,
2264*3737Shx147065 		    (uint16_t *)rf_p->rf_ckeys);
2265*3737Shx147065 		break;
2266*3737Shx147065 	}
2267*3737Shx147065 done:
2268*3737Shx147065 	return (PCWL_SUCCESS);
2269*3737Shx147065 }
2270*3737Shx147065 
2271*3737Shx147065 static void
2272*3737Shx147065 pcwl_start_locked(pcwl_maci_t *pcwl_p)
2273*3737Shx147065 {
2274*3737Shx147065 	pcwl_p->pcwl_flag |= PCWL_CARD_INTREN;
2275*3737Shx147065 	PCWL_ENABLE_INTR(pcwl_p);
2276*3737Shx147065 }
2277*3737Shx147065 
2278*3737Shx147065 static void
2279*3737Shx147065 pcwl_stop_locked(pcwl_maci_t *pcwl_p)
2280*3737Shx147065 {
2281*3737Shx147065 	PCWL_DISABLE_INTR(pcwl_p);
2282*3737Shx147065 	pcwl_p->pcwl_flag &= (~PCWL_CARD_INTREN);
2283*3737Shx147065 	PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX|WL_EV_RX|WL_EV_TX_EXC|
2284*3737Shx147065 	    WL_EV_ALLOC|WL_EV_INFO|WL_EV_INFO_DROP);
2285*3737Shx147065 	PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX|WL_EV_RX|WL_EV_TX_EXC|
2286*3737Shx147065 	    WL_EV_ALLOC| WL_EV_INFO|WL_EV_INFO_DROP);
2287*3737Shx147065 }
2288*3737Shx147065 
2289*3737Shx147065 /*ARGSUSED*/
2290*3737Shx147065 static int
2291*3737Shx147065 pcwl_saddr_locked(pcwl_maci_t *pcwl_p)
2292*3737Shx147065 {
2293*3737Shx147065 	int ret;
2294*3737Shx147065 	uint16_t buf[ETHERADDRL >> 1];
2295*3737Shx147065 
2296*3737Shx147065 	ether_copy(pcwl_p->pcwl_mac_addr, buf);
2297*3737Shx147065 	PCWL_SWAP16(buf, ETHERADDRL);
2298*3737Shx147065 	ret = pcwl_put_ltv(pcwl_p, ETHERADDRL, WL_RID_MAC_NODE, buf);
2299*3737Shx147065 	if (ret) {
2300*3737Shx147065 		cmn_err(CE_WARN, "pcwl set_mac_addr: failed %x\n", ret);
2301*3737Shx147065 		return (PCWL_FAIL);
2302*3737Shx147065 	}
2303*3737Shx147065 	return (PCWL_SUCCESS);
2304*3737Shx147065 }
2305*3737Shx147065 
2306*3737Shx147065 static void
2307*3737Shx147065 pcwl_chip_type(pcwl_maci_t *pcwl_p)
2308*3737Shx147065 {
2309*3737Shx147065 	pcwl_ltv_ver_t ver;
2310*3737Shx147065 	pcwl_ltv_fwver_t f;
2311*3737Shx147065 
2312*3737Shx147065 	bzero(&ver, sizeof (ver));
2313*3737Shx147065 	(void) pcwl_get_ltv(pcwl_p, sizeof (ver),
2314*3737Shx147065 	    WL_RID_CARD_ID, (uint16_t *)&ver);
2315*3737Shx147065 	PCWLDBG((CE_NOTE, "card id:%04x-%04x-%04x-%04x\n",
2316*3737Shx147065 	    ver.wl_compid, ver.wl_variant, ver.wl_major, ver.wl_minor));
2317*3737Shx147065 	if ((ver.wl_compid & 0xf000) != 0x8000)
2318*3737Shx147065 		return;	/* lucent */
2319*3737Shx147065 
2320*3737Shx147065 	pcwl_p->pcwl_chip_type = PCWL_CHIP_PRISMII;
2321*3737Shx147065 	(void) pcwl_get_ltv(pcwl_p, sizeof (ver), WL_RID_COMP_IDENT,
2322*3737Shx147065 	    (uint16_t *)&ver);
2323*3737Shx147065 	PCWLDBG((CE_NOTE, "PRISM-II ver:%04x-%04x-%04x-%04x\n",
2324*3737Shx147065 	    ver.wl_compid, ver.wl_variant, ver.wl_major, ver.wl_minor));
2325*3737Shx147065 
2326*3737Shx147065 	bzero(&f, sizeof (f));
2327*3737Shx147065 	(void) pcwl_get_ltv(pcwl_p, sizeof (f), WL_RID_FWVER, (uint16_t *)&f);
2328*3737Shx147065 	PCWL_SWAP16((uint16_t *)&f, sizeof (f));
2329*3737Shx147065 	PCWLDBG((CE_NOTE, "Firmware Pri:%s 2,3:%s\n",
2330*3737Shx147065 	    (char *)f.pri, (char *)f.st));
2331*3737Shx147065 }
2332*3737Shx147065 
2333*3737Shx147065 /*
2334*3737Shx147065  * for wificonfig and dladm ioctl
2335*3737Shx147065  */
2336*3737Shx147065 
2337*3737Shx147065 static int
2338*3737Shx147065 pcwl_cfg_essid(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
2339*3737Shx147065 {
2340*3737Shx147065 	char ssid[36];
2341*3737Shx147065 	uint16_t ret, i;
2342*3737Shx147065 	uint16_t val;
2343*3737Shx147065 	pcwl_rf_t *rf_p;
2344*3737Shx147065 	char *value;
2345*3737Shx147065 	wldp_t	*infp;
2346*3737Shx147065 	wldp_t *outfp;
2347*3737Shx147065 	char *buf;
2348*3737Shx147065 	int iret;
2349*3737Shx147065 
2350*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
2351*3737Shx147065 	if (buf == NULL) {
2352*3737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
2353*3737Shx147065 		    MAX_BUF_LEN));
2354*3737Shx147065 		return (ENOMEM);
2355*3737Shx147065 	}
2356*3737Shx147065 	outfp = (wldp_t *)buf;
2357*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
2358*3737Shx147065 	infp = (wldp_t *)mp->b_rptr;
2359*3737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
2360*3737Shx147065 
2361*3737Shx147065 	bzero(ssid, sizeof (ssid));
2362*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
2363*3737Shx147065 		ret =  pcwl_get_ltv(pcwl_p, 2,
2364*3737Shx147065 		    WL_RID_PORTSTATUS, &val);
2365*3737Shx147065 		if (ret) {
2366*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
2367*3737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
2368*3737Shx147065 			PCWLDBG((CE_WARN, "cfg_essid_get_error\n"));
2369*3737Shx147065 			goto done;
2370*3737Shx147065 		}
2371*3737Shx147065 		PCWLDBG((CE_NOTE, "PortStatus = %d\n", val));
2372*3737Shx147065 
2373*3737Shx147065 		if (val == WL_PORT_DISABLED || val == WL_PORT_INITIAL) {
2374*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET +
2375*3737Shx147065 			    offsetof(wl_essid_t, wl_essid_essid) +
2376*3737Shx147065 			    mi_strlen(rf_p->rf_desired_ssid);
2377*3737Shx147065 			((wl_essid_t *)(outfp->wldp_buf))->wl_essid_length =
2378*3737Shx147065 			    mi_strlen(rf_p->rf_desired_ssid);
2379*3737Shx147065 			bcopy(rf_p->rf_desired_ssid, buf + WIFI_BUF_OFFSET +
2380*3737Shx147065 			    offsetof(wl_essid_t, wl_essid_essid),
2381*3737Shx147065 			    mi_strlen(rf_p->rf_desired_ssid));
2382*3737Shx147065 		} else if (val == WL_PORT_TO_IBSS ||
2383*3737Shx147065 		    val == WL_PORT_TO_BSS ||
2384*3737Shx147065 		    val == WL_PORT_OOR) {
2385*3737Shx147065 			(void) pcwl_get_ltv((pcwl_p), 34,
2386*3737Shx147065 			    WL_RID_SSID, (uint16_t *)ssid);
2387*3737Shx147065 			PCWL_SWAP16((uint16_t *)(ssid+2), *(uint16_t *)ssid);
2388*3737Shx147065 			ssid[*(uint16_t *)ssid + 2] = '\0';
2389*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET +
2390*3737Shx147065 			    offsetof(wl_essid_t, wl_essid_essid) +
2391*3737Shx147065 			    mi_strlen(ssid+2);
2392*3737Shx147065 			((wl_essid_t *)(outfp->wldp_buf))->wl_essid_length =
2393*3737Shx147065 			    mi_strlen(ssid+2);
2394*3737Shx147065 			bcopy(ssid + 2, buf + WIFI_BUF_OFFSET +
2395*3737Shx147065 			    offsetof(wl_essid_t, wl_essid_essid),
2396*3737Shx147065 			    mi_strlen(ssid+2));
2397*3737Shx147065 		} else {
2398*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
2399*3737Shx147065 		}
2400*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
2401*3737Shx147065 		PCWLDBG((CE_CONT, "outfp->length=%d\n", outfp->wldp_length));
2402*3737Shx147065 		PCWLDBG((CE_CONT, "pcwl: get desired essid=%s\n",
2403*3737Shx147065 		    rf_p->rf_desired_ssid));
2404*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
2405*3737Shx147065 		value = ((wl_essid_t *)(infp->wldp_buf))->wl_essid_essid;
2406*3737Shx147065 		(void) strncpy(rf_p->rf_desired_ssid, value,
2407*3737Shx147065 		    MIN(32, strlen(value)));
2408*3737Shx147065 		rf_p->rf_desired_ssid[strlen(value)] = '\0';
2409*3737Shx147065 		(void) strncpy(rf_p->rf_own_ssid, value,
2410*3737Shx147065 		    MIN(32, strlen(value)));
2411*3737Shx147065 		rf_p->rf_own_ssid[strlen(value)] = '\0';
2412*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
2413*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
2414*3737Shx147065 		PCWLDBG((CE_CONT, "pcwl: set: desired essid=%s\n",
2415*3737Shx147065 		    rf_p->rf_desired_ssid));
2416*3737Shx147065 	} else {
2417*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
2418*3737Shx147065 		return (EINVAL);
2419*3737Shx147065 	}
2420*3737Shx147065 done:
2421*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
2422*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
2423*3737Shx147065 	iret = (int)(outfp->wldp_result);
2424*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
2425*3737Shx147065 	return (iret);
2426*3737Shx147065 }
2427*3737Shx147065 
2428*3737Shx147065 static int
2429*3737Shx147065 pcwl_cfg_bssid(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
2430*3737Shx147065 {
2431*3737Shx147065 	uint16_t ret, i;
2432*3737Shx147065 	int iret;
2433*3737Shx147065 	wldp_t *outfp;
2434*3737Shx147065 	char *buf;
2435*3737Shx147065 	uint8_t bssid[6];
2436*3737Shx147065 
2437*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
2438*3737Shx147065 	if (buf == NULL) {
2439*3737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
2440*3737Shx147065 		    MAX_BUF_LEN));
2441*3737Shx147065 		return (ENOMEM);
2442*3737Shx147065 	}
2443*3737Shx147065 	outfp = (wldp_t *)buf;
2444*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
2445*3737Shx147065 
2446*3737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_bssid_t);
2447*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
2448*3737Shx147065 		if (ret = pcwl_get_ltv(pcwl_p, 2,
2449*3737Shx147065 		    WL_RID_PORTSTATUS, &ret)) {
2450*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
2451*3737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
2452*3737Shx147065 			goto done;
2453*3737Shx147065 		}
2454*3737Shx147065 		PCWLDBG((CE_NOTE, "PortStatus = %d\n", ret));
2455*3737Shx147065 		if (ret == WL_PORT_DISABLED || ret == WL_PORT_INITIAL) {
2456*3737Shx147065 			bzero(buf + WIFI_BUF_OFFSET,
2457*3737Shx147065 			    sizeof (wl_bssid_t));
2458*3737Shx147065 		} else if (ret == WL_PORT_TO_IBSS ||
2459*3737Shx147065 			ret == WL_PORT_TO_BSS || ret == WL_PORT_OOR) {
2460*3737Shx147065 			(void) pcwl_get_ltv(pcwl_p, 6,
2461*3737Shx147065 			    WL_RID_BSSID, (uint16_t *)bssid);
2462*3737Shx147065 			PCWL_SWAP16((uint16_t *)bssid, 6);
2463*3737Shx147065 			bcopy(bssid, buf + WIFI_BUF_OFFSET,
2464*3737Shx147065 			    sizeof (wl_bssid_t));
2465*3737Shx147065 		}
2466*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
2467*3737Shx147065 
2468*3737Shx147065 		PCWLDBG((CE_CONT, "pcwl_getset: bssid=%x %x %x %x %x %x\n",
2469*3737Shx147065 		    bssid[0], bssid[1], bssid[2],
2470*3737Shx147065 		    bssid[3], bssid[4], bssid[5]));
2471*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
2472*3737Shx147065 		outfp->wldp_result = WL_READONLY;
2473*3737Shx147065 	} else {
2474*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
2475*3737Shx147065 		return (EINVAL);
2476*3737Shx147065 	}
2477*3737Shx147065 done:
2478*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
2479*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
2480*3737Shx147065 	iret = (int)(outfp->wldp_result);
2481*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
2482*3737Shx147065 	return (iret);
2483*3737Shx147065 }
2484*3737Shx147065 
2485*3737Shx147065 /*ARGSUSED*/
2486*3737Shx147065 static int
2487*3737Shx147065 pcwl_cmd_scan(pcwl_maci_t *pcwl_p)
2488*3737Shx147065 {
2489*3737Shx147065 	uint16_t vall[18], ret = WL_SUCCESS;
2490*3737Shx147065 	pcwl_rf_t *rf_p;
2491*3737Shx147065 	uint32_t enable, i;
2492*3737Shx147065 	size_t	len;
2493*3737Shx147065 
2494*3737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
2495*3737Shx147065 
2496*3737Shx147065 	/*
2497*3737Shx147065 	 * The logic of this funtion is really tricky.
2498*3737Shx147065 	 * Firstly, the chip can only scan in BSS mode, so necessary
2499*3737Shx147065 	 * backup and restore is required before and after the scan
2500*3737Shx147065 	 * command.
2501*3737Shx147065 	 * Secondly, for Lucent chip, Alrealy associated with an AP
2502*3737Shx147065 	 * can only scan the APes on the fixed channel, so we must
2503*3737Shx147065 	 * set the desired_ssid as "" before scan and restore after.
2504*3737Shx147065 	 * Thirdly, scan cmd is effective only when the card is enabled
2505*3737Shx147065 	 * and any 'set' operation(such as set bsstype, ssid)must disable
2506*3737Shx147065 	 * the card first and then enable the card after the 'set'
2507*3737Shx147065 	 */
2508*3737Shx147065 	enable = pcwl_p->pcwl_flag & PCWL_ENABLED;
2509*3737Shx147065 	len = strlen(rf_p->rf_desired_ssid);
2510*3737Shx147065 
2511*3737Shx147065 	if (pcwl_p->pcwl_rf.rf_porttype != WL_BSS_BSS) {
2512*3737Shx147065 		if ((enable) &&
2513*3737Shx147065 		    (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0))) {
2514*3737Shx147065 			ret = (int)WL_HW_ERROR;
2515*3737Shx147065 			goto done;
2516*3737Shx147065 		}
2517*3737Shx147065 		FIL_LTV(pcwl_p, 2, WL_RID_PORTTYPE, WL_BSS_BSS);
2518*3737Shx147065 	}
2519*3737Shx147065 
2520*3737Shx147065 	if ((pcwl_p->pcwl_chip_type == PCWL_CHIP_LUCENT) && (len != 0)) {
2521*3737Shx147065 		if ((enable) &&
2522*3737Shx147065 		    (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0))) {
2523*3737Shx147065 			ret = (int)WL_HW_ERROR;
2524*3737Shx147065 			goto done;
2525*3737Shx147065 		}
2526*3737Shx147065 		PUT_STR(pcwl_p, WL_RID_DESIRED_SSID, "");
2527*3737Shx147065 	}
2528*3737Shx147065 
2529*3737Shx147065 	if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
2530*3737Shx147065 		ret = (int)WL_HW_ERROR;
2531*3737Shx147065 		goto done;
2532*3737Shx147065 	}
2533*3737Shx147065 	pcwl_delay(pcwl_p, 1000000);
2534*3737Shx147065 
2535*3737Shx147065 	switch (pcwl_p->pcwl_chip_type) {
2536*3737Shx147065 	case PCWL_CHIP_PRISMII:
2537*3737Shx147065 		bzero(vall, sizeof (vall));
2538*3737Shx147065 		vall[0] = 0x3fff; /* channel mask */
2539*3737Shx147065 		vall[1] = 0x1; /* tx rate */
2540*3737Shx147065 		for (i = 0; i < WL_MAX_SCAN_TIMES; i++) {
2541*3737Shx147065 			PUT_LTV(pcwl_p, sizeof (vall),
2542*3737Shx147065 			    WL_RID_HSCAN_REQUEST, vall);
2543*3737Shx147065 			pcwl_delay(pcwl_p, 1000000);
2544*3737Shx147065 			if (pcwl_p->pcwl_scan_num >= WL_SCAN_AGAIN_THRESHOLD)
2545*3737Shx147065 				break;
2546*3737Shx147065 		}
2547*3737Shx147065 		PCWLDBG((CE_NOTE, "PRISM chip\n"));
2548*3737Shx147065 		break;
2549*3737Shx147065 
2550*3737Shx147065 	case PCWL_CHIP_LUCENT:
2551*3737Shx147065 		PCWLDBG((CE_NOTE, "LUCENT chip\n"));
2552*3737Shx147065 	default:
2553*3737Shx147065 		for (i = 0; i < WL_MAX_SCAN_TIMES; i++) {
2554*3737Shx147065 			if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_INQUIRE,
2555*3737Shx147065 			    WL_INFO_SCAN_RESULTS)) {
2556*3737Shx147065 				ret = (int)WL_HW_ERROR;
2557*3737Shx147065 				goto done;
2558*3737Shx147065 			}
2559*3737Shx147065 			pcwl_delay(pcwl_p, 500000);
2560*3737Shx147065 			if (pcwl_p->pcwl_scan_num >= WL_SCAN_AGAIN_THRESHOLD)
2561*3737Shx147065 				break;
2562*3737Shx147065 		}
2563*3737Shx147065 		break;
2564*3737Shx147065 	}
2565*3737Shx147065 	if ((pcwl_p->pcwl_rf.rf_porttype != WL_BSS_BSS) ||
2566*3737Shx147065 	    ((pcwl_p->pcwl_chip_type == PCWL_CHIP_LUCENT) && (len != 0))) {
2567*3737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
2568*3737Shx147065 			ret = (int)WL_HW_ERROR;
2569*3737Shx147065 			goto done;
2570*3737Shx147065 		}
2571*3737Shx147065 		if (ret = pcwl_config_rf(pcwl_p)) {
2572*3737Shx147065 			ret = (int)WL_HW_ERROR;
2573*3737Shx147065 			goto done;
2574*3737Shx147065 		}
2575*3737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
2576*3737Shx147065 			ret = (int)WL_HW_ERROR;
2577*3737Shx147065 			goto done;
2578*3737Shx147065 		}
2579*3737Shx147065 
2580*3737Shx147065 		pcwl_delay(pcwl_p, 1000000);
2581*3737Shx147065 	}
2582*3737Shx147065 
2583*3737Shx147065 	if ((!enable) && (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0))) {
2584*3737Shx147065 		ret = (int)WL_HW_ERROR;
2585*3737Shx147065 	}
2586*3737Shx147065 done:
2587*3737Shx147065 	if (ret)
2588*3737Shx147065 		cmn_err(CE_WARN, "pcwl: scan failed due to hareware error");
2589*3737Shx147065 	return (ret);
2590*3737Shx147065 
2591*3737Shx147065 }
2592*3737Shx147065 
2593*3737Shx147065 /*ARGSUSED*/
2594*3737Shx147065 static int
2595*3737Shx147065 pcwl_cfg_scan(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
2596*3737Shx147065 {
2597*3737Shx147065 	wl_ess_conf_t *p_ess_conf;
2598*3737Shx147065 	wldp_t *outfp;
2599*3737Shx147065 	char *buf;
2600*3737Shx147065 	uint16_t i;
2601*3737Shx147065 	wl_scan_list_t *scan_item;
2602*3737Shx147065 
2603*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
2604*3737Shx147065 	if (buf == NULL) {
2605*3737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
2606*3737Shx147065 		    MAX_BUF_LEN));
2607*3737Shx147065 		return (ENOMEM);
2608*3737Shx147065 	}
2609*3737Shx147065 	outfp = (wldp_t *)buf;
2610*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
2611*3737Shx147065 
2612*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_scanlist_lock);
2613*3737Shx147065 	((wl_ess_list_t *)(outfp->wldp_buf))->wl_ess_list_num =
2614*3737Shx147065 	    pcwl_p->pcwl_scan_num;
2615*3737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET +
2616*3737Shx147065 	    offsetof(wl_ess_list_t, wl_ess_list_ess) +
2617*3737Shx147065 	    pcwl_p->pcwl_scan_num * sizeof (wl_ess_conf_t);
2618*3737Shx147065 
2619*3737Shx147065 	scan_item = list_head(&pcwl_p->pcwl_scan_list);
2620*3737Shx147065 	for (i = 0; i < pcwl_p->pcwl_scan_num; i++) {
2621*3737Shx147065 		if (!scan_item)
2622*3737Shx147065 			goto done;
2623*3737Shx147065 		p_ess_conf = (wl_ess_conf_t *)(buf + WIFI_BUF_OFFSET +
2624*3737Shx147065 		    offsetof(wl_ess_list_t, wl_ess_list_ess) +
2625*3737Shx147065 		    i * sizeof (wl_ess_conf_t));
2626*3737Shx147065 		bcopy(scan_item->wl_val.wl_srt_ssid,
2627*3737Shx147065 		    p_ess_conf->wl_ess_conf_essid.wl_essid_essid,
2628*3737Shx147065 		    mi_strlen(scan_item->wl_val.wl_srt_ssid));
2629*3737Shx147065 		bcopy(scan_item->wl_val.wl_srt_bssid,
2630*3737Shx147065 		    p_ess_conf->wl_ess_conf_bssid, 6);
2631*3737Shx147065 		(p_ess_conf->wl_phy_conf).wl_phy_dsss_conf.wl_dsss_subtype
2632*3737Shx147065 		    = WL_DSSS;
2633*3737Shx147065 		p_ess_conf->wl_ess_conf_wepenabled =
2634*3737Shx147065 		    (scan_item->wl_val.wl_srt_cap & 0x10 ?
2635*3737Shx147065 		    WL_ENC_WEP : WL_NOENCRYPTION);
2636*3737Shx147065 		p_ess_conf->wl_ess_conf_bsstype =
2637*3737Shx147065 		    (scan_item->wl_val.wl_srt_cap & 0x1 ?
2638*3737Shx147065 		    WL_BSS_BSS : WL_BSS_IBSS);
2639*3737Shx147065 		p_ess_conf->wl_phy_conf.wl_phy_dsss_conf.wl_dsss_channel =
2640*3737Shx147065 		    scan_item->wl_val.wl_srt_chid;
2641*3737Shx147065 		if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) {
2642*3737Shx147065 			p_ess_conf->wl_ess_conf_sl =
2643*3737Shx147065 			    min(scan_item->wl_val.wl_srt_sl * 15 / 85 + 1,
2644*3737Shx147065 			    15);
2645*3737Shx147065 		} else {
2646*3737Shx147065 			if (scan_item->wl_val.wl_srt_sl <= 27)
2647*3737Shx147065 				p_ess_conf->wl_ess_conf_sl = 1;
2648*3737Shx147065 			else if (scan_item->wl_val.wl_srt_sl > 154)
2649*3737Shx147065 				p_ess_conf->wl_ess_conf_sl = 15;
2650*3737Shx147065 			else
2651*3737Shx147065 				p_ess_conf->wl_ess_conf_sl = min(15,
2652*3737Shx147065 				    ((scan_item->wl_val.wl_srt_sl - 27)
2653*3737Shx147065 				    * 15 / 127));
2654*3737Shx147065 		}
2655*3737Shx147065 		p_ess_conf->wl_supported_rates[0] = WL_RATE_1M;
2656*3737Shx147065 		p_ess_conf->wl_supported_rates[1] = WL_RATE_2M;
2657*3737Shx147065 		p_ess_conf->wl_supported_rates[2] = WL_RATE_5_5M;
2658*3737Shx147065 		p_ess_conf->wl_supported_rates[3] = WL_RATE_11M;
2659*3737Shx147065 		scan_item = list_next(&pcwl_p->pcwl_scan_list, scan_item);
2660*3737Shx147065 	}
2661*3737Shx147065 done:
2662*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_scanlist_lock);
2663*3737Shx147065 	outfp->wldp_result = WL_SUCCESS;
2664*3737Shx147065 
2665*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
2666*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
2667*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
2668*3737Shx147065 	return (WL_SUCCESS);
2669*3737Shx147065 }
2670*3737Shx147065 
2671*3737Shx147065 /*ARGSUSED*/
2672*3737Shx147065 static int
2673*3737Shx147065 pcwl_cfg_linkstatus(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
2674*3737Shx147065 {
2675*3737Shx147065 	wldp_t *outfp;
2676*3737Shx147065 	char *buf;
2677*3737Shx147065 	uint16_t i, ret, val;
2678*3737Shx147065 	int iret;
2679*3737Shx147065 
2680*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
2681*3737Shx147065 	if (buf == NULL) {
2682*3737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
2683*3737Shx147065 		    MAX_BUF_LEN));
2684*3737Shx147065 		return (ENOMEM);
2685*3737Shx147065 	}
2686*3737Shx147065 	outfp = (wldp_t *)buf;
2687*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
2688*3737Shx147065 
2689*3737Shx147065 	ret =  pcwl_get_ltv(pcwl_p, 2,
2690*3737Shx147065 	    WL_RID_PORTSTATUS, &val);
2691*3737Shx147065 	if (ret) {
2692*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
2693*3737Shx147065 		outfp->wldp_result = WL_HW_ERROR;
2694*3737Shx147065 		PCWLDBG((CE_WARN, "cfg_linkstatus_get_error\n"));
2695*3737Shx147065 		goto done;
2696*3737Shx147065 	}
2697*3737Shx147065 	PCWLDBG((CE_NOTE, "PortStatus = %d\n", val));
2698*3737Shx147065 	if (val == WL_PORT_DISABLED || val == WL_PORT_INITIAL) {
2699*3737Shx147065 		*(wl_linkstatus_t *)(outfp->wldp_buf) = WL_NOTCONNECTED;
2700*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET +
2701*3737Shx147065 		    sizeof (wl_linkstatus_t);
2702*3737Shx147065 	} else if (val == WL_PORT_TO_IBSS ||
2703*3737Shx147065 	    val == WL_PORT_TO_BSS || val == WL_PORT_OOR) {
2704*3737Shx147065 		*(wl_linkstatus_t *)(outfp->wldp_buf) = WL_CONNECTED;
2705*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET +
2706*3737Shx147065 		    sizeof (wl_linkstatus_t);
2707*3737Shx147065 	} else {
2708*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
2709*3737Shx147065 	}
2710*3737Shx147065 	outfp->wldp_result = WL_SUCCESS;
2711*3737Shx147065 done:
2712*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
2713*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
2714*3737Shx147065 	iret = (int)(outfp->wldp_result);
2715*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
2716*3737Shx147065 	return (iret);
2717*3737Shx147065 }
2718*3737Shx147065 
2719*3737Shx147065 static int
2720*3737Shx147065 pcwl_cfg_bsstype(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
2721*3737Shx147065 {
2722*3737Shx147065 	uint16_t ret, i;
2723*3737Shx147065 	pcwl_rf_t *rf_p;
2724*3737Shx147065 	wldp_t	*infp;
2725*3737Shx147065 	wldp_t *outfp;
2726*3737Shx147065 	char *buf;
2727*3737Shx147065 	int iret;
2728*3737Shx147065 
2729*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
2730*3737Shx147065 	if (buf == NULL) {
2731*3737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
2732*3737Shx147065 		    MAX_BUF_LEN));
2733*3737Shx147065 		return (ENOMEM);
2734*3737Shx147065 	}
2735*3737Shx147065 	outfp = (wldp_t *)buf;
2736*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
2737*3737Shx147065 	infp = (wldp_t *)mp->b_rptr;
2738*3737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
2739*3737Shx147065 
2740*3737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_bss_type_t);
2741*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
2742*3737Shx147065 		*(wl_bss_type_t *)(outfp->wldp_buf) = rf_p->rf_porttype;
2743*3737Shx147065 		PCWLDBG((CE_CONT, "pcwl_getset: porttype=%d\n",
2744*3737Shx147065 		    rf_p->rf_porttype));
2745*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
2746*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
2747*3737Shx147065 		ret = (uint16_t)(*(wl_bss_type_t *)(infp->wldp_buf));
2748*3737Shx147065 		if ((ret != WL_BSS_BSS) &&
2749*3737Shx147065 		    (ret != WL_BSS_IBSS) &&
2750*3737Shx147065 		    (ret != WL_BSS_ANY)) {
2751*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
2752*3737Shx147065 			outfp->wldp_result = WL_NOTSUPPORTED;
2753*3737Shx147065 			goto done;
2754*3737Shx147065 		}
2755*3737Shx147065 		rf_p->rf_porttype = ret;
2756*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
2757*3737Shx147065 	} else {
2758*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
2759*3737Shx147065 		return (EINVAL);
2760*3737Shx147065 	}
2761*3737Shx147065 done:
2762*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
2763*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
2764*3737Shx147065 	iret = (int)(outfp->wldp_result);
2765*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
2766*3737Shx147065 	return (iret);
2767*3737Shx147065 }
2768*3737Shx147065 
2769*3737Shx147065 static int
2770*3737Shx147065 pcwl_cfg_phy(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
2771*3737Shx147065 {
2772*3737Shx147065 	uint16_t ret, i;
2773*3737Shx147065 	pcwl_rf_t *rf_p;
2774*3737Shx147065 	wldp_t	*infp;
2775*3737Shx147065 	wldp_t *outfp;
2776*3737Shx147065 	char *buf;
2777*3737Shx147065 	int iret;
2778*3737Shx147065 
2779*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
2780*3737Shx147065 	if (buf == NULL) {
2781*3737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
2782*3737Shx147065 		    MAX_BUF_LEN));
2783*3737Shx147065 		return (ENOMEM);
2784*3737Shx147065 	}
2785*3737Shx147065 	outfp = (wldp_t *)buf;
2786*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
2787*3737Shx147065 	infp = (wldp_t *)mp->b_rptr;
2788*3737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
2789*3737Shx147065 
2790*3737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_dsss_t);
2791*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
2792*3737Shx147065 		if (ret = pcwl_get_ltv(pcwl_p, 2,
2793*3737Shx147065 		    WL_RID_CURRENT_CHNL, &ret)) {
2794*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
2795*3737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
2796*3737Shx147065 			goto done;
2797*3737Shx147065 		}
2798*3737Shx147065 		((wl_dsss_t *)(outfp->wldp_buf))->wl_dsss_channel = ret;
2799*3737Shx147065 		PCWLDBG((CE_CONT, "pcwl_getset: channel=%d\n", ret));
2800*3737Shx147065 		((wl_dsss_t *)(outfp->wldp_buf))->wl_dsss_subtype = WL_DSSS;
2801*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
2802*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
2803*3737Shx147065 		ret = (uint16_t)
2804*3737Shx147065 		    (((wl_phy_conf_t *)(infp->wldp_buf))
2805*3737Shx147065 		    ->wl_phy_dsss_conf.wl_dsss_channel);
2806*3737Shx147065 		if (ret < 1 || ret > 14) {
2807*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
2808*3737Shx147065 			outfp->wldp_result = WL_NOTSUPPORTED;
2809*3737Shx147065 			goto done;
2810*3737Shx147065 		}
2811*3737Shx147065 		rf_p->rf_own_chnl = ret;
2812*3737Shx147065 		PCWLDBG((CE_CONT, "pcwl: set channel=%d\n", rf_p->rf_own_chnl));
2813*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
2814*3737Shx147065 	} else {
2815*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
2816*3737Shx147065 		return (EINVAL);
2817*3737Shx147065 	}
2818*3737Shx147065 done:
2819*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
2820*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
2821*3737Shx147065 	iret = (int)(outfp->wldp_result);
2822*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
2823*3737Shx147065 	return (iret);
2824*3737Shx147065 
2825*3737Shx147065 }
2826*3737Shx147065 
2827*3737Shx147065 static int
2828*3737Shx147065 pcwl_cfg_desiredrates(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
2829*3737Shx147065 {
2830*3737Shx147065 	uint16_t rate;
2831*3737Shx147065 	uint16_t i;
2832*3737Shx147065 	pcwl_rf_t *rf_p;
2833*3737Shx147065 	wldp_t	*infp;
2834*3737Shx147065 	wldp_t *outfp;
2835*3737Shx147065 	char *buf;
2836*3737Shx147065 	int iret;
2837*3737Shx147065 	char rates[4];
2838*3737Shx147065 	char maxrate;
2839*3737Shx147065 
2840*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
2841*3737Shx147065 	if (buf == NULL) {
2842*3737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
2843*3737Shx147065 		    MAX_BUF_LEN));
2844*3737Shx147065 		return (ENOMEM);
2845*3737Shx147065 	}
2846*3737Shx147065 	outfp = (wldp_t *)buf;
2847*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
2848*3737Shx147065 	infp = (wldp_t *)mp->b_rptr;
2849*3737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
2850*3737Shx147065 
2851*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
2852*3737Shx147065 		if (i = pcwl_get_ltv(pcwl_p, 2, WL_RID_TX_RATE, &rate)) {
2853*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
2854*3737Shx147065 			outfp->wldp_result = WL_HW_ERROR;
2855*3737Shx147065 			goto done;
2856*3737Shx147065 		}
2857*3737Shx147065 
2858*3737Shx147065 		if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) {
2859*3737Shx147065 			((wl_rates_t *)(outfp->wldp_buf))->wl_rates_num = 1;
2860*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET +
2861*3737Shx147065 			    offsetof(wl_rates_t, wl_rates_rates) +
2862*3737Shx147065 			    1 * sizeof (char);
2863*3737Shx147065 			switch (rate) {
2864*3737Shx147065 			case WL_SPEED_1Mbps_P2:
2865*3737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
2866*3737Shx147065 				    wl_rates_rates)[0] = WL_RATE_1M;
2867*3737Shx147065 				break;
2868*3737Shx147065 			case WL_SPEED_2Mbps_P2:
2869*3737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
2870*3737Shx147065 				    wl_rates_rates)[0] = WL_RATE_2M;
2871*3737Shx147065 				break;
2872*3737Shx147065 			case WL_SPEED_55Mbps_P2:
2873*3737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
2874*3737Shx147065 				    wl_rates_rates)[0] = WL_RATE_5_5M;
2875*3737Shx147065 				break;
2876*3737Shx147065 			case WL_SPEED_11Mbps_P2:
2877*3737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
2878*3737Shx147065 				    wl_rates_rates)[0] = WL_RATE_11M;
2879*3737Shx147065 				break;
2880*3737Shx147065 			default:
2881*3737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET;
2882*3737Shx147065 				outfp->wldp_result = WL_HW_ERROR;
2883*3737Shx147065 				goto done;
2884*3737Shx147065 			}
2885*3737Shx147065 		} else {
2886*3737Shx147065 			switch (rate) {
2887*3737Shx147065 			case WL_L_TX_RATE_FIX_1M:
2888*3737Shx147065 				((wl_rates_t *)(outfp->wldp_buf))->
2889*3737Shx147065 				    wl_rates_num = 1;
2890*3737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
2891*3737Shx147065 				    wl_rates_rates)[0] = WL_RATE_1M;
2892*3737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET +
2893*3737Shx147065 				    offsetof(wl_rates_t, wl_rates_rates) +
2894*3737Shx147065 				    1 * sizeof (char);
2895*3737Shx147065 				break;
2896*3737Shx147065 			case WL_L_TX_RATE_FIX_2M:
2897*3737Shx147065 				((wl_rates_t *)(outfp->wldp_buf))->
2898*3737Shx147065 				    wl_rates_num = 1;
2899*3737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
2900*3737Shx147065 				    wl_rates_rates)[0] = WL_RATE_2M;
2901*3737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET +
2902*3737Shx147065 				    offsetof(wl_rates_t, wl_rates_rates) +
2903*3737Shx147065 				    1 * sizeof (char);
2904*3737Shx147065 				break;
2905*3737Shx147065 			case WL_L_TX_RATE_AUTO_H:
2906*3737Shx147065 				((wl_rates_t *)(outfp->wldp_buf))->
2907*3737Shx147065 				    wl_rates_num = 4;
2908*3737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
2909*3737Shx147065 				    wl_rates_rates)[0] = WL_RATE_1M;
2910*3737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
2911*3737Shx147065 				    wl_rates_rates)[1] = WL_RATE_2M;
2912*3737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
2913*3737Shx147065 				    wl_rates_rates)[2] = WL_RATE_5_5M;
2914*3737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
2915*3737Shx147065 				    wl_rates_rates)[3] = WL_RATE_11M;
2916*3737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET +
2917*3737Shx147065 				    offsetof(wl_rates_t, wl_rates_rates) +
2918*3737Shx147065 				    4 * sizeof (char);
2919*3737Shx147065 				break;
2920*3737Shx147065 			case WL_L_TX_RATE_FIX_5M:
2921*3737Shx147065 				((wl_rates_t *)(outfp->wldp_buf))->
2922*3737Shx147065 				    wl_rates_num = 1;
2923*3737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
2924*3737Shx147065 				    wl_rates_rates)[0] = WL_RATE_5_5M;
2925*3737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET +
2926*3737Shx147065 				    offsetof(wl_rates_t, wl_rates_rates) +
2927*3737Shx147065 				    1 * sizeof (char);
2928*3737Shx147065 				break;
2929*3737Shx147065 			case WL_L_TX_RATE_FIX_11M:
2930*3737Shx147065 				((wl_rates_t *)(outfp->wldp_buf))->
2931*3737Shx147065 				    wl_rates_num = 1;
2932*3737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
2933*3737Shx147065 				    wl_rates_rates)[0] = WL_RATE_11M;
2934*3737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET +
2935*3737Shx147065 				    offsetof(wl_rates_t, wl_rates_rates) +
2936*3737Shx147065 				    1 * sizeof (char);
2937*3737Shx147065 				break;
2938*3737Shx147065 			case WL_L_TX_RATE_AUTO_L:
2939*3737Shx147065 				((wl_rates_t *)(outfp->wldp_buf))->
2940*3737Shx147065 				    wl_rates_num = 2;
2941*3737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
2942*3737Shx147065 				    wl_rates_rates)[0] = WL_RATE_1M;
2943*3737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
2944*3737Shx147065 				    wl_rates_rates)[1] = WL_RATE_2M;
2945*3737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET +
2946*3737Shx147065 				    offsetof(wl_rates_t, wl_rates_rates) +
2947*3737Shx147065 				    2 * sizeof (char);
2948*3737Shx147065 				break;
2949*3737Shx147065 			case WL_L_TX_RATE_AUTO_M:
2950*3737Shx147065 				((wl_rates_t *)(outfp->wldp_buf))->
2951*3737Shx147065 				    wl_rates_num = 3;
2952*3737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
2953*3737Shx147065 				    wl_rates_rates)[0] = WL_RATE_1M;
2954*3737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
2955*3737Shx147065 				    wl_rates_rates)[1] = WL_RATE_2M;
2956*3737Shx147065 				(((wl_rates_t *)(outfp->wldp_buf))->
2957*3737Shx147065 				    wl_rates_rates)[2] = WL_RATE_5_5M;
2958*3737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET +
2959*3737Shx147065 				    offsetof(wl_rates_t, wl_rates_rates) +
2960*3737Shx147065 				    3 * sizeof (char);
2961*3737Shx147065 				break;
2962*3737Shx147065 			default:
2963*3737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET;
2964*3737Shx147065 				outfp->wldp_result = WL_HW_ERROR;
2965*3737Shx147065 				goto done;
2966*3737Shx147065 			}
2967*3737Shx147065 		}
2968*3737Shx147065 		PCWLDBG((CE_CONT, "pcwl: get rate=%d\n", rate));
2969*3737Shx147065 		    outfp->wldp_result = WL_SUCCESS;
2970*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
2971*3737Shx147065 		bzero(rates, sizeof (rates));
2972*3737Shx147065 		for (i = 0; i < 4; i++) {
2973*3737Shx147065 			rates[i] = (((wl_rates_t *)
2974*3737Shx147065 			    (infp->wldp_buf))->wl_rates_rates)[i];
2975*3737Shx147065 			PCWLDBG((CE_CONT, "pcwl: set tx_rate[%d]=%d\n",
2976*3737Shx147065 			    i, rates[i]));
2977*3737Shx147065 		}
2978*3737Shx147065 		PCWLDBG((CE_CONT, "pcwl: set rate_num=%d\n",
2979*3737Shx147065 		    ((wl_rates_t *)(infp->wldp_buf))
2980*3737Shx147065 		    ->wl_rates_num));
2981*3737Shx147065 		switch (((wl_rates_t *)
2982*3737Shx147065 		    (infp->wldp_buf))->wl_rates_num) {
2983*3737Shx147065 		case 1:
2984*3737Shx147065 			switch (rates[0]) {
2985*3737Shx147065 			case WL_RATE_1M:
2986*3737Shx147065 				rf_p->rf_tx_rate = WL_TX_RATE_FIX_1M(pcwl_p);
2987*3737Shx147065 				break;
2988*3737Shx147065 			case WL_RATE_2M:
2989*3737Shx147065 				rf_p->rf_tx_rate = WL_TX_RATE_FIX_2M(pcwl_p);
2990*3737Shx147065 				break;
2991*3737Shx147065 			case WL_RATE_11M:
2992*3737Shx147065 				rf_p->rf_tx_rate = WL_TX_RATE_FIX_11M(pcwl_p);
2993*3737Shx147065 				break;
2994*3737Shx147065 			case WL_RATE_5_5M:
2995*3737Shx147065 				rf_p->rf_tx_rate = WL_TX_RATE_FIX_5M(pcwl_p);
2996*3737Shx147065 				break;
2997*3737Shx147065 			default:
2998*3737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET;
2999*3737Shx147065 				outfp->wldp_result = WL_NOTSUPPORTED;
3000*3737Shx147065 				goto done;
3001*3737Shx147065 			}
3002*3737Shx147065 			break;
3003*3737Shx147065 		case 2:
3004*3737Shx147065 			maxrate = (rates[0] > rates[1] ?
3005*3737Shx147065 			    rates[0] : rates[1]);
3006*3737Shx147065 			switch (maxrate) {
3007*3737Shx147065 			case WL_RATE_2M:
3008*3737Shx147065 				rf_p->rf_tx_rate = WL_TX_RATE_AUTO_L(pcwl_p);
3009*3737Shx147065 				break;
3010*3737Shx147065 			case WL_RATE_11M:
3011*3737Shx147065 				rf_p->rf_tx_rate = WL_TX_RATE_AUTO_H(pcwl_p);
3012*3737Shx147065 				break;
3013*3737Shx147065 			case WL_RATE_5_5M:
3014*3737Shx147065 				rf_p->rf_tx_rate = WL_TX_RATE_AUTO_M(pcwl_p);
3015*3737Shx147065 				break;
3016*3737Shx147065 			default:
3017*3737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET;
3018*3737Shx147065 				outfp->wldp_result = WL_NOTSUPPORTED;
3019*3737Shx147065 				goto done;
3020*3737Shx147065 			}
3021*3737Shx147065 			break;
3022*3737Shx147065 		case 3:
3023*3737Shx147065 			maxrate = (rates[0] > rates[1] ?
3024*3737Shx147065 			    rates[0] : rates[1]);
3025*3737Shx147065 			maxrate = (rates[2] > maxrate ?
3026*3737Shx147065 			    rates[2] : maxrate);
3027*3737Shx147065 			switch (maxrate) {
3028*3737Shx147065 			case WL_RATE_11M:
3029*3737Shx147065 				rf_p->rf_tx_rate = WL_TX_RATE_AUTO_H(pcwl_p);
3030*3737Shx147065 				break;
3031*3737Shx147065 			case WL_RATE_5_5M:
3032*3737Shx147065 				rf_p->rf_tx_rate = WL_TX_RATE_AUTO_M(pcwl_p);
3033*3737Shx147065 				break;
3034*3737Shx147065 			default:
3035*3737Shx147065 				outfp->wldp_length = WIFI_BUF_OFFSET;
3036*3737Shx147065 				outfp->wldp_result = WL_NOTSUPPORTED;
3037*3737Shx147065 				goto done;
3038*3737Shx147065 			}
3039*3737Shx147065 			break;
3040*3737Shx147065 		case 4:
3041*3737Shx147065 			rf_p->rf_tx_rate = WL_TX_RATE_AUTO_H(pcwl_p);
3042*3737Shx147065 			break;
3043*3737Shx147065 		default:
3044*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
3045*3737Shx147065 			outfp->wldp_result = WL_LACK_FEATURE;
3046*3737Shx147065 			goto done;
3047*3737Shx147065 		}
3048*3737Shx147065 		PCWLDBG((CE_CONT, "pcwl: set tx_rate=%d\n", rf_p->rf_tx_rate));
3049*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
3050*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3051*3737Shx147065 	} else {
3052*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3053*3737Shx147065 		return (EINVAL);
3054*3737Shx147065 	}
3055*3737Shx147065 done:
3056*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3057*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3058*3737Shx147065 	iret = (int)(outfp->wldp_result);
3059*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3060*3737Shx147065 	return (iret);
3061*3737Shx147065 }
3062*3737Shx147065 
3063*3737Shx147065 /*ARGSUSED*/
3064*3737Shx147065 static int
3065*3737Shx147065 pcwl_cfg_supportrates(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
3066*3737Shx147065 {
3067*3737Shx147065 	uint16_t i;
3068*3737Shx147065 	wldp_t *outfp;
3069*3737Shx147065 	char *buf;
3070*3737Shx147065 
3071*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3072*3737Shx147065 	if (buf == NULL) {
3073*3737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
3074*3737Shx147065 		    MAX_BUF_LEN));
3075*3737Shx147065 		return (ENOMEM);
3076*3737Shx147065 	}
3077*3737Shx147065 	outfp = (wldp_t *)buf;
3078*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3079*3737Shx147065 
3080*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3081*3737Shx147065 		((wl_rates_t *)(outfp->wldp_buf))->wl_rates_num = 4;
3082*3737Shx147065 		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[0]
3083*3737Shx147065 		    = WL_RATE_1M;
3084*3737Shx147065 		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[1]
3085*3737Shx147065 		    = WL_RATE_2M;
3086*3737Shx147065 		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[2]
3087*3737Shx147065 		    = WL_RATE_5_5M;
3088*3737Shx147065 		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[3]
3089*3737Shx147065 		    = WL_RATE_11M;
3090*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET +
3091*3737Shx147065 		    offsetof(wl_rates_t, wl_rates_rates) +
3092*3737Shx147065 		    4 * sizeof (char);
3093*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3094*3737Shx147065 		for (i = 0; i < (outfp->wldp_length); i++)
3095*3737Shx147065 			(void) mi_mpprintf_putc((char *)mp, buf[i]);
3096*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3097*3737Shx147065 		return (WL_SUCCESS);
3098*3737Shx147065 	} else {
3099*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3100*3737Shx147065 		return (EINVAL);
3101*3737Shx147065 	}
3102*3737Shx147065 }
3103*3737Shx147065 
3104*3737Shx147065 static int
3105*3737Shx147065 pcwl_cfg_powermode(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
3106*3737Shx147065 {
3107*3737Shx147065 	uint16_t i, ret;
3108*3737Shx147065 	pcwl_rf_t *rf_p;
3109*3737Shx147065 	wldp_t	*infp;
3110*3737Shx147065 	wldp_t *outfp;
3111*3737Shx147065 	char *buf;
3112*3737Shx147065 	int iret;
3113*3737Shx147065 
3114*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3115*3737Shx147065 	if (buf == NULL) {
3116*3737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
3117*3737Shx147065 		    MAX_BUF_LEN));
3118*3737Shx147065 		return (ENOMEM);
3119*3737Shx147065 	}
3120*3737Shx147065 	outfp = (wldp_t *)buf;
3121*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3122*3737Shx147065 	infp = (wldp_t *)mp->b_rptr;
3123*3737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
3124*3737Shx147065 
3125*3737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_ps_mode_t);
3126*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3127*3737Shx147065 		((wl_ps_mode_t *)(outfp->wldp_buf))->wl_ps_mode =
3128*3737Shx147065 		    rf_p->rf_pm_enabled;
3129*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3130*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
3131*3737Shx147065 		ret = (uint16_t)(((wl_ps_mode_t *)(infp->wldp_buf))
3132*3737Shx147065 		    ->wl_ps_mode);
3133*3737Shx147065 		if (ret != WL_PM_AM && ret != WL_PM_MPS && ret != WL_PM_FAST) {
3134*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
3135*3737Shx147065 			outfp->wldp_result = WL_NOTSUPPORTED;
3136*3737Shx147065 			goto done;
3137*3737Shx147065 		}
3138*3737Shx147065 		rf_p->rf_pm_enabled = ret;
3139*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3140*3737Shx147065 	} else {
3141*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3142*3737Shx147065 		return (EINVAL);
3143*3737Shx147065 	}
3144*3737Shx147065 done:
3145*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3146*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3147*3737Shx147065 	iret = (int)(outfp->wldp_result);
3148*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3149*3737Shx147065 	return (iret);
3150*3737Shx147065 
3151*3737Shx147065 }
3152*3737Shx147065 
3153*3737Shx147065 static int
3154*3737Shx147065 pcwl_cfg_authmode(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
3155*3737Shx147065 {
3156*3737Shx147065 	uint16_t i, ret;
3157*3737Shx147065 	pcwl_rf_t *rf_p;
3158*3737Shx147065 	wldp_t	*infp;
3159*3737Shx147065 	wldp_t *outfp;
3160*3737Shx147065 	char *buf;
3161*3737Shx147065 	int iret;
3162*3737Shx147065 
3163*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3164*3737Shx147065 	if (buf == NULL) {
3165*3737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
3166*3737Shx147065 		    MAX_BUF_LEN));
3167*3737Shx147065 		return (ENOMEM);
3168*3737Shx147065 	}
3169*3737Shx147065 	outfp = (wldp_t *)buf;
3170*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3171*3737Shx147065 	infp = (wldp_t *)mp->b_rptr;
3172*3737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
3173*3737Shx147065 
3174*3737Shx147065 
3175*3737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_authmode_t);
3176*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3177*3737Shx147065 		*(wl_authmode_t *)(outfp->wldp_buf) = rf_p->rf_authtype;
3178*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3179*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
3180*3737Shx147065 		ret = (uint16_t)(*(wl_authmode_t *)(infp->wldp_buf));
3181*3737Shx147065 		if (ret != WL_OPENSYSTEM && ret != WL_SHAREDKEY) {
3182*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
3183*3737Shx147065 			outfp->wldp_result = WL_NOTSUPPORTED;
3184*3737Shx147065 			goto done;
3185*3737Shx147065 		}
3186*3737Shx147065 		rf_p->rf_authtype = ret;
3187*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3188*3737Shx147065 	} else {
3189*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3190*3737Shx147065 		return (EINVAL);
3191*3737Shx147065 	}
3192*3737Shx147065 done:
3193*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3194*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3195*3737Shx147065 	iret = (int)(outfp->wldp_result);
3196*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3197*3737Shx147065 	return (iret);
3198*3737Shx147065 }
3199*3737Shx147065 
3200*3737Shx147065 static int
3201*3737Shx147065 pcwl_cfg_encryption(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
3202*3737Shx147065 {
3203*3737Shx147065 	uint16_t i, ret;
3204*3737Shx147065 	pcwl_rf_t *rf_p;
3205*3737Shx147065 	wldp_t	*infp;
3206*3737Shx147065 	wldp_t *outfp;
3207*3737Shx147065 	char *buf;
3208*3737Shx147065 	int iret;
3209*3737Shx147065 
3210*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3211*3737Shx147065 	if (buf == NULL) {
3212*3737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
3213*3737Shx147065 		    MAX_BUF_LEN));
3214*3737Shx147065 		return (ENOMEM);
3215*3737Shx147065 	}
3216*3737Shx147065 	outfp = (wldp_t *)buf;
3217*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3218*3737Shx147065 	infp = (wldp_t *)mp->b_rptr;
3219*3737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
3220*3737Shx147065 
3221*3737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_encryption_t);
3222*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3223*3737Shx147065 		*(wl_encryption_t *)(outfp->wldp_buf) = rf_p->rf_encryption;
3224*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3225*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
3226*3737Shx147065 		ret = (uint16_t)(*(wl_encryption_t *)(infp->wldp_buf));
3227*3737Shx147065 		PCWLDBG((CE_NOTE, "set encryption: %d\n", ret));
3228*3737Shx147065 		if (ret != WL_NOENCRYPTION && ret != WL_ENC_WEP) {
3229*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
3230*3737Shx147065 			outfp->wldp_result = WL_NOTSUPPORTED;
3231*3737Shx147065 			goto done;
3232*3737Shx147065 		}
3233*3737Shx147065 		rf_p->rf_encryption = ret;
3234*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3235*3737Shx147065 	} else {
3236*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3237*3737Shx147065 		return (EINVAL);
3238*3737Shx147065 	}
3239*3737Shx147065 done:
3240*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3241*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3242*3737Shx147065 	iret = (int)(outfp->wldp_result);
3243*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3244*3737Shx147065 	return (iret);
3245*3737Shx147065 }
3246*3737Shx147065 
3247*3737Shx147065 static int
3248*3737Shx147065 pcwl_cfg_wepkeyid(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
3249*3737Shx147065 {
3250*3737Shx147065 	uint16_t i, ret;
3251*3737Shx147065 	pcwl_rf_t *rf_p;
3252*3737Shx147065 	wldp_t	*infp;
3253*3737Shx147065 	wldp_t *outfp;
3254*3737Shx147065 	char *buf;
3255*3737Shx147065 	int iret;
3256*3737Shx147065 
3257*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3258*3737Shx147065 	if (buf == NULL) {
3259*3737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
3260*3737Shx147065 		    MAX_BUF_LEN));
3261*3737Shx147065 		return (ENOMEM);
3262*3737Shx147065 	}
3263*3737Shx147065 	outfp = (wldp_t *)buf;
3264*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3265*3737Shx147065 	infp = (wldp_t *)mp->b_rptr;
3266*3737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
3267*3737Shx147065 
3268*3737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_wep_key_id_t);
3269*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3270*3737Shx147065 		*(wl_wep_key_id_t *)(outfp->wldp_buf) = rf_p->rf_tx_crypt_key;
3271*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3272*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
3273*3737Shx147065 		ret = (uint16_t)(*(wl_wep_key_id_t *)(infp->wldp_buf));
3274*3737Shx147065 		if (ret >= MAX_NWEPKEYS) {
3275*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
3276*3737Shx147065 			outfp->wldp_result = WL_NOTSUPPORTED;
3277*3737Shx147065 			goto done;
3278*3737Shx147065 		}
3279*3737Shx147065 		rf_p->rf_tx_crypt_key = ret;
3280*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3281*3737Shx147065 	} else {
3282*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3283*3737Shx147065 		return (EINVAL);
3284*3737Shx147065 	}
3285*3737Shx147065 done:
3286*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3287*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3288*3737Shx147065 	iret = (int)(outfp->wldp_result);
3289*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3290*3737Shx147065 	return (iret);
3291*3737Shx147065 }
3292*3737Shx147065 
3293*3737Shx147065 static int
3294*3737Shx147065 pcwl_cfg_createibss(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
3295*3737Shx147065 {
3296*3737Shx147065 	uint16_t i, ret;
3297*3737Shx147065 	pcwl_rf_t *rf_p;
3298*3737Shx147065 	wldp_t	*infp;
3299*3737Shx147065 	wldp_t *outfp;
3300*3737Shx147065 	char *buf;
3301*3737Shx147065 	int iret;
3302*3737Shx147065 
3303*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3304*3737Shx147065 	if (buf == NULL) {
3305*3737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
3306*3737Shx147065 		    MAX_BUF_LEN));
3307*3737Shx147065 		return (ENOMEM);
3308*3737Shx147065 	}
3309*3737Shx147065 	outfp = (wldp_t *)buf;
3310*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3311*3737Shx147065 	infp = (wldp_t *)mp->b_rptr;
3312*3737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
3313*3737Shx147065 
3314*3737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_create_ibss_t);
3315*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3316*3737Shx147065 		*(wl_create_ibss_t *)(outfp->wldp_buf) = rf_p->rf_create_ibss;
3317*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3318*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
3319*3737Shx147065 		ret = (uint16_t)(*(wl_create_ibss_t *)(infp->wldp_buf));
3320*3737Shx147065 		if (ret != 0 && ret != 1) {
3321*3737Shx147065 			outfp->wldp_length = WIFI_BUF_OFFSET;
3322*3737Shx147065 			outfp->wldp_result = WL_NOTSUPPORTED;
3323*3737Shx147065 			goto done;
3324*3737Shx147065 		}
3325*3737Shx147065 		rf_p->rf_create_ibss = ret;
3326*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3327*3737Shx147065 	} else {
3328*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3329*3737Shx147065 		return (EINVAL);
3330*3737Shx147065 	}
3331*3737Shx147065 done:
3332*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3333*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3334*3737Shx147065 	iret = (int)(outfp->wldp_result);
3335*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3336*3737Shx147065 	return (iret);
3337*3737Shx147065 }
3338*3737Shx147065 
3339*3737Shx147065 static int
3340*3737Shx147065 pcwl_cfg_rssi(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
3341*3737Shx147065 {
3342*3737Shx147065 	uint16_t i;
3343*3737Shx147065 	int iret;
3344*3737Shx147065 	wldp_t *outfp;
3345*3737Shx147065 	char *buf;
3346*3737Shx147065 
3347*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3348*3737Shx147065 	if (buf == NULL) {
3349*3737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
3350*3737Shx147065 		    MAX_BUF_LEN));
3351*3737Shx147065 		return (ENOMEM);
3352*3737Shx147065 	}
3353*3737Shx147065 	outfp = (wldp_t *)buf;
3354*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3355*3737Shx147065 
3356*3737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_rssi_t);
3357*3737Shx147065 
3358*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3359*3737Shx147065 		if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) {
3360*3737Shx147065 			*(wl_rssi_t *)(outfp->wldp_buf) =
3361*3737Shx147065 			    min((pcwl_p->pcwl_rssi * 15 / 85 + 1), 15);
3362*3737Shx147065 		} else {
3363*3737Shx147065 		/*
3364*3737Shx147065 		 * According to the description of the
3365*3737Shx147065 		 * datasheet(Lucent card), the signal level
3366*3737Shx147065 		 * value is between 27 -- 154.
3367*3737Shx147065 		 * we reflect these value to 1-15 as rssi.
3368*3737Shx147065 		 */
3369*3737Shx147065 			if (pcwl_p->pcwl_rssi <= 27)
3370*3737Shx147065 				*(wl_rssi_t *)(outfp->wldp_buf) = 1;
3371*3737Shx147065 			else if (pcwl_p->pcwl_rssi > 154)
3372*3737Shx147065 				*(wl_rssi_t *)(outfp->wldp_buf) = 15;
3373*3737Shx147065 			else
3374*3737Shx147065 				*(wl_rssi_t *)(outfp->wldp_buf) =
3375*3737Shx147065 				    min(15, ((pcwl_p->pcwl_rssi - 27)
3376*3737Shx147065 				    * 15 / 127));
3377*3737Shx147065 		}
3378*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3379*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
3380*3737Shx147065 		outfp->wldp_result = WL_READONLY;
3381*3737Shx147065 	} else {
3382*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3383*3737Shx147065 		return (EINVAL);
3384*3737Shx147065 	}
3385*3737Shx147065 done:
3386*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3387*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3388*3737Shx147065 	iret = (int)(outfp->wldp_result);
3389*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3390*3737Shx147065 	return (iret);
3391*3737Shx147065 }
3392*3737Shx147065 
3393*3737Shx147065 /*ARGSUSED*/
3394*3737Shx147065 static int
3395*3737Shx147065 pcwl_cfg_radio(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
3396*3737Shx147065 {
3397*3737Shx147065 	uint16_t i;
3398*3737Shx147065 	int iret;
3399*3737Shx147065 	wldp_t *outfp;
3400*3737Shx147065 	char *buf;
3401*3737Shx147065 
3402*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3403*3737Shx147065 	if (buf == NULL) {
3404*3737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
3405*3737Shx147065 		    MAX_BUF_LEN));
3406*3737Shx147065 		return (ENOMEM);
3407*3737Shx147065 	}
3408*3737Shx147065 	outfp = (wldp_t *)buf;
3409*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3410*3737Shx147065 
3411*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3412*3737Shx147065 		*(wl_radio_t *)(outfp->wldp_buf) = B_TRUE;
3413*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_radio_t);
3414*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3415*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
3416*3737Shx147065 		outfp->wldp_length = WIFI_BUF_OFFSET;
3417*3737Shx147065 		outfp->wldp_result = WL_LACK_FEATURE;
3418*3737Shx147065 	} else {
3419*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3420*3737Shx147065 		return (EINVAL);
3421*3737Shx147065 	}
3422*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3423*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3424*3737Shx147065 	iret = (int)(outfp->wldp_result);
3425*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3426*3737Shx147065 	return (iret);
3427*3737Shx147065 }
3428*3737Shx147065 
3429*3737Shx147065 static int
3430*3737Shx147065 pcwl_cfg_wepkey(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
3431*3737Shx147065 {
3432*3737Shx147065 	uint16_t i;
3433*3737Shx147065 	wl_wep_key_t *p_wepkey_tab;
3434*3737Shx147065 	pcwl_rf_t *rf_p;
3435*3737Shx147065 	wldp_t	*infp;
3436*3737Shx147065 	wldp_t *outfp;
3437*3737Shx147065 	char *buf;
3438*3737Shx147065 	int iret;
3439*3737Shx147065 
3440*3737Shx147065 	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
3441*3737Shx147065 	if (buf == NULL) {
3442*3737Shx147065 		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
3443*3737Shx147065 		    MAX_BUF_LEN));
3444*3737Shx147065 		return (ENOMEM);
3445*3737Shx147065 	}
3446*3737Shx147065 	outfp = (wldp_t *)buf;
3447*3737Shx147065 	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
3448*3737Shx147065 	infp = (wldp_t *)mp->b_rptr;
3449*3737Shx147065 	rf_p = &pcwl_p->pcwl_rf;
3450*3737Shx147065 	bzero((rf_p->rf_ckeys), sizeof (rf_ckey_t) * MAX_NWEPKEYS);
3451*3737Shx147065 
3452*3737Shx147065 	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_wep_key_tab_t);
3453*3737Shx147065 	if (cmd == WLAN_GET_PARAM) {
3454*3737Shx147065 		outfp->wldp_result = WL_WRITEONLY;
3455*3737Shx147065 	} else if (cmd == WLAN_SET_PARAM) {
3456*3737Shx147065 		p_wepkey_tab = (wl_wep_key_t *)(infp->wldp_buf);
3457*3737Shx147065 		for (i = 0; i < MAX_NWEPKEYS; i++) {
3458*3737Shx147065 			if (p_wepkey_tab[i].wl_wep_operation == WL_ADD) {
3459*3737Shx147065 				rf_p->rf_ckeys[i].ckey_len =
3460*3737Shx147065 				    p_wepkey_tab[i].wl_wep_length;
3461*3737Shx147065 				bcopy(p_wepkey_tab[i].wl_wep_key,
3462*3737Shx147065 				    rf_p->rf_ckeys[i].ckey_dat,
3463*3737Shx147065 				    p_wepkey_tab[i].wl_wep_length);
3464*3737Shx147065 				PCWL_SWAP16((uint16_t *)
3465*3737Shx147065 				    &rf_p->rf_ckeys[i].ckey_dat,
3466*3737Shx147065 				    rf_p->rf_ckeys[i].ckey_len + 1);
3467*3737Shx147065 				PCWLDBG((CE_CONT, "%s, %d\n",
3468*3737Shx147065 				    rf_p->rf_ckeys[i].ckey_dat, i));
3469*3737Shx147065 			}
3470*3737Shx147065 			PCWLDBG((CE_CONT, "pcwl: rf_ckeys[%d]=%s\n", i,
3471*3737Shx147065 				(char *)(rf_p->rf_ckeys[i].ckey_dat)));
3472*3737Shx147065 		}
3473*3737Shx147065 		outfp->wldp_result = WL_SUCCESS;
3474*3737Shx147065 	} else {
3475*3737Shx147065 		kmem_free(buf, MAX_BUF_LEN);
3476*3737Shx147065 		return (EINVAL);
3477*3737Shx147065 	}
3478*3737Shx147065 done:
3479*3737Shx147065 	for (i = 0; i < (outfp->wldp_length); i++)
3480*3737Shx147065 		(void) mi_mpprintf_putc((char *)mp, buf[i]);
3481*3737Shx147065 	iret = (int)(outfp->wldp_result);
3482*3737Shx147065 	kmem_free(buf, MAX_BUF_LEN);
3483*3737Shx147065 	return (iret);
3484*3737Shx147065 }
3485*3737Shx147065 
3486*3737Shx147065 static void
3487*3737Shx147065 pcwl_connect_timeout(void *arg)
3488*3737Shx147065 {
3489*3737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
3490*3737Shx147065 	uint16_t ret = 0;
3491*3737Shx147065 
3492*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
3493*3737Shx147065 	PCWL_DISABLE_INTR(pcwl_p);
3494*3737Shx147065 	if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
3495*3737Shx147065 		goto done;
3496*3737Shx147065 	}
3497*3737Shx147065 	if (ret = pcwl_config_rf(pcwl_p)) {
3498*3737Shx147065 		goto done;
3499*3737Shx147065 	}
3500*3737Shx147065 	if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
3501*3737Shx147065 		goto done;
3502*3737Shx147065 	}
3503*3737Shx147065 	PCWL_ENABLE_INTR(pcwl_p);
3504*3737Shx147065 done:
3505*3737Shx147065 	if (ret)
3506*3737Shx147065 		cmn_err(CE_WARN, "pcwl: connect failed due to hareware error");
3507*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
3508*3737Shx147065 	pcwl_p->pcwl_connect_timeout_id = 0;
3509*3737Shx147065 }
3510*3737Shx147065 
3511*3737Shx147065 static int
3512*3737Shx147065 pcwl_getset(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
3513*3737Shx147065 {
3514*3737Shx147065 	int ret = WL_SUCCESS;
3515*3737Shx147065 	int connect = 0;
3516*3737Shx147065 
3517*3737Shx147065 	mutex_enter(&pcwl_p->pcwl_glock);
3518*3737Shx147065 	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
3519*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
3520*3737Shx147065 		return (PCWL_FAIL);
3521*3737Shx147065 	}
3522*3737Shx147065 	switch (((wldp_t *)mp->b_rptr)->wldp_id) {
3523*3737Shx147065 	case WL_ESSID:
3524*3737Shx147065 		ret = pcwl_cfg_essid(mp, pcwl_p, cmd);
3525*3737Shx147065 		connect = 1;
3526*3737Shx147065 		PCWLDBG((CE_NOTE, "cfg_essid\n"));
3527*3737Shx147065 		break;
3528*3737Shx147065 	case WL_BSSID:
3529*3737Shx147065 		ret = pcwl_cfg_bssid(mp, pcwl_p, cmd);
3530*3737Shx147065 		connect = 1;
3531*3737Shx147065 		PCWLDBG((CE_NOTE, "cfg_bssid\n"));
3532*3737Shx147065 		break;
3533*3737Shx147065 	case WL_ESS_LIST:
3534*3737Shx147065 		ret = pcwl_cfg_scan(mp, pcwl_p, cmd);
3535*3737Shx147065 		PCWLDBG((CE_NOTE, "cfg_scan\n"));
3536*3737Shx147065 		break;
3537*3737Shx147065 	case WL_LINKSTATUS:
3538*3737Shx147065 		ret = pcwl_cfg_linkstatus(mp, pcwl_p, cmd);
3539*3737Shx147065 		PCWLDBG((CE_NOTE, "cfg_linkstatus\n"));
3540*3737Shx147065 		break;
3541*3737Shx147065 	case WL_BSS_TYPE:
3542*3737Shx147065 		ret = pcwl_cfg_bsstype(mp, pcwl_p, cmd);
3543*3737Shx147065 		connect = 1;
3544*3737Shx147065 		PCWLDBG((CE_NOTE, "cfg_bsstype\n"));
3545*3737Shx147065 		break;
3546*3737Shx147065 	case WL_PHY_CONFIG:
3547*3737Shx147065 		ret = pcwl_cfg_phy(mp, pcwl_p, cmd);
3548*3737Shx147065 		connect = 1;
3549*3737Shx147065 		PCWLDBG((CE_NOTE, "cfg_phy\n"));
3550*3737Shx147065 		break;
3551*3737Shx147065 	case WL_DESIRED_RATES:
3552*3737Shx147065 		ret = pcwl_cfg_desiredrates(mp, pcwl_p, cmd);
3553*3737Shx147065 		connect = 1;
3554*3737Shx147065 		PCWLDBG((CE_NOTE, "cfg_disred-rates\n"));
3555*3737Shx147065 		break;
3556*3737Shx147065 	case WL_SUPPORTED_RATES:
3557*3737Shx147065 		ret = pcwl_cfg_supportrates(mp, pcwl_p, cmd);
3558*3737Shx147065 		PCWLDBG((CE_NOTE, "cfg_supported-rates\n"));
3559*3737Shx147065 		break;
3560*3737Shx147065 	case WL_POWER_MODE:
3561*3737Shx147065 		ret = pcwl_cfg_powermode(mp, pcwl_p, cmd);
3562*3737Shx147065 		PCWLDBG((CE_NOTE, "cfg_powermode\n"));
3563*3737Shx147065 		break;
3564*3737Shx147065 	case WL_AUTH_MODE:
3565*3737Shx147065 		ret = pcwl_cfg_authmode(mp, pcwl_p, cmd);
3566*3737Shx147065 		connect = 1;
3567*3737Shx147065 		PCWLDBG((CE_NOTE, "cfg_authmode\n"));
3568*3737Shx147065 		break;
3569*3737Shx147065 	case WL_ENCRYPTION:
3570*3737Shx147065 		ret = pcwl_cfg_encryption(mp, pcwl_p, cmd);
3571*3737Shx147065 		connect = 1;
3572*3737Shx147065 		PCWLDBG((CE_NOTE, "cfg_encryption\n"));
3573*3737Shx147065 		break;
3574*3737Shx147065 	case WL_WEP_KEY_ID:
3575*3737Shx147065 		ret = pcwl_cfg_wepkeyid(mp, pcwl_p, cmd);
3576*3737Shx147065 		connect = 1;
3577*3737Shx147065 		PCWLDBG((CE_NOTE, "cfg_wepkeyid\n"));
3578*3737Shx147065 		break;
3579*3737Shx147065 	case WL_CREATE_IBSS:
3580*3737Shx147065 		ret = pcwl_cfg_createibss(mp, pcwl_p, cmd);
3581*3737Shx147065 		connect = 1;
3582*3737Shx147065 		PCWLDBG((CE_NOTE, "cfg_create-ibss\n"));
3583*3737Shx147065 		break;
3584*3737Shx147065 	case WL_RSSI:
3585*3737Shx147065 		ret = pcwl_cfg_rssi(mp, pcwl_p, cmd);
3586*3737Shx147065 		PCWLDBG((CE_NOTE, "cfg_rssi\n"));
3587*3737Shx147065 		break;
3588*3737Shx147065 	case WL_RADIO:
3589*3737Shx147065 		ret = pcwl_cfg_radio(mp, pcwl_p, cmd);
3590*3737Shx147065 		PCWLDBG((CE_NOTE, "cfg_radio\n"));
3591*3737Shx147065 		break;
3592*3737Shx147065 	case WL_WEP_KEY_TAB:
3593*3737Shx147065 		ret = pcwl_cfg_wepkey(mp, pcwl_p, cmd);
3594*3737Shx147065 		connect = 1;
3595*3737Shx147065 		PCWLDBG((CE_NOTE, "cfg_wepkey\n"));
3596*3737Shx147065 		break;
3597*3737Shx147065 	case WL_SCAN:
3598*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
3599*3737Shx147065 		if (pcwl_p->pcwl_connect_timeout_id != 0) {
3600*3737Shx147065 			(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
3601*3737Shx147065 			pcwl_p->pcwl_connect_timeout_id = 0;
3602*3737Shx147065 		}
3603*3737Shx147065 		mutex_enter(&pcwl_p->pcwl_glock);
3604*3737Shx147065 		ret = pcwl_cmd_scan(pcwl_p);
3605*3737Shx147065 		break;
3606*3737Shx147065 	case WL_LOAD_DEFAULTS:
3607*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
3608*3737Shx147065 		if (pcwl_p->pcwl_connect_timeout_id != 0) {
3609*3737Shx147065 			(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
3610*3737Shx147065 			pcwl_p->pcwl_connect_timeout_id = 0;
3611*3737Shx147065 		}
3612*3737Shx147065 		mutex_enter(&pcwl_p->pcwl_glock);
3613*3737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
3614*3737Shx147065 			ret = (int)WL_HW_ERROR;
3615*3737Shx147065 			break;
3616*3737Shx147065 		}
3617*3737Shx147065 		if (ret = pcwl_loaddef_rf(pcwl_p)) {
3618*3737Shx147065 			ret = (int)WL_HW_ERROR;
3619*3737Shx147065 			PCWLDBG((CE_WARN, "cfg_loaddef_err\n"));
3620*3737Shx147065 			break;
3621*3737Shx147065 		}
3622*3737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
3623*3737Shx147065 			ret = (int)WL_HW_ERROR;
3624*3737Shx147065 			break;
3625*3737Shx147065 		}
3626*3737Shx147065 		pcwl_delay(pcwl_p, 1000000);
3627*3737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
3628*3737Shx147065 			ret = (int)WL_HW_ERROR;
3629*3737Shx147065 			break;
3630*3737Shx147065 		}
3631*3737Shx147065 		PCWLDBG((CE_NOTE, "loaddef\n"));
3632*3737Shx147065 		break;
3633*3737Shx147065 	case WL_DISASSOCIATE:
3634*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
3635*3737Shx147065 		if (pcwl_p->pcwl_connect_timeout_id != 0) {
3636*3737Shx147065 			(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
3637*3737Shx147065 			pcwl_p->pcwl_connect_timeout_id = 0;
3638*3737Shx147065 		}
3639*3737Shx147065 
3640*3737Shx147065 		mutex_enter(&pcwl_p->pcwl_glock);
3641*3737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
3642*3737Shx147065 			ret = (int)WL_HW_ERROR;
3643*3737Shx147065 			break;
3644*3737Shx147065 		}
3645*3737Shx147065 		/*
3646*3737Shx147065 		 * A workaround here: If the card is in ad-hoc mode, the
3647*3737Shx147065 		 * following scan will not work correctly, so any
3648*3737Shx147065 		 * 'dladm connect-wifi' which need a scan first will not
3649*3737Shx147065 		 * succeed. software reset the card here as a workround.
3650*3737Shx147065 		 */
3651*3737Shx147065 		if ((pcwl_p->pcwl_rf.rf_porttype == WL_BSS_IBSS) &&
3652*3737Shx147065 		    (pcwl_p->pcwl_chip_type == PCWL_CHIP_LUCENT)) {
3653*3737Shx147065 			if (ret = pcwl_reset_backend(pcwl_p)) {
3654*3737Shx147065 				ret = (int)WL_HW_ERROR;
3655*3737Shx147065 				break;
3656*3737Shx147065 			}
3657*3737Shx147065 			if (ret = pcwl_init_nicmem(pcwl_p)) {
3658*3737Shx147065 				ret = (int)WL_HW_ERROR;
3659*3737Shx147065 				break;
3660*3737Shx147065 			}
3661*3737Shx147065 			pcwl_start_locked(pcwl_p);
3662*3737Shx147065 		}
3663*3737Shx147065 		if (ret = pcwl_loaddef_rf(pcwl_p)) {
3664*3737Shx147065 			ret = (int)WL_HW_ERROR;
3665*3737Shx147065 			PCWLDBG((CE_WARN, "cfg_loaddef_err\n"));
3666*3737Shx147065 			break;
3667*3737Shx147065 		}
3668*3737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
3669*3737Shx147065 			ret = (int)WL_HW_ERROR;
3670*3737Shx147065 			break;
3671*3737Shx147065 		}
3672*3737Shx147065 		pcwl_delay(pcwl_p, 1000000);
3673*3737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
3674*3737Shx147065 			ret = (int)WL_HW_ERROR;
3675*3737Shx147065 			break;
3676*3737Shx147065 		}
3677*3737Shx147065 		PCWLDBG((CE_NOTE, "disassociate\n"));
3678*3737Shx147065 		break;
3679*3737Shx147065 	case WL_REASSOCIATE:
3680*3737Shx147065 	case WL_ASSOCIAT:
3681*3737Shx147065 		mutex_exit(&pcwl_p->pcwl_glock);
3682*3737Shx147065 		if (pcwl_p->pcwl_connect_timeout_id != 0) {
3683*3737Shx147065 			(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
3684*3737Shx147065 			pcwl_p->pcwl_connect_timeout_id = 0;
3685*3737Shx147065 		}
3686*3737Shx147065 		mutex_enter(&pcwl_p->pcwl_glock);
3687*3737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
3688*3737Shx147065 			ret = (int)WL_HW_ERROR;
3689*3737Shx147065 			break;
3690*3737Shx147065 		}
3691*3737Shx147065 		if (ret = pcwl_config_rf(pcwl_p)) {
3692*3737Shx147065 			ret = (int)WL_HW_ERROR;
3693*3737Shx147065 			break;
3694*3737Shx147065 		}
3695*3737Shx147065 		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
3696*3737Shx147065 			ret = (int)WL_HW_ERROR;
3697*3737Shx147065 			break;
3698*3737Shx147065 		}
3699*3737Shx147065 		PCWLDBG((CE_NOTE, "associate"));
3700*3737Shx147065 		break;
3701*3737Shx147065 	default:
3702*3737Shx147065 		break;
3703*3737Shx147065 	}
3704*3737Shx147065 	mutex_exit(&pcwl_p->pcwl_glock);
3705*3737Shx147065 	if ((cmd == WLAN_SET_PARAM) && (connect)) {
3706*3737Shx147065 		(void) pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0);
3707*3737Shx147065 		if (pcwl_p->pcwl_connect_timeout_id != 0) {
3708*3737Shx147065 			(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
3709*3737Shx147065 			pcwl_p->pcwl_connect_timeout_id = 0;
3710*3737Shx147065 		}
3711*3737Shx147065 		pcwl_p->pcwl_connect_timeout_id = timeout(pcwl_connect_timeout,
3712*3737Shx147065 		    pcwl_p, 2 * drv_usectohz(1000000));
3713*3737Shx147065 	}
3714*3737Shx147065 	return (ret);
3715*3737Shx147065 }
3716*3737Shx147065 
3717*3737Shx147065 static void
3718*3737Shx147065 pcwl_wlan_ioctl(pcwl_maci_t *pcwl_p, queue_t *wq, mblk_t *mp, uint32_t cmd)
3719*3737Shx147065 {
3720*3737Shx147065 
3721*3737Shx147065 	struct	iocblk	*iocp = (struct iocblk *)mp->b_rptr;
3722*3737Shx147065 	wldp_t 	*infp;
3723*3737Shx147065 	uint32_t len, ret;
3724*3737Shx147065 	mblk_t		*mp1;
3725*3737Shx147065 
3726*3737Shx147065 	/*
3727*3737Shx147065 	 * sanity check
3728*3737Shx147065 	 */
3729*3737Shx147065 	if (iocp->ioc_count == 0 || !(mp1 = mp->b_cont)) {
3730*3737Shx147065 		miocnak(wq, mp, 0, EINVAL);
3731*3737Shx147065 		return;
3732*3737Shx147065 	}
3733*3737Shx147065 
3734*3737Shx147065 	/*
3735*3737Shx147065 	 * assuming single data block
3736*3737Shx147065 	 */
3737*3737Shx147065 	if (mp1->b_cont) {
3738*3737Shx147065 		freemsg(mp1->b_cont);
3739*3737Shx147065 		mp1->b_cont = NULL;
3740*3737Shx147065 	}
3741*3737Shx147065 
3742*3737Shx147065 	/*
3743*3737Shx147065 	 * we will overwrite everything
3744*3737Shx147065 	 */
3745*3737Shx147065 	mp1->b_wptr = mp1->b_rptr;
3746*3737Shx147065 
3747*3737Shx147065 	infp = (wldp_t *)mp1->b_rptr;
3748*3737Shx147065 	PCWLDBG((CE_NOTE, "pcwl: wldp->length=0x%x\n", infp->wldp_length));
3749*3737Shx147065 	PCWLDBG((CE_NOTE, "pcwl: wldp->type =:%s\n",
3750*3737Shx147065 	    infp->wldp_type == NET_802_11 ? "NET_802_11" : "Unknown"));
3751*3737Shx147065 	PCWLDBG((CE_NOTE, "pcwl: wldp->id=0x%x\n", infp->wldp_id));
3752*3737Shx147065 	PCWLDBG((CE_NOTE, "pcwl: wldp->result=0x%x\n", infp->wldp_result));
3753*3737Shx147065 
3754*3737Shx147065 	ret = pcwl_getset(mp1, pcwl_p, cmd);
3755*3737Shx147065 	len = msgdsize(mp1);
3756*3737Shx147065 	PCWLDBG((CE_CONT, "pcwl: ioctl message length = %d\n", len));
3757*3737Shx147065 	miocack(wq, mp, len, ret);
3758*3737Shx147065 
3759*3737Shx147065 }
3760*3737Shx147065 
3761*3737Shx147065 
3762*3737Shx147065 static void
3763*3737Shx147065 pcwl_ioctl(void *arg, queue_t *wq, mblk_t *mp)
3764*3737Shx147065 {
3765*3737Shx147065 	struct iocblk *iocp;
3766*3737Shx147065 	uint32_t cmd, ret;
3767*3737Shx147065 	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
3768*3737Shx147065 	boolean_t need_privilege = B_TRUE;
3769*3737Shx147065 
3770*3737Shx147065 	/*
3771*3737Shx147065 	 * Validate the command before bothering with the mutexen ...
3772*3737Shx147065 	 */
3773*3737Shx147065 	iocp = (struct iocblk *)mp->b_rptr;
3774*3737Shx147065 	iocp->ioc_error = 0;
3775*3737Shx147065 	cmd = iocp->ioc_cmd;
3776*3737Shx147065 	switch (cmd) {
3777*3737Shx147065 	default:
3778*3737Shx147065 		PCWLDBG((CE_CONT, "pcwl_ioctl: unknown cmd 0x%x", cmd));
3779*3737Shx147065 		miocnak(wq, mp, 0, EINVAL);
3780*3737Shx147065 		return;
3781*3737Shx147065 	case WLAN_GET_PARAM:
3782*3737Shx147065 		need_privilege = B_TRUE;
3783*3737Shx147065 		break;
3784*3737Shx147065 	case WLAN_SET_PARAM:
3785*3737Shx147065 	case WLAN_COMMAND:
3786*3737Shx147065 		break;
3787*3737Shx147065 	}
3788*3737Shx147065 	/*
3789*3737Shx147065 	 * Check net_config privilege
3790*3737Shx147065 	 */
3791*3737Shx147065 	if (need_privilege) {
3792*3737Shx147065 		if (ret = secpolicy_net_config(iocp->ioc_cr, B_FALSE)) {
3793*3737Shx147065 			miocnak(wq, mp, 0, ret);
3794*3737Shx147065 			return;
3795*3737Shx147065 		}
3796*3737Shx147065 	}
3797*3737Shx147065 	pcwl_wlan_ioctl(pcwl_p, wq, mp, cmd);
3798*3737Shx147065 }
3799