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