xref: /illumos-gate/usr/src/uts/common/io/iwi/ipw2200.c (revision 76939ce0e89c177cb48bf98208fd3d831eb283d5)
1bb5e3b2fSeh146360 /*
2bb5e3b2fSeh146360  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3bb5e3b2fSeh146360  * Use is subject to license terms.
4bb5e3b2fSeh146360  */
5bb5e3b2fSeh146360 
6bb5e3b2fSeh146360 /*
7bb5e3b2fSeh146360  * Copyright (c) 2004, 2005
8bb5e3b2fSeh146360  *      Damien Bergamini <damien.bergamini@free.fr>. All rights reserved.
9bb5e3b2fSeh146360  *
10bb5e3b2fSeh146360  * Redistribution and use in source and binary forms, with or without
11bb5e3b2fSeh146360  * modification, are permitted provided that the following conditions
12bb5e3b2fSeh146360  * are met:
13bb5e3b2fSeh146360  * 1. Redistributions of source code must retain the above copyright
14bb5e3b2fSeh146360  *    notice unmodified, this list of conditions, and the following
15bb5e3b2fSeh146360  *    disclaimer.
16bb5e3b2fSeh146360  * 2. Redistributions in binary form must reproduce the above copyright
17bb5e3b2fSeh146360  *    notice, this list of conditions and the following disclaimer in the
18bb5e3b2fSeh146360  *    documentation and/or other materials provided with the distribution.
19bb5e3b2fSeh146360  *
20bb5e3b2fSeh146360  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21bb5e3b2fSeh146360  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22bb5e3b2fSeh146360  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23bb5e3b2fSeh146360  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24bb5e3b2fSeh146360  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25bb5e3b2fSeh146360  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26bb5e3b2fSeh146360  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27bb5e3b2fSeh146360  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28bb5e3b2fSeh146360  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29bb5e3b2fSeh146360  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30bb5e3b2fSeh146360  * SUCH DAMAGE.
31bb5e3b2fSeh146360  */
32bb5e3b2fSeh146360 
33bb5e3b2fSeh146360 #pragma ident	"%Z%%M%	%I%	%E% SMI"
34bb5e3b2fSeh146360 
35bb5e3b2fSeh146360 #include <sys/types.h>
36bb5e3b2fSeh146360 #include <sys/byteorder.h>
37bb5e3b2fSeh146360 #include <sys/conf.h>
38bb5e3b2fSeh146360 #include <sys/cmn_err.h>
39bb5e3b2fSeh146360 #include <sys/stat.h>
40bb5e3b2fSeh146360 #include <sys/ddi.h>
41bb5e3b2fSeh146360 #include <sys/sunddi.h>
42bb5e3b2fSeh146360 #include <sys/strsubr.h>
43bb5e3b2fSeh146360 #include <sys/ethernet.h>
44bb5e3b2fSeh146360 #include <inet/common.h>
45bb5e3b2fSeh146360 #include <inet/nd.h>
46bb5e3b2fSeh146360 #include <inet/mi.h>
47bb5e3b2fSeh146360 #include <sys/note.h>
48bb5e3b2fSeh146360 #include <sys/stream.h>
49bb5e3b2fSeh146360 #include <sys/strsun.h>
50bb5e3b2fSeh146360 #include <sys/modctl.h>
51bb5e3b2fSeh146360 #include <sys/devops.h>
52bb5e3b2fSeh146360 #include <sys/dlpi.h>
53bb5e3b2fSeh146360 #include <sys/mac.h>
54bb5e3b2fSeh146360 #include <sys/mac_wifi.h>
55bb5e3b2fSeh146360 #include <sys/varargs.h>
56bb5e3b2fSeh146360 #include <sys/pci.h>
57bb5e3b2fSeh146360 #include <sys/policy.h>
58bb5e3b2fSeh146360 #include <sys/random.h>
59bb5e3b2fSeh146360 
60bb5e3b2fSeh146360 #include "ipw2200.h"
61bb5e3b2fSeh146360 #include "ipw2200_impl.h"
62bb5e3b2fSeh146360 #include <inet/wifi_ioctl.h>
63bb5e3b2fSeh146360 
64bb5e3b2fSeh146360 /*
65bb5e3b2fSeh146360  * minimal size reserved in tx-ring
66bb5e3b2fSeh146360  */
67bb5e3b2fSeh146360 #define	IPW2200_TX_RING_MIN	(8)
68bb5e3b2fSeh146360 #define	IPW2200_TXBUF_SIZE	(IEEE80211_MAX_LEN)
69bb5e3b2fSeh146360 #define	IPW2200_RXBUF_SIZE	(4096)
70bb5e3b2fSeh146360 
71bb5e3b2fSeh146360 static void  *ipw2200_ssp = NULL;
72bb5e3b2fSeh146360 static char ipw2200_ident[] = IPW2200_DRV_DESC " " IPW2200_DRV_REV;
73bb5e3b2fSeh146360 
74bb5e3b2fSeh146360 /*
75bb5e3b2fSeh146360  * PIO access attributor for registers
76bb5e3b2fSeh146360  */
77bb5e3b2fSeh146360 static ddi_device_acc_attr_t ipw2200_csr_accattr = {
78bb5e3b2fSeh146360 	DDI_DEVICE_ATTR_V0,
79bb5e3b2fSeh146360 	DDI_STRUCTURE_LE_ACC,
80bb5e3b2fSeh146360 	DDI_STRICTORDER_ACC
81bb5e3b2fSeh146360 };
82bb5e3b2fSeh146360 
83bb5e3b2fSeh146360 /*
84bb5e3b2fSeh146360  * DMA access attributor for descriptors
85bb5e3b2fSeh146360  */
86bb5e3b2fSeh146360 static ddi_device_acc_attr_t ipw2200_dma_accattr = {
87bb5e3b2fSeh146360 	DDI_DEVICE_ATTR_V0,
88bb5e3b2fSeh146360 	DDI_NEVERSWAP_ACC,
89bb5e3b2fSeh146360 	DDI_STRICTORDER_ACC
90bb5e3b2fSeh146360 };
91bb5e3b2fSeh146360 
92bb5e3b2fSeh146360 /*
93bb5e3b2fSeh146360  * Describes the chip's DMA engine
94bb5e3b2fSeh146360  */
95bb5e3b2fSeh146360 static ddi_dma_attr_t ipw2200_dma_attr = {
96bb5e3b2fSeh146360 	DMA_ATTR_V0,		/* version */
97bb5e3b2fSeh146360 	0x0000000000000000ULL,  /* addr_lo */
98bb5e3b2fSeh146360 	0x00000000ffffffffULL,  /* addr_hi */
99bb5e3b2fSeh146360 	0x00000000ffffffffULL,  /* counter */
100bb5e3b2fSeh146360 	0x0000000000000004ULL,  /* alignment */
101bb5e3b2fSeh146360 	0xfff,			/* burst */
102bb5e3b2fSeh146360 	1,			/* min xfer */
103bb5e3b2fSeh146360 	0x00000000ffffffffULL,  /* max xfer */
104bb5e3b2fSeh146360 	0x00000000ffffffffULL,  /* seg boud */
105bb5e3b2fSeh146360 	1,			/* s/g list */
106bb5e3b2fSeh146360 	1,			/* granularity */
107bb5e3b2fSeh146360 	0			/* flags */
108bb5e3b2fSeh146360 };
109bb5e3b2fSeh146360 
110bb5e3b2fSeh146360 static uint8_t ipw2200_broadcast_addr[] = {
111bb5e3b2fSeh146360 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
112bb5e3b2fSeh146360 };
113bb5e3b2fSeh146360 static const struct ieee80211_rateset ipw2200_rateset_11a = { 8,
114bb5e3b2fSeh146360 	{12, 18, 24, 36, 48, 72, 96, 108}
115bb5e3b2fSeh146360 };
116bb5e3b2fSeh146360 static const struct ieee80211_rateset ipw2200_rateset_11b = { 4,
117bb5e3b2fSeh146360 	{2, 4, 11, 22}
118bb5e3b2fSeh146360 };
119bb5e3b2fSeh146360 static const struct ieee80211_rateset ipw2200_rateset_11g = { 12,
120bb5e3b2fSeh146360 	{2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108}
121bb5e3b2fSeh146360 };
122bb5e3b2fSeh146360 
123bb5e3b2fSeh146360 /*
124bb5e3b2fSeh146360  * Used by multi function thread
125bb5e3b2fSeh146360  */
126bb5e3b2fSeh146360 extern pri_t minclsyspri;
127bb5e3b2fSeh146360 
128bb5e3b2fSeh146360 /*
129bb5e3b2fSeh146360  * ipw2200 specific hardware operations
130bb5e3b2fSeh146360  */
131bb5e3b2fSeh146360 static void	ipw2200_hwconf_get(struct ipw2200_softc *sc);
132bb5e3b2fSeh146360 static int	ipw2200_chip_reset(struct ipw2200_softc *sc);
133bb5e3b2fSeh146360 static void	ipw2200_master_stop(struct ipw2200_softc *sc);
134bb5e3b2fSeh146360 static void	ipw2200_stop(struct ipw2200_softc *sc);
135bb5e3b2fSeh146360 static int	ipw2200_config(struct ipw2200_softc *sc);
136bb5e3b2fSeh146360 static int	ipw2200_cmd(struct ipw2200_softc *sc,
137bb5e3b2fSeh146360     uint32_t type, void *buf, size_t len, int async);
138bb5e3b2fSeh146360 static void	ipw2200_ring_hwsetup(struct ipw2200_softc *sc);
139bb5e3b2fSeh146360 static int	ipw2200_ring_alloc(struct ipw2200_softc *sc);
140bb5e3b2fSeh146360 static void	ipw2200_ring_free(struct ipw2200_softc *sc);
141bb5e3b2fSeh146360 static void	ipw2200_ring_reset(struct ipw2200_softc *sc);
142bb5e3b2fSeh146360 static int	ipw2200_ring_init(struct ipw2200_softc *sc);
143bb5e3b2fSeh146360 
144bb5e3b2fSeh146360 /*
145bb5e3b2fSeh146360  * GLD specific operations
146bb5e3b2fSeh146360  */
147bb5e3b2fSeh146360 static int	ipw2200_m_stat(void *arg, uint_t stat, uint64_t *val);
148bb5e3b2fSeh146360 static int	ipw2200_m_start(void *arg);
149bb5e3b2fSeh146360 static void	ipw2200_m_stop(void *arg);
150bb5e3b2fSeh146360 static int	ipw2200_m_unicst(void *arg, const uint8_t *macaddr);
151bb5e3b2fSeh146360 static int	ipw2200_m_multicst(void *arg, boolean_t add, const uint8_t *m);
152bb5e3b2fSeh146360 static int	ipw2200_m_promisc(void *arg, boolean_t on);
153bb5e3b2fSeh146360 static void	ipw2200_m_ioctl(void *arg, queue_t *wq, mblk_t *mp);
154bb5e3b2fSeh146360 static mblk_t  *ipw2200_m_tx(void *arg, mblk_t *mp);
155bb5e3b2fSeh146360 
156bb5e3b2fSeh146360 /*
157bb5e3b2fSeh146360  * Interrupt and Data transferring operations
158bb5e3b2fSeh146360  */
159bb5e3b2fSeh146360 static uint_t	ipw2200_intr(caddr_t arg);
160bb5e3b2fSeh146360 static int	ipw2200_send(struct ieee80211com *ic, mblk_t *mp, uint8_t type);
161bb5e3b2fSeh146360 static void	ipw2200_rcv_frame(struct ipw2200_softc *sc,
162bb5e3b2fSeh146360     struct ipw2200_frame *frame);
163bb5e3b2fSeh146360 static void	ipw2200_rcv_notif(struct ipw2200_softc *sc,
164bb5e3b2fSeh146360     struct ipw2200_notif *notif);
165bb5e3b2fSeh146360 
166bb5e3b2fSeh146360 /*
167bb5e3b2fSeh146360  * WiFi specific operations
168bb5e3b2fSeh146360  */
169bb5e3b2fSeh146360 static int	ipw2200_newstate(struct ieee80211com *ic,
170bb5e3b2fSeh146360     enum ieee80211_state state, int arg);
171bb5e3b2fSeh146360 static void	ipw2200_thread(struct ipw2200_softc *sc);
172bb5e3b2fSeh146360 
173bb5e3b2fSeh146360 /*
174bb5e3b2fSeh146360  * IOCTL Handler
175bb5e3b2fSeh146360  */
176bb5e3b2fSeh146360 static int	ipw2200_ioctl(struct ipw2200_softc *sc, queue_t *q, mblk_t *m);
177bb5e3b2fSeh146360 static int	ipw2200_getset(struct ipw2200_softc *sc,
178bb5e3b2fSeh146360     mblk_t *m, uint32_t cmd, boolean_t *need_net80211);
179bb5e3b2fSeh146360 static int	iwi_wificfg_radio(struct ipw2200_softc *sc,
180bb5e3b2fSeh146360     uint32_t cmd,  wldp_t *outfp);
181bb5e3b2fSeh146360 static int	iwi_wificfg_desrates(wldp_t *outfp);
182bb5e3b2fSeh146360 
183bb5e3b2fSeh146360 /*
184bb5e3b2fSeh146360  * Mac Call Back entries
185bb5e3b2fSeh146360  */
186bb5e3b2fSeh146360 mac_callbacks_t	ipw2200_m_callbacks = {
187bb5e3b2fSeh146360 	MC_IOCTL,
188bb5e3b2fSeh146360 	ipw2200_m_stat,
189bb5e3b2fSeh146360 	ipw2200_m_start,
190bb5e3b2fSeh146360 	ipw2200_m_stop,
191bb5e3b2fSeh146360 	ipw2200_m_promisc,
192bb5e3b2fSeh146360 	ipw2200_m_multicst,
193bb5e3b2fSeh146360 	ipw2200_m_unicst,
194bb5e3b2fSeh146360 	ipw2200_m_tx,
195bb5e3b2fSeh146360 	NULL,
196bb5e3b2fSeh146360 	ipw2200_m_ioctl
197bb5e3b2fSeh146360 };
198bb5e3b2fSeh146360 
199bb5e3b2fSeh146360 /*
200bb5e3b2fSeh146360  * DEBUG Facility
201bb5e3b2fSeh146360  */
202bb5e3b2fSeh146360 #define		MAX_MSG		(128)
203bb5e3b2fSeh146360 uint32_t	ipw2200_debug = 0;
204bb5e3b2fSeh146360 /*
205bb5e3b2fSeh146360  * supported debug marks are:
206bb5e3b2fSeh146360  *	| IPW2200_DBG_CSR
207bb5e3b2fSeh146360  *	| IPW2200_DBG_TABLE
208bb5e3b2fSeh146360  *	| IPW2200_DBG_HWCAP
209bb5e3b2fSeh146360  *	| IPW2200_DBG_TX
210bb5e3b2fSeh146360  *	| IPW2200_DBG_INIT
211bb5e3b2fSeh146360  *	| IPW2200_DBG_FW
212bb5e3b2fSeh146360  *	| IPW2200_DBG_NOTIF
213bb5e3b2fSeh146360  *	| IPW2200_DBG_SCAN
214bb5e3b2fSeh146360  *	| IPW2200_DBG_IOCTL
215bb5e3b2fSeh146360  *	| IPW2200_DBG_RING
216bb5e3b2fSeh146360  *	| IPW2200_DBG_INT
217bb5e3b2fSeh146360  *	| IPW2200_DBG_RX
218bb5e3b2fSeh146360  *	| IPW2200_DBG_DMA
219bb5e3b2fSeh146360  *	| IPW2200_DBG_GLD
220bb5e3b2fSeh146360  *	| IPW2200_DBG_WIFI
221bb5e3b2fSeh146360  *	| IPW2200_DBG_SOFTINT
222bb5e3b2fSeh146360  */
223bb5e3b2fSeh146360 
224bb5e3b2fSeh146360 /*
225bb5e3b2fSeh146360  * Global tunning parameter to work around unknown hardware issues
226bb5e3b2fSeh146360  */
227bb5e3b2fSeh146360 static uint32_t delay_config_stable	= 100000;	/* 100ms */
228bb5e3b2fSeh146360 static uint32_t delay_fatal_recover	= 100000 * 20;	/* 2s */
229bb5e3b2fSeh146360 static uint32_t delay_aux_thread	= 100000;	/* 100ms */
230bb5e3b2fSeh146360 
231bb5e3b2fSeh146360 #define	IEEE80211_IS_CHAN_2GHZ(_c) \
232bb5e3b2fSeh146360 	(((_c)->ich_flags & IEEE80211_CHAN_2GHZ) != 0)
233bb5e3b2fSeh146360 #define	IEEE80211_IS_CHAN_5GHZ(_c) \
234bb5e3b2fSeh146360 	(((_c)->ich_flags & IEEE80211_CHAN_5GHZ) != 0)
235bb5e3b2fSeh146360 #define	isset(a, i)	((a)[(i)/NBBY] & (1 << ((i)%NBBY)))
236bb5e3b2fSeh146360 
237bb5e3b2fSeh146360 void
238bb5e3b2fSeh146360 ipw2200_dbg(dev_info_t *dip, int level, const char *fmt, ...)
239bb5e3b2fSeh146360 {
240bb5e3b2fSeh146360 	va_list	ap;
241bb5e3b2fSeh146360 	char    buf[MAX_MSG];
242bb5e3b2fSeh146360 	int	instance;
243bb5e3b2fSeh146360 
244bb5e3b2fSeh146360 	va_start(ap, fmt);
245bb5e3b2fSeh146360 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
246bb5e3b2fSeh146360 	va_end(ap);
247bb5e3b2fSeh146360 
248bb5e3b2fSeh146360 	if (dip) {
249bb5e3b2fSeh146360 		instance = ddi_get_instance(dip);
250bb5e3b2fSeh146360 		cmn_err(level, "%s%d: %s", IPW2200_DRV_NAME, instance, buf);
251bb5e3b2fSeh146360 	} else
252bb5e3b2fSeh146360 		cmn_err(level, "%s: %s", IPW2200_DRV_NAME, buf);
253bb5e3b2fSeh146360 
254bb5e3b2fSeh146360 }
255bb5e3b2fSeh146360 
256bb5e3b2fSeh146360 /*
257bb5e3b2fSeh146360  * Device operations
258bb5e3b2fSeh146360  */
259bb5e3b2fSeh146360 int
260bb5e3b2fSeh146360 ipw2200_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
261bb5e3b2fSeh146360 {
262bb5e3b2fSeh146360 	struct ipw2200_softc	*sc;
263bb5e3b2fSeh146360 	ddi_acc_handle_t	cfgh;
264bb5e3b2fSeh146360 	caddr_t			regs;
265bb5e3b2fSeh146360 	struct ieee80211com	*ic;
266bb5e3b2fSeh146360 	int			instance, err, i;
267bb5e3b2fSeh146360 	char			strbuf[32];
268bb5e3b2fSeh146360 	wifi_data_t		wd = { 0 };
269bb5e3b2fSeh146360 	mac_register_t		*macp;
270bb5e3b2fSeh146360 	uint16_t		vendor, device, subven, subdev;
271bb5e3b2fSeh146360 
272bb5e3b2fSeh146360 	if (cmd != DDI_ATTACH) {
273bb5e3b2fSeh146360 		err = DDI_FAILURE;
274bb5e3b2fSeh146360 		goto fail1;
275bb5e3b2fSeh146360 	}
276bb5e3b2fSeh146360 
277bb5e3b2fSeh146360 	instance = ddi_get_instance(dip);
278bb5e3b2fSeh146360 	err = ddi_soft_state_zalloc(ipw2200_ssp, instance);
279bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
280bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
281bb5e3b2fSeh146360 		    "ipw2200_attach(): unable to allocate soft state\n"));
282bb5e3b2fSeh146360 		goto fail1;
283bb5e3b2fSeh146360 	}
284bb5e3b2fSeh146360 	sc = ddi_get_soft_state(ipw2200_ssp, instance);
285bb5e3b2fSeh146360 	sc->sc_dip = dip;
286bb5e3b2fSeh146360 
287bb5e3b2fSeh146360 	/*
288bb5e3b2fSeh146360 	 * Map config spaces register to read the vendor id, device id, sub
289bb5e3b2fSeh146360 	 * vendor id, and sub device id.
290bb5e3b2fSeh146360 	 */
291bb5e3b2fSeh146360 	err = ddi_regs_map_setup(dip, IPW2200_PCI_CFG_RNUM, &regs,
292bb5e3b2fSeh146360 	    0, 0, &ipw2200_csr_accattr, &cfgh);
293bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
294bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
295bb5e3b2fSeh146360 		    "ipw2200_attach(): unable to map spaces regs\n"));
296bb5e3b2fSeh146360 		goto fail2;
297bb5e3b2fSeh146360 	}
298bb5e3b2fSeh146360 	ddi_put8(cfgh, (uint8_t *)(regs + 0x41), 0);
299bb5e3b2fSeh146360 	vendor = ddi_get16(cfgh, (uint16_t *)(regs + PCI_CONF_VENID));
300bb5e3b2fSeh146360 	device = ddi_get16(cfgh, (uint16_t *)(regs + PCI_CONF_DEVID));
301bb5e3b2fSeh146360 	subven = ddi_get16(cfgh, (uint16_t *)(regs + PCI_CONF_SUBVENID));
302bb5e3b2fSeh146360 	subdev = ddi_get16(cfgh, (uint16_t *)(regs + PCI_CONF_SUBSYSID));
303bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
304bb5e3b2fSeh146360 	    "ipw2200_attach(): vendor = 0x%04x, devic = 0x%04x,"
305bb5e3b2fSeh146360 	    "subversion = 0x%04x, subdev = 0x%04x",
306bb5e3b2fSeh146360 	    vendor, device, subven, subdev));
307bb5e3b2fSeh146360 	ddi_regs_map_free(&cfgh);
308bb5e3b2fSeh146360 
309bb5e3b2fSeh146360 	/*
310bb5e3b2fSeh146360 	 * Map operating registers
311bb5e3b2fSeh146360 	 */
312bb5e3b2fSeh146360 	err = ddi_regs_map_setup(dip, IPW2200_PCI_CSR_RNUM, &sc->sc_regs,
313bb5e3b2fSeh146360 	    0, 0, &ipw2200_csr_accattr, &sc->sc_ioh);
314bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
315bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
316bb5e3b2fSeh146360 		    "ipw2200_attach(): ddi_regs_map_setup() failed\n"));
317bb5e3b2fSeh146360 		goto fail2;
318bb5e3b2fSeh146360 	}
319bb5e3b2fSeh146360 
320bb5e3b2fSeh146360 	/*
321bb5e3b2fSeh146360 	 * Reset the chip
322bb5e3b2fSeh146360 	 */
323bb5e3b2fSeh146360 	err = ipw2200_chip_reset(sc);
324bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
325bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
326bb5e3b2fSeh146360 		    "ipw2200_attach(): ipw2200_chip_reset() failed\n"));
327bb5e3b2fSeh146360 		goto fail3;
328bb5e3b2fSeh146360 	}
329bb5e3b2fSeh146360 
330bb5e3b2fSeh146360 	/*
331bb5e3b2fSeh146360 	 * Get the hardware configuration, including the MAC address
332bb5e3b2fSeh146360 	 * Then, init all the rings needed.
333bb5e3b2fSeh146360 	 */
334bb5e3b2fSeh146360 	ipw2200_hwconf_get(sc);
335bb5e3b2fSeh146360 	err = ipw2200_ring_init(sc);
336bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
337bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
338bb5e3b2fSeh146360 		    "ipw2200_attach(): ipw2200_ring_init() failed\n"));
339bb5e3b2fSeh146360 		goto fail3;
340bb5e3b2fSeh146360 	}
341bb5e3b2fSeh146360 
342bb5e3b2fSeh146360 	/*
343bb5e3b2fSeh146360 	 * Initialize mutexs and condvars
344bb5e3b2fSeh146360 	 */
345bb5e3b2fSeh146360 	err = ddi_get_iblock_cookie(dip, 0, &sc->sc_iblk);
346bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
347bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
348bb5e3b2fSeh146360 		    "ipw2200_attach(): ddi_get_iblock_cookie() failed\n"));
349bb5e3b2fSeh146360 		goto fail4;
350bb5e3b2fSeh146360 	}
351bb5e3b2fSeh146360 
352bb5e3b2fSeh146360 	/*
353bb5e3b2fSeh146360 	 * interrupt lock
354bb5e3b2fSeh146360 	 */
355bb5e3b2fSeh146360 	mutex_init(&sc->sc_ilock, "intr-lock", MUTEX_DRIVER,
356bb5e3b2fSeh146360 	    (void *) sc->sc_iblk);
357bb5e3b2fSeh146360 	cv_init(&sc->sc_fw_cond, "firmware-ok", CV_DRIVER, NULL);
358bb5e3b2fSeh146360 	cv_init(&sc->sc_cmd_status_cond, "cmd-status-ring", CV_DRIVER, NULL);
359bb5e3b2fSeh146360 
360bb5e3b2fSeh146360 	/*
361bb5e3b2fSeh146360 	 * command ring lock
362bb5e3b2fSeh146360 	 */
363bb5e3b2fSeh146360 	mutex_init(&sc->sc_cmd_lock, "cmd-ring", MUTEX_DRIVER,
364bb5e3b2fSeh146360 	    (void *) sc->sc_iblk);
365bb5e3b2fSeh146360 	cv_init(&sc->sc_cmd_cond, "cmd-ring", CV_DRIVER, NULL);
366bb5e3b2fSeh146360 
367bb5e3b2fSeh146360 	/*
368bb5e3b2fSeh146360 	 * tx ring lock
369bb5e3b2fSeh146360 	 */
370bb5e3b2fSeh146360 	mutex_init(&sc->sc_tx_lock, "tx-ring", MUTEX_DRIVER,
371bb5e3b2fSeh146360 	    (void *) sc->sc_iblk);
372bb5e3b2fSeh146360 
373bb5e3b2fSeh146360 	/*
374bb5e3b2fSeh146360 	 * multi-function lock, may acquire this during interrupt
375bb5e3b2fSeh146360 	 */
376bb5e3b2fSeh146360 	mutex_init(&sc->sc_mflock, "function-lock", MUTEX_DRIVER,
377bb5e3b2fSeh146360 	    (void *) sc->sc_iblk);
378bb5e3b2fSeh146360 	cv_init(&sc->sc_mfthread_cv, NULL, CV_DRIVER, NULL);
379bb5e3b2fSeh146360 	sc->sc_mf_thread = NULL;
380bb5e3b2fSeh146360 	sc->sc_mfthread_switch = 0;
381bb5e3b2fSeh146360 
382bb5e3b2fSeh146360 	/*
383bb5e3b2fSeh146360 	 * Initialize the WiFi part, which will be used by generic layer
384bb5e3b2fSeh146360 	 * Need support more features in the furture, such as
385bb5e3b2fSeh146360 	 * IEEE80211_C_IBSS
386bb5e3b2fSeh146360 	 */
387bb5e3b2fSeh146360 	ic = &sc->sc_ic;
388bb5e3b2fSeh146360 	ic->ic_phytype  = IEEE80211_T_OFDM;
389bb5e3b2fSeh146360 	ic->ic_opmode   = IEEE80211_M_STA;
390bb5e3b2fSeh146360 	ic->ic_state    = IEEE80211_S_INIT;
391bb5e3b2fSeh146360 	ic->ic_maxrssi  = 100; /* experimental number */
392bb5e3b2fSeh146360 	ic->ic_caps = IEEE80211_C_SHPREAMBLE | IEEE80211_C_TXPMGT
393bb5e3b2fSeh146360 	    | IEEE80211_C_PMGT | IEEE80211_C_WEP;
394bb5e3b2fSeh146360 
395bb5e3b2fSeh146360 	/*
396bb5e3b2fSeh146360 	 * set mac addr
397bb5e3b2fSeh146360 	 */
398bb5e3b2fSeh146360 	IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->sc_macaddr);
399bb5e3b2fSeh146360 
400bb5e3b2fSeh146360 	/*
401bb5e3b2fSeh146360 	 * set supported .11a rates and channel - (2915ABG only)
402bb5e3b2fSeh146360 	 */
403bb5e3b2fSeh146360 	if (device >= 0x4223) {
404bb5e3b2fSeh146360 		/* .11a rates */
405bb5e3b2fSeh146360 		ic->ic_sup_rates[IEEE80211_MODE_11A] = ipw2200_rateset_11a;
406bb5e3b2fSeh146360 		/* .11a channels */
407bb5e3b2fSeh146360 		for (i = 36; i <= 64; i += 4) {
408bb5e3b2fSeh146360 			ic->ic_sup_channels[i].ich_freq =
409bb5e3b2fSeh146360 			    ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
410bb5e3b2fSeh146360 			ic->ic_sup_channels[i].ich_flags = /* CHAN_A */
411bb5e3b2fSeh146360 			    IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM;
412bb5e3b2fSeh146360 		}
413bb5e3b2fSeh146360 		for (i = 149; i <= 165; i += 4) {
414bb5e3b2fSeh146360 			ic->ic_sup_channels[i].ich_freq =
415bb5e3b2fSeh146360 			    ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
416bb5e3b2fSeh146360 			ic->ic_sup_channels[i].ich_flags = /* CHAN_A */
417bb5e3b2fSeh146360 			    IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM;
418bb5e3b2fSeh146360 		}
419bb5e3b2fSeh146360 	}
420bb5e3b2fSeh146360 
421bb5e3b2fSeh146360 	/*
422bb5e3b2fSeh146360 	 * set supported .11b and .11g rates
423bb5e3b2fSeh146360 	 */
424bb5e3b2fSeh146360 	ic->ic_sup_rates[IEEE80211_MODE_11B] = ipw2200_rateset_11b;
425bb5e3b2fSeh146360 	ic->ic_sup_rates[IEEE80211_MODE_11G] = ipw2200_rateset_11g;
426bb5e3b2fSeh146360 
427bb5e3b2fSeh146360 	/*
428bb5e3b2fSeh146360 	 * set supported .11b and .11g channels(1 through 14)
429bb5e3b2fSeh146360 	 */
430bb5e3b2fSeh146360 	for (i = 1; i < 14; i++) {
431bb5e3b2fSeh146360 		ic->ic_sup_channels[i].ich_freq  =
432bb5e3b2fSeh146360 		    ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
433bb5e3b2fSeh146360 		ic->ic_sup_channels[i].ich_flags =
434bb5e3b2fSeh146360 		    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
435bb5e3b2fSeh146360 		    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
436bb5e3b2fSeh146360 	}
437bb5e3b2fSeh146360 
438bb5e3b2fSeh146360 	/*
439bb5e3b2fSeh146360 	 * IBSS channal undefined for now
440bb5e3b2fSeh146360 	 */
441bb5e3b2fSeh146360 	ic->ic_ibss_chan = &ic->ic_sup_channels[0];
442bb5e3b2fSeh146360 	ic->ic_xmit = ipw2200_send;
443bb5e3b2fSeh146360 
444bb5e3b2fSeh146360 	/*
445bb5e3b2fSeh146360 	 * init generic layer, then override state transition machine
446bb5e3b2fSeh146360 	 */
447bb5e3b2fSeh146360 	ieee80211_attach(ic);
448bb5e3b2fSeh146360 
449bb5e3b2fSeh146360 	/*
450bb5e3b2fSeh146360 	 * Override 80211 default routines
451bb5e3b2fSeh146360 	 */
452bb5e3b2fSeh146360 	ieee80211_media_init(ic); /* initial the node table and bss */
453bb5e3b2fSeh146360 	sc->sc_newstate = ic->ic_newstate;
454bb5e3b2fSeh146360 	ic->ic_newstate = ipw2200_newstate;
455bb5e3b2fSeh146360 	ic->ic_def_txkey = 0;
456bb5e3b2fSeh146360 	sc->sc_authmode = IEEE80211_AUTH_OPEN;
457bb5e3b2fSeh146360 
458bb5e3b2fSeh146360 	/*
459bb5e3b2fSeh146360 	 * Add the interrupt handler
460bb5e3b2fSeh146360 	 */
461bb5e3b2fSeh146360 	err = ddi_add_intr(dip, 0, &sc->sc_iblk, NULL,
462bb5e3b2fSeh146360 	    ipw2200_intr, (caddr_t)sc);
463bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
464bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
465bb5e3b2fSeh146360 		    "ipw2200_attach(): ddi_add_intr() failed\n"));
466bb5e3b2fSeh146360 		goto fail5;
467bb5e3b2fSeh146360 	}
468bb5e3b2fSeh146360 
469bb5e3b2fSeh146360 	/*
470bb5e3b2fSeh146360 	 * Initialize pointer to device specific functions
471bb5e3b2fSeh146360 	 */
472bb5e3b2fSeh146360 	wd.wd_secalloc = WIFI_SEC_NONE;
473bb5e3b2fSeh146360 	wd.wd_opmode = ic->ic_opmode;
474bb5e3b2fSeh146360 	IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_macaddr);
475bb5e3b2fSeh146360 
476bb5e3b2fSeh146360 	macp = mac_alloc(MAC_VERSION);
477bb5e3b2fSeh146360 	if (err != 0) {
478bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
479bb5e3b2fSeh146360 		    "ipw2200_attach(): mac_alloc() failed\n"));
480bb5e3b2fSeh146360 		goto fail6;
481bb5e3b2fSeh146360 	}
482bb5e3b2fSeh146360 
483bb5e3b2fSeh146360 	macp->m_type_ident	= MAC_PLUGIN_IDENT_WIFI;
484bb5e3b2fSeh146360 	macp->m_driver		= sc;
485bb5e3b2fSeh146360 	macp->m_dip		= dip;
486bb5e3b2fSeh146360 	macp->m_src_addr	= ic->ic_macaddr;
487bb5e3b2fSeh146360 	macp->m_callbacks	= &ipw2200_m_callbacks;
488bb5e3b2fSeh146360 	macp->m_min_sdu		= 0;
489bb5e3b2fSeh146360 	macp->m_max_sdu		= IEEE80211_MTU;
490bb5e3b2fSeh146360 	macp->m_pdata		= &wd;
491bb5e3b2fSeh146360 	macp->m_pdata_size	= sizeof (wd);
492bb5e3b2fSeh146360 
493bb5e3b2fSeh146360 	/*
494bb5e3b2fSeh146360 	 * Register the macp to mac
495bb5e3b2fSeh146360 	 */
496bb5e3b2fSeh146360 	err = mac_register(macp, &ic->ic_mach);
497bb5e3b2fSeh146360 	mac_free(macp);
498bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
499bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
500bb5e3b2fSeh146360 		    "ipw2200_attach(): mac_register() failed\n"));
501bb5e3b2fSeh146360 		goto fail6;
502bb5e3b2fSeh146360 	}
503bb5e3b2fSeh146360 
504bb5e3b2fSeh146360 	/*
505bb5e3b2fSeh146360 	 * Create minor node of type DDI_NT_NET_WIFI
506bb5e3b2fSeh146360 	 */
507bb5e3b2fSeh146360 	(void) snprintf(strbuf, sizeof (strbuf), "%s%d",
508bb5e3b2fSeh146360 	    IPW2200_DRV_NAME, instance);
509bb5e3b2fSeh146360 	err = ddi_create_minor_node(dip, strbuf, S_IFCHR,
510bb5e3b2fSeh146360 	    instance + 1, DDI_NT_NET_WIFI, 0);
511bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
512bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
513bb5e3b2fSeh146360 		    "ipw2200_attach(): ddi_create_minor_node() failed\n"));
514bb5e3b2fSeh146360 
515bb5e3b2fSeh146360 	/*
516bb5e3b2fSeh146360 	 * Cache firmware will always be true
517bb5e3b2fSeh146360 	 */
518bb5e3b2fSeh146360 	(void) ipw2200_cache_firmware(sc);
519bb5e3b2fSeh146360 
520bb5e3b2fSeh146360 	/*
521bb5e3b2fSeh146360 	 * Notify link is down now
522bb5e3b2fSeh146360 	 */
523bb5e3b2fSeh146360 	mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
524bb5e3b2fSeh146360 
525bb5e3b2fSeh146360 	/*
526bb5e3b2fSeh146360 	 * Create the mf thread to handle the link status,
527bb5e3b2fSeh146360 	 * recovery fatal error, etc.
528bb5e3b2fSeh146360 	 */
529bb5e3b2fSeh146360 	sc->sc_mfthread_switch = 1;
530bb5e3b2fSeh146360 	if (sc->sc_mf_thread == NULL)
531bb5e3b2fSeh146360 		sc->sc_mf_thread = thread_create((caddr_t)NULL, 0,
532bb5e3b2fSeh146360 		    ipw2200_thread, sc, 0, &p0, TS_RUN, minclsyspri);
533bb5e3b2fSeh146360 
534bb5e3b2fSeh146360 	return (DDI_SUCCESS);
535bb5e3b2fSeh146360 
536bb5e3b2fSeh146360 fail6:
537bb5e3b2fSeh146360 	ddi_remove_intr(dip, 0, sc->sc_iblk);
538bb5e3b2fSeh146360 fail5:
539bb5e3b2fSeh146360 	ieee80211_detach(ic);
540bb5e3b2fSeh146360 
541bb5e3b2fSeh146360 	mutex_destroy(&sc->sc_ilock);
542bb5e3b2fSeh146360 	mutex_destroy(&sc->sc_cmd_lock);
543bb5e3b2fSeh146360 	mutex_destroy(&sc->sc_tx_lock);
544bb5e3b2fSeh146360 	mutex_destroy(&sc->sc_mflock);
545bb5e3b2fSeh146360 	cv_destroy(&sc->sc_fw_cond);
546bb5e3b2fSeh146360 	cv_destroy(&sc->sc_cmd_status_cond);
547bb5e3b2fSeh146360 	cv_destroy(&sc->sc_cmd_cond);
548bb5e3b2fSeh146360 	cv_destroy(&sc->sc_mfthread_cv);
549bb5e3b2fSeh146360 fail4:
550bb5e3b2fSeh146360 	ipw2200_ring_free(sc);
551bb5e3b2fSeh146360 fail3:
552bb5e3b2fSeh146360 	ddi_regs_map_free(&sc->sc_ioh);
553bb5e3b2fSeh146360 fail2:
554bb5e3b2fSeh146360 	ddi_soft_state_free(ipw2200_ssp, instance);
555bb5e3b2fSeh146360 fail1:
556bb5e3b2fSeh146360 	return (err);
557bb5e3b2fSeh146360 }
558bb5e3b2fSeh146360 
559bb5e3b2fSeh146360 
560bb5e3b2fSeh146360 int
561bb5e3b2fSeh146360 ipw2200_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
562bb5e3b2fSeh146360 {
563bb5e3b2fSeh146360 	struct ipw2200_softc	*sc =
564bb5e3b2fSeh146360 	    ddi_get_soft_state(ipw2200_ssp, ddi_get_instance(dip));
565bb5e3b2fSeh146360 	int			err;
566bb5e3b2fSeh146360 	ASSERT(sc != NULL);
567bb5e3b2fSeh146360 
568bb5e3b2fSeh146360 	if (cmd != DDI_DETACH)
569bb5e3b2fSeh146360 		return (DDI_FAILURE);
570bb5e3b2fSeh146360 
571bb5e3b2fSeh146360 	ipw2200_stop(sc);
572bb5e3b2fSeh146360 
573bb5e3b2fSeh146360 	/*
574bb5e3b2fSeh146360 	 * Destroy the mf_thread
575bb5e3b2fSeh146360 	 */
576bb5e3b2fSeh146360 	mutex_enter(&sc->sc_mflock);
577bb5e3b2fSeh146360 	sc->sc_mfthread_switch = 0;
578bb5e3b2fSeh146360 	while (sc->sc_mf_thread != NULL) {
579bb5e3b2fSeh146360 		if (cv_wait_sig(&sc->sc_mfthread_cv, &sc->sc_mflock) == 0)
580bb5e3b2fSeh146360 			break;
581bb5e3b2fSeh146360 	}
582bb5e3b2fSeh146360 	mutex_exit(&sc->sc_mflock);
583bb5e3b2fSeh146360 
584bb5e3b2fSeh146360 	/*
585bb5e3b2fSeh146360 	 * Unregister from the MAC layer subsystem
586bb5e3b2fSeh146360 	 */
587bb5e3b2fSeh146360 	err = mac_unregister(sc->sc_ic.ic_mach);
588bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
589bb5e3b2fSeh146360 		return (err);
590bb5e3b2fSeh146360 
591bb5e3b2fSeh146360 	ddi_remove_intr(dip, IPW2200_PCI_INTR_NUM, sc->sc_iblk);
592bb5e3b2fSeh146360 
593bb5e3b2fSeh146360 	mutex_destroy(&sc->sc_ilock);
594bb5e3b2fSeh146360 	mutex_destroy(&sc->sc_cmd_lock);
595bb5e3b2fSeh146360 	mutex_destroy(&sc->sc_tx_lock);
596bb5e3b2fSeh146360 	mutex_destroy(&sc->sc_mflock);
597bb5e3b2fSeh146360 	cv_destroy(&sc->sc_fw_cond);
598bb5e3b2fSeh146360 	cv_destroy(&sc->sc_cmd_status_cond);
599bb5e3b2fSeh146360 	cv_destroy(&sc->sc_cmd_cond);
600bb5e3b2fSeh146360 	cv_destroy(&sc->sc_mfthread_cv);
601bb5e3b2fSeh146360 
602bb5e3b2fSeh146360 	/*
603bb5e3b2fSeh146360 	 * Detach ieee80211
604bb5e3b2fSeh146360 	 */
605bb5e3b2fSeh146360 	ieee80211_detach(&sc->sc_ic);
606bb5e3b2fSeh146360 
607bb5e3b2fSeh146360 	(void) ipw2200_free_firmware(sc);
608bb5e3b2fSeh146360 	ipw2200_ring_free(sc);
609bb5e3b2fSeh146360 
610bb5e3b2fSeh146360 	ddi_regs_map_free(&sc->sc_ioh);
611bb5e3b2fSeh146360 	ddi_remove_minor_node(dip, NULL);
612bb5e3b2fSeh146360 	ddi_soft_state_free(ipw2200_ssp, ddi_get_instance(dip));
613bb5e3b2fSeh146360 
614bb5e3b2fSeh146360 	return (DDI_SUCCESS);
615bb5e3b2fSeh146360 }
616bb5e3b2fSeh146360 
617*76939ce0Seh146360 /* ARGSUSED */
618*76939ce0Seh146360 int
619*76939ce0Seh146360 ipw2200_reset(dev_info_t *dip, ddi_reset_cmd_t cmd)
620*76939ce0Seh146360 {
621*76939ce0Seh146360 	struct ipw2200_softc	*sc =
622*76939ce0Seh146360 	    ddi_get_soft_state(ipw2200_ssp, ddi_get_instance(dip));
623*76939ce0Seh146360 	ASSERT(sc != NULL);
624*76939ce0Seh146360 
625*76939ce0Seh146360 	ipw2200_stop(sc);
626*76939ce0Seh146360 
627*76939ce0Seh146360 	return (DDI_SUCCESS);
628*76939ce0Seh146360 }
629*76939ce0Seh146360 
630bb5e3b2fSeh146360 static void
631bb5e3b2fSeh146360 ipw2200_stop(struct ipw2200_softc *sc)
632bb5e3b2fSeh146360 {
633bb5e3b2fSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
634bb5e3b2fSeh146360 
635bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_HWCAP, (sc->sc_dip, CE_CONT,
636bb5e3b2fSeh146360 	    "ipw2200_stop(): enter\n"));
637bb5e3b2fSeh146360 
638bb5e3b2fSeh146360 	ipw2200_master_stop(sc);
639bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_SW_RESET);
640bb5e3b2fSeh146360 
641bb5e3b2fSeh146360 	/*
642bb5e3b2fSeh146360 	 * Reset ring
643bb5e3b2fSeh146360 	 */
644bb5e3b2fSeh146360 	ipw2200_ring_reset(sc);
645bb5e3b2fSeh146360 
646bb5e3b2fSeh146360 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
647bb5e3b2fSeh146360 	sc->sc_flags &= ~IPW2200_FLAG_SCANNING;
648bb5e3b2fSeh146360 
649bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_HWCAP, (sc->sc_dip, CE_CONT,
650bb5e3b2fSeh146360 	    "ipw2200_stop(): exit\n"));
651bb5e3b2fSeh146360 }
652bb5e3b2fSeh146360 
653bb5e3b2fSeh146360 static int
654bb5e3b2fSeh146360 ipw2200_config(struct ipw2200_softc *sc)
655bb5e3b2fSeh146360 {
656bb5e3b2fSeh146360 	struct ieee80211com		*ic = &sc->sc_ic;
657bb5e3b2fSeh146360 	struct ipw2200_configuration	cfg;
658bb5e3b2fSeh146360 	uint32_t			data;
659bb5e3b2fSeh146360 	struct ipw2200_txpower		pwr;
660bb5e3b2fSeh146360 	struct ipw2200_rateset		rs;
661bb5e3b2fSeh146360 	struct ipw2200_wep_key		wkey;
662bb5e3b2fSeh146360 	int				err, i;
663bb5e3b2fSeh146360 
664bb5e3b2fSeh146360 	/*
665bb5e3b2fSeh146360 	 * Set the IBSS mode channel: Tx power
666bb5e3b2fSeh146360 	 */
667bb5e3b2fSeh146360 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
668bb5e3b2fSeh146360 		pwr.mode  = IPW2200_MODE_11B;
669bb5e3b2fSeh146360 		pwr.nchan = 11;
670bb5e3b2fSeh146360 		for (i = 0; i < pwr.nchan; i++) {
671bb5e3b2fSeh146360 			pwr.chan[i].chan  = i + 1;
672bb5e3b2fSeh146360 			pwr.chan[i].power = IPW2200_TXPOWER_MAX;
673bb5e3b2fSeh146360 		}
674bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
675bb5e3b2fSeh146360 		    "ipw2200_config(): Setting .11b channels Tx power\n"));
676bb5e3b2fSeh146360 		err = ipw2200_cmd(sc, IPW2200_CMD_SET_TX_POWER,
677bb5e3b2fSeh146360 		    &pwr, sizeof (pwr), 0);
678bb5e3b2fSeh146360 		if (err != DDI_SUCCESS)
679bb5e3b2fSeh146360 			return (err);
680bb5e3b2fSeh146360 
681bb5e3b2fSeh146360 		pwr.mode  = IPW2200_MODE_11G;
682bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
683bb5e3b2fSeh146360 		    "ipw2200_config(): Setting .11g channels Tx power\n"));
684bb5e3b2fSeh146360 		err = ipw2200_cmd(sc, IPW2200_CMD_SET_TX_POWER,
685bb5e3b2fSeh146360 		    &pwr, sizeof (pwr), 0);
686bb5e3b2fSeh146360 		if (err != DDI_SUCCESS)
687bb5e3b2fSeh146360 			return (err);
688bb5e3b2fSeh146360 	}
689bb5e3b2fSeh146360 
690bb5e3b2fSeh146360 	/*
691bb5e3b2fSeh146360 	 * Set MAC address
692bb5e3b2fSeh146360 	 */
693bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
694bb5e3b2fSeh146360 	    "ipw2200_config(): Setting MAC address to "
695bb5e3b2fSeh146360 	    "%02x:%02x:%02x:%02x:%02x:%02x\n",
696bb5e3b2fSeh146360 	    ic->ic_macaddr[0], ic->ic_macaddr[1], ic->ic_macaddr[2],
697bb5e3b2fSeh146360 	    ic->ic_macaddr[3], ic->ic_macaddr[4], ic->ic_macaddr[5]));
698bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_MAC_ADDRESS, ic->ic_macaddr,
699bb5e3b2fSeh146360 	    IEEE80211_ADDR_LEN, 0);
700bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
701bb5e3b2fSeh146360 		return (err);
702bb5e3b2fSeh146360 
703bb5e3b2fSeh146360 	/*
704bb5e3b2fSeh146360 	 * Set basic system config settings: configuration(capabilities)
705bb5e3b2fSeh146360 	 */
706bb5e3b2fSeh146360 	(void) memset(&cfg, 0, sizeof (cfg));
707bb5e3b2fSeh146360 	cfg.bluetooth_coexistence	 = 1;
708bb5e3b2fSeh146360 	cfg.multicast_enabled		 = 1;
709bb5e3b2fSeh146360 	cfg.answer_pbreq		 = 1;
710bb5e3b2fSeh146360 	cfg.noise_reported		 = 1;
711bb5e3b2fSeh146360 
712bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
713bb5e3b2fSeh146360 	    "ipw2200_config(): Configuring adapter\n"));
714bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_CONFIG,
715bb5e3b2fSeh146360 	    &cfg, sizeof (cfg), 0);
716bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
717bb5e3b2fSeh146360 		return (err);
718bb5e3b2fSeh146360 
719bb5e3b2fSeh146360 	/*
720bb5e3b2fSeh146360 	 * Set power mode
721bb5e3b2fSeh146360 	 */
722bb5e3b2fSeh146360 	data = LE_32(IPW2200_POWER_MODE_CAM);
723bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
724bb5e3b2fSeh146360 	    "ipw2200_config(): Setting power mode to %u\n", LE_32(data)));
725bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_POWER_MODE,
726bb5e3b2fSeh146360 	    &data, sizeof (data), 0);
727bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
728bb5e3b2fSeh146360 		return (err);
729bb5e3b2fSeh146360 
730bb5e3b2fSeh146360 	/*
731bb5e3b2fSeh146360 	 * Set supported rates
732bb5e3b2fSeh146360 	 */
733bb5e3b2fSeh146360 	rs.mode = IPW2200_MODE_11G;
734bb5e3b2fSeh146360 	rs.type = IPW2200_RATESET_TYPE_SUPPORTED;
735bb5e3b2fSeh146360 	rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11G].ir_nrates;
736bb5e3b2fSeh146360 	(void) memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11G].ir_rates,
737bb5e3b2fSeh146360 	    rs.nrates);
738bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
739bb5e3b2fSeh146360 	    "ipw2200_config(): Setting .11g supported rates(%u)\n", rs.nrates));
740bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 0);
741bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
742bb5e3b2fSeh146360 		return (err);
743bb5e3b2fSeh146360 
744bb5e3b2fSeh146360 	rs.mode = IPW2200_MODE_11A;
745bb5e3b2fSeh146360 	rs.type = IPW2200_RATESET_TYPE_SUPPORTED;
746bb5e3b2fSeh146360 	rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11A].ir_nrates;
747bb5e3b2fSeh146360 	(void) memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11A].ir_rates,
748bb5e3b2fSeh146360 	    rs.nrates);
749bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
750bb5e3b2fSeh146360 	    "ipw2200_config(): Setting .11a supported rates(%u)\n", rs.nrates));
751bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 0);
752bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
753bb5e3b2fSeh146360 		return (err);
754bb5e3b2fSeh146360 
755bb5e3b2fSeh146360 	/*
756bb5e3b2fSeh146360 	 * Set RTS(request-to-send) threshold
757bb5e3b2fSeh146360 	 */
758bb5e3b2fSeh146360 	data = LE_32(ic->ic_rtsthreshold);
759bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
760bb5e3b2fSeh146360 	    "ipw2200_config(): Setting RTS threshold to %u\n", LE_32(data)));
761bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_RTS_THRESHOLD, &data,
762bb5e3b2fSeh146360 	    sizeof (data), 0);
763bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
764bb5e3b2fSeh146360 		return (err);
765bb5e3b2fSeh146360 
766bb5e3b2fSeh146360 	/*
767bb5e3b2fSeh146360 	 * Set fragmentation threshold
768bb5e3b2fSeh146360 	 */
769bb5e3b2fSeh146360 	data = LE_32(ic->ic_fragthreshold);
770bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
771bb5e3b2fSeh146360 	    "ipw2200_config(): Setting fragmentation threshold to %u\n",
772bb5e3b2fSeh146360 	    LE_32(data)));
773bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_FRAG_THRESHOLD, &data,
774bb5e3b2fSeh146360 	    sizeof (data), 0);
775bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
776bb5e3b2fSeh146360 		return (err);
777bb5e3b2fSeh146360 
778bb5e3b2fSeh146360 	/*
779bb5e3b2fSeh146360 	 * Set desired ESSID if we have
780bb5e3b2fSeh146360 	 */
781bb5e3b2fSeh146360 	if (ic->ic_des_esslen != 0) {
782bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
783bb5e3b2fSeh146360 		    "ipw2200_config(): Setting desired ESSID to "
784bb5e3b2fSeh146360 		    "(%u),%c%c%c%c%c%c%c%c\n",
785bb5e3b2fSeh146360 		    ic->ic_des_esslen,
786bb5e3b2fSeh146360 		    ic->ic_des_essid[0], ic->ic_des_essid[1],
787bb5e3b2fSeh146360 		    ic->ic_des_essid[2], ic->ic_des_essid[3],
788bb5e3b2fSeh146360 		    ic->ic_des_essid[4], ic->ic_des_essid[5],
789bb5e3b2fSeh146360 		    ic->ic_des_essid[6], ic->ic_des_essid[7]));
790bb5e3b2fSeh146360 		err = ipw2200_cmd(sc, IPW2200_CMD_SET_ESSID, ic->ic_des_essid,
791bb5e3b2fSeh146360 		    ic->ic_des_esslen, 0);
792bb5e3b2fSeh146360 		if (err != DDI_SUCCESS)
793bb5e3b2fSeh146360 			return (err);
794bb5e3b2fSeh146360 	}
795bb5e3b2fSeh146360 
796bb5e3b2fSeh146360 	/*
797bb5e3b2fSeh146360 	 * Set WEP initial vector(random seed)
798bb5e3b2fSeh146360 	 */
799bb5e3b2fSeh146360 	(void) random_get_pseudo_bytes((uint8_t *)&data, sizeof (data));
800bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
801bb5e3b2fSeh146360 	    "ipw2200_config(): Setting initialization vector to %u\n",
802bb5e3b2fSeh146360 	    LE_32(data)));
803bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_IV, &data, sizeof (data), 0);
804bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
805bb5e3b2fSeh146360 		return (err);
806bb5e3b2fSeh146360 
807bb5e3b2fSeh146360 	/*
808bb5e3b2fSeh146360 	 * Set WEP if any
809bb5e3b2fSeh146360 	 */
810bb5e3b2fSeh146360 	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
811bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
812bb5e3b2fSeh146360 		    "ipw2200_config(): Setting Wep Key\n", LE_32(data)));
813bb5e3b2fSeh146360 		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
814bb5e3b2fSeh146360 			wkey.cmd = IPW2200_WEP_KEY_CMD_SETKEY;
815bb5e3b2fSeh146360 			wkey.idx = (uint8_t)i;
816bb5e3b2fSeh146360 			wkey.len = ic->ic_nw_keys[i].wk_keylen;
817bb5e3b2fSeh146360 			(void) memset(wkey.key, 0, sizeof (wkey.key));
818bb5e3b2fSeh146360 			if (ic->ic_nw_keys[i].wk_keylen)
819bb5e3b2fSeh146360 				(void) memcpy(wkey.key,
820bb5e3b2fSeh146360 				    ic->ic_nw_keys[i].wk_key,
821bb5e3b2fSeh146360 				    ic->ic_nw_keys[i].wk_keylen);
822bb5e3b2fSeh146360 			err = ipw2200_cmd(sc, IPW2200_CMD_SET_WEP_KEY,
823bb5e3b2fSeh146360 			    &wkey, sizeof (wkey), 0);
824bb5e3b2fSeh146360 			if (err != DDI_SUCCESS)
825bb5e3b2fSeh146360 				return (err);
826bb5e3b2fSeh146360 		}
827bb5e3b2fSeh146360 	}
828bb5e3b2fSeh146360 
829bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
830bb5e3b2fSeh146360 	    "ipw2200_config(): Enabling adapter\n"));
831bb5e3b2fSeh146360 
832bb5e3b2fSeh146360 	return (ipw2200_cmd(sc, IPW2200_CMD_ENABLE, NULL, 0, 0));
833bb5e3b2fSeh146360 }
834bb5e3b2fSeh146360 
835bb5e3b2fSeh146360 static int
836bb5e3b2fSeh146360 ipw2200_cmd(struct ipw2200_softc *sc,
837bb5e3b2fSeh146360 	uint32_t type, void *buf, size_t len, int async)
838bb5e3b2fSeh146360 {
839bb5e3b2fSeh146360 	struct		ipw2200_cmd_desc *cmd;
840bb5e3b2fSeh146360 	clock_t		clk;
841bb5e3b2fSeh146360 	uint32_t	idx;
842bb5e3b2fSeh146360 
843bb5e3b2fSeh146360 	mutex_enter(&sc->sc_cmd_lock);
844bb5e3b2fSeh146360 	while (sc->sc_cmd_free < 1)
845bb5e3b2fSeh146360 		cv_wait(&sc->sc_cmd_cond, &sc->sc_cmd_lock);
846bb5e3b2fSeh146360 
847bb5e3b2fSeh146360 	idx = sc->sc_cmd_cur;
848bb5e3b2fSeh146360 	cmd = &sc->sc_cmdsc[idx];
849bb5e3b2fSeh146360 	(void) memset(cmd, 0, sizeof (*cmd));
850bb5e3b2fSeh146360 
851bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT,
852bb5e3b2fSeh146360 	    "ipw2200_cmd(): cmd-cur=%d\n", idx));
853bb5e3b2fSeh146360 
854bb5e3b2fSeh146360 	cmd->hdr.type   = IPW2200_HDR_TYPE_COMMAND;
855bb5e3b2fSeh146360 	cmd->hdr.flags  = IPW2200_HDR_FLAG_IRQ;
856bb5e3b2fSeh146360 	cmd->type	= (uint8_t)type;
857bb5e3b2fSeh146360 	if (len == 0 || buf == NULL)
858bb5e3b2fSeh146360 		cmd->len  = 0;
859bb5e3b2fSeh146360 	else {
860bb5e3b2fSeh146360 		cmd->len  = (uint8_t)len;
861bb5e3b2fSeh146360 		(void) memcpy(cmd->data, buf, len);
862bb5e3b2fSeh146360 	}
863bb5e3b2fSeh146360 	sc->sc_done[idx] = 0;
864bb5e3b2fSeh146360 
865bb5e3b2fSeh146360 	/*
866bb5e3b2fSeh146360 	 * DMA sync
867bb5e3b2fSeh146360 	 */
868bb5e3b2fSeh146360 	(void) ddi_dma_sync(sc->sc_dma_cmdsc.dr_hnd,
869bb5e3b2fSeh146360 	    idx * sizeof (struct ipw2200_cmd_desc),
870bb5e3b2fSeh146360 	    sizeof (struct ipw2200_cmd_desc), DDI_DMA_SYNC_FORDEV);
871bb5e3b2fSeh146360 
872bb5e3b2fSeh146360 	sc->sc_cmd_cur = RING_FORWARD(sc->sc_cmd_cur, 1, IPW2200_CMD_RING_SIZE);
873bb5e3b2fSeh146360 	sc->sc_cmd_free--;
874bb5e3b2fSeh146360 
875bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_CMD_WRITE_INDEX, sc->sc_cmd_cur);
876bb5e3b2fSeh146360 
877bb5e3b2fSeh146360 	mutex_exit(&sc->sc_cmd_lock);
878bb5e3b2fSeh146360 
879bb5e3b2fSeh146360 	if (async)
880bb5e3b2fSeh146360 		goto out;
881bb5e3b2fSeh146360 
882bb5e3b2fSeh146360 	/*
883bb5e3b2fSeh146360 	 * Wait for command done
884bb5e3b2fSeh146360 	 */
885bb5e3b2fSeh146360 	mutex_enter(&sc->sc_ilock);
886bb5e3b2fSeh146360 	while (sc->sc_done[idx] == 0) {
887bb5e3b2fSeh146360 		/* pending */
888bb5e3b2fSeh146360 		clk = ddi_get_lbolt() + drv_usectohz(5000000);  /* 5 second */
889bb5e3b2fSeh146360 		if (cv_timedwait(&sc->sc_cmd_status_cond, &sc->sc_ilock, clk)
890bb5e3b2fSeh146360 		    < 0)
891bb5e3b2fSeh146360 			break;
892bb5e3b2fSeh146360 	}
893bb5e3b2fSeh146360 	mutex_exit(&sc->sc_ilock);
894bb5e3b2fSeh146360 
895bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT,
896bb5e3b2fSeh146360 	    "ipw2200_cmd(): cmd-done=%s\n", sc->sc_done[idx] ? "yes" : "no"));
897bb5e3b2fSeh146360 
898bb5e3b2fSeh146360 	if (sc->sc_done[idx] == 0)
899bb5e3b2fSeh146360 		return (DDI_FAILURE);
900bb5e3b2fSeh146360 
901bb5e3b2fSeh146360 out:
902bb5e3b2fSeh146360 	return (DDI_SUCCESS);
903bb5e3b2fSeh146360 }
904bb5e3b2fSeh146360 
905bb5e3b2fSeh146360 /*
906bb5e3b2fSeh146360  * If init failed, it will call stop internally. Therefore, it's unnecessary
907bb5e3b2fSeh146360  * to call ipw2200_stop() when this subroutine is failed. Otherwise, it may
908bb5e3b2fSeh146360  * be called twice.
909bb5e3b2fSeh146360  */
910bb5e3b2fSeh146360 int
911bb5e3b2fSeh146360 ipw2200_init(struct ipw2200_softc *sc)
912bb5e3b2fSeh146360 {
913bb5e3b2fSeh146360 	int	err;
914bb5e3b2fSeh146360 
915bb5e3b2fSeh146360 	/*
916bb5e3b2fSeh146360 	 * No firmware is available, failed
917bb5e3b2fSeh146360 	 */
918bb5e3b2fSeh146360 	if (!(sc->sc_flags & IPW2200_FLAG_FW_CACHED)) {
919bb5e3b2fSeh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
920bb5e3b2fSeh146360 		    "ipw2200_init(): no firmware is available\n"));
921bb5e3b2fSeh146360 		return (DDI_FAILURE); /* return directly */
922bb5e3b2fSeh146360 	}
923bb5e3b2fSeh146360 
924bb5e3b2fSeh146360 	ipw2200_stop(sc);
925bb5e3b2fSeh146360 
926bb5e3b2fSeh146360 	err = ipw2200_chip_reset(sc);
927bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
928bb5e3b2fSeh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
929bb5e3b2fSeh146360 		    "ipw2200_init(): could not reset adapter\n"));
930bb5e3b2fSeh146360 		goto fail;
931bb5e3b2fSeh146360 	}
932bb5e3b2fSeh146360 
933bb5e3b2fSeh146360 	/*
934bb5e3b2fSeh146360 	 * Load boot code
935bb5e3b2fSeh146360 	 */
936bb5e3b2fSeh146360 	err = ipw2200_load_fw(sc, sc->sc_fw.boot_base, sc->sc_fw.boot_size);
937bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
938bb5e3b2fSeh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
939bb5e3b2fSeh146360 		    "ipw2200_init(): could not load boot code\n"));
940bb5e3b2fSeh146360 		goto fail;
941bb5e3b2fSeh146360 	}
942bb5e3b2fSeh146360 
943bb5e3b2fSeh146360 	/*
944bb5e3b2fSeh146360 	 * Load boot microcode
945bb5e3b2fSeh146360 	 */
946bb5e3b2fSeh146360 	err = ipw2200_load_uc(sc, sc->sc_fw.uc_base, sc->sc_fw.uc_size);
947bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
948bb5e3b2fSeh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
949bb5e3b2fSeh146360 		    "ipw2200_init(): could not load microcode\n"));
950bb5e3b2fSeh146360 		goto fail;
951bb5e3b2fSeh146360 	}
952bb5e3b2fSeh146360 
953bb5e3b2fSeh146360 	ipw2200_master_stop(sc);
954bb5e3b2fSeh146360 	ipw2200_ring_hwsetup(sc);
955bb5e3b2fSeh146360 
956bb5e3b2fSeh146360 	/*
957bb5e3b2fSeh146360 	 * Load firmware
958bb5e3b2fSeh146360 	 */
959bb5e3b2fSeh146360 	err = ipw2200_load_fw(sc, sc->sc_fw.fw_base, sc->sc_fw.fw_size);
960bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
961bb5e3b2fSeh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
962bb5e3b2fSeh146360 		    "ipw2200_init(): could not load firmware\n"));
963bb5e3b2fSeh146360 		goto fail;
964bb5e3b2fSeh146360 	}
965bb5e3b2fSeh146360 
966bb5e3b2fSeh146360 	sc->sc_flags |= IPW2200_FLAG_FW_INITED;
967bb5e3b2fSeh146360 
968bb5e3b2fSeh146360 	/*
969bb5e3b2fSeh146360 	 * Hardware will be enabled after configuration
970bb5e3b2fSeh146360 	 */
971bb5e3b2fSeh146360 	err = ipw2200_config(sc);
972bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
973bb5e3b2fSeh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
974bb5e3b2fSeh146360 		    "ipw2200_init(): device configuration failed\n"));
975bb5e3b2fSeh146360 		goto fail;
976bb5e3b2fSeh146360 	}
977bb5e3b2fSeh146360 
978bb5e3b2fSeh146360 	/*
979bb5e3b2fSeh146360 	 * workround to prevent too many h/w error.
980bb5e3b2fSeh146360 	 * delay for a while till h/w is stable.
981bb5e3b2fSeh146360 	 */
982bb5e3b2fSeh146360 	delay(drv_usectohz(delay_config_stable));
983bb5e3b2fSeh146360 
984bb5e3b2fSeh146360 	return (DDI_SUCCESS); /* return successfully */
985bb5e3b2fSeh146360 fail:
986bb5e3b2fSeh146360 	ipw2200_stop(sc);
987bb5e3b2fSeh146360 	return (err);
988bb5e3b2fSeh146360 }
989bb5e3b2fSeh146360 
990bb5e3b2fSeh146360 /*
991bb5e3b2fSeh146360  * get hardware configurations from EEPROM embedded within PRO/2200
992bb5e3b2fSeh146360  */
993bb5e3b2fSeh146360 static void
994bb5e3b2fSeh146360 ipw2200_hwconf_get(struct ipw2200_softc *sc)
995bb5e3b2fSeh146360 {
996bb5e3b2fSeh146360 	int		i;
997bb5e3b2fSeh146360 	uint16_t	val;
998bb5e3b2fSeh146360 
999bb5e3b2fSeh146360 	/*
1000bb5e3b2fSeh146360 	 * Get mac address
1001bb5e3b2fSeh146360 	 */
1002bb5e3b2fSeh146360 	i = 0;
1003bb5e3b2fSeh146360 	val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 0);
1004bb5e3b2fSeh146360 	sc->sc_macaddr[i++] = val >> 8;
1005bb5e3b2fSeh146360 	sc->sc_macaddr[i++] = val & 0xff;
1006bb5e3b2fSeh146360 	val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 1);
1007bb5e3b2fSeh146360 	sc->sc_macaddr[i++] = val >> 8;
1008bb5e3b2fSeh146360 	sc->sc_macaddr[i++] = val & 0xff;
1009bb5e3b2fSeh146360 	val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 2);
1010bb5e3b2fSeh146360 	sc->sc_macaddr[i++] = val >> 8;
1011bb5e3b2fSeh146360 	sc->sc_macaddr[i++] = val & 0xff;
1012bb5e3b2fSeh146360 
1013bb5e3b2fSeh146360 	/*
1014bb5e3b2fSeh146360 	 * formatted MAC address string
1015bb5e3b2fSeh146360 	 */
1016bb5e3b2fSeh146360 	(void) snprintf(sc->sc_macstr, sizeof (sc->sc_macstr),
1017bb5e3b2fSeh146360 	    "%02x:%02x:%02x:%02x:%02x:%02x",
1018bb5e3b2fSeh146360 	    sc->sc_macaddr[0], sc->sc_macaddr[1],
1019bb5e3b2fSeh146360 	    sc->sc_macaddr[2], sc->sc_macaddr[3],
1020bb5e3b2fSeh146360 	    sc->sc_macaddr[4], sc->sc_macaddr[5]);
1021bb5e3b2fSeh146360 
1022bb5e3b2fSeh146360 }
1023bb5e3b2fSeh146360 
1024bb5e3b2fSeh146360 /*
1025bb5e3b2fSeh146360  * all ipw2200 interrupts will be masked by this routine
1026bb5e3b2fSeh146360  */
1027bb5e3b2fSeh146360 static void
1028bb5e3b2fSeh146360 ipw2200_master_stop(struct ipw2200_softc *sc)
1029bb5e3b2fSeh146360 {
1030bb5e3b2fSeh146360 	int	ntries;
1031bb5e3b2fSeh146360 
1032bb5e3b2fSeh146360 	/*
1033bb5e3b2fSeh146360 	 * disable interrupts
1034bb5e3b2fSeh146360 	 */
1035bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, 0);
1036bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_STOP_MASTER);
1037bb5e3b2fSeh146360 
1038bb5e3b2fSeh146360 	/*
1039bb5e3b2fSeh146360 	 * wait long enough to ensure hardware stop successfully.
1040bb5e3b2fSeh146360 	 */
1041bb5e3b2fSeh146360 	for (ntries = 0; ntries < 500; ntries++) {
1042bb5e3b2fSeh146360 		if (ipw2200_csr_get32(sc, IPW2200_CSR_RST) &
1043bb5e3b2fSeh146360 		    IPW2200_RST_MASTER_DISABLED)
1044bb5e3b2fSeh146360 			break;
1045bb5e3b2fSeh146360 		/* wait for a while */
1046bb5e3b2fSeh146360 		drv_usecwait(100);
1047bb5e3b2fSeh146360 	}
1048bb5e3b2fSeh146360 	if (ntries == 500)
1049bb5e3b2fSeh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
1050bb5e3b2fSeh146360 		    "ipw2200_master_stop(): timeout\n"));
1051bb5e3b2fSeh146360 
1052bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_RST,
1053bb5e3b2fSeh146360 	    IPW2200_RST_PRINCETON_RESET |
1054bb5e3b2fSeh146360 	    ipw2200_csr_get32(sc, IPW2200_CSR_RST));
1055bb5e3b2fSeh146360 
1056bb5e3b2fSeh146360 	sc->sc_flags &= ~IPW2200_FLAG_FW_INITED;
1057bb5e3b2fSeh146360 }
1058bb5e3b2fSeh146360 
1059bb5e3b2fSeh146360 /*
1060bb5e3b2fSeh146360  * all ipw2200 interrupts will be masked by this routine
1061bb5e3b2fSeh146360  */
1062bb5e3b2fSeh146360 static int
1063bb5e3b2fSeh146360 ipw2200_chip_reset(struct ipw2200_softc *sc)
1064bb5e3b2fSeh146360 {
1065bb5e3b2fSeh146360 	uint32_t	tmp;
1066bb5e3b2fSeh146360 	int		ntries, i;
1067bb5e3b2fSeh146360 
1068bb5e3b2fSeh146360 	ipw2200_master_stop(sc);
1069bb5e3b2fSeh146360 
1070bb5e3b2fSeh146360 	/*
1071bb5e3b2fSeh146360 	 * Move adapter to DO state
1072bb5e3b2fSeh146360 	 */
1073bb5e3b2fSeh146360 	tmp = ipw2200_csr_get32(sc, IPW2200_CSR_CTL);
1074bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_CTL, tmp | IPW2200_CTL_INIT);
1075bb5e3b2fSeh146360 
1076bb5e3b2fSeh146360 	/*
1077bb5e3b2fSeh146360 	 * Initialize Phase-Locked Level (PLL)
1078bb5e3b2fSeh146360 	 */
1079bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_READ_INT, IPW2200_READ_INT_INIT_HOST);
1080bb5e3b2fSeh146360 
1081bb5e3b2fSeh146360 	/*
1082bb5e3b2fSeh146360 	 * Wait for clock stabilization
1083bb5e3b2fSeh146360 	 */
1084bb5e3b2fSeh146360 	for (ntries = 0; ntries < 1000; ntries++) {
1085bb5e3b2fSeh146360 		if (ipw2200_csr_get32(sc, IPW2200_CSR_CTL) &
1086bb5e3b2fSeh146360 		    IPW2200_CTL_CLOCK_READY)
1087bb5e3b2fSeh146360 			break;
1088bb5e3b2fSeh146360 		drv_usecwait(200);
1089bb5e3b2fSeh146360 	}
1090bb5e3b2fSeh146360 	if (ntries == 1000) {
1091bb5e3b2fSeh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
1092bb5e3b2fSeh146360 		    "ipw2200_chip_reset(): timeout\n"));
1093bb5e3b2fSeh146360 		return (DDI_FAILURE);
1094bb5e3b2fSeh146360 	}
1095bb5e3b2fSeh146360 
1096bb5e3b2fSeh146360 	tmp = ipw2200_csr_get32(sc, IPW2200_CSR_RST);
1097bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_RST, tmp | IPW2200_RST_SW_RESET);
1098bb5e3b2fSeh146360 
1099bb5e3b2fSeh146360 	drv_usecwait(10);
1100bb5e3b2fSeh146360 
1101bb5e3b2fSeh146360 	tmp = ipw2200_csr_get32(sc, IPW2200_CSR_CTL);
1102bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_CTL, tmp | IPW2200_CTL_INIT);
1103bb5e3b2fSeh146360 
1104bb5e3b2fSeh146360 	/*
1105bb5e3b2fSeh146360 	 * clear NIC memory
1106bb5e3b2fSeh146360 	 */
1107bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_ADDR, 0);
1108bb5e3b2fSeh146360 	for (i = 0; i < 0xc000; i++)
1109bb5e3b2fSeh146360 		ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, 0);
1110bb5e3b2fSeh146360 
1111bb5e3b2fSeh146360 	return (DDI_SUCCESS);
1112bb5e3b2fSeh146360 }
1113bb5e3b2fSeh146360 
1114bb5e3b2fSeh146360 /*
1115bb5e3b2fSeh146360  * This function is used by wificonfig/dladm to get the current
1116bb5e3b2fSeh146360  * radio status, it is off/on
1117bb5e3b2fSeh146360  */
1118bb5e3b2fSeh146360 int
1119bb5e3b2fSeh146360 ipw2200_radio_status(struct ipw2200_softc *sc)
1120bb5e3b2fSeh146360 {
1121bb5e3b2fSeh146360 	int	val;
1122bb5e3b2fSeh146360 
1123bb5e3b2fSeh146360 	val = (ipw2200_csr_get32(sc, IPW2200_CSR_IO) &
1124bb5e3b2fSeh146360 	    IPW2200_IO_RADIO_ENABLED) ? 1 : 0;
1125bb5e3b2fSeh146360 
1126bb5e3b2fSeh146360 	return (val);
1127bb5e3b2fSeh146360 }
1128bb5e3b2fSeh146360 /*
1129bb5e3b2fSeh146360  * This function is used to get the statistic
1130bb5e3b2fSeh146360  */
1131bb5e3b2fSeh146360 void
1132bb5e3b2fSeh146360 ipw2200_get_statistics(struct ipw2200_softc *sc)
1133bb5e3b2fSeh146360 {
1134bb5e3b2fSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
1135bb5e3b2fSeh146360 
1136bb5e3b2fSeh146360 	uint32_t size, buf[128];
1137bb5e3b2fSeh146360 
1138bb5e3b2fSeh146360 	if (!(sc->sc_flags & IPW2200_FLAG_FW_INITED)) {
1139bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
1140bb5e3b2fSeh146360 		    "ipw2200_get_statistic(): fw doesn't download yet."));
1141bb5e3b2fSeh146360 		return;
1142bb5e3b2fSeh146360 	}
1143bb5e3b2fSeh146360 
1144bb5e3b2fSeh146360 	size = min(ipw2200_csr_get32(sc, IPW2200_CSR_TABLE0_SIZE), 128 - 1);
1145bb5e3b2fSeh146360 	ipw2200_csr_getbuf32(sc, IPW2200_CSR_TABLE0_BASE, &buf[1], size);
1146bb5e3b2fSeh146360 
1147bb5e3b2fSeh146360 	/*
1148bb5e3b2fSeh146360 	 * To retrieve the statistic information into proper places. There are
1149bb5e3b2fSeh146360 	 * lot of information. These table will be read once a second.
1150bb5e3b2fSeh146360 	 * Hopefully, it will not effect the performance.
1151bb5e3b2fSeh146360 	 */
1152bb5e3b2fSeh146360 
1153bb5e3b2fSeh146360 	/*
1154bb5e3b2fSeh146360 	 * For the tx/crc information, we can get them from chip directly;
1155bb5e3b2fSeh146360 	 * For the rx/wep error/(rts) related information, leave them net80211.
1156bb5e3b2fSeh146360 	 */
1157bb5e3b2fSeh146360 	/* WIFI_STAT_TX_FRAGS */
1158bb5e3b2fSeh146360 	ic->ic_stats.is_tx_frags = (uint32_t)buf[5];
1159bb5e3b2fSeh146360 	/* WIFI_STAT_MCAST_TX */
1160bb5e3b2fSeh146360 	ic->ic_stats.is_tx_mcast = (uint32_t)buf[31];
1161bb5e3b2fSeh146360 	/* WIFI_STAT_TX_RETRANS */
1162bb5e3b2fSeh146360 	ic->ic_stats.is_tx_retries = (uint32_t)buf[56];
1163bb5e3b2fSeh146360 	/* WIFI_STAT_TX_FAILED */
1164bb5e3b2fSeh146360 	ic->ic_stats.is_tx_failed = (uint32_t)buf[57];
1165bb5e3b2fSeh146360 	/* MAC_STAT_OBYTES */
1166bb5e3b2fSeh146360 	ic->ic_stats.is_tx_bytes = (uint32_t)buf[64];
1167bb5e3b2fSeh146360 }
1168bb5e3b2fSeh146360 
1169bb5e3b2fSeh146360 /*
1170bb5e3b2fSeh146360  * DMA region alloc subroutine
1171bb5e3b2fSeh146360  */
1172bb5e3b2fSeh146360 int
1173bb5e3b2fSeh146360 ipw2200_dma_region_alloc(struct ipw2200_softc *sc, struct dma_region *dr,
1174bb5e3b2fSeh146360 	size_t size, uint_t dir, uint_t flags)
1175bb5e3b2fSeh146360 {
1176bb5e3b2fSeh146360 	dev_info_t	*dip = sc->sc_dip;
1177bb5e3b2fSeh146360 	int		err;
1178bb5e3b2fSeh146360 
1179bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
1180bb5e3b2fSeh146360 	    "ipw2200_dma_region_alloc(): size =%u\n", size));
1181bb5e3b2fSeh146360 
1182bb5e3b2fSeh146360 	err = ddi_dma_alloc_handle(dip, &ipw2200_dma_attr, DDI_DMA_SLEEP, NULL,
1183bb5e3b2fSeh146360 	    &dr->dr_hnd);
1184bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
1185bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
1186bb5e3b2fSeh146360 		    "ipw2200_dma_region_alloc(): "
1187bb5e3b2fSeh146360 		    "ddi_dma_alloc_handle() failed\n"));
1188bb5e3b2fSeh146360 		goto fail0;
1189bb5e3b2fSeh146360 	}
1190bb5e3b2fSeh146360 
1191bb5e3b2fSeh146360 	err = ddi_dma_mem_alloc(dr->dr_hnd, size, &ipw2200_dma_accattr,
1192bb5e3b2fSeh146360 	    flags, DDI_DMA_SLEEP, NULL,
1193bb5e3b2fSeh146360 	    &dr->dr_base, &dr->dr_size, &dr->dr_acc);
1194bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
1195bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
1196bb5e3b2fSeh146360 		    "ipw2200_dma_region_alloc(): "
1197bb5e3b2fSeh146360 		    "ddi_dma_mem_alloc() failed\n"));
1198bb5e3b2fSeh146360 		goto fail1;
1199bb5e3b2fSeh146360 	}
1200bb5e3b2fSeh146360 
1201bb5e3b2fSeh146360 	err = ddi_dma_addr_bind_handle(dr->dr_hnd, NULL,
1202bb5e3b2fSeh146360 	    dr->dr_base, dr->dr_size,
1203bb5e3b2fSeh146360 	    dir | flags, DDI_DMA_SLEEP, NULL,
1204bb5e3b2fSeh146360 	    &dr->dr_cookie, &dr->dr_ccnt);
1205bb5e3b2fSeh146360 	if (err != DDI_DMA_MAPPED) {
1206bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
1207bb5e3b2fSeh146360 		    "ipw2200_dma_region_alloc(): "
1208bb5e3b2fSeh146360 		    "ddi_dma_addr_bind_handle() failed\n"));
1209bb5e3b2fSeh146360 		goto fail2;
1210bb5e3b2fSeh146360 	}
1211bb5e3b2fSeh146360 
1212bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
1213bb5e3b2fSeh146360 	    "ipw2200_dma_region_alloc(): ccnt=%u\n", dr->dr_ccnt));
1214bb5e3b2fSeh146360 
1215bb5e3b2fSeh146360 	if (dr->dr_ccnt != 1) {
1216bb5e3b2fSeh146360 		err = DDI_FAILURE;
1217bb5e3b2fSeh146360 		goto fail3;
1218bb5e3b2fSeh146360 	}
1219bb5e3b2fSeh146360 
1220bb5e3b2fSeh146360 	dr->dr_pbase = dr->dr_cookie.dmac_address;
1221bb5e3b2fSeh146360 
1222bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
1223bb5e3b2fSeh146360 	    "ipw2200_dma_region_alloc(): get physical-base=0x%08x\n",
1224bb5e3b2fSeh146360 	    dr->dr_pbase));
1225bb5e3b2fSeh146360 
1226bb5e3b2fSeh146360 	return (DDI_SUCCESS);
1227bb5e3b2fSeh146360 
1228bb5e3b2fSeh146360 fail3:
1229bb5e3b2fSeh146360 	(void) ddi_dma_unbind_handle(dr->dr_hnd);
1230bb5e3b2fSeh146360 fail2:
1231bb5e3b2fSeh146360 	ddi_dma_mem_free(&dr->dr_acc);
1232bb5e3b2fSeh146360 fail1:
1233bb5e3b2fSeh146360 	ddi_dma_free_handle(&dr->dr_hnd);
1234bb5e3b2fSeh146360 fail0:
1235bb5e3b2fSeh146360 	return (err);
1236bb5e3b2fSeh146360 }
1237bb5e3b2fSeh146360 
1238bb5e3b2fSeh146360 void
1239bb5e3b2fSeh146360 ipw2200_dma_region_free(struct dma_region *dr)
1240bb5e3b2fSeh146360 {
1241bb5e3b2fSeh146360 	(void) ddi_dma_unbind_handle(dr->dr_hnd);
1242bb5e3b2fSeh146360 	ddi_dma_mem_free(&dr->dr_acc);
1243bb5e3b2fSeh146360 	ddi_dma_free_handle(&dr->dr_hnd);
1244bb5e3b2fSeh146360 }
1245bb5e3b2fSeh146360 
1246bb5e3b2fSeh146360 static int
1247bb5e3b2fSeh146360 ipw2200_ring_alloc(struct ipw2200_softc *sc)
1248bb5e3b2fSeh146360 {
1249bb5e3b2fSeh146360 	int	err, i;
1250bb5e3b2fSeh146360 
1251bb5e3b2fSeh146360 	/*
1252bb5e3b2fSeh146360 	 * tx desc ring
1253bb5e3b2fSeh146360 	 */
1254bb5e3b2fSeh146360 	sc->sc_dma_txdsc.dr_name = "ipw2200-tx-desc-ring";
1255bb5e3b2fSeh146360 	err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_txdsc,
1256bb5e3b2fSeh146360 	    IPW2200_TX_RING_SIZE * sizeof (struct ipw2200_tx_desc),
1257bb5e3b2fSeh146360 	    DDI_DMA_WRITE, DDI_DMA_CONSISTENT);
1258bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
1259bb5e3b2fSeh146360 		goto fail0;
1260bb5e3b2fSeh146360 	/*
1261bb5e3b2fSeh146360 	 * tx buffer array
1262bb5e3b2fSeh146360 	 */
1263bb5e3b2fSeh146360 	for (i = 0; i < IPW2200_TX_RING_SIZE; i++) {
1264bb5e3b2fSeh146360 		sc->sc_dma_txbufs[i].dr_name = "ipw2200-tx-buf";
1265bb5e3b2fSeh146360 		err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_txbufs[i],
1266bb5e3b2fSeh146360 		    IPW2200_TXBUF_SIZE, DDI_DMA_WRITE, DDI_DMA_STREAMING);
1267bb5e3b2fSeh146360 		if (err != DDI_SUCCESS) {
1268bb5e3b2fSeh146360 			while (i >= 0) {
1269bb5e3b2fSeh146360 				ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]);
1270bb5e3b2fSeh146360 				i--;
1271bb5e3b2fSeh146360 			}
1272bb5e3b2fSeh146360 			goto fail1;
1273bb5e3b2fSeh146360 		}
1274bb5e3b2fSeh146360 	}
1275bb5e3b2fSeh146360 	/*
1276bb5e3b2fSeh146360 	 * rx buffer array
1277bb5e3b2fSeh146360 	 */
1278bb5e3b2fSeh146360 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++) {
1279bb5e3b2fSeh146360 		sc->sc_dma_rxbufs[i].dr_name = "ipw2200-rx-buf";
1280bb5e3b2fSeh146360 		err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_rxbufs[i],
1281bb5e3b2fSeh146360 		    IPW2200_RXBUF_SIZE, DDI_DMA_READ, DDI_DMA_STREAMING);
1282bb5e3b2fSeh146360 		if (err != DDI_SUCCESS) {
1283bb5e3b2fSeh146360 			while (i >= 0) {
1284bb5e3b2fSeh146360 				ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]);
1285bb5e3b2fSeh146360 				i--;
1286bb5e3b2fSeh146360 			}
1287bb5e3b2fSeh146360 			goto fail2;
1288bb5e3b2fSeh146360 		}
1289bb5e3b2fSeh146360 	}
1290bb5e3b2fSeh146360 	/*
1291bb5e3b2fSeh146360 	 * cmd desc ring
1292bb5e3b2fSeh146360 	 */
1293bb5e3b2fSeh146360 	sc->sc_dma_cmdsc.dr_name = "ipw2200-cmd-desc-ring";
1294bb5e3b2fSeh146360 	err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_cmdsc,
1295bb5e3b2fSeh146360 	    IPW2200_CMD_RING_SIZE * sizeof (struct ipw2200_cmd_desc),
1296bb5e3b2fSeh146360 	    DDI_DMA_WRITE, DDI_DMA_CONSISTENT);
1297bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
1298bb5e3b2fSeh146360 		goto fail3;
1299bb5e3b2fSeh146360 
1300bb5e3b2fSeh146360 	return (DDI_SUCCESS);
1301bb5e3b2fSeh146360 
1302bb5e3b2fSeh146360 fail3:
1303bb5e3b2fSeh146360 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
1304bb5e3b2fSeh146360 		ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]);
1305bb5e3b2fSeh146360 fail2:
1306bb5e3b2fSeh146360 	for (i = 0; i < IPW2200_TX_RING_SIZE; i++)
1307bb5e3b2fSeh146360 		ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]);
1308bb5e3b2fSeh146360 fail1:
1309bb5e3b2fSeh146360 	ipw2200_dma_region_free(&sc->sc_dma_txdsc);
1310bb5e3b2fSeh146360 fail0:
1311bb5e3b2fSeh146360 	return (err);
1312bb5e3b2fSeh146360 }
1313bb5e3b2fSeh146360 
1314bb5e3b2fSeh146360 static void
1315bb5e3b2fSeh146360 ipw2200_ring_free(struct ipw2200_softc *sc)
1316bb5e3b2fSeh146360 {
1317bb5e3b2fSeh146360 	int	i;
1318bb5e3b2fSeh146360 
1319bb5e3b2fSeh146360 	/*
1320bb5e3b2fSeh146360 	 * tx ring desc
1321bb5e3b2fSeh146360 	 */
1322bb5e3b2fSeh146360 	ipw2200_dma_region_free(&sc->sc_dma_txdsc);
1323bb5e3b2fSeh146360 	/*
1324bb5e3b2fSeh146360 	 * tx buf
1325bb5e3b2fSeh146360 	 */
1326bb5e3b2fSeh146360 	for (i = 0; i < IPW2200_TX_RING_SIZE; i++)
1327bb5e3b2fSeh146360 		ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]);
1328bb5e3b2fSeh146360 	/*
1329bb5e3b2fSeh146360 	 * rx buf
1330bb5e3b2fSeh146360 	 */
1331bb5e3b2fSeh146360 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
1332bb5e3b2fSeh146360 		ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]);
1333bb5e3b2fSeh146360 	/*
1334bb5e3b2fSeh146360 	 * command ring desc
1335bb5e3b2fSeh146360 	 */
1336bb5e3b2fSeh146360 	ipw2200_dma_region_free(&sc->sc_dma_cmdsc);
1337bb5e3b2fSeh146360 }
1338bb5e3b2fSeh146360 
1339bb5e3b2fSeh146360 static void
1340bb5e3b2fSeh146360 ipw2200_ring_reset(struct ipw2200_softc *sc)
1341bb5e3b2fSeh146360 {
1342bb5e3b2fSeh146360 	int i;
1343bb5e3b2fSeh146360 
1344bb5e3b2fSeh146360 	/*
1345bb5e3b2fSeh146360 	 * tx desc ring & buffer array
1346bb5e3b2fSeh146360 	 */
1347bb5e3b2fSeh146360 	sc->sc_tx_cur   = 0;
1348bb5e3b2fSeh146360 	sc->sc_tx_free  = IPW2200_TX_RING_SIZE;
1349bb5e3b2fSeh146360 	sc->sc_txdsc    = (struct ipw2200_tx_desc *)sc->sc_dma_txdsc.dr_base;
1350bb5e3b2fSeh146360 	for (i = 0; i < IPW2200_TX_RING_SIZE; i++)
1351bb5e3b2fSeh146360 		sc->sc_txbufs[i] = (uint8_t *)sc->sc_dma_txbufs[i].dr_base;
1352bb5e3b2fSeh146360 	/*
1353bb5e3b2fSeh146360 	 * rx buffer array
1354bb5e3b2fSeh146360 	 */
1355bb5e3b2fSeh146360 	sc->sc_rx_cur   = 0;
1356bb5e3b2fSeh146360 	sc->sc_rx_free  = IPW2200_RX_RING_SIZE;
1357bb5e3b2fSeh146360 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
1358bb5e3b2fSeh146360 		sc->sc_rxbufs[i] = (uint8_t *)sc->sc_dma_rxbufs[i].dr_base;
1359bb5e3b2fSeh146360 
1360bb5e3b2fSeh146360 	/*
1361bb5e3b2fSeh146360 	 * command desc ring
1362bb5e3b2fSeh146360 	 */
1363bb5e3b2fSeh146360 	sc->sc_cmd_cur  = 0;
1364bb5e3b2fSeh146360 	sc->sc_cmd_free = IPW2200_CMD_RING_SIZE;
1365bb5e3b2fSeh146360 	sc->sc_cmdsc    = (struct ipw2200_cmd_desc *)sc->sc_dma_cmdsc.dr_base;
1366bb5e3b2fSeh146360 }
1367bb5e3b2fSeh146360 
1368bb5e3b2fSeh146360 /*
1369bb5e3b2fSeh146360  * tx, rx rings and command initialization
1370bb5e3b2fSeh146360  */
1371bb5e3b2fSeh146360 static int
1372bb5e3b2fSeh146360 ipw2200_ring_init(struct ipw2200_softc *sc)
1373bb5e3b2fSeh146360 {
1374bb5e3b2fSeh146360 	int	err;
1375bb5e3b2fSeh146360 
1376bb5e3b2fSeh146360 	err = ipw2200_ring_alloc(sc);
1377bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
1378bb5e3b2fSeh146360 		return (err);
1379bb5e3b2fSeh146360 
1380bb5e3b2fSeh146360 	ipw2200_ring_reset(sc);
1381bb5e3b2fSeh146360 
1382bb5e3b2fSeh146360 	return (DDI_SUCCESS);
1383bb5e3b2fSeh146360 }
1384bb5e3b2fSeh146360 
1385bb5e3b2fSeh146360 static void
1386bb5e3b2fSeh146360 ipw2200_ring_hwsetup(struct ipw2200_softc *sc)
1387bb5e3b2fSeh146360 {
1388bb5e3b2fSeh146360 	int	i;
1389bb5e3b2fSeh146360 
1390bb5e3b2fSeh146360 	/*
1391bb5e3b2fSeh146360 	 * command desc ring
1392bb5e3b2fSeh146360 	 */
1393bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_CMD_BASE, sc->sc_dma_cmdsc.dr_pbase);
1394bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_CMD_SIZE, IPW2200_CMD_RING_SIZE);
1395bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_CMD_WRITE_INDEX, sc->sc_cmd_cur);
1396bb5e3b2fSeh146360 
1397bb5e3b2fSeh146360 	/*
1398bb5e3b2fSeh146360 	 * tx desc ring.  only tx1 is used, tx2, tx3, and tx4 are unused
1399bb5e3b2fSeh146360 	 */
1400bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX1_BASE, sc->sc_dma_txdsc.dr_pbase);
1401bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX1_SIZE, IPW2200_TX_RING_SIZE);
1402bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX1_WRITE_INDEX, sc->sc_tx_cur);
1403bb5e3b2fSeh146360 
1404bb5e3b2fSeh146360 	/*
1405bb5e3b2fSeh146360 	 * tx2, tx3, tx4 is not used
1406bb5e3b2fSeh146360 	 */
1407bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX2_BASE, sc->sc_dma_txdsc.dr_pbase);
1408bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX2_SIZE, IPW2200_TX_RING_SIZE);
1409bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX2_READ_INDEX, 0);
1410bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX2_WRITE_INDEX, 0);
1411bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX3_BASE, sc->sc_dma_txdsc.dr_pbase);
1412bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX3_SIZE, IPW2200_TX_RING_SIZE);
1413bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX3_READ_INDEX, 0);
1414bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX3_WRITE_INDEX, 0);
1415bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX4_BASE, sc->sc_dma_txdsc.dr_pbase);
1416bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX4_SIZE, IPW2200_TX_RING_SIZE);
1417bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX4_READ_INDEX, 0);
1418bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX4_WRITE_INDEX, 0);
1419bb5e3b2fSeh146360 
1420bb5e3b2fSeh146360 	/*
1421bb5e3b2fSeh146360 	 * rx buffer ring
1422bb5e3b2fSeh146360 	 */
1423bb5e3b2fSeh146360 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
1424bb5e3b2fSeh146360 		ipw2200_csr_put32(sc, IPW2200_CSR_RX_BASE + i * 4,
1425bb5e3b2fSeh146360 		    sc->sc_dma_rxbufs[i].dr_pbase);
1426bb5e3b2fSeh146360 	/*
1427bb5e3b2fSeh146360 	 * all rx buffer are empty, rx-rd-index == 0 && rx-wr-index == N-1
1428bb5e3b2fSeh146360 	 */
1429bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_RX_WRITE_INDEX,
1430bb5e3b2fSeh146360 	    RING_BACKWARD(sc->sc_rx_cur, 1, IPW2200_RX_RING_SIZE));
1431bb5e3b2fSeh146360 }
1432bb5e3b2fSeh146360 
1433bb5e3b2fSeh146360 int
1434bb5e3b2fSeh146360 ipw2200_start_scan(struct ipw2200_softc *sc)
1435bb5e3b2fSeh146360 {
1436bb5e3b2fSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
1437bb5e3b2fSeh146360 	struct ipw2200_scan	scan;
1438bb5e3b2fSeh146360 	uint8_t			*ch;
1439bb5e3b2fSeh146360 	int			cnt, i;
1440bb5e3b2fSeh146360 
1441bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT,
1442bb5e3b2fSeh146360 	    "ipw2200_start_scan(): start scanning \n"));
1443bb5e3b2fSeh146360 
1444bb5e3b2fSeh146360 	/*
1445bb5e3b2fSeh146360 	 * start scanning
1446bb5e3b2fSeh146360 	 */
1447bb5e3b2fSeh146360 	sc->sc_flags |= IPW2200_FLAG_SCANNING;
1448bb5e3b2fSeh146360 
1449bb5e3b2fSeh146360 	(void) memset(&scan, 0, sizeof (scan));
1450bb5e3b2fSeh146360 	scan.type = (ic->ic_des_esslen != 0) ? IPW2200_SCAN_TYPE_BDIRECTED :
1451bb5e3b2fSeh146360 	    IPW2200_SCAN_TYPE_BROADCAST;
1452bb5e3b2fSeh146360 	scan.dwelltime = LE_16(40); /* The interval is set up to 40 */
1453bb5e3b2fSeh146360 
1454bb5e3b2fSeh146360 	/*
1455bb5e3b2fSeh146360 	 * Compact supported channel number(5G) into a single buffer
1456bb5e3b2fSeh146360 	 */
1457bb5e3b2fSeh146360 	ch = scan.channels;
1458bb5e3b2fSeh146360 	cnt = 0;
1459bb5e3b2fSeh146360 	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
1460bb5e3b2fSeh146360 		if (IEEE80211_IS_CHAN_5GHZ(&ic->ic_sup_channels[i]) &&
1461bb5e3b2fSeh146360 		    isset(ic->ic_chan_active, i)) {
1462bb5e3b2fSeh146360 			*++ch = (uint8_t)i;
1463bb5e3b2fSeh146360 			cnt++;
1464bb5e3b2fSeh146360 		}
1465bb5e3b2fSeh146360 	}
1466bb5e3b2fSeh146360 	*(ch - cnt) = IPW2200_CHAN_5GHZ | (uint8_t)cnt;
14673b608f65Sql147931 	ch = (cnt > 0) ? (ch + 1) : (scan.channels);
1468bb5e3b2fSeh146360 
1469bb5e3b2fSeh146360 	/*
1470bb5e3b2fSeh146360 	 * Compact supported channel number(2G) into a single buffer
1471bb5e3b2fSeh146360 	 */
1472bb5e3b2fSeh146360 	cnt = 0;
1473bb5e3b2fSeh146360 	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
1474bb5e3b2fSeh146360 		if (IEEE80211_IS_CHAN_2GHZ(&ic->ic_sup_channels[i]) &&
1475bb5e3b2fSeh146360 		    isset(ic->ic_chan_active, i)) {
1476bb5e3b2fSeh146360 			*++ch = (uint8_t)i;
1477bb5e3b2fSeh146360 			cnt++;
1478bb5e3b2fSeh146360 		}
1479bb5e3b2fSeh146360 	}
1480bb5e3b2fSeh146360 	*(ch - cnt) = IPW2200_CHAN_2GHZ | cnt;
1481bb5e3b2fSeh146360 
1482bb5e3b2fSeh146360 	return (ipw2200_cmd(sc, IPW2200_CMD_SCAN, &scan, sizeof (scan), 1));
1483bb5e3b2fSeh146360 }
1484bb5e3b2fSeh146360 
1485bb5e3b2fSeh146360 int
1486bb5e3b2fSeh146360 ipw2200_auth_and_assoc(struct ipw2200_softc *sc)
1487bb5e3b2fSeh146360 {
1488bb5e3b2fSeh146360 	struct ieee80211com		*ic = &sc->sc_ic;
1489bb5e3b2fSeh146360 	struct ieee80211_node		*in = ic->ic_bss;
1490bb5e3b2fSeh146360 	struct ipw2200_configuration	cfg;
1491bb5e3b2fSeh146360 	struct ipw2200_rateset		rs;
1492bb5e3b2fSeh146360 	struct ipw2200_associate	assoc;
1493bb5e3b2fSeh146360 	uint32_t			data;
1494bb5e3b2fSeh146360 	int				err;
1495bb5e3b2fSeh146360 
1496bb5e3b2fSeh146360 	/*
1497bb5e3b2fSeh146360 	 * set the confiuration
1498bb5e3b2fSeh146360 	 */
1499bb5e3b2fSeh146360 	if (IEEE80211_IS_CHAN_2GHZ(in->in_chan)) {
1500bb5e3b2fSeh146360 		/* enable b/g auto-detection */
1501bb5e3b2fSeh146360 		(void) memset(&cfg, 0, sizeof (cfg));
1502bb5e3b2fSeh146360 		cfg.bluetooth_coexistence = 1;
1503bb5e3b2fSeh146360 		cfg.multicast_enabled	  = 1;
1504bb5e3b2fSeh146360 		cfg.use_protection	  = 1;
1505bb5e3b2fSeh146360 		cfg.answer_pbreq	  = 1;
1506bb5e3b2fSeh146360 		cfg.noise_reported	  = 1;
1507bb5e3b2fSeh146360 		err = ipw2200_cmd(sc, IPW2200_CMD_SET_CONFIG,
1508bb5e3b2fSeh146360 		    &cfg, sizeof (cfg), 1);
1509bb5e3b2fSeh146360 		if (err != DDI_SUCCESS)
1510bb5e3b2fSeh146360 			return (err);
1511bb5e3b2fSeh146360 	}
1512bb5e3b2fSeh146360 
1513bb5e3b2fSeh146360 	/*
1514bb5e3b2fSeh146360 	 * set the essid, may be null/hidden AP
1515bb5e3b2fSeh146360 	 */
1516bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
1517bb5e3b2fSeh146360 	    "ipw2200_auth_and_assoc(): "
1518bb5e3b2fSeh146360 	    "setting ESSID to(%u),%c%c%c%c%c%c%c%c\n",
1519bb5e3b2fSeh146360 	    in->in_esslen,
1520bb5e3b2fSeh146360 	    in->in_essid[0], in->in_essid[1],
1521bb5e3b2fSeh146360 	    in->in_essid[2], in->in_essid[3],
1522bb5e3b2fSeh146360 	    in->in_essid[4], in->in_essid[5],
1523bb5e3b2fSeh146360 	    in->in_essid[6], in->in_essid[7]));
1524bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_ESSID, in->in_essid,
1525bb5e3b2fSeh146360 	    in->in_esslen, 1);
1526bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
1527bb5e3b2fSeh146360 		return (err);
1528bb5e3b2fSeh146360 
1529bb5e3b2fSeh146360 	/*
1530bb5e3b2fSeh146360 	 * set the rate: the rate set has already been ''negocitated''
1531bb5e3b2fSeh146360 	 */
1532bb5e3b2fSeh146360 	rs.mode = IEEE80211_IS_CHAN_5GHZ(in->in_chan) ?
1533bb5e3b2fSeh146360 	    IPW2200_MODE_11A : IPW2200_MODE_11G;
1534bb5e3b2fSeh146360 	rs.type = IPW2200_RATESET_TYPE_NEGOCIATED;
1535bb5e3b2fSeh146360 	rs.nrates = in->in_rates.ir_nrates;
1536bb5e3b2fSeh146360 	(void) memcpy(rs.rates, in->in_rates.ir_rates, in->in_rates.ir_nrates);
1537bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
1538bb5e3b2fSeh146360 	    "ipw2200_auth_and_assoc(): "
1539bb5e3b2fSeh146360 	    "setting negotiated rates to(nrates = %u)\n", rs.nrates));
1540bb5e3b2fSeh146360 
1541bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 1);
1542bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
1543bb5e3b2fSeh146360 		return (err);
1544bb5e3b2fSeh146360 
1545bb5e3b2fSeh146360 	/*
1546bb5e3b2fSeh146360 	 * set the sensitive
1547bb5e3b2fSeh146360 	 */
1548bb5e3b2fSeh146360 	data = LE_32(in->in_rssi);
1549bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
1550bb5e3b2fSeh146360 	    "ipw2200_auth_and_assoc(): "
1551bb5e3b2fSeh146360 	    "setting sensitivity to rssi:(%u)\n", (uint8_t)in->in_rssi));
1552bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_SENSITIVITY,
1553bb5e3b2fSeh146360 	    &data, sizeof (data), 1);
1554bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
1555bb5e3b2fSeh146360 		return (err);
1556bb5e3b2fSeh146360 
1557bb5e3b2fSeh146360 	/*
1558bb5e3b2fSeh146360 	 * invoke command associate
1559bb5e3b2fSeh146360 	 */
1560bb5e3b2fSeh146360 	(void) memset(&assoc, 0, sizeof (assoc));
1561bb5e3b2fSeh146360 	assoc.mode = IEEE80211_IS_CHAN_5GHZ(in->in_chan) ?
1562bb5e3b2fSeh146360 	    IPW2200_MODE_11A : IPW2200_MODE_11G;
1563bb5e3b2fSeh146360 	assoc.chan = ieee80211_chan2ieee(ic, in->in_chan);
1564bb5e3b2fSeh146360 	/*
1565bb5e3b2fSeh146360 	 * use the value set to ic_bss to retraive current sharedmode
1566bb5e3b2fSeh146360 	 */
1567bb5e3b2fSeh146360 	if (ic->ic_bss->in_authmode == WL_SHAREDKEY) {
1568bb5e3b2fSeh146360 		assoc.auth = (ic->ic_def_txkey << 4) | IPW2200_AUTH_SHARED;
1569bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
1570bb5e3b2fSeh146360 		    "ipw2200_auth_and_assoc(): "
1571bb5e3b2fSeh146360 		    "associate to shared key mode, set thru. ioctl"));
1572bb5e3b2fSeh146360 	}
1573bb5e3b2fSeh146360 	(void) memcpy(assoc.tstamp, in->in_tstamp.data, 8);
1574bb5e3b2fSeh146360 	assoc.capinfo = LE_16(in->in_capinfo);
1575bb5e3b2fSeh146360 	assoc.lintval = LE_16(ic->ic_lintval);
1576bb5e3b2fSeh146360 	assoc.intval  = LE_16(in->in_intval);
1577bb5e3b2fSeh146360 	IEEE80211_ADDR_COPY(assoc.bssid, in->in_bssid);
1578bb5e3b2fSeh146360 	if (ic->ic_opmode == IEEE80211_M_IBSS)
1579bb5e3b2fSeh146360 		IEEE80211_ADDR_COPY(assoc.dst, ipw2200_broadcast_addr);
1580bb5e3b2fSeh146360 	else
1581bb5e3b2fSeh146360 		IEEE80211_ADDR_COPY(assoc.dst, in->in_bssid);
1582bb5e3b2fSeh146360 
1583bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
1584bb5e3b2fSeh146360 	    "ipw2200_auth_and_assoc(): "
1585bb5e3b2fSeh146360 	    "associate to bssid(%2x:%2x:%2x:%2x:%2x:%2x:), "
1586bb5e3b2fSeh146360 	    "chan(%u), auth(%u)\n",
1587bb5e3b2fSeh146360 	    assoc.bssid[0], assoc.bssid[1], assoc.bssid[2],
1588bb5e3b2fSeh146360 	    assoc.bssid[3], assoc.bssid[4], assoc.bssid[5],
1589bb5e3b2fSeh146360 	    assoc.chan, assoc.auth));
1590bb5e3b2fSeh146360 	return (ipw2200_cmd(sc, IPW2200_CMD_ASSOCIATE,
1591bb5e3b2fSeh146360 	    &assoc, sizeof (assoc), 1));
1592bb5e3b2fSeh146360 }
1593bb5e3b2fSeh146360 
1594bb5e3b2fSeh146360 /* ARGSUSED */
1595bb5e3b2fSeh146360 static int
1596bb5e3b2fSeh146360 ipw2200_newstate(struct ieee80211com *ic, enum ieee80211_state state, int arg)
1597bb5e3b2fSeh146360 {
1598bb5e3b2fSeh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)ic;
1599bb5e3b2fSeh146360 	wifi_data_t		wd = { 0 };
1600bb5e3b2fSeh146360 
1601bb5e3b2fSeh146360 	switch (state) {
1602bb5e3b2fSeh146360 	case IEEE80211_S_SCAN:
1603bb5e3b2fSeh146360 		if (!(sc->sc_flags & IPW2200_FLAG_SCANNING)) {
1604bb5e3b2fSeh146360 			ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN;
1605bb5e3b2fSeh146360 			(void) ipw2200_start_scan(sc);
1606bb5e3b2fSeh146360 		}
1607bb5e3b2fSeh146360 		break;
1608bb5e3b2fSeh146360 	case IEEE80211_S_AUTH:
1609bb5e3b2fSeh146360 		(void) ipw2200_auth_and_assoc(sc);
1610bb5e3b2fSeh146360 		break;
1611bb5e3b2fSeh146360 	case IEEE80211_S_RUN:
1612bb5e3b2fSeh146360 		/*
1613bb5e3b2fSeh146360 		 * We can send data now; update the fastpath with our
1614bb5e3b2fSeh146360 		 * current associated BSSID and other relevant settings.
1615bb5e3b2fSeh146360 		 *
1616bb5e3b2fSeh146360 		 * Hardware to ahndle the wep encryption. Set the encryption
1617bb5e3b2fSeh146360 		 * as false.
1618bb5e3b2fSeh146360 		 * wd.wd_wep    = ic->ic_flags & IEEE80211_F_WEPON;
1619bb5e3b2fSeh146360 		 */
1620bb5e3b2fSeh146360 		wd.wd_secalloc	= WIFI_SEC_NONE;
1621bb5e3b2fSeh146360 		wd.wd_opmode	= ic->ic_opmode;
1622bb5e3b2fSeh146360 		IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
1623bb5e3b2fSeh146360 		(void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd));
1624bb5e3b2fSeh146360 		break;
1625bb5e3b2fSeh146360 	case IEEE80211_S_ASSOC:
1626bb5e3b2fSeh146360 	case IEEE80211_S_INIT:
1627bb5e3b2fSeh146360 		break;
1628bb5e3b2fSeh146360 	}
1629bb5e3b2fSeh146360 
1630bb5e3b2fSeh146360 	/*
1631bb5e3b2fSeh146360 	 * notify to update the link
1632bb5e3b2fSeh146360 	 */
1633bb5e3b2fSeh146360 	if ((ic->ic_state != IEEE80211_S_RUN) && (state == IEEE80211_S_RUN)) {
1634bb5e3b2fSeh146360 		mac_link_update(ic->ic_mach, LINK_STATE_UP);
1635bb5e3b2fSeh146360 	} else if ((ic->ic_state == IEEE80211_S_RUN) &&
1636bb5e3b2fSeh146360 	    (state != IEEE80211_S_RUN)) {
1637bb5e3b2fSeh146360 		mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
1638bb5e3b2fSeh146360 	}
1639bb5e3b2fSeh146360 
1640bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
1641bb5e3b2fSeh146360 	    "ipw2200_newstat(): %s -> %s\n",
1642bb5e3b2fSeh146360 	    ieee80211_state_name[ic->ic_state],
1643bb5e3b2fSeh146360 	    ieee80211_state_name[state]));
1644bb5e3b2fSeh146360 
1645bb5e3b2fSeh146360 	ic->ic_state = state;
1646bb5e3b2fSeh146360 	return (DDI_SUCCESS);
1647bb5e3b2fSeh146360 }
1648bb5e3b2fSeh146360 
1649bb5e3b2fSeh146360 /*
1650bb5e3b2fSeh146360  * GLD operations
1651bb5e3b2fSeh146360  */
1652bb5e3b2fSeh146360 /* ARGSUSED */
1653bb5e3b2fSeh146360 static int
1654bb5e3b2fSeh146360 ipw2200_m_stat(void *arg, uint_t stat, uint64_t *val)
1655bb5e3b2fSeh146360 {
1656bb5e3b2fSeh146360 	ieee80211com_t	*ic = (ieee80211com_t *)arg;
1657bb5e3b2fSeh146360 
1658bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (((struct ipw2200_softc *)arg)->sc_dip,
1659bb5e3b2fSeh146360 	    CE_CONT,
1660bb5e3b2fSeh146360 	    "ipw2200_m_stat(): enter\n"));
1661bb5e3b2fSeh146360 
1662bb5e3b2fSeh146360 	/*
1663bb5e3b2fSeh146360 	 * Some of below statistic data are from hardware, some from net80211
1664bb5e3b2fSeh146360 	 */
1665bb5e3b2fSeh146360 	switch (stat) {
1666bb5e3b2fSeh146360 	case MAC_STAT_RBYTES:
1667bb5e3b2fSeh146360 		*val = ic->ic_stats.is_rx_bytes;
1668bb5e3b2fSeh146360 		break;
1669bb5e3b2fSeh146360 	case MAC_STAT_IPACKETS:
1670bb5e3b2fSeh146360 		*val = ic->ic_stats.is_rx_frags;
1671bb5e3b2fSeh146360 		break;
1672bb5e3b2fSeh146360 	case MAC_STAT_OBYTES:
1673bb5e3b2fSeh146360 		*val = ic->ic_stats.is_tx_bytes;
1674bb5e3b2fSeh146360 		break;
1675bb5e3b2fSeh146360 	case MAC_STAT_OPACKETS:
1676bb5e3b2fSeh146360 		*val = ic->ic_stats.is_tx_frags;
1677bb5e3b2fSeh146360 		break;
1678bb5e3b2fSeh146360 	/*
1679bb5e3b2fSeh146360 	 * Get below from hardware statistic, retraive net80211 value once 1s
1680bb5e3b2fSeh146360 	 */
1681bb5e3b2fSeh146360 	case WIFI_STAT_TX_FRAGS:
1682bb5e3b2fSeh146360 	case WIFI_STAT_MCAST_TX:
1683bb5e3b2fSeh146360 	case WIFI_STAT_TX_FAILED:
1684bb5e3b2fSeh146360 	case WIFI_STAT_TX_RETRANS:
1685bb5e3b2fSeh146360 	/*
1686bb5e3b2fSeh146360 	 * Get blow information from net80211
1687bb5e3b2fSeh146360 	 */
1688bb5e3b2fSeh146360 	case WIFI_STAT_RTS_SUCCESS:
1689bb5e3b2fSeh146360 	case WIFI_STAT_RTS_FAILURE:
1690bb5e3b2fSeh146360 	case WIFI_STAT_ACK_FAILURE:
1691bb5e3b2fSeh146360 	case WIFI_STAT_RX_FRAGS:
1692bb5e3b2fSeh146360 	case WIFI_STAT_MCAST_RX:
1693bb5e3b2fSeh146360 	case WIFI_STAT_RX_DUPS:
1694bb5e3b2fSeh146360 	case WIFI_STAT_FCS_ERRORS:
1695bb5e3b2fSeh146360 	case WIFI_STAT_WEP_ERRORS:
1696bb5e3b2fSeh146360 		return (ieee80211_stat(ic, stat, val));
1697bb5e3b2fSeh146360 	/*
1698bb5e3b2fSeh146360 	 * Need be supported later
1699bb5e3b2fSeh146360 	 */
1700bb5e3b2fSeh146360 	case MAC_STAT_IFSPEED:
1701bb5e3b2fSeh146360 	case MAC_STAT_NOXMTBUF:
1702bb5e3b2fSeh146360 	case MAC_STAT_IERRORS:
1703bb5e3b2fSeh146360 	case MAC_STAT_OERRORS:
1704bb5e3b2fSeh146360 	default:
1705bb5e3b2fSeh146360 		return (ENOTSUP);
1706bb5e3b2fSeh146360 	}
1707bb5e3b2fSeh146360 	return (0);
1708bb5e3b2fSeh146360 }
1709bb5e3b2fSeh146360 
1710bb5e3b2fSeh146360 /* ARGSUSED */
1711bb5e3b2fSeh146360 static int
1712bb5e3b2fSeh146360 ipw2200_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
1713bb5e3b2fSeh146360 {
1714bb5e3b2fSeh146360 	/* not supported */
1715bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (((struct ipw2200_softc *)arg)->sc_dip,
1716bb5e3b2fSeh146360 	    CE_CONT,
1717bb5e3b2fSeh146360 	    "ipw2200_m_multicst(): enter\n"));
1718bb5e3b2fSeh146360 
1719bb5e3b2fSeh146360 	return (DDI_SUCCESS);
1720bb5e3b2fSeh146360 }
1721bb5e3b2fSeh146360 
1722bb5e3b2fSeh146360 /*
1723bb5e3b2fSeh146360  * Multithread handler for linkstatus, fatal error recovery, get statistic
1724bb5e3b2fSeh146360  */
1725bb5e3b2fSeh146360 static void
1726bb5e3b2fSeh146360 ipw2200_thread(struct ipw2200_softc *sc)
1727bb5e3b2fSeh146360 {
1728bb5e3b2fSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
1729982d85fcSeh146360 	enum ieee80211_state	ostate;
1730bb5e3b2fSeh146360 	int32_t			nlstate;
1731bb5e3b2fSeh146360 	int			stat_cnt = 0;
1732bb5e3b2fSeh146360 
1733bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_SOFTINT, (sc->sc_dip, CE_CONT,
1734bb5e3b2fSeh146360 	    "ipw2200_thread(): enter, linkstate %d\n", sc->sc_linkstate));
1735bb5e3b2fSeh146360 
1736bb5e3b2fSeh146360 	mutex_enter(&sc->sc_mflock);
1737bb5e3b2fSeh146360 
1738bb5e3b2fSeh146360 	while (sc->sc_mfthread_switch) {
1739bb5e3b2fSeh146360 		/*
1740bb5e3b2fSeh146360 		 * notify the link state
1741bb5e3b2fSeh146360 		 */
1742bb5e3b2fSeh146360 		if (ic->ic_mach && (sc->sc_flags & IPW2200_FLAG_LINK_CHANGE)) {
1743bb5e3b2fSeh146360 
1744bb5e3b2fSeh146360 			IPW2200_DBG(IPW2200_DBG_SOFTINT, (sc->sc_dip, CE_CONT,
1745bb5e3b2fSeh146360 			    "ipw2200_thread(): link status --> %d\n",
1746bb5e3b2fSeh146360 			    sc->sc_linkstate));
1747bb5e3b2fSeh146360 
1748bb5e3b2fSeh146360 			sc->sc_flags &= ~IPW2200_FLAG_LINK_CHANGE;
1749bb5e3b2fSeh146360 			nlstate = sc->sc_linkstate;
1750bb5e3b2fSeh146360 
1751bb5e3b2fSeh146360 			mutex_exit(&sc->sc_mflock);
1752bb5e3b2fSeh146360 			mac_link_update(ic->ic_mach, nlstate);
1753bb5e3b2fSeh146360 			mutex_enter(&sc->sc_mflock);
1754bb5e3b2fSeh146360 		}
1755bb5e3b2fSeh146360 
1756bb5e3b2fSeh146360 		/*
1757bb5e3b2fSeh146360 		 * recovery fatal error
1758bb5e3b2fSeh146360 		 */
1759bb5e3b2fSeh146360 		if (ic->ic_mach &&
1760bb5e3b2fSeh146360 		    (sc->sc_flags & IPW2200_FLAG_HW_ERR_RECOVER)) {
1761bb5e3b2fSeh146360 
1762bb5e3b2fSeh146360 			IPW2200_DBG(IPW2200_DBG_FATAL, (sc->sc_dip, CE_CONT,
1763bb5e3b2fSeh146360 			    "ipw2200_thread(): "
1764bb5e3b2fSeh146360 			    "try to recover fatal hw error\n"));
1765bb5e3b2fSeh146360 
1766bb5e3b2fSeh146360 			sc->sc_flags &= ~IPW2200_FLAG_HW_ERR_RECOVER;
1767bb5e3b2fSeh146360 
1768bb5e3b2fSeh146360 			mutex_exit(&sc->sc_mflock);
1769982d85fcSeh146360 
1770982d85fcSeh146360 			ostate = ic->ic_state;
1771bb5e3b2fSeh146360 			(void) ipw2200_init(sc); /* Force state machine */
1772bb5e3b2fSeh146360 			/*
1773bb5e3b2fSeh146360 			 * workround. Delay for a while after init especially
1774bb5e3b2fSeh146360 			 * when something wrong happened already.
1775bb5e3b2fSeh146360 			 */
1776bb5e3b2fSeh146360 			delay(drv_usectohz(delay_fatal_recover));
1777982d85fcSeh146360 
1778982d85fcSeh146360 			/*
1779982d85fcSeh146360 			 * Init scan will recovery the original connection if
1780982d85fcSeh146360 			 * the original state is run
1781982d85fcSeh146360 			 */
1782982d85fcSeh146360 			if (ostate != IEEE80211_S_INIT)
1783982d85fcSeh146360 				ieee80211_begin_scan(ic, 0);
1784982d85fcSeh146360 
1785bb5e3b2fSeh146360 			mutex_enter(&sc->sc_mflock);
1786bb5e3b2fSeh146360 		}
1787bb5e3b2fSeh146360 
1788bb5e3b2fSeh146360 		/*
1789bb5e3b2fSeh146360 		 * get statistic, the value will be retrieved by m_stat
1790bb5e3b2fSeh146360 		 */
1791bb5e3b2fSeh146360 		if (stat_cnt == 10) {
1792bb5e3b2fSeh146360 
1793bb5e3b2fSeh146360 			stat_cnt = 0; /* re-start */
1794bb5e3b2fSeh146360 			mutex_exit(&sc->sc_mflock);
1795bb5e3b2fSeh146360 			ipw2200_get_statistics(sc);
1796bb5e3b2fSeh146360 			mutex_enter(&sc->sc_mflock);
1797bb5e3b2fSeh146360 
1798bb5e3b2fSeh146360 		} else
1799bb5e3b2fSeh146360 			stat_cnt++; /* until 1s */
1800bb5e3b2fSeh146360 
1801bb5e3b2fSeh146360 		mutex_exit(&sc->sc_mflock);
1802bb5e3b2fSeh146360 		delay(drv_usectohz(delay_aux_thread));
1803bb5e3b2fSeh146360 		mutex_enter(&sc->sc_mflock);
1804bb5e3b2fSeh146360 
1805bb5e3b2fSeh146360 	}
1806bb5e3b2fSeh146360 	sc->sc_mf_thread = NULL;
1807bb5e3b2fSeh146360 	cv_signal(&sc->sc_mfthread_cv);
1808bb5e3b2fSeh146360 	mutex_exit(&sc->sc_mflock);
1809bb5e3b2fSeh146360 }
1810bb5e3b2fSeh146360 
1811bb5e3b2fSeh146360 static int
1812bb5e3b2fSeh146360 ipw2200_m_start(void *arg)
1813bb5e3b2fSeh146360 {
1814bb5e3b2fSeh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
1815982d85fcSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
1816bb5e3b2fSeh146360 
1817bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
1818bb5e3b2fSeh146360 	    "ipw2200_m_start(): enter\n"));
1819bb5e3b2fSeh146360 	/*
1820bb5e3b2fSeh146360 	 * initialize ipw2200 hardware, everything ok will start scan
1821bb5e3b2fSeh146360 	 */
1822bb5e3b2fSeh146360 	(void) ipw2200_init(sc);
1823982d85fcSeh146360 	/*
1824982d85fcSeh146360 	 * set the state machine to INIT
1825982d85fcSeh146360 	 */
1826982d85fcSeh146360 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
1827bb5e3b2fSeh146360 
1828bb5e3b2fSeh146360 	sc->sc_flags |= IPW2200_FLAG_RUNNING;
1829bb5e3b2fSeh146360 
1830bb5e3b2fSeh146360 	return (DDI_SUCCESS);
1831bb5e3b2fSeh146360 }
1832bb5e3b2fSeh146360 
1833bb5e3b2fSeh146360 static void
1834bb5e3b2fSeh146360 ipw2200_m_stop(void *arg)
1835bb5e3b2fSeh146360 {
1836bb5e3b2fSeh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
1837982d85fcSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
1838bb5e3b2fSeh146360 
1839bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
1840bb5e3b2fSeh146360 	    "ipw2200_m_stop(): enter\n"));
1841bb5e3b2fSeh146360 
1842bb5e3b2fSeh146360 	ipw2200_stop(sc);
1843982d85fcSeh146360 	/*
1844982d85fcSeh146360 	 * set the state machine to INIT
1845982d85fcSeh146360 	 */
1846982d85fcSeh146360 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
1847bb5e3b2fSeh146360 
1848bb5e3b2fSeh146360 	sc->sc_flags &= ~IPW2200_FLAG_RUNNING;
1849bb5e3b2fSeh146360 }
1850bb5e3b2fSeh146360 
1851bb5e3b2fSeh146360 static int
1852bb5e3b2fSeh146360 ipw2200_m_unicst(void *arg, const uint8_t *macaddr)
1853bb5e3b2fSeh146360 {
1854bb5e3b2fSeh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
1855bb5e3b2fSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
1856bb5e3b2fSeh146360 	int			err;
1857bb5e3b2fSeh146360 
1858bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
1859bb5e3b2fSeh146360 	    "ipw2200_m_unicst(): enter\n"));
1860bb5e3b2fSeh146360 
1861bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
1862bb5e3b2fSeh146360 	    "ipw2200_m_unicst(): GLD setting MAC address to "
1863bb5e3b2fSeh146360 	    "%02x:%02x:%02x:%02x:%02x:%02x\n",
1864bb5e3b2fSeh146360 	    macaddr[0], macaddr[1], macaddr[2],
1865bb5e3b2fSeh146360 	    macaddr[3], macaddr[4], macaddr[5]));
1866bb5e3b2fSeh146360 
1867bb5e3b2fSeh146360 	if (!IEEE80211_ADDR_EQ(ic->ic_macaddr, macaddr)) {
1868bb5e3b2fSeh146360 
1869bb5e3b2fSeh146360 		IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr);
1870bb5e3b2fSeh146360 
1871bb5e3b2fSeh146360 		if (sc->sc_flags & IPW2200_FLAG_RUNNING) {
1872bb5e3b2fSeh146360 			err = ipw2200_config(sc);
1873bb5e3b2fSeh146360 			if (err != DDI_SUCCESS) {
1874bb5e3b2fSeh146360 				IPW2200_WARN((sc->sc_dip, CE_WARN,
1875bb5e3b2fSeh146360 				    "ipw2200_m_unicst(): "
1876bb5e3b2fSeh146360 				    "device configuration failed\n"));
1877bb5e3b2fSeh146360 				goto fail;
1878bb5e3b2fSeh146360 			}
1879bb5e3b2fSeh146360 		}
1880bb5e3b2fSeh146360 	}
1881bb5e3b2fSeh146360 	return (DDI_SUCCESS);
1882bb5e3b2fSeh146360 fail:
1883bb5e3b2fSeh146360 	return (err);
1884bb5e3b2fSeh146360 }
1885bb5e3b2fSeh146360 
1886bb5e3b2fSeh146360 static int
1887bb5e3b2fSeh146360 ipw2200_m_promisc(void *arg, boolean_t on)
1888bb5e3b2fSeh146360 {
1889bb5e3b2fSeh146360 	/* not supported */
1890bb5e3b2fSeh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
1891bb5e3b2fSeh146360 
1892bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
1893bb5e3b2fSeh146360 	    "ipw2200_m_promisc(): enter. "
1894bb5e3b2fSeh146360 	    "GLD setting promiscuous mode - %d\n", on));
1895bb5e3b2fSeh146360 
1896bb5e3b2fSeh146360 	return (DDI_SUCCESS);
1897bb5e3b2fSeh146360 }
1898bb5e3b2fSeh146360 
1899bb5e3b2fSeh146360 static mblk_t *
1900bb5e3b2fSeh146360 ipw2200_m_tx(void *arg, mblk_t *mp)
1901bb5e3b2fSeh146360 {
1902bb5e3b2fSeh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
1903bb5e3b2fSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
1904bb5e3b2fSeh146360 	mblk_t			*next;
1905bb5e3b2fSeh146360 
1906bb5e3b2fSeh146360 	/*
1907bb5e3b2fSeh146360 	 * No data frames go out unless we're associated; this
1908bb5e3b2fSeh146360 	 * should not happen as the 802.11 layer does not enable
1909bb5e3b2fSeh146360 	 * the xmit queue until we enter the RUN state.
1910bb5e3b2fSeh146360 	 */
1911bb5e3b2fSeh146360 	if (ic->ic_state != IEEE80211_S_RUN) {
1912bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
1913bb5e3b2fSeh146360 		    "ipw2200_m_tx(): discard msg, ic_state = %u\n",
1914bb5e3b2fSeh146360 		    ic->ic_state));
1915bb5e3b2fSeh146360 		freemsgchain(mp);
1916bb5e3b2fSeh146360 		return (NULL);
1917bb5e3b2fSeh146360 	}
1918bb5e3b2fSeh146360 
1919bb5e3b2fSeh146360 	while (mp != NULL) {
1920bb5e3b2fSeh146360 		next = mp->b_next;
1921bb5e3b2fSeh146360 		mp->b_next = NULL;
1922bb5e3b2fSeh146360 		if (ipw2200_send(ic, mp, IEEE80211_FC0_TYPE_DATA) ==
1923bb5e3b2fSeh146360 		    DDI_FAILURE) {
1924bb5e3b2fSeh146360 			mp->b_next = next;
1925bb5e3b2fSeh146360 			break;
1926bb5e3b2fSeh146360 		}
1927bb5e3b2fSeh146360 		mp = next;
1928bb5e3b2fSeh146360 	}
1929bb5e3b2fSeh146360 	return (mp);
1930bb5e3b2fSeh146360 }
1931bb5e3b2fSeh146360 
1932bb5e3b2fSeh146360 /* ARGSUSED */
1933bb5e3b2fSeh146360 static int
1934bb5e3b2fSeh146360 ipw2200_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
1935bb5e3b2fSeh146360 {
1936bb5e3b2fSeh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)ic;
1937bb5e3b2fSeh146360 	struct ieee80211_node	*in;
1938bb5e3b2fSeh146360 	struct ieee80211_frame	*wh;
1939bb5e3b2fSeh146360 	mblk_t			*m0;
1940bb5e3b2fSeh146360 	size_t			cnt, off;
1941bb5e3b2fSeh146360 	struct ipw2200_tx_desc	*txdsc;
1942bb5e3b2fSeh146360 	struct dma_region	*dr;
1943bb5e3b2fSeh146360 	uint32_t		idx;
1944bb5e3b2fSeh146360 	int			err;
1945bb5e3b2fSeh146360 	/* tmp pointer, used to pack header and payload */
1946bb5e3b2fSeh146360 	uint8_t			*p;
1947bb5e3b2fSeh146360 
1948bb5e3b2fSeh146360 	ASSERT(mp->b_next == NULL);
1949bb5e3b2fSeh146360 
1950bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
1951bb5e3b2fSeh146360 	    "ipw2200_send(): enter\n"));
1952bb5e3b2fSeh146360 
1953bb5e3b2fSeh146360 	m0 = NULL;
1954bb5e3b2fSeh146360 	err = DDI_SUCCESS;
1955bb5e3b2fSeh146360 
1956bb5e3b2fSeh146360 	if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA) {
1957bb5e3b2fSeh146360 		/*
1958bb5e3b2fSeh146360 		 * skip all management frames since ipw2200 won't generate any
1959bb5e3b2fSeh146360 		 * management frames. Therefore, drop this package.
1960bb5e3b2fSeh146360 		 */
1961bb5e3b2fSeh146360 		freemsg(mp);
1962bb5e3b2fSeh146360 		err = DDI_SUCCESS;
1963bb5e3b2fSeh146360 		goto fail0;
1964bb5e3b2fSeh146360 	}
1965bb5e3b2fSeh146360 
1966bb5e3b2fSeh146360 	mutex_enter(&sc->sc_tx_lock);
1967bb5e3b2fSeh146360 
1968bb5e3b2fSeh146360 	/*
1969bb5e3b2fSeh146360 	 * need 1 empty descriptor
1970bb5e3b2fSeh146360 	 */
1971bb5e3b2fSeh146360 	if (sc->sc_tx_free <= IPW2200_TX_RING_MIN) {
1972bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_WARN,
1973bb5e3b2fSeh146360 		    "ipw2200_send(): no enough descriptors(%d)\n",
1974bb5e3b2fSeh146360 		    sc->sc_tx_free));
1975bb5e3b2fSeh146360 		ic->ic_stats.is_tx_nobuf++; /* no enough buffer */
1976bb5e3b2fSeh146360 		sc->sc_flags |= IPW2200_FLAG_TX_SCHED;
1977bb5e3b2fSeh146360 		err = DDI_FAILURE;
1978bb5e3b2fSeh146360 		goto fail1;
1979bb5e3b2fSeh146360 	}
1980bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT,
1981bb5e3b2fSeh146360 	    "ipw2200_send():  tx-free=%d,tx-curr=%d\n",
1982bb5e3b2fSeh146360 	    sc->sc_tx_free, sc->sc_tx_cur));
1983bb5e3b2fSeh146360 
1984bb5e3b2fSeh146360 	wh = (struct ieee80211_frame *)mp->b_rptr;
1985bb5e3b2fSeh146360 	in = ieee80211_find_txnode(ic, wh->i_addr1);
1986bb5e3b2fSeh146360 	if (in == NULL) { /* can not find the tx node, drop the package */
1987bb5e3b2fSeh146360 		ic->ic_stats.is_tx_failed++;
1988bb5e3b2fSeh146360 		freemsg(mp);
1989bb5e3b2fSeh146360 		err = DDI_SUCCESS;
1990bb5e3b2fSeh146360 		goto fail1;
1991bb5e3b2fSeh146360 	}
1992bb5e3b2fSeh146360 	in->in_inact = 0;
1993bb5e3b2fSeh146360 	(void) ieee80211_encap(ic, mp, in);
1994bb5e3b2fSeh146360 	ieee80211_free_node(in);
1995bb5e3b2fSeh146360 
1996bb5e3b2fSeh146360 	/*
1997bb5e3b2fSeh146360 	 * get txdsc and wh
1998bb5e3b2fSeh146360 	 */
1999bb5e3b2fSeh146360 	idx	= sc->sc_tx_cur;
2000bb5e3b2fSeh146360 	txdsc	= &sc->sc_txdsc[idx];
2001bb5e3b2fSeh146360 	(void) memset(txdsc, 0, sizeof (*txdsc));
2002bb5e3b2fSeh146360 	wh	= (struct ieee80211_frame *)&txdsc->wh;
2003bb5e3b2fSeh146360 
2004bb5e3b2fSeh146360 	/*
2005bb5e3b2fSeh146360 	 * extract header from message
2006bb5e3b2fSeh146360 	 */
2007bb5e3b2fSeh146360 	p	= (uint8_t *)&txdsc->wh;
2008bb5e3b2fSeh146360 	off	= 0;
2009bb5e3b2fSeh146360 	m0	= mp;
2010bb5e3b2fSeh146360 	while (off < sizeof (struct ieee80211_frame)) {
2011bb5e3b2fSeh146360 		cnt = MBLKL(m0);
2012bb5e3b2fSeh146360 		if (cnt > (sizeof (struct ieee80211_frame) - off))
2013bb5e3b2fSeh146360 			cnt = sizeof (struct ieee80211_frame) - off;
2014bb5e3b2fSeh146360 		if (cnt) {
2015bb5e3b2fSeh146360 			(void) memcpy(p + off, m0->b_rptr, cnt);
2016bb5e3b2fSeh146360 			off += cnt;
2017bb5e3b2fSeh146360 			m0->b_rptr += cnt;
2018bb5e3b2fSeh146360 		} else
2019bb5e3b2fSeh146360 			m0 = m0->b_cont;
2020bb5e3b2fSeh146360 	}
2021bb5e3b2fSeh146360 
2022bb5e3b2fSeh146360 	/*
2023bb5e3b2fSeh146360 	 * extract payload from message
2024bb5e3b2fSeh146360 	 */
2025bb5e3b2fSeh146360 	dr	= &sc->sc_dma_txbufs[idx];
2026bb5e3b2fSeh146360 	p	= sc->sc_txbufs[idx];
2027bb5e3b2fSeh146360 	off	= 0;
2028bb5e3b2fSeh146360 	while (m0) {
2029bb5e3b2fSeh146360 		cnt = MBLKL(m0);
2030bb5e3b2fSeh146360 		if (cnt)
2031bb5e3b2fSeh146360 			(void) memcpy(p + off, m0->b_rptr, cnt);
2032bb5e3b2fSeh146360 		off += cnt;
2033bb5e3b2fSeh146360 		m0 = m0->b_cont;
2034bb5e3b2fSeh146360 	}
2035bb5e3b2fSeh146360 
2036bb5e3b2fSeh146360 	txdsc->hdr.type   = IPW2200_HDR_TYPE_DATA;
2037bb5e3b2fSeh146360 	txdsc->hdr.flags  = IPW2200_HDR_FLAG_IRQ;
2038bb5e3b2fSeh146360 	txdsc->cmd	  = IPW2200_DATA_CMD_TX;
2039bb5e3b2fSeh146360 	txdsc->len	  = LE_16(off);
2040bb5e3b2fSeh146360 	txdsc->flags	  = 0;
2041bb5e3b2fSeh146360 
2042bb5e3b2fSeh146360 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
2043bb5e3b2fSeh146360 		if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
2044bb5e3b2fSeh146360 			txdsc->flags |= IPW2200_DATA_FLAG_NEED_ACK;
2045bb5e3b2fSeh146360 	} else if (!IEEE80211_IS_MULTICAST(wh->i_addr3))
2046bb5e3b2fSeh146360 		txdsc->flags |= IPW2200_DATA_FLAG_NEED_ACK;
2047bb5e3b2fSeh146360 
2048bb5e3b2fSeh146360 	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
2049bb5e3b2fSeh146360 		wh->i_fc[1] |= IEEE80211_FC1_WEP;
2050bb5e3b2fSeh146360 		txdsc->wep_txkey = ic->ic_def_txkey;
2051bb5e3b2fSeh146360 	}
2052bb5e3b2fSeh146360 	else
2053bb5e3b2fSeh146360 		txdsc->flags |= IPW2200_DATA_FLAG_NO_WEP;
2054bb5e3b2fSeh146360 
2055bb5e3b2fSeh146360 	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
2056bb5e3b2fSeh146360 		txdsc->flags |= IPW2200_DATA_FLAG_SHPREAMBLE;
2057bb5e3b2fSeh146360 
2058bb5e3b2fSeh146360 	txdsc->nseg	    = LE_32(1);
2059bb5e3b2fSeh146360 	txdsc->seg_addr[0]  = LE_32(dr->dr_pbase);
2060bb5e3b2fSeh146360 	txdsc->seg_len[0]   = LE_32(off);
2061bb5e3b2fSeh146360 
2062bb5e3b2fSeh146360 	/*
2063bb5e3b2fSeh146360 	 * DMA sync: buffer and desc
2064bb5e3b2fSeh146360 	 */
2065bb5e3b2fSeh146360 	(void) ddi_dma_sync(dr->dr_hnd, 0,
2066bb5e3b2fSeh146360 	    IPW2200_TXBUF_SIZE, DDI_DMA_SYNC_FORDEV);
2067bb5e3b2fSeh146360 	(void) ddi_dma_sync(sc->sc_dma_txdsc.dr_hnd,
2068bb5e3b2fSeh146360 	    idx * sizeof (struct ipw2200_tx_desc),
2069bb5e3b2fSeh146360 	    sizeof (struct ipw2200_tx_desc), DDI_DMA_SYNC_FORDEV);
2070bb5e3b2fSeh146360 
2071bb5e3b2fSeh146360 	sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2200_TX_RING_SIZE);
2072bb5e3b2fSeh146360 	sc->sc_tx_free--;
2073bb5e3b2fSeh146360 
2074bb5e3b2fSeh146360 	/*
2075bb5e3b2fSeh146360 	 * update txcur
2076bb5e3b2fSeh146360 	 */
2077bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX1_WRITE_INDEX, sc->sc_tx_cur);
2078bb5e3b2fSeh146360 
2079bb5e3b2fSeh146360 	/*
2080bb5e3b2fSeh146360 	 * success, free the original message
2081bb5e3b2fSeh146360 	 */
2082bb5e3b2fSeh146360 	if (mp)
2083bb5e3b2fSeh146360 		freemsg(mp);
2084bb5e3b2fSeh146360 
2085bb5e3b2fSeh146360 fail1:
2086bb5e3b2fSeh146360 	mutex_exit(&sc->sc_tx_lock);
2087bb5e3b2fSeh146360 fail0:
2088bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
2089bb5e3b2fSeh146360 	    "ipw2200_send(): exit - err=%d\n", err));
2090bb5e3b2fSeh146360 
2091bb5e3b2fSeh146360 	return (err);
2092bb5e3b2fSeh146360 }
2093bb5e3b2fSeh146360 
2094bb5e3b2fSeh146360 /*
2095bb5e3b2fSeh146360  * IOCTL handlers
2096bb5e3b2fSeh146360  */
2097bb5e3b2fSeh146360 #define	IEEE80211_IOCTL_REQUIRED	(1)
2098bb5e3b2fSeh146360 #define	IEEE80211_IOCTL_NOT_REQUIRED	(0)
2099bb5e3b2fSeh146360 static void
2100bb5e3b2fSeh146360 ipw2200_m_ioctl(void *arg, queue_t *q, mblk_t *m)
2101bb5e3b2fSeh146360 {
2102bb5e3b2fSeh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
2103bb5e3b2fSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
2104bb5e3b2fSeh146360 	uint32_t		err;
2105bb5e3b2fSeh146360 
2106bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
2107bb5e3b2fSeh146360 	    "ipw2200_m_ioctl(): enter\n"));
2108bb5e3b2fSeh146360 
2109bb5e3b2fSeh146360 	/*
2110bb5e3b2fSeh146360 	 * Check whether or not need to handle this in net80211
2111bb5e3b2fSeh146360 	 *
2112bb5e3b2fSeh146360 	 */
2113bb5e3b2fSeh146360 	if (ipw2200_ioctl(sc, q, m) == IEEE80211_IOCTL_NOT_REQUIRED)
2114bb5e3b2fSeh146360 		return;
2115bb5e3b2fSeh146360 
2116bb5e3b2fSeh146360 	err = ieee80211_ioctl(ic, q, m);
2117bb5e3b2fSeh146360 	if (err == ENETRESET) {
2118bb5e3b2fSeh146360 		if (sc->sc_flags & IPW2200_FLAG_RUNNING) {
2119bb5e3b2fSeh146360 			(void) ipw2200_m_start(sc);
2120bb5e3b2fSeh146360 			(void) ieee80211_new_state(ic,
2121bb5e3b2fSeh146360 			    IEEE80211_S_SCAN, -1);
2122bb5e3b2fSeh146360 		}
2123bb5e3b2fSeh146360 	}
2124bb5e3b2fSeh146360 	if (err == ERESTART) {
2125bb5e3b2fSeh146360 		if (sc->sc_flags & IPW2200_FLAG_RUNNING)
2126bb5e3b2fSeh146360 			(void) ipw2200_chip_reset(sc);
2127bb5e3b2fSeh146360 	}
2128bb5e3b2fSeh146360 }
2129bb5e3b2fSeh146360 static int
2130bb5e3b2fSeh146360 ipw2200_ioctl(struct ipw2200_softc *sc, queue_t *q, mblk_t *m)
2131bb5e3b2fSeh146360 {
2132bb5e3b2fSeh146360 	struct iocblk	*iocp;
2133bb5e3b2fSeh146360 	uint32_t	len, ret, cmd;
2134bb5e3b2fSeh146360 	mblk_t		*m0;
2135bb5e3b2fSeh146360 	boolean_t	need_privilege;
2136bb5e3b2fSeh146360 	boolean_t	need_net80211;
2137bb5e3b2fSeh146360 
2138bb5e3b2fSeh146360 	if (MBLKL(m) < sizeof (struct iocblk)) {
2139bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
2140bb5e3b2fSeh146360 		    "ipw2200_ioctl(): ioctl buffer too short, %u\n",
2141bb5e3b2fSeh146360 		    MBLKL(m)));
2142bb5e3b2fSeh146360 		miocnak(q, m, 0, EINVAL);
2143bb5e3b2fSeh146360 		/*
2144bb5e3b2fSeh146360 		 * Buf not enough, do not need net80211 either
2145bb5e3b2fSeh146360 		 */
2146bb5e3b2fSeh146360 		return (IEEE80211_IOCTL_NOT_REQUIRED);
2147bb5e3b2fSeh146360 	}
2148bb5e3b2fSeh146360 
2149bb5e3b2fSeh146360 	/*
2150bb5e3b2fSeh146360 	 * Validate the command
2151bb5e3b2fSeh146360 	 */
2152bb5e3b2fSeh146360 	iocp = (struct iocblk *)m->b_rptr;
2153bb5e3b2fSeh146360 	iocp->ioc_error = 0;
2154bb5e3b2fSeh146360 	cmd = iocp->ioc_cmd;
2155bb5e3b2fSeh146360 	need_privilege = B_TRUE;
2156bb5e3b2fSeh146360 	switch (cmd) {
2157bb5e3b2fSeh146360 	case WLAN_SET_PARAM:
2158bb5e3b2fSeh146360 	case WLAN_COMMAND:
2159bb5e3b2fSeh146360 		break;
2160bb5e3b2fSeh146360 	case WLAN_GET_PARAM:
2161bb5e3b2fSeh146360 		need_privilege = B_FALSE;
2162bb5e3b2fSeh146360 		break;
2163bb5e3b2fSeh146360 	default:
2164bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
2165bb5e3b2fSeh146360 		    "ipw2200_ioctl(): unknown cmd 0x%x", cmd));
2166bb5e3b2fSeh146360 		miocnak(q, m, 0, EINVAL);
2167bb5e3b2fSeh146360 		/*
2168bb5e3b2fSeh146360 		 * Unknown cmd, do not need net80211 either
2169bb5e3b2fSeh146360 		 */
2170bb5e3b2fSeh146360 		return (IEEE80211_IOCTL_NOT_REQUIRED);
2171bb5e3b2fSeh146360 	}
2172bb5e3b2fSeh146360 
2173bb5e3b2fSeh146360 	if (need_privilege) {
2174bb5e3b2fSeh146360 		/*
2175bb5e3b2fSeh146360 		 * Check for specific net_config privilege on Solaris 10+.
2176bb5e3b2fSeh146360 		 * Otherwise just check for root access ...
2177bb5e3b2fSeh146360 		 */
2178bb5e3b2fSeh146360 		if (secpolicy_net_config != NULL)
2179bb5e3b2fSeh146360 			ret = secpolicy_net_config(iocp->ioc_cr, B_FALSE);
2180bb5e3b2fSeh146360 		else
2181bb5e3b2fSeh146360 			ret = drv_priv(iocp->ioc_cr);
2182bb5e3b2fSeh146360 		if (ret != 0) {
2183bb5e3b2fSeh146360 			miocnak(q, m, 0, ret);
2184bb5e3b2fSeh146360 			/*
2185bb5e3b2fSeh146360 			 * privilege check fail, do not need net80211 either
2186bb5e3b2fSeh146360 			 */
2187bb5e3b2fSeh146360 			return (IEEE80211_IOCTL_NOT_REQUIRED);
2188bb5e3b2fSeh146360 		}
2189bb5e3b2fSeh146360 	}
2190bb5e3b2fSeh146360 	/*
2191bb5e3b2fSeh146360 	 * sanity check
2192bb5e3b2fSeh146360 	 */
2193bb5e3b2fSeh146360 	m0 = m->b_cont;
2194bb5e3b2fSeh146360 	if (iocp->ioc_count == 0 || iocp->ioc_count < sizeof (wldp_t) ||
2195bb5e3b2fSeh146360 	    m0 == NULL) {
2196bb5e3b2fSeh146360 		miocnak(q, m, 0, EINVAL);
2197bb5e3b2fSeh146360 		/*
2198bb5e3b2fSeh146360 		 * invalid format, do not need net80211 either
2199bb5e3b2fSeh146360 		 */
2200bb5e3b2fSeh146360 		return (IEEE80211_IOCTL_NOT_REQUIRED);
2201bb5e3b2fSeh146360 	}
2202bb5e3b2fSeh146360 	/*
2203bb5e3b2fSeh146360 	 * assuming single data block
2204bb5e3b2fSeh146360 	 */
2205bb5e3b2fSeh146360 	if (m0->b_cont) {
2206bb5e3b2fSeh146360 		freemsg(m0->b_cont);
2207bb5e3b2fSeh146360 		m0->b_cont = NULL;
2208bb5e3b2fSeh146360 	}
2209bb5e3b2fSeh146360 
2210bb5e3b2fSeh146360 	need_net80211 = B_FALSE;
2211bb5e3b2fSeh146360 	ret = ipw2200_getset(sc, m0, cmd, &need_net80211);
2212bb5e3b2fSeh146360 	if (!need_net80211) {
2213bb5e3b2fSeh146360 		len = msgdsize(m0);
2214bb5e3b2fSeh146360 
2215bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
2216bb5e3b2fSeh146360 		    "ipw2200_ioctl(): go to call miocack with "
2217bb5e3b2fSeh146360 		    "ret = %d, len = %d\n", ret, len));
2218bb5e3b2fSeh146360 		miocack(q, m, len, ret);
2219bb5e3b2fSeh146360 		return (IEEE80211_IOCTL_NOT_REQUIRED);
2220bb5e3b2fSeh146360 	}
2221bb5e3b2fSeh146360 
2222bb5e3b2fSeh146360 	/*
2223bb5e3b2fSeh146360 	 * IEEE80211_IOCTL - need net80211 handle
2224bb5e3b2fSeh146360 	 */
2225bb5e3b2fSeh146360 	return (IEEE80211_IOCTL_REQUIRED);
2226bb5e3b2fSeh146360 }
2227bb5e3b2fSeh146360 
2228bb5e3b2fSeh146360 static int
2229bb5e3b2fSeh146360 ipw2200_getset(struct ipw2200_softc *sc, mblk_t *m, uint32_t cmd,
2230bb5e3b2fSeh146360 	boolean_t *need_net80211)
2231bb5e3b2fSeh146360 {
2232bb5e3b2fSeh146360 	wldp_t		*infp, *outfp;
2233bb5e3b2fSeh146360 	uint32_t	id;
2234bb5e3b2fSeh146360 	int		ret;
2235bb5e3b2fSeh146360 
2236bb5e3b2fSeh146360 	infp = (wldp_t *)m->b_rptr;
2237bb5e3b2fSeh146360 	outfp = (wldp_t *)m->b_rptr;
2238bb5e3b2fSeh146360 	outfp->wldp_result = WL_NOTSUPPORTED;
2239bb5e3b2fSeh146360 
2240bb5e3b2fSeh146360 	id = infp->wldp_id;
2241bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
2242bb5e3b2fSeh146360 	    "ipw2200_getset(): id = 0x%x\n", id));
2243bb5e3b2fSeh146360 	switch (id) {
2244bb5e3b2fSeh146360 	case WL_RADIO: /* which is not supported by net80211 */
2245bb5e3b2fSeh146360 		ret = iwi_wificfg_radio(sc, cmd, outfp);
2246bb5e3b2fSeh146360 		break;
2247bb5e3b2fSeh146360 	case WL_DESIRED_RATES: /* hardware doesn't support fix-rates */
2248bb5e3b2fSeh146360 		ret = iwi_wificfg_desrates(outfp);
2249bb5e3b2fSeh146360 		break;
2250bb5e3b2fSeh146360 	default:
2251bb5e3b2fSeh146360 		/*
2252bb5e3b2fSeh146360 		 * The wifi IOCTL net80211 supported:
2253bb5e3b2fSeh146360 		 *	case WL_ESSID:
2254bb5e3b2fSeh146360 		 *	case WL_BSSID:
2255bb5e3b2fSeh146360 		 *	case WL_WEP_KEY_TAB:
2256bb5e3b2fSeh146360 		 *	case WL_WEP_KEY_ID:
2257bb5e3b2fSeh146360 		 *	case WL_AUTH_MODE:
2258bb5e3b2fSeh146360 		 *	case WL_ENCRYPTION:
2259bb5e3b2fSeh146360 		 *	case WL_BSS_TYPE:
2260bb5e3b2fSeh146360 		 *	case WL_ESS_LIST:
2261bb5e3b2fSeh146360 		 *	case WL_LINKSTATUS:
2262bb5e3b2fSeh146360 		 *	case WL_RSSI:
2263bb5e3b2fSeh146360 		 *	case WL_SCAN:
2264bb5e3b2fSeh146360 		 *	case WL_LOAD_DEFAULTS:
2265bb5e3b2fSeh146360 		 *	case WL_DISASSOCIATE:
2266bb5e3b2fSeh146360 		 */
2267bb5e3b2fSeh146360 		*need_net80211 = B_TRUE; /* let net80211 do the rest */
2268bb5e3b2fSeh146360 		return (0);
2269bb5e3b2fSeh146360 	}
2270bb5e3b2fSeh146360 	/*
2271bb5e3b2fSeh146360 	 * we will overwrite everything
2272bb5e3b2fSeh146360 	 */
2273bb5e3b2fSeh146360 	m->b_wptr = m->b_rptr + outfp->wldp_length;
2274bb5e3b2fSeh146360 	return (ret);
2275bb5e3b2fSeh146360 }
2276bb5e3b2fSeh146360 
2277bb5e3b2fSeh146360 static int
2278bb5e3b2fSeh146360 iwi_wificfg_radio(struct ipw2200_softc *sc, uint32_t cmd, wldp_t *outfp)
2279bb5e3b2fSeh146360 {
2280bb5e3b2fSeh146360 	uint32_t	ret = ENOTSUP;
2281bb5e3b2fSeh146360 
2282bb5e3b2fSeh146360 	switch (cmd) {
2283bb5e3b2fSeh146360 	case WLAN_GET_PARAM:
2284bb5e3b2fSeh146360 		*(wl_linkstatus_t *)(outfp->wldp_buf) =
2285bb5e3b2fSeh146360 		    ipw2200_radio_status(sc);
2286bb5e3b2fSeh146360 		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_linkstatus_t);
2287bb5e3b2fSeh146360 		outfp->wldp_result = WL_SUCCESS;
2288bb5e3b2fSeh146360 		ret = 0; /* command success */
2289bb5e3b2fSeh146360 		break;
2290bb5e3b2fSeh146360 	case WLAN_SET_PARAM:
2291bb5e3b2fSeh146360 	default:
2292bb5e3b2fSeh146360 		break;
2293bb5e3b2fSeh146360 	}
2294bb5e3b2fSeh146360 	return (ret);
2295bb5e3b2fSeh146360 }
2296bb5e3b2fSeh146360 
2297bb5e3b2fSeh146360 static int
2298bb5e3b2fSeh146360 iwi_wificfg_desrates(wldp_t *outfp)
2299bb5e3b2fSeh146360 {
2300bb5e3b2fSeh146360 	/* return success, but with result NOTSUPPORTED */
2301bb5e3b2fSeh146360 	outfp->wldp_length = WIFI_BUF_OFFSET;
2302bb5e3b2fSeh146360 	outfp->wldp_result = WL_NOTSUPPORTED;
2303bb5e3b2fSeh146360 	return (0);
2304bb5e3b2fSeh146360 }
2305bb5e3b2fSeh146360 /* End of IOCTL Handlers */
2306bb5e3b2fSeh146360 
2307bb5e3b2fSeh146360 void
2308bb5e3b2fSeh146360 ipw2200_fix_channel(struct ieee80211com *ic, mblk_t *m)
2309bb5e3b2fSeh146360 {
2310bb5e3b2fSeh146360 	struct ieee80211_frame	*wh;
2311bb5e3b2fSeh146360 	uint8_t			subtype;
2312bb5e3b2fSeh146360 	uint8_t			*frm, *efrm;
2313bb5e3b2fSeh146360 
2314bb5e3b2fSeh146360 	wh = (struct ieee80211_frame *)m->b_rptr;
2315bb5e3b2fSeh146360 
2316bb5e3b2fSeh146360 	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT)
2317bb5e3b2fSeh146360 		return;
2318bb5e3b2fSeh146360 
2319bb5e3b2fSeh146360 	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
2320bb5e3b2fSeh146360 
2321bb5e3b2fSeh146360 	if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
2322bb5e3b2fSeh146360 	    subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP)
2323bb5e3b2fSeh146360 		return;
2324bb5e3b2fSeh146360 
2325bb5e3b2fSeh146360 	/*
2326bb5e3b2fSeh146360 	 * assume the message contains only 1 block
2327bb5e3b2fSeh146360 	 */
2328bb5e3b2fSeh146360 	frm   = (uint8_t *)(wh + 1);
2329bb5e3b2fSeh146360 	efrm  = (uint8_t *)m->b_wptr;
2330bb5e3b2fSeh146360 	frm  += 12;  /* skip tstamp, bintval and capinfo fields */
2331bb5e3b2fSeh146360 	while (frm < efrm) {
2332bb5e3b2fSeh146360 		if (*frm == IEEE80211_ELEMID_DSPARMS)
2333bb5e3b2fSeh146360 #if IEEE80211_CHAN_MAX < 255
2334bb5e3b2fSeh146360 		if (frm[2] <= IEEE80211_CHAN_MAX)
2335bb5e3b2fSeh146360 #endif
2336bb5e3b2fSeh146360 			ic->ic_curchan = &ic->ic_sup_channels[frm[2]];
2337bb5e3b2fSeh146360 		frm += frm[1] + 2;
2338bb5e3b2fSeh146360 	}
2339bb5e3b2fSeh146360 }
2340bb5e3b2fSeh146360 
2341bb5e3b2fSeh146360 static void
2342bb5e3b2fSeh146360 ipw2200_rcv_frame(struct ipw2200_softc *sc, struct ipw2200_frame *frame)
2343bb5e3b2fSeh146360 {
2344bb5e3b2fSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
2345bb5e3b2fSeh146360 	uint8_t			*data = (uint8_t *)frame;
2346bb5e3b2fSeh146360 	uint32_t		len;
2347bb5e3b2fSeh146360 	struct ieee80211_frame	*wh;
2348bb5e3b2fSeh146360 	struct ieee80211_node	*in;
2349bb5e3b2fSeh146360 	mblk_t			*m;
2350bb5e3b2fSeh146360 	int			i;
2351bb5e3b2fSeh146360 
2352bb5e3b2fSeh146360 	len = LE_16(frame->len);
2353bb5e3b2fSeh146360 
2354bb5e3b2fSeh146360 	/*
2355bb5e3b2fSeh146360 	 * Skip the frame header, get the real data from the input
2356bb5e3b2fSeh146360 	 */
2357bb5e3b2fSeh146360 	data += sizeof (struct ipw2200_frame);
2358bb5e3b2fSeh146360 
2359bb5e3b2fSeh146360 	if ((len < sizeof (struct ieee80211_frame_min)) ||
2360bb5e3b2fSeh146360 	    (len > IPW2200_RXBUF_SIZE)) {
2361bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT,
2362bb5e3b2fSeh146360 		    "ipw2200_rcv_frame(): bad frame length=%u\n",
2363bb5e3b2fSeh146360 		    LE_16(frame->len)));
2364bb5e3b2fSeh146360 		return;
2365bb5e3b2fSeh146360 	}
2366bb5e3b2fSeh146360 
2367bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT,
2368bb5e3b2fSeh146360 	    "ipw2200_rcv_frame(): chan = %d, length = %d\n", frame->chan, len));
2369bb5e3b2fSeh146360 
2370bb5e3b2fSeh146360 	m = allocb(len, BPRI_MED);
2371bb5e3b2fSeh146360 	if (m) {
2372bb5e3b2fSeh146360 		(void) memcpy(m->b_wptr, data, len);
2373bb5e3b2fSeh146360 		m->b_wptr += len;
2374bb5e3b2fSeh146360 
2375bb5e3b2fSeh146360 		wh = (struct ieee80211_frame *)m->b_rptr;
2376bb5e3b2fSeh146360 		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
2377bb5e3b2fSeh146360 			/*
2378bb5e3b2fSeh146360 			 * h/w decyption leaves the WEP bit, iv and CRC fields
2379bb5e3b2fSeh146360 			 */
2380bb5e3b2fSeh146360 			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
2381bb5e3b2fSeh146360 			for (i = sizeof (struct ieee80211_frame) - 1;
2382bb5e3b2fSeh146360 			    i >= 0; i--)
2383bb5e3b2fSeh146360 				*(m->b_rptr + IEEE80211_WEP_IVLEN +
2384bb5e3b2fSeh146360 				    IEEE80211_WEP_KIDLEN + i) =
2385bb5e3b2fSeh146360 				    *(m->b_rptr + i);
2386bb5e3b2fSeh146360 			m->b_rptr += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
2387bb5e3b2fSeh146360 			m->b_wptr -= IEEE80211_WEP_CRCLEN;
2388bb5e3b2fSeh146360 			wh = (struct ieee80211_frame *)m->b_rptr;
2389bb5e3b2fSeh146360 		}
2390bb5e3b2fSeh146360 
2391bb5e3b2fSeh146360 		if (ic->ic_state == IEEE80211_S_SCAN) {
2392bb5e3b2fSeh146360 			ic->ic_ibss_chan = &ic->ic_sup_channels[frame->chan];
2393bb5e3b2fSeh146360 			ipw2200_fix_channel(ic, m);
2394bb5e3b2fSeh146360 		}
2395bb5e3b2fSeh146360 
2396bb5e3b2fSeh146360 		in = ieee80211_find_rxnode(ic, wh);
2397bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT,
2398bb5e3b2fSeh146360 		    "ipw2200_rcv_frame(): "
2399bb5e3b2fSeh146360 		    "ni_esslen:%d, ni_essid[0-5]:%c%c%c%c%c%c\n",
2400bb5e3b2fSeh146360 		    in->in_esslen,
2401bb5e3b2fSeh146360 		    in->in_essid[0], in->in_essid[1], in->in_essid[2],
2402bb5e3b2fSeh146360 		    in->in_essid[3], in->in_essid[4], in->in_essid[5]));
2403bb5e3b2fSeh146360 
2404bb5e3b2fSeh146360 		(void) ieee80211_input(ic, m, in, frame->rssi_dbm, 0);
2405bb5e3b2fSeh146360 
2406bb5e3b2fSeh146360 		ieee80211_free_node(in);
2407bb5e3b2fSeh146360 	}
2408bb5e3b2fSeh146360 	else
2409bb5e3b2fSeh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
2410bb5e3b2fSeh146360 		    "ipw2200_rcv_frame(): "
2411bb5e3b2fSeh146360 		    "cannot allocate receive message(%u)\n",
2412bb5e3b2fSeh146360 		    LE_16(frame->len)));
2413bb5e3b2fSeh146360 }
2414bb5e3b2fSeh146360 
2415bb5e3b2fSeh146360 static void
2416bb5e3b2fSeh146360 ipw2200_rcv_notif(struct ipw2200_softc *sc, struct ipw2200_notif *notif)
2417bb5e3b2fSeh146360 {
2418bb5e3b2fSeh146360 	struct ieee80211com			*ic = &sc->sc_ic;
2419bb5e3b2fSeh146360 	struct ipw2200_notif_association	*assoc;
2420bb5e3b2fSeh146360 	struct ipw2200_notif_authentication	*auth;
2421bb5e3b2fSeh146360 	uint8_t					*ndata = (uint8_t *)notif;
2422bb5e3b2fSeh146360 
2423bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT,
2424bb5e3b2fSeh146360 	    "ipw2200_rcv_notif(): type=%u\n", notif->type));
2425bb5e3b2fSeh146360 
2426bb5e3b2fSeh146360 	ndata += sizeof (struct ipw2200_notif);
2427bb5e3b2fSeh146360 	switch (notif->type) {
2428bb5e3b2fSeh146360 	case IPW2200_NOTIF_TYPE_ASSOCIATION:
2429bb5e3b2fSeh146360 		assoc = (struct ipw2200_notif_association *)ndata;
2430bb5e3b2fSeh146360 
2431bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
2432bb5e3b2fSeh146360 		    "ipw2200_rcv_notif(): association=%u,%u\n",
2433bb5e3b2fSeh146360 		    assoc->state, assoc->status));
2434bb5e3b2fSeh146360 
2435bb5e3b2fSeh146360 		switch (assoc->state) {
2436bb5e3b2fSeh146360 		case IPW2200_ASSOC_SUCCESS:
2437bb5e3b2fSeh146360 			ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
2438bb5e3b2fSeh146360 			break;
2439bb5e3b2fSeh146360 		case IPW2200_ASSOC_FAIL:
2440bb5e3b2fSeh146360 			ieee80211_begin_scan(ic, 1); /* reset */
2441bb5e3b2fSeh146360 			break;
2442bb5e3b2fSeh146360 		default:
2443bb5e3b2fSeh146360 			break;
2444bb5e3b2fSeh146360 		}
2445bb5e3b2fSeh146360 		break;
2446bb5e3b2fSeh146360 
2447bb5e3b2fSeh146360 	case IPW2200_NOTIF_TYPE_AUTHENTICATION:
2448bb5e3b2fSeh146360 		auth = (struct ipw2200_notif_authentication *)ndata;
2449bb5e3b2fSeh146360 
2450bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
2451bb5e3b2fSeh146360 		    "ipw2200_rcv_notif(): authentication=%u\n", auth->state));
2452bb5e3b2fSeh146360 
2453bb5e3b2fSeh146360 		switch (auth->state) {
2454bb5e3b2fSeh146360 		case IPW2200_AUTH_SUCCESS:
2455bb5e3b2fSeh146360 			ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
2456bb5e3b2fSeh146360 			break;
2457bb5e3b2fSeh146360 		case IPW2200_AUTH_FAIL:
2458bb5e3b2fSeh146360 			break;
2459bb5e3b2fSeh146360 		default:
2460bb5e3b2fSeh146360 			IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT,
2461bb5e3b2fSeh146360 			    "ipw2200_rcv_notif(): "
2462bb5e3b2fSeh146360 			    "unknown authentication state(%u)\n", auth->state));
2463bb5e3b2fSeh146360 			break;
2464bb5e3b2fSeh146360 		}
2465bb5e3b2fSeh146360 		break;
2466bb5e3b2fSeh146360 
2467bb5e3b2fSeh146360 	case IPW2200_NOTIF_TYPE_SCAN_CHANNEL:
2468bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT,
2469bb5e3b2fSeh146360 		    "ipw2200_rcv_notif(): scan-channel=%u\n",
2470bb5e3b2fSeh146360 		    ((struct ipw2200_notif_scan_channel *)ndata)->nchan));
2471bb5e3b2fSeh146360 		break;
2472bb5e3b2fSeh146360 
2473bb5e3b2fSeh146360 	case IPW2200_NOTIF_TYPE_SCAN_COMPLETE:
2474bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT,
2475bb5e3b2fSeh146360 		    "ipw2200_rcv_notif():scan-completed,(%u,%u)\n",
2476bb5e3b2fSeh146360 		    ((struct ipw2200_notif_scan_complete *)ndata)->nchan,
2477bb5e3b2fSeh146360 		    ((struct ipw2200_notif_scan_complete *)ndata)->status));
2478bb5e3b2fSeh146360 
2479bb5e3b2fSeh146360 		/*
2480bb5e3b2fSeh146360 		 * scan complete
2481bb5e3b2fSeh146360 		 */
2482bb5e3b2fSeh146360 		sc->sc_flags &= ~IPW2200_FLAG_SCANNING;
2483bb5e3b2fSeh146360 		ieee80211_end_scan(ic);
2484bb5e3b2fSeh146360 		break;
2485bb5e3b2fSeh146360 
2486bb5e3b2fSeh146360 	case IPW2200_NOTIF_TYPE_BEACON:
2487bb5e3b2fSeh146360 	case IPW2200_NOTIF_TYPE_CALIBRATION:
2488bb5e3b2fSeh146360 	case IPW2200_NOTIF_TYPE_NOISE:
2489bb5e3b2fSeh146360 		/*
2490bb5e3b2fSeh146360 		 * just ignore
2491bb5e3b2fSeh146360 		 */
2492bb5e3b2fSeh146360 		break;
2493bb5e3b2fSeh146360 	default:
2494bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT,
2495bb5e3b2fSeh146360 		    "ipw2200_rcv_notif(): unknown notification type(%u)\n",
2496bb5e3b2fSeh146360 		    notif->type));
2497bb5e3b2fSeh146360 		break;
2498bb5e3b2fSeh146360 	}
2499bb5e3b2fSeh146360 }
2500bb5e3b2fSeh146360 
2501bb5e3b2fSeh146360 static uint_t
2502bb5e3b2fSeh146360 ipw2200_intr(caddr_t arg)
2503bb5e3b2fSeh146360 {
2504bb5e3b2fSeh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
2505bb5e3b2fSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
2506bb5e3b2fSeh146360 	uint32_t		ireg, ridx, len, i;
2507bb5e3b2fSeh146360 	uint8_t			*p, *rxbuf;
2508bb5e3b2fSeh146360 	struct dma_region	*dr;
2509bb5e3b2fSeh146360 	struct ipw2200_hdr	*hdr;
2510bb5e3b2fSeh146360 	int			need_sched;
2511bb5e3b2fSeh146360 	uint32_t		widx;
2512bb5e3b2fSeh146360 
2513bb5e3b2fSeh146360 	ireg = ipw2200_csr_get32(sc, IPW2200_CSR_INTR);
2514bb5e3b2fSeh146360 
2515bb5e3b2fSeh146360 	if (ireg == 0xffffffff)
2516bb5e3b2fSeh146360 		return (DDI_INTR_UNCLAIMED);
2517bb5e3b2fSeh146360 
2518bb5e3b2fSeh146360 	if (!(ireg & IPW2200_INTR_MASK_ALL))
2519bb5e3b2fSeh146360 		return (DDI_INTR_UNCLAIMED);
2520bb5e3b2fSeh146360 
2521bb5e3b2fSeh146360 	/*
2522bb5e3b2fSeh146360 	 * mask all interrupts
2523bb5e3b2fSeh146360 	 */
2524bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, 0);
2525bb5e3b2fSeh146360 
2526bb5e3b2fSeh146360 	/*
2527bb5e3b2fSeh146360 	 * acknowledge all fired interrupts
2528bb5e3b2fSeh146360 	 */
2529bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INTR, ireg);
2530bb5e3b2fSeh146360 
2531bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT,
2532bb5e3b2fSeh146360 	    "ipw2200_intr(): enter. interrupt fired, int=0x%08x\n", ireg));
2533bb5e3b2fSeh146360 
2534bb5e3b2fSeh146360 	if (ireg & IPW2200_INTR_MASK_ERR) {
2535bb5e3b2fSeh146360 
2536bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_FATAL, (sc->sc_dip, CE_CONT,
2537bb5e3b2fSeh146360 		    "ipw2200 interrupt(): int= 0x%08x\n", ireg));
2538bb5e3b2fSeh146360 
2539bb5e3b2fSeh146360 		/*
2540bb5e3b2fSeh146360 		 * inform mfthread to recover hw error by stopping it
2541bb5e3b2fSeh146360 		 */
2542bb5e3b2fSeh146360 		mutex_enter(&sc->sc_mflock);
2543bb5e3b2fSeh146360 		sc->sc_flags |= IPW2200_FLAG_HW_ERR_RECOVER;
2544bb5e3b2fSeh146360 		mutex_exit(&sc->sc_mflock);
2545bb5e3b2fSeh146360 
2546bb5e3b2fSeh146360 	} else {
2547bb5e3b2fSeh146360 		if (ireg & IPW2200_INTR_FW_INITED) {
2548bb5e3b2fSeh146360 			mutex_enter(&sc->sc_ilock);
2549bb5e3b2fSeh146360 			sc->sc_fw_ok = 1;
2550bb5e3b2fSeh146360 			cv_signal(&sc->sc_fw_cond);
2551bb5e3b2fSeh146360 			mutex_exit(&sc->sc_ilock);
2552bb5e3b2fSeh146360 		}
2553bb5e3b2fSeh146360 		if (ireg & IPW2200_INTR_RADIO_OFF) {
2554bb5e3b2fSeh146360 			IPW2200_REPORT((sc->sc_dip, CE_CONT,
2555bb5e3b2fSeh146360 			    "ipw2200_intr(): radio is OFF\n"));
2556bb5e3b2fSeh146360 			/*
2557bb5e3b2fSeh146360 			 * Stop hardware, will notify LINK is down
2558bb5e3b2fSeh146360 			 */
2559bb5e3b2fSeh146360 			ipw2200_stop(sc);
2560bb5e3b2fSeh146360 		}
2561bb5e3b2fSeh146360 		if (ireg & IPW2200_INTR_CMD_TRANSFER) {
2562bb5e3b2fSeh146360 			mutex_enter(&sc->sc_cmd_lock);
2563bb5e3b2fSeh146360 			ridx = ipw2200_csr_get32(sc,
2564bb5e3b2fSeh146360 			    IPW2200_CSR_CMD_READ_INDEX);
2565bb5e3b2fSeh146360 			i = RING_FORWARD(sc->sc_cmd_cur,
2566bb5e3b2fSeh146360 			    sc->sc_cmd_free, IPW2200_CMD_RING_SIZE);
2567bb5e3b2fSeh146360 			len = RING_FLEN(i, ridx, IPW2200_CMD_RING_SIZE);
2568bb5e3b2fSeh146360 
2569bb5e3b2fSeh146360 			IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT,
2570bb5e3b2fSeh146360 			    "ipw2200_intr(): cmd-ring,i=%u,ridx=%u,len=%u\n",
2571bb5e3b2fSeh146360 			    i, ridx, len));
2572bb5e3b2fSeh146360 
2573bb5e3b2fSeh146360 			if (len > 0) {
2574bb5e3b2fSeh146360 				sc->sc_cmd_free += len;
2575bb5e3b2fSeh146360 				cv_signal(&sc->sc_cmd_cond);
2576bb5e3b2fSeh146360 			}
2577bb5e3b2fSeh146360 			for (; i != ridx;
2578bb5e3b2fSeh146360 			    i = RING_FORWARD(i, 1, IPW2200_CMD_RING_SIZE))
2579bb5e3b2fSeh146360 				sc->sc_done[i] = 1;
2580bb5e3b2fSeh146360 			mutex_exit(&sc->sc_cmd_lock);
2581bb5e3b2fSeh146360 
2582bb5e3b2fSeh146360 			mutex_enter(&sc->sc_ilock);
2583bb5e3b2fSeh146360 			cv_signal(&sc->sc_cmd_status_cond);
2584bb5e3b2fSeh146360 			mutex_exit(&sc->sc_ilock);
2585bb5e3b2fSeh146360 		}
2586bb5e3b2fSeh146360 		if (ireg & IPW2200_INTR_RX_TRANSFER) {
2587bb5e3b2fSeh146360 			ridx = ipw2200_csr_get32(sc,
2588bb5e3b2fSeh146360 			    IPW2200_CSR_RX_READ_INDEX);
2589bb5e3b2fSeh146360 			widx = ipw2200_csr_get32(sc,
2590bb5e3b2fSeh146360 			    IPW2200_CSR_RX_WRITE_INDEX);
2591bb5e3b2fSeh146360 
2592bb5e3b2fSeh146360 			IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT,
2593bb5e3b2fSeh146360 			    "ipw2200_intr(): rx-ring,widx=%u,ridx=%u\n",
2594bb5e3b2fSeh146360 			    ridx, widx));
2595bb5e3b2fSeh146360 
2596bb5e3b2fSeh146360 			for (; sc->sc_rx_cur != ridx;
2597bb5e3b2fSeh146360 			    sc->sc_rx_cur = RING_FORWARD(sc->sc_rx_cur, 1,
2598bb5e3b2fSeh146360 			    IPW2200_RX_RING_SIZE)) {
2599bb5e3b2fSeh146360 				i	= sc->sc_rx_cur;
2600bb5e3b2fSeh146360 				rxbuf	= sc->sc_rxbufs[i];
2601bb5e3b2fSeh146360 				dr	= &sc->sc_dma_rxbufs[i];
2602bb5e3b2fSeh146360 
2603bb5e3b2fSeh146360 				/*
2604bb5e3b2fSeh146360 				 * DMA sync
2605bb5e3b2fSeh146360 				 */
2606bb5e3b2fSeh146360 				(void) ddi_dma_sync(dr->dr_hnd, 0,
2607bb5e3b2fSeh146360 				    IPW2200_RXBUF_SIZE, DDI_DMA_SYNC_FORKERNEL);
2608bb5e3b2fSeh146360 				/*
2609bb5e3b2fSeh146360 				 * Get rx header(hdr) and rx data(p) from rxbuf
2610bb5e3b2fSeh146360 				 */
2611bb5e3b2fSeh146360 				p	= rxbuf;
2612bb5e3b2fSeh146360 				hdr	= (struct ipw2200_hdr *)p;
2613bb5e3b2fSeh146360 				p	+= sizeof (struct ipw2200_hdr);
2614bb5e3b2fSeh146360 
2615bb5e3b2fSeh146360 				IPW2200_DBG(IPW2200_DBG_INT,
2616bb5e3b2fSeh146360 				    (sc->sc_dip, CE_CONT,
2617bb5e3b2fSeh146360 				    "ipw2200_intr(): Rx hdr type %u\n",
2618bb5e3b2fSeh146360 				    hdr->type));
2619bb5e3b2fSeh146360 
2620bb5e3b2fSeh146360 				switch (hdr->type) {
2621bb5e3b2fSeh146360 				case IPW2200_HDR_TYPE_FRAME:
2622bb5e3b2fSeh146360 					ipw2200_rcv_frame(sc,
2623bb5e3b2fSeh146360 					    (struct ipw2200_frame *)p);
2624bb5e3b2fSeh146360 					break;
2625bb5e3b2fSeh146360 
2626bb5e3b2fSeh146360 				case IPW2200_HDR_TYPE_NOTIF:
2627bb5e3b2fSeh146360 					ipw2200_rcv_notif(sc,
2628bb5e3b2fSeh146360 					    (struct ipw2200_notif *)p);
2629bb5e3b2fSeh146360 					break;
2630bb5e3b2fSeh146360 				default:
2631bb5e3b2fSeh146360 					IPW2200_DBG(IPW2200_DBG_INT,
2632bb5e3b2fSeh146360 					    (sc->sc_dip, CE_CONT,
2633bb5e3b2fSeh146360 					    "ipw2200_intr(): "
2634bb5e3b2fSeh146360 					    "unknown Rx hdr type %u\n",
2635bb5e3b2fSeh146360 					    hdr->type));
2636bb5e3b2fSeh146360 					break;
2637bb5e3b2fSeh146360 				}
2638bb5e3b2fSeh146360 			}
2639bb5e3b2fSeh146360 			/*
2640bb5e3b2fSeh146360 			 * write sc_rx_cur backward 1 step into RX_WRITE_INDEX
2641bb5e3b2fSeh146360 			 */
2642bb5e3b2fSeh146360 			ipw2200_csr_put32(sc, IPW2200_CSR_RX_WRITE_INDEX,
2643bb5e3b2fSeh146360 			    RING_BACKWARD(sc->sc_rx_cur, 1,
2644bb5e3b2fSeh146360 			    IPW2200_RX_RING_SIZE));
2645bb5e3b2fSeh146360 		}
2646bb5e3b2fSeh146360 		if (ireg & IPW2200_INTR_TX1_TRANSFER) {
2647bb5e3b2fSeh146360 			mutex_enter(&sc->sc_tx_lock);
2648bb5e3b2fSeh146360 			ridx = ipw2200_csr_get32(sc,
2649bb5e3b2fSeh146360 			    IPW2200_CSR_TX1_READ_INDEX);
2650bb5e3b2fSeh146360 			len  = RING_FLEN(RING_FORWARD(sc->sc_tx_cur,
2651bb5e3b2fSeh146360 			    sc->sc_tx_free, IPW2200_TX_RING_SIZE),
2652bb5e3b2fSeh146360 			    ridx, IPW2200_TX_RING_SIZE);
2653bb5e3b2fSeh146360 			IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT,
2654bb5e3b2fSeh146360 			    "ipw2200_intr(): tx-ring,ridx=%u,len=%u\n",
2655bb5e3b2fSeh146360 			    ridx, len));
2656bb5e3b2fSeh146360 			sc->sc_tx_free += len;
2657bb5e3b2fSeh146360 
2658bb5e3b2fSeh146360 			need_sched = 0;
2659bb5e3b2fSeh146360 			if ((sc->sc_tx_free > IPW2200_TX_RING_MIN) &&
2660bb5e3b2fSeh146360 			    (sc->sc_flags & IPW2200_FLAG_TX_SCHED)) {
2661bb5e3b2fSeh146360 				IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip,
2662bb5e3b2fSeh146360 				    CE_CONT,
2663bb5e3b2fSeh146360 				    "ipw2200_intr(): Need Reschedule!"));
2664bb5e3b2fSeh146360 				need_sched = 1;
2665bb5e3b2fSeh146360 				sc->sc_flags &= ~IPW2200_FLAG_TX_SCHED;
2666bb5e3b2fSeh146360 			}
2667bb5e3b2fSeh146360 			mutex_exit(&sc->sc_tx_lock);
2668bb5e3b2fSeh146360 
2669bb5e3b2fSeh146360 			if (need_sched)
2670bb5e3b2fSeh146360 				mac_tx_update(ic->ic_mach);
2671bb5e3b2fSeh146360 		}
2672bb5e3b2fSeh146360 	}
2673bb5e3b2fSeh146360 
2674bb5e3b2fSeh146360 	/*
2675bb5e3b2fSeh146360 	 * enable all interrupts
2676bb5e3b2fSeh146360 	 */
2677bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, IPW2200_INTR_MASK_ALL);
2678bb5e3b2fSeh146360 
2679bb5e3b2fSeh146360 	return (DDI_INTR_CLAIMED);
2680bb5e3b2fSeh146360 }
2681bb5e3b2fSeh146360 
2682bb5e3b2fSeh146360 
2683bb5e3b2fSeh146360 /*
2684bb5e3b2fSeh146360  *  Module Loading Data & Entry Points
2685bb5e3b2fSeh146360  */
2686bb5e3b2fSeh146360 DDI_DEFINE_STREAM_OPS(ipw2200_devops, nulldev, nulldev, ipw2200_attach,
2687*76939ce0Seh146360     ipw2200_detach, ipw2200_reset, NULL, D_MP, NULL);
2688bb5e3b2fSeh146360 
2689bb5e3b2fSeh146360 static struct modldrv ipw2200_modldrv = {
2690bb5e3b2fSeh146360 	&mod_driverops,
2691bb5e3b2fSeh146360 	ipw2200_ident,
2692bb5e3b2fSeh146360 	&ipw2200_devops
2693bb5e3b2fSeh146360 };
2694bb5e3b2fSeh146360 
2695bb5e3b2fSeh146360 static struct modlinkage ipw2200_modlinkage = {
2696bb5e3b2fSeh146360 	MODREV_1,
2697bb5e3b2fSeh146360 	&ipw2200_modldrv,
2698bb5e3b2fSeh146360 	NULL
2699bb5e3b2fSeh146360 };
2700bb5e3b2fSeh146360 
2701bb5e3b2fSeh146360 int
2702bb5e3b2fSeh146360 _init(void)
2703bb5e3b2fSeh146360 {
2704bb5e3b2fSeh146360 	int status;
2705bb5e3b2fSeh146360 
2706bb5e3b2fSeh146360 	status = ddi_soft_state_init(&ipw2200_ssp,
2707bb5e3b2fSeh146360 	    sizeof (struct ipw2200_softc), 1);
2708bb5e3b2fSeh146360 	if (status != DDI_SUCCESS)
2709bb5e3b2fSeh146360 		return (status);
2710bb5e3b2fSeh146360 
2711bb5e3b2fSeh146360 	mac_init_ops(&ipw2200_devops, IPW2200_DRV_NAME);
2712bb5e3b2fSeh146360 	status = mod_install(&ipw2200_modlinkage);
2713bb5e3b2fSeh146360 	if (status != DDI_SUCCESS) {
2714bb5e3b2fSeh146360 		mac_fini_ops(&ipw2200_devops);
2715bb5e3b2fSeh146360 		ddi_soft_state_fini(&ipw2200_ssp);
2716bb5e3b2fSeh146360 	}
2717bb5e3b2fSeh146360 
2718bb5e3b2fSeh146360 	return (status);
2719bb5e3b2fSeh146360 }
2720bb5e3b2fSeh146360 
2721bb5e3b2fSeh146360 int
2722bb5e3b2fSeh146360 _fini(void)
2723bb5e3b2fSeh146360 {
2724bb5e3b2fSeh146360 	int status;
2725bb5e3b2fSeh146360 
2726bb5e3b2fSeh146360 	status = mod_remove(&ipw2200_modlinkage);
2727bb5e3b2fSeh146360 	if (status == DDI_SUCCESS) {
2728bb5e3b2fSeh146360 		mac_fini_ops(&ipw2200_devops);
2729bb5e3b2fSeh146360 		ddi_soft_state_fini(&ipw2200_ssp);
2730bb5e3b2fSeh146360 	}
2731bb5e3b2fSeh146360 
2732bb5e3b2fSeh146360 	return (status);
2733bb5e3b2fSeh146360 }
2734bb5e3b2fSeh146360 
2735bb5e3b2fSeh146360 int
2736bb5e3b2fSeh146360 _info(struct modinfo *modinfop)
2737bb5e3b2fSeh146360 {
2738bb5e3b2fSeh146360 	return (mod_info(&ipw2200_modlinkage, modinfop));
2739bb5e3b2fSeh146360 }
2740