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