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