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