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