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