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