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