xref: /titanic_51/usr/src/uts/common/io/iwi/ipw2200.c (revision 4e9cfc9a015e8ca7d41f7d018c74dc8a692305b3)
1 /*
2  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 2004, 2005
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/pci.h>
57 #include <sys/policy.h>
58 #include <sys/random.h>
59 
60 #include "ipw2200.h"
61 #include "ipw2200_impl.h"
62 #include <inet/wifi_ioctl.h>
63 
64 /*
65  * minimal size reserved in tx-ring
66  */
67 #define	IPW2200_TX_RING_MIN	(8)
68 #define	IPW2200_TXBUF_SIZE	(IEEE80211_MAX_LEN)
69 #define	IPW2200_RXBUF_SIZE	(4096)
70 
71 static void  *ipw2200_ssp = NULL;
72 static char ipw2200_ident[] = IPW2200_DRV_DESC " " IPW2200_DRV_REV;
73 
74 /*
75  * PIO access attributor for registers
76  */
77 static ddi_device_acc_attr_t ipw2200_csr_accattr = {
78 	DDI_DEVICE_ATTR_V0,
79 	DDI_STRUCTURE_LE_ACC,
80 	DDI_STRICTORDER_ACC
81 };
82 
83 /*
84  * DMA access attributor for descriptors
85  */
86 static ddi_device_acc_attr_t ipw2200_dma_accattr = {
87 	DDI_DEVICE_ATTR_V0,
88 	DDI_NEVERSWAP_ACC,
89 	DDI_STRICTORDER_ACC
90 };
91 
92 /*
93  * Describes the chip's DMA engine
94  */
95 static ddi_dma_attr_t ipw2200_dma_attr = {
96 	DMA_ATTR_V0,		/* version */
97 	0x0000000000000000ULL,  /* addr_lo */
98 	0x00000000ffffffffULL,  /* addr_hi */
99 	0x00000000ffffffffULL,  /* counter */
100 	0x0000000000000004ULL,  /* alignment */
101 	0xfff,			/* burst */
102 	1,			/* min xfer */
103 	0x00000000ffffffffULL,  /* max xfer */
104 	0x00000000ffffffffULL,  /* seg boud */
105 	1,			/* s/g list */
106 	1,			/* granularity */
107 	0			/* flags */
108 };
109 
110 static uint8_t ipw2200_broadcast_addr[] = {
111 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
112 };
113 static const struct ieee80211_rateset ipw2200_rateset_11a = { 8,
114 	{12, 18, 24, 36, 48, 72, 96, 108}
115 };
116 static const struct ieee80211_rateset ipw2200_rateset_11b = { 4,
117 	{2, 4, 11, 22}
118 };
119 static const struct ieee80211_rateset ipw2200_rateset_11g = { 12,
120 	{2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108}
121 };
122 
123 /*
124  * Used by multi function thread
125  */
126 extern pri_t minclsyspri;
127 
128 /*
129  * ipw2200 specific hardware operations
130  */
131 static void	ipw2200_hwconf_get(struct ipw2200_softc *sc);
132 static int	ipw2200_chip_reset(struct ipw2200_softc *sc);
133 static void	ipw2200_master_stop(struct ipw2200_softc *sc);
134 static void	ipw2200_stop(struct ipw2200_softc *sc);
135 static int	ipw2200_config(struct ipw2200_softc *sc);
136 static int	ipw2200_cmd(struct ipw2200_softc *sc,
137     uint32_t type, void *buf, size_t len, int async);
138 static void	ipw2200_ring_hwsetup(struct ipw2200_softc *sc);
139 static int	ipw2200_ring_alloc(struct ipw2200_softc *sc);
140 static void	ipw2200_ring_free(struct ipw2200_softc *sc);
141 static void	ipw2200_ring_reset(struct ipw2200_softc *sc);
142 static int	ipw2200_ring_init(struct ipw2200_softc *sc);
143 
144 /*
145  * GLD specific operations
146  */
147 static int	ipw2200_m_stat(void *arg, uint_t stat, uint64_t *val);
148 static int	ipw2200_m_start(void *arg);
149 static void	ipw2200_m_stop(void *arg);
150 static int	ipw2200_m_unicst(void *arg, const uint8_t *macaddr);
151 static int	ipw2200_m_multicst(void *arg, boolean_t add, const uint8_t *m);
152 static int	ipw2200_m_promisc(void *arg, boolean_t on);
153 static void	ipw2200_m_ioctl(void *arg, queue_t *wq, mblk_t *mp);
154 static mblk_t  *ipw2200_m_tx(void *arg, mblk_t *mp);
155 
156 /*
157  * Interrupt and Data transferring operations
158  */
159 static uint_t	ipw2200_intr(caddr_t arg);
160 static int	ipw2200_send(struct ieee80211com *ic, mblk_t *mp, uint8_t type);
161 static void	ipw2200_rcv_frame(struct ipw2200_softc *sc,
162     struct ipw2200_frame *frame);
163 static void	ipw2200_rcv_notif(struct ipw2200_softc *sc,
164     struct ipw2200_notif *notif);
165 
166 /*
167  * WiFi specific operations
168  */
169 static int	ipw2200_newstate(struct ieee80211com *ic,
170     enum ieee80211_state state, int arg);
171 static void	ipw2200_thread(struct ipw2200_softc *sc);
172 
173 /*
174  * IOCTL Handler
175  */
176 static int	ipw2200_ioctl(struct ipw2200_softc *sc, queue_t *q, mblk_t *m);
177 static int	ipw2200_getset(struct ipw2200_softc *sc,
178     mblk_t *m, uint32_t cmd, boolean_t *need_net80211);
179 static int	iwi_wificfg_radio(struct ipw2200_softc *sc,
180     uint32_t cmd,  wldp_t *outfp);
181 static int	iwi_wificfg_desrates(wldp_t *outfp);
182 
183 /*
184  * Mac Call Back entries
185  */
186 mac_callbacks_t	ipw2200_m_callbacks = {
187 	MC_IOCTL,
188 	ipw2200_m_stat,
189 	ipw2200_m_start,
190 	ipw2200_m_stop,
191 	ipw2200_m_promisc,
192 	ipw2200_m_multicst,
193 	ipw2200_m_unicst,
194 	ipw2200_m_tx,
195 	NULL,
196 	ipw2200_m_ioctl
197 };
198 
199 /*
200  * DEBUG Facility
201  */
202 #define		MAX_MSG		(128)
203 uint32_t	ipw2200_debug = 0;
204 /*
205  * supported debug marks are:
206  *	| IPW2200_DBG_CSR
207  *	| IPW2200_DBG_TABLE
208  *	| IPW2200_DBG_HWCAP
209  *	| IPW2200_DBG_TX
210  *	| IPW2200_DBG_INIT
211  *	| IPW2200_DBG_FW
212  *	| IPW2200_DBG_NOTIF
213  *	| IPW2200_DBG_SCAN
214  *	| IPW2200_DBG_IOCTL
215  *	| IPW2200_DBG_RING
216  *	| IPW2200_DBG_INT
217  *	| IPW2200_DBG_RX
218  *	| IPW2200_DBG_DMA
219  *	| IPW2200_DBG_GLD
220  *	| IPW2200_DBG_WIFI
221  *	| IPW2200_DBG_SOFTINT
222  */
223 
224 /*
225  * Global tunning parameter to work around unknown hardware issues
226  */
227 static uint32_t delay_config_stable	= 100000;	/* 100ms */
228 static uint32_t delay_fatal_recover	= 100000 * 20;	/* 2s */
229 static uint32_t delay_aux_thread	= 100000;	/* 100ms */
230 
231 #define	IEEE80211_IS_CHAN_2GHZ(_c) \
232 	(((_c)->ich_flags & IEEE80211_CHAN_2GHZ) != 0)
233 #define	IEEE80211_IS_CHAN_5GHZ(_c) \
234 	(((_c)->ich_flags & IEEE80211_CHAN_5GHZ) != 0)
235 #define	isset(a, i)	((a)[(i)/NBBY] & (1 << ((i)%NBBY)))
236 
237 void
238 ipw2200_dbg(dev_info_t *dip, int level, const char *fmt, ...)
239 {
240 	va_list	ap;
241 	char    buf[MAX_MSG];
242 	int	instance;
243 
244 	va_start(ap, fmt);
245 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
246 	va_end(ap);
247 
248 	if (dip) {
249 		instance = ddi_get_instance(dip);
250 		cmn_err(level, "%s%d: %s", IPW2200_DRV_NAME, instance, buf);
251 	} else
252 		cmn_err(level, "%s: %s", IPW2200_DRV_NAME, buf);
253 
254 }
255 
256 /*
257  * Device operations
258  */
259 int
260 ipw2200_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
261 {
262 	struct ipw2200_softc	*sc;
263 	ddi_acc_handle_t	cfgh;
264 	caddr_t			regs;
265 	struct ieee80211com	*ic;
266 	int			instance, err, i;
267 	char			strbuf[32];
268 	wifi_data_t		wd = { 0 };
269 	mac_register_t		*macp;
270 	uint16_t		vendor, device, subven, subdev;
271 
272 	if (cmd != DDI_ATTACH) {
273 		err = DDI_FAILURE;
274 		goto fail1;
275 	}
276 
277 	instance = ddi_get_instance(dip);
278 	err = ddi_soft_state_zalloc(ipw2200_ssp, instance);
279 	if (err != DDI_SUCCESS) {
280 		IPW2200_WARN((dip, CE_WARN,
281 		    "ipw2200_attach(): unable to allocate soft state\n"));
282 		goto fail1;
283 	}
284 	sc = ddi_get_soft_state(ipw2200_ssp, instance);
285 	sc->sc_dip = dip;
286 
287 	/*
288 	 * Map config spaces register to read the vendor id, device id, sub
289 	 * vendor id, and sub device id.
290 	 */
291 	err = ddi_regs_map_setup(dip, IPW2200_PCI_CFG_RNUM, &regs,
292 	    0, 0, &ipw2200_csr_accattr, &cfgh);
293 	if (err != DDI_SUCCESS) {
294 		IPW2200_WARN((dip, CE_WARN,
295 		    "ipw2200_attach(): unable to map spaces regs\n"));
296 		goto fail2;
297 	}
298 	ddi_put8(cfgh, (uint8_t *)(regs + 0x41), 0);
299 	vendor = ddi_get16(cfgh, (uint16_t *)(regs + PCI_CONF_VENID));
300 	device = ddi_get16(cfgh, (uint16_t *)(regs + PCI_CONF_DEVID));
301 	subven = ddi_get16(cfgh, (uint16_t *)(regs + PCI_CONF_SUBVENID));
302 	subdev = ddi_get16(cfgh, (uint16_t *)(regs + PCI_CONF_SUBSYSID));
303 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
304 	    "ipw2200_attach(): vendor = 0x%04x, devic = 0x%04x,"
305 	    "subversion = 0x%04x, subdev = 0x%04x",
306 	    vendor, device, subven, subdev));
307 	ddi_regs_map_free(&cfgh);
308 
309 	/*
310 	 * Map operating registers
311 	 */
312 	err = ddi_regs_map_setup(dip, IPW2200_PCI_CSR_RNUM, &sc->sc_regs,
313 	    0, 0, &ipw2200_csr_accattr, &sc->sc_ioh);
314 	if (err != DDI_SUCCESS) {
315 		IPW2200_WARN((dip, CE_WARN,
316 		    "ipw2200_attach(): ddi_regs_map_setup() failed\n"));
317 		goto fail2;
318 	}
319 
320 	/*
321 	 * Reset the chip
322 	 */
323 	err = ipw2200_chip_reset(sc);
324 	if (err != DDI_SUCCESS) {
325 		IPW2200_WARN((dip, CE_WARN,
326 		    "ipw2200_attach(): ipw2200_chip_reset() failed\n"));
327 		goto fail3;
328 	}
329 
330 	/*
331 	 * Get the hardware configuration, including the MAC address
332 	 * Then, init all the rings needed.
333 	 */
334 	ipw2200_hwconf_get(sc);
335 	err = ipw2200_ring_init(sc);
336 	if (err != DDI_SUCCESS) {
337 		IPW2200_WARN((dip, CE_WARN,
338 		    "ipw2200_attach(): ipw2200_ring_init() failed\n"));
339 		goto fail3;
340 	}
341 
342 	/*
343 	 * Initialize mutexs and condvars
344 	 */
345 	err = ddi_get_iblock_cookie(dip, 0, &sc->sc_iblk);
346 	if (err != DDI_SUCCESS) {
347 		IPW2200_WARN((dip, CE_WARN,
348 		    "ipw2200_attach(): ddi_get_iblock_cookie() failed\n"));
349 		goto fail4;
350 	}
351 
352 	/*
353 	 * interrupt lock
354 	 */
355 	mutex_init(&sc->sc_ilock, "intr-lock", MUTEX_DRIVER,
356 	    (void *) sc->sc_iblk);
357 	cv_init(&sc->sc_fw_cond, "firmware-ok", CV_DRIVER, NULL);
358 	cv_init(&sc->sc_cmd_status_cond, "cmd-status-ring", CV_DRIVER, NULL);
359 
360 	/*
361 	 * command ring lock
362 	 */
363 	mutex_init(&sc->sc_cmd_lock, "cmd-ring", MUTEX_DRIVER,
364 	    (void *) sc->sc_iblk);
365 	cv_init(&sc->sc_cmd_cond, "cmd-ring", CV_DRIVER, NULL);
366 
367 	/*
368 	 * tx ring lock
369 	 */
370 	mutex_init(&sc->sc_tx_lock, "tx-ring", MUTEX_DRIVER,
371 	    (void *) sc->sc_iblk);
372 
373 	/*
374 	 * multi-function lock, may acquire this during interrupt
375 	 */
376 	mutex_init(&sc->sc_mflock, "function-lock", MUTEX_DRIVER,
377 	    (void *) sc->sc_iblk);
378 	cv_init(&sc->sc_mfthread_cv, NULL, CV_DRIVER, NULL);
379 	sc->sc_mf_thread = NULL;
380 	sc->sc_mfthread_switch = 0;
381 
382 	/*
383 	 * Initialize the WiFi part, which will be used by generic layer
384 	 * Need support more features in the furture, such as
385 	 * IEEE80211_C_IBSS
386 	 */
387 	ic = &sc->sc_ic;
388 	ic->ic_phytype  = IEEE80211_T_OFDM;
389 	ic->ic_opmode   = IEEE80211_M_STA;
390 	ic->ic_state    = IEEE80211_S_INIT;
391 	ic->ic_maxrssi  = 100; /* experimental number */
392 	ic->ic_caps = IEEE80211_C_SHPREAMBLE | IEEE80211_C_TXPMGT
393 	    | IEEE80211_C_PMGT | IEEE80211_C_WEP;
394 
395 	/*
396 	 * set mac addr
397 	 */
398 	IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->sc_macaddr);
399 
400 	/*
401 	 * set supported .11a rates and channel - (2915ABG only)
402 	 */
403 	if (device >= 0x4223) {
404 		/* .11a rates */
405 		ic->ic_sup_rates[IEEE80211_MODE_11A] = ipw2200_rateset_11a;
406 		/* .11a channels */
407 		for (i = 36; i <= 64; i += 4) {
408 			ic->ic_sup_channels[i].ich_freq =
409 			    ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
410 			ic->ic_sup_channels[i].ich_flags = /* CHAN_A */
411 			    IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM;
412 		}
413 		for (i = 149; i <= 165; i += 4) {
414 			ic->ic_sup_channels[i].ich_freq =
415 			    ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
416 			ic->ic_sup_channels[i].ich_flags = /* CHAN_A */
417 			    IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM;
418 		}
419 	}
420 
421 	/*
422 	 * set supported .11b and .11g rates
423 	 */
424 	ic->ic_sup_rates[IEEE80211_MODE_11B] = ipw2200_rateset_11b;
425 	ic->ic_sup_rates[IEEE80211_MODE_11G] = ipw2200_rateset_11g;
426 
427 	/*
428 	 * set supported .11b and .11g channels(1 through 14)
429 	 */
430 	for (i = 1; i < 14; i++) {
431 		ic->ic_sup_channels[i].ich_freq  =
432 		    ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
433 		ic->ic_sup_channels[i].ich_flags =
434 		    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
435 		    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
436 	}
437 
438 	/*
439 	 * IBSS channal undefined for now
440 	 */
441 	ic->ic_ibss_chan = &ic->ic_sup_channels[0];
442 	ic->ic_xmit = ipw2200_send;
443 
444 	/*
445 	 * init generic layer, then override state transition machine
446 	 */
447 	ieee80211_attach(ic);
448 
449 	/*
450 	 * Override 80211 default routines
451 	 */
452 	ieee80211_media_init(ic); /* initial the node table and bss */
453 	sc->sc_newstate = ic->ic_newstate;
454 	ic->ic_newstate = ipw2200_newstate;
455 	ic->ic_def_txkey = 0;
456 	sc->sc_authmode = IEEE80211_AUTH_OPEN;
457 
458 	/*
459 	 * Add the interrupt handler
460 	 */
461 	err = ddi_add_intr(dip, 0, &sc->sc_iblk, NULL,
462 	    ipw2200_intr, (caddr_t)sc);
463 	if (err != DDI_SUCCESS) {
464 		IPW2200_WARN((dip, CE_WARN,
465 		    "ipw2200_attach(): ddi_add_intr() failed\n"));
466 		goto fail5;
467 	}
468 
469 	/*
470 	 * Initialize pointer to device specific functions
471 	 */
472 	wd.wd_secalloc = WIFI_SEC_NONE;
473 	wd.wd_opmode = ic->ic_opmode;
474 	IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_macaddr);
475 
476 	macp = mac_alloc(MAC_VERSION);
477 	if (err != 0) {
478 		IPW2200_WARN((dip, CE_WARN,
479 		    "ipw2200_attach(): mac_alloc() failed\n"));
480 		goto fail6;
481 	}
482 
483 	macp->m_type_ident	= MAC_PLUGIN_IDENT_WIFI;
484 	macp->m_driver		= sc;
485 	macp->m_dip		= dip;
486 	macp->m_src_addr	= ic->ic_macaddr;
487 	macp->m_callbacks	= &ipw2200_m_callbacks;
488 	macp->m_min_sdu		= 0;
489 	macp->m_max_sdu		= IEEE80211_MTU;
490 	macp->m_pdata		= &wd;
491 	macp->m_pdata_size	= sizeof (wd);
492 
493 	/*
494 	 * Register the macp to mac
495 	 */
496 	err = mac_register(macp, &ic->ic_mach);
497 	mac_free(macp);
498 	if (err != DDI_SUCCESS) {
499 		IPW2200_WARN((dip, CE_WARN,
500 		    "ipw2200_attach(): mac_register() failed\n"));
501 		goto fail6;
502 	}
503 
504 	/*
505 	 * Create minor node of type DDI_NT_NET_WIFI
506 	 */
507 	(void) snprintf(strbuf, sizeof (strbuf), "%s%d",
508 	    IPW2200_DRV_NAME, instance);
509 	err = ddi_create_minor_node(dip, strbuf, S_IFCHR,
510 	    instance + 1, DDI_NT_NET_WIFI, 0);
511 	if (err != DDI_SUCCESS)
512 		IPW2200_WARN((dip, CE_WARN,
513 		    "ipw2200_attach(): ddi_create_minor_node() failed\n"));
514 
515 	/*
516 	 * Cache firmware will always be true
517 	 */
518 	(void) ipw2200_cache_firmware(sc);
519 
520 	/*
521 	 * Notify link is down now
522 	 */
523 	mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
524 
525 	/*
526 	 * Create the mf thread to handle the link status,
527 	 * recovery fatal error, etc.
528 	 */
529 	sc->sc_mfthread_switch = 1;
530 	if (sc->sc_mf_thread == NULL)
531 		sc->sc_mf_thread = thread_create((caddr_t)NULL, 0,
532 		    ipw2200_thread, sc, 0, &p0, TS_RUN, minclsyspri);
533 
534 	return (DDI_SUCCESS);
535 
536 fail6:
537 	ddi_remove_intr(dip, 0, sc->sc_iblk);
538 fail5:
539 	ieee80211_detach(ic);
540 
541 	mutex_destroy(&sc->sc_ilock);
542 	mutex_destroy(&sc->sc_cmd_lock);
543 	mutex_destroy(&sc->sc_tx_lock);
544 	mutex_destroy(&sc->sc_mflock);
545 	cv_destroy(&sc->sc_fw_cond);
546 	cv_destroy(&sc->sc_cmd_status_cond);
547 	cv_destroy(&sc->sc_cmd_cond);
548 	cv_destroy(&sc->sc_mfthread_cv);
549 fail4:
550 	ipw2200_ring_free(sc);
551 fail3:
552 	ddi_regs_map_free(&sc->sc_ioh);
553 fail2:
554 	ddi_soft_state_free(ipw2200_ssp, instance);
555 fail1:
556 	return (err);
557 }
558 
559 
560 int
561 ipw2200_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
562 {
563 	struct ipw2200_softc	*sc =
564 	    ddi_get_soft_state(ipw2200_ssp, ddi_get_instance(dip));
565 	int			err;
566 	ASSERT(sc != NULL);
567 
568 	if (cmd != DDI_DETACH)
569 		return (DDI_FAILURE);
570 
571 	ipw2200_stop(sc);
572 
573 	/*
574 	 * Destroy the mf_thread
575 	 */
576 	mutex_enter(&sc->sc_mflock);
577 	sc->sc_mfthread_switch = 0;
578 	while (sc->sc_mf_thread != NULL) {
579 		if (cv_wait_sig(&sc->sc_mfthread_cv, &sc->sc_mflock) == 0)
580 			break;
581 	}
582 	mutex_exit(&sc->sc_mflock);
583 
584 	/*
585 	 * Unregister from the MAC layer subsystem
586 	 */
587 	err = mac_unregister(sc->sc_ic.ic_mach);
588 	if (err != DDI_SUCCESS)
589 		return (err);
590 
591 	ddi_remove_intr(dip, IPW2200_PCI_INTR_NUM, sc->sc_iblk);
592 
593 	mutex_destroy(&sc->sc_ilock);
594 	mutex_destroy(&sc->sc_cmd_lock);
595 	mutex_destroy(&sc->sc_tx_lock);
596 	mutex_destroy(&sc->sc_mflock);
597 	cv_destroy(&sc->sc_fw_cond);
598 	cv_destroy(&sc->sc_cmd_status_cond);
599 	cv_destroy(&sc->sc_cmd_cond);
600 	cv_destroy(&sc->sc_mfthread_cv);
601 
602 	/*
603 	 * Detach ieee80211
604 	 */
605 	ieee80211_detach(&sc->sc_ic);
606 
607 	(void) ipw2200_free_firmware(sc);
608 	ipw2200_ring_free(sc);
609 
610 	ddi_regs_map_free(&sc->sc_ioh);
611 	ddi_remove_minor_node(dip, NULL);
612 	ddi_soft_state_free(ipw2200_ssp, ddi_get_instance(dip));
613 
614 	return (DDI_SUCCESS);
615 }
616 
617 /* ARGSUSED */
618 int
619 ipw2200_reset(dev_info_t *dip, ddi_reset_cmd_t cmd)
620 {
621 	struct ipw2200_softc	*sc =
622 	    ddi_get_soft_state(ipw2200_ssp, ddi_get_instance(dip));
623 	ASSERT(sc != NULL);
624 
625 	ipw2200_stop(sc);
626 
627 	return (DDI_SUCCESS);
628 }
629 
630 static void
631 ipw2200_stop(struct ipw2200_softc *sc)
632 {
633 	struct ieee80211com	*ic = &sc->sc_ic;
634 
635 	IPW2200_DBG(IPW2200_DBG_HWCAP, (sc->sc_dip, CE_CONT,
636 	    "ipw2200_stop(): enter\n"));
637 
638 	ipw2200_master_stop(sc);
639 	ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_SW_RESET);
640 
641 	/*
642 	 * Reset ring
643 	 */
644 	ipw2200_ring_reset(sc);
645 
646 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
647 	sc->sc_flags &= ~IPW2200_FLAG_SCANNING;
648 
649 	IPW2200_DBG(IPW2200_DBG_HWCAP, (sc->sc_dip, CE_CONT,
650 	    "ipw2200_stop(): exit\n"));
651 }
652 
653 static int
654 ipw2200_config(struct ipw2200_softc *sc)
655 {
656 	struct ieee80211com		*ic = &sc->sc_ic;
657 	struct ipw2200_configuration	cfg;
658 	uint32_t			data;
659 	struct ipw2200_txpower		pwr;
660 	struct ipw2200_rateset		rs;
661 	struct ipw2200_wep_key		wkey;
662 	int				err, i;
663 
664 	/*
665 	 * Set the IBSS mode channel: Tx power
666 	 */
667 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
668 		pwr.mode  = IPW2200_MODE_11B;
669 		pwr.nchan = 11;
670 		for (i = 0; i < pwr.nchan; i++) {
671 			pwr.chan[i].chan  = i + 1;
672 			pwr.chan[i].power = IPW2200_TXPOWER_MAX;
673 		}
674 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
675 		    "ipw2200_config(): Setting .11b channels Tx power\n"));
676 		err = ipw2200_cmd(sc, IPW2200_CMD_SET_TX_POWER,
677 		    &pwr, sizeof (pwr), 0);
678 		if (err != DDI_SUCCESS)
679 			return (err);
680 
681 		pwr.mode  = IPW2200_MODE_11G;
682 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
683 		    "ipw2200_config(): Setting .11g channels Tx power\n"));
684 		err = ipw2200_cmd(sc, IPW2200_CMD_SET_TX_POWER,
685 		    &pwr, sizeof (pwr), 0);
686 		if (err != DDI_SUCCESS)
687 			return (err);
688 	}
689 
690 	/*
691 	 * Set MAC address
692 	 */
693 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
694 	    "ipw2200_config(): Setting MAC address to "
695 	    "%02x:%02x:%02x:%02x:%02x:%02x\n",
696 	    ic->ic_macaddr[0], ic->ic_macaddr[1], ic->ic_macaddr[2],
697 	    ic->ic_macaddr[3], ic->ic_macaddr[4], ic->ic_macaddr[5]));
698 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_MAC_ADDRESS, ic->ic_macaddr,
699 	    IEEE80211_ADDR_LEN, 0);
700 	if (err != DDI_SUCCESS)
701 		return (err);
702 
703 	/*
704 	 * Set basic system config settings: configuration(capabilities)
705 	 */
706 	(void) memset(&cfg, 0, sizeof (cfg));
707 	cfg.bluetooth_coexistence	 = 1;
708 	cfg.multicast_enabled		 = 1;
709 	cfg.answer_pbreq		 = 1;
710 	cfg.noise_reported		 = 1;
711 
712 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
713 	    "ipw2200_config(): Configuring adapter\n"));
714 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_CONFIG,
715 	    &cfg, sizeof (cfg), 0);
716 	if (err != DDI_SUCCESS)
717 		return (err);
718 
719 	/*
720 	 * Set power mode
721 	 */
722 	data = LE_32(IPW2200_POWER_MODE_CAM);
723 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
724 	    "ipw2200_config(): Setting power mode to %u\n", LE_32(data)));
725 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_POWER_MODE,
726 	    &data, sizeof (data), 0);
727 	if (err != DDI_SUCCESS)
728 		return (err);
729 
730 	/*
731 	 * Set supported rates
732 	 */
733 	rs.mode = IPW2200_MODE_11G;
734 	rs.type = IPW2200_RATESET_TYPE_SUPPORTED;
735 	rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11G].ir_nrates;
736 	(void) memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11G].ir_rates,
737 	    rs.nrates);
738 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
739 	    "ipw2200_config(): Setting .11g supported rates(%u)\n", rs.nrates));
740 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 0);
741 	if (err != DDI_SUCCESS)
742 		return (err);
743 
744 	rs.mode = IPW2200_MODE_11A;
745 	rs.type = IPW2200_RATESET_TYPE_SUPPORTED;
746 	rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11A].ir_nrates;
747 	(void) memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11A].ir_rates,
748 	    rs.nrates);
749 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
750 	    "ipw2200_config(): Setting .11a supported rates(%u)\n", rs.nrates));
751 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 0);
752 	if (err != DDI_SUCCESS)
753 		return (err);
754 
755 	/*
756 	 * Set RTS(request-to-send) threshold
757 	 */
758 	data = LE_32(ic->ic_rtsthreshold);
759 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
760 	    "ipw2200_config(): Setting RTS threshold to %u\n", LE_32(data)));
761 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_RTS_THRESHOLD, &data,
762 	    sizeof (data), 0);
763 	if (err != DDI_SUCCESS)
764 		return (err);
765 
766 	/*
767 	 * Set fragmentation threshold
768 	 */
769 	data = LE_32(ic->ic_fragthreshold);
770 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
771 	    "ipw2200_config(): Setting fragmentation threshold to %u\n",
772 	    LE_32(data)));
773 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_FRAG_THRESHOLD, &data,
774 	    sizeof (data), 0);
775 	if (err != DDI_SUCCESS)
776 		return (err);
777 
778 	/*
779 	 * Set desired ESSID if we have
780 	 */
781 	if (ic->ic_des_esslen != 0) {
782 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
783 		    "ipw2200_config(): Setting desired ESSID to "
784 		    "(%u),%c%c%c%c%c%c%c%c\n",
785 		    ic->ic_des_esslen,
786 		    ic->ic_des_essid[0], ic->ic_des_essid[1],
787 		    ic->ic_des_essid[2], ic->ic_des_essid[3],
788 		    ic->ic_des_essid[4], ic->ic_des_essid[5],
789 		    ic->ic_des_essid[6], ic->ic_des_essid[7]));
790 		err = ipw2200_cmd(sc, IPW2200_CMD_SET_ESSID, ic->ic_des_essid,
791 		    ic->ic_des_esslen, 0);
792 		if (err != DDI_SUCCESS)
793 			return (err);
794 	}
795 
796 	/*
797 	 * Set WEP initial vector(random seed)
798 	 */
799 	(void) random_get_pseudo_bytes((uint8_t *)&data, sizeof (data));
800 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
801 	    "ipw2200_config(): Setting initialization vector to %u\n",
802 	    LE_32(data)));
803 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_IV, &data, sizeof (data), 0);
804 	if (err != DDI_SUCCESS)
805 		return (err);
806 
807 	/*
808 	 * Set WEP if any
809 	 */
810 	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
811 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
812 		    "ipw2200_config(): Setting Wep Key\n", LE_32(data)));
813 		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
814 			wkey.cmd = IPW2200_WEP_KEY_CMD_SETKEY;
815 			wkey.idx = (uint8_t)i;
816 			wkey.len = ic->ic_nw_keys[i].wk_keylen;
817 			(void) memset(wkey.key, 0, sizeof (wkey.key));
818 			if (ic->ic_nw_keys[i].wk_keylen)
819 				(void) memcpy(wkey.key,
820 				    ic->ic_nw_keys[i].wk_key,
821 				    ic->ic_nw_keys[i].wk_keylen);
822 			err = ipw2200_cmd(sc, IPW2200_CMD_SET_WEP_KEY,
823 			    &wkey, sizeof (wkey), 0);
824 			if (err != DDI_SUCCESS)
825 				return (err);
826 		}
827 	}
828 
829 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
830 	    "ipw2200_config(): Enabling adapter\n"));
831 
832 	return (ipw2200_cmd(sc, IPW2200_CMD_ENABLE, NULL, 0, 0));
833 }
834 
835 static int
836 ipw2200_cmd(struct ipw2200_softc *sc,
837 	uint32_t type, void *buf, size_t len, int async)
838 {
839 	struct		ipw2200_cmd_desc *cmd;
840 	clock_t		clk;
841 	uint32_t	idx;
842 
843 	mutex_enter(&sc->sc_cmd_lock);
844 	while (sc->sc_cmd_free < 1)
845 		cv_wait(&sc->sc_cmd_cond, &sc->sc_cmd_lock);
846 
847 	idx = sc->sc_cmd_cur;
848 	cmd = &sc->sc_cmdsc[idx];
849 	(void) memset(cmd, 0, sizeof (*cmd));
850 
851 	IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT,
852 	    "ipw2200_cmd(): cmd-cur=%d\n", idx));
853 
854 	cmd->hdr.type   = IPW2200_HDR_TYPE_COMMAND;
855 	cmd->hdr.flags  = IPW2200_HDR_FLAG_IRQ;
856 	cmd->type	= (uint8_t)type;
857 	if (len == 0 || buf == NULL)
858 		cmd->len  = 0;
859 	else {
860 		cmd->len  = (uint8_t)len;
861 		(void) memcpy(cmd->data, buf, len);
862 	}
863 	sc->sc_done[idx] = 0;
864 
865 	/*
866 	 * DMA sync
867 	 */
868 	(void) ddi_dma_sync(sc->sc_dma_cmdsc.dr_hnd,
869 	    idx * sizeof (struct ipw2200_cmd_desc),
870 	    sizeof (struct ipw2200_cmd_desc), DDI_DMA_SYNC_FORDEV);
871 
872 	sc->sc_cmd_cur = RING_FORWARD(sc->sc_cmd_cur, 1, IPW2200_CMD_RING_SIZE);
873 	sc->sc_cmd_free--;
874 
875 	ipw2200_csr_put32(sc, IPW2200_CSR_CMD_WRITE_INDEX, sc->sc_cmd_cur);
876 
877 	mutex_exit(&sc->sc_cmd_lock);
878 
879 	if (async)
880 		goto out;
881 
882 	/*
883 	 * Wait for command done
884 	 */
885 	mutex_enter(&sc->sc_ilock);
886 	while (sc->sc_done[idx] == 0) {
887 		/* pending */
888 		clk = ddi_get_lbolt() + drv_usectohz(5000000);  /* 5 second */
889 		if (cv_timedwait(&sc->sc_cmd_status_cond, &sc->sc_ilock, clk)
890 		    < 0)
891 			break;
892 	}
893 	mutex_exit(&sc->sc_ilock);
894 
895 	IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT,
896 	    "ipw2200_cmd(): cmd-done=%s\n", sc->sc_done[idx] ? "yes" : "no"));
897 
898 	if (sc->sc_done[idx] == 0)
899 		return (DDI_FAILURE);
900 
901 out:
902 	return (DDI_SUCCESS);
903 }
904 
905 /*
906  * If init failed, it will call stop internally. Therefore, it's unnecessary
907  * to call ipw2200_stop() when this subroutine is failed. Otherwise, it may
908  * be called twice.
909  */
910 int
911 ipw2200_init(struct ipw2200_softc *sc)
912 {
913 	int	err;
914 
915 	/*
916 	 * No firmware is available, failed
917 	 */
918 	if (!(sc->sc_flags & IPW2200_FLAG_FW_CACHED)) {
919 		IPW2200_WARN((sc->sc_dip, CE_WARN,
920 		    "ipw2200_init(): no firmware is available\n"));
921 		return (DDI_FAILURE); /* return directly */
922 	}
923 
924 	ipw2200_stop(sc);
925 
926 	err = ipw2200_chip_reset(sc);
927 	if (err != DDI_SUCCESS) {
928 		IPW2200_WARN((sc->sc_dip, CE_WARN,
929 		    "ipw2200_init(): could not reset adapter\n"));
930 		goto fail;
931 	}
932 
933 	/*
934 	 * Load boot code
935 	 */
936 	err = ipw2200_load_fw(sc, sc->sc_fw.boot_base, sc->sc_fw.boot_size);
937 	if (err != DDI_SUCCESS) {
938 		IPW2200_WARN((sc->sc_dip, CE_WARN,
939 		    "ipw2200_init(): could not load boot code\n"));
940 		goto fail;
941 	}
942 
943 	/*
944 	 * Load boot microcode
945 	 */
946 	err = ipw2200_load_uc(sc, sc->sc_fw.uc_base, sc->sc_fw.uc_size);
947 	if (err != DDI_SUCCESS) {
948 		IPW2200_WARN((sc->sc_dip, CE_WARN,
949 		    "ipw2200_init(): could not load microcode\n"));
950 		goto fail;
951 	}
952 
953 	ipw2200_master_stop(sc);
954 	ipw2200_ring_hwsetup(sc);
955 
956 	/*
957 	 * Load firmware
958 	 */
959 	err = ipw2200_load_fw(sc, sc->sc_fw.fw_base, sc->sc_fw.fw_size);
960 	if (err != DDI_SUCCESS) {
961 		IPW2200_WARN((sc->sc_dip, CE_WARN,
962 		    "ipw2200_init(): could not load firmware\n"));
963 		goto fail;
964 	}
965 
966 	sc->sc_flags |= IPW2200_FLAG_FW_INITED;
967 
968 	/*
969 	 * Hardware will be enabled after configuration
970 	 */
971 	err = ipw2200_config(sc);
972 	if (err != DDI_SUCCESS) {
973 		IPW2200_WARN((sc->sc_dip, CE_WARN,
974 		    "ipw2200_init(): device configuration failed\n"));
975 		goto fail;
976 	}
977 
978 	/*
979 	 * workround to prevent too many h/w error.
980 	 * delay for a while till h/w is stable.
981 	 */
982 	delay(drv_usectohz(delay_config_stable));
983 
984 	return (DDI_SUCCESS); /* return successfully */
985 fail:
986 	ipw2200_stop(sc);
987 	return (err);
988 }
989 
990 /*
991  * get hardware configurations from EEPROM embedded within PRO/2200
992  */
993 static void
994 ipw2200_hwconf_get(struct ipw2200_softc *sc)
995 {
996 	int		i;
997 	uint16_t	val;
998 
999 	/*
1000 	 * Get mac address
1001 	 */
1002 	i = 0;
1003 	val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 0);
1004 	sc->sc_macaddr[i++] = val >> 8;
1005 	sc->sc_macaddr[i++] = val & 0xff;
1006 	val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 1);
1007 	sc->sc_macaddr[i++] = val >> 8;
1008 	sc->sc_macaddr[i++] = val & 0xff;
1009 	val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 2);
1010 	sc->sc_macaddr[i++] = val >> 8;
1011 	sc->sc_macaddr[i++] = val & 0xff;
1012 
1013 	/*
1014 	 * formatted MAC address string
1015 	 */
1016 	(void) snprintf(sc->sc_macstr, sizeof (sc->sc_macstr),
1017 	    "%02x:%02x:%02x:%02x:%02x:%02x",
1018 	    sc->sc_macaddr[0], sc->sc_macaddr[1],
1019 	    sc->sc_macaddr[2], sc->sc_macaddr[3],
1020 	    sc->sc_macaddr[4], sc->sc_macaddr[5]);
1021 
1022 }
1023 
1024 /*
1025  * all ipw2200 interrupts will be masked by this routine
1026  */
1027 static void
1028 ipw2200_master_stop(struct ipw2200_softc *sc)
1029 {
1030 	int	ntries;
1031 
1032 	/*
1033 	 * disable interrupts
1034 	 */
1035 	ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, 0);
1036 	ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_STOP_MASTER);
1037 
1038 	/*
1039 	 * wait long enough to ensure hardware stop successfully.
1040 	 */
1041 	for (ntries = 0; ntries < 500; ntries++) {
1042 		if (ipw2200_csr_get32(sc, IPW2200_CSR_RST) &
1043 		    IPW2200_RST_MASTER_DISABLED)
1044 			break;
1045 		/* wait for a while */
1046 		drv_usecwait(100);
1047 	}
1048 	if (ntries == 500)
1049 		IPW2200_WARN((sc->sc_dip, CE_WARN,
1050 		    "ipw2200_master_stop(): timeout\n"));
1051 
1052 	ipw2200_csr_put32(sc, IPW2200_CSR_RST,
1053 	    IPW2200_RST_PRINCETON_RESET |
1054 	    ipw2200_csr_get32(sc, IPW2200_CSR_RST));
1055 
1056 	sc->sc_flags &= ~IPW2200_FLAG_FW_INITED;
1057 }
1058 
1059 /*
1060  * all ipw2200 interrupts will be masked by this routine
1061  */
1062 static int
1063 ipw2200_chip_reset(struct ipw2200_softc *sc)
1064 {
1065 	uint32_t	tmp;
1066 	int		ntries, i;
1067 
1068 	ipw2200_master_stop(sc);
1069 
1070 	/*
1071 	 * Move adapter to DO state
1072 	 */
1073 	tmp = ipw2200_csr_get32(sc, IPW2200_CSR_CTL);
1074 	ipw2200_csr_put32(sc, IPW2200_CSR_CTL, tmp | IPW2200_CTL_INIT);
1075 
1076 	/*
1077 	 * Initialize Phase-Locked Level (PLL)
1078 	 */
1079 	ipw2200_csr_put32(sc, IPW2200_CSR_READ_INT, IPW2200_READ_INT_INIT_HOST);
1080 
1081 	/*
1082 	 * Wait for clock stabilization
1083 	 */
1084 	for (ntries = 0; ntries < 1000; ntries++) {
1085 		if (ipw2200_csr_get32(sc, IPW2200_CSR_CTL) &
1086 		    IPW2200_CTL_CLOCK_READY)
1087 			break;
1088 		drv_usecwait(200);
1089 	}
1090 	if (ntries == 1000) {
1091 		IPW2200_WARN((sc->sc_dip, CE_WARN,
1092 		    "ipw2200_chip_reset(): timeout\n"));
1093 		return (DDI_FAILURE);
1094 	}
1095 
1096 	tmp = ipw2200_csr_get32(sc, IPW2200_CSR_RST);
1097 	ipw2200_csr_put32(sc, IPW2200_CSR_RST, tmp | IPW2200_RST_SW_RESET);
1098 
1099 	drv_usecwait(10);
1100 
1101 	tmp = ipw2200_csr_get32(sc, IPW2200_CSR_CTL);
1102 	ipw2200_csr_put32(sc, IPW2200_CSR_CTL, tmp | IPW2200_CTL_INIT);
1103 
1104 	/*
1105 	 * clear NIC memory
1106 	 */
1107 	ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_ADDR, 0);
1108 	for (i = 0; i < 0xc000; i++)
1109 		ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, 0);
1110 
1111 	return (DDI_SUCCESS);
1112 }
1113 
1114 /*
1115  * This function is used by wificonfig/dladm to get the current
1116  * radio status, it is off/on
1117  */
1118 int
1119 ipw2200_radio_status(struct ipw2200_softc *sc)
1120 {
1121 	int	val;
1122 
1123 	val = (ipw2200_csr_get32(sc, IPW2200_CSR_IO) &
1124 	    IPW2200_IO_RADIO_ENABLED) ? 1 : 0;
1125 
1126 	return (val);
1127 }
1128 /*
1129  * This function is used to get the statistic
1130  */
1131 void
1132 ipw2200_get_statistics(struct ipw2200_softc *sc)
1133 {
1134 	struct ieee80211com	*ic = &sc->sc_ic;
1135 
1136 	uint32_t size, buf[128];
1137 
1138 	if (!(sc->sc_flags & IPW2200_FLAG_FW_INITED)) {
1139 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
1140 		    "ipw2200_get_statistic(): fw doesn't download yet."));
1141 		return;
1142 	}
1143 
1144 	size = min(ipw2200_csr_get32(sc, IPW2200_CSR_TABLE0_SIZE), 128 - 1);
1145 	ipw2200_csr_getbuf32(sc, IPW2200_CSR_TABLE0_BASE, &buf[1], size);
1146 
1147 	/*
1148 	 * To retrieve the statistic information into proper places. There are
1149 	 * lot of information. These table will be read once a second.
1150 	 * Hopefully, it will not effect the performance.
1151 	 */
1152 
1153 	/*
1154 	 * For the tx/crc information, we can get them from chip directly;
1155 	 * For the rx/wep error/(rts) related information, leave them net80211.
1156 	 */
1157 	/* WIFI_STAT_TX_FRAGS */
1158 	ic->ic_stats.is_tx_frags = (uint32_t)buf[5];
1159 	/* WIFI_STAT_MCAST_TX */
1160 	ic->ic_stats.is_tx_mcast = (uint32_t)buf[31];
1161 	/* WIFI_STAT_TX_RETRANS */
1162 	ic->ic_stats.is_tx_retries = (uint32_t)buf[56];
1163 	/* WIFI_STAT_TX_FAILED */
1164 	ic->ic_stats.is_tx_failed = (uint32_t)buf[57];
1165 	/* MAC_STAT_OBYTES */
1166 	ic->ic_stats.is_tx_bytes = (uint32_t)buf[64];
1167 }
1168 
1169 /*
1170  * DMA region alloc subroutine
1171  */
1172 int
1173 ipw2200_dma_region_alloc(struct ipw2200_softc *sc, struct dma_region *dr,
1174 	size_t size, uint_t dir, uint_t flags)
1175 {
1176 	dev_info_t	*dip = sc->sc_dip;
1177 	int		err;
1178 
1179 	IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
1180 	    "ipw2200_dma_region_alloc(): size =%u\n", size));
1181 
1182 	err = ddi_dma_alloc_handle(dip, &ipw2200_dma_attr, DDI_DMA_SLEEP, NULL,
1183 	    &dr->dr_hnd);
1184 	if (err != DDI_SUCCESS) {
1185 		IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
1186 		    "ipw2200_dma_region_alloc(): "
1187 		    "ddi_dma_alloc_handle() failed\n"));
1188 		goto fail0;
1189 	}
1190 
1191 	err = ddi_dma_mem_alloc(dr->dr_hnd, size, &ipw2200_dma_accattr,
1192 	    flags, DDI_DMA_SLEEP, NULL,
1193 	    &dr->dr_base, &dr->dr_size, &dr->dr_acc);
1194 	if (err != DDI_SUCCESS) {
1195 		IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
1196 		    "ipw2200_dma_region_alloc(): "
1197 		    "ddi_dma_mem_alloc() failed\n"));
1198 		goto fail1;
1199 	}
1200 
1201 	err = ddi_dma_addr_bind_handle(dr->dr_hnd, NULL,
1202 	    dr->dr_base, dr->dr_size,
1203 	    dir | flags, DDI_DMA_SLEEP, NULL,
1204 	    &dr->dr_cookie, &dr->dr_ccnt);
1205 	if (err != DDI_DMA_MAPPED) {
1206 		IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
1207 		    "ipw2200_dma_region_alloc(): "
1208 		    "ddi_dma_addr_bind_handle() failed\n"));
1209 		goto fail2;
1210 	}
1211 
1212 	IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
1213 	    "ipw2200_dma_region_alloc(): ccnt=%u\n", dr->dr_ccnt));
1214 
1215 	if (dr->dr_ccnt != 1) {
1216 		err = DDI_FAILURE;
1217 		goto fail3;
1218 	}
1219 
1220 	dr->dr_pbase = dr->dr_cookie.dmac_address;
1221 
1222 	IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
1223 	    "ipw2200_dma_region_alloc(): get physical-base=0x%08x\n",
1224 	    dr->dr_pbase));
1225 
1226 	return (DDI_SUCCESS);
1227 
1228 fail3:
1229 	(void) ddi_dma_unbind_handle(dr->dr_hnd);
1230 fail2:
1231 	ddi_dma_mem_free(&dr->dr_acc);
1232 fail1:
1233 	ddi_dma_free_handle(&dr->dr_hnd);
1234 fail0:
1235 	return (err);
1236 }
1237 
1238 void
1239 ipw2200_dma_region_free(struct dma_region *dr)
1240 {
1241 	(void) ddi_dma_unbind_handle(dr->dr_hnd);
1242 	ddi_dma_mem_free(&dr->dr_acc);
1243 	ddi_dma_free_handle(&dr->dr_hnd);
1244 }
1245 
1246 static int
1247 ipw2200_ring_alloc(struct ipw2200_softc *sc)
1248 {
1249 	int	err, i;
1250 
1251 	/*
1252 	 * tx desc ring
1253 	 */
1254 	sc->sc_dma_txdsc.dr_name = "ipw2200-tx-desc-ring";
1255 	err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_txdsc,
1256 	    IPW2200_TX_RING_SIZE * sizeof (struct ipw2200_tx_desc),
1257 	    DDI_DMA_WRITE, DDI_DMA_CONSISTENT);
1258 	if (err != DDI_SUCCESS)
1259 		goto fail0;
1260 	/*
1261 	 * tx buffer array
1262 	 */
1263 	for (i = 0; i < IPW2200_TX_RING_SIZE; i++) {
1264 		sc->sc_dma_txbufs[i].dr_name = "ipw2200-tx-buf";
1265 		err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_txbufs[i],
1266 		    IPW2200_TXBUF_SIZE, DDI_DMA_WRITE, DDI_DMA_STREAMING);
1267 		if (err != DDI_SUCCESS) {
1268 			while (i >= 0) {
1269 				ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]);
1270 				i--;
1271 			}
1272 			goto fail1;
1273 		}
1274 	}
1275 	/*
1276 	 * rx buffer array
1277 	 */
1278 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++) {
1279 		sc->sc_dma_rxbufs[i].dr_name = "ipw2200-rx-buf";
1280 		err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_rxbufs[i],
1281 		    IPW2200_RXBUF_SIZE, DDI_DMA_READ, DDI_DMA_STREAMING);
1282 		if (err != DDI_SUCCESS) {
1283 			while (i >= 0) {
1284 				ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]);
1285 				i--;
1286 			}
1287 			goto fail2;
1288 		}
1289 	}
1290 	/*
1291 	 * cmd desc ring
1292 	 */
1293 	sc->sc_dma_cmdsc.dr_name = "ipw2200-cmd-desc-ring";
1294 	err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_cmdsc,
1295 	    IPW2200_CMD_RING_SIZE * sizeof (struct ipw2200_cmd_desc),
1296 	    DDI_DMA_WRITE, DDI_DMA_CONSISTENT);
1297 	if (err != DDI_SUCCESS)
1298 		goto fail3;
1299 
1300 	return (DDI_SUCCESS);
1301 
1302 fail3:
1303 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
1304 		ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]);
1305 fail2:
1306 	for (i = 0; i < IPW2200_TX_RING_SIZE; i++)
1307 		ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]);
1308 fail1:
1309 	ipw2200_dma_region_free(&sc->sc_dma_txdsc);
1310 fail0:
1311 	return (err);
1312 }
1313 
1314 static void
1315 ipw2200_ring_free(struct ipw2200_softc *sc)
1316 {
1317 	int	i;
1318 
1319 	/*
1320 	 * tx ring desc
1321 	 */
1322 	ipw2200_dma_region_free(&sc->sc_dma_txdsc);
1323 	/*
1324 	 * tx buf
1325 	 */
1326 	for (i = 0; i < IPW2200_TX_RING_SIZE; i++)
1327 		ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]);
1328 	/*
1329 	 * rx buf
1330 	 */
1331 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
1332 		ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]);
1333 	/*
1334 	 * command ring desc
1335 	 */
1336 	ipw2200_dma_region_free(&sc->sc_dma_cmdsc);
1337 }
1338 
1339 static void
1340 ipw2200_ring_reset(struct ipw2200_softc *sc)
1341 {
1342 	int i;
1343 
1344 	/*
1345 	 * tx desc ring & buffer array
1346 	 */
1347 	sc->sc_tx_cur   = 0;
1348 	sc->sc_tx_free  = IPW2200_TX_RING_SIZE;
1349 	sc->sc_txdsc    = (struct ipw2200_tx_desc *)sc->sc_dma_txdsc.dr_base;
1350 	for (i = 0; i < IPW2200_TX_RING_SIZE; i++)
1351 		sc->sc_txbufs[i] = (uint8_t *)sc->sc_dma_txbufs[i].dr_base;
1352 	/*
1353 	 * rx buffer array
1354 	 */
1355 	sc->sc_rx_cur   = 0;
1356 	sc->sc_rx_free  = IPW2200_RX_RING_SIZE;
1357 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
1358 		sc->sc_rxbufs[i] = (uint8_t *)sc->sc_dma_rxbufs[i].dr_base;
1359 
1360 	/*
1361 	 * command desc ring
1362 	 */
1363 	sc->sc_cmd_cur  = 0;
1364 	sc->sc_cmd_free = IPW2200_CMD_RING_SIZE;
1365 	sc->sc_cmdsc    = (struct ipw2200_cmd_desc *)sc->sc_dma_cmdsc.dr_base;
1366 }
1367 
1368 /*
1369  * tx, rx rings and command initialization
1370  */
1371 static int
1372 ipw2200_ring_init(struct ipw2200_softc *sc)
1373 {
1374 	int	err;
1375 
1376 	err = ipw2200_ring_alloc(sc);
1377 	if (err != DDI_SUCCESS)
1378 		return (err);
1379 
1380 	ipw2200_ring_reset(sc);
1381 
1382 	return (DDI_SUCCESS);
1383 }
1384 
1385 static void
1386 ipw2200_ring_hwsetup(struct ipw2200_softc *sc)
1387 {
1388 	int	i;
1389 
1390 	/*
1391 	 * command desc ring
1392 	 */
1393 	ipw2200_csr_put32(sc, IPW2200_CSR_CMD_BASE, sc->sc_dma_cmdsc.dr_pbase);
1394 	ipw2200_csr_put32(sc, IPW2200_CSR_CMD_SIZE, IPW2200_CMD_RING_SIZE);
1395 	ipw2200_csr_put32(sc, IPW2200_CSR_CMD_WRITE_INDEX, sc->sc_cmd_cur);
1396 
1397 	/*
1398 	 * tx desc ring.  only tx1 is used, tx2, tx3, and tx4 are unused
1399 	 */
1400 	ipw2200_csr_put32(sc, IPW2200_CSR_TX1_BASE, sc->sc_dma_txdsc.dr_pbase);
1401 	ipw2200_csr_put32(sc, IPW2200_CSR_TX1_SIZE, IPW2200_TX_RING_SIZE);
1402 	ipw2200_csr_put32(sc, IPW2200_CSR_TX1_WRITE_INDEX, sc->sc_tx_cur);
1403 
1404 	/*
1405 	 * tx2, tx3, tx4 is not used
1406 	 */
1407 	ipw2200_csr_put32(sc, IPW2200_CSR_TX2_BASE, sc->sc_dma_txdsc.dr_pbase);
1408 	ipw2200_csr_put32(sc, IPW2200_CSR_TX2_SIZE, IPW2200_TX_RING_SIZE);
1409 	ipw2200_csr_put32(sc, IPW2200_CSR_TX2_READ_INDEX, 0);
1410 	ipw2200_csr_put32(sc, IPW2200_CSR_TX2_WRITE_INDEX, 0);
1411 	ipw2200_csr_put32(sc, IPW2200_CSR_TX3_BASE, sc->sc_dma_txdsc.dr_pbase);
1412 	ipw2200_csr_put32(sc, IPW2200_CSR_TX3_SIZE, IPW2200_TX_RING_SIZE);
1413 	ipw2200_csr_put32(sc, IPW2200_CSR_TX3_READ_INDEX, 0);
1414 	ipw2200_csr_put32(sc, IPW2200_CSR_TX3_WRITE_INDEX, 0);
1415 	ipw2200_csr_put32(sc, IPW2200_CSR_TX4_BASE, sc->sc_dma_txdsc.dr_pbase);
1416 	ipw2200_csr_put32(sc, IPW2200_CSR_TX4_SIZE, IPW2200_TX_RING_SIZE);
1417 	ipw2200_csr_put32(sc, IPW2200_CSR_TX4_READ_INDEX, 0);
1418 	ipw2200_csr_put32(sc, IPW2200_CSR_TX4_WRITE_INDEX, 0);
1419 
1420 	/*
1421 	 * rx buffer ring
1422 	 */
1423 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
1424 		ipw2200_csr_put32(sc, IPW2200_CSR_RX_BASE + i * 4,
1425 		    sc->sc_dma_rxbufs[i].dr_pbase);
1426 	/*
1427 	 * all rx buffer are empty, rx-rd-index == 0 && rx-wr-index == N-1
1428 	 */
1429 	ipw2200_csr_put32(sc, IPW2200_CSR_RX_WRITE_INDEX,
1430 	    RING_BACKWARD(sc->sc_rx_cur, 1, IPW2200_RX_RING_SIZE));
1431 }
1432 
1433 int
1434 ipw2200_start_scan(struct ipw2200_softc *sc)
1435 {
1436 	struct ieee80211com	*ic = &sc->sc_ic;
1437 	struct ipw2200_scan	scan;
1438 	uint8_t			*ch;
1439 	int			cnt, i;
1440 
1441 	IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT,
1442 	    "ipw2200_start_scan(): start scanning \n"));
1443 
1444 	/*
1445 	 * start scanning
1446 	 */
1447 	sc->sc_flags |= IPW2200_FLAG_SCANNING;
1448 
1449 	(void) memset(&scan, 0, sizeof (scan));
1450 	scan.type = (ic->ic_des_esslen != 0) ? IPW2200_SCAN_TYPE_BDIRECTED :
1451 	    IPW2200_SCAN_TYPE_BROADCAST;
1452 	scan.dwelltime = LE_16(40); /* The interval is set up to 40 */
1453 
1454 	/*
1455 	 * Compact supported channel number(5G) into a single buffer
1456 	 */
1457 	ch = scan.channels;
1458 	cnt = 0;
1459 	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
1460 		if (IEEE80211_IS_CHAN_5GHZ(&ic->ic_sup_channels[i]) &&
1461 		    isset(ic->ic_chan_active, i)) {
1462 			*++ch = (uint8_t)i;
1463 			cnt++;
1464 		}
1465 	}
1466 	*(ch - cnt) = IPW2200_CHAN_5GHZ | (uint8_t)cnt;
1467 	ch = (cnt > 0) ? (ch + 1) : (scan.channels);
1468 
1469 	/*
1470 	 * Compact supported channel number(2G) into a single buffer
1471 	 */
1472 	cnt = 0;
1473 	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
1474 		if (IEEE80211_IS_CHAN_2GHZ(&ic->ic_sup_channels[i]) &&
1475 		    isset(ic->ic_chan_active, i)) {
1476 			*++ch = (uint8_t)i;
1477 			cnt++;
1478 		}
1479 	}
1480 	*(ch - cnt) = IPW2200_CHAN_2GHZ | cnt;
1481 
1482 	return (ipw2200_cmd(sc, IPW2200_CMD_SCAN, &scan, sizeof (scan), 1));
1483 }
1484 
1485 int
1486 ipw2200_auth_and_assoc(struct ipw2200_softc *sc)
1487 {
1488 	struct ieee80211com		*ic = &sc->sc_ic;
1489 	struct ieee80211_node		*in = ic->ic_bss;
1490 	struct ipw2200_configuration	cfg;
1491 	struct ipw2200_rateset		rs;
1492 	struct ipw2200_associate	assoc;
1493 	uint32_t			data;
1494 	int				err;
1495 
1496 	/*
1497 	 * set the confiuration
1498 	 */
1499 	if (IEEE80211_IS_CHAN_2GHZ(in->in_chan)) {
1500 		/* enable b/g auto-detection */
1501 		(void) memset(&cfg, 0, sizeof (cfg));
1502 		cfg.bluetooth_coexistence = 1;
1503 		cfg.multicast_enabled	  = 1;
1504 		cfg.use_protection	  = 1;
1505 		cfg.answer_pbreq	  = 1;
1506 		cfg.noise_reported	  = 1;
1507 		err = ipw2200_cmd(sc, IPW2200_CMD_SET_CONFIG,
1508 		    &cfg, sizeof (cfg), 1);
1509 		if (err != DDI_SUCCESS)
1510 			return (err);
1511 	}
1512 
1513 	/*
1514 	 * set the essid, may be null/hidden AP
1515 	 */
1516 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
1517 	    "ipw2200_auth_and_assoc(): "
1518 	    "setting ESSID to(%u),%c%c%c%c%c%c%c%c\n",
1519 	    in->in_esslen,
1520 	    in->in_essid[0], in->in_essid[1],
1521 	    in->in_essid[2], in->in_essid[3],
1522 	    in->in_essid[4], in->in_essid[5],
1523 	    in->in_essid[6], in->in_essid[7]));
1524 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_ESSID, in->in_essid,
1525 	    in->in_esslen, 1);
1526 	if (err != DDI_SUCCESS)
1527 		return (err);
1528 
1529 	/*
1530 	 * set the rate: the rate set has already been ''negocitated''
1531 	 */
1532 	rs.mode = IEEE80211_IS_CHAN_5GHZ(in->in_chan) ?
1533 	    IPW2200_MODE_11A : IPW2200_MODE_11G;
1534 	rs.type = IPW2200_RATESET_TYPE_NEGOCIATED;
1535 	rs.nrates = in->in_rates.ir_nrates;
1536 	(void) memcpy(rs.rates, in->in_rates.ir_rates, in->in_rates.ir_nrates);
1537 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
1538 	    "ipw2200_auth_and_assoc(): "
1539 	    "setting negotiated rates to(nrates = %u)\n", rs.nrates));
1540 
1541 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 1);
1542 	if (err != DDI_SUCCESS)
1543 		return (err);
1544 
1545 	/*
1546 	 * set the sensitive
1547 	 */
1548 	data = LE_32(in->in_rssi);
1549 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
1550 	    "ipw2200_auth_and_assoc(): "
1551 	    "setting sensitivity to rssi:(%u)\n", (uint8_t)in->in_rssi));
1552 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_SENSITIVITY,
1553 	    &data, sizeof (data), 1);
1554 	if (err != DDI_SUCCESS)
1555 		return (err);
1556 
1557 	/*
1558 	 * invoke command associate
1559 	 */
1560 	(void) memset(&assoc, 0, sizeof (assoc));
1561 	assoc.mode = IEEE80211_IS_CHAN_5GHZ(in->in_chan) ?
1562 	    IPW2200_MODE_11A : IPW2200_MODE_11G;
1563 	assoc.chan = ieee80211_chan2ieee(ic, in->in_chan);
1564 	/*
1565 	 * use the value set to ic_bss to retraive current sharedmode
1566 	 */
1567 	if (ic->ic_bss->in_authmode == WL_SHAREDKEY) {
1568 		assoc.auth = (ic->ic_def_txkey << 4) | IPW2200_AUTH_SHARED;
1569 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
1570 		    "ipw2200_auth_and_assoc(): "
1571 		    "associate to shared key mode, set thru. ioctl"));
1572 	}
1573 	(void) memcpy(assoc.tstamp, in->in_tstamp.data, 8);
1574 	assoc.capinfo = LE_16(in->in_capinfo);
1575 	assoc.lintval = LE_16(ic->ic_lintval);
1576 	assoc.intval  = LE_16(in->in_intval);
1577 	IEEE80211_ADDR_COPY(assoc.bssid, in->in_bssid);
1578 	if (ic->ic_opmode == IEEE80211_M_IBSS)
1579 		IEEE80211_ADDR_COPY(assoc.dst, ipw2200_broadcast_addr);
1580 	else
1581 		IEEE80211_ADDR_COPY(assoc.dst, in->in_bssid);
1582 
1583 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
1584 	    "ipw2200_auth_and_assoc(): "
1585 	    "associate to bssid(%2x:%2x:%2x:%2x:%2x:%2x:), "
1586 	    "chan(%u), auth(%u)\n",
1587 	    assoc.bssid[0], assoc.bssid[1], assoc.bssid[2],
1588 	    assoc.bssid[3], assoc.bssid[4], assoc.bssid[5],
1589 	    assoc.chan, assoc.auth));
1590 	return (ipw2200_cmd(sc, IPW2200_CMD_ASSOCIATE,
1591 	    &assoc, sizeof (assoc), 1));
1592 }
1593 
1594 /* ARGSUSED */
1595 static int
1596 ipw2200_newstate(struct ieee80211com *ic, enum ieee80211_state state, int arg)
1597 {
1598 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)ic;
1599 	wifi_data_t		wd = { 0 };
1600 
1601 	switch (state) {
1602 	case IEEE80211_S_SCAN:
1603 		if (!(sc->sc_flags & IPW2200_FLAG_SCANNING)) {
1604 			ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN;
1605 			(void) ipw2200_start_scan(sc);
1606 		}
1607 		break;
1608 	case IEEE80211_S_AUTH:
1609 		(void) ipw2200_auth_and_assoc(sc);
1610 		break;
1611 	case IEEE80211_S_RUN:
1612 		/*
1613 		 * We can send data now; update the fastpath with our
1614 		 * current associated BSSID and other relevant settings.
1615 		 *
1616 		 * Hardware to ahndle the wep encryption. Set the encryption
1617 		 * as false.
1618 		 * wd.wd_wep    = ic->ic_flags & IEEE80211_F_WEPON;
1619 		 */
1620 		wd.wd_secalloc	= WIFI_SEC_NONE;
1621 		wd.wd_opmode	= ic->ic_opmode;
1622 		IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
1623 		(void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd));
1624 		break;
1625 	case IEEE80211_S_ASSOC:
1626 	case IEEE80211_S_INIT:
1627 		break;
1628 	}
1629 
1630 	/*
1631 	 * notify to update the link
1632 	 */
1633 	if ((ic->ic_state != IEEE80211_S_RUN) && (state == IEEE80211_S_RUN)) {
1634 		mac_link_update(ic->ic_mach, LINK_STATE_UP);
1635 	} else if ((ic->ic_state == IEEE80211_S_RUN) &&
1636 	    (state != IEEE80211_S_RUN)) {
1637 		mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
1638 	}
1639 
1640 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
1641 	    "ipw2200_newstat(): %s -> %s\n",
1642 	    ieee80211_state_name[ic->ic_state],
1643 	    ieee80211_state_name[state]));
1644 
1645 	ic->ic_state = state;
1646 	return (DDI_SUCCESS);
1647 }
1648 
1649 /*
1650  * GLD operations
1651  */
1652 /* ARGSUSED */
1653 static int
1654 ipw2200_m_stat(void *arg, uint_t stat, uint64_t *val)
1655 {
1656 	ieee80211com_t	*ic = (ieee80211com_t *)arg;
1657 
1658 	IPW2200_DBG(IPW2200_DBG_GLD, (((struct ipw2200_softc *)arg)->sc_dip,
1659 	    CE_CONT,
1660 	    "ipw2200_m_stat(): enter\n"));
1661 
1662 	/*
1663 	 * Some of below statistic data are from hardware, some from net80211
1664 	 */
1665 	switch (stat) {
1666 	case MAC_STAT_RBYTES:
1667 		*val = ic->ic_stats.is_rx_bytes;
1668 		break;
1669 	case MAC_STAT_IPACKETS:
1670 		*val = ic->ic_stats.is_rx_frags;
1671 		break;
1672 	case MAC_STAT_OBYTES:
1673 		*val = ic->ic_stats.is_tx_bytes;
1674 		break;
1675 	case MAC_STAT_OPACKETS:
1676 		*val = ic->ic_stats.is_tx_frags;
1677 		break;
1678 	/*
1679 	 * Get below from hardware statistic, retraive net80211 value once 1s
1680 	 */
1681 	case WIFI_STAT_TX_FRAGS:
1682 	case WIFI_STAT_MCAST_TX:
1683 	case WIFI_STAT_TX_FAILED:
1684 	case WIFI_STAT_TX_RETRANS:
1685 	/*
1686 	 * Get blow information from net80211
1687 	 */
1688 	case WIFI_STAT_RTS_SUCCESS:
1689 	case WIFI_STAT_RTS_FAILURE:
1690 	case WIFI_STAT_ACK_FAILURE:
1691 	case WIFI_STAT_RX_FRAGS:
1692 	case WIFI_STAT_MCAST_RX:
1693 	case WIFI_STAT_RX_DUPS:
1694 	case WIFI_STAT_FCS_ERRORS:
1695 	case WIFI_STAT_WEP_ERRORS:
1696 		return (ieee80211_stat(ic, stat, val));
1697 	/*
1698 	 * Need be supported later
1699 	 */
1700 	case MAC_STAT_IFSPEED:
1701 	case MAC_STAT_NOXMTBUF:
1702 	case MAC_STAT_IERRORS:
1703 	case MAC_STAT_OERRORS:
1704 	default:
1705 		return (ENOTSUP);
1706 	}
1707 	return (0);
1708 }
1709 
1710 /* ARGSUSED */
1711 static int
1712 ipw2200_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
1713 {
1714 	/* not supported */
1715 	IPW2200_DBG(IPW2200_DBG_GLD, (((struct ipw2200_softc *)arg)->sc_dip,
1716 	    CE_CONT,
1717 	    "ipw2200_m_multicst(): enter\n"));
1718 
1719 	return (DDI_SUCCESS);
1720 }
1721 
1722 /*
1723  * Multithread handler for linkstatus, fatal error recovery, get statistic
1724  */
1725 static void
1726 ipw2200_thread(struct ipw2200_softc *sc)
1727 {
1728 	struct ieee80211com	*ic = &sc->sc_ic;
1729 	enum ieee80211_state	ostate;
1730 	int32_t			nlstate;
1731 	int			stat_cnt = 0;
1732 
1733 	IPW2200_DBG(IPW2200_DBG_SOFTINT, (sc->sc_dip, CE_CONT,
1734 	    "ipw2200_thread(): enter, linkstate %d\n", sc->sc_linkstate));
1735 
1736 	mutex_enter(&sc->sc_mflock);
1737 
1738 	while (sc->sc_mfthread_switch) {
1739 		/*
1740 		 * notify the link state
1741 		 */
1742 		if (ic->ic_mach && (sc->sc_flags & IPW2200_FLAG_LINK_CHANGE)) {
1743 
1744 			IPW2200_DBG(IPW2200_DBG_SOFTINT, (sc->sc_dip, CE_CONT,
1745 			    "ipw2200_thread(): link status --> %d\n",
1746 			    sc->sc_linkstate));
1747 
1748 			sc->sc_flags &= ~IPW2200_FLAG_LINK_CHANGE;
1749 			nlstate = sc->sc_linkstate;
1750 
1751 			mutex_exit(&sc->sc_mflock);
1752 			mac_link_update(ic->ic_mach, nlstate);
1753 			mutex_enter(&sc->sc_mflock);
1754 		}
1755 
1756 		/*
1757 		 * recovery fatal error
1758 		 */
1759 		if (ic->ic_mach &&
1760 		    (sc->sc_flags & IPW2200_FLAG_HW_ERR_RECOVER)) {
1761 
1762 			IPW2200_DBG(IPW2200_DBG_FATAL, (sc->sc_dip, CE_CONT,
1763 			    "ipw2200_thread(): "
1764 			    "try to recover fatal hw error\n"));
1765 
1766 			sc->sc_flags &= ~IPW2200_FLAG_HW_ERR_RECOVER;
1767 
1768 			mutex_exit(&sc->sc_mflock);
1769 
1770 			ostate = ic->ic_state;
1771 			(void) ipw2200_init(sc); /* Force state machine */
1772 			/*
1773 			 * workround. Delay for a while after init especially
1774 			 * when something wrong happened already.
1775 			 */
1776 			delay(drv_usectohz(delay_fatal_recover));
1777 
1778 			/*
1779 			 * Init scan will recovery the original connection if
1780 			 * the original state is run
1781 			 */
1782 			if (ostate != IEEE80211_S_INIT)
1783 				ieee80211_begin_scan(ic, 0);
1784 
1785 			mutex_enter(&sc->sc_mflock);
1786 		}
1787 
1788 		/*
1789 		 * get statistic, the value will be retrieved by m_stat
1790 		 */
1791 		if (stat_cnt == 10) {
1792 
1793 			stat_cnt = 0; /* re-start */
1794 			mutex_exit(&sc->sc_mflock);
1795 			ipw2200_get_statistics(sc);
1796 			mutex_enter(&sc->sc_mflock);
1797 
1798 		} else
1799 			stat_cnt++; /* until 1s */
1800 
1801 		mutex_exit(&sc->sc_mflock);
1802 		delay(drv_usectohz(delay_aux_thread));
1803 		mutex_enter(&sc->sc_mflock);
1804 
1805 	}
1806 	sc->sc_mf_thread = NULL;
1807 	cv_signal(&sc->sc_mfthread_cv);
1808 	mutex_exit(&sc->sc_mflock);
1809 }
1810 
1811 static int
1812 ipw2200_m_start(void *arg)
1813 {
1814 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
1815 	struct ieee80211com	*ic = &sc->sc_ic;
1816 
1817 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
1818 	    "ipw2200_m_start(): enter\n"));
1819 	/*
1820 	 * initialize ipw2200 hardware, everything ok will start scan
1821 	 */
1822 	(void) ipw2200_init(sc);
1823 	/*
1824 	 * set the state machine to INIT
1825 	 */
1826 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
1827 
1828 	sc->sc_flags |= IPW2200_FLAG_RUNNING;
1829 
1830 	return (DDI_SUCCESS);
1831 }
1832 
1833 static void
1834 ipw2200_m_stop(void *arg)
1835 {
1836 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
1837 	struct ieee80211com	*ic = &sc->sc_ic;
1838 
1839 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
1840 	    "ipw2200_m_stop(): enter\n"));
1841 
1842 	ipw2200_stop(sc);
1843 	/*
1844 	 * set the state machine to INIT
1845 	 */
1846 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
1847 
1848 	sc->sc_flags &= ~IPW2200_FLAG_RUNNING;
1849 }
1850 
1851 static int
1852 ipw2200_m_unicst(void *arg, const uint8_t *macaddr)
1853 {
1854 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
1855 	struct ieee80211com	*ic = &sc->sc_ic;
1856 	int			err;
1857 
1858 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
1859 	    "ipw2200_m_unicst(): enter\n"));
1860 
1861 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
1862 	    "ipw2200_m_unicst(): GLD setting MAC address to "
1863 	    "%02x:%02x:%02x:%02x:%02x:%02x\n",
1864 	    macaddr[0], macaddr[1], macaddr[2],
1865 	    macaddr[3], macaddr[4], macaddr[5]));
1866 
1867 	if (!IEEE80211_ADDR_EQ(ic->ic_macaddr, macaddr)) {
1868 
1869 		IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr);
1870 
1871 		if (sc->sc_flags & IPW2200_FLAG_RUNNING) {
1872 			err = ipw2200_config(sc);
1873 			if (err != DDI_SUCCESS) {
1874 				IPW2200_WARN((sc->sc_dip, CE_WARN,
1875 				    "ipw2200_m_unicst(): "
1876 				    "device configuration failed\n"));
1877 				goto fail;
1878 			}
1879 		}
1880 	}
1881 	return (DDI_SUCCESS);
1882 fail:
1883 	return (err);
1884 }
1885 
1886 static int
1887 ipw2200_m_promisc(void *arg, boolean_t on)
1888 {
1889 	/* not supported */
1890 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
1891 
1892 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
1893 	    "ipw2200_m_promisc(): enter. "
1894 	    "GLD setting promiscuous mode - %d\n", on));
1895 
1896 	return (DDI_SUCCESS);
1897 }
1898 
1899 static mblk_t *
1900 ipw2200_m_tx(void *arg, mblk_t *mp)
1901 {
1902 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
1903 	struct ieee80211com	*ic = &sc->sc_ic;
1904 	mblk_t			*next;
1905 
1906 	/*
1907 	 * No data frames go out unless we're associated; this
1908 	 * should not happen as the 802.11 layer does not enable
1909 	 * the xmit queue until we enter the RUN state.
1910 	 */
1911 	if (ic->ic_state != IEEE80211_S_RUN) {
1912 		IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
1913 		    "ipw2200_m_tx(): discard msg, ic_state = %u\n",
1914 		    ic->ic_state));
1915 		freemsgchain(mp);
1916 		return (NULL);
1917 	}
1918 
1919 	while (mp != NULL) {
1920 		next = mp->b_next;
1921 		mp->b_next = NULL;
1922 		if (ipw2200_send(ic, mp, IEEE80211_FC0_TYPE_DATA) ==
1923 		    DDI_FAILURE) {
1924 			mp->b_next = next;
1925 			break;
1926 		}
1927 		mp = next;
1928 	}
1929 	return (mp);
1930 }
1931 
1932 /* ARGSUSED */
1933 static int
1934 ipw2200_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
1935 {
1936 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)ic;
1937 	struct ieee80211_node	*in;
1938 	struct ieee80211_frame	*wh;
1939 	mblk_t			*m0;
1940 	size_t			cnt, off;
1941 	struct ipw2200_tx_desc	*txdsc;
1942 	struct dma_region	*dr;
1943 	uint32_t		idx;
1944 	int			err;
1945 	/* tmp pointer, used to pack header and payload */
1946 	uint8_t			*p;
1947 
1948 	ASSERT(mp->b_next == NULL);
1949 
1950 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
1951 	    "ipw2200_send(): enter\n"));
1952 
1953 	m0 = NULL;
1954 	err = DDI_SUCCESS;
1955 
1956 	if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA) {
1957 		/*
1958 		 * skip all management frames since ipw2200 won't generate any
1959 		 * management frames. Therefore, drop this package.
1960 		 */
1961 		freemsg(mp);
1962 		err = DDI_SUCCESS;
1963 		goto fail0;
1964 	}
1965 
1966 	mutex_enter(&sc->sc_tx_lock);
1967 
1968 	/*
1969 	 * need 1 empty descriptor
1970 	 */
1971 	if (sc->sc_tx_free <= IPW2200_TX_RING_MIN) {
1972 		IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_WARN,
1973 		    "ipw2200_send(): no enough descriptors(%d)\n",
1974 		    sc->sc_tx_free));
1975 		ic->ic_stats.is_tx_nobuf++; /* no enough buffer */
1976 		sc->sc_flags |= IPW2200_FLAG_TX_SCHED;
1977 		err = DDI_FAILURE;
1978 		goto fail1;
1979 	}
1980 	IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT,
1981 	    "ipw2200_send():  tx-free=%d,tx-curr=%d\n",
1982 	    sc->sc_tx_free, sc->sc_tx_cur));
1983 
1984 	wh = (struct ieee80211_frame *)mp->b_rptr;
1985 	in = ieee80211_find_txnode(ic, wh->i_addr1);
1986 	if (in == NULL) { /* can not find the tx node, drop the package */
1987 		ic->ic_stats.is_tx_failed++;
1988 		freemsg(mp);
1989 		err = DDI_SUCCESS;
1990 		goto fail1;
1991 	}
1992 	in->in_inact = 0;
1993 	(void) ieee80211_encap(ic, mp, in);
1994 	ieee80211_free_node(in);
1995 
1996 	/*
1997 	 * get txdsc and wh
1998 	 */
1999 	idx	= sc->sc_tx_cur;
2000 	txdsc	= &sc->sc_txdsc[idx];
2001 	(void) memset(txdsc, 0, sizeof (*txdsc));
2002 	wh	= (struct ieee80211_frame *)&txdsc->wh;
2003 
2004 	/*
2005 	 * extract header from message
2006 	 */
2007 	p	= (uint8_t *)&txdsc->wh;
2008 	off	= 0;
2009 	m0	= mp;
2010 	while (off < sizeof (struct ieee80211_frame)) {
2011 		cnt = MBLKL(m0);
2012 		if (cnt > (sizeof (struct ieee80211_frame) - off))
2013 			cnt = sizeof (struct ieee80211_frame) - off;
2014 		if (cnt) {
2015 			(void) memcpy(p + off, m0->b_rptr, cnt);
2016 			off += cnt;
2017 			m0->b_rptr += cnt;
2018 		} else
2019 			m0 = m0->b_cont;
2020 	}
2021 
2022 	/*
2023 	 * extract payload from message
2024 	 */
2025 	dr	= &sc->sc_dma_txbufs[idx];
2026 	p	= sc->sc_txbufs[idx];
2027 	off	= 0;
2028 	while (m0) {
2029 		cnt = MBLKL(m0);
2030 		if (cnt)
2031 			(void) memcpy(p + off, m0->b_rptr, cnt);
2032 		off += cnt;
2033 		m0 = m0->b_cont;
2034 	}
2035 
2036 	txdsc->hdr.type   = IPW2200_HDR_TYPE_DATA;
2037 	txdsc->hdr.flags  = IPW2200_HDR_FLAG_IRQ;
2038 	txdsc->cmd	  = IPW2200_DATA_CMD_TX;
2039 	txdsc->len	  = LE_16(off);
2040 	txdsc->flags	  = 0;
2041 
2042 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
2043 		if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
2044 			txdsc->flags |= IPW2200_DATA_FLAG_NEED_ACK;
2045 	} else if (!IEEE80211_IS_MULTICAST(wh->i_addr3))
2046 		txdsc->flags |= IPW2200_DATA_FLAG_NEED_ACK;
2047 
2048 	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
2049 		wh->i_fc[1] |= IEEE80211_FC1_WEP;
2050 		txdsc->wep_txkey = ic->ic_def_txkey;
2051 	}
2052 	else
2053 		txdsc->flags |= IPW2200_DATA_FLAG_NO_WEP;
2054 
2055 	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
2056 		txdsc->flags |= IPW2200_DATA_FLAG_SHPREAMBLE;
2057 
2058 	txdsc->nseg	    = LE_32(1);
2059 	txdsc->seg_addr[0]  = LE_32(dr->dr_pbase);
2060 	txdsc->seg_len[0]   = LE_32(off);
2061 
2062 	/*
2063 	 * DMA sync: buffer and desc
2064 	 */
2065 	(void) ddi_dma_sync(dr->dr_hnd, 0,
2066 	    IPW2200_TXBUF_SIZE, DDI_DMA_SYNC_FORDEV);
2067 	(void) ddi_dma_sync(sc->sc_dma_txdsc.dr_hnd,
2068 	    idx * sizeof (struct ipw2200_tx_desc),
2069 	    sizeof (struct ipw2200_tx_desc), DDI_DMA_SYNC_FORDEV);
2070 
2071 	sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2200_TX_RING_SIZE);
2072 	sc->sc_tx_free--;
2073 
2074 	/*
2075 	 * update txcur
2076 	 */
2077 	ipw2200_csr_put32(sc, IPW2200_CSR_TX1_WRITE_INDEX, sc->sc_tx_cur);
2078 
2079 	/*
2080 	 * success, free the original message
2081 	 */
2082 	if (mp)
2083 		freemsg(mp);
2084 
2085 fail1:
2086 	mutex_exit(&sc->sc_tx_lock);
2087 fail0:
2088 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
2089 	    "ipw2200_send(): exit - err=%d\n", err));
2090 
2091 	return (err);
2092 }
2093 
2094 /*
2095  * IOCTL handlers
2096  */
2097 #define	IEEE80211_IOCTL_REQUIRED	(1)
2098 #define	IEEE80211_IOCTL_NOT_REQUIRED	(0)
2099 static void
2100 ipw2200_m_ioctl(void *arg, queue_t *q, mblk_t *m)
2101 {
2102 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
2103 	struct ieee80211com	*ic = &sc->sc_ic;
2104 	uint32_t		err;
2105 
2106 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
2107 	    "ipw2200_m_ioctl(): enter\n"));
2108 
2109 	/*
2110 	 * Check whether or not need to handle this in net80211
2111 	 *
2112 	 */
2113 	if (ipw2200_ioctl(sc, q, m) == IEEE80211_IOCTL_NOT_REQUIRED)
2114 		return;
2115 
2116 	err = ieee80211_ioctl(ic, q, m);
2117 	if (err == ENETRESET) {
2118 		if (sc->sc_flags & IPW2200_FLAG_RUNNING) {
2119 			(void) ipw2200_m_start(sc);
2120 			(void) ieee80211_new_state(ic,
2121 			    IEEE80211_S_SCAN, -1);
2122 		}
2123 	}
2124 	if (err == ERESTART) {
2125 		if (sc->sc_flags & IPW2200_FLAG_RUNNING)
2126 			(void) ipw2200_chip_reset(sc);
2127 	}
2128 }
2129 static int
2130 ipw2200_ioctl(struct ipw2200_softc *sc, queue_t *q, mblk_t *m)
2131 {
2132 	struct iocblk	*iocp;
2133 	uint32_t	len, ret, cmd;
2134 	mblk_t		*m0;
2135 	boolean_t	need_privilege;
2136 	boolean_t	need_net80211;
2137 
2138 	if (MBLKL(m) < sizeof (struct iocblk)) {
2139 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
2140 		    "ipw2200_ioctl(): ioctl buffer too short, %u\n",
2141 		    MBLKL(m)));
2142 		miocnak(q, m, 0, EINVAL);
2143 		/*
2144 		 * Buf not enough, do not need net80211 either
2145 		 */
2146 		return (IEEE80211_IOCTL_NOT_REQUIRED);
2147 	}
2148 
2149 	/*
2150 	 * Validate the command
2151 	 */
2152 	iocp = (struct iocblk *)m->b_rptr;
2153 	iocp->ioc_error = 0;
2154 	cmd = iocp->ioc_cmd;
2155 	need_privilege = B_TRUE;
2156 	switch (cmd) {
2157 	case WLAN_SET_PARAM:
2158 	case WLAN_COMMAND:
2159 		break;
2160 	case WLAN_GET_PARAM:
2161 		need_privilege = B_FALSE;
2162 		break;
2163 	default:
2164 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
2165 		    "ipw2200_ioctl(): unknown cmd 0x%x", cmd));
2166 		miocnak(q, m, 0, EINVAL);
2167 		/*
2168 		 * Unknown cmd, do not need net80211 either
2169 		 */
2170 		return (IEEE80211_IOCTL_NOT_REQUIRED);
2171 	}
2172 
2173 	if (need_privilege) {
2174 		/*
2175 		 * Check for specific net_config privilege on Solaris 10+.
2176 		 * Otherwise just check for root access ...
2177 		 */
2178 		if (secpolicy_net_config != NULL)
2179 			ret = secpolicy_net_config(iocp->ioc_cr, B_FALSE);
2180 		else
2181 			ret = drv_priv(iocp->ioc_cr);
2182 		if (ret != 0) {
2183 			miocnak(q, m, 0, ret);
2184 			/*
2185 			 * privilege check fail, do not need net80211 either
2186 			 */
2187 			return (IEEE80211_IOCTL_NOT_REQUIRED);
2188 		}
2189 	}
2190 	/*
2191 	 * sanity check
2192 	 */
2193 	m0 = m->b_cont;
2194 	if (iocp->ioc_count == 0 || iocp->ioc_count < sizeof (wldp_t) ||
2195 	    m0 == NULL) {
2196 		miocnak(q, m, 0, EINVAL);
2197 		/*
2198 		 * invalid format, do not need net80211 either
2199 		 */
2200 		return (IEEE80211_IOCTL_NOT_REQUIRED);
2201 	}
2202 	/*
2203 	 * assuming single data block
2204 	 */
2205 	if (m0->b_cont) {
2206 		freemsg(m0->b_cont);
2207 		m0->b_cont = NULL;
2208 	}
2209 
2210 	need_net80211 = B_FALSE;
2211 	ret = ipw2200_getset(sc, m0, cmd, &need_net80211);
2212 	if (!need_net80211) {
2213 		len = msgdsize(m0);
2214 
2215 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
2216 		    "ipw2200_ioctl(): go to call miocack with "
2217 		    "ret = %d, len = %d\n", ret, len));
2218 		miocack(q, m, len, ret);
2219 		return (IEEE80211_IOCTL_NOT_REQUIRED);
2220 	}
2221 
2222 	/*
2223 	 * IEEE80211_IOCTL - need net80211 handle
2224 	 */
2225 	return (IEEE80211_IOCTL_REQUIRED);
2226 }
2227 
2228 static int
2229 ipw2200_getset(struct ipw2200_softc *sc, mblk_t *m, uint32_t cmd,
2230 	boolean_t *need_net80211)
2231 {
2232 	wldp_t		*infp, *outfp;
2233 	uint32_t	id;
2234 	int		ret;
2235 
2236 	infp = (wldp_t *)m->b_rptr;
2237 	outfp = (wldp_t *)m->b_rptr;
2238 	outfp->wldp_result = WL_NOTSUPPORTED;
2239 
2240 	id = infp->wldp_id;
2241 	IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
2242 	    "ipw2200_getset(): id = 0x%x\n", id));
2243 	switch (id) {
2244 	case WL_RADIO: /* which is not supported by net80211 */
2245 		ret = iwi_wificfg_radio(sc, cmd, outfp);
2246 		break;
2247 	case WL_DESIRED_RATES: /* hardware doesn't support fix-rates */
2248 		ret = iwi_wificfg_desrates(outfp);
2249 		break;
2250 	default:
2251 		/*
2252 		 * The wifi IOCTL net80211 supported:
2253 		 *	case WL_ESSID:
2254 		 *	case WL_BSSID:
2255 		 *	case WL_WEP_KEY_TAB:
2256 		 *	case WL_WEP_KEY_ID:
2257 		 *	case WL_AUTH_MODE:
2258 		 *	case WL_ENCRYPTION:
2259 		 *	case WL_BSS_TYPE:
2260 		 *	case WL_ESS_LIST:
2261 		 *	case WL_LINKSTATUS:
2262 		 *	case WL_RSSI:
2263 		 *	case WL_SCAN:
2264 		 *	case WL_LOAD_DEFAULTS:
2265 		 *	case WL_DISASSOCIATE:
2266 		 */
2267 		*need_net80211 = B_TRUE; /* let net80211 do the rest */
2268 		return (0);
2269 	}
2270 	/*
2271 	 * we will overwrite everything
2272 	 */
2273 	m->b_wptr = m->b_rptr + outfp->wldp_length;
2274 	return (ret);
2275 }
2276 
2277 static int
2278 iwi_wificfg_radio(struct ipw2200_softc *sc, uint32_t cmd, wldp_t *outfp)
2279 {
2280 	uint32_t	ret = ENOTSUP;
2281 
2282 	switch (cmd) {
2283 	case WLAN_GET_PARAM:
2284 		*(wl_linkstatus_t *)(outfp->wldp_buf) =
2285 		    ipw2200_radio_status(sc);
2286 		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_linkstatus_t);
2287 		outfp->wldp_result = WL_SUCCESS;
2288 		ret = 0; /* command success */
2289 		break;
2290 	case WLAN_SET_PARAM:
2291 	default:
2292 		break;
2293 	}
2294 	return (ret);
2295 }
2296 
2297 static int
2298 iwi_wificfg_desrates(wldp_t *outfp)
2299 {
2300 	/* return success, but with result NOTSUPPORTED */
2301 	outfp->wldp_length = WIFI_BUF_OFFSET;
2302 	outfp->wldp_result = WL_NOTSUPPORTED;
2303 	return (0);
2304 }
2305 /* End of IOCTL Handlers */
2306 
2307 void
2308 ipw2200_fix_channel(struct ieee80211com *ic, mblk_t *m)
2309 {
2310 	struct ieee80211_frame	*wh;
2311 	uint8_t			subtype;
2312 	uint8_t			*frm, *efrm;
2313 
2314 	wh = (struct ieee80211_frame *)m->b_rptr;
2315 
2316 	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT)
2317 		return;
2318 
2319 	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
2320 
2321 	if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
2322 	    subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP)
2323 		return;
2324 
2325 	/*
2326 	 * assume the message contains only 1 block
2327 	 */
2328 	frm   = (uint8_t *)(wh + 1);
2329 	efrm  = (uint8_t *)m->b_wptr;
2330 	frm  += 12;  /* skip tstamp, bintval and capinfo fields */
2331 	while (frm < efrm) {
2332 		if (*frm == IEEE80211_ELEMID_DSPARMS)
2333 #if IEEE80211_CHAN_MAX < 255
2334 		if (frm[2] <= IEEE80211_CHAN_MAX)
2335 #endif
2336 			ic->ic_curchan = &ic->ic_sup_channels[frm[2]];
2337 		frm += frm[1] + 2;
2338 	}
2339 }
2340 
2341 static void
2342 ipw2200_rcv_frame(struct ipw2200_softc *sc, struct ipw2200_frame *frame)
2343 {
2344 	struct ieee80211com	*ic = &sc->sc_ic;
2345 	uint8_t			*data = (uint8_t *)frame;
2346 	uint32_t		len;
2347 	struct ieee80211_frame	*wh;
2348 	struct ieee80211_node	*in;
2349 	mblk_t			*m;
2350 	int			i;
2351 
2352 	len = LE_16(frame->len);
2353 
2354 	/*
2355 	 * Skip the frame header, get the real data from the input
2356 	 */
2357 	data += sizeof (struct ipw2200_frame);
2358 
2359 	if ((len < sizeof (struct ieee80211_frame_min)) ||
2360 	    (len > IPW2200_RXBUF_SIZE)) {
2361 		IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT,
2362 		    "ipw2200_rcv_frame(): bad frame length=%u\n",
2363 		    LE_16(frame->len)));
2364 		return;
2365 	}
2366 
2367 	IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT,
2368 	    "ipw2200_rcv_frame(): chan = %d, length = %d\n", frame->chan, len));
2369 
2370 	m = allocb(len, BPRI_MED);
2371 	if (m) {
2372 		(void) memcpy(m->b_wptr, data, len);
2373 		m->b_wptr += len;
2374 
2375 		wh = (struct ieee80211_frame *)m->b_rptr;
2376 		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
2377 			/*
2378 			 * h/w decyption leaves the WEP bit, iv and CRC fields
2379 			 */
2380 			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
2381 			for (i = sizeof (struct ieee80211_frame) - 1;
2382 			    i >= 0; i--)
2383 				*(m->b_rptr + IEEE80211_WEP_IVLEN +
2384 				    IEEE80211_WEP_KIDLEN + i) =
2385 				    *(m->b_rptr + i);
2386 			m->b_rptr += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
2387 			m->b_wptr -= IEEE80211_WEP_CRCLEN;
2388 			wh = (struct ieee80211_frame *)m->b_rptr;
2389 		}
2390 
2391 		if (ic->ic_state == IEEE80211_S_SCAN) {
2392 			ic->ic_ibss_chan = &ic->ic_sup_channels[frame->chan];
2393 			ipw2200_fix_channel(ic, m);
2394 		}
2395 
2396 		in = ieee80211_find_rxnode(ic, wh);
2397 		IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT,
2398 		    "ipw2200_rcv_frame(): "
2399 		    "ni_esslen:%d, ni_essid[0-5]:%c%c%c%c%c%c\n",
2400 		    in->in_esslen,
2401 		    in->in_essid[0], in->in_essid[1], in->in_essid[2],
2402 		    in->in_essid[3], in->in_essid[4], in->in_essid[5]));
2403 
2404 		(void) ieee80211_input(ic, m, in, frame->rssi_dbm, 0);
2405 
2406 		ieee80211_free_node(in);
2407 	}
2408 	else
2409 		IPW2200_WARN((sc->sc_dip, CE_WARN,
2410 		    "ipw2200_rcv_frame(): "
2411 		    "cannot allocate receive message(%u)\n",
2412 		    LE_16(frame->len)));
2413 }
2414 
2415 static void
2416 ipw2200_rcv_notif(struct ipw2200_softc *sc, struct ipw2200_notif *notif)
2417 {
2418 	struct ieee80211com			*ic = &sc->sc_ic;
2419 	struct ipw2200_notif_association	*assoc;
2420 	struct ipw2200_notif_authentication	*auth;
2421 	uint8_t					*ndata = (uint8_t *)notif;
2422 
2423 	IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT,
2424 	    "ipw2200_rcv_notif(): type=%u\n", notif->type));
2425 
2426 	ndata += sizeof (struct ipw2200_notif);
2427 	switch (notif->type) {
2428 	case IPW2200_NOTIF_TYPE_ASSOCIATION:
2429 		assoc = (struct ipw2200_notif_association *)ndata;
2430 
2431 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
2432 		    "ipw2200_rcv_notif(): association=%u,%u\n",
2433 		    assoc->state, assoc->status));
2434 
2435 		switch (assoc->state) {
2436 		case IPW2200_ASSOC_SUCCESS:
2437 			ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
2438 			break;
2439 		case IPW2200_ASSOC_FAIL:
2440 			ieee80211_begin_scan(ic, 1); /* reset */
2441 			break;
2442 		default:
2443 			break;
2444 		}
2445 		break;
2446 
2447 	case IPW2200_NOTIF_TYPE_AUTHENTICATION:
2448 		auth = (struct ipw2200_notif_authentication *)ndata;
2449 
2450 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
2451 		    "ipw2200_rcv_notif(): authentication=%u\n", auth->state));
2452 
2453 		switch (auth->state) {
2454 		case IPW2200_AUTH_SUCCESS:
2455 			ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
2456 			break;
2457 		case IPW2200_AUTH_FAIL:
2458 			break;
2459 		default:
2460 			IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT,
2461 			    "ipw2200_rcv_notif(): "
2462 			    "unknown authentication state(%u)\n", auth->state));
2463 			break;
2464 		}
2465 		break;
2466 
2467 	case IPW2200_NOTIF_TYPE_SCAN_CHANNEL:
2468 		IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT,
2469 		    "ipw2200_rcv_notif(): scan-channel=%u\n",
2470 		    ((struct ipw2200_notif_scan_channel *)ndata)->nchan));
2471 		break;
2472 
2473 	case IPW2200_NOTIF_TYPE_SCAN_COMPLETE:
2474 		IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT,
2475 		    "ipw2200_rcv_notif():scan-completed,(%u,%u)\n",
2476 		    ((struct ipw2200_notif_scan_complete *)ndata)->nchan,
2477 		    ((struct ipw2200_notif_scan_complete *)ndata)->status));
2478 
2479 		/*
2480 		 * scan complete
2481 		 */
2482 		sc->sc_flags &= ~IPW2200_FLAG_SCANNING;
2483 		ieee80211_end_scan(ic);
2484 		break;
2485 
2486 	case IPW2200_NOTIF_TYPE_BEACON:
2487 	case IPW2200_NOTIF_TYPE_CALIBRATION:
2488 	case IPW2200_NOTIF_TYPE_NOISE:
2489 		/*
2490 		 * just ignore
2491 		 */
2492 		break;
2493 	default:
2494 		IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT,
2495 		    "ipw2200_rcv_notif(): unknown notification type(%u)\n",
2496 		    notif->type));
2497 		break;
2498 	}
2499 }
2500 
2501 static uint_t
2502 ipw2200_intr(caddr_t arg)
2503 {
2504 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
2505 	struct ieee80211com	*ic = &sc->sc_ic;
2506 	uint32_t		ireg, ridx, len, i;
2507 	uint8_t			*p, *rxbuf;
2508 	struct dma_region	*dr;
2509 	struct ipw2200_hdr	*hdr;
2510 	int			need_sched;
2511 	uint32_t		widx;
2512 
2513 	ireg = ipw2200_csr_get32(sc, IPW2200_CSR_INTR);
2514 
2515 	if (ireg == 0xffffffff)
2516 		return (DDI_INTR_UNCLAIMED);
2517 
2518 	if (!(ireg & IPW2200_INTR_MASK_ALL))
2519 		return (DDI_INTR_UNCLAIMED);
2520 
2521 	/*
2522 	 * mask all interrupts
2523 	 */
2524 	ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, 0);
2525 
2526 	/*
2527 	 * acknowledge all fired interrupts
2528 	 */
2529 	ipw2200_csr_put32(sc, IPW2200_CSR_INTR, ireg);
2530 
2531 	IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT,
2532 	    "ipw2200_intr(): enter. interrupt fired, int=0x%08x\n", ireg));
2533 
2534 	if (ireg & IPW2200_INTR_MASK_ERR) {
2535 
2536 		IPW2200_DBG(IPW2200_DBG_FATAL, (sc->sc_dip, CE_CONT,
2537 		    "ipw2200 interrupt(): int= 0x%08x\n", ireg));
2538 
2539 		/*
2540 		 * inform mfthread to recover hw error by stopping it
2541 		 */
2542 		mutex_enter(&sc->sc_mflock);
2543 		sc->sc_flags |= IPW2200_FLAG_HW_ERR_RECOVER;
2544 		mutex_exit(&sc->sc_mflock);
2545 
2546 	} else {
2547 		if (ireg & IPW2200_INTR_FW_INITED) {
2548 			mutex_enter(&sc->sc_ilock);
2549 			sc->sc_fw_ok = 1;
2550 			cv_signal(&sc->sc_fw_cond);
2551 			mutex_exit(&sc->sc_ilock);
2552 		}
2553 		if (ireg & IPW2200_INTR_RADIO_OFF) {
2554 			IPW2200_REPORT((sc->sc_dip, CE_CONT,
2555 			    "ipw2200_intr(): radio is OFF\n"));
2556 			/*
2557 			 * Stop hardware, will notify LINK is down
2558 			 */
2559 			ipw2200_stop(sc);
2560 		}
2561 		if (ireg & IPW2200_INTR_CMD_TRANSFER) {
2562 			mutex_enter(&sc->sc_cmd_lock);
2563 			ridx = ipw2200_csr_get32(sc,
2564 			    IPW2200_CSR_CMD_READ_INDEX);
2565 			i = RING_FORWARD(sc->sc_cmd_cur,
2566 			    sc->sc_cmd_free, IPW2200_CMD_RING_SIZE);
2567 			len = RING_FLEN(i, ridx, IPW2200_CMD_RING_SIZE);
2568 
2569 			IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT,
2570 			    "ipw2200_intr(): cmd-ring,i=%u,ridx=%u,len=%u\n",
2571 			    i, ridx, len));
2572 
2573 			if (len > 0) {
2574 				sc->sc_cmd_free += len;
2575 				cv_signal(&sc->sc_cmd_cond);
2576 			}
2577 			for (; i != ridx;
2578 			    i = RING_FORWARD(i, 1, IPW2200_CMD_RING_SIZE))
2579 				sc->sc_done[i] = 1;
2580 			mutex_exit(&sc->sc_cmd_lock);
2581 
2582 			mutex_enter(&sc->sc_ilock);
2583 			cv_signal(&sc->sc_cmd_status_cond);
2584 			mutex_exit(&sc->sc_ilock);
2585 		}
2586 		if (ireg & IPW2200_INTR_RX_TRANSFER) {
2587 			ridx = ipw2200_csr_get32(sc,
2588 			    IPW2200_CSR_RX_READ_INDEX);
2589 			widx = ipw2200_csr_get32(sc,
2590 			    IPW2200_CSR_RX_WRITE_INDEX);
2591 
2592 			IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT,
2593 			    "ipw2200_intr(): rx-ring,widx=%u,ridx=%u\n",
2594 			    ridx, widx));
2595 
2596 			for (; sc->sc_rx_cur != ridx;
2597 			    sc->sc_rx_cur = RING_FORWARD(sc->sc_rx_cur, 1,
2598 			    IPW2200_RX_RING_SIZE)) {
2599 				i	= sc->sc_rx_cur;
2600 				rxbuf	= sc->sc_rxbufs[i];
2601 				dr	= &sc->sc_dma_rxbufs[i];
2602 
2603 				/*
2604 				 * DMA sync
2605 				 */
2606 				(void) ddi_dma_sync(dr->dr_hnd, 0,
2607 				    IPW2200_RXBUF_SIZE, DDI_DMA_SYNC_FORKERNEL);
2608 				/*
2609 				 * Get rx header(hdr) and rx data(p) from rxbuf
2610 				 */
2611 				p	= rxbuf;
2612 				hdr	= (struct ipw2200_hdr *)p;
2613 				p	+= sizeof (struct ipw2200_hdr);
2614 
2615 				IPW2200_DBG(IPW2200_DBG_INT,
2616 				    (sc->sc_dip, CE_CONT,
2617 				    "ipw2200_intr(): Rx hdr type %u\n",
2618 				    hdr->type));
2619 
2620 				switch (hdr->type) {
2621 				case IPW2200_HDR_TYPE_FRAME:
2622 					ipw2200_rcv_frame(sc,
2623 					    (struct ipw2200_frame *)p);
2624 					break;
2625 
2626 				case IPW2200_HDR_TYPE_NOTIF:
2627 					ipw2200_rcv_notif(sc,
2628 					    (struct ipw2200_notif *)p);
2629 					break;
2630 				default:
2631 					IPW2200_DBG(IPW2200_DBG_INT,
2632 					    (sc->sc_dip, CE_CONT,
2633 					    "ipw2200_intr(): "
2634 					    "unknown Rx hdr type %u\n",
2635 					    hdr->type));
2636 					break;
2637 				}
2638 			}
2639 			/*
2640 			 * write sc_rx_cur backward 1 step into RX_WRITE_INDEX
2641 			 */
2642 			ipw2200_csr_put32(sc, IPW2200_CSR_RX_WRITE_INDEX,
2643 			    RING_BACKWARD(sc->sc_rx_cur, 1,
2644 			    IPW2200_RX_RING_SIZE));
2645 		}
2646 		if (ireg & IPW2200_INTR_TX1_TRANSFER) {
2647 			mutex_enter(&sc->sc_tx_lock);
2648 			ridx = ipw2200_csr_get32(sc,
2649 			    IPW2200_CSR_TX1_READ_INDEX);
2650 			len  = RING_FLEN(RING_FORWARD(sc->sc_tx_cur,
2651 			    sc->sc_tx_free, IPW2200_TX_RING_SIZE),
2652 			    ridx, IPW2200_TX_RING_SIZE);
2653 			IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT,
2654 			    "ipw2200_intr(): tx-ring,ridx=%u,len=%u\n",
2655 			    ridx, len));
2656 			sc->sc_tx_free += len;
2657 
2658 			need_sched = 0;
2659 			if ((sc->sc_tx_free > IPW2200_TX_RING_MIN) &&
2660 			    (sc->sc_flags & IPW2200_FLAG_TX_SCHED)) {
2661 				IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip,
2662 				    CE_CONT,
2663 				    "ipw2200_intr(): Need Reschedule!"));
2664 				need_sched = 1;
2665 				sc->sc_flags &= ~IPW2200_FLAG_TX_SCHED;
2666 			}
2667 			mutex_exit(&sc->sc_tx_lock);
2668 
2669 			if (need_sched)
2670 				mac_tx_update(ic->ic_mach);
2671 		}
2672 	}
2673 
2674 	/*
2675 	 * enable all interrupts
2676 	 */
2677 	ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, IPW2200_INTR_MASK_ALL);
2678 
2679 	return (DDI_INTR_CLAIMED);
2680 }
2681 
2682 
2683 /*
2684  *  Module Loading Data & Entry Points
2685  */
2686 DDI_DEFINE_STREAM_OPS(ipw2200_devops, nulldev, nulldev, ipw2200_attach,
2687     ipw2200_detach, ipw2200_reset, NULL, D_MP, NULL);
2688 
2689 static struct modldrv ipw2200_modldrv = {
2690 	&mod_driverops,
2691 	ipw2200_ident,
2692 	&ipw2200_devops
2693 };
2694 
2695 static struct modlinkage ipw2200_modlinkage = {
2696 	MODREV_1,
2697 	&ipw2200_modldrv,
2698 	NULL
2699 };
2700 
2701 int
2702 _init(void)
2703 {
2704 	int status;
2705 
2706 	status = ddi_soft_state_init(&ipw2200_ssp,
2707 	    sizeof (struct ipw2200_softc), 1);
2708 	if (status != DDI_SUCCESS)
2709 		return (status);
2710 
2711 	mac_init_ops(&ipw2200_devops, IPW2200_DRV_NAME);
2712 	status = mod_install(&ipw2200_modlinkage);
2713 	if (status != DDI_SUCCESS) {
2714 		mac_fini_ops(&ipw2200_devops);
2715 		ddi_soft_state_fini(&ipw2200_ssp);
2716 	}
2717 
2718 	return (status);
2719 }
2720 
2721 int
2722 _fini(void)
2723 {
2724 	int status;
2725 
2726 	status = mod_remove(&ipw2200_modlinkage);
2727 	if (status == DDI_SUCCESS) {
2728 		mac_fini_ops(&ipw2200_devops);
2729 		ddi_soft_state_fini(&ipw2200_ssp);
2730 	}
2731 
2732 	return (status);
2733 }
2734 
2735 int
2736 _info(struct modinfo *modinfop)
2737 {
2738 	return (mod_info(&ipw2200_modlinkage, modinfop));
2739 }
2740