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