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