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