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