xref: /illumos-gate/usr/src/uts/common/io/ipw/ipw2100.c (revision 2a8d6eba033e4713ab12b61178f0513f1f075482)
1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright(c) 2004
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 #include <sys/types.h>
34 #include <sys/byteorder.h>
35 #include <sys/conf.h>
36 #include <sys/cmn_err.h>
37 #include <sys/stat.h>
38 #include <sys/ddi.h>
39 #include <sys/sunddi.h>
40 #include <sys/strsubr.h>
41 #include <sys/ethernet.h>
42 #include <inet/common.h>
43 #include <inet/nd.h>
44 #include <inet/mi.h>
45 #include <sys/note.h>
46 #include <sys/stream.h>
47 #include <sys/strsun.h>
48 #include <sys/modctl.h>
49 #include <sys/devops.h>
50 #include <sys/dlpi.h>
51 #include <sys/mac_provider.h>
52 #include <net/if.h>
53 #include <sys/mac_wifi.h>
54 #include <sys/varargs.h>
55 #include <sys/policy.h>
56 
57 #include "ipw2100.h"
58 #include "ipw2100_impl.h"
59 #include <inet/wifi_ioctl.h>
60 
61 /*
62  * kCF framework include files
63  */
64 #include <sys/crypto/common.h>
65 #include <sys/crypto/api.h>
66 
67 static void   *ipw2100_ssp	= NULL;
68 static char   ipw2100_ident[]	= IPW2100_DRV_DESC;
69 
70 /*
71  * PIO access attribute for register
72  */
73 static ddi_device_acc_attr_t ipw2100_csr_accattr = {
74 	DDI_DEVICE_ATTR_V0,
75 	DDI_STRUCTURE_LE_ACC,
76 	DDI_STRICTORDER_ACC
77 };
78 
79 static ddi_device_acc_attr_t ipw2100_dma_accattr = {
80 	DDI_DEVICE_ATTR_V0,
81 	DDI_NEVERSWAP_ACC,
82 	DDI_STRICTORDER_ACC
83 };
84 
85 static ddi_dma_attr_t ipw2100_dma_attr = {
86 	DMA_ATTR_V0,
87 	0x0000000000000000ULL,
88 	0x00000000ffffffffULL,
89 	0x00000000ffffffffULL,
90 	0x0000000000000004ULL,
91 	0xfff,
92 	1,
93 	0x00000000ffffffffULL,
94 	0x00000000ffffffffULL,
95 	1,
96 	1,
97 	0
98 };
99 
100 static const struct ieee80211_rateset ipw2100_rateset_11b = { 4,
101 	{2, 4, 11, 22}
102 };
103 
104 /*
105  * For mfthread only
106  */
107 extern pri_t minclsyspri;
108 
109 /*
110  * ipw2100 specific hardware operations
111  */
112 static void	ipw2100_hwconf_get(struct ipw2100_softc *sc);
113 static int	ipw2100_chip_reset(struct ipw2100_softc *sc);
114 static void	ipw2100_master_stop(struct ipw2100_softc *sc);
115 static void	ipw2100_stop(struct ipw2100_softc *sc);
116 static int	ipw2100_config(struct ipw2100_softc *sc);
117 static int	ipw2100_cmd(struct ipw2100_softc *sc, uint32_t type,
118     void *buf, size_t len);
119 static int	ipw2100_dma_region_alloc(struct ipw2100_softc *sc,
120     struct dma_region *dr, size_t size, uint_t dir, uint_t flags);
121 static void	ipw2100_dma_region_free(struct dma_region *dr);
122 static void	ipw2100_tables_init(struct ipw2100_softc *sc);
123 static void	ipw2100_ring_hwsetup(struct ipw2100_softc *sc);
124 static int	ipw2100_ring_alloc(struct ipw2100_softc *sc);
125 static void	ipw2100_ring_free(struct ipw2100_softc *sc);
126 static void	ipw2100_ring_reset(struct ipw2100_softc *sc);
127 static int	ipw2100_ring_init(struct ipw2100_softc *sc);
128 
129 /*
130  * GLD specific operations
131  */
132 static int	ipw2100_m_stat(void *arg, uint_t stat, uint64_t *val);
133 static int	ipw2100_m_start(void *arg);
134 static void	ipw2100_m_stop(void *arg);
135 static int	ipw2100_m_unicst(void *arg, const uint8_t *macaddr);
136 static int	ipw2100_m_multicst(void *arg, boolean_t add, const uint8_t *m);
137 static int	ipw2100_m_promisc(void *arg, boolean_t on);
138 static mblk_t  *ipw2100_m_tx(void *arg, mblk_t *mp);
139 static void	ipw2100_m_ioctl(void *arg, queue_t *wq, mblk_t *mp);
140 static int	ipw2100_m_setprop(void *arg, const char *pr_name,
141     mac_prop_id_t wldp_pr_num, uint_t wldp_length, const void *wldp_buf);
142 static int	ipw2100_m_getprop(void *arg, const char *pr_name,
143     mac_prop_id_t wldp_pr_num, uint_t pr_flags, uint_t wldp_length,
144     void *wldp_buf, uint_t *perm);
145 
146 
147 /*
148  * Interrupt and Data transferring operations
149  */
150 static uint_t	ipw2100_intr(caddr_t arg);
151 static int	ipw2100_send(struct ieee80211com *ic, mblk_t *mp, uint8_t type);
152 static void	ipw2100_rcvpkt(struct ipw2100_softc *sc,
153     struct ipw2100_status *status, uint8_t *rxbuf);
154 
155 /*
156  * WiFi specific operations
157  */
158 static int	ipw2100_newstate(struct ieee80211com *ic,
159     enum ieee80211_state state, int arg);
160 static void	ipw2100_thread(struct ipw2100_softc *sc);
161 
162 /*
163  * IOCTL Handler
164  */
165 static int	ipw2100_ioctl(struct ipw2100_softc *sc, queue_t *q, mblk_t *m);
166 static int	ipw2100_getset(struct ipw2100_softc *sc,
167     mblk_t *m, uint32_t cmd, boolean_t *need_net80211);
168 static int	ipw_wificfg_radio(struct ipw2100_softc *sc,
169     uint32_t cmd,  wldp_t *outfp);
170 static int	ipw_wificfg_desrates(wldp_t *outfp);
171 static int	ipw_wificfg_disassoc(struct ipw2100_softc *sc,
172     wldp_t *outfp);
173 
174 /*
175  * Suspend / Resume operations
176  */
177 static int	ipw2100_cpr_suspend(struct ipw2100_softc *sc);
178 static int	ipw2100_cpr_resume(struct ipw2100_softc *sc);
179 
180 /*
181  * Mac Call Back entries
182  */
183 mac_callbacks_t	ipw2100_m_callbacks = {
184 	MC_IOCTL | MC_SETPROP | MC_GETPROP,
185 	ipw2100_m_stat,
186 	ipw2100_m_start,
187 	ipw2100_m_stop,
188 	ipw2100_m_promisc,
189 	ipw2100_m_multicst,
190 	ipw2100_m_unicst,
191 	ipw2100_m_tx,
192 	ipw2100_m_ioctl,
193 	NULL,
194 	NULL,
195 	NULL,
196 	ipw2100_m_setprop,
197 	ipw2100_m_getprop
198 };
199 
200 
201 /*
202  * DEBUG Facility
203  */
204 #define	MAX_MSG (128)
205 uint32_t ipw2100_debug = 0;
206 /*
207  * supported debug marsks:
208  *	| IPW2100_DBG_INIT
209  *	| IPW2100_DBG_GLD
210  *	| IPW2100_DBG_TABLE
211  *	| IPW2100_DBG_SOFTINT
212  *	| IPW2100_DBG_CSR
213  *	| IPW2100_DBG_INT
214  *	| IPW2100_DBG_FW
215  *	| IPW2100_DBG_IOCTL
216  *	| IPW2100_DBG_HWCAP
217  *	| IPW2100_DBG_STATISTIC
218  *	| IPW2100_DBG_RING
219  *	| IPW2100_DBG_WIFI
220  *	| IPW2100_DBG_BRUSSELS
221  */
222 
223 /*
224  * global tuning parameters to work around unknown hardware issues
225  */
226 static uint32_t delay_config_stable 	= 100000;	/* 100ms */
227 static uint32_t delay_fatal_recover	= 100000 * 20;	/* 2s */
228 static uint32_t delay_aux_thread 	= 100000;	/* 100ms */
229 
230 void
231 ipw2100_dbg(dev_info_t *dip, int level, const char *fmt, ...)
232 {
233 	va_list	ap;
234 	char    buf[MAX_MSG];
235 	int	instance;
236 
237 	va_start(ap, fmt);
238 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
239 	va_end(ap);
240 
241 	if (dip) {
242 		instance = ddi_get_instance(dip);
243 		cmn_err(level, "%s%d: %s", IPW2100_DRV_NAME, instance, buf);
244 	} else
245 		cmn_err(level, "%s: %s", IPW2100_DRV_NAME, buf);
246 }
247 
248 /*
249  * device operations
250  */
251 int
252 ipw2100_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
253 {
254 	struct ipw2100_softc	*sc;
255 	ddi_acc_handle_t	cfgh;
256 	caddr_t			regs;
257 	struct ieee80211com	*ic;
258 	int			instance, err, i;
259 	char			strbuf[32];
260 	wifi_data_t		wd = { 0 };
261 	mac_register_t		*macp;
262 
263 	switch (cmd) {
264 	case DDI_ATTACH:
265 		break;
266 	case DDI_RESUME:
267 		sc = ddi_get_soft_state(ipw2100_ssp, ddi_get_instance(dip));
268 		if (sc == NULL) {
269 			err = DDI_FAILURE;
270 			goto fail1;
271 		}
272 		return (ipw2100_cpr_resume(sc));
273 	default:
274 		err = DDI_FAILURE;
275 		goto fail1;
276 	}
277 
278 	instance = ddi_get_instance(dip);
279 	err = ddi_soft_state_zalloc(ipw2100_ssp, instance);
280 	if (err != DDI_SUCCESS) {
281 		IPW2100_WARN((dip, CE_WARN,
282 		    "ipw2100_attach(): unable to allocate soft state\n"));
283 		goto fail1;
284 	}
285 	sc = ddi_get_soft_state(ipw2100_ssp, instance);
286 	sc->sc_dip = dip;
287 
288 	/*
289 	 * Map config spaces register
290 	 */
291 	err = ddi_regs_map_setup(dip, IPW2100_PCI_CFG_RNUM, &regs,
292 	    0, 0, &ipw2100_csr_accattr, &cfgh);
293 	if (err != DDI_SUCCESS) {
294 		IPW2100_WARN((dip, CE_WARN,
295 		    "ipw2100_attach(): unable to map spaces regs\n"));
296 		goto fail2;
297 	}
298 	ddi_put8(cfgh, (uint8_t *)(regs + 0x41), 0);
299 	ddi_regs_map_free(&cfgh);
300 
301 	/*
302 	 * Map operating registers
303 	 */
304 	err = ddi_regs_map_setup(dip, IPW2100_PCI_CSR_RNUM, &sc->sc_regs,
305 	    0, 0, &ipw2100_csr_accattr, &sc->sc_ioh);
306 	if (err != DDI_SUCCESS) {
307 		IPW2100_WARN((dip, CE_WARN,
308 		    "ipw2100_attach(): unable to map device regs\n"));
309 		goto fail2;
310 	}
311 
312 	/*
313 	 * Reset the chip
314 	 */
315 	err = ipw2100_chip_reset(sc);
316 	if (err != DDI_SUCCESS) {
317 		IPW2100_WARN((dip, CE_WARN,
318 		    "ipw2100_attach(): reset failed\n"));
319 		goto fail3;
320 	}
321 
322 	/*
323 	 * Get the hw conf, including MAC address, then init all rings.
324 	 */
325 	ipw2100_hwconf_get(sc);
326 	err = ipw2100_ring_init(sc);
327 	if (err != DDI_SUCCESS) {
328 		IPW2100_WARN((dip, CE_WARN,
329 		    "ipw2100_attach(): "
330 		    "unable to allocate and initialize rings\n"));
331 		goto fail3;
332 	}
333 
334 	/*
335 	 * Initialize mutexs and condvars
336 	 */
337 	err = ddi_get_iblock_cookie(dip, 0, &sc->sc_iblk);
338 	if (err != DDI_SUCCESS) {
339 		IPW2100_WARN((dip, CE_WARN,
340 		    "ipw2100_attach(): ddi_get_iblock_cookie() failed\n"));
341 		goto fail4;
342 	}
343 	/*
344 	 * interrupt lock
345 	 */
346 	mutex_init(&sc->sc_ilock, "interrupt-lock", MUTEX_DRIVER,
347 	    (void *) sc->sc_iblk);
348 	cv_init(&sc->sc_fw_cond, "firmware", CV_DRIVER, NULL);
349 	cv_init(&sc->sc_cmd_cond, "command", CV_DRIVER, NULL);
350 	/*
351 	 * tx ring lock
352 	 */
353 	mutex_init(&sc->sc_tx_lock, "tx-ring", MUTEX_DRIVER,
354 	    (void *) sc->sc_iblk);
355 	cv_init(&sc->sc_tx_cond, "tx-ring", CV_DRIVER, NULL);
356 	/*
357 	 * rescheuled lock
358 	 */
359 	mutex_init(&sc->sc_resched_lock, "reschedule-lock", MUTEX_DRIVER,
360 	    (void *) sc->sc_iblk);
361 	/*
362 	 * initialize the mfthread
363 	 */
364 	mutex_init(&sc->sc_mflock, "function-lock", MUTEX_DRIVER,
365 	    (void *) sc->sc_iblk);
366 	cv_init(&sc->sc_mfthread_cv, NULL, CV_DRIVER, NULL);
367 	sc->sc_mf_thread = NULL;
368 	sc->sc_mfthread_switch = 0;
369 	/*
370 	 * Initialize the wifi part, which will be used by
371 	 * generic layer
372 	 */
373 	ic = &sc->sc_ic;
374 	ic->ic_phytype  = IEEE80211_T_DS;
375 	ic->ic_opmode   = IEEE80211_M_STA;
376 	ic->ic_state    = IEEE80211_S_INIT;
377 	ic->ic_maxrssi  = 49;
378 	/*
379 	 * Future, could use s/w to handle encryption: IEEE80211_C_WEP
380 	 * and need to add support for IEEE80211_C_IBSS
381 	 */
382 	ic->ic_caps = IEEE80211_C_SHPREAMBLE | IEEE80211_C_TXPMGT |
383 	    IEEE80211_C_PMGT;
384 	ic->ic_sup_rates[IEEE80211_MODE_11B] = ipw2100_rateset_11b;
385 	IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->sc_macaddr);
386 	for (i = 1; i < 16; i++) {
387 		if (sc->sc_chmask &(1 << i)) {
388 			/* IEEE80211_CHAN_B */
389 			ic->ic_sup_channels[i].ich_freq  = ieee80211_ieee2mhz(i,
390 			    IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK);
391 			ic->ic_sup_channels[i].ich_flags =
392 			    IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK;
393 		}
394 	}
395 	ic->ic_ibss_chan = &ic->ic_sup_channels[0];
396 	ic->ic_xmit = ipw2100_send;
397 	/*
398 	 * init Wifi layer
399 	 */
400 	ieee80211_attach(ic);
401 
402 	/*
403 	 * Override 80211 default routines
404 	 */
405 	ieee80211_media_init(ic);
406 	sc->sc_newstate = ic->ic_newstate;
407 	ic->ic_newstate = ipw2100_newstate;
408 	/*
409 	 * initialize default tx key
410 	 */
411 	ic->ic_def_txkey = 0;
412 	/*
413 	 * Set the Authentication to AUTH_Open only.
414 	 */
415 	sc->sc_authmode = IEEE80211_AUTH_OPEN;
416 
417 	/*
418 	 * Add the interrupt handler
419 	 */
420 	err = ddi_add_intr(dip, 0, &sc->sc_iblk, NULL,
421 	    ipw2100_intr, (caddr_t)sc);
422 	if (err != DDI_SUCCESS) {
423 		IPW2100_WARN((dip, CE_WARN,
424 		    "ipw2100_attach(): ddi_add_intr() failed\n"));
425 		goto fail5;
426 	}
427 
428 	/*
429 	 * Initialize pointer to device specific functions
430 	 */
431 	wd.wd_secalloc = WIFI_SEC_NONE;
432 	wd.wd_opmode = ic->ic_opmode;
433 	IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
434 
435 	macp = mac_alloc(MAC_VERSION);
436 	if (err != 0) {
437 		IPW2100_WARN((dip, CE_WARN,
438 		    "ipw2100_attach(): mac_alloc() failed\n"));
439 		goto fail6;
440 	}
441 
442 	macp->m_type_ident	= MAC_PLUGIN_IDENT_WIFI;
443 	macp->m_driver		= sc;
444 	macp->m_dip		= dip;
445 	macp->m_src_addr	= ic->ic_macaddr;
446 	macp->m_callbacks	= &ipw2100_m_callbacks;
447 	macp->m_min_sdu		= 0;
448 	macp->m_max_sdu		= IEEE80211_MTU;
449 	macp->m_pdata		= &wd;
450 	macp->m_pdata_size	= sizeof (wd);
451 
452 	/*
453 	 * Register the macp to mac
454 	 */
455 	err = mac_register(macp, &ic->ic_mach);
456 	mac_free(macp);
457 	if (err != DDI_SUCCESS) {
458 		IPW2100_WARN((dip, CE_WARN,
459 		    "ipw2100_attach(): mac_register() failed\n"));
460 		goto fail6;
461 	}
462 
463 	/*
464 	 * Create minor node of type DDI_NT_NET_WIFI
465 	 */
466 	(void) snprintf(strbuf, sizeof (strbuf), "%s%d",
467 	    IPW2100_DRV_NAME, instance);
468 	err = ddi_create_minor_node(dip, strbuf, S_IFCHR,
469 	    instance + 1, DDI_NT_NET_WIFI, 0);
470 	if (err != DDI_SUCCESS)
471 		IPW2100_WARN((dip, CE_WARN,
472 		    "ipw2100_attach(): ddi_create_minor_node() failed\n"));
473 
474 	/*
475 	 * Cache firmware, always return true
476 	 */
477 	(void) ipw2100_cache_firmware(sc);
478 
479 	/*
480 	 * Notify link is down now
481 	 */
482 	mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
483 
484 	/*
485 	 * create the mf thread to handle the link status,
486 	 * recovery fatal error, etc.
487 	 */
488 	sc->sc_mfthread_switch = 1;
489 	if (sc->sc_mf_thread == NULL)
490 		sc->sc_mf_thread = thread_create((caddr_t)NULL, 0,
491 		    ipw2100_thread, sc, 0, &p0, TS_RUN, minclsyspri);
492 
493 	return (DDI_SUCCESS);
494 
495 fail6:
496 	ddi_remove_intr(dip, 0, sc->sc_iblk);
497 fail5:
498 	ieee80211_detach(ic);
499 
500 	mutex_destroy(&sc->sc_ilock);
501 	mutex_destroy(&sc->sc_tx_lock);
502 	mutex_destroy(&sc->sc_mflock);
503 	mutex_destroy(&sc->sc_resched_lock);
504 	cv_destroy(&sc->sc_mfthread_cv);
505 	cv_destroy(&sc->sc_tx_cond);
506 	cv_destroy(&sc->sc_cmd_cond);
507 	cv_destroy(&sc->sc_fw_cond);
508 fail4:
509 	ipw2100_ring_free(sc);
510 fail3:
511 	ddi_regs_map_free(&sc->sc_ioh);
512 fail2:
513 	ddi_soft_state_free(ipw2100_ssp, instance);
514 fail1:
515 	return (err);
516 }
517 
518 int
519 ipw2100_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
520 {
521 	struct ipw2100_softc	*sc =
522 	    ddi_get_soft_state(ipw2100_ssp, ddi_get_instance(dip));
523 	int err;
524 
525 	ASSERT(sc != NULL);
526 
527 	switch (cmd) {
528 	case DDI_DETACH:
529 		break;
530 	case DDI_SUSPEND:
531 		return (ipw2100_cpr_suspend(sc));
532 	default:
533 		return (DDI_FAILURE);
534 	}
535 
536 	/*
537 	 * Destroy the mf_thread
538 	 */
539 	mutex_enter(&sc->sc_mflock);
540 	sc->sc_mfthread_switch = 0;
541 	while (sc->sc_mf_thread != NULL) {
542 		if (cv_wait_sig(&sc->sc_mfthread_cv, &sc->sc_mflock) == 0)
543 			break;
544 	}
545 	mutex_exit(&sc->sc_mflock);
546 
547 	/*
548 	 * Unregister from the MAC layer subsystem
549 	 */
550 	err = mac_unregister(sc->sc_ic.ic_mach);
551 	if (err != DDI_SUCCESS)
552 		return (err);
553 
554 	ddi_remove_intr(dip, 0, sc->sc_iblk);
555 
556 	/*
557 	 * destroy the cv
558 	 */
559 	mutex_destroy(&sc->sc_ilock);
560 	mutex_destroy(&sc->sc_tx_lock);
561 	mutex_destroy(&sc->sc_mflock);
562 	mutex_destroy(&sc->sc_resched_lock);
563 	cv_destroy(&sc->sc_mfthread_cv);
564 	cv_destroy(&sc->sc_tx_cond);
565 	cv_destroy(&sc->sc_cmd_cond);
566 	cv_destroy(&sc->sc_fw_cond);
567 
568 	/*
569 	 * detach ieee80211
570 	 */
571 	ieee80211_detach(&sc->sc_ic);
572 
573 	(void) ipw2100_free_firmware(sc);
574 	ipw2100_ring_free(sc);
575 
576 	ddi_regs_map_free(&sc->sc_ioh);
577 	ddi_remove_minor_node(dip, NULL);
578 	ddi_soft_state_free(ipw2100_ssp, ddi_get_instance(dip));
579 
580 	return (DDI_SUCCESS);
581 }
582 
583 int
584 ipw2100_cpr_suspend(struct ipw2100_softc *sc)
585 {
586 	IPW2100_DBG(IPW2100_DBG_INIT, (sc->sc_dip, CE_CONT,
587 	    "ipw2100_cpr_suspend(): enter\n"));
588 
589 	/*
590 	 * Destroy the mf_thread
591 	 */
592 	mutex_enter(&sc->sc_mflock);
593 	sc->sc_mfthread_switch = 0;
594 	while (sc->sc_mf_thread != NULL) {
595 		if (cv_wait_sig(&sc->sc_mfthread_cv, &sc->sc_mflock) == 0)
596 			break;
597 	}
598 	mutex_exit(&sc->sc_mflock);
599 
600 	/*
601 	 * stop the hardware; this mask all interrupts
602 	 */
603 	ipw2100_stop(sc);
604 	sc->sc_flags &= ~IPW2100_FLAG_RUNNING;
605 	sc->sc_suspended = 1;
606 
607 	(void) ipw2100_free_firmware(sc);
608 	ipw2100_ring_free(sc);
609 
610 	return (DDI_SUCCESS);
611 }
612 
613 int
614 ipw2100_cpr_resume(struct ipw2100_softc *sc)
615 {
616 	struct ieee80211com	*ic = &sc->sc_ic;
617 	dev_info_t		*dip = sc->sc_dip;
618 	int			err;
619 
620 	IPW2100_DBG(IPW2100_DBG_INIT, (sc->sc_dip, CE_CONT,
621 	    "ipw2100_cpr_resume(): enter\n"));
622 
623 	/*
624 	 * Reset the chip
625 	 */
626 	err = ipw2100_chip_reset(sc);
627 	if (err != DDI_SUCCESS) {
628 		IPW2100_WARN((dip, CE_WARN,
629 		    "ipw2100_attach(): reset failed\n"));
630 		return (DDI_FAILURE);
631 	}
632 
633 	/*
634 	 * Get the hw conf, including MAC address, then init all rings.
635 	 */
636 	/* ipw2100_hwconf_get(sc); */
637 	err = ipw2100_ring_init(sc);
638 	if (err != DDI_SUCCESS) {
639 		IPW2100_WARN((dip, CE_WARN,
640 		    "ipw2100_attach(): "
641 		    "unable to allocate and initialize rings\n"));
642 		return (DDI_FAILURE);
643 	}
644 
645 	/*
646 	 * Cache firmware, always return true
647 	 */
648 	(void) ipw2100_cache_firmware(sc);
649 
650 	/*
651 	 * Notify link is down now
652 	 */
653 	mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
654 
655 	/*
656 	 * create the mf thread to handle the link status,
657 	 * recovery fatal error, etc.
658 	 */
659 	sc->sc_mfthread_switch = 1;
660 	if (sc->sc_mf_thread == NULL)
661 		sc->sc_mf_thread = thread_create((caddr_t)NULL, 0,
662 		    ipw2100_thread, sc, 0, &p0, TS_RUN, minclsyspri);
663 
664 	/*
665 	 * enable all interrupts
666 	 */
667 	sc->sc_suspended = 0;
668 	ipw2100_csr_put32(sc, IPW2100_CSR_INTR_MASK, IPW2100_INTR_MASK_ALL);
669 
670 	/*
671 	 * initialize ipw2100 hardware
672 	 */
673 	(void) ipw2100_init(sc);
674 
675 	sc->sc_flags |= IPW2100_FLAG_RUNNING;
676 
677 	return (DDI_SUCCESS);
678 }
679 
680 /*
681  * quiesce(9E) entry point.
682  * This function is called when the system is single-threaded at high
683  * PIL with preemption disabled. Therefore, this function must not be
684  * blocked.
685  * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
686  * DDI_FAILURE indicates an error condition and should almost never happen.
687  * Contributed by Juergen Keil, <jk@tools.de>.
688  */
689 static int
690 ipw2100_quiesce(dev_info_t *dip)
691 {
692 	struct ipw2100_softc	*sc =
693 	    ddi_get_soft_state(ipw2100_ssp, ddi_get_instance(dip));
694 
695 	if (sc == NULL)
696 		return (DDI_FAILURE);
697 
698 	/*
699 	 * No more blocking is allowed while we are in the
700 	 * quiesce(9E) entry point.
701 	 */
702 	sc->sc_flags |= IPW2100_FLAG_QUIESCED;
703 
704 	/*
705 	 * Disable and mask all interrupts.
706 	 */
707 	ipw2100_stop(sc);
708 	return (DDI_SUCCESS);
709 }
710 
711 static void
712 ipw2100_tables_init(struct ipw2100_softc *sc)
713 {
714 	sc->sc_table1_base = ipw2100_csr_get32(sc, IPW2100_CSR_TABLE1_BASE);
715 	sc->sc_table2_base = ipw2100_csr_get32(sc, IPW2100_CSR_TABLE2_BASE);
716 }
717 
718 static void
719 ipw2100_stop(struct ipw2100_softc *sc)
720 {
721 	struct ieee80211com	*ic = &sc->sc_ic;
722 
723 	ipw2100_master_stop(sc);
724 	ipw2100_csr_put32(sc, IPW2100_CSR_RST, IPW2100_RST_SW_RESET);
725 	sc->sc_flags &= ~IPW2100_FLAG_FW_INITED;
726 
727 	if (!(sc->sc_flags & IPW2100_FLAG_QUIESCED))
728 		ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
729 }
730 
731 static int
732 ipw2100_config(struct ipw2100_softc *sc)
733 {
734 	struct ieee80211com		*ic = &sc->sc_ic;
735 	struct ipw2100_security		sec;
736 	struct ipw2100_wep_key		wkey;
737 	struct ipw2100_scan_options	sopt;
738 	struct ipw2100_configuration	cfg;
739 	uint32_t			data;
740 	int				err, i;
741 
742 	/*
743 	 * operation mode
744 	 */
745 	switch (ic->ic_opmode) {
746 	case IEEE80211_M_STA:
747 	case IEEE80211_M_HOSTAP:
748 		data = LE_32(IPW2100_MODE_BSS);
749 		break;
750 
751 	case IEEE80211_M_IBSS:
752 	case IEEE80211_M_AHDEMO:
753 		data = LE_32(IPW2100_MODE_IBSS);
754 		break;
755 	}
756 
757 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
758 	    "ipw2100_config(): Setting mode to %u\n", LE_32(data)));
759 
760 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_MODE,
761 	    &data, sizeof (data));
762 	if (err != DDI_SUCCESS)
763 		return (err);
764 
765 	/*
766 	 * operation channel if IBSS or MONITOR
767 	 */
768 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
769 
770 		data = LE_32(ieee80211_chan2ieee(ic, ic->ic_ibss_chan));
771 
772 		IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
773 		    "ipw2100_config(): Setting channel to %u\n", LE_32(data)));
774 
775 		err = ipw2100_cmd(sc, IPW2100_CMD_SET_CHANNEL,
776 		    &data, sizeof (data));
777 		if (err != DDI_SUCCESS)
778 			return (err);
779 	}
780 
781 	/*
782 	 * set MAC address
783 	 */
784 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
785 	    "ipw2100_config(): Setting MAC address to "
786 	    "%02x:%02x:%02x:%02x:%02x:%02x\n",
787 	    ic->ic_macaddr[0], ic->ic_macaddr[1], ic->ic_macaddr[2],
788 	    ic->ic_macaddr[3], ic->ic_macaddr[4], ic->ic_macaddr[5]));
789 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_MAC_ADDRESS, ic->ic_macaddr,
790 	    IEEE80211_ADDR_LEN);
791 	if (err != DDI_SUCCESS)
792 		return (err);
793 
794 	/*
795 	 * configuration capabilities
796 	 */
797 	cfg.flags = IPW2100_CFG_BSS_MASK | IPW2100_CFG_IBSS_MASK |
798 	    IPW2100_CFG_PREAMBLE_AUTO | IPW2100_CFG_802_1x_ENABLE;
799 	if (ic->ic_opmode == IEEE80211_M_IBSS)
800 		cfg.flags |= IPW2100_CFG_IBSS_AUTO_START;
801 	if (sc->if_flags & IFF_PROMISC)
802 		cfg.flags |= IPW2100_CFG_PROMISCUOUS;
803 	cfg.flags	= LE_32(cfg.flags);
804 	cfg.bss_chan	= LE_32(sc->sc_chmask >> 1);
805 	cfg.ibss_chan	= LE_32(sc->sc_chmask >> 1);
806 
807 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
808 	    "ipw2100_config(): Setting configuration to 0x%x\n",
809 	    LE_32(cfg.flags)));
810 
811 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_CONFIGURATION,
812 	    &cfg, sizeof (cfg));
813 
814 	if (err != DDI_SUCCESS)
815 		return (err);
816 
817 	/*
818 	 * set 802.11 Tx rates
819 	 */
820 	data = LE_32(0x3);  /* 1, 2 */
821 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
822 	    "ipw2100_config(): Setting 802.11 Tx rates to 0x%x\n",
823 	    LE_32(data)));
824 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_BASIC_TX_RATES,
825 	    &data, sizeof (data));
826 	if (err != DDI_SUCCESS)
827 		return (err);
828 
829 	/*
830 	 * set 802.11b Tx rates
831 	 */
832 	data = LE_32(0xf);  /* 1, 2, 5.5, 11 */
833 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
834 	    "ipw2100_config(): Setting 802.11b Tx rates to 0x%x\n",
835 	    LE_32(data)));
836 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_TX_RATES, &data, sizeof (data));
837 	if (err != DDI_SUCCESS)
838 		return (err);
839 
840 	/*
841 	 * set power mode
842 	 */
843 	data = LE_32(IPW2100_POWER_MODE_CAM);
844 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
845 	    "ipw2100_config(): Setting power mode to %u\n", LE_32(data)));
846 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_POWER_MODE, &data, sizeof (data));
847 	if (err != DDI_SUCCESS)
848 		return (err);
849 
850 	/*
851 	 * set power index
852 	 */
853 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
854 		data = LE_32(32);
855 		IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
856 		    "ipw2100_config(): Setting Tx power index to %u\n",
857 		    LE_32(data)));
858 		err = ipw2100_cmd(sc, IPW2100_CMD_SET_TX_POWER_INDEX,
859 		    &data, sizeof (data));
860 		if (err != DDI_SUCCESS)
861 			return (err);
862 	}
863 
864 	/*
865 	 * set RTS threshold
866 	 */
867 	ic->ic_rtsthreshold = 2346;
868 	data = LE_32(ic->ic_rtsthreshold);
869 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
870 	    "ipw2100_config(): Setting RTS threshold to %u\n", LE_32(data)));
871 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_RTS_THRESHOLD,
872 	    &data, sizeof (data));
873 	if (err != DDI_SUCCESS)
874 		return (err);
875 
876 	/*
877 	 * set frag threshold
878 	 */
879 	ic->ic_fragthreshold = 2346;
880 	data = LE_32(ic->ic_fragthreshold);
881 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
882 	    "ipw2100_config(): Setting frag threshold to %u\n", LE_32(data)));
883 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_FRAG_THRESHOLD,
884 	    &data, sizeof (data));
885 	if (err != DDI_SUCCESS)
886 		return (err);
887 
888 	/*
889 	 * set ESSID
890 	 */
891 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
892 	    "ipw2100_config(): Setting ESSID to %u, ESSID[0]%c\n",
893 	    ic->ic_des_esslen, ic->ic_des_essid[0]));
894 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_ESSID,
895 	    ic->ic_des_essid, ic->ic_des_esslen);
896 	if (err != DDI_SUCCESS)
897 		return (err);
898 
899 	/*
900 	 * no mandatory BSSID
901 	 */
902 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_MANDATORY_BSSID, NULL, 0);
903 	if (err != DDI_SUCCESS)
904 		return (err);
905 
906 	/*
907 	 * set BSSID, if any
908 	 */
909 	if (ic->ic_flags & IEEE80211_F_DESBSSID) {
910 		IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
911 		    "ipw2100_config(): Setting BSSID to %u\n",
912 		    IEEE80211_ADDR_LEN));
913 		err = ipw2100_cmd(sc, IPW2100_CMD_SET_DESIRED_BSSID,
914 		    ic->ic_des_bssid, IEEE80211_ADDR_LEN);
915 		if (err != DDI_SUCCESS)
916 			return (err);
917 	}
918 
919 	/*
920 	 * set security information
921 	 */
922 	(void) memset(&sec, 0, sizeof (sec));
923 	/*
924 	 * use the value set to ic_bss to retrieve current sharedmode
925 	 */
926 	sec.authmode = (ic->ic_bss->in_authmode == WL_SHAREDKEY) ?
927 	    IPW2100_AUTH_SHARED : IPW2100_AUTH_OPEN;
928 	sec.ciphers = LE_32(IPW2100_CIPHER_NONE);
929 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
930 	    "ipw2100_config(): Setting authmode to %u\n", sec.authmode));
931 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_SECURITY_INFORMATION,
932 	    &sec, sizeof (sec));
933 	if (err != DDI_SUCCESS)
934 		return (err);
935 
936 	/*
937 	 * set WEP if any
938 	 */
939 	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
940 		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
941 			if (ic->ic_nw_keys[i].wk_keylen == 0)
942 				continue;
943 			wkey.idx = (uint8_t)i;
944 			wkey.len = ic->ic_nw_keys[i].wk_keylen;
945 			(void) memset(wkey.key, 0, sizeof (wkey.key));
946 			if (ic->ic_nw_keys[i].wk_keylen)
947 				(void) memcpy(wkey.key,
948 				    ic->ic_nw_keys[i].wk_key,
949 				    ic->ic_nw_keys[i].wk_keylen);
950 			err = ipw2100_cmd(sc, IPW2100_CMD_SET_WEP_KEY,
951 			    &wkey, sizeof (wkey));
952 			if (err != DDI_SUCCESS)
953 				return (err);
954 		}
955 		data = LE_32(ic->ic_def_txkey);
956 		err = ipw2100_cmd(sc, IPW2100_CMD_SET_WEP_KEY_INDEX,
957 		    &data, sizeof (data));
958 		if (err != DDI_SUCCESS)
959 			return (err);
960 	}
961 
962 	/*
963 	 * turn on WEP
964 	 */
965 	data = LE_32((ic->ic_flags & IEEE80211_F_PRIVACY) ? 0x8 : 0);
966 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
967 	    "ipw2100_config(): Setting WEP flags to %u\n", LE_32(data)));
968 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_WEP_FLAGS, &data, sizeof (data));
969 	if (err != DDI_SUCCESS)
970 		return (err);
971 
972 	/*
973 	 * set beacon interval if IBSS or HostAP
974 	 */
975 	if (ic->ic_opmode == IEEE80211_M_IBSS ||
976 	    ic->ic_opmode == IEEE80211_M_HOSTAP) {
977 
978 		data = LE_32(ic->ic_lintval);
979 		IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
980 		    "ipw2100_config(): Setting beacon interval to %u\n",
981 		    LE_32(data)));
982 		err = ipw2100_cmd(sc, IPW2100_CMD_SET_BEACON_INTERVAL,
983 		    &data, sizeof (data));
984 		if (err != DDI_SUCCESS)
985 			return (err);
986 	}
987 
988 	/*
989 	 * set scan options
990 	 */
991 	sopt.flags = LE_32(0);
992 	sopt.channels = LE_32(sc->sc_chmask >> 1);
993 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_SCAN_OPTIONS,
994 	    &sopt, sizeof (sopt));
995 	if (err != DDI_SUCCESS)
996 		return (err);
997 
998 en_adapter:
999 
1000 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
1001 	    "ipw2100_config(): Enabling adapter\n"));
1002 
1003 	return (ipw2100_cmd(sc, IPW2100_CMD_ENABLE, NULL, 0));
1004 }
1005 
1006 static int
1007 ipw2100_cmd(struct ipw2100_softc *sc, uint32_t type, void *buf, size_t len)
1008 {
1009 	struct ipw2100_bd	*txbd;
1010 	clock_t			clk;
1011 	uint32_t		idx;
1012 
1013 	/*
1014 	 * prepare command buffer
1015 	 */
1016 	sc->sc_cmd->type = LE_32(type);
1017 	sc->sc_cmd->subtype = LE_32(0);
1018 	sc->sc_cmd->seq = LE_32(0);
1019 	/*
1020 	 * copy data if any
1021 	 */
1022 	if (len && buf)
1023 		(void) memcpy(sc->sc_cmd->data, buf, len);
1024 	sc->sc_cmd->len = LE_32(len);
1025 
1026 	/*
1027 	 * get host & device descriptor to submit command
1028 	 */
1029 	mutex_enter(&sc->sc_tx_lock);
1030 
1031 	IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT,
1032 	    "ipw2100_cmd(): tx-free=%d\n", sc->sc_tx_free));
1033 
1034 	/*
1035 	 * command need 1 descriptor
1036 	 */
1037 	while (sc->sc_tx_free < 1)  {
1038 		sc->sc_flags |= IPW2100_FLAG_CMD_WAIT;
1039 		cv_wait(&sc->sc_tx_cond, &sc->sc_tx_lock);
1040 	}
1041 	idx = sc->sc_tx_cur;
1042 
1043 	IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT,
1044 	    "ipw2100_cmd(): tx-cur=%d\n", idx));
1045 
1046 	sc->sc_done = 0;
1047 
1048 	txbd		= &sc->sc_txbd[idx];
1049 	txbd->phyaddr	= LE_32(sc->sc_dma_cmd.dr_pbase);
1050 	txbd->len	= LE_32(sizeof (struct ipw2100_cmd));
1051 	txbd->flags	= IPW2100_BD_FLAG_TX_FRAME_COMMAND
1052 	    | IPW2100_BD_FLAG_TX_LAST_FRAGMENT;
1053 	txbd->nfrag	= 1;
1054 	/*
1055 	 * sync for device
1056 	 */
1057 	(void) ddi_dma_sync(sc->sc_dma_cmd.dr_hnd, 0,
1058 	    sizeof (struct ipw2100_cmd), DDI_DMA_SYNC_FORDEV);
1059 	(void) ddi_dma_sync(sc->sc_dma_txbd.dr_hnd,
1060 	    idx * sizeof (struct ipw2100_bd),
1061 	    sizeof (struct ipw2100_bd), DDI_DMA_SYNC_FORDEV);
1062 
1063 	/*
1064 	 * ring move forward
1065 	 */
1066 	sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2100_NUM_TXBD);
1067 	sc->sc_tx_free--;
1068 	ipw2100_csr_put32(sc, IPW2100_CSR_TX_WRITE_INDEX, sc->sc_tx_cur);
1069 	mutex_exit(&sc->sc_tx_lock);
1070 
1071 	/*
1072 	 * wait for command done
1073 	 */
1074 	mutex_enter(&sc->sc_ilock);
1075 	while (sc->sc_done == 0) {
1076 		/*
1077 		 * pending for the response
1078 		 */
1079 		clk = ddi_get_lbolt() + drv_usectohz(1000000);  /* 1 second */
1080 		if (cv_timedwait(&sc->sc_cmd_cond, &sc->sc_ilock, clk) < 0)
1081 			break;
1082 	}
1083 	mutex_exit(&sc->sc_ilock);
1084 
1085 	IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT,
1086 	    "ipw2100_cmd(): cmd-done=%s\n", sc->sc_done ? "yes" : "no"));
1087 
1088 	if (sc->sc_done == 0)
1089 		return (DDI_FAILURE);
1090 
1091 	return (DDI_SUCCESS);
1092 }
1093 
1094 int
1095 ipw2100_init(struct ipw2100_softc *sc)
1096 {
1097 	int	err;
1098 
1099 	IPW2100_DBG(IPW2100_DBG_INIT, (sc->sc_dip, CE_CONT,
1100 	    "ipw2100_init(): enter\n"));
1101 
1102 	/*
1103 	 * no firmware is available, return fail directly
1104 	 */
1105 	if (!(sc->sc_flags & IPW2100_FLAG_FW_CACHED)) {
1106 		IPW2100_WARN((sc->sc_dip, CE_WARN,
1107 		    "ipw2100_init(): no firmware is available\n"));
1108 		return (DDI_FAILURE);
1109 	}
1110 
1111 	ipw2100_stop(sc);
1112 
1113 	err = ipw2100_chip_reset(sc);
1114 	if (err != DDI_SUCCESS) {
1115 		IPW2100_WARN((sc->sc_dip, CE_WARN,
1116 		    "ipw2100_init(): could not reset adapter\n"));
1117 		goto fail;
1118 	}
1119 
1120 	/*
1121 	 * load microcode
1122 	 */
1123 	IPW2100_DBG(IPW2100_DBG_INIT, (sc->sc_dip, CE_CONT,
1124 	    "ipw2100_init(): loading microcode\n"));
1125 	err = ipw2100_load_uc(sc);
1126 	if (err != DDI_SUCCESS) {
1127 		IPW2100_WARN((sc->sc_dip, CE_WARN,
1128 		    "ipw2100_init(): could not load microcode, try again\n"));
1129 		goto fail;
1130 	}
1131 
1132 	ipw2100_master_stop(sc);
1133 
1134 	ipw2100_ring_hwsetup(sc);
1135 
1136 	/*
1137 	 * load firmware
1138 	 */
1139 	IPW2100_DBG(IPW2100_DBG_INIT, (sc->sc_dip, CE_CONT,
1140 	    "ipw2100_init(): loading firmware\n"));
1141 	err = ipw2100_load_fw(sc);
1142 	if (err != DDI_SUCCESS) {
1143 		IPW2100_WARN((sc->sc_dip, CE_WARN,
1144 		    "ipw2100_init(): could not load firmware, try again\n"));
1145 		goto fail;
1146 	}
1147 
1148 	/*
1149 	 * initialize tables
1150 	 */
1151 	ipw2100_tables_init(sc);
1152 	ipw2100_table1_put32(sc, IPW2100_INFO_LOCK, 0);
1153 
1154 	/*
1155 	 * Hardware will be enabled after configuration
1156 	 */
1157 	err = ipw2100_config(sc);
1158 	if (err != DDI_SUCCESS) {
1159 		IPW2100_WARN((sc->sc_dip, CE_WARN,
1160 		    "ipw2100_init(): device configuration failed\n"));
1161 		goto fail;
1162 	}
1163 
1164 	delay(drv_usectohz(delay_config_stable));
1165 
1166 	return (DDI_SUCCESS);
1167 
1168 fail:
1169 	ipw2100_stop(sc);
1170 
1171 	return (err);
1172 }
1173 
1174 /*
1175  * get hardware configurations from EEPROM embedded within chip
1176  */
1177 static void
1178 ipw2100_hwconf_get(struct ipw2100_softc *sc)
1179 {
1180 	int		i;
1181 	uint16_t	val;
1182 
1183 	/*
1184 	 * MAC address
1185 	 */
1186 	i = 0;
1187 	val = ipw2100_rom_get16(sc, IPW2100_ROM_MAC + 0);
1188 	sc->sc_macaddr[i++] = val >> 8;
1189 	sc->sc_macaddr[i++] = val & 0xff;
1190 	val = ipw2100_rom_get16(sc, IPW2100_ROM_MAC + 1);
1191 	sc->sc_macaddr[i++] = val >> 8;
1192 	sc->sc_macaddr[i++] = val & 0xff;
1193 	val = ipw2100_rom_get16(sc, IPW2100_ROM_MAC + 2);
1194 	sc->sc_macaddr[i++] = val >> 8;
1195 	sc->sc_macaddr[i++] = val & 0xff;
1196 
1197 	/*
1198 	 * formatted MAC address string
1199 	 */
1200 	(void) snprintf(sc->sc_macstr, sizeof (sc->sc_macstr),
1201 	    "%02x:%02x:%02x:%02x:%02x:%02x",
1202 	    sc->sc_macaddr[0], sc->sc_macaddr[1],
1203 	    sc->sc_macaddr[2], sc->sc_macaddr[3],
1204 	    sc->sc_macaddr[4], sc->sc_macaddr[5]);
1205 
1206 	/*
1207 	 * channel mask
1208 	 */
1209 	val = ipw2100_rom_get16(sc, IPW2100_ROM_CHANNEL_LIST);
1210 	if (val == 0)
1211 		val = 0x7ff;
1212 	sc->sc_chmask = val << 1;
1213 	IPW2100_DBG(IPW2100_DBG_HWCAP, (sc->sc_dip, CE_CONT,
1214 	    "ipw2100_hwconf_get(): channel-mask=0x%08x\n", sc->sc_chmask));
1215 
1216 	/*
1217 	 * radio switch
1218 	 */
1219 	val = ipw2100_rom_get16(sc, IPW2100_ROM_RADIO);
1220 	if (val & 0x08)
1221 		sc->sc_flags |= IPW2100_FLAG_HAS_RADIO_SWITCH;
1222 
1223 	IPW2100_DBG(IPW2100_DBG_HWCAP, (sc->sc_dip, CE_CONT,
1224 	    "ipw2100_hwconf_get(): has-radio-switch=%s(%u)\n",
1225 	    (sc->sc_flags & IPW2100_FLAG_HAS_RADIO_SWITCH)?  "yes" : "no",
1226 	    val));
1227 }
1228 
1229 /*
1230  * all ipw2100 interrupts will be masked by this routine
1231  */
1232 static void
1233 ipw2100_master_stop(struct ipw2100_softc *sc)
1234 {
1235 	uint32_t	tmp;
1236 	int		ntries;
1237 
1238 	/*
1239 	 * disable interrupts
1240 	 */
1241 	ipw2100_csr_put32(sc, IPW2100_CSR_INTR_MASK, 0);
1242 
1243 	ipw2100_csr_put32(sc, IPW2100_CSR_RST, IPW2100_RST_STOP_MASTER);
1244 	for (ntries = 0; ntries < 50; ntries++) {
1245 		if (ipw2100_csr_get32(sc, IPW2100_CSR_RST)
1246 		    & IPW2100_RST_MASTER_DISABLED)
1247 			break;
1248 		drv_usecwait(10);
1249 	}
1250 	if (ntries == 50 && !(sc->sc_flags & IPW2100_FLAG_QUIESCED))
1251 		IPW2100_WARN((sc->sc_dip, CE_WARN,
1252 		    "ipw2100_master_stop(): timeout when stop master\n"));
1253 
1254 	tmp = ipw2100_csr_get32(sc, IPW2100_CSR_RST);
1255 	ipw2100_csr_put32(sc, IPW2100_CSR_RST,
1256 	    tmp | IPW2100_RST_PRINCETON_RESET);
1257 
1258 	sc->sc_flags &= ~IPW2100_FLAG_FW_INITED;
1259 }
1260 
1261 /*
1262  * all ipw2100 interrupts will be masked by this routine
1263  */
1264 static int
1265 ipw2100_chip_reset(struct ipw2100_softc *sc)
1266 {
1267 	int		ntries;
1268 	uint32_t	tmp;
1269 
1270 	ipw2100_master_stop(sc);
1271 
1272 	/*
1273 	 * move adatper to DO state
1274 	 */
1275 	tmp = ipw2100_csr_get32(sc, IPW2100_CSR_CTL);
1276 	ipw2100_csr_put32(sc, IPW2100_CSR_CTL, tmp | IPW2100_CTL_INIT);
1277 
1278 	/*
1279 	 * wait for clock stabilization
1280 	 */
1281 	for (ntries = 0; ntries < 1000; ntries++) {
1282 		if (ipw2100_csr_get32(sc, IPW2100_CSR_CTL)
1283 		    & IPW2100_CTL_CLOCK_READY)
1284 			break;
1285 		drv_usecwait(200);
1286 	}
1287 	if (ntries == 1000)
1288 		return (DDI_FAILURE);
1289 
1290 	tmp = ipw2100_csr_get32(sc, IPW2100_CSR_RST);
1291 	ipw2100_csr_put32(sc, IPW2100_CSR_RST, tmp | IPW2100_RST_SW_RESET);
1292 
1293 	drv_usecwait(10);
1294 
1295 	tmp = ipw2100_csr_get32(sc, IPW2100_CSR_CTL);
1296 	ipw2100_csr_put32(sc, IPW2100_CSR_CTL, tmp | IPW2100_CTL_INIT);
1297 
1298 	return (DDI_SUCCESS);
1299 }
1300 
1301 /*
1302  * get the radio status from IPW_CSR_IO, invoked by wificonfig/dladm
1303  */
1304 int
1305 ipw2100_get_radio(struct ipw2100_softc *sc)
1306 {
1307 	if (ipw2100_csr_get32(sc, IPW2100_CSR_IO) & IPW2100_IO_RADIO_DISABLED)
1308 		return (0);
1309 	else
1310 		return (1);
1311 
1312 }
1313 /*
1314  * This function is used to get the statistic, invoked by wificonfig/dladm
1315  */
1316 void
1317 ipw2100_get_statistics(struct ipw2100_softc *sc)
1318 {
1319 	struct ieee80211com	*ic = &sc->sc_ic;
1320 	uint32_t		addr, size, i;
1321 	uint32_t		atbl[256], *datatbl;
1322 
1323 	datatbl = atbl;
1324 
1325 	if (!(sc->sc_flags & IPW2100_FLAG_FW_INITED)) {
1326 		IPW2100_DBG(IPW2100_DBG_STATISTIC, (sc->sc_dip, CE_CONT,
1327 		    "ipw2100_get_statistic(): fw doesn't download yet."));
1328 		return;
1329 	}
1330 
1331 	ipw2100_csr_put32(sc, IPW2100_CSR_AUTOINC_ADDR, sc->sc_table1_base);
1332 
1333 	size = ipw2100_csr_get32(sc, IPW2100_CSR_AUTOINC_DATA);
1334 	atbl[0] = size;
1335 	for (i = 1, ++datatbl; i < size; i++, datatbl++) {
1336 		addr = ipw2100_csr_get32(sc, IPW2100_CSR_AUTOINC_DATA);
1337 		*datatbl = ipw2100_imem_get32(sc, addr);
1338 	}
1339 
1340 	/*
1341 	 * To retrieve the statistic information into proper places. There are
1342 	 * lot of information.
1343 	 */
1344 	IPW2100_DBG(IPW2100_DBG_STATISTIC, (sc->sc_dip, CE_CONT,
1345 	    "ipw2100_get_statistic(): \n"
1346 	    "operating mode = %u\n"
1347 	    "type of authentification= %u\n"
1348 	    "average RSSI= %u\n"
1349 	    "current channel = %d\n",
1350 	    atbl[191], atbl[199], atbl[173], atbl[189]));
1351 	/* WIFI_STAT_TX_FRAGS */
1352 	ic->ic_stats.is_tx_frags = (uint32_t)atbl[2];
1353 	/* WIFI_STAT_MCAST_TX = (all frame - unicast frame) */
1354 	ic->ic_stats.is_tx_mcast = (uint32_t)atbl[2] - (uint32_t)atbl[3];
1355 	/* WIFI_STAT_TX_RETRANS */
1356 	ic->ic_stats.is_tx_retries = (uint32_t)atbl[42];
1357 	/* WIFI_STAT_TX_FAILED */
1358 	ic->ic_stats.is_tx_failed = (uint32_t)atbl[51];
1359 	/* MAC_STAT_OBYTES */
1360 	ic->ic_stats.is_tx_bytes = (uint32_t)atbl[41];
1361 	/* WIFI_STAT_RX_FRAGS */
1362 	ic->ic_stats.is_rx_frags = (uint32_t)atbl[61];
1363 	/* WIFI_STAT_MCAST_RX */
1364 	ic->ic_stats.is_rx_mcast = (uint32_t)atbl[71];
1365 	/* MAC_STAT_IBYTES */
1366 	ic->ic_stats.is_rx_bytes = (uint32_t)atbl[101];
1367 	/* WIFI_STAT_ACK_FAILURE */
1368 	ic->ic_stats.is_ack_failure = (uint32_t)atbl[59];
1369 	/* WIFI_STAT_RTS_SUCCESS */
1370 	ic->ic_stats.is_rts_success = (uint32_t)atbl[22];
1371 }
1372 
1373 /*
1374  * dma region alloc
1375  */
1376 static int
1377 ipw2100_dma_region_alloc(struct ipw2100_softc *sc,
1378     struct dma_region *dr, size_t size, uint_t dir, uint_t flags)
1379 {
1380 	dev_info_t	*dip = sc->sc_dip;
1381 	int		err;
1382 
1383 	IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT,
1384 	    "ipw2100_dma_region_alloc() name=%s size=%u\n",
1385 	    dr->dr_name, size));
1386 
1387 	err = ddi_dma_alloc_handle(dip, &ipw2100_dma_attr, DDI_DMA_SLEEP, NULL,
1388 	    &dr->dr_hnd);
1389 	if (err != DDI_SUCCESS) {
1390 		IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT,
1391 		    "ipw2100_dma_region_alloc(): "
1392 		    "ddi_dma_alloc_handle() failed\n"));
1393 		goto fail0;
1394 	}
1395 
1396 	err = ddi_dma_mem_alloc(dr->dr_hnd, size, &ipw2100_dma_accattr,
1397 	    flags, DDI_DMA_SLEEP, NULL, &dr->dr_base,
1398 	    &dr->dr_size, &dr->dr_acc);
1399 	if (err != DDI_SUCCESS) {
1400 		IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT,
1401 		    "ipw2100_dma_region_alloc(): "
1402 		    "ddi_dma_mem_alloc() failed\n"));
1403 		goto fail1;
1404 	}
1405 
1406 	err = ddi_dma_addr_bind_handle(dr->dr_hnd, NULL,
1407 	    dr->dr_base, dr->dr_size, dir | flags, DDI_DMA_SLEEP, NULL,
1408 	    &dr->dr_cookie, &dr->dr_ccnt);
1409 	if (err != DDI_DMA_MAPPED) {
1410 		IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT,
1411 		    "ipw2100_dma_region_alloc(): "
1412 		    "ddi_dma_addr_bind_handle() failed\n"));
1413 		goto fail2;
1414 	}
1415 
1416 	if (dr->dr_ccnt != 1) {
1417 		err = DDI_FAILURE;
1418 		goto fail3;
1419 	}
1420 	dr->dr_pbase = dr->dr_cookie.dmac_address;
1421 
1422 	IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT,
1423 	    "ipw2100_dma_region_alloc(): get physical-base=0x%08x\n",
1424 	    dr->dr_pbase));
1425 
1426 	return (DDI_SUCCESS);
1427 
1428 fail3:
1429 	(void) ddi_dma_unbind_handle(dr->dr_hnd);
1430 fail2:
1431 	ddi_dma_mem_free(&dr->dr_acc);
1432 fail1:
1433 	ddi_dma_free_handle(&dr->dr_hnd);
1434 fail0:
1435 	return (err);
1436 }
1437 
1438 static void
1439 ipw2100_dma_region_free(struct dma_region *dr)
1440 {
1441 	(void) ddi_dma_unbind_handle(dr->dr_hnd);
1442 	ddi_dma_mem_free(&dr->dr_acc);
1443 	ddi_dma_free_handle(&dr->dr_hnd);
1444 }
1445 
1446 static int
1447 ipw2100_ring_alloc(struct ipw2100_softc *sc)
1448 {
1449 	int	err, i;
1450 
1451 	/*
1452 	 * tx ring
1453 	 */
1454 	sc->sc_dma_txbd.dr_name = "ipw2100-tx-ring-bd";
1455 	err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_txbd,
1456 	    IPW2100_TXBD_SIZE, DDI_DMA_WRITE, DDI_DMA_CONSISTENT);
1457 	if (err != DDI_SUCCESS)
1458 		goto fail0;
1459 	/*
1460 	 * tx bufs
1461 	 */
1462 	for (i = 0; i < IPW2100_NUM_TXBUF; i++) {
1463 		sc->sc_dma_txbufs[i].dr_name = "ipw2100-tx-buf";
1464 		err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_txbufs[i],
1465 		    IPW2100_TXBUF_SIZE, DDI_DMA_WRITE, DDI_DMA_STREAMING);
1466 		if (err != DDI_SUCCESS) {
1467 			while (i > 0) {
1468 				i--;
1469 				ipw2100_dma_region_free(&sc->sc_dma_txbufs[i]);
1470 			}
1471 			goto fail1;
1472 		}
1473 	}
1474 	/*
1475 	 * rx ring
1476 	 */
1477 	sc->sc_dma_rxbd.dr_name = "ipw2100-rx-ring-bd";
1478 	err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_rxbd,
1479 	    IPW2100_RXBD_SIZE, DDI_DMA_WRITE, DDI_DMA_CONSISTENT);
1480 	if (err != DDI_SUCCESS)
1481 		goto fail2;
1482 	/*
1483 	 * rx bufs
1484 	 */
1485 	for (i = 0; i < IPW2100_NUM_RXBUF; i++) {
1486 		sc->sc_dma_rxbufs[i].dr_name = "ipw2100-rx-buf";
1487 		err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_rxbufs[i],
1488 		    IPW2100_RXBUF_SIZE, DDI_DMA_READ, DDI_DMA_STREAMING);
1489 		if (err != DDI_SUCCESS) {
1490 			while (i > 0) {
1491 				i--;
1492 				ipw2100_dma_region_free(&sc->sc_dma_rxbufs[i]);
1493 			}
1494 			goto fail3;
1495 		}
1496 	}
1497 	/*
1498 	 * status
1499 	 */
1500 	sc->sc_dma_status.dr_name = "ipw2100-rx-status";
1501 	err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_status,
1502 	    IPW2100_STATUS_SIZE, DDI_DMA_READ, DDI_DMA_CONSISTENT);
1503 	if (err != DDI_SUCCESS)
1504 		goto fail4;
1505 	/*
1506 	 * command
1507 	 */
1508 	sc->sc_dma_cmd.dr_name = "ipw2100-cmd";
1509 	err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_cmd, IPW2100_CMD_SIZE,
1510 	    DDI_DMA_WRITE, DDI_DMA_CONSISTENT);
1511 	if (err != DDI_SUCCESS)
1512 		goto fail5;
1513 
1514 	return (DDI_SUCCESS);
1515 
1516 fail5:
1517 	ipw2100_dma_region_free(&sc->sc_dma_status);
1518 fail4:
1519 	for (i = 0; i < IPW2100_NUM_RXBUF; i++)
1520 		ipw2100_dma_region_free(&sc->sc_dma_rxbufs[i]);
1521 fail3:
1522 	ipw2100_dma_region_free(&sc->sc_dma_rxbd);
1523 fail2:
1524 	for (i = 0; i < IPW2100_NUM_TXBUF; i++)
1525 		ipw2100_dma_region_free(&sc->sc_dma_txbufs[i]);
1526 fail1:
1527 	ipw2100_dma_region_free(&sc->sc_dma_txbd);
1528 fail0:
1529 	return (err);
1530 }
1531 
1532 static void
1533 ipw2100_ring_free(struct ipw2100_softc *sc)
1534 {
1535 	int	i;
1536 
1537 	/*
1538 	 * tx ring
1539 	 */
1540 	ipw2100_dma_region_free(&sc->sc_dma_txbd);
1541 	/*
1542 	 * tx buf
1543 	 */
1544 	for (i = 0; i < IPW2100_NUM_TXBUF; i++)
1545 		ipw2100_dma_region_free(&sc->sc_dma_txbufs[i]);
1546 	/*
1547 	 * rx ring
1548 	 */
1549 	ipw2100_dma_region_free(&sc->sc_dma_rxbd);
1550 	/*
1551 	 * rx buf
1552 	 */
1553 	for (i = 0; i < IPW2100_NUM_RXBUF; i++)
1554 		ipw2100_dma_region_free(&sc->sc_dma_rxbufs[i]);
1555 	/*
1556 	 * status
1557 	 */
1558 	ipw2100_dma_region_free(&sc->sc_dma_status);
1559 	/*
1560 	 * command
1561 	 */
1562 	ipw2100_dma_region_free(&sc->sc_dma_cmd);
1563 }
1564 
1565 static void
1566 ipw2100_ring_reset(struct ipw2100_softc *sc)
1567 {
1568 	int	i;
1569 
1570 	/*
1571 	 * tx ring
1572 	 */
1573 	sc->sc_tx_cur   = 0;
1574 	sc->sc_tx_free  = IPW2100_NUM_TXBD;
1575 	sc->sc_txbd	= (struct ipw2100_bd *)sc->sc_dma_txbd.dr_base;
1576 	for (i = 0; i < IPW2100_NUM_TXBUF; i++)
1577 		sc->sc_txbufs[i] =
1578 		    (struct ipw2100_txb *)sc->sc_dma_txbufs[i].dr_base;
1579 	/*
1580 	 * rx ring
1581 	 */
1582 	sc->sc_rx_cur   = 0;
1583 	sc->sc_rx_free  = IPW2100_NUM_RXBD;
1584 	sc->sc_status   = (struct ipw2100_status *)sc->sc_dma_status.dr_base;
1585 	sc->sc_rxbd	= (struct ipw2100_bd *)sc->sc_dma_rxbd.dr_base;
1586 	for (i = 0; i < IPW2100_NUM_RXBUF; i++) {
1587 		sc->sc_rxbufs[i] =
1588 		    (struct ipw2100_rxb *)sc->sc_dma_rxbufs[i].dr_base;
1589 		/*
1590 		 * initialize Rx buffer descriptors, both host and device
1591 		 */
1592 		sc->sc_rxbd[i].phyaddr  = LE_32(sc->sc_dma_rxbufs[i].dr_pbase);
1593 		sc->sc_rxbd[i].len	= LE_32(sc->sc_dma_rxbufs[i].dr_size);
1594 		sc->sc_rxbd[i].flags	= 0;
1595 		sc->sc_rxbd[i].nfrag	= 1;
1596 	}
1597 	/*
1598 	 * command
1599 	 */
1600 	sc->sc_cmd = (struct ipw2100_cmd *)sc->sc_dma_cmd.dr_base;
1601 }
1602 
1603 /*
1604  * tx, rx rings and command initialization
1605  */
1606 static int
1607 ipw2100_ring_init(struct ipw2100_softc *sc)
1608 {
1609 	int	err;
1610 
1611 	err = ipw2100_ring_alloc(sc);
1612 	if (err != DDI_SUCCESS)
1613 		return (err);
1614 
1615 	ipw2100_ring_reset(sc);
1616 
1617 	return (DDI_SUCCESS);
1618 }
1619 
1620 static void
1621 ipw2100_ring_hwsetup(struct ipw2100_softc *sc)
1622 {
1623 	ipw2100_ring_reset(sc);
1624 	/*
1625 	 * tx ring
1626 	 */
1627 	ipw2100_csr_put32(sc, IPW2100_CSR_TX_BD_BASE, sc->sc_dma_txbd.dr_pbase);
1628 	ipw2100_csr_put32(sc, IPW2100_CSR_TX_BD_SIZE, IPW2100_NUM_TXBD);
1629 	/*
1630 	 * no new packet to transmit, tx-rd-index == tx-wr-index
1631 	 */
1632 	ipw2100_csr_put32(sc, IPW2100_CSR_TX_READ_INDEX, sc->sc_tx_cur);
1633 	ipw2100_csr_put32(sc, IPW2100_CSR_TX_WRITE_INDEX, sc->sc_tx_cur);
1634 	/*
1635 	 * rx ring
1636 	 */
1637 	ipw2100_csr_put32(sc, IPW2100_CSR_RX_BD_BASE, sc->sc_dma_rxbd.dr_pbase);
1638 	ipw2100_csr_put32(sc, IPW2100_CSR_RX_BD_SIZE, IPW2100_NUM_RXBD);
1639 	/*
1640 	 * all rx buffer are empty, rx-rd-index == 0 && rx-wr-index == N-1
1641 	 */
1642 	IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT,
1643 	    "ipw2100_ring_hwsetup(): rx-cur=%u, backward=%u\n",
1644 	    sc->sc_rx_cur, RING_BACKWARD(sc->sc_rx_cur, 1, IPW2100_NUM_RXBD)));
1645 	ipw2100_csr_put32(sc, IPW2100_CSR_RX_READ_INDEX, sc->sc_rx_cur);
1646 	ipw2100_csr_put32(sc, IPW2100_CSR_RX_WRITE_INDEX,
1647 	    RING_BACKWARD(sc->sc_rx_cur, 1, IPW2100_NUM_RXBD));
1648 	/*
1649 	 * status
1650 	 */
1651 	ipw2100_csr_put32(sc, IPW2100_CSR_RX_STATUS_BASE,
1652 	    sc->sc_dma_status.dr_pbase);
1653 }
1654 
1655 /*
1656  * ieee80211_new_state() is not be used, since the hardware can handle the
1657  * state transfer. Here, we just keep the status of the hardware notification
1658  * result.
1659  */
1660 /* ARGSUSED */
1661 static int
1662 ipw2100_newstate(struct ieee80211com *ic, enum ieee80211_state state, int arg)
1663 {
1664 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)ic;
1665 	struct ieee80211_node	*in;
1666 	uint8_t			macaddr[IEEE80211_ADDR_LEN];
1667 	uint32_t		len;
1668 	wifi_data_t		wd = { 0 };
1669 
1670 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
1671 	    "ipw2100_newstate(): %s -> %s\n",
1672 	    ieee80211_state_name[ic->ic_state], ieee80211_state_name[state]));
1673 
1674 	switch (state) {
1675 	case IEEE80211_S_RUN:
1676 		/*
1677 		 * we only need to use BSSID as to find the node
1678 		 */
1679 		drv_usecwait(200); /* firmware needs a short delay here */
1680 		len = IEEE80211_ADDR_LEN;
1681 		(void) ipw2100_table2_getbuf(sc, IPW2100_INFO_CURRENT_BSSID,
1682 		    macaddr, &len);
1683 
1684 		in = ieee80211_find_node(&ic->ic_scan, macaddr);
1685 		if (in == NULL)
1686 			break;
1687 
1688 		(void) ieee80211_sta_join(ic, in);
1689 		ieee80211_node_authorize(in);
1690 
1691 		/*
1692 		 * We can send data now; update the fastpath with our
1693 		 * current associated BSSID.
1694 		 */
1695 		if (ic->ic_flags & IEEE80211_F_PRIVACY)
1696 			wd.wd_secalloc = WIFI_SEC_WEP;
1697 		else
1698 			wd.wd_secalloc = WIFI_SEC_NONE;
1699 		wd.wd_opmode = ic->ic_opmode;
1700 		IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
1701 		(void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd));
1702 
1703 		break;
1704 
1705 	case IEEE80211_S_INIT:
1706 	case IEEE80211_S_SCAN:
1707 	case IEEE80211_S_AUTH:
1708 	case IEEE80211_S_ASSOC:
1709 		break;
1710 	}
1711 
1712 	/*
1713 	 * notify to update the link
1714 	 */
1715 	if ((ic->ic_state != IEEE80211_S_RUN) && (state == IEEE80211_S_RUN)) {
1716 		/*
1717 		 * previously disconnected and now connected
1718 		 */
1719 		sc->sc_linkstate = LINK_STATE_UP;
1720 		sc->sc_flags |= IPW2100_FLAG_LINK_CHANGE;
1721 	} else if ((ic->ic_state == IEEE80211_S_RUN) &&
1722 	    (state != IEEE80211_S_RUN)) {
1723 		/*
1724 		 * previously connected andd now disconnected
1725 		 */
1726 		sc->sc_linkstate = LINK_STATE_DOWN;
1727 		sc->sc_flags |= IPW2100_FLAG_LINK_CHANGE;
1728 	}
1729 
1730 	ic->ic_state = state;
1731 	return (DDI_SUCCESS);
1732 }
1733 
1734 /*
1735  * GLD operations
1736  */
1737 /* ARGSUSED */
1738 static int
1739 ipw2100_m_stat(void *arg, uint_t stat, uint64_t *val)
1740 {
1741 	ieee80211com_t	*ic = (ieee80211com_t *)arg;
1742 	IPW2100_DBG(IPW2100_DBG_GLD, (((struct ipw2100_softc *)arg)->sc_dip,
1743 	    CE_CONT,
1744 	    "ipw2100_m_stat(): enter\n"));
1745 	/*
1746 	 * some of below statistic data are from hardware, some from net80211
1747 	 */
1748 	switch (stat) {
1749 	case MAC_STAT_RBYTES:
1750 		*val = ic->ic_stats.is_rx_bytes;
1751 		break;
1752 	case MAC_STAT_IPACKETS:
1753 		*val = ic->ic_stats.is_rx_frags;
1754 		break;
1755 	case MAC_STAT_OBYTES:
1756 		*val = ic->ic_stats.is_tx_bytes;
1757 		break;
1758 	case MAC_STAT_OPACKETS:
1759 		*val = ic->ic_stats.is_tx_frags;
1760 		break;
1761 	/*
1762 	 * Get below from hardware statistic, retrieve net80211 value once 1s
1763 	 */
1764 	case WIFI_STAT_TX_FRAGS:
1765 	case WIFI_STAT_MCAST_TX:
1766 	case WIFI_STAT_TX_FAILED:
1767 	case WIFI_STAT_TX_RETRANS:
1768 	case WIFI_STAT_RTS_SUCCESS:
1769 	case WIFI_STAT_ACK_FAILURE:
1770 	case WIFI_STAT_RX_FRAGS:
1771 	case WIFI_STAT_MCAST_RX:
1772 	/*
1773 	 * Get blow information from net80211
1774 	 */
1775 	case WIFI_STAT_RTS_FAILURE:
1776 	case WIFI_STAT_RX_DUPS:
1777 	case WIFI_STAT_FCS_ERRORS:
1778 	case WIFI_STAT_WEP_ERRORS:
1779 		return (ieee80211_stat(ic, stat, val));
1780 	/*
1781 	 * need be supported in the future
1782 	 */
1783 	case MAC_STAT_IFSPEED:
1784 	case MAC_STAT_NOXMTBUF:
1785 	case MAC_STAT_IERRORS:
1786 	case MAC_STAT_OERRORS:
1787 	default:
1788 		return (ENOTSUP);
1789 	}
1790 	return (0);
1791 }
1792 
1793 /* ARGSUSED */
1794 static int
1795 ipw2100_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
1796 {
1797 	/* not supported */
1798 	IPW2100_DBG(IPW2100_DBG_GLD, (((struct ipw2100_softc *)arg)->sc_dip,
1799 	    CE_CONT,
1800 	    "ipw2100_m_multicst(): enter\n"));
1801 
1802 	return (0);
1803 }
1804 
1805 /*
1806  * This thread function is used to handle the fatal error.
1807  */
1808 static void
1809 ipw2100_thread(struct ipw2100_softc *sc)
1810 {
1811 	struct ieee80211com	*ic = &sc->sc_ic;
1812 	int32_t			nlstate;
1813 	int			stat_cnt = 0;
1814 
1815 	IPW2100_DBG(IPW2100_DBG_SOFTINT, (sc->sc_dip, CE_CONT,
1816 	    "ipw2100_thread(): into ipw2100 thread--> %d\n",
1817 	    sc->sc_linkstate));
1818 
1819 	mutex_enter(&sc->sc_mflock);
1820 
1821 	while (sc->sc_mfthread_switch) {
1822 		/*
1823 		 * notify the link state
1824 		 */
1825 		if (ic->ic_mach && (sc->sc_flags & IPW2100_FLAG_LINK_CHANGE)) {
1826 			IPW2100_DBG(IPW2100_DBG_SOFTINT, (sc->sc_dip, CE_CONT,
1827 			    "ipw2100_thread(): link status --> %d\n",
1828 			    sc->sc_linkstate));
1829 
1830 			sc->sc_flags &= ~IPW2100_FLAG_LINK_CHANGE;
1831 			nlstate = sc->sc_linkstate;
1832 
1833 			mutex_exit(&sc->sc_mflock);
1834 			mac_link_update(ic->ic_mach, nlstate);
1835 			mutex_enter(&sc->sc_mflock);
1836 		}
1837 
1838 		/*
1839 		 * recovery interrupt fatal error
1840 		 */
1841 		if (ic->ic_mach &&
1842 		    (sc->sc_flags & IPW2100_FLAG_HW_ERR_RECOVER)) {
1843 
1844 			IPW2100_DBG(IPW2100_DBG_FATAL, (sc->sc_dip, CE_CONT,
1845 			    "try to recover fatal hw error\n"));
1846 			sc->sc_flags &= ~IPW2100_FLAG_HW_ERR_RECOVER;
1847 
1848 			mutex_exit(&sc->sc_mflock);
1849 			(void) ipw2100_init(sc); /* Force stat machine */
1850 			delay(drv_usectohz(delay_fatal_recover));
1851 			mutex_enter(&sc->sc_mflock);
1852 		}
1853 
1854 		/*
1855 		 * get statistic, the value will be retrieved by m_stat
1856 		 */
1857 		if (stat_cnt == 10) {
1858 			stat_cnt = 0; /* re-start */
1859 
1860 			mutex_exit(&sc->sc_mflock);
1861 			ipw2100_get_statistics(sc);
1862 			mutex_enter(&sc->sc_mflock);
1863 		} else
1864 			stat_cnt++; /* until 1s */
1865 
1866 		mutex_exit(&sc->sc_mflock);
1867 		delay(drv_usectohz(delay_aux_thread));
1868 		mutex_enter(&sc->sc_mflock);
1869 	}
1870 	sc->sc_mf_thread = NULL;
1871 	cv_broadcast(&sc->sc_mfthread_cv);
1872 	mutex_exit(&sc->sc_mflock);
1873 }
1874 
1875 static int
1876 ipw2100_m_start(void *arg)
1877 {
1878 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)arg;
1879 
1880 	IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT,
1881 	    "ipw2100_m_start(): enter\n"));
1882 
1883 	/*
1884 	 * initialize ipw2100 hardware
1885 	 */
1886 	(void) ipw2100_init(sc);
1887 
1888 	sc->sc_flags |= IPW2100_FLAG_RUNNING;
1889 	/*
1890 	 * fix KCF bug. - workaround, need to fix it in net80211
1891 	 */
1892 	(void) crypto_mech2id(SUN_CKM_RC4);
1893 
1894 	return (0);
1895 }
1896 
1897 static void
1898 ipw2100_m_stop(void *arg)
1899 {
1900 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)arg;
1901 
1902 	IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT,
1903 	    "ipw2100_m_stop(): enter\n"));
1904 
1905 	ipw2100_stop(sc);
1906 
1907 	sc->sc_flags &= ~IPW2100_FLAG_RUNNING;
1908 }
1909 
1910 static int
1911 ipw2100_m_unicst(void *arg, const uint8_t *macaddr)
1912 {
1913 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)arg;
1914 	struct ieee80211com	*ic = &sc->sc_ic;
1915 	int			err;
1916 
1917 	IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT,
1918 	    "ipw2100_m_unicst(): enter\n"));
1919 
1920 	IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT,
1921 	    "ipw2100_m_unicst(): GLD setting MAC address to "
1922 	    "%02x:%02x:%02x:%02x:%02x:%02x\n",
1923 	    macaddr[0], macaddr[1], macaddr[2],
1924 	    macaddr[3], macaddr[4], macaddr[5]));
1925 
1926 	if (!IEEE80211_ADDR_EQ(ic->ic_macaddr, macaddr)) {
1927 		IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr);
1928 
1929 		if (sc->sc_flags & IPW2100_FLAG_RUNNING) {
1930 			err = ipw2100_config(sc);
1931 			if (err != DDI_SUCCESS) {
1932 				IPW2100_WARN((sc->sc_dip, CE_WARN,
1933 				    "ipw2100_m_unicst(): "
1934 				    "device configuration failed\n"));
1935 				goto fail;
1936 			}
1937 		}
1938 	}
1939 
1940 	return (0);
1941 fail:
1942 	return (EIO);
1943 }
1944 
1945 static int
1946 ipw2100_m_promisc(void *arg, boolean_t on)
1947 {
1948 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)arg;
1949 	int recfg, err;
1950 
1951 	IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT,
1952 	    "ipw2100_m_promisc(): enter. "
1953 	    "GLD setting promiscuous mode - %d\n", on));
1954 
1955 	recfg = 0;
1956 	if (on)
1957 		if (!(sc->if_flags & IFF_PROMISC)) {
1958 			sc->if_flags |= IFF_PROMISC;
1959 			recfg = 1;
1960 		}
1961 	else
1962 		if (sc->if_flags & IFF_PROMISC) {
1963 			sc->if_flags &= ~IFF_PROMISC;
1964 			recfg = 1;
1965 		}
1966 
1967 	if (recfg && (sc->sc_flags & IPW2100_FLAG_RUNNING)) {
1968 		err = ipw2100_config(sc);
1969 		if (err != DDI_SUCCESS) {
1970 			IPW2100_WARN((sc->sc_dip, CE_WARN,
1971 			    "ipw2100_m_promisc(): "
1972 			    "device configuration failed\n"));
1973 			goto fail;
1974 		}
1975 	}
1976 
1977 	return (0);
1978 fail:
1979 	return (EIO);
1980 }
1981 
1982 static mblk_t *
1983 ipw2100_m_tx(void *arg, mblk_t *mp)
1984 {
1985 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)arg;
1986 	struct ieee80211com	*ic = &sc->sc_ic;
1987 	mblk_t			*next;
1988 
1989 	/*
1990 	 * No data frames go out unless we're associated; this
1991 	 * should not happen as the 802.11 layer does not enable
1992 	 * the xmit queue until we enter the RUN state.
1993 	 */
1994 	if (ic->ic_state != IEEE80211_S_RUN) {
1995 		IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT,
1996 		    "ipw2100_m_tx(): discard msg, ic_state = %u\n",
1997 		    ic->ic_state));
1998 		freemsgchain(mp);
1999 		return (NULL);
2000 	}
2001 
2002 	while (mp != NULL) {
2003 		next = mp->b_next;
2004 		mp->b_next = NULL;
2005 		if (ipw2100_send(ic, mp, IEEE80211_FC0_TYPE_DATA) !=
2006 		    DDI_SUCCESS) {
2007 			mp->b_next = next;
2008 			break;
2009 		}
2010 		mp = next;
2011 	}
2012 	return (mp);
2013 }
2014 
2015 /* ARGSUSED */
2016 static int
2017 ipw2100_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
2018 {
2019 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)ic;
2020 	struct ieee80211_node	*in;
2021 	struct ieee80211_frame	wh, *wh_tmp;
2022 	struct ieee80211_key	*k;
2023 	uint8_t			*hdat;
2024 	mblk_t			*m0, *m;
2025 	size_t			cnt, off;
2026 	struct ipw2100_bd	*txbd[2];
2027 	struct ipw2100_txb	*txbuf;
2028 	struct dma_region	*dr;
2029 	struct ipw2100_hdr	*h;
2030 	uint32_t		idx, bidx;
2031 	int			err;
2032 
2033 	ASSERT(mp->b_next == NULL);
2034 
2035 	m0 = NULL;
2036 	m = NULL;
2037 	err = DDI_SUCCESS;
2038 
2039 	IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT,
2040 	    "ipw2100_send(): enter\n"));
2041 
2042 	if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA) {
2043 		/*
2044 		 * it is impossible to send non-data 802.11 frame in current
2045 		 * ipw driver. Therefore, drop the package
2046 		 */
2047 		freemsg(mp);
2048 		err = DDI_SUCCESS;
2049 		goto fail0;
2050 	}
2051 
2052 	mutex_enter(&sc->sc_tx_lock);
2053 
2054 	/*
2055 	 * need 2 descriptors: 1 for SEND cmd parameter header,
2056 	 * and the other for payload, i.e., 802.11 frame including 802.11
2057 	 * frame header
2058 	 */
2059 	if (sc->sc_tx_free < 2) {
2060 		mutex_enter(&sc->sc_resched_lock);
2061 		IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_WARN,
2062 		    "ipw2100_send(): no enough descriptors(%d)\n",
2063 		    sc->sc_tx_free));
2064 		ic->ic_stats.is_tx_nobuf++; /* no enough buffer */
2065 		sc->sc_flags |= IPW2100_FLAG_TX_SCHED;
2066 		err = DDI_FAILURE;
2067 		mutex_exit(&sc->sc_resched_lock);
2068 		goto fail1;
2069 	}
2070 	IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT,
2071 	    "ipw2100_send(): tx-free=%d,tx-curr=%d\n",
2072 	    sc->sc_tx_free, sc->sc_tx_cur));
2073 
2074 	wh_tmp = (struct ieee80211_frame *)mp->b_rptr;
2075 	in = ieee80211_find_txnode(ic, wh_tmp->i_addr1);
2076 	if (in == NULL) { /* can not find tx node, drop the package */
2077 		freemsg(mp);
2078 		err = DDI_SUCCESS;
2079 		goto fail1;
2080 	}
2081 	in->in_inact = 0;
2082 	(void) ieee80211_encap(ic, mp, in);
2083 	ieee80211_free_node(in);
2084 
2085 	if (wh_tmp->i_fc[1] & IEEE80211_FC1_WEP) {
2086 		/*
2087 		 * it is very bad that ieee80211_crypto_encap can only accept a
2088 		 * single continuous buffer.
2089 		 */
2090 		/*
2091 		 * allocate 32 more bytes is to be compatible with further
2092 		 * ieee802.11i standard.
2093 		 */
2094 		m = allocb(msgdsize(mp) + 32, BPRI_MED);
2095 		if (m == NULL) { /* can not alloc buf, drop this package */
2096 			IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
2097 			    "ipw2100_send(): msg allocation failed\n"));
2098 
2099 			freemsg(mp);
2100 
2101 			err = DDI_SUCCESS;
2102 			goto fail1;
2103 		}
2104 		off = 0;
2105 		m0 = mp;
2106 		while (m0) {
2107 			cnt = MBLKL(m0);
2108 			if (cnt) {
2109 				(void) memcpy(m->b_rptr + off, m0->b_rptr, cnt);
2110 				off += cnt;
2111 			}
2112 			m0 = m0->b_cont;
2113 		}
2114 		m->b_wptr += off;
2115 		IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
2116 		    "ipw2100_send(): "
2117 		    "Encrypting 802.11 frame started, %d, %d\n",
2118 		    msgdsize(mp), MBLKL(mp)));
2119 		k = ieee80211_crypto_encap(ic, m);
2120 		if (k == NULL) { /* can not get the key, drop packages */
2121 			IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
2122 			    "ipw2100_send(): "
2123 			    "Encrypting 802.11 frame failed\n"));
2124 
2125 			freemsg(mp);
2126 			err = DDI_SUCCESS;
2127 			goto fail2;
2128 		}
2129 		IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
2130 		    "ipw2100_send(): "
2131 		    "Encrypting 802.11 frame finished, %d, %d, k=0x%08x\n",
2132 		    msgdsize(mp), MBLKL(mp), k->wk_flags));
2133 	}
2134 
2135 	/*
2136 	 * header descriptor
2137 	 */
2138 	idx = sc->sc_tx_cur;
2139 	txbd[0]  = &sc->sc_txbd[idx];
2140 	if ((idx & 1) == 0)
2141 		bidx = idx / 2;
2142 	sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2100_NUM_TXBD);
2143 	sc->sc_tx_free--;
2144 
2145 	/*
2146 	 * payload descriptor
2147 	 */
2148 	idx = sc->sc_tx_cur;
2149 	txbd[1]  = &sc->sc_txbd[idx];
2150 	if ((idx & 1) == 0)
2151 		bidx = idx / 2;
2152 	sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2100_NUM_TXBD);
2153 	sc->sc_tx_free--;
2154 
2155 	/*
2156 	 * one buffer, SEND cmd header and payload buffer
2157 	 */
2158 	txbuf = sc->sc_txbufs[bidx];
2159 	dr = &sc->sc_dma_txbufs[bidx];
2160 
2161 	/*
2162 	 * extract 802.11 header from message, fill wh from m0
2163 	 */
2164 	hdat = (uint8_t *)&wh;
2165 	off = 0;
2166 	if (m)
2167 		m0 = m;
2168 	else
2169 		m0 = mp;
2170 	while (off < sizeof (wh)) {
2171 		cnt = MBLKL(m0);
2172 		if (cnt > (sizeof (wh) - off))
2173 			cnt = sizeof (wh) - off;
2174 		if (cnt) {
2175 			(void) memcpy(hdat + off, m0->b_rptr, cnt);
2176 			off += cnt;
2177 			m0->b_rptr += cnt;
2178 		}
2179 		else
2180 			m0 = m0->b_cont;
2181 	}
2182 
2183 	/*
2184 	 * prepare SEND cmd header
2185 	 */
2186 	h		= &txbuf->txb_hdr;
2187 	h->type		= LE_32(IPW2100_CMD_SEND);
2188 	h->subtype	= LE_32(0);
2189 	h->encrypted    = ic->ic_flags & IEEE80211_F_PRIVACY ? 1 : 0;
2190 	h->encrypt	= 0;
2191 	h->keyidx	= 0;
2192 	h->keysz	= 0;
2193 	h->fragsz	= LE_16(0);
2194 	IEEE80211_ADDR_COPY(h->saddr, wh.i_addr2);
2195 	if (ic->ic_opmode == IEEE80211_M_STA)
2196 		IEEE80211_ADDR_COPY(h->daddr, wh.i_addr3);
2197 	else
2198 		IEEE80211_ADDR_COPY(h->daddr, wh.i_addr1);
2199 
2200 	/*
2201 	 * extract payload from message into tx data buffer
2202 	 */
2203 	off = 0;
2204 	while (m0) {
2205 		cnt = MBLKL(m0);
2206 		if (cnt) {
2207 			(void) memcpy(&txbuf->txb_dat[off], m0->b_rptr, cnt);
2208 			off += cnt;
2209 		}
2210 		m0 = m0->b_cont;
2211 	}
2212 
2213 	/*
2214 	 * fill SEND cmd header descriptor
2215 	 */
2216 	txbd[0]->phyaddr = LE_32(dr->dr_pbase +
2217 	    OFFSETOF(struct ipw2100_txb, txb_hdr));
2218 	txbd[0]->len	= LE_32(sizeof (struct ipw2100_hdr));
2219 	txbd[0]->flags	= IPW2100_BD_FLAG_TX_FRAME_802_3 |
2220 	    IPW2100_BD_FLAG_TX_NOT_LAST_FRAGMENT;
2221 	txbd[0]->nfrag	= 2;
2222 	/*
2223 	 * fill payload descriptor
2224 	 */
2225 	txbd[1]->phyaddr = LE_32(dr->dr_pbase +
2226 	    OFFSETOF(struct ipw2100_txb, txb_dat[0]));
2227 	txbd[1]->len	= LE_32(off);
2228 	txbd[1]->flags	= IPW2100_BD_FLAG_TX_FRAME_802_3 |
2229 	    IPW2100_BD_FLAG_TX_LAST_FRAGMENT;
2230 	txbd[1]->nfrag	= 0;
2231 
2232 	/*
2233 	 * dma sync
2234 	 */
2235 	(void) ddi_dma_sync(dr->dr_hnd, 0, sizeof (struct ipw2100_txb),
2236 	    DDI_DMA_SYNC_FORDEV);
2237 	(void) ddi_dma_sync(sc->sc_dma_txbd.dr_hnd,
2238 	    (txbd[0] - sc->sc_txbd) * sizeof (struct ipw2100_bd),
2239 	    sizeof (struct ipw2100_bd), DDI_DMA_SYNC_FORDEV);
2240 	/*
2241 	 * since txbd[1] may not be successive to txbd[0] due to the ring
2242 	 * organization, another dma_sync is needed to simplify the logic
2243 	 */
2244 	(void) ddi_dma_sync(sc->sc_dma_txbd.dr_hnd,
2245 	    (txbd[1] - sc->sc_txbd) * sizeof (struct ipw2100_bd),
2246 	    sizeof (struct ipw2100_bd), DDI_DMA_SYNC_FORDEV);
2247 	/*
2248 	 * update txcur
2249 	 */
2250 	ipw2100_csr_put32(sc, IPW2100_CSR_TX_WRITE_INDEX, sc->sc_tx_cur);
2251 
2252 	if (mp) /* success, free the original message */
2253 		freemsg(mp);
2254 fail2:
2255 	if (m)
2256 		freemsg(m);
2257 fail1:
2258 	mutex_exit(&sc->sc_tx_lock);
2259 fail0:
2260 	IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT,
2261 	    "ipw2100_send(): exit - err=%d\n", err));
2262 
2263 	return (err);
2264 }
2265 
2266 /*
2267  * IOCTL Handler
2268  */
2269 #define	IEEE80211_IOCTL_REQUIRED	(1)
2270 #define	IEEE80211_IOCTL_NOT_REQUIRED	(0)
2271 static void
2272 ipw2100_m_ioctl(void *arg, queue_t *q, mblk_t *m)
2273 {
2274 	struct ipw2100_softc	*sc  = (struct ipw2100_softc *)arg;
2275 	struct ieee80211com	*ic = &sc->sc_ic;
2276 	int			err;
2277 
2278 	IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT,
2279 	    "ipw2100_m_ioctl(): enter\n"));
2280 
2281 	/*
2282 	 * check whether or not need to handle this in net80211
2283 	 */
2284 	if (ipw2100_ioctl(sc, q, m) == IEEE80211_IOCTL_NOT_REQUIRED)
2285 		return; /* succes or fail */
2286 
2287 	err = ieee80211_ioctl(ic, q, m);
2288 	if (err == ENETRESET) {
2289 		if (sc->sc_flags & IPW2100_FLAG_RUNNING) {
2290 			(void) ipw2100_m_start(sc);
2291 			(void) ieee80211_new_state(ic,
2292 			    IEEE80211_S_SCAN, -1);
2293 		}
2294 	}
2295 	if (err == ERESTART) {
2296 		if (sc->sc_flags & IPW2100_FLAG_RUNNING)
2297 			(void) ipw2100_chip_reset(sc);
2298 	}
2299 }
2300 
2301 static int
2302 ipw2100_ioctl(struct ipw2100_softc *sc, queue_t *q, mblk_t *m)
2303 {
2304 	struct iocblk	*iocp;
2305 	uint32_t	len, ret, cmd;
2306 	mblk_t		*m0;
2307 	boolean_t	need_privilege;
2308 	boolean_t	need_net80211;
2309 
2310 	if (MBLKL(m) < sizeof (struct iocblk)) {
2311 		IPW2100_DBG(IPW2100_DBG_IOCTL, (sc->sc_dip, CE_CONT,
2312 		    "ipw2100_ioctl(): ioctl buffer too short, %u\n",
2313 		    MBLKL(m)));
2314 		miocnak(q, m, 0, EINVAL);
2315 		return (IEEE80211_IOCTL_NOT_REQUIRED);
2316 	}
2317 
2318 	/*
2319 	 * Validate the command
2320 	 */
2321 	iocp = (struct iocblk *)(uintptr_t)m->b_rptr;
2322 	iocp->ioc_error = 0;
2323 	cmd = iocp->ioc_cmd;
2324 	need_privilege = B_TRUE;
2325 	switch (cmd) {
2326 	case WLAN_SET_PARAM:
2327 	case WLAN_COMMAND:
2328 		break;
2329 	case WLAN_GET_PARAM:
2330 		need_privilege = B_FALSE;
2331 		break;
2332 	default:
2333 		IPW2100_DBG(IPW2100_DBG_IOCTL, (sc->sc_dip, CE_CONT,
2334 		    "ieee80211_ioctl(): unknown cmd 0x%x", cmd));
2335 		miocnak(q, m, 0, EINVAL);
2336 		return (IEEE80211_IOCTL_NOT_REQUIRED);
2337 	}
2338 
2339 	if (need_privilege && (ret = secpolicy_dl_config(iocp->ioc_cr)) != 0) {
2340 		miocnak(q, m, 0, ret);
2341 		return (IEEE80211_IOCTL_NOT_REQUIRED);
2342 	}
2343 
2344 	/*
2345 	 * sanity check
2346 	 */
2347 	m0 = m->b_cont;
2348 	if (iocp->ioc_count == 0 || iocp->ioc_count < sizeof (wldp_t) ||
2349 	    m0 == NULL) {
2350 		miocnak(q, m, 0, EINVAL);
2351 		return (IEEE80211_IOCTL_NOT_REQUIRED);
2352 	}
2353 	/*
2354 	 * assuming single data block
2355 	 */
2356 	if (m0->b_cont) {
2357 		freemsg(m0->b_cont);
2358 		m0->b_cont = NULL;
2359 	}
2360 
2361 	need_net80211 = B_FALSE;
2362 	ret = ipw2100_getset(sc, m0, cmd, &need_net80211);
2363 	if (!need_net80211) {
2364 		len = msgdsize(m0);
2365 
2366 		IPW2100_DBG(IPW2100_DBG_IOCTL, (sc->sc_dip, CE_CONT,
2367 		    "ipw2100_ioctl(): go to call miocack with "
2368 		    "ret = %d, len = %d\n", ret, len));
2369 		miocack(q, m, len, ret);
2370 		return (IEEE80211_IOCTL_NOT_REQUIRED);
2371 	}
2372 
2373 	/*
2374 	 * IEEE80211_IOCTL_REQUIRED - need net80211 handle
2375 	 */
2376 	return (IEEE80211_IOCTL_REQUIRED);
2377 }
2378 
2379 static int
2380 ipw2100_getset(struct ipw2100_softc *sc, mblk_t *m, uint32_t cmd,
2381 	boolean_t *need_net80211)
2382 {
2383 	wldp_t		*infp, *outfp;
2384 	uint32_t	id;
2385 	int		ret; /* IEEE80211_IOCTL - handled by net80211 */
2386 
2387 	infp  = (wldp_t *)(uintptr_t)m->b_rptr;
2388 	outfp = (wldp_t *)(uintptr_t)m->b_rptr;
2389 	outfp->wldp_result = WL_NOTSUPPORTED;
2390 
2391 	id = infp->wldp_id;
2392 	IPW2100_DBG(IPW2100_DBG_IOCTL, (sc->sc_dip, CE_CONT,
2393 	    "ipw2100_getset(): id = 0x%x\n", id));
2394 	switch (id) {
2395 	/*
2396 	 * which is not supported by net80211, so it
2397 	 * has to be handled from driver side
2398 	 */
2399 	case WL_RADIO:
2400 		ret = ipw_wificfg_radio(sc, cmd, outfp);
2401 		break;
2402 	/*
2403 	 * so far, drier doesn't support fix-rates
2404 	 */
2405 	case WL_DESIRED_RATES:
2406 		ret = ipw_wificfg_desrates(outfp);
2407 		break;
2408 	/*
2409 	 * current net80211 implementation clears the bssid while
2410 	 * this command received, which will result in the all zero
2411 	 * mac address for scan'ed AP which is just disconnected.
2412 	 * This is a workaround solution until net80211 find a
2413 	 * better method.
2414 	 */
2415 	case WL_DISASSOCIATE:
2416 		ret = ipw_wificfg_disassoc(sc, outfp);
2417 		break;
2418 	default:
2419 		/*
2420 		 * The wifi IOCTL net80211 supported:
2421 		 *	case WL_ESSID:
2422 		 *	case WL_BSSID:
2423 		 *	case WL_WEP_KEY_TAB:
2424 		 *	case WL_WEP_KEY_ID:
2425 		 *	case WL_AUTH_MODE:
2426 		 *	case WL_ENCRYPTION:
2427 		 *	case WL_BSS_TYPE:
2428 		 *	case WL_ESS_LIST:
2429 		 *	case WL_LINKSTATUS:
2430 		 *	case WL_RSSI:
2431 		 *	case WL_SCAN:
2432 		 *	case WL_LOAD_DEFAULTS:
2433 		 */
2434 
2435 		/*
2436 		 * When radio is off, need to ignore all ioctl.  What need to
2437 		 * do is to check radio status firstly.  If radio is ON, pass
2438 		 * it to net80211, otherwise, return to upper layer directly.
2439 		 *
2440 		 * Considering the WL_SUCCESS also means WL_CONNECTED for
2441 		 * checking linkstatus, one exception for WL_LINKSTATUS is to
2442 		 * let net80211 handle it.
2443 		 */
2444 		if ((ipw2100_get_radio(sc) == 0) &&
2445 		    (id != WL_LINKSTATUS)) {
2446 
2447 			IPW2100_REPORT((sc->sc_dip, CE_WARN,
2448 			    "ipw: RADIO is OFF\n"));
2449 
2450 			outfp->wldp_length = WIFI_BUF_OFFSET;
2451 			outfp->wldp_result = WL_SUCCESS;
2452 			ret = 0;
2453 			break;
2454 		}
2455 
2456 		*need_net80211 = B_TRUE; /* let net80211 do the rest */
2457 		return (0);
2458 	}
2459 	/*
2460 	 * we will overwrite everything
2461 	 */
2462 	m->b_wptr = m->b_rptr + outfp->wldp_length;
2463 
2464 	return (ret);
2465 }
2466 
2467 /*
2468  * Call back functions for get/set proporty
2469  */
2470 static int
2471 ipw2100_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2472     uint_t pr_flags, uint_t wldp_length, void *wldp_buf, uint_t *perm)
2473 {
2474 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)arg;
2475 	struct ieee80211com	*ic = &sc->sc_ic;
2476 	int 			err = 0;
2477 
2478 	switch (wldp_pr_num) {
2479 	/* mac_prop_id */
2480 	case MAC_PROP_WL_DESIRED_RATES:
2481 		IPW2100_DBG(IPW2100_DBG_BRUSSELS, (sc->sc_dip, CE_CONT,
2482 		    "ipw2100_m_getprop(): Not Support DESIRED_RATES\n"));
2483 		err = ENOTSUP;
2484 		break;
2485 	case MAC_PROP_WL_RADIO:
2486 		*(wl_linkstatus_t *)wldp_buf = ipw2100_get_radio(sc);
2487 		break;
2488 	default:
2489 		/* go through net80211 */
2490 		err = ieee80211_getprop(ic, pr_name, wldp_pr_num, pr_flags,
2491 		    wldp_length, wldp_buf, perm);
2492 		break;
2493 	}
2494 
2495 	return (err);
2496 }
2497 
2498 static int
2499 ipw2100_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2500     uint_t wldp_length, const void *wldp_buf)
2501 {
2502 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)arg;
2503 	struct ieee80211com	*ic = &sc->sc_ic;
2504 	int			err;
2505 
2506 	switch (wldp_pr_num) {
2507 	/* mac_prop_id */
2508 	case MAC_PROP_WL_DESIRED_RATES:
2509 		IPW2100_DBG(IPW2100_DBG_BRUSSELS, (sc->sc_dip, CE_CONT,
2510 		    "ipw2100_m_setprop(): Not Support DESIRED_RATES\n"));
2511 		err = ENOTSUP;
2512 		break;
2513 	case MAC_PROP_WL_RADIO:
2514 		IPW2100_DBG(IPW2100_DBG_BRUSSELS, (sc->sc_dip, CE_CONT,
2515 		    "ipw2100_m_setprop(): Not Support RADIO\n"));
2516 		err = ENOTSUP;
2517 		break;
2518 	default:
2519 		/* go through net80211 */
2520 		err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length,
2521 		    wldp_buf);
2522 		break;
2523 	}
2524 
2525 	if (err == ENETRESET) {
2526 		if (sc->sc_flags & IPW2100_FLAG_RUNNING) {
2527 			(void) ipw2100_m_start(sc);
2528 			(void) ieee80211_new_state(ic,
2529 			    IEEE80211_S_SCAN, -1);
2530 		}
2531 
2532 		err = 0;
2533 	}
2534 
2535 	return (err);
2536 }
2537 
2538 static int
2539 ipw_wificfg_radio(struct ipw2100_softc *sc, uint32_t cmd, wldp_t *outfp)
2540 {
2541 	uint32_t	ret = ENOTSUP;
2542 
2543 	switch (cmd) {
2544 	case WLAN_GET_PARAM:
2545 		*(wl_linkstatus_t *)(outfp->wldp_buf) = ipw2100_get_radio(sc);
2546 		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_linkstatus_t);
2547 		outfp->wldp_result = WL_SUCCESS;
2548 		ret = 0; /* command sucess */
2549 		break;
2550 	case WLAN_SET_PARAM:
2551 	default:
2552 		break;
2553 	}
2554 	return (ret);
2555 }
2556 
2557 static int
2558 ipw_wificfg_desrates(wldp_t *outfp)
2559 {
2560 	/*
2561 	 * return success, but with result NOTSUPPORTED
2562 	 */
2563 	outfp->wldp_length = WIFI_BUF_OFFSET;
2564 	outfp->wldp_result = WL_NOTSUPPORTED;
2565 	return (0);
2566 }
2567 
2568 static int
2569 ipw_wificfg_disassoc(struct ipw2100_softc *sc, wldp_t *outfp)
2570 {
2571 	struct ieee80211com	*ic = &sc->sc_ic;
2572 
2573 	/*
2574 	 * init the state
2575 	 */
2576 	if (ic->ic_state != IEEE80211_S_INIT) {
2577 		(void) ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
2578 	}
2579 
2580 	/*
2581 	 * return success always
2582 	 */
2583 	outfp->wldp_length = WIFI_BUF_OFFSET;
2584 	outfp->wldp_result = WL_SUCCESS;
2585 	return (0);
2586 }
2587 /* End of IOCTL Handler */
2588 
2589 static void
2590 ipw2100_fix_channel(struct ieee80211com *ic, mblk_t *m)
2591 {
2592 	struct ieee80211_frame	*wh;
2593 	uint8_t			subtype;
2594 	uint8_t			*frm, *efrm;
2595 
2596 	wh = (struct ieee80211_frame *)m->b_rptr;
2597 
2598 	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT)
2599 		return;
2600 
2601 	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
2602 
2603 	if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
2604 	    subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP)
2605 		return;
2606 
2607 	/*
2608 	 * assume the message contains only 1 block
2609 	 */
2610 	frm   = (uint8_t *)(wh + 1);
2611 	efrm  = (uint8_t *)m->b_wptr;
2612 	frm  += 12;  /* skip tstamp, bintval and capinfo fields */
2613 	while (frm < efrm) {
2614 		if (*frm == IEEE80211_ELEMID_DSPARMS) {
2615 #if IEEE80211_CHAN_MAX < 255
2616 			if (frm[2] <= IEEE80211_CHAN_MAX)
2617 #endif
2618 			{
2619 				ic->ic_curchan = &ic->ic_sup_channels[frm[2]];
2620 			}
2621 		}
2622 		frm += frm[1] + 2;
2623 	}
2624 }
2625 
2626 static void
2627 ipw2100_rcvpkt(struct ipw2100_softc *sc, struct ipw2100_status *status,
2628     uint8_t *rxbuf)
2629 {
2630 	struct ieee80211com	*ic = &sc->sc_ic;
2631 	mblk_t			*m;
2632 	struct ieee80211_frame	*wh = (struct ieee80211_frame *)rxbuf;
2633 	struct ieee80211_node	*in;
2634 	uint32_t		rlen;
2635 
2636 	in = ieee80211_find_rxnode(ic, wh);
2637 	rlen = LE_32(status->len);
2638 	m = allocb(rlen, BPRI_MED);
2639 	if (m) {
2640 		(void) memcpy(m->b_wptr, rxbuf, rlen);
2641 		m->b_wptr += rlen;
2642 		if (ic->ic_state == IEEE80211_S_SCAN)
2643 			ipw2100_fix_channel(ic, m);
2644 		(void) ieee80211_input(ic, m, in, status->rssi, 0);
2645 	} else
2646 		IPW2100_WARN((sc->sc_dip, CE_WARN,
2647 		    "ipw2100_rcvpkg(): cannot allocate receive message(%u)\n",
2648 		    LE_32(status->len)));
2649 	ieee80211_free_node(in);
2650 }
2651 
2652 static uint_t
2653 ipw2100_intr(caddr_t arg)
2654 {
2655 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)(uintptr_t)arg;
2656 	uint32_t		ireg, ridx, len, i;
2657 	struct ieee80211com	*ic = &sc->sc_ic;
2658 	struct ipw2100_status	*status;
2659 	uint8_t			*rxbuf;
2660 	struct dma_region	*dr;
2661 	uint32_t		state;
2662 #if DEBUG
2663 	struct ipw2100_bd *rxbd;
2664 #endif
2665 
2666 	if (sc->sc_suspended)
2667 		return (DDI_INTR_UNCLAIMED);
2668 
2669 	ireg = ipw2100_csr_get32(sc, IPW2100_CSR_INTR);
2670 
2671 	if (!(ireg & IPW2100_INTR_MASK_ALL))
2672 		return (DDI_INTR_UNCLAIMED);
2673 
2674 	/*
2675 	 * mask all interrupts
2676 	 */
2677 	ipw2100_csr_put32(sc, IPW2100_CSR_INTR_MASK, 0);
2678 
2679 	/*
2680 	 * acknowledge all fired interrupts
2681 	 */
2682 	ipw2100_csr_put32(sc, IPW2100_CSR_INTR, ireg);
2683 
2684 	IPW2100_DBG(IPW2100_DBG_INT, (sc->sc_dip, CE_CONT,
2685 	    "ipw2100_intr(): interrupt is fired. int=0x%08x\n", ireg));
2686 
2687 	if (ireg & IPW2100_INTR_MASK_ERR) {
2688 
2689 		IPW2100_DBG(IPW2100_DBG_FATAL, (sc->sc_dip, CE_CONT,
2690 		    "ipw2100_intr(): interrupt is fired, MASK = 0x%08x\n",
2691 		    ireg));
2692 
2693 		/*
2694 		 * inform mfthread to recover hw error
2695 		 */
2696 		mutex_enter(&sc->sc_mflock);
2697 		sc->sc_flags |= IPW2100_FLAG_HW_ERR_RECOVER;
2698 		mutex_exit(&sc->sc_mflock);
2699 
2700 		goto enable_interrupt;
2701 	}
2702 
2703 	/*
2704 	 * FW intr
2705 	 */
2706 	if (ireg & IPW2100_INTR_FW_INIT_DONE) {
2707 		mutex_enter(&sc->sc_ilock);
2708 		sc->sc_flags |= IPW2100_FLAG_FW_INITED;
2709 		cv_signal(&sc->sc_fw_cond);
2710 		mutex_exit(&sc->sc_ilock);
2711 	}
2712 
2713 	/*
2714 	 * RX intr
2715 	 */
2716 	if (ireg & IPW2100_INTR_RX_TRANSFER) {
2717 		ridx = ipw2100_csr_get32(sc,
2718 		    IPW2100_CSR_RX_READ_INDEX);
2719 
2720 		for (; sc->sc_rx_cur != ridx;
2721 		    sc->sc_rx_cur = RING_FORWARD(
2722 		    sc->sc_rx_cur, 1, IPW2100_NUM_RXBD)) {
2723 
2724 			i	= sc->sc_rx_cur;
2725 			status	= &sc->sc_status[i];
2726 			rxbuf	= &sc->sc_rxbufs[i]->rxb_dat[0];
2727 			dr	= &sc->sc_dma_rxbufs[i];
2728 
2729 			/*
2730 			 * sync
2731 			 */
2732 			(void) ddi_dma_sync(sc->sc_dma_status.dr_hnd,
2733 			    i * sizeof (struct ipw2100_status),
2734 			    sizeof (struct ipw2100_status),
2735 			    DDI_DMA_SYNC_FORKERNEL);
2736 			(void) ddi_dma_sync(sc->sc_dma_rxbd.dr_hnd,
2737 			    i * sizeof (struct ipw2100_bd),
2738 			    sizeof (struct ipw2100_bd),
2739 			    DDI_DMA_SYNC_FORKERNEL);
2740 			(void) ddi_dma_sync(dr->dr_hnd, 0,
2741 			    sizeof (struct ipw2100_rxb),
2742 			    DDI_DMA_SYNC_FORKERNEL);
2743 			IPW2100_DBG(IPW2100_DBG_INT, (sc->sc_dip, CE_CONT,
2744 			    "ipw2100_intr(): status code=0x%04x, len=0x%08x, "
2745 			    "flags=0x%02x, rssi=%02x\n",
2746 			    LE_16(status->code), LE_32(status->len),
2747 			    status->flags, status->rssi));
2748 #if DEBUG
2749 			rxbd	= &sc->sc_rxbd[i];
2750 			IPW2100_DBG(IPW2100_DBG_INT, (sc->sc_dip, CE_CONT,
2751 			    "ipw2100_intr(): rxbd,phyaddr=0x%08x, len=0x%08x, "
2752 			    "flags=0x%02x,nfrag=%02x\n",
2753 			    LE_32(rxbd->phyaddr), LE_32(rxbd->len),
2754 			    rxbd->flags, rxbd->nfrag));
2755 #endif
2756 			switch (LE_16(status->code) & 0x0f) {
2757 			/*
2758 			 * command complete response
2759 			 */
2760 			case IPW2100_STATUS_CODE_COMMAND:
2761 				mutex_enter(&sc->sc_ilock);
2762 				sc->sc_done = 1;
2763 				cv_signal(&sc->sc_cmd_cond);
2764 				mutex_exit(&sc->sc_ilock);
2765 				break;
2766 			/*
2767 			 * change state
2768 			 */
2769 			case IPW2100_STATUS_CODE_NEWSTATE:
2770 				state = LE_32(* ((uint32_t *)(uintptr_t)rxbuf));
2771 				IPW2100_DBG(IPW2100_DBG_INT,
2772 				    (sc->sc_dip, CE_CONT,
2773 				    "ipw2100_intr(): newstate,state=0x%x\n",
2774 				    state));
2775 
2776 				switch (state) {
2777 				case IPW2100_STATE_ASSOCIATED:
2778 					ieee80211_new_state(ic,
2779 					    IEEE80211_S_RUN, -1);
2780 					break;
2781 				case IPW2100_STATE_ASSOCIATION_LOST:
2782 					case IPW2100_STATE_DISABLED:
2783 					ieee80211_new_state(ic,
2784 					    IEEE80211_S_INIT, -1);
2785 					break;
2786 				/*
2787 				 * When radio is OFF, need a better
2788 				 * scan approach to ensure scan
2789 				 * result correct.
2790 				 */
2791 				case IPW2100_STATE_RADIO_DISABLED:
2792 					IPW2100_REPORT((sc->sc_dip, CE_WARN,
2793 					    "ipw2100_intr(): RADIO is OFF\n"));
2794 					ipw2100_stop(sc);
2795 					break;
2796 				case IPW2100_STATE_SCAN_COMPLETE:
2797 					ieee80211_cancel_scan(ic);
2798 					break;
2799 				case IPW2100_STATE_SCANNING:
2800 					if (ic->ic_state != IEEE80211_S_RUN)
2801 						ieee80211_new_state(ic,
2802 						    IEEE80211_S_SCAN, -1);
2803 					ic->ic_flags |= IEEE80211_F_SCAN;
2804 
2805 					break;
2806 				default:
2807 					break;
2808 				}
2809 				break;
2810 			case IPW2100_STATUS_CODE_DATA_802_11:
2811 			case IPW2100_STATUS_CODE_DATA_802_3:
2812 				ipw2100_rcvpkt(sc, status, rxbuf);
2813 				break;
2814 			case IPW2100_STATUS_CODE_NOTIFICATION:
2815 				break;
2816 			default:
2817 				IPW2100_WARN((sc->sc_dip, CE_WARN,
2818 				    "ipw2100_intr(): "
2819 				    "unknown status code 0x%04x\n",
2820 				    LE_16(status->code)));
2821 				break;
2822 			}
2823 		}
2824 		/*
2825 		 * write sc_rx_cur backward 1 step to RX_WRITE_INDEX
2826 		 */
2827 		ipw2100_csr_put32(sc, IPW2100_CSR_RX_WRITE_INDEX,
2828 		    RING_BACKWARD(sc->sc_rx_cur, 1, IPW2100_NUM_RXBD));
2829 	}
2830 
2831 	/*
2832 	 * TX intr
2833 	 */
2834 	if (ireg & IPW2100_INTR_TX_TRANSFER) {
2835 		mutex_enter(&sc->sc_tx_lock);
2836 		ridx = ipw2100_csr_get32(sc, IPW2100_CSR_TX_READ_INDEX);
2837 		len = RING_FLEN(RING_FORWARD(sc->sc_tx_cur,
2838 		    sc->sc_tx_free, IPW2100_NUM_TXBD),
2839 		    ridx, IPW2100_NUM_TXBD);
2840 		sc->sc_tx_free += len;
2841 		IPW2100_DBG(IPW2100_DBG_INT, (sc->sc_dip, CE_CONT,
2842 		    "ipw2100_intr(): len=%d\n", len));
2843 		mutex_exit(&sc->sc_tx_lock);
2844 
2845 		mutex_enter(&sc->sc_resched_lock);
2846 		if (len > 1 && (sc->sc_flags & IPW2100_FLAG_TX_SCHED)) {
2847 			sc->sc_flags &= ~IPW2100_FLAG_TX_SCHED;
2848 			mac_tx_update(ic->ic_mach);
2849 		}
2850 		mutex_exit(&sc->sc_resched_lock);
2851 	}
2852 
2853 enable_interrupt:
2854 	/*
2855 	 * enable all interrupts
2856 	 */
2857 	ipw2100_csr_put32(sc, IPW2100_CSR_INTR_MASK, IPW2100_INTR_MASK_ALL);
2858 
2859 	return (DDI_INTR_CLAIMED);
2860 }
2861 
2862 
2863 /*
2864  * Module Loading Data & Entry Points
2865  */
2866 DDI_DEFINE_STREAM_OPS(ipw2100_devops, nulldev, nulldev, ipw2100_attach,
2867     ipw2100_detach, nodev, NULL, D_MP, NULL, ipw2100_quiesce);
2868 
2869 static struct modldrv ipw2100_modldrv = {
2870 	&mod_driverops,
2871 	ipw2100_ident,
2872 	&ipw2100_devops
2873 };
2874 
2875 static struct modlinkage ipw2100_modlinkage = {
2876 	MODREV_1,
2877 	&ipw2100_modldrv,
2878 	NULL
2879 };
2880 
2881 int
2882 _init(void)
2883 {
2884 	int	status;
2885 
2886 	status = ddi_soft_state_init(&ipw2100_ssp,
2887 	    sizeof (struct ipw2100_softc), 1);
2888 	if (status != DDI_SUCCESS)
2889 		return (status);
2890 
2891 	mac_init_ops(&ipw2100_devops, IPW2100_DRV_NAME);
2892 	status = mod_install(&ipw2100_modlinkage);
2893 	if (status != DDI_SUCCESS) {
2894 		mac_fini_ops(&ipw2100_devops);
2895 		ddi_soft_state_fini(&ipw2100_ssp);
2896 	}
2897 
2898 	return (status);
2899 }
2900 
2901 int
2902 _fini(void)
2903 {
2904 	int status;
2905 
2906 	status = mod_remove(&ipw2100_modlinkage);
2907 	if (status == DDI_SUCCESS) {
2908 		mac_fini_ops(&ipw2100_devops);
2909 		ddi_soft_state_fini(&ipw2100_ssp);
2910 	}
2911 
2912 	return (status);
2913 }
2914 
2915 int
2916 _info(struct modinfo *mip)
2917 {
2918 	return (mod_info(&ipw2100_modlinkage, mip));
2919 }
2920